void initialize_soil (cell_data_struct **cell, soil_con_struct *soil_con, veg_con_struct *veg_con, int veg_num) /********************************************************************** initialize_soil Keith Cherkauer July 31, 1996 This routine initializes the soil variable arrays for each new grid cell. modifications: 11-18-02 Modified to initialize wetland soil moisture. LCB 2006-Nov-07 Removed LAKE_MODEL option. TJB 2007-Aug-10 Added features for EXCESS_ICE option. JCA 2009-Mar-16 Modified to use min_liq (minimum allowable liquid water content) instead of resid_moist. For unfrozen soil, min_liq = resid_moist. TJB 2009-Jul-31 Replaced extra lake/wetland veg tile with reference to veg_con[j].LAKE. TJB 2009-Dec-11 Removed min_liq and options.MIN_LIQ. TJB 2011-Mar-01 Now initializes more cell data structure terms, including asat and zwt. TJB **********************************************************************/ { extern option_struct options; int veg, band, lindex, frost_area; double tmp_moist[MAX_LAYERS]; double tmp_runoff; for ( veg = 0 ; veg <= veg_num ; veg++) { for(band=0;band<options.SNOW_BAND;band++) { cell[veg][band].baseflow = 0; cell[veg][band].runoff = 0; for(lindex=0;lindex<options.Nlayer;lindex++) { cell[veg][band].layer[lindex].evap = 0; cell[veg][band].layer[lindex].moist = soil_con->init_moist[lindex]; if (cell[veg][band].layer[lindex].moist > soil_con->max_moist[lindex]) cell[veg][band].layer[lindex].moist = soil_con->max_moist[ lindex]; tmp_moist[lindex] = cell[veg][band].layer[lindex].moist; #if SPATIAL_FROST for (frost_area=0; frost_area<FROST_SUBAREAS; frost_area++) { cell[veg][band].layer[lindex].ice[frost_area] = 0; } #else cell[veg][band].layer[lindex].ice = 0; #endif } compute_runoff_and_asat(soil_con, tmp_moist, 0, &(cell[veg][band].asat), &tmp_runoff); wrap_compute_zwt(soil_con, &(cell[veg][band])); } } }
/****************************************************************************** * @brief Compute the state variables (energy balance, water balance, * and snow components) that are derived from the variables that * are stored in state files. *****************************************************************************/ void compute_derived_state_vars(all_vars_struct *all_vars, soil_con_struct *soil_con, veg_con_struct *veg_con) { extern global_param_struct global_param; extern option_struct options; char FIRST_VEG; size_t Nveg; size_t veg; size_t lidx; size_t band; size_t tmpTshape[] = { options.Nlayer, options.Nnode, options.Nfrost + 1 }; size_t tmpZshape[] = { options.Nlayer, options.Nnode }; int ErrorFlag; double Cv; double moist[MAX_VEG][MAX_BANDS][MAX_LAYERS]; double dt_thresh; double tmp_runoff; double ***tmpT; double **tmpZ; cell_data_struct **cell; energy_bal_struct **energy; snow_data_struct **snow; cell = all_vars->cell; energy = all_vars->energy; snow = all_vars->snow; Nveg = veg_con[0].vegetat_type_num; // allocate memory for tmpT and tmpZ malloc_3d_double(tmpTshape, &tmpT); malloc_2d_double(tmpZshape, &tmpZ); /****************************************** Compute derived soil layer vars ******************************************/ for (veg = 0; veg <= Nveg; veg++) { // Initialize soil for existing vegetation types Cv = veg_con[veg].Cv; if (Cv > 0) { for (band = 0; band < options.SNOW_BAND; band++) { // Initialize soil for existing snow elevation bands if (soil_con->AreaFract[band] > 0.) { // set up temporary moist arrays for (lidx = 0; lidx < options.Nlayer; lidx++) { moist[veg][band][lidx] = cell[veg][band].layer[lidx].moist; } // compute saturated area and water table compute_runoff_and_asat(soil_con, moist[veg][band], 0, &(cell[veg][band].asat), &tmp_runoff); wrap_compute_zwt(soil_con, &(cell[veg][band])); } } } } /****************************************** Compute derived soil snow state vars ******************************************/ for (veg = 0; veg <= Nveg; veg++) { for (band = 0; band < options.SNOW_BAND; band++) { if (snow[veg][band].density > 0.) { snow[veg][band].depth = CONST_RHOFW * snow[veg][band].swq / snow[veg][band].density; } } } /****************************************** Compute soil thermal node properties ******************************************/ FIRST_VEG = true; for (veg = 0; veg <= Nveg; veg++) { // Initialize soil for existing vegetation types Cv = veg_con[veg].Cv; if (Cv > 0) { for (band = 0; band < options.SNOW_BAND; band++) { // Initialize soil for existing snow elevation bands if (soil_con->AreaFract[band] > 0.) { /** Set soil properties for all soil nodes **/ if (FIRST_VEG) { FIRST_VEG = false; set_node_parameters(soil_con->Zsum_node, soil_con->max_moist_node, soil_con->expt_node, soil_con->bubble_node, soil_con->alpha, soil_con->beta, soil_con->gamma, soil_con->depth, soil_con->max_moist, soil_con->expt, soil_con->bubble, options.Nnode, options.Nlayer); } // set soil moisture properties for all soil thermal nodes if (options.FULL_ENERGY || options.FROZEN_SOIL) { ErrorFlag = distribute_node_moisture_properties( energy[veg][band].moist, energy[veg][band].ice, energy[veg][band].kappa_node, energy[veg][band].Cs_node, soil_con->Zsum_node, energy[veg][band].T, soil_con->max_moist_node, soil_con->expt_node, soil_con->bubble_node, moist[veg][band], soil_con->depth, soil_con->soil_dens_min, soil_con->bulk_dens_min, soil_con->quartz, soil_con->soil_density, soil_con->bulk_density, soil_con->organic, options.Nnode, options.Nlayer, soil_con->FS_ACTIVE); if (ErrorFlag == ERROR) { log_err("Error setting physical properties for " "soil thermal nodes"); } } // Check node spacing v time step // (note this is only approximate since heat capacity and // conductivity can change considerably during the // simulation depending on soil moisture and ice content) if ((options.FROZEN_SOIL && !options.QUICK_FLUX) && !options.IMPLICIT) { // in seconds dt_thresh = 0.5 * energy[veg][band].Cs_node[1] / energy[veg][band].kappa_node[1] * pow((soil_con->dz_node[1]), 2); if (global_param.dt > dt_thresh) { log_err("You are currently running FROZEN SOIL " "with an explicit method (IMPLICIT is " "set to FALSE). For the explicit method " "to be stable, time step %f seconds is too " "large for the given thermal node spacing " "%f m, soil heat capacity %f J/m3/K, and " "soil thermal conductivity %f J/m/s/K. " "Either set IMPLICIT to TRUE in your " "global parameter file (this is the " "recommended action), or decrease time " "step length to <= %f seconds, or decrease " "the number of soil thermal nodes.", global_param.dt, soil_con->dz_node[1], energy[veg][band].Cs_node[1], energy[veg][band].kappa_node[1], dt_thresh); } } /* calculate soil layer temperatures */ if (options.QUICK_FLUX) { ErrorFlag = estimate_layer_temperature_quick_flux( cell[veg][band].layer, soil_con->depth, soil_con->dp, energy[veg][band].T[0], energy[veg][band].T[1], soil_con->avg_temp); if (ErrorFlag == ERROR) { log_err("Error calculating layer temperature " "using QUICK_FLUX option"); } } else { estimate_frost_temperature_and_depth( tmpT, tmpZ, soil_con->Zsum_node, energy[veg][band].T, soil_con->depth, soil_con->frost_fract, soil_con->frost_slope, options.Nnode, options.Nlayer); ErrorFlag = estimate_layer_temperature( cell[veg][band].layer, tmpT, tmpZ, soil_con->Zsum_node, soil_con->depth, options.Nnode, options.Nlayer); if (ErrorFlag == ERROR) { log_err("Error calculating layer temperature"); } } /* Find freezing and thawing front depths */ if (!options.QUICK_FLUX && soil_con->FS_ACTIVE) { find_0_degree_fronts(&energy[veg][band], soil_con->Zsum_node, energy[veg][band].T, options.Nnode); } } } } } // free memory for tmpT and tmpZ free_3d_double(tmpTshape, tmpT); free_2d_double(tmpZshape, tmpZ); }
int runoff(cell_data_struct *cell, energy_bal_struct *energy, soil_con_struct *soil_con, double ppt, double *frost_fract, int dt, int Nnodes, int band, int rec, int iveg) /********************************************************************** runoff.c Keith Cherkauer May 18, 1996 This subroutine calculates infiltration and runoff from the surface, gravity driven drainage between all soil layers, and generates baseflow from the bottom layer.. sublayer indecies are always [layer number][sublayer number] [layer number] is the current VIC model moisture layer [sublayer number] is the current sublayer number where: 0 = thawed sublayer, 1 = frozen sublayer, and 2 = unfrozen sublayer. when the model is run withoputfrozen soils, the sublayer number is always = 2 (unfrozen). UNITS: Ksat (mm/day) Q12 (mm/time step) liq, ice (mm) inflow (mm) runoff (mm) Variables: ppt incoming precipitation and snow melt mu fraction of area that receives precipitation inflow incoming water corrected for fractional area of precip (mu) MODIFICATIONS: 5/22/96 Routine modified to account for spatially varying precipitation, and it's effects on runoff. KAC 11/96 Code modified to account for extra model layers needed for frozen soils modeling. KAC 1/9/97 Infiltration and other rate parameters modified for time scales of less than 1 day. KAC 4-1-98 Soil moisture transport is now done on an hourly time step, irregardless to the model time step, to prevent numerical stabilities in the solution Dag and KAC 01-24-00 simplified handling of soil moisture for the frozen soil algorithm. all option selection now use the same soil moisture transport method KAC 6-8-2000 modified to handle spatially distributed soil frost KAC 06-07-03 modified so that infiltration is computed using only the top two soil moisture layers, rather than all but the bottom most layer. This preserves the functionality of the original model design, but is more realistic for handling multiple soil moisture layers 06-Sep-03 Changed calculation of dt_baseflow to go to zero when soil liquid moisture <= residual moisture. Changed block that handles case of total soil moisture < residual moisture to not allow dt_baseflow to go negative. TJB 17-May-04 Changed block that handles baseflow when soil moisture drops below residual moisture. Now, the block is only entered if baseflow > 0 and soil moisture < residual, and the amount of water taken out of baseflow and given to the soil cannot exceed baseflow. In addition, error messages are no longer printed, since it isn't an error to be in that block. TJB 2007-Apr-04 Modified to return Error status from distribute_node_moisture_properties GCT/KAC 2007-Apr-24 Passes soil_con->Zsum_node to distribute_node_moisture_properties. JCA 2007-Jun-13 Fixed bug arising from earlier fix to dt_baseflow calculation. Earlier fix took residual moisture into account in the linear part of the baseflow eqn, but not in the non-linear part. Now we take residual moisture into account correctly throughout the whole equation. Also re-wrote equation in simpler form. TJB 2007-Aug-15 Changed SPATIAL_FROST if statement to enclose the correct end-bracket for the frost_area loop. JCA 2007-Aug-09 Added features for EXCESS_ICE option. JCA Including adding SubsidenceUpdate flag for parts of the routine that will be used if redistributing soil moisture after subsidence. 2007-Sep-18 Modified to correctly handle evaporation from spatially distributed soil frost. Original version could produce negative soil moisture in fractions with high ice content since only total evaporation was checked versus total liquid water content, not versus available liquid water in each frost subsection. KAC via TJB 2007-Sep-20 Removed logic that reset resid_moist[i]. Previously, resid_moist[i] was reset to 0 for i > 0 when resid_moist[0] == 0. Such resetting of soil properties was deemed unnecessary and confusing, since VIC would end up using different residual moisture values than those specified by the user. If a user truly wants to specify residual moisture in all layers to be 0, the user should set these explicitly in the soil parameter file. Also fixed typo in fprintf() on line 289. TJB 2007-Oct-13 Fixed the checks on the lower bound of soil moisture. Previously, the condition was (moist[lindex]+ice[lindex]) < resid_moist[lindex] which led to liquid soil moisture falling below residual during winter conditions. This has been changed to moist[lindex] < resid_moist[lindex] to eliminate these errors and make the logic consistent with the rest of the code. TJB 2007-Oct-13 Renamed all *moist* variables to *liq* if they only refer to liquid soil moisture. This makes the logic much easier to understand. TJB 2007-Oct-13 Modified the caps on Q12 and baseflow for the case of frozen soil. Now, the lower bound on liquid soil moisture is the maximum unfrozen component of residual moisture at current soil temperature, i.e. liquid soil moisture may be less than residual moisture as long as the total (liq + ice) moisture is >= residual moisture AND the liquid fraction of the total is appropriate for the temperature. Without this condition, we could have an apparent loss of liquid moisture due to conversion to ice and the resulting adjustments of Q12 and baseflow could pull water out of the air to bring liquid moisture up to residual. This fix should set a reasonable lower bound and still ensure that no extra water is condensed out of the air simply to bring liquid water up to residual. TJB 2008-Oct-23 Added check to make sure top_moist never exceeds top_max_moist; otherwise rounding errors could cause it to exceed top_max_moist and produce NaN's. LCB via TJB 2009-Feb-09 Removed dz_node from call to distribute_node_moisture_properties. KAC via TJB 2009=Feb-10 Replaced all occurrences of resid_moist with min_liq, after min_liq was defined. This makes the use of min_liq consistent with its documented role in the subroutine. KAC via TJB 2009-Feb-10 Removed Tlayer from selection criteria to include ice in min_liq calculation. Soil layers can be above 0C with ice present, as ice content is set from soil nodes. KAC via TJB 2009-Mar-16 Made min_liq an element of the layer_data_struct, so that its value can be computed earlier in the model code, in a more efficient manner (in initialize_soil() and estimate_layer_ice_content()). TJB 2009-May-17 Added asat to cell_data. TJB 2009-Jun-26 Simplified argument list of runoff() by passing all cell_data variables via a single reference to the cell data structure. TJB 2009-Dec-11 Removed min_liq and options.MIN_LIQ. Constraints on liq[lindex] have been removed and/or replaced by constraints on (liq[lindex]+ice[lindex]). Thus, it is possible to freeze all of the soil moisture, as long as total moisture > residual moisture. TJB 2010-Feb-07 Fixed bug in runoff computation for case when soil column is completely saturated. TJB 2010-Nov-29 Moved computation of saturated area to correct place in code for handling SPATIAL_FROST. TJB 2010-Dec-01 Added call to compute_zwt(). TJB 2011-Mar-01 Replaced compute_zwt() with wrap_compute_zwt(). Moved computation of runoff and saturated area to a separate function compute_runoff_and_asat(), which can be called elsewhere. TJB 2011-Jun-03 Added options.ORGANIC_FRACT. Soil properties now take organic fraction into account. TJB 2012-Jan-16 Removed LINK_DEBUG code BN 2013-Dec-26 Replaced LOW_RES_MOIST compile-time option with LOG_MATRIC run-time option. TJB 2013-Dec-26 Removed EXCESS_ICE option. TJB 2013-Dec-27 Moved SPATIAL_FROST to options_struct. TJB 2013-Dec-27 Removed QUICK_FS option. TJB 2014-Mar-28 Removed DIST_PRCP option. TJB 2014-May-09 Added check on liquid soil moisture to ensure always >= 0. TJB **********************************************************************/ { extern option_struct options; int firstlayer, lindex; int i; int last_layer[MAX_LAYERS*3]; int last_index; int last_cnt; int time_step; int tmplayer; int frost_area; int ErrorFlag; double A, frac; double tmp_runoff; double inflow; double resid_moist[MAX_LAYERS]; // residual moisture (mm) double org_moist[MAX_LAYERS]; // total soil moisture (liquid and frozen) at beginning of this function (mm) double avail_liq[MAX_LAYERS][MAX_FROST_AREAS]; // liquid soil moisture available for evap/drainage (mm) double liq[MAX_LAYERS]; // current liquid soil moisture (mm) double ice[MAX_LAYERS]; // current frozen soil moisture (mm) double moist[MAX_LAYERS]; // current total soil moisture (liquid and frozen) (mm) double max_moist[MAX_LAYERS]; // maximum storable moisture (liquid and frozen) (mm) double Ksat[MAX_LAYERS]; double Q12[MAX_LAYERS-1]; double Dsmax; double tmp_inflow; double tmp_moist; double tmp_moist_for_runoff[MAX_LAYERS]; double tmp_liq; double dt_inflow; double dt_runoff; double runoff[MAX_FROST_AREAS]; double tmp_dt_runoff[MAX_FROST_AREAS]; double baseflow[MAX_FROST_AREAS]; double dt_baseflow; double rel_moist; double evap[MAX_LAYERS][MAX_FROST_AREAS]; double sum_liq; double evap_fraction; double evap_sum; double min_temp; double max_temp; double tmp_fract; double Tlayer_spatial[MAX_LAYERS][MAX_FROST_AREAS]; double b[MAX_LAYERS]; layer_data_struct *layer; layer_data_struct tmp_layer; /** Set Residual Moisture **/ for ( i = 0; i < options.Nlayer; i++ ) resid_moist[i] = soil_con->resid_moist[i] * soil_con->depth[i] * 1000.; /** Allocate and Set Values for Soil Sublayers **/ layer = cell->layer; cell->runoff = 0; cell->baseflow = 0; cell->asat = 0; for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) baseflow[frost_area] = 0; for ( lindex = 0; lindex < options.Nlayer; lindex++ ) { evap[lindex][0] = layer[lindex].evap/(double)dt; org_moist[lindex] = layer[lindex].moist; layer[lindex].moist = 0; if ( evap[lindex][0] > 0 ) { // if there is positive evaporation sum_liq = 0; // compute available soil moisture for each frost sub area. for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) { avail_liq[lindex][frost_area] = (org_moist[lindex] - layer[lindex].ice[frost_area] - resid_moist[lindex]); if (avail_liq[lindex][frost_area] < 0) avail_liq[lindex][frost_area] = 0; sum_liq += avail_liq[lindex][frost_area]*frost_fract[frost_area]; } // compute fraction of available soil moisture that is evaporated if (sum_liq > 0) { evap_fraction = evap[lindex][0] / sum_liq; } else { evap_fraction = 1.0; } // distribute evaporation between frost sub areas by percentage evap_sum = evap[lindex][0]; for ( frost_area = options.Nfrost - 1; frost_area >= 0; frost_area-- ) { evap[lindex][frost_area] = avail_liq[lindex][frost_area] * evap_fraction; avail_liq[lindex][frost_area] -= evap[lindex][frost_area]; evap_sum -= evap[lindex][frost_area] * frost_fract[frost_area]; } } else { for ( frost_area = options.Nfrost - 1; frost_area > 0; frost_area-- ) evap[lindex][frost_area] = evap[lindex][0]; } } // compute temperatures of frost subareas for ( lindex = 0; lindex < options.Nlayer; lindex++ ) { min_temp = layer[lindex].T - soil_con->frost_slope / 2.; max_temp = min_temp + soil_con->frost_slope; for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) { if ( options.Nfrost > 1 ) { if ( frost_area == 0 ) tmp_fract = frost_fract[0] / 2.; else tmp_fract += (frost_fract[frost_area-1] + frost_fract[frost_area]) / 2.; Tlayer_spatial[lindex][frost_area] = linear_interp(tmp_fract, 0, 1, min_temp, max_temp); } else Tlayer_spatial[lindex][frost_area] = layer[lindex].T; } } for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) { /** ppt = amount of liquid water coming to the surface **/ inflow = ppt; /************************************************** Initialize Variables **************************************************/ for ( lindex = 0; lindex < options.Nlayer; lindex++ ) { Ksat[lindex] = soil_con->Ksat[lindex] / 24.; b[lindex] = (soil_con->expt[lindex] - 3.) / 2.; /** Set Layer Liquid Moisture Content **/ liq[lindex] = org_moist[lindex] - layer[lindex].ice[frost_area]; /** Set Layer Frozen Moisture Content **/ ice[lindex] = layer[lindex].ice[frost_area]; /** Set Layer Maximum Moisture Content **/ max_moist[lindex] = soil_con->max_moist[lindex]; } // initialize variables for each layer /****************************************************** Runoff Based on Soil Moisture Level of Upper Layers ******************************************************/ for(lindex=0;lindex<options.Nlayer;lindex++) { tmp_moist_for_runoff[lindex] = (liq[lindex] + ice[lindex]); } compute_runoff_and_asat(soil_con, tmp_moist_for_runoff, inflow, &A, &(runoff[frost_area])); // save dt_runoff based on initial runoff estimate, // since we will modify total runoff below for the case of completely saturated soil tmp_dt_runoff[frost_area] = runoff[frost_area] / (double) dt; /************************************************** Compute Flow Between Soil Layers (using an hourly time step) **************************************************/ dt_inflow = inflow / (double) dt; for (time_step = 0; time_step < dt; time_step++) { inflow = dt_inflow; last_cnt = 0; /************************************* Compute Drainage between Sublayers *************************************/ for( lindex = 0; lindex < options.Nlayer-1; lindex++ ) { /** Brooks & Corey relation for hydraulic conductivity **/ if((tmp_liq = liq[lindex] - evap[lindex][frost_area]) < resid_moist[lindex]) tmp_liq = resid_moist[lindex]; if(liq[lindex] > resid_moist[lindex]) { Q12[lindex] = Ksat[lindex] * pow(((tmp_liq - resid_moist[lindex]) / (soil_con->max_moist[lindex] - resid_moist[lindex])), soil_con->expt[lindex]); } else Q12[lindex] = 0.; last_layer[last_cnt] = lindex; } /************************************************** Solve for Current Soil Layer Moisture, and Check Versus Maximum and Minimum Moisture Contents. **************************************************/ firstlayer = TRUE; last_index = 0; for ( lindex = 0; lindex < options.Nlayer - 1; lindex++ ) { if ( lindex == 0 ) dt_runoff = tmp_dt_runoff[frost_area]; else dt_runoff = 0; /* transport moisture for all sublayers **/ tmp_inflow = 0.; /** Update soil layer moisture content **/ liq[lindex] = liq[lindex] + (inflow - dt_runoff) - (Q12[lindex] + evap[lindex][frost_area]); /** Verify that soil layer moisture is less than maximum **/ if((liq[lindex]+ice[lindex]) > max_moist[lindex]) { tmp_inflow = (liq[lindex]+ice[lindex]) - max_moist[lindex]; liq[lindex] = max_moist[lindex] - ice[lindex]; if(lindex==0) { Q12[lindex] += tmp_inflow; tmp_inflow = 0; } else { tmplayer = lindex; while(tmp_inflow > 0) { tmplayer--; if ( tmplayer < 0 ) { /** If top layer saturated, add to runoff **/ runoff[frost_area] += tmp_inflow; tmp_inflow = 0; } else { /** else add excess soil moisture to next higher layer **/ liq[tmplayer] += tmp_inflow; if((liq[tmplayer]+ice[tmplayer]) > max_moist[tmplayer]) { tmp_inflow = ((liq[tmplayer] + ice[tmplayer]) - max_moist[tmplayer]); liq[tmplayer] = max_moist[tmplayer] - ice[tmplayer]; } else tmp_inflow=0; } } } /** end trapped excess moisture **/ } /** end check if excess moisture in top layer **/ firstlayer=FALSE; /** verify that current layer moisture is greater than minimum **/ if (liq[lindex] < 0) { /** liquid cannot fall below 0 **/ Q12[lindex] += liq[lindex]; liq[lindex] = 0; } if ((liq[lindex]+ice[lindex]) < resid_moist[lindex]) { /** moisture cannot fall below minimum **/ Q12[lindex] += (liq[lindex]+ice[lindex]) - resid_moist[lindex]; liq[lindex] = resid_moist[lindex] - ice[lindex]; } inflow = (Q12[lindex]+tmp_inflow); Q12[lindex] += tmp_inflow; last_index++; } /* end loop through soil layers */ /************************************************** Compute Baseflow **************************************************/ /** ARNO model for the bottom soil layer (based on bottom soil layer moisture from previous time step) **/ lindex = options.Nlayer-1; Dsmax = soil_con->Dsmax / 24.; /** Compute relative moisture **/ rel_moist = (liq[lindex]-resid_moist[lindex]) / (soil_con->max_moist[lindex]-resid_moist[lindex]); /** Compute baseflow as function of relative moisture **/ frac = Dsmax * soil_con->Ds / soil_con->Ws; dt_baseflow = frac * rel_moist; if (rel_moist > soil_con->Ws) { frac = (rel_moist - soil_con->Ws) / (1 - soil_con->Ws); dt_baseflow += Dsmax * (1 - soil_con->Ds / soil_con->Ws) * pow(frac,soil_con->c); } /** Make sure baseflow isn't negative **/ if(dt_baseflow < 0) dt_baseflow = 0; /** Extract baseflow from the bottom soil layer **/ liq[lindex] += Q12[lindex-1] - (evap[lindex][frost_area] + dt_baseflow); /** Check Lower Sub-Layer Moistures **/ tmp_moist = 0; /* If soil moisture has gone below minimum, take water out * of baseflow and add back to soil to make up the difference * Note: this may lead to negative baseflow, in which case we will * reduce evap to make up for it */ if((liq[lindex]+ice[lindex]) < resid_moist[lindex]) { dt_baseflow += (liq[lindex]+ice[lindex]) - resid_moist[lindex]; liq[lindex] = resid_moist[lindex] - ice[lindex]; } if((liq[lindex]+ice[lindex]) > max_moist[lindex]) { /* soil moisture above maximum */ tmp_moist = ((liq[lindex]+ice[lindex]) - max_moist[lindex]); liq[lindex] = max_moist[lindex] - ice[lindex]; tmplayer = lindex; while(tmp_moist > 0) { tmplayer--; if(tmplayer<0) { /** If top layer saturated, add to runoff **/ runoff[frost_area] += tmp_moist; tmp_moist = 0; } else { /** else if sublayer exists, add excess soil moisture **/ liq[tmplayer] += tmp_moist ; if ( ( liq[tmplayer] + ice[tmplayer]) > max_moist[tmplayer] ) { tmp_moist = ((liq[tmplayer] + ice[tmplayer]) - max_moist[tmplayer]); liq[tmplayer] = max_moist[tmplayer] - ice[tmplayer]; } else tmp_moist=0; } } } baseflow[frost_area] += dt_baseflow; } /* end of hourly time step loop */ /** If negative baseflow, reduce evap accordingly **/ if ( baseflow[frost_area] < 0 ) { layer[lindex].evap += baseflow[frost_area]; baseflow[frost_area] = 0; } /** Recompute Asat based on final moisture level of upper layers **/ for(lindex=0;lindex<options.Nlayer;lindex++) { tmp_moist_for_runoff[lindex] = (liq[lindex] + ice[lindex]); } compute_runoff_and_asat(soil_con, tmp_moist_for_runoff, 0, &A, &tmp_runoff); /** Store tile-wide values **/ for ( lindex = 0; lindex < options.Nlayer; lindex++ ) layer[lindex].moist += ((liq[lindex] + ice[lindex]) * frost_fract[frost_area]); cell->asat += A * frost_fract[frost_area]; cell->runoff += runoff[frost_area] * frost_fract[frost_area]; cell->baseflow += baseflow[frost_area] * frost_fract[frost_area]; } /** Compute water table depth **/ wrap_compute_zwt(soil_con, cell); /** Recompute Thermal Parameters Based on New Moisture Distribution **/ if(options.FULL_ENERGY || options.FROZEN_SOIL) { for(lindex=0;lindex<options.Nlayer;lindex++) { tmp_layer = cell->layer[lindex]; moist[lindex] = tmp_layer.moist; } ErrorFlag = distribute_node_moisture_properties(energy->moist, energy->ice, energy->kappa_node, energy->Cs_node, soil_con->Zsum_node, energy->T, soil_con->max_moist_node, soil_con->expt_node, soil_con->bubble_node, moist, soil_con->depth, soil_con->soil_dens_min, soil_con->bulk_dens_min, soil_con->quartz, soil_con->soil_density, soil_con->bulk_density, soil_con->organic, Nnodes, options.Nlayer, soil_con->FS_ACTIVE); if ( ErrorFlag == ERROR ) return (ERROR); } return (0); }