internal NormalRunoff runoff; //let derived PondSurface see this but not outside world. #endregion Fields #region Constructors /// <summary> /// Initializes a new instance of the <see cref="NormalSurface"/> class. /// </summary> /// <param name="SoilObject">The soil object.</param> /// <param name="Clock">The clock.</param> public NormalSurface(SoilWaterSoil SoilObject, Clock Clock) { SurfaceType = Surfaces.NormalSurface; base.constants = SoilObject.Constants; runoff = new NormalRunoff(SoilObject); //Soil is needed to initialise the cn2bare, etc. evap = new NormalEvaporation(SoilObject, Clock); }
//Constructor public NormalEvaporation(SoilWaterSoil SoilObject, Clock Clock) { cons = SoilObject.Constants; salb = SoilObject.Salb; summerCona = SoilObject.SummerCona; summerU = SoilObject.SummerU; summerDate = SoilObject.SummerDate; winterCona = SoilObject.WinterCona; winterU = SoilObject.WinterU; winterDate = SoilObject.WinterDate; // soilwat2_soil_property_param() //u - can either use (one value for summer and winter) or two different values. // (must also take into consideration where they enter two values [one for summer and one for winter] but they make them both the same) if ((Double.IsNaN(summerU) || (Double.IsNaN(winterU)))) { throw new Exception("A single value for u OR BOTH values for summeru and winteru must be specified"); } //if they entered two values but they made them the same if (summerU == winterU) { u = summerU; //u is now no longer null. As if the user had entered a value for u. } //cona - can either use (one value for summer and winter) or two different values. // (must also take into consideration where they enter two values [one for summer and one for winter] but they make them both the same) if ((Double.IsNaN(summerCona)) || (Double.IsNaN(winterCona))) { throw new Exception("A single value for cona OR BOTH values for summercona and wintercona must be specified"); } //if they entered two values but they made them the same. if (summerCona == winterCona) { cona = summerCona; //cona is now no longer null. As if the user had entered a value for cona. } //summer and winter default dates. if (summerDate == "not_read") { summerDate = "1-oct"; } if (winterDate == "not_read") { winterDate = "1-apr"; } InitialiseAccumulatingVars(SoilObject, Clock); }
//Constructor public NormalRunoff(SoilWaterSoil SoilObject) { cons = SoilObject.Constants; _cn2_bare = SoilObject.cn2_bare; coverCnRed = SoilObject.cn_red; coverCnCov = SoilObject.cn_cov; //soilwat2_soil_property_param() if (coverCnRed >= _cn2_bare) { coverCnRed = _cn2_bare - 0.00009; } }
private void soilwat2_runoff_depth_factor(SoilWaterSoil SoilObject, ref double[] runoff_wf) { //runoff_wf -> ! (OUTPUT) weighting factor for runoff //*+ Purpose //* Calculate the weighting factor hydraulic effectiveness used //* to weight the effect of soil moisture on runoff. //*+ Mission Statement //* Calculate soil moisture effect on runoff double cum_depth; //! cumulative depth (mm) double hydrol_effective_depth_local; //! hydrologically effective depth for runoff (mm) - for when erosion turned on int hydrol_effective_layer; //! layer number that the effective depth occurs in () double scale_fact; //! scaling factor for wf function to sum to 1 double wf_tot; //! total of wf () double wx; //! depth weighting factor for current total depth. intermediate variable for deriving wf (total wfs to current layer) double xx; //! intermediate variable for deriving wf total wfs to previous layer xx = 0.0; cum_depth = 0.0; wf_tot = 0.0; //! check if hydro_effective_depth applies for eroded profile. hydrol_effective_depth_local = Math.Min(cons.hydrol_effective_depth, SoilObject.DepthTotal); scale_fact = 1.0 / (1.0 - Math.Exp(-4.16)); hydrol_effective_layer = SoilObject.FindLayerNo(hydrol_effective_depth_local); foreach (Layer lyr in SoilObject.TopToX(hydrol_effective_layer)) { cum_depth = cum_depth + lyr.dlayer; cum_depth = Math.Min(cum_depth, hydrol_effective_depth_local); //! assume water content to c%hydrol_effective_depth affects runoff //! sum of wf should = 1 - may need to be bounded? <dms 7-7-95> wx = scale_fact * (1.0 - Math.Exp(-4.16 * MathUtilities.Divide(cum_depth, hydrol_effective_depth_local, 0.0))); runoff_wf[lyr.number-1] = wx - xx; //zero based array xx = wx; wf_tot = wf_tot + runoff_wf[lyr.number-1]; //zero based array. } cons.bound_check_real_var(wf_tot, 0.9999, 1.0001, "wf_tot"); }
private void CalcRunoff_USDA_SoilConservationService(double WaterForRunoff, SoilWaterSoil SoilObject) { //private void soilwat2_scs_runoff(double Rain, double Runon, double TotalInterception, ref double Runoff) // { //cn2_new (output) //Runoff (output) double cn; //! scs curve number double cn1; //! curve no. for dry soil (antecedant) moisture double cn3; //! curve no. for wet soil (antecedant) moisture double cnpd; //! cn proportional in dry range (dul to ll15) double s; //! potential max retention (surface ponding + infiltration) double xpb; //! intermedite variable for deriving runof double[] runoff_wf; //! weighting factor for depth for each layer double dul_fraction; // if between (0-1) not above dul, if (1-infinity) above dul double cover_fract; //! proportion of maximum cover effect on runoff (0-1) double cover_reduction = 0.0; double tillage_fract; double tillage_reduction = 0.0; //! reduction in cn due to tillage runoff_wf = new double[SoilObject.num_layers]; soilwat2_runoff_depth_factor(SoilObject, ref runoff_wf); cnpd = 0.0; foreach (Layer lyr in SoilObject) { dul_fraction = MathUtilities.Divide((lyr.sw_dep - lyr.ll15_dep), (lyr.dul_dep - lyr.ll15_dep), 0.0); cnpd = cnpd + dul_fraction * runoff_wf[lyr.number-1]; //zero based array. } cnpd = cons.bound(cnpd, 0.0, 1.0); //reduce cn2 for the day due to the cover effect //nb. cover_surface_runoff should really be a parameter to this function cover_fract = MathUtilities.Divide(cover_surface_runoff, coverCnCov, 0.0); cover_fract = cons.bound(cover_fract, 0.0, 1.0); cover_reduction = coverCnRed * cover_fract; cn2_new = _cn2_bare - cover_reduction; //tillage reduction on cn //nb. tillage_cn_red, tillage_cn_rain, and tillage_rain_sum, should really be parameters to this function if (tillageCnCumWater > 0.0) { //We minus 1 because we want the opposite fraction. //Tillage Reduction is biggest (CnRed value) straight after Tillage and gets smaller and becomes 0 when reaches CumWater. //unlike the Cover Reduction, where the reduction starts out smallest (0) and gets bigger and becomes (CnRed value) when you hit CnCover. tillage_fract = MathUtilities.Divide(cumWaterSinceTillage, tillageCnCumWater, 0.0) - 1.0; tillage_reduction = tillageCnRed * tillage_fract; cn2_new = cn2_new + tillage_reduction; } else { //nothing } //! cut off response to cover at high covers if p%cn_red < 100. cn2_new = cons.bound(cn2_new, 0.0, 100.0); cn1 = MathUtilities.Divide(cn2_new, (2.334 - 0.01334 * cn2_new), 0.0); cn3 = MathUtilities.Divide(cn2_new, (0.4036 + 0.005964 * cn2_new), 0.0); cn = cn1 + (cn3 - cn1) * cnpd; // ! curve number will be decided from scs curve number table ??dms s = 254.0 * (MathUtilities.Divide(100.0, cn, 1000000.0) - 1.0); xpb = WaterForRunoff - 0.2 * s; xpb = Math.Max(xpb, 0.0); //assign the output variable Runoff = MathUtilities.Divide(xpb * xpb, (WaterForRunoff + 0.8 * s), 0.0); //sv- I added these output variables cn_red_cov = cover_reduction; cn_red_till = tillage_reduction; //bound check the ouput variable cons.bound_check_real_var(Runoff, 0.0, WaterForRunoff, "runoff"); }
public void CalcRunoff(double WaterForRunoff, SoilWaterSoil SoilObject) { //private void soilwat2_runoff(double Rain, double Runon, double TotalInterception, ref double Runoff) //{ Runoff = 0.0; //zero the return parameter if (WaterForRunoff > 0.0) { CalcRunoff_USDA_SoilConservationService(WaterForRunoff, SoilObject); cumWaterSinceTillage = cumWaterSinceTillage + WaterForRunoff; ShouldIStopTillageCNReduction(); //! NB. this needs to be done _after_ cn calculation. } }
//Initialise the Accumulating Variables public void InitialiseAccumulatingVars(SoilWaterSoil SoilObject, Clock Clock) { //reset the accumulated Evap variables (sumes1, sumes2, t) //nb. sumes1 -> is sum of es during stage1 //used in the SoilWater Init, Reset event // soilwat2_soil_property_param() //assign u and cona to either sumer or winter values // Need to add 12 hours to move from "midnight" to "noon", or this won't work as expected if (DateUtilities.WithinDates(winterDate, Clock.Today, summerDate)) { cona = winterCona; u = winterU; } else { cona = summerCona; u = summerU; } //private void soilwat2_evap_init() // { //################## //Evap Init --> soilwat2_evap_init (), soilwat2_ritchie_init() //################## //soilwat2_ritchie_init(); //*+ Mission Statement //* Initialise ritchie evaporation model double swr_top; //! stage 2 evaporation occurs ratio available sw potentially available sw in top layer Layer top = SoilObject.GetTopLayer(); //! set up evaporation stage swr_top = MathUtilities.Divide((top.sw_dep - top.ll15_dep), (top.dul_dep - top.ll15_dep), 0.0); swr_top = cons.bound(swr_top, 0.0, 1.0); //! are we in stage1 or stage2 evap? if (swr_top < cons.sw_top_crit) { //! stage 2 evap sumes2 = cons.sumes2_max - (cons.sumes2_max * MathUtilities.Divide(swr_top, cons.sw_top_crit, 0.0)); sumes1 = u; t = MathUtilities.Sqr(MathUtilities.Divide(sumes2, cona, 0.0)); } else { //! stage 1 evap sumes2 = 0.0; sumes1 = cons.sumes1_max - (cons.sumes1_max * swr_top); t = 0.0; } }
public void CalcEs_RitchieEq_LimitedBySW(SoilWaterSoil SoilObject, Clock Clock, double Infiltration) { //private void soilwat2_ritchie_evaporation() // { //es -> ! (output) actual evaporation (mm) //eos -> ! (input) potential rate of evaporation (mm/day) //avail_sw_top -> ! (input) upper limit of soil evaporation (mm/day) !sv- now calculated in here, not passed in as a argument. //*+ Purpose //* ****** calculate actual evaporation from soil surface (es) ****** //* most es takes place in two stages: the constant rate stage //* and the falling rate stage (philip, 1957). in the constant //* rate stage (stage 1), the soil is sufficiently wet for water //* be transported to the surface at a rate at least equal to the //* evaporation potential (eos). //* in the falling rate stage (stage 2), the surface soil water //* content has decreased below a threshold value, so that es //* depends on the flux of water through the upper layer of soil //* to the evaporating site near the surface. //*+ Notes //* This changes globals - sumes1/2 and t. Es = 0.0; //Zero the return value. double avail_sw_top; //! available soil water in top layer for actual soil evaporation (mm) //2. get available soil water for evaporation Layer top = SoilObject.GetTopLayer(); avail_sw_top = top.sw_dep - top.air_dry_dep; avail_sw_top = cons.bound(avail_sw_top, 0.0, Eo); //3. get actual soil water evaporation double esoil1; //! actual soil evap in stage 1 double esoil2; //! actual soil evap in stage 2 double sumes1_max; //! upper limit of sumes1 double w_inf; //! infiltration into top layer (mm) // Need to add 12 hours to move from "midnight" to "noon", or this won't work as expected if (DateUtilities.WithinDates(winterDate, Clock.Today, summerDate)) { cona = winterCona; u = winterU; } else { cona = summerCona; u = summerU; } sumes1_max = u; w_inf = Infiltration; //! if infiltration, reset sumes1 //! reset sumes2 if infil exceeds sumes1 if (w_inf > 0.0) { sumes2 = Math.Max(0.0, (sumes2 - Math.Max(0.0, w_inf - sumes1))); sumes1 = Math.Max(0.0, sumes1 - w_inf); //! update t (incase sumes2 changed) t = MathUtilities.Sqr(MathUtilities.Divide(sumes2, cona, 0.0)); } else { //! no infiltration, no re-set. } //! are we in stage1 ? if (sumes1 < sumes1_max) { //! we are in stage1 //! set esoil1 = potential, or limited by u. esoil1 = Math.Min(Eos, sumes1_max - sumes1); if ((Eos > esoil1) && (esoil1 < avail_sw_top)) { //* ! eos not satisfied by 1st stage drying, //* ! & there is evaporative sw excess to air_dry, allowing for esoil1. //* ! need to calc. some stage 2 drying(esoil2). //* if g%sumes2.gt.0.0 then esoil2 =f(sqrt(time),p%cona,g%sumes2,g%eos-esoil1). //* if g%sumes2 is zero, then use ritchie's empirical transition constant (0.6). if (sumes2 > 0.0) { t = t + 1.0; esoil2 = Math.Min((Eos - esoil1), (cona * Math.Pow(t, 0.5) - sumes2)); } else { esoil2 = 0.6 * (Eos - esoil1); } } else { //! no deficit (or esoil1.eq.eos_max,) no esoil2 on this day esoil2 = 0.0; } //! check any esoil2 with lower limit of evaporative sw. esoil2 = Math.Min(esoil2, avail_sw_top - esoil1); //! update 1st and 2nd stage soil evaporation. sumes1 = sumes1 + esoil1; sumes2 = sumes2 + esoil2; t = MathUtilities.Sqr(MathUtilities.Divide(sumes2, cona, 0.0)); } else { //! no 1st stage drying. calc. 2nd stage esoil1 = 0.0; t = t + 1.0; esoil2 = Math.Min(Eos, (cona * Math.Pow(t, 0.5) - sumes2)); //! check with lower limit of evaporative sw. esoil2 = Math.Min(esoil2, avail_sw_top); //! update 2nd stage soil evaporation. sumes2 = sumes2 + esoil2; } Es = esoil1 + esoil2; //! make sure we are within bounds Es = cons.bound(Es, 0.0, Eos); Es = cons.bound(Es, 0.0, avail_sw_top); }
/// <summary> /// Removes the evaporation from soil. /// </summary> /// <param name="SoilObject">The soil object.</param> public override void RemoveEvaporationFromSoil(ref SoilWaterSoil SoilObject) { Layer top = SoilObject.GetTopLayer(); top.sw_dep = top.sw_dep - Es; }
/// <summary> /// Gets the surface. /// </summary> /// <param name="SoilObject">The soil object.</param> /// <param name="Clock">The clock.</param> /// <returns></returns> public Surface GetSurface(SoilWaterSoil SoilObject, Clock Clock) { Surface surface; if (SoilObject.max_pond <= 0.0) surface = new NormalSurface(SoilObject, Clock); else surface = new PondSurface(SoilObject, Clock); return surface; }
/// <summary> /// Removes the evaporation from soil. /// </summary> /// <param name="SoilObject">The soil object.</param> public virtual void RemoveEvaporationFromSoil(ref SoilWaterSoil SoilObject) { }
//nb. Use the methods below rather then just using a SoilObject method to add infiltration or remove water, // because we might want to create Surfaces such as cracking clays that adds infiltration to not just // the top layer but multiple layers. Also these surfaces might want to remove evaporation from // multiple layers and not just the top layer. They will need logic to decide what fractions of // infiltration or evaporation that they want to remove from which layers. /// <summary> /// Adds the infiltration to soil. /// </summary> /// <param name="SoilOject">The soil oject.</param> public virtual void AddInfiltrationToSoil(ref SoilWaterSoil SoilOject) { }
/// <summary> /// Adds the backed up water to surface. /// </summary> /// <param name="BackedUp">The backed up.</param> /// <param name="SoilObject">The soil object.</param> public virtual void AddBackedUpWaterToSurface(double BackedUp, ref SoilWaterSoil SoilObject) { }
/// <summary> /// Adds the backed up water to surface. /// </summary> /// <param name="BackedUp">The backed up.</param> /// <param name="SoilObject">The soil object.</param> public override void AddBackedUpWaterToSurface(double BackedUp, ref SoilWaterSoil SoilObject) { //do normal surface add backup to surface base.AddBackedUpWaterToSurface(BackedUp, ref SoilObject); //Any extra_runoff then it becomes a pond. pond = Math.Min(base.Runoff, SoilObject.max_pond); //If there is too much for the pond handle then add the excess to normal runoff. base.Runoff = base.Runoff - pond; }
/// <summary> /// Initializes a new instance of the <see cref="PondSurface"/> class. /// </summary> /// <param name="SoilObject">The soil object.</param> /// <param name="Clock">The clock.</param> public PondSurface(SoilWaterSoil SoilObject, Clock Clock) : base(SoilObject, Clock) { base.SurfaceType = Surfaces.PondSurface; pond = 0.0; pond_evap = 0.0; }
/// <summary> /// Adds the backed up water to surface. /// </summary> /// <param name="BackedUp">The backed up.</param> /// <param name="SoilObject">The soil object.</param> public override void AddBackedUpWaterToSurface(double BackedUp, ref SoilWaterSoil SoilObject) { //If Infiltration was more water that the top layer of soil had empty then the extra water had no choice but to back up. //In this case turn the backed up water into runoff and reduce the infiltration to only what the top layer could take before backing up. //The amount the top layer can take must be equal to the infiltration - backedup amount. //nb. What if lateral inflow caused it to back up? We are assuming only source of water into top layer is infiltration from surface. //remove backed up amount from the top layer of the soil. (All of the infiltration did not infiltrate) Layer top = SoilObject.GetTopLayer(); top.sw_dep = top.sw_dep - BackedUp; //now reduce the infiltration amount by what backed up. base.Infiltration = base.Infiltration - BackedUp; //turn the proportion of the infiltration that backed up into runoff. base.Runoff = base.Runoff + BackedUp; }
private void OnSimulationCommencing(object sender, EventArgs e) { SaveModuleConstants(); //daily inputs met = new MetData(); irrig = new IrrigData(); canopy = new CanopyData(); surfaceCover = new SurfaceCoverData(); //optional daily inputs runon = 0.0; interception = 0.0; residueinterception = 0.0; if (Soil.Thickness != null) { try { SoilObject = new SoilWaterSoil(constants, Soil); //constructor can throw an Exception surfaceFactory = new SurfaceFactory(); surface = surfaceFactory.GetSurface(SoilObject, Clock); //constructor can throw an Exception (Evap class constructor too) //optional inputs (array) inflow_lat = null; } catch (Exception Ex) { throw new ApsimXException(this, Ex.Message); //catch any constructor Exceptions and rethrow as an ApsimXException. } } else { throw new ApsimXException(this, "SoilWater module has detected that the Soil has no layers."); } }
/// <summary> /// Adds the infiltration to soil. /// </summary> /// <param name="SoilObject">The soil object.</param> public override void AddInfiltrationToSoil(ref SoilWaterSoil SoilObject) { Layer top = SoilObject.GetTopLayer(); top.sw_dep = top.sw_dep + Infiltration; }