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])); } } }
int initialize_model_state(all_vars_struct *all_vars, all_vars_struct *all_vars_crop, dmy_struct dmy, global_param_struct *global_param, filep_struct filep, int cellnum, int Nveg, int Nnodes, double surf_temp, soil_con_struct *soil_con, veg_con_struct *veg_con, lake_con_struct lake_con) /********************************************************************** initialize_model_state Keith Cherkauer April 17, 2000 This routine initializes the model state (energy balance, water balance, and snow components). If a state file is provided to the model than its contents are checked to see if it agrees with the current simulation set-up, if so it is used to initialize the model state. If no state file is provided the model initializes all variables with defaults and the user should expect to throw out the beginning of the simulation period as model start-up. UNITS: (m, s, kg, C, moisture in mm) unless otherwise specified Modifications: 4-17-00 Modified from initialize_energy_bal.c and initialize_snow.c to provide a single controlling routine for initializing the model state. 9-00 Fixed bug where initialization of soil node temperatures and moitures was within two vegetation loops, thus only the first vegetation type was properly initialized. KAC 2-19-03 Modified to initialize soil and vegetation parameters for the dry grid cell fraction, if distributed precipitation is activated. KAC 11-18-02 Modified to initialize lake and wetland algorithms variables. LCB 2-10-03 Fixed looping problem with initialization of soil moisture. KAC 3-12-03 Modified so that soil layer ice content is only calculated when frozen soil is implemented and active in the current grid cell. KAC 04-10-03 Modified to read storm parameters from model state file. KAC 04-25-03 Modified to work with vegetation type specific storm parameters. KAC 07-May-04 Initialize soil_con->dz_node[Nnodes] to 0.0, since it is accessed in set_node_parameters(). TJB 01-Nov-04 Added support for state files containing SPATIAL_FROST and LAKE_MODEL state variables. TJB 2006-Apr-21 Replaced Cv (uninitialized) with lake_con.Cl[0] in surfstor calculation. TJB 2006-Sep-23 Implemented flexible output configuration; uses the new save_data structure to track changes in moisture storage over each time step; this needs initialization here. TJB 2006-Oct-10 Added snow[veg][band].snow_canopy to save_data.swe. TJB 2006-Oct-16 Merged infiles and outfiles structs into filep_struct; This included removing the unused init_snow file. TJB 2006-Nov-07 Removed LAKE_MODEL option. TJB 2007-Apr-24 Added EXP_TRANS option. JCA 2007-Apr-24 Zsum_node loaded into soil_con structure for later use without having to recalculate. JCA 2007-Aug-09 Added features for EXCESS_ICE option. JCA 2007-Aug-21 Return value of ErrorFlag if error in distribute_node_moisture_properties. JCA 2007-Sep-18 Check for soil moist exceeding max moist moved from read_initial_model_state to here. JCA 2007-Oct-24 Modified initialize_lake() to return ErrorFlag. TJB 2008-Mar-01 Reinserted missing logic for QUICK_FS in calls to distribute_node_moisture_properties() and estimate_layer_ice_content(). TJB 2009-Feb-09 Removed dz_node from call to distribute_node_moisture_properties. KAC via TJB 2009-Feb-09 Removed dz_node from call to find_0_degree_front. KAC via TJB 2009-Mar-15 Modified to not call estimate_layer_ice_content() if not modeling frozen soil. KAC via TJB 2009-Mar-16 Added resid_moist to argument list of estimate_layer_ice_content(). This allows computation of min_liq, the minimum allowable liquid water content in each layer as a function of temperature. TJB 2009-Jun-09 Modified to use extension of veg_lib structure to contain bare soil information. TJB 2009-Jul-26 Added initial estimate of incoming longwave at surface (LongUnderOut) for use in canopy snow T iteration. TJB 2009-Jul-31 Removed extra lake/wetland veg tile. TJB 2009-Sep-19 Added T fbcount to count TFALLBACK occurrences. TJB 2009-Sep-19 Made initialization of Tfoliage more accurate for snow bands. TJB 2009-Sep-28 Added initialization of energy structure. TJB 2009-Nov-15 Added check to ensure that depth of first thermal node is <= depth of first soil layer. TJB 2009-Dec-11 Removed initialization of save_data structure, since this is now performed by the initial call to put_data(). TJB 2009-Dec-11 Removed min_liq and options.MIN_LIQ. TJB 2010-Nov-11 Updated call to initialize_lake() to accommodate new skip_hydro flag. TJB 2011-Mar-01 Updated calls to initialize_soil() and initialize_lake() to accommodate new arguments. Added more detailed validation of soil moisture. TJB 2011-Mar-05 Added validation of initial soil moisture, ice, and snow variables to make sure they are self-consistent. TJB 2011-May-31 Removed options.GRND_FLUX. Now soil temperatures and ground flux are always computed. TJB 2011-Jun-03 Added options.ORGANIC_FRACT. Soil properties now take organic fraction into account. TJB 2011-Jul-05 Changed logic initializing soil temperatures so that type of initialization depends solely on options.QUICK_FLUX; options.Nnodes is no longer automatically reset here. TJB 2012-Jan-16 Removed LINK_DEBUG code BN 2012-Jan-28 Added stability check for case of (FROZEN_SOIL=TRUE && IMPLICIT=FALSE). TJB 2013-Jul-25 Fixed incorrect condition on lake initialization. TJB 2013-Jul-25 Moved computation of tmp_moist argument of compute_runoff_and_asat() so that it would always be initialized. 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-Jan-13 Added validation of Nnodes and dp for EXP_TRANS=TRUE. TJB 2014-Feb-09 Made non-spinup initial temperatures more consistent with annual average air temperature and bottom boundary temperature. TJB 2014-Mar-28 Removed DIST_PRCP option. TJB **********************************************************************/ { extern option_struct options; extern veg_lib_struct *veg_lib; char ErrStr[MAXSTRING]; char FIRST_VEG; int i, j, ii, veg, index; int idx; int cidx; int lidx; int nidx; double tmp_moist[MAX_LAYERS]; double tmp_runoff; int band; int frost_area; int ErrorFlag; double Cv; double Zsum, dp; double tmpdp, tmpadj, Bexp; double Tair; double tmp; double *M; double moist[MAX_VEG][MAX_BANDS][MAX_LAYERS]; double ice[MAX_VEG][MAX_BANDS][MAX_LAYERS][MAX_FROST_AREAS]; double Clake; double surf_swq; double pack_swq; double TreeAdjustFactor[MAX_BANDS]; double dt_thresh; int tmp_lake_idx; cell_data_struct **cell; energy_bal_struct **energy; lake_var_struct *lake_var; snow_data_struct **snow; veg_var_struct **veg_var; cell = all_vars->cell; energy = all_vars->energy; lake_var = &all_vars->lake_var; snow = all_vars->snow; veg_var = all_vars->veg_var; // Initialize soil depths dp = soil_con->dp; FIRST_VEG = TRUE; // increase initial soil surface temperature if air is very cold Tair = surf_temp; if ( surf_temp < -1. ) surf_temp = -1.; /******************************************** Initialize all snow pack variables - some may be reset if state file present ********************************************/ initialize_snow(snow, Nveg, cellnum); /******************************************** Initialize all soil layer variables - some may be reset if state file present ********************************************/ initialize_soil(cell, soil_con, veg_con, Nveg); /******************************************** Initialize all vegetation variables - some may be reset if state file present ********************************************/ initialize_veg(veg_var, veg_con, global_param, Nveg); /******************************************** Initialize all lake variables ********************************************/ if ( options.LAKES ) { tmp_lake_idx = lake_con.lake_idx; if (tmp_lake_idx < 0) tmp_lake_idx = 0; ErrorFlag = initialize_lake(lake_var, lake_con, soil_con, &(cell[tmp_lake_idx][0]), surf_temp, 0); if (ErrorFlag == ERROR) return(ErrorFlag); } /******************************************** Initialize all spatial frost variables ********************************************/ for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) { if ( options.Nfrost == 1 ) soil_con->frost_fract[frost_area] = 1.; else if (options.Nfrost == 2 ) soil_con->frost_fract[frost_area] = 0.5; else { soil_con->frost_fract[frost_area] = 1. / (options.Nfrost - 1); if ( frost_area == 0 || frost_area == options.Nfrost-1 ) soil_con->frost_fract[frost_area] /= 2.; } } /******************************************************** Compute grid cell fractions for all subareas used in spatial distribution of soil frost routines. ********************************************************/ /************************************************************************ CASE 1: Not using quick ground heat flux, and initial conditions files provided ************************************************************************/ if(options.INIT_STATE) { read_initial_model_state(filep.init_state, all_vars, global_param, Nveg, options.SNOW_BAND, cellnum, soil_con, lake_con); /******Check that soil moisture does not exceed maximum allowed************/ for ( veg = 0 ; veg <= Nveg ; veg++ ) { for( band = 0; band < options.SNOW_BAND; band++ ) { for( lidx = 0; lidx < options.Nlayer; lidx++ ) { if ( cell[veg][band].layer[lidx].moist > soil_con->max_moist[lidx] ) { fprintf( stderr, "WARNING: Initial soil moisture (%f mm) exceeds maximum (%f mm) in layer %d for veg tile %d and snow band%d. Resetting to maximum.\n", cell[veg][band].layer[lidx].moist, soil_con->max_moist[lidx], lidx, veg, band ); for ( frost_area = 0; frost_area < options.Nfrost; frost_area++) cell[veg][band].layer[lidx].ice[frost_area] *= soil_con->max_moist[lidx]/cell[veg][band].layer[lidx].moist; cell[veg][band].layer[lidx].moist = soil_con->max_moist[lidx]; } for ( frost_area = 0; frost_area < options.Nfrost; frost_area++) { if (cell[veg][band].layer[lidx].ice[frost_area] > cell[veg][band].layer[lidx].moist) cell[veg][band].layer[lidx].ice[frost_area] = cell[veg][band].layer[lidx].moist; } tmp_moist[lidx] = cell[veg][band].layer[lidx].moist; } compute_runoff_and_asat(soil_con, tmp_moist, 0, &(cell[veg][band].asat), &tmp_runoff); } // Override possible bad values of soil moisture under lake coming from state file // (ideally we wouldn't store these in the state file in the first place) if (options.LAKES && veg == lake_con.lake_idx) { for( lidx = 0; lidx < options.Nlayer; lidx++ ) { lake_var->soil.layer[lidx].moist = soil_con->max_moist[lidx]; for ( frost_area = 0; frost_area < options.Nfrost; frost_area++) { if (lake_var->soil.layer[lidx].ice[frost_area] > lake_var->soil.layer[lidx].moist) lake_var->soil.layer[lidx].ice[frost_area] = lake_var->soil.layer[lidx].moist; } } } } /****** initialize moist and ice ************/ 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++ ) { for( lidx = 0; lidx < options.Nlayer; lidx++ ) { moist[veg][band][lidx] = cell[veg][band].layer[lidx].moist; for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) ice[veg][band][lidx][frost_area] = cell[veg][band].layer[lidx].ice[frost_area]; } } } } /******Check that snow pack terms are self-consistent************/ for ( veg = 0 ; veg <= Nveg ; veg++ ) { for ( band = 0 ; band < options.SNOW_BAND ; band++ ) { if (snow[veg][band].swq > MAX_SURFACE_SWE) { pack_swq = snow[veg][band].swq-MAX_SURFACE_SWE; surf_swq = MAX_SURFACE_SWE; } else { pack_swq = 0; surf_swq = snow[veg][band].swq; snow[veg][band].pack_temp = 0; } if (snow[veg][band].surf_water > LIQUID_WATER_CAPACITY*surf_swq) { snow[veg][band].pack_water += snow[veg][band].surf_water - (LIQUID_WATER_CAPACITY*surf_swq); snow[veg][band].surf_water = LIQUID_WATER_CAPACITY*surf_swq; } if (snow[veg][band].pack_water > LIQUID_WATER_CAPACITY*pack_swq) { snow[veg][band].pack_water = LIQUID_WATER_CAPACITY*pack_swq; } } } } /************************************************************************ CASE 2: Initialize soil if using quick heat flux, and no initial soil properties file given ************************************************************************/ else if(options.QUICK_FLUX) { Nnodes = options.Nnode; /* Initialize soil node thicknesses */ soil_con->dz_node[0] = soil_con->depth[0]; soil_con->dz_node[1] = soil_con->depth[0]; soil_con->dz_node[2] = 2. * (dp - 1.5 * soil_con->depth[0]); soil_con->Zsum_node[0] = 0; soil_con->Zsum_node[1] = soil_con->depth[0]; soil_con->Zsum_node[2] = dp; 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 node temperatures */ energy[veg][band].T[0] = surf_temp; energy[veg][band].T[1] = surf_temp; energy[veg][band].T[2] = soil_con->avg_temp; /* Initialize soil layer moisture and ice contents */ for ( lidx = 0; lidx < options.Nlayer; lidx++ ) { moist[veg][band][lidx] = cell[veg][band].layer[lidx].moist; for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) ice[veg][band][lidx][frost_area] = 0.; } } } } } /***************************************************************** CASE 3: Initialize Energy Balance Variables if not using quick ground heat flux, and no Initial Condition File Given *****************************************************************/ else if(!options.QUICK_FLUX) { 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++ ) { if(!options.EXP_TRANS){ /* Initialize soil node temperatures and thicknesses Nodes set at surface, the depth of the first layer, twice the depth of the first layer, and at the damping depth. Extra nodes are placed equal distance between the damping depth and twice the depth of the first layer. */ soil_con->dz_node[0] = soil_con->depth[0]; soil_con->dz_node[1] = soil_con->depth[0]; soil_con->dz_node[2] = soil_con->depth[0]; soil_con->Zsum_node[0] = 0; soil_con->Zsum_node[1] = soil_con[0].depth[0]; Zsum = 2. * soil_con[0].depth[0]; soil_con->Zsum_node[2] = Zsum; tmpdp = dp - soil_con[0].depth[0] * 2.5; tmpadj = 3.5; for ( index = 3; index < Nnodes-1; index++ ) { if ( FIRST_VEG ) { soil_con->dz_node[index] = tmpdp/(((double)Nnodes-tmpadj)); } Zsum += (soil_con->dz_node[index] +soil_con->dz_node[index-1])/2.; soil_con->Zsum_node[index] = Zsum; } energy[veg][band].T[0] = surf_temp; for ( index = 1; index < Nnodes; index++ ) { energy[veg][band].T[index] = soil_con->avg_temp; } if ( FIRST_VEG ) { FIRST_VEG = FALSE; soil_con->dz_node[Nnodes-1] = (dp - Zsum - soil_con->dz_node[Nnodes-2] / 2. ) * 2.; Zsum += (soil_con->dz_node[Nnodes-2] +soil_con->dz_node[Nnodes-1])/2.; soil_con->Zsum_node[Nnodes-1] = Zsum; if((int)(Zsum*1000+0.5) != (int)(dp*1000+0.5)) { sprintf(ErrStr,"Sum of thermal node thicknesses (%f) in initialize_model_state do not equal dp (%f), check initialization procedure",Zsum,dp); nrerror(ErrStr); } } } else{ /* exponential grid transformation, EXP_TRANS = TRUE*/ /*calculate exponential function parameter */ if ( FIRST_VEG ) { Bexp = logf(dp+1.)/(double)(Nnodes-1); //to force Zsum=dp at bottom node /* validate Nnodes by requiring that there be at least 3 nodes in the top 50cm */ if (Nnodes < 5*logf(dp+1.)+1) { sprintf(ErrStr,"The number of soil thermal nodes (%d) is too small for the supplied damping depth (%f) with EXP_TRANS set to TRUE, leading to fewer than 3 nodes in the top 50 cm of the soil column. For EXP_TRANS=TRUE, Nnodes and dp must follow the relationship:\n5*ln(dp+1)<Nnodes-1\nEither set Nnodes to at least %d in the global param file or reduce damping depth to %f in the soil parameter file. Or set EXP_TRANS to FALSE in the global parameter file.",Nnodes,dp,(int)(5*logf(dp+1.))+2,exp(0.2*(Nnodes-1))+1); nrerror(ErrStr); } for ( index = 0; index <= Nnodes-1; index++ ) soil_con->Zsum_node[index] = expf(Bexp*index)-1.; if(soil_con->Zsum_node[0] > soil_con->depth[0]) { sprintf(ErrStr,"Depth of first thermal node (%f) in initialize_model_state is greater than depth of first soil layer (%f); increase the number of nodes or decrease the thermal damping depth dp (%f)",soil_con->Zsum_node[0],soil_con->depth[0],dp); nrerror(ErrStr); } } //top node index=0; if ( FIRST_VEG ) soil_con->dz_node[index] = soil_con->Zsum_node[index+1]-soil_con->Zsum_node[index]; energy[veg][band].T[index] = surf_temp; //middle nodes for ( index = 1; index < Nnodes-1; index++ ) { if ( FIRST_VEG ) { soil_con->dz_node[index] = (soil_con->Zsum_node[index+1]-soil_con->Zsum_node[index])/2.+(soil_con->Zsum_node[index]-soil_con->Zsum_node[index-1])/2.; } // energy[veg][band].T[index] = exp_interp(soil_con->Zsum_node[index],0.,soil_con[0].dp, // surf_temp,soil_con[0].avg_temp); energy[veg][band].T[index] = soil_con->avg_temp; } //bottom node index=Nnodes-1; if ( FIRST_VEG ) soil_con->dz_node[index] = soil_con->Zsum_node[index]-soil_con->Zsum_node[index-1]; energy[veg][band].T[index] = soil_con->avg_temp; } // end if !EXP_TRANS //initialize moisture and ice for each soil layer for ( lidx = 0; lidx < options.Nlayer; lidx++ ) { moist[veg][band][lidx] = cell[veg][band].layer[lidx].moist; for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) ice[veg][band][lidx][frost_area] = 0.; } } } } } /********************************* CASE 4: Unknown option *********************************/ else { 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. ) { for ( index = 0; index < options.Nlayer; index++ ) { soil_con->dz_node[index] = 1.; } } } } } } /****************************************** Initialize 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->dz_node, 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, soil_con->quartz, Nnodes, options.Nlayer, soil_con->FS_ACTIVE); } /* set soil moisture properties for all soil thermal nodes */ 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, Nnodes, options.Nlayer, soil_con->FS_ACTIVE); if ( ErrorFlag == ERROR ) return ( ErrorFlag ); /* 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) { dt_thresh = 0.5*energy[veg][band].Cs_node[1]/energy[veg][band].kappa_node[1]*pow((soil_con->dz_node[1]),2)/3600; // in hours if (global_param->dt > dt_thresh) { sprintf(ErrStr,"ERROR: You are currently running FROZEN SOIL with an explicit method (IMPLICIT is set to FALSE). For the explicit method to be stable, time step %d hours 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 hours, 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); nrerror(ErrStr); } } /* initialize layer moistures and ice contents */ for ( lidx = 0; lidx < options.Nlayer; lidx++ ) { cell[veg][band].layer[lidx].moist = moist[veg][band][lidx]; for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) cell[veg][band].layer[lidx].ice[frost_area] = ice[veg][band][lidx][frost_area]; } if (options.QUICK_FLUX) { ErrorFlag = estimate_layer_ice_content_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, soil_con->max_moist, soil_con->expt, soil_con->bubble, soil_con->frost_fract, soil_con->frost_slope, soil_con->FS_ACTIVE); } else { ErrorFlag = estimate_layer_ice_content(cell[veg][band].layer, soil_con->Zsum_node, energy[veg][band].T, soil_con->max_moist_node, soil_con->expt_node, soil_con->bubble_node, soil_con->depth, soil_con->max_moist, soil_con->expt, soil_con->bubble, soil_con->frost_fract, soil_con->frost_slope, Nnodes, options.Nlayer, soil_con->FS_ACTIVE); } /* 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, Nnodes); } } } } // initialize miscellaneous energy balance terms for ( veg = 0 ; veg <= Nveg ; veg++) { for ( band = 0; band < options.SNOW_BAND; band++ ) { /* Set fluxes to 0 */ energy[veg][band].advected_sensible = 0.0; energy[veg][band].advection = 0.0; energy[veg][band].AtmosError = 0.0; energy[veg][band].AtmosLatent = 0.0; energy[veg][band].AtmosLatentSub = 0.0; energy[veg][band].AtmosSensible = 0.0; energy[veg][band].canopy_advection = 0.0; energy[veg][band].canopy_latent = 0.0; energy[veg][band].canopy_latent_sub = 0.0; energy[veg][band].canopy_refreeze = 0.0; energy[veg][band].canopy_sensible = 0.0; energy[veg][band].deltaCC = 0.0; energy[veg][band].deltaH = 0.0; energy[veg][band].error = 0.0; energy[veg][band].fusion = 0.0; energy[veg][band].grnd_flux = 0.0; energy[veg][band].latent = 0.0; energy[veg][band].latent_sub = 0.0; energy[veg][band].longwave = 0.0; energy[veg][band].LongOverIn = 0.0; energy[veg][band].LongUnderIn = 0.0; energy[veg][band].LongUnderOut = 0.0; energy[veg][band].melt_energy = 0.0; energy[veg][band].NetLongAtmos = 0.0; energy[veg][band].NetLongOver = 0.0; energy[veg][band].NetLongUnder = 0.0; energy[veg][band].NetShortAtmos = 0.0; energy[veg][band].NetShortGrnd = 0.0; energy[veg][band].NetShortOver = 0.0; energy[veg][band].NetShortUnder = 0.0; energy[veg][band].out_long_canopy = 0.0; energy[veg][band].out_long_surface = 0.0; energy[veg][band].refreeze_energy = 0.0; energy[veg][band].sensible = 0.0; energy[veg][band].shortwave = 0.0; energy[veg][band].ShortOverIn = 0.0; energy[veg][band].ShortUnderIn = 0.0; energy[veg][band].snow_flux = 0.0; /* Initial estimate of LongUnderOut for use by snow_intercept() */ tmp = energy[veg][band].T[0] + KELVIN; energy[veg][band].LongUnderOut = STEFAN_B * tmp * tmp * tmp * tmp; energy[veg][band].Tfoliage = Tair + soil_con->Tfactor[band]; } } // initialize Tfallback counters for ( veg = 0 ; veg <= Nveg ; veg++) { for ( band = 0; band < options.SNOW_BAND; band++ ) { energy[veg][band].Tfoliage_fbcount = 0; energy[veg][band].Tcanopy_fbcount = 0; energy[veg][band].Tsurf_fbcount = 0; for ( index = 0; index < Nnodes-1; index++ ) { energy[veg][band].T_fbcount[index] = 0; } } } // Compute treeline adjustment factors for ( band = 0; band < options.SNOW_BAND; band++ ) { if ( soil_con->AboveTreeLine[band] ) { Cv = 0; for ( veg = 0 ; veg < veg_con[0].vegetat_type_num ; veg++ ) { if ( veg_lib[veg_con[veg].veg_class].overstory ) Cv += veg_con[veg].Cv; } TreeAdjustFactor[band] = 1. / ( 1. - Cv ); } else TreeAdjustFactor[band] = 1.; } // Initialize crop structures for ( veg = 0 ; veg < veg_con[0].vegetat_type_num ; veg++ ) { if (veg_con[veg].crop_frac_active) { for ( band = 0; band < options.SNOW_BAND; band++ ) { for (idx = veg_con[veg].crop_frac_idx; idx<veg_con[veg].crop_frac_idx+2; idx++) { // Copy veg_var state data if (idx % 2 == 0) all_vars_crop->veg_var[idx][band].Wdew = 0; else all_vars_crop->veg_var[idx][band].Wdew = veg_var[veg][band].Wdew; if (options.CARBON) { for (cidx=0; cidx<options.Ncanopy; cidx++) { all_vars_crop->veg_var[idx][band].NscaleFactor[cidx] = veg_var[veg][band].NscaleFactor[cidx]; all_vars_crop->veg_var[idx][band].aPARLayer[cidx] = veg_var[veg][band].aPARLayer[cidx]; all_vars_crop->veg_var[idx][band].CiLayer[cidx] = veg_var[veg][band].CiLayer[cidx]; all_vars_crop->veg_var[idx][band].rsLayer[cidx] = veg_var[veg][band].rsLayer[cidx]; } } all_vars_crop->veg_var[idx][band].Ci = veg_var[veg][band].Ci; all_vars_crop->veg_var[idx][band].rc = veg_var[veg][band].rc; all_vars_crop->veg_var[idx][band].NPPfactor = veg_var[veg][band].NPPfactor; all_vars_crop->veg_var[idx][band].AnnualNPP = veg_var[veg][band].AnnualNPP; all_vars_crop->veg_var[idx][band].AnnualNPPPrev = veg_var[veg][band].AnnualNPPPrev; // Copy veg_var flux data all_vars_crop->veg_var[idx][band].canopyevap = veg_var[veg][band].canopyevap; all_vars_crop->veg_var[idx][band].throughfall = veg_var[veg][band].throughfall; all_vars_crop->veg_var[idx][band].aPAR = veg_var[veg][band].aPAR; all_vars_crop->veg_var[idx][band].GPP = veg_var[veg][band].GPP; all_vars_crop->veg_var[idx][band].Rphoto = veg_var[veg][band].Rphoto; all_vars_crop->veg_var[idx][band].Rdark = veg_var[veg][band].Rdark; all_vars_crop->veg_var[idx][band].Rmaint = veg_var[veg][band].Rmaint; all_vars_crop->veg_var[idx][band].Rgrowth = veg_var[veg][band].Rgrowth; all_vars_crop->veg_var[idx][band].Raut = veg_var[veg][band].Raut; all_vars_crop->veg_var[idx][band].NPP = veg_var[veg][band].NPP; all_vars_crop->veg_var[idx][band].Litterfall = veg_var[veg][band].Litterfall; // Copy cell state data for ( lidx = 0; lidx < 2; lidx++ ) { all_vars_crop->cell[idx][band].aero_resist[lidx] = cell[veg][band].aero_resist[lidx]; } all_vars_crop->cell[idx][band].asat = cell[veg][band].asat; all_vars_crop->cell[idx][band].CLitter = cell[veg][band].CLitter; all_vars_crop->cell[idx][band].CInter = cell[veg][band].CInter; all_vars_crop->cell[idx][band].CSlow = cell[veg][band].CSlow; for ( lidx = 0; lidx < options.Nlayer; lidx++ ) { all_vars_crop->cell[idx][band].layer[lidx].bare_evap_frac = cell[veg][band].layer[lidx].bare_evap_frac; all_vars_crop->cell[idx][band].layer[lidx].Cs = cell[veg][band].layer[lidx].Cs; all_vars_crop->cell[idx][band].layer[lidx].kappa = cell[veg][band].layer[lidx].kappa; all_vars_crop->cell[idx][band].layer[lidx].moist = cell[veg][band].layer[lidx].moist; for ( frost_area = 0; frost_area < options.Nfrost; frost_area++ ) all_vars_crop->cell[idx][band].layer[lidx].ice[frost_area] = cell[veg][band].layer[lidx].ice[frost_area]; all_vars_crop->cell[idx][band].layer[lidx].phi = cell[veg][band].layer[lidx].phi; all_vars_crop->cell[idx][band].layer[lidx].T = cell[veg][band].layer[lidx].T; all_vars_crop->cell[idx][band].layer[lidx].zwt = cell[veg][band].layer[lidx].zwt; } // Copy cell flux data all_vars_crop->cell[idx][band].baseflow = cell[veg][band].baseflow; for ( lidx = 0; lidx < options.Nlayer; lidx++ ) { all_vars_crop->cell[idx][band].layer[lidx].evap = cell[veg][band].layer[lidx].evap; } all_vars_crop->cell[idx][band].inflow = cell[veg][band].inflow; for ( lidx = 0; lidx < N_PET_TYPES; lidx++ ) { all_vars_crop->cell[idx][band].pot_evap[lidx] = cell[veg][band].pot_evap[lidx]; } all_vars_crop->cell[idx][band].runoff = cell[veg][band].runoff; all_vars_crop->cell[idx][band].RhLitter = cell[veg][band].RhLitter; all_vars_crop->cell[idx][band].RhLitter2Atm = cell[veg][band].RhLitter2Atm; all_vars_crop->cell[idx][band].RhInter = cell[veg][band].RhInter; all_vars_crop->cell[idx][band].RhSlow = cell[veg][band].RhSlow; all_vars_crop->cell[idx][band].RhTot = cell[veg][band].RhTot; all_vars_crop->cell[idx][band].rootmoist = cell[veg][band].rootmoist; all_vars_crop->cell[idx][band].wetness = cell[veg][band].wetness; all_vars_crop->cell[idx][band].zwt = cell[veg][band].zwt; all_vars_crop->cell[idx][band].zwt_lumped = cell[veg][band].zwt_lumped; // Copy snow state data all_vars_crop->snow[idx][band].albedo = snow[veg][band].albedo; all_vars_crop->snow[idx][band].canopy_albedo = snow[veg][band].canopy_albedo; all_vars_crop->snow[idx][band].coldcontent = snow[veg][band].coldcontent; all_vars_crop->snow[idx][band].coverage = snow[veg][band].coverage; all_vars_crop->snow[idx][band].density = snow[veg][band].density; all_vars_crop->snow[idx][band].depth = snow[veg][band].depth; all_vars_crop->snow[idx][band].last_snow = snow[veg][band].last_snow; all_vars_crop->snow[idx][band].max_snow_depth = snow[veg][band].max_snow_depth; all_vars_crop->snow[idx][band].MELTING = snow[veg][band].MELTING; all_vars_crop->snow[idx][band].pack_temp = snow[veg][band].pack_temp; all_vars_crop->snow[idx][band].pack_water = snow[veg][band].pack_water; all_vars_crop->snow[idx][band].snow = snow[veg][band].snow; if (idx % 2 == 0) all_vars_crop->snow[idx][band].snow_canopy = 0; else all_vars_crop->snow[idx][band].snow_canopy = snow[veg][band].snow_canopy; all_vars_crop->snow[idx][band].store_coverage = snow[veg][band].store_coverage; all_vars_crop->snow[idx][band].store_snow = snow[veg][band].store_snow; all_vars_crop->snow[idx][band].store_swq = snow[veg][band].store_swq; all_vars_crop->snow[idx][band].surf_temp = snow[veg][band].surf_temp; all_vars_crop->snow[idx][band].surf_temp_fbcount = snow[veg][band].surf_temp_fbcount; all_vars_crop->snow[idx][band].surf_temp_fbflag = snow[veg][band].surf_temp_fbflag; all_vars_crop->snow[idx][band].surf_water = snow[veg][band].surf_water; all_vars_crop->snow[idx][band].swq = snow[veg][band].swq; all_vars_crop->snow[idx][band].snow_distrib_slope = snow[veg][band].snow_distrib_slope; all_vars_crop->snow[idx][band].tmp_int_storage = snow[veg][band].tmp_int_storage; // Copy snow flux data all_vars_crop->snow[idx][band].blowing_flux = snow[veg][band].blowing_flux; all_vars_crop->snow[idx][band].canopy_vapor_flux = snow[veg][band].canopy_vapor_flux; all_vars_crop->snow[idx][band].mass_error = snow[veg][band].mass_error; all_vars_crop->snow[idx][band].melt = snow[veg][band].melt; all_vars_crop->snow[idx][band].Qnet = snow[veg][band].Qnet; all_vars_crop->snow[idx][band].surface_flux = snow[veg][band].surface_flux; all_vars_crop->snow[idx][band].transport = snow[veg][band].transport; all_vars_crop->snow[idx][band].vapor_flux = snow[veg][band].albedo; // Copy energy state data all_vars_crop->energy[idx][band].AlbedoLake = energy[veg][band].AlbedoLake; all_vars_crop->energy[idx][band].AlbedoOver = energy[veg][band].AlbedoOver; all_vars_crop->energy[idx][band].AlbedoUnder = energy[veg][band].AlbedoUnder; all_vars_crop->energy[idx][band].frozen = energy[veg][band].frozen; all_vars_crop->energy[idx][band].Nfrost = energy[veg][band].Nfrost; all_vars_crop->energy[idx][band].Nthaw = energy[veg][band].Nthaw; all_vars_crop->energy[idx][band].T1_index = energy[veg][band].T1_index; all_vars_crop->energy[idx][band].Tcanopy = energy[veg][band].Tcanopy; all_vars_crop->energy[idx][band].Tcanopy_fbcount = energy[veg][band].Tcanopy_fbcount; all_vars_crop->energy[idx][band].Tcanopy_fbflag = energy[veg][band].Tcanopy_fbflag; all_vars_crop->energy[idx][band].Tfoliage = energy[veg][band].Tfoliage; all_vars_crop->energy[idx][band].Tfoliage_fbcount = energy[veg][band].Tfoliage_fbcount; all_vars_crop->energy[idx][band].Tfoliage_fbflag = energy[veg][band].Tfoliage_fbflag; all_vars_crop->energy[idx][band].Tsurf = energy[veg][band].Tsurf; all_vars_crop->energy[idx][band].Tsurf_fbcount = energy[veg][band].Tsurf_fbcount; all_vars_crop->energy[idx][band].Tsurf_fbflag = energy[veg][band].Tsurf_fbflag; all_vars_crop->energy[idx][band].unfrozen = energy[veg][band].unfrozen; for (lidx=0; lidx<2; lidx++) { all_vars_crop->energy[idx][band].Cs[lidx] = energy[veg][band].Cs[lidx]; all_vars_crop->energy[idx][band].kappa[lidx] = energy[veg][band].kappa[lidx]; } for (index=0; index<Nnodes; index++) { all_vars_crop->energy[idx][band].Cs_node[index] = energy[veg][band].Cs_node[index]; all_vars_crop->energy[idx][band].fdepth[index] = energy[veg][band].fdepth[index]; all_vars_crop->energy[idx][band].ice[index] = energy[veg][band].ice[index]; all_vars_crop->energy[idx][band].kappa_node[index] = energy[veg][band].kappa_node[index]; all_vars_crop->energy[idx][band].moist[index] = energy[veg][band].moist[index]; all_vars_crop->energy[idx][band].T[index] = energy[veg][band].T[index]; all_vars_crop->energy[idx][band].T_fbcount[index] = energy[veg][band].T_fbcount[index]; all_vars_crop->energy[idx][band].T_fbflag[index] = energy[veg][band].T_fbflag[index]; all_vars_crop->energy[idx][band].tdepth[index] = energy[veg][band].tdepth[index]; } // Copy energy flux data all_vars_crop->energy[idx][band].advected_sensible = energy[veg][band].advected_sensible; all_vars_crop->energy[idx][band].advection = energy[veg][band].advection; all_vars_crop->energy[idx][band].AtmosError = energy[veg][band].AtmosError; all_vars_crop->energy[idx][band].AtmosLatent = energy[veg][band].AtmosLatent; all_vars_crop->energy[idx][band].AtmosLatentSub = energy[veg][band].AtmosLatentSub; all_vars_crop->energy[idx][band].AtmosSensible = energy[veg][band].AtmosSensible; all_vars_crop->energy[idx][band].canopy_advection = energy[veg][band].canopy_advection; all_vars_crop->energy[idx][band].canopy_latent = energy[veg][band].canopy_latent; all_vars_crop->energy[idx][band].canopy_latent_sub = energy[veg][band].canopy_latent_sub; all_vars_crop->energy[idx][band].canopy_refreeze = energy[veg][band].canopy_refreeze; all_vars_crop->energy[idx][band].canopy_sensible = energy[veg][band].canopy_sensible; all_vars_crop->energy[idx][band].deltaCC = energy[veg][band].deltaCC; all_vars_crop->energy[idx][band].deltaH = energy[veg][band].deltaH; all_vars_crop->energy[idx][band].error = energy[veg][band].error; all_vars_crop->energy[idx][band].fusion = energy[veg][band].fusion; all_vars_crop->energy[idx][band].grnd_flux = energy[veg][band].grnd_flux; all_vars_crop->energy[idx][band].latent = energy[veg][band].latent; all_vars_crop->energy[idx][band].latent_sub = energy[veg][band].latent_sub; all_vars_crop->energy[idx][band].longwave = energy[veg][band].longwave; all_vars_crop->energy[idx][band].LongOverIn = energy[veg][band].LongOverIn; all_vars_crop->energy[idx][band].LongUnderIn = energy[veg][band].LongUnderIn; all_vars_crop->energy[idx][band].LongUnderOut = energy[veg][band].LongUnderOut; all_vars_crop->energy[idx][band].melt_energy = energy[veg][band].melt_energy; all_vars_crop->energy[idx][band].NetLongAtmos = energy[veg][band].NetLongAtmos; all_vars_crop->energy[idx][band].NetLongOver = energy[veg][band].NetLongOver; all_vars_crop->energy[idx][band].NetLongUnder = energy[veg][band].NetLongUnder; all_vars_crop->energy[idx][band].NetShortAtmos = energy[veg][band].NetShortAtmos; all_vars_crop->energy[idx][band].NetShortGrnd = energy[veg][band].NetShortGrnd; all_vars_crop->energy[idx][band].NetShortOver = energy[veg][band].NetShortOver; all_vars_crop->energy[idx][band].NetShortUnder = energy[veg][band].NetShortUnder; all_vars_crop->energy[idx][band].out_long_canopy = energy[veg][band].out_long_canopy; all_vars_crop->energy[idx][band].out_long_surface = energy[veg][band].out_long_surface; all_vars_crop->energy[idx][band].refreeze_energy = energy[veg][band].refreeze_energy; all_vars_crop->energy[idx][band].sensible = energy[veg][band].sensible; all_vars_crop->energy[idx][band].shortwave = energy[veg][band].shortwave; all_vars_crop->energy[idx][band].ShortOverIn = energy[veg][band].ShortOverIn; all_vars_crop->energy[idx][band].ShortUnderIn = energy[veg][band].ShortUnderIn; all_vars_crop->energy[idx][band].snow_flux = energy[veg][band].snow_flux; } } } } return(0); }
int initialize_model_state(dist_prcp_struct *prcp, dmy_struct dmy, global_param_struct *global_param, filep_struct filep, int cellnum, int Nveg, int Nnodes, int Ndist, double surf_temp, soil_con_struct *soil_con, veg_con_struct *veg_con, lake_con_struct lake_con, char **init_STILL_STORM, int **init_DRY_TIME) /********************************************************************** initialize_model_state Keith Cherkauer April 17, 2000 This routine initializes the model state (energy balance, water balance, and snow components). If a state file is provided to the model than its contents are checked to see if it agrees with the current simulation set-up, if so it is used to initialize the model state. If no state file is provided the model initializes all variables with defaults and the user should expect to throw out the beginning of the simulation period as model start-up. UNITS: (m, s, kg, C, moisture in mm) unless otherwise specified Modifications: 4-17-00 Modified from initialize_energy_bal.c and initialize_snow.c to provide a single controlling routine for initializing the model state. 9-00 Fixed bug where initialization of soil node temperatures and moitures was within two vegetation loops, thus only the first vegetation type was properly initialized. KAC 2-19-03 Modified to initialize soil and vegetation parameters for the dry grid cell fraction, if distributed precipitation is activated. KAC 11-18-02 Modified to initialize lake and wetland algorithms variables. LCB 2-10-03 Fixed looping problem with initialization of soil moisture. KAC 3-12-03 Modified so that soil layer ice content is only calculated when frozen soil is implemented and active in the current grid cell. KAC 04-10-03 Modified to read storm parameters from model state file. KAC 04-25-03 Modified to work with vegetation type specific storm parameters. KAC 07-May-04 Initialize soil_con->dz_node[Nnodes] to 0.0, since it is accessed in set_node_parameters(). TJB 01-Nov-04 Added support for state files containing SPATIAL_FROST and LAKE_MODEL state variables. TJB 2006-Apr-21 Replaced Cv (uninitialized) with lake_con.Cl[0] in surfstor calculation. TJB 2006-Sep-23 Implemented flexible output configuration; uses the new save_data structure to track changes in moisture storage over each time step; this needs initialization here. TJB 2006-Oct-10 Added snow[veg][band].snow_canopy to save_data.swe. TJB 2006-Oct-16 Merged infiles and outfiles structs into filep_struct; This included removing the unused init_snow file. TJB 2006-Nov-07 Removed LAKE_MODEL option. TJB 2007-Apr-24 Added EXP_TRANS option. JCA 2007-Apr-24 Zsum_node loaded into soil_con structure for later use without having to recalculate. JCA 2007-Aug-09 Added features for EXCESS_ICE option. JCA 2007-Aug-21 Return value of ErrorFlag if error in distribute_node_moisture_properties. JCA 2007-Sep-18 Check for soil moist exceeding max moist moved from read_initial_model_state to here. JCA 2007-Oct-24 Modified initialize_lake() to return ErrorFlag. TJB 2008-Mar-01 Reinserted missing logic for QUICK_FS in calls to distribute_node_moisture_properties() and estimate_layer_ice_content(). TJB 2009-Feb-09 Removed dz_node from call to distribute_node_moisture_properties. KAC via TJB 2009-Feb-09 Removed dz_node from call to find_0_degree_front. KAC via TJB 2009-Mar-15 Modified to not call estimate_layer_ice_content() if not modeling frozen soil. KAC via TJB 2009-Mar-16 Added resid_moist to argument list of estimate_layer_ice_content(). This allows computation of min_liq, the minimum allowable liquid water content in each layer as a function of temperature. TJB 2009-Jun-09 Modified to use extension of veg_lib structure to contain bare soil information. TJB 2009-Jul-26 Added initial estimate of incoming longwave at surface (LongUnderOut) for use in canopy snow T iteration. TJB 2009-Jul-31 Removed extra lake/wetland veg tile. TJB 2009-Sep-19 Added T fbcount to count TFALLBACK occurrences. TJB 2009-Sep-19 Made initialization of Tfoliage more accurate for snow bands. TJB 2009-Sep-28 Added initialization of energy structure. TJB 2009-Nov-15 Added check to ensure that depth of first thermal node is <= depth of first soil layer. TJB 2009-Dec-11 Removed initialization of save_data structure, since this is now performed by the initial call to put_data(). TJB 2009-Dec-11 Removed min_liq and options.MIN_LIQ. TJB 2010-Nov-11 Updated call to initialize_lake() to accommodate new skip_hydro flag. TJB 2011-Mar-01 Updated calls to initialize_soil() and initialize_lake() to accommodate new arguments. Added more detailed validation of soil moisture. TJB 2011-Mar-05 Added validation of initial soil moisture, ice, and snow variables to make sure they are self-consistent. TJB 2011-May-31 Removed options.GRND_FLUX. Now soil temperatures and ground flux are always computed. TJB 2011-Jun-03 Added options.ORGANIC_FRACT. Soil properties now take organic fraction into account. TJB 2011-Jul-05 Changed logic initializing soil temperatures so that type of initialization depends solely on options.QUICK_FLUX; options.Nnodes is no longer automatically reset here. TJB 2012-Jan-16 Removed LINK_DEBUG code BN 2012-Jan-28 Added stability check for case of (FROZEN_SOIL=TRUE && IMPLICIT=FALSE). TJB 2013-Jul-25 Fixed incorrect condition on lake initialization. TJB 2013-Jul-25 Moved computation of tmp_moist argument of compute_runoff_and_asat() so that it would always be initialized. TJB 2014-Jan-13 Added validation of Nnodes and dp for EXP_TRANS=TRUE. TJB 2014-Feb-09 Made non-spinup initial temperatures more consistent with annual average air temperature and bottom boundary temperature. TJB **********************************************************************/ { extern option_struct options; extern veg_lib_struct *veg_lib; #if QUICK_FS extern double temps[]; #endif char ErrStr[MAXSTRING]; char FIRST_VEG; int i, j, ii, veg, index, dist; int lidx; double tmp_moist[MAX_LAYERS]; double tmp_runoff; int dry; int band; #if SPATIAL_FROST int frost_area; #endif int ErrorFlag; double Cv; double Zsum, dp; double tmpdp, tmpadj, Bexp; double Tair; double tmp; double *M; double moist[MAX_VEG][MAX_BANDS][MAX_LAYERS]; #if SPATIAL_FROST double ice[MAX_VEG][MAX_BANDS][MAX_LAYERS][FROST_SUBAREAS]; #else double ice[MAX_VEG][MAX_BANDS][MAX_LAYERS]; #endif // SPATIAL_FROST #if QUICK_FS double Aufwc, Bufwc; #endif double Clake; double mu; double surf_swq; double pack_swq; double TreeAdjustFactor[MAX_BANDS]; #if EXCESS_ICE double sum_mindepth, sum_depth_pre, sum_depth_post, tmp_mindepth; #endif double dt_thresh; int tmp_lake_idx; cell_data_struct ***cell; energy_bal_struct **energy; lake_var_struct *lake_var; snow_data_struct **snow; veg_var_struct ***veg_var; cell = prcp->cell; energy = prcp->energy; lake_var = &prcp->lake_var; snow = prcp->snow; veg_var = prcp->veg_var; // Initialize soil depths dp = soil_con->dp; FIRST_VEG = TRUE; // increase initial soil surface temperature if air is very cold Tair = surf_temp; if ( surf_temp < -1. ) surf_temp = -1.; // initialize storm parameters to start a new simulation (*init_STILL_STORM) = (char *)malloc((Nveg+1)*sizeof(char)); (*init_DRY_TIME) = (int *)malloc((Nveg+1)*sizeof(int)); for ( veg = 0 ; veg <= Nveg ; veg++ ) (*init_DRY_TIME)[veg] = -999; /******************************************** Initialize all snow pack variables - some may be reset if state file present ********************************************/ initialize_snow(snow, Nveg, cellnum); /******************************************** Initialize all soil layer variables - some may be reset if state file present ********************************************/ initialize_soil(cell[WET], soil_con, veg_con, Nveg); if ( options.DIST_PRCP ) initialize_soil(cell[DRY], soil_con, veg_con, Nveg); /******************************************** Initialize all vegetation variables - some may be reset if state file present ********************************************/ initialize_veg(veg_var[WET], veg_con, global_param, Nveg); if ( options.DIST_PRCP ) initialize_veg(veg_var[DRY], veg_con, global_param, Nveg); /******************************************** Initialize all lake variables ********************************************/ if ( options.LAKES ) { tmp_lake_idx = lake_con.lake_idx; if (tmp_lake_idx < 0) tmp_lake_idx = 0; ErrorFlag = initialize_lake(lake_var, lake_con, soil_con, &(cell[WET][tmp_lake_idx][0]), surf_temp, 0); if (ErrorFlag == ERROR) return(ErrorFlag); } /******************************************** Initialize all spatial frost variables ********************************************/ #if SPATIAL_FROST for ( frost_area = 0; frost_area < FROST_SUBAREAS; frost_area++ ) { if ( FROST_SUBAREAS == 1 ) soil_con->frost_fract[frost_area] = 1.; else if (FROST_SUBAREAS == 2 ) soil_con->frost_fract[frost_area] = 0.5; else { soil_con->frost_fract[frost_area] = 1. / (FROST_SUBAREAS - 1); if ( frost_area == 0 || frost_area == FROST_SUBAREAS-1 ) soil_con->frost_fract[frost_area] /= 2.; } } #endif // SPATIAL_FROST /******************************************************** Compute grid cell fractions for all subareas used in spatial distribution of soil frost routines. ********************************************************/ #if QUICK_FS if(options.FROZEN_SOIL) { /*********************************************************** Prepare table of maximum unfrozen water content values - This linearizes the equation for maximum unfrozen water content, reducing computation time for the frozen soil model. ***********************************************************/ for(lidx=0;lidx<options.Nlayer;lidx++) { for(ii=0;ii<QUICK_FS_TEMPS;ii++) { Aufwc = maximum_unfrozen_water(temps[ii], 1.0, soil_con->bubble[lidx], soil_con->expt[lidx]); Bufwc = maximum_unfrozen_water(temps[ii+1], 1.0, soil_con->bubble[lidx], soil_con->expt[lidx]); soil_con->ufwc_table_layer[lidx][ii][0] = linear_interp(0., temps[ii], temps[ii+1], Aufwc, Bufwc); soil_con->ufwc_table_layer[lidx][ii][1] = (Bufwc - Aufwc) / (temps[ii+1] - temps[ii]); } } } #endif // QUICK_FS /************************************************************************ CASE 1: Not using quick ground heat flux, and initial conditions files provided ************************************************************************/ if(options.INIT_STATE) { #if EXCESS_ICE sum_mindepth = 0; sum_depth_pre = 0; for( lidx = 0; lidx < options.Nlayer; lidx++ ){ tmp_mindepth = (float)(int)(soil_con->min_depth[lidx] * 1000 + 0.5) / 1000; sum_mindepth += tmp_mindepth; sum_depth_pre += soil_con->depth[lidx]; } #endif read_initial_model_state(filep.init_state, prcp, global_param, Nveg, options.SNOW_BAND, cellnum, soil_con, Ndist, *init_STILL_STORM, *init_DRY_TIME, lake_con); #if EXCESS_ICE // calculate dynamic soil and veg properties if excess_ice is present sum_depth_post = 0; for( lidx = 0; lidx < options.Nlayer; lidx++ ) sum_depth_post += soil_con->depth[lidx]; if( sum_depth_post != sum_depth_pre) { /*update soil_con properties*/ for( lidx = 0; lidx < options.Nlayer; lidx++ ) { soil_con->bulk_dens_min[lidx] *= (1.0-soil_con->effective_porosity[lidx])*soil_con->soil_density[lidx]/soil_con->bulk_density[lidx]; if (soil_con->organic[layer] > 0) soil_con->bulk_dens_org[lidx] *= (1.0-soil_con->effective_porosity[lidx])*soil_con->soil_density[lidx]/soil_con->bulk_density[lidx]; soil_con->bulk_density[lidx] = (1.0-soil_con->effective_porosity[lidx])*soil_con->soil_density[lidx]; soil_con->max_moist[lidx] = soil_con->depth[lidx] * soil_con->effective_porosity[lidx] * 1000.; } //loop for each soil layer /********update remaining soil_con properties**********/ /* update Maximum Infiltration for Upper Layers */ if(options.Nlayer==2) soil_con->max_infil = (1.0+soil_con->b_infilt)*soil_con->max_moist[0]; else soil_con->max_infil = (1.0+soil_con->b_infilt)*(soil_con->max_moist[0]+soil_con->max_moist[1]); /* Soil Layer Critical and Wilting Point Moisture Contents */ for(lidx=0;lidx<options.Nlayer;lidx++) {//soil layer soil_con->Wcr[lidx] = soil_con->Wcr_FRACT[lidx] * soil_con->max_moist[lidx]; soil_con->Wpwp[lidx] = soil_con->Wpwp_FRACT[lidx] * soil_con->max_moist[lidx]; if(soil_con->Wpwp[lidx] > soil_con->Wcr[lidx]) { sprintf(ErrStr,"Updated wilting point moisture (%f mm) is greater than updated critical point moisture (%f mm) for layer %d.\n\tIn the soil parameter file, Wpwp_FRACT MUST be <= Wcr_FRACT.\n", soil_con->Wpwp[lidx], soil_con->Wcr[lidx], lidx); nrerror(ErrStr); } if(soil_con->Wpwp[lidx] < soil_con->resid_moist[lidx] * soil_con->depth[lidx] * 1000.) { sprintf(ErrStr,"Updated wilting point moisture (%f mm) is less than updated residual moisture (%f mm) for layer %d.\n\tIn the soil parameter file, Wpwp_FRACT MUST be >= resid_moist / (1.0 - bulk_density/soil_density).\n", soil_con->Wpwp[lidx], soil_con->resid_moist[lidx] * soil_con->depth[lidx] * 1000., lidx); nrerror(ErrStr); } } /* If BASEFLOW = NIJSSEN2001 then convert ARNO baseflow parameters d1, d2, d3, and d4 to Ds, Dsmax, Ws, and c */ if(options.BASEFLOW == NIJSSEN2001) { lidx = options.Nlayer-1; soil_con->Dsmax = soil_con->Dsmax_orig * pow((double)(1./(soil_con->max_moist[lidx]-soil_con->Ws_orig)), -soil_con->c) + soil_con->Ds_orig * soil_con->max_moist[lidx]; soil_con->Ds = soil_con->Ds_orig * soil_con->Ws_orig / soil_con->Dsmax_orig; soil_con->Ws = soil_con->Ws_orig/soil_con->max_moist[lidx]; } /*********** update root fractions ***************/ calc_root_fractions(veg_con, soil_con); #if VERBOSE /* write changes to screen */ fprintf(stderr,"Soil properties initialized from state file:\n"); for(lidx=0;lidx<options.Nlayer;lidx++) {//soil layer fprintf(stderr,"\tFor layer %d:\n",lidx+1); fprintf(stderr,"\t\tDepth of soil layer = %.2f m.\n",soil_con->depth[lidx]); fprintf(stderr,"\t\tEffective porosity = %.2f.\n",soil_con->effective_porosity[lidx]); fprintf(stderr,"\t\tBulk density = %.2f kg/m^3.\n",soil_con->bulk_density[lidx]); } fprintf(stderr,"\tDamping depth = %.2f m.\n",soil_con->dp); if(sum_depth_post == sum_mindepth) fprintf(stderr,"\tExcess ice is no longer present in the soil column.\n"); #endif //VERBOSE }//updated initial conditions due to state file #endif //EXCESS_ICE /******Check that soil moisture does not exceed maximum allowed************/ for ( dist = 0; dist < Ndist; dist ++ ) { for ( veg = 0 ; veg <= Nveg ; veg++ ) { for( band = 0; band < options.SNOW_BAND; band++ ) { for( lidx = 0; lidx < options.Nlayer; lidx++ ) { if ( cell[dist][veg][band].layer[lidx].moist > soil_con->max_moist[lidx] ) { fprintf( stderr, "WARNING: Initial soil moisture (%f mm) exceeds maximum (%f mm) in layer %d for veg tile %d and snow band%d. Resetting to maximum.\n", cell[dist][veg][band].layer[lidx].moist, soil_con->max_moist[lidx], lidx, veg, band ); #if SPATIAL_FROST for ( frost_area = 0; frost_area < FROST_SUBAREAS; frost_area++) cell[dist][veg][band].layer[lidx].ice[frost_area] *= soil_con->max_moist[lidx]/cell[dist][veg][band].layer[lidx].moist; #else cell[dist][veg][band].layer[lidx].ice *= soil_con->max_moist[lidx]/cell[dist][veg][band].layer[lidx].moist; #endif cell[dist][veg][band].layer[lidx].moist = soil_con->max_moist[lidx]; } #if SPATIAL_FROST for ( frost_area = 0; frost_area < FROST_SUBAREAS; frost_area++) { if (cell[dist][veg][band].layer[lidx].ice[frost_area] > cell[dist][veg][band].layer[lidx].moist) cell[dist][veg][band].layer[lidx].ice[frost_area] = cell[dist][veg][band].layer[lidx].moist; } #else if (cell[dist][veg][band].layer[lidx].ice > cell[dist][veg][band].layer[lidx].moist) cell[dist][veg][band].layer[lidx].ice = cell[dist][veg][band].layer[lidx].moist; #endif tmp_moist[lidx] = cell[dist][veg][band].layer[lidx].moist; } compute_runoff_and_asat(soil_con, tmp_moist, 0, &(cell[dist][veg][band].asat), &tmp_runoff); } // Override possible bad values of soil moisture under lake coming from state file // (ideally we wouldn't store these in the state file in the first place) if (options.LAKES && veg == lake_con.lake_idx) { for( lidx = 0; lidx < options.Nlayer; lidx++ ) { lake_var->soil.layer[lidx].moist = soil_con->max_moist[lidx]; #if SPATIAL_FROST for ( frost_area = 0; frost_area < FROST_SUBAREAS; frost_area++) { if (lake_var->soil.layer[lidx].ice[frost_area] > lake_var->soil.layer[lidx].moist) lake_var->soil.layer[lidx].ice[frost_area] = lake_var->soil.layer[lidx].moist; } #else if (lake_var->soil.layer[lidx].ice > lake_var->soil.layer[lidx].moist) lake_var->soil.layer[lidx].ice = lake_var->soil.layer[lidx].moist; #endif } } } } /****** initialize moist and ice ************/ 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++ ) { for( lidx = 0; lidx < options.Nlayer; lidx++ ) { moist[veg][band][lidx] = cell[0][veg][band].layer[lidx].moist; #if SPATIAL_FROST for ( frost_area = 0; frost_area < FROST_SUBAREAS; frost_area++ ) ice[veg][band][lidx][frost_area] = cell[0][veg][band].layer[lidx].ice[frost_area]; #else ice[veg][band][lidx] = cell[0][veg][band].layer[lidx].ice; #endif } } } } /******Check that snow pack terms are self-consistent************/ for ( veg = 0 ; veg <= Nveg ; veg++ ) { for ( band = 0 ; band < options.SNOW_BAND ; band++ ) { if (snow[veg][band].swq > MAX_SURFACE_SWE) { pack_swq = snow[veg][band].swq-MAX_SURFACE_SWE; surf_swq = MAX_SURFACE_SWE; } else { pack_swq = 0; surf_swq = snow[veg][band].swq; snow[veg][band].pack_temp = 0; } if (snow[veg][band].surf_water > LIQUID_WATER_CAPACITY*surf_swq) { snow[veg][band].pack_water += snow[veg][band].surf_water - (LIQUID_WATER_CAPACITY*surf_swq); snow[veg][band].surf_water = LIQUID_WATER_CAPACITY*surf_swq; } if (snow[veg][band].pack_water > LIQUID_WATER_CAPACITY*pack_swq) { snow[veg][band].pack_water = LIQUID_WATER_CAPACITY*pack_swq; } } } } /************************************************************************ CASE 2: Initialize soil if using quick heat flux, and no initial soil properties file given ************************************************************************/ else if(options.QUICK_FLUX) { Nnodes = options.Nnode; /* Initialize soil node thicknesses */ soil_con->dz_node[0] = soil_con->depth[0]; soil_con->dz_node[1] = soil_con->depth[0]; soil_con->dz_node[2] = 2. * (dp - 1.5 * soil_con->depth[0]); soil_con->Zsum_node[0] = 0; soil_con->Zsum_node[1] = soil_con->depth[0]; soil_con->Zsum_node[2] = dp; 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 node temperatures */ energy[veg][band].T[0] = surf_temp; energy[veg][band].T[1] = surf_temp; energy[veg][band].T[2] = soil_con->avg_temp; /* Initialize soil layer moisture and ice contents */ for ( lidx = 0; lidx < options.Nlayer; lidx++ ) { moist[veg][band][lidx] = cell[0][veg][band].layer[lidx].moist; #if SPATIAL_FROST for ( frost_area = 0; frost_area < FROST_SUBAREAS; frost_area++ ) ice[veg][band][lidx][frost_area] = 0.; #else ice[veg][band][lidx] = 0.; #endif } } } } } /***************************************************************** CASE 3: Initialize Energy Balance Variables if not using quick ground heat flux, and no Initial Condition File Given *****************************************************************/ else if(!options.QUICK_FLUX) { 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++ ) { if(!options.EXP_TRANS){ /* Initialize soil node temperatures and thicknesses Nodes set at surface, the depth of the first layer, twice the depth of the first layer, and at the damping depth. Extra nodes are placed equal distance between the damping depth and twice the depth of the first layer. */ soil_con->dz_node[0] = soil_con->depth[0]; soil_con->dz_node[1] = soil_con->depth[0]; soil_con->dz_node[2] = soil_con->depth[0]; soil_con->Zsum_node[0] = 0; soil_con->Zsum_node[1] = soil_con[0].depth[0]; Zsum = 2. * soil_con[0].depth[0]; soil_con->Zsum_node[2] = Zsum; tmpdp = dp - soil_con[0].depth[0] * 2.5; tmpadj = 3.5; for ( index = 3; index < Nnodes-1; index++ ) { if ( FIRST_VEG ) { soil_con->dz_node[index] = tmpdp/(((double)Nnodes-tmpadj)); } Zsum += (soil_con->dz_node[index] +soil_con->dz_node[index-1])/2.; soil_con->Zsum_node[index] = Zsum; } energy[veg][band].T[0] = surf_temp; for ( index = 1; index < Nnodes; index++ ) { energy[veg][band].T[index] = soil_con->avg_temp; } if ( FIRST_VEG ) { FIRST_VEG = FALSE; soil_con->dz_node[Nnodes-1] = (dp - Zsum - soil_con->dz_node[Nnodes-2] / 2. ) * 2.; Zsum += (soil_con->dz_node[Nnodes-2] +soil_con->dz_node[Nnodes-1])/2.; soil_con->Zsum_node[Nnodes-1] = Zsum; if((int)(Zsum*1000+0.5) != (int)(dp*1000+0.5)) { sprintf(ErrStr,"Sum of thermal node thicknesses (%f) in initialize_model_state do not equal dp (%f), check initialization procedure",Zsum,dp); nrerror(ErrStr); } } } else{ /* exponential grid transformation, EXP_TRANS = TRUE*/ if ( FIRST_VEG ) { /*calculate exponential function parameter */ Bexp = logf(dp+1.)/(double)(Nnodes-1); //to force Zsum=dp at bottom node /* validate Nnodes by requiring that there be at least 3 nodes in the top 50cm */ if (Nnodes < 5*logf(dp+1.)+1) { sprintf(ErrStr,"The number of soil thermal nodes (%d) is too small for the supplied damping depth (%f) with EXP_TRANS set to TRUE, leading to fewer than 3 nodes in the top 50 cm of the soil column. For EXP_TRANS=TRUE, Nnodes and dp must follow the relationship:\n5*ln(dp+1)<Nnodes-1\nEither set Nnodes to at least %d in the global param file or reduce damping depth to %f in the soil parameter file. Or set EXP_TRANS to FALSE in the global parameter file.",Nnodes,dp,(int)(5*logf(dp+1.))+2,exp(0.2*(Nnodes-1))+1); nrerror(ErrStr); } for ( index = 0; index <= Nnodes-1; index++ ) soil_con->Zsum_node[index] = expf(Bexp*index)-1.; if(soil_con->Zsum_node[0] > soil_con->depth[0]) { sprintf(ErrStr,"Depth of first thermal node (%f) in initialize_model_state is greater than depth of first soil layer (%f); increase the number of nodes or decrease the thermal damping depth dp (%f)",soil_con->Zsum_node[0],soil_con->depth[0],dp); nrerror(ErrStr); } } //top node index=0; if ( FIRST_VEG ) soil_con->dz_node[index] = soil_con->Zsum_node[index+1]-soil_con->Zsum_node[index]; energy[veg][band].T[index] = surf_temp; //middle nodes for ( index = 1; index < Nnodes-1; index++ ) { if ( FIRST_VEG ) { soil_con->dz_node[index] = (soil_con->Zsum_node[index+1]-soil_con->Zsum_node[index])/2.+(soil_con->Zsum_node[index]-soil_con->Zsum_node[index-1])/2.; } // energy[veg][band].T[index] = exp_interp(soil_con->Zsum_node[index],0.,soil_con[0].dp, // surf_temp,soil_con[0].avg_temp); energy[veg][band].T[index] = soil_con->avg_temp; } //bottom node index=Nnodes-1; if ( FIRST_VEG ) soil_con->dz_node[index] = soil_con->Zsum_node[index]-soil_con->Zsum_node[index-1]; energy[veg][band].T[index] = soil_con->avg_temp; } // end if !EXP_TRANS //initialize moisture and ice for each soil layer for ( lidx = 0; lidx < options.Nlayer; lidx++ ) { moist[veg][band][lidx] = cell[0][veg][band].layer[lidx].moist; #if SPATIAL_FROST for ( frost_area = 0; frost_area < FROST_SUBAREAS; frost_area++ ) ice[veg][band][lidx][frost_area] = 0.; #else ice[veg][band][lidx] = 0.; #endif } } } } } /********************************* CASE 4: Unknown option *********************************/ else { 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. ) { for ( index = 0; index < options.Nlayer; index++ ) { soil_con->dz_node[index] = 1.; } } } } } } /******************************************** Initialize subsidence ********************************************/ #if EXCESS_ICE for ( lidx = 0; lidx < options.Nlayer; lidx++ ) soil_con->subsidence[lidx] = 0.0; #endif // EXCESS_ICE /****************************************** Initialize 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->dz_node, 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, soil_con->quartz, #if QUICK_FS soil_con->ufwc_table_node, #endif // QUICK_FS #if EXCESS_ICE soil_con->porosity, soil_con->effective_porosity, soil_con->porosity_node, soil_con->effective_porosity_node, #endif // EXCESS_ICE Nnodes, options.Nlayer, soil_con->FS_ACTIVE); } /* set soil moisture properties for all soil thermal nodes */ 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, #if QUICK_FS soil_con->ufwc_table_node, #else soil_con->expt_node, soil_con->bubble_node, #endif // QUICK_FS #if EXCESS_ICE soil_con->porosity_node, soil_con->effective_porosity_node, #endif // EXCESS_ICE 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, Nnodes, options.Nlayer, soil_con->FS_ACTIVE); if ( ErrorFlag == ERROR ) return ( ErrorFlag ); /* 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) { dt_thresh = 0.5*energy[veg][band].Cs_node[1]/energy[veg][band].kappa_node[1]*pow((soil_con->dz_node[1]),2)/3600; // in hours if (global_param->dt > dt_thresh) { sprintf(ErrStr,"ERROR: You are currently running FROZEN SOIL with an explicit method (IMPLICIT is set to FALSE). For the explicit method to be stable, time step %d hours 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 hours, 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); nrerror(ErrStr); } } /* initialize layer moistures and ice contents */ for ( dry = 0; dry < Ndist; dry++ ) { for ( lidx = 0; lidx < options.Nlayer; lidx++ ) { cell[dry][veg][band].layer[lidx].moist = moist[veg][band][lidx]; #if SPATIAL_FROST for ( frost_area = 0; frost_area < FROST_SUBAREAS; frost_area++ ) cell[dry][veg][band].layer[lidx].ice[frost_area] = ice[veg][band][lidx][frost_area]; #else cell[dry][veg][band].layer[lidx].ice = ice[veg][band][lidx]; #endif } if (options.QUICK_FLUX) { ErrorFlag = estimate_layer_ice_content_quick_flux(cell[dry][veg][band].layer, soil_con->depth, soil_con->dp, energy[veg][band].T[0], energy[veg][band].T[1], soil_con->avg_temp, soil_con->max_moist, #if QUICK_FS soil_con->ufwc_table_layer, #else soil_con->expt, soil_con->bubble, #endif // QUICK_FS #if SPATIAL_FROST soil_con->frost_fract, soil_con->frost_slope, #endif // SPATIAL_FROST #if EXCESS_ICE soil_con->porosity, soil_con->effective_porosity, #endif // EXCESS_ICE soil_con->FS_ACTIVE); } else { ErrorFlag = estimate_layer_ice_content(cell[dry][veg][band].layer, soil_con->Zsum_node, energy[veg][band].T, soil_con->max_moist_node, #if QUICK_FS soil_con->ufwc_table_node, #else soil_con->expt_node, soil_con->bubble_node, #endif // QUICK_FS soil_con->depth, soil_con->max_moist, #if QUICK_FS soil_con->ufwc_table_layer, #else soil_con->expt, soil_con->bubble, #endif // QUICK_FS #if SPATIAL_FROST soil_con->frost_fract, soil_con->frost_slope, #endif // SPATIAL_FROST #if EXCESS_ICE soil_con->porosity, soil_con->effective_porosity, #endif // EXCESS_ICE Nnodes, options.Nlayer, soil_con->FS_ACTIVE); } } /* 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, Nnodes); } } } } // initialize miscellaneous energy balance terms for ( veg = 0 ; veg <= Nveg ; veg++) { for ( band = 0; band < options.SNOW_BAND; band++ ) { /* Set fluxes to 0 */ energy[veg][band].advected_sensible = 0.0; energy[veg][band].advection = 0.0; energy[veg][band].AtmosError = 0.0; energy[veg][band].AtmosLatent = 0.0; energy[veg][band].AtmosLatentSub = 0.0; energy[veg][band].AtmosSensible = 0.0; energy[veg][band].canopy_advection = 0.0; energy[veg][band].canopy_latent = 0.0; energy[veg][band].canopy_latent_sub = 0.0; energy[veg][band].canopy_refreeze = 0.0; energy[veg][band].canopy_sensible = 0.0; energy[veg][band].deltaCC = 0.0; energy[veg][band].deltaH = 0.0; energy[veg][band].error = 0.0; energy[veg][band].fusion = 0.0; energy[veg][band].grnd_flux = 0.0; energy[veg][band].latent = 0.0; energy[veg][band].latent_sub = 0.0; energy[veg][band].longwave = 0.0; energy[veg][band].LongOverIn = 0.0; energy[veg][band].LongUnderIn = 0.0; energy[veg][band].LongUnderOut = 0.0; energy[veg][band].melt_energy = 0.0; energy[veg][band].NetLongAtmos = 0.0; energy[veg][band].NetLongOver = 0.0; energy[veg][band].NetLongUnder = 0.0; energy[veg][band].NetShortAtmos = 0.0; energy[veg][band].NetShortGrnd = 0.0; energy[veg][band].NetShortOver = 0.0; energy[veg][band].NetShortUnder = 0.0; energy[veg][band].out_long_canopy = 0.0; energy[veg][band].out_long_surface = 0.0; energy[veg][band].refreeze_energy = 0.0; energy[veg][band].sensible = 0.0; energy[veg][band].shortwave = 0.0; energy[veg][band].ShortOverIn = 0.0; energy[veg][band].ShortUnderIn = 0.0; energy[veg][band].snow_flux = 0.0; /* Initial estimate of LongUnderOut for use by snow_intercept() */ tmp = energy[veg][band].T[0] + KELVIN; energy[veg][band].LongUnderOut = STEFAN_B * tmp * tmp * tmp * tmp; energy[veg][band].Tfoliage = Tair + soil_con->Tfactor[band]; } } // initialize Tfallback counters for ( veg = 0 ; veg <= Nveg ; veg++) { for ( band = 0; band < options.SNOW_BAND; band++ ) { energy[veg][band].Tfoliage_fbcount = 0; energy[veg][band].Tcanopy_fbcount = 0; energy[veg][band].Tsurf_fbcount = 0; for ( index = 0; index < Nnodes-1; index++ ) { energy[veg][band].T_fbcount[index] = 0; } } } // Compute treeline adjustment factors for ( band = 0; band < options.SNOW_BAND; band++ ) { if ( soil_con->AboveTreeLine[band] ) { Cv = 0; for ( veg = 0 ; veg < veg_con[0].vegetat_type_num ; veg++ ) { if ( veg_lib[veg_con[veg].veg_class].overstory ) Cv += veg_con[veg].Cv; } TreeAdjustFactor[band] = 1. / ( 1. - Cv ); } else TreeAdjustFactor[band] = 1.; } return(0); }
/****************************************************************************** * @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); }