#include #include #include #include #include #define MAXIT 1000 static char vcid[] = "$Id: frozen_soil.c,v 5.4.2.17 2009/09/20 02:32:07 vicadmin Exp $"; int finish_frozen_soil_calcs(energy_bal_struct *energy, layer_data_struct *layer_wet, layer_data_struct *layer_dry, layer_data_struct *layer, soil_con_struct *soil_con, int Nnodes, int veg, double mu, double *T, double *kappa, double *Cs, double *moist) { /****************************************************************** finish_frozen_soil_calcs Keith Cherkauer July 27, 1998 This subroutine redistributes soil properties based on the thermal solutions found for the current time step. Modifications: 3-12-03 Modified so that soil layer ice content is only calculated if the frozen soil algorithm is implemented and active in the current grid cell. KAC 2007-Apr-24 Removed setup_frozen_soil function (was above this one). JCA 2007-Apr-24 Added functionality for EXP_TRANS option. JCA (including passing Zsum_node to find_0_degree_fronts) 2007-Apr-24 For IMPLICIT option, added Ming Pan's new functions (solve_T_profile_implicit and fda_heat_eqn). JCA 2007-Aug-09 Added features for EXCESS_ICE option. JCA 2009-Feb-09 Removed dz_node from call to find_0_degree_front. KAC via TJB 2009-Feb-09 Modified to handle error flags and pass Zsum_node instead of dz_node to esimate_layer_ice_content. 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-10 Fixed incorrect placement of checks on ErrorFlag. TJB 2009-Jul-31 Removed unused layer_node_fract array from call to estimate_layer_ice_content(). TJB ******************************************************************/ extern option_struct options; #if LINK_DEBUG extern debug_struct debug; #endif int i, ErrorFlag; find_0_degree_fronts(energy, soil_con->Zsum_node, T, Nnodes); /** Store Layer Temperature Values **/ for(i=0;iT[i] = T[i]; if(energy->Nfrost>0) energy->frozen = TRUE; else energy->frozen = FALSE; /** Redistribute Soil Properties for New Frozen Soil Layer Size **/ if(soil_con->FS_ACTIVE && options.FROZEN_SOIL) { ErrorFlag = estimate_layer_ice_content(layer_wet, soil_con->Zsum_node, energy->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 soil_con->bulk_density, soil_con->soil_density, soil_con->quartz, soil_con->resid_moist, Nnodes, options.Nlayer, soil_con->FS_ACTIVE); if ( ErrorFlag == ERROR ) return (ERROR); } if(options.DIST_PRCP && soil_con->FS_ACTIVE && options.FROZEN_SOIL) { ErrorFlag = estimate_layer_ice_content(layer_dry, soil_con->Zsum_node, energy->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 soil_con->bulk_density, soil_con->soil_density, soil_con->quartz, soil_con->resid_moist, Nnodes, options.Nlayer, soil_con->FS_ACTIVE); if ( ErrorFlag == ERROR ) return (ERROR); } #if LINK_DEBUG if(debug.PRT_BALANCE && debug.DEBUG) { printf("After Moisture Redistribution\n"); #if SPATIAL_FROST write_layer(layer, veg, options.Nlayer, soil_con->frost_fract, soil_con->depth); #else write_layer(layer, veg, options.Nlayer, soil_con->depth); #endif } #endif return (0); } int solve_T_profile(double *T, double *T0, char *Tfbflag, int *Tfbcount, double *Zsum, double *kappa, double *Cs, double *moist, double deltat, double *max_moist, double *bubble, double *expt, double *ice, double *alpha, double *beta, double *gamma, double Dp, double *depth, #if QUICK_FS double ***ufwc_table_node, #endif #if EXCESS_ICE double *porosity, double *effective_porosity, #endif int Nnodes, int *FIRST_SOLN, int FS_ACTIVE, int NOFLUX, int EXP_TRANS, int veg_class) { /********************************************************************** This subroutine was written to iteratively solve the soil temperature profile using a numerical difference equation. The solution equation is second order in space, and first order in time. Modifications: 2007-Apr-11 Changed type of Error from char to int. GCT 2007-Apr-24 Added option for EXP_TRANS. JCA (including passing in dz_node, Zsum, Dp, depth, EXP_TRANS, veg_class; and removing FIRST_TIME and fprime) 2007-Apr-24 Rearranged terms in finite-difference heat equation (equation 8 of Cherkauer et al. (1999)); Therefore the constants (A-E) are calculated in a new way. These constants are equal to the constants in each of the terms in equation 8 multiplied by alpha^2*deltat. This was done to make EXP_TRANS option easier to code. JCA 2007-Apr-24 Replaced second term of heat flux with alternate derivative approximation (a form found in most text books). JCA 2007-Aug-08 Added option for EXCESS_ICE. JCA 2007-Oct-08 Fixed error in EXP_TRANS formulation. JCA 2007-Oct-11 Fixed error in EXP_TRANS formulation. JCA 2009-Feb-09 Removed dz_node from call to solve_T_profile and solve_T_profile_implicit. KAC 2009-Jun-19 Added T fbflag to indicate whether TFALLBACK occurred. TJB 2009-Sep-19 Added T fbcount to count TFALLBACK occurrences. TJB **********************************************************************/ extern option_struct options; #if LINK_DEBUG extern debug_struct debug; #endif static double A[MAX_NODES]; static double B[MAX_NODES]; static double C[MAX_NODES]; static double D[MAX_NODES]; static double E[MAX_NODES]; double *aa, *bb, *cc, *dd, *ee, Bexp; int Error; int j; if(FIRST_SOLN[0]) { //fprintf(stderr,"*************EXPLICIT SOLUTION***********\n"); if(EXP_TRANS) Bexp = logf(Dp+1.)/(double)(Nnodes-1); FIRST_SOLN[0] = FALSE; if(!EXP_TRANS) { for(j=1;j= 0 || !FS_ACTIVE || !options.FROZEN_SOIL) { if(!EXP_TRANS) T[j] = (A[j]*T0[j]+B[j]*(T[j+1]-T[j-1])+C[j]*T[j+1]+D[j]*T[j-1]+E[j]*(0.-ice[j]))/(A[j]+C[j]+D[j]); else T[j] = (A[j]*T0[j]+B[j]*(T[j+1]-T[j-1])+C[j]*(T[j+1]+T[j-1])-D[j]*(T[j+1]-T[j-1])+E[j]*(0.-ice[j]))/(A[j]+2.*C[j]); } else { #if QUICK_FS T[j] = root_brent(T0[j]-(SOIL_DT), T0[j]+(SOIL_DT), ErrorString, soil_thermal_eqn, T[j+1], T[j-1], T0[j], moist[j], max_moist[j], ufwc_table_node[j], ice[j], gamma[j-1], A[j], B[j], C[j], D[j], E[j], EXP_TRANS, j); #else T[j] = root_brent(T0[j]-(SOIL_DT), T0[j]+(SOIL_DT), ErrorString, soil_thermal_eqn, T[j+1], T[j-1], T0[j], moist[j], max_moist[j], bubble[j], expt[j], #if EXCESS_ICE porosity[j], effective_porosity[j], #endif ice[j], gamma[j-1], A[j], B[j], C[j], D[j], E[j], EXP_TRANS, j); #endif if(T[j] <= -998 ) { if (options.TFALLBACK) { T[j] = oldT; Tfbflag[j] = 1; Tfbcount[j]++; } else { error_solve_T_profile(T[j], T[j+1], T[j-1], T0[j], moist[j], max_moist[j], bubble[j], expt[j], ice[j], gamma[j-1], A[j], B[j], C[j], D[j], E[j], ErrorString); return ( ERROR ); } } } diff=fabs(oldT-T[j]); if(diff > maxdiff) maxdiff=diff; } if(NOFLUX) { /** Solve for bottom temperature if using no flux lower boundary **/ oldT=T[Nnodes-1]; j = Nnodes-1; if(T[j] >= 0 || !FS_ACTIVE || !options.FROZEN_SOIL) { if(!EXP_TRANS ) T[j] = (A[j]*T0[j]+B[j]*(T[j]-T[j-1])+C[j]*T[j]+D[j]*T[j-1]+E[j]*(0.-ice[j]))/(A[j]+C[j]+D[j]); else T[j] = (A[j]*T0[j]+B[j]*(T[j]-T[j-1])+C[j]*(T[j]+T[j-1])-D[j]*(T[j]-T[j-1])+E[j]*(0.-ice[j]))/(A[j]+2.*C[j]); } else { #if QUICK_FS T[Nnodes-1] = root_brent(T0[Nnodes-1]-SOIL_DT, T0[Nnodes-1]+SOIL_DT, ErrorString, soil_thermal_eqn, T[Nnodes-1], T[Nnodes-2], T0[Nnodes-1], moist[Nnodes-1], max_moist[Nnodes-1], ufwc_table_node[Nnodes-1], ice[Nnodes-1], gamma[Nnodes-2], A[j], B[j], C[j], D[j], E[j], EXP_TRANS, j); #else T[Nnodes-1] = root_brent(T0[Nnodes-1]-SOIL_DT, T0[Nnodes-1]+SOIL_DT, ErrorString, soil_thermal_eqn, T[Nnodes-1], T[Nnodes-2], T0[Nnodes-1], moist[Nnodes-1], max_moist[Nnodes-1], bubble[j], expt[Nnodes-1], #if EXCESS_ICE porosity[Nnodes-1], effective_porosity[Nnodes-1], #endif ice[Nnodes-1], gamma[Nnodes-2], A[j], B[j], C[j], D[j], E[j], EXP_TRANS, j); #endif if(T[j] <= -998 ) { if (options.TFALLBACK) { T[j] = oldT; Tfbflag[j] = 1; Tfbcount[j]++; } else { error_solve_T_profile(T[Nnodes-1], T[Nnodes-1], T[Nnodes-2], T0[Nnodes-1], moist[Nnodes-1], max_moist[Nnodes-1], bubble[Nnodes-1], expt[Nnodes-1], ice[Nnodes-1], gamma[Nnodes-2], A[j], B[j], C[j], D[j], E[j], ErrorString); return ( ERROR ); } } } diff=fabs(oldT-T[Nnodes-1]); if(diff>maxdiff) maxdiff=diff; } if(maxdiff <= threshold) Done=TRUE; } if(!Done && !Error) { fprintf(stderr,"ERROR: Temperature Profile Unable to Converge!!!\n"); fprintf(stderr,"Dumping Profile Temperatures (last, new).\n"); for(j=0;j=1) { //all but surface node // update ice contents if (T_2[i-1]<0) { ice_new[i] = moist[i] - maximum_unfrozen_water(T_2[i-1], #if EXCESS_ICE porosity[i], effective_porosity[i], #endif max_moist[i], bubble[i], expt[i]); if (ice_new[i]<0) ice_new[i]=0; } else ice_new[i] = 0; Cs_new[i]=Cs[i]; // update other states due to ice content change /***********************************************/ if (ice_new[i]!=ice[i]) { kappa_new[i] = soil_conductivity(moist[i], moist[i] - ice_new[i], soil_density[lidx], bulk_density[lidx], quartz[lidx]); Cs_new[i] = volumetric_heat_capacity(bulk_density[lidx]/soil_density[lidx], moist[i]-ice_new[i], ice_new[i]); } /************************************************/ } if(Zsum[i] > Lsum + depth[lidx] && !PAST_BOTTOM) { Lsum += depth[lidx]; lidx++; if( lidx == Nlayers ) { PAST_BOTTOM = TRUE; lidx = Nlayers-1; } } } // constants used in fda equation for (i=0; i5. && (T_2[i]0) && fabs(flux_term1)>fabs(flux_term2)){ flux_term1 = 0; #if VERBOSE fprintf(stderr,"WARNING: resetting thermal flux term in soil heat solution to zero for node %d.\nT[i]=%.2f T[i-1]=%.2f T[i+1]=%.2f flux_term1=%.2f flux_term2=%.2f\n",i+1,T_2[i],T_up[i],T_2[i+1],flux_term1,flux_term2); #endif } } // } flux_term = flux_term1+flux_term2; phase_term = ice_density*Lf * (ice_new[i+1] - ice[i+1])/deltat; res[i] = flux_term + phase_term - storage_term; } } // only calculate entries focus-1, focus, and focus+1 if focus has a value>=0 else { if (focus==0) left=0; else left=focus-1; if (focus==n-1) right=n-1; else right=focus+1; // update ice content for node focus and its adjacents for (i=left; i<=right; i++) { if (T_2[i]<0) { ice_new[i+1] = moist[i+1] - maximum_unfrozen_water(T_2[i], #if EXCESS_ICE porosity[i+1], effective_porosity[i+1], #endif max_moist[i+1], bubble[i+1], expt[i+1]); if (ice_new[i+1]<0) ice_new[i+1]=0; } else ice_new[i+1]=0; } // update other parameters due to ice content change /********************************************************/ lidx = 0; Lsum = 0.; PAST_BOTTOM = FALSE; for (i=0; i<=right+1; i++) { if(i>=left+1) { if (ice_new[i]!=ice[i]) { kappa_new[i] = soil_conductivity(moist[i], moist[i] - ice_new[i], soil_density[lidx], bulk_density[lidx], quartz[lidx]); Cs_new[i] = volumetric_heat_capacity(bulk_density[lidx]/soil_density[lidx], moist[i]-ice_new[i], ice_new[i]); } } if(Zsum[i] > Lsum + depth[lidx] && !PAST_BOTTOM) { Lsum += depth[lidx]; lidx++; if( lidx == Nlayers ) { PAST_BOTTOM = TRUE; lidx = Nlayers-1; } } } /*********************************************************/ // update other states due to ice content change for (i=left; i<=right; i++) { if (i==0) { DT[i]=T_2[i+1]-Ts; DT_up[i]=T_2[i]-Ts; DT_down[i]=T_2[i+1]-T_2[i]; T_up[i]=Ts; } else if (i==n-1) { DT[i]=Tb-T_2[i-1]; DT_up[i]=T_2[i]-T_2[i-1]; DT_down[i]=Tb-T_2[i]; T_up[i]=T_2[i-1]; } else { DT[i]=T_2[i+1]-T_2[i-1]; DT_up[i]=T_2[i]-T_2[i-1]; DT_down[i]=T_2[i+1]-T_2[i]; T_up[i]=T_2[i-1]; } //update Dkappa due to ice content change /*******************************************/ if(i5. && (T_2[i]0) && fabs(flux_term1)>fabs(flux_term2)){ flux_term1 = 0; #if VERBOSE fprintf(stderr,"WARNING: resetting thermal flux term in soil heat solution to zero for node %d.\nT[i]=%.2f T[i-1]=%.2f T[i+1]=%.2f flux_term1=%.2f flux_term2=%.2f\n",i+1,T_2[i],T_up[i],T_2[i+1],flux_term1,flux_term2); #endif } } } flux_term = flux_term1+flux_term2; phase_term = ice_density*Lf * (ice_new[i+1] - ice[i+1]) / deltat; res[i] = flux_term + phase_term - storage_term; } } // end of calculation of focus node only } // end of non-init }