Beispiel #1
0
        //---------------------------------------------------------------------------
        private static double SoilMoistureMultiplier(AnnualClimate weather, ISpecies species, double dryDays)

        {
            double sppAllowableDrought = SpeciesData.MaxDrought[species];
            double growDays            = 0.0;
            double maxDrought;
            double Soil_Moist_GF = 0.0;

            growDays = weather.EndGrowing - weather.BeginGrowing + 1.0;
            if (growDays < 2.0)
            {
                PlugIn.ModelCore.UI.WriteLine("Begin Grow = {0}, End Grow = {1}", weather.BeginGrowing, weather.EndGrowing);
                throw new System.ApplicationException("Error: Too few growing days.");
            }
            //Calc species soil moisture multipliers
            maxDrought = sppAllowableDrought * growDays;

            if (maxDrought < dryDays)
            {
                Soil_Moist_GF = 0.0;
            }
            else
            {
                Soil_Moist_GF = System.Math.Sqrt((double)(maxDrought - dryDays) / maxDrought);
            }
            //PlugIn.ModelCore.UI.WriteLine("BeginGrow={0}, EndGrow={1}, dryDays={2}, maxDrought={3}", weather.BeginGrowing, weather.EndGrowing, dryDays, maxDrought);
            return(Soil_Moist_GF);
        }
        //---------------------------------------------------------------------------
        private static double SoilMoistureMultiplier(AnnualClimate weather, ISpecies species, double dryDays)
        //Calc soil moisture multipliers based on Degree_Day (supplied by calc_temperature()),
        //dryDays (supplied by MOIST).

        {
            double sppAllowableDrought = SpeciesData.MaxDrought[species];
            double growDays            = 0.0;
            double maxDrought;
            double Soil_Moist_GF = 0.0;

            growDays = weather.EndGrowing - weather.BeginGrowing + 1.0;
            if (growDays < 2.0)
            {
                PlugIn.ModelCore.UI.WriteLine("Begin Grow = {0}, End Grow = {1}", weather.BeginGrowing, weather.EndGrowing);
                throw new System.ApplicationException("Error: Too few growing days.");
            }
            //Calc species soil moisture multipliers
            maxDrought = sppAllowableDrought * growDays;

            //PlugIn.ModelCore.UI.WriteLine("SppMaxDr={0:0.00}, growDays={1:0.0}, dryDays={2:0.0}.", sppAllowableDrought, growDays, dryDays);
            if (maxDrought < dryDays)
            {
                Soil_Moist_GF = 0.0;
            }
            else
            {
                Soil_Moist_GF = System.Math.Sqrt((double)(maxDrought - dryDays) / maxDrought);
            }
            return(Soil_Moist_GF);
        }
        //---------------------------------------------------------------------------
        private static double BotkinDegreeDayMultiplier(AnnualClimate weather, ISpecies species)
        {
            //Calc species degree day multipliers
            //Botkin et al. 1972. J. Ecol. 60:849 - 87

            double max_Grow_Deg_Days = SpeciesData.GDDmax[species];
            double min_Grow_Deg_Days = SpeciesData.GDDmin[species];

            double Deg_Day_GF = 0.0;
            double Deg_Days   = (double)weather.GrowingDegreeDays;
            double totalGDD   = max_Grow_Deg_Days - min_Grow_Deg_Days;

            Deg_Day_GF = (4.0 * (Deg_Days - min_Grow_Deg_Days) *
                          (max_Grow_Deg_Days - Deg_Days)) / (totalGDD * totalGDD);

            if (Deg_Day_GF < 0)
            {
                Deg_Day_GF = 0.0;
            }

            return(Deg_Day_GF);
        }
Beispiel #4
0
        //---------------------------------------------------------------------------
        private static double BotkinDegreeDayMultiplier(AnnualClimate weather, ISpecies species)
        {
            //Calc species degree day multipliers
            //Botkin et al. 1972. J. Ecol. 60:849 - 87

            double max_Grow_Deg_Days = SpeciesData.GDDmax[species];
            double min_Grow_Deg_Days = SpeciesData.GDDmin[species];

            double Deg_Day_GF = 0.0;
            double Deg_Days   = (double)weather.GrowingDegreeDays;
            double totalGDD   = max_Grow_Deg_Days - min_Grow_Deg_Days;

            Deg_Day_GF = (4.0 * (Deg_Days - min_Grow_Deg_Days) *
                          (max_Grow_Deg_Days - Deg_Days)) / (totalGDD * totalGDD);

            if (Deg_Day_GF < 0)
            {
                Deg_Day_GF = 0.0;
            }
            //PlugIn.ModelCore.UI.WriteLine("SppMaxDD={0:0.00}, sppMinGDD={1:0.0}, actualGDD={2:0}, gddM={3:0.00}.", max_Grow_Deg_Days, min_Grow_Deg_Days, Deg_Days, Deg_Day_GF);

            return(Deg_Day_GF);
        }
Beispiel #5
0
        //---------------------------------------------------------------------------
        private static double CalculateSoilMoisture(AnnualClimate_Monthly weather, IEcoregion ecoregion, int year, ActiveSite site)
        // Calculate fraction of growing season with unfavorable soil moisture
        // for growth (Dry_Days_in_Grow_Seas) used in SoilMoistureMultiplier to determine soil
        // moisture growth multipliers.
        //
        // Simulates method of Thorthwaite and Mather (1957) as modified by Pastor and Post (1984).
        //
        // This method necessary to estimate annual soil moisture at the ECOREGION scale, whereas
        // the SiteVar AvailableWater exists at the site level and is updated monthly.

        //field_cap = centimeters of water the soil can hold at field capacity
        //field_dry = centimeters of water below which tree growth stops
        //            (-15 bars)
        // NOTE:  Because the original LINKAGES calculations were based on a 100 cm rooting depth,
        // 100 cm are used here, although soil depth may be given differently for Century
        // calculations.

        //beg_grow_seas = year day on which the growing season begins
        //end_grow_seas = year day on which the growing season ends
        //latitude = latitude of region (degrees north)

        {
            double xFieldCap,                                                                           //
                   waterAvail,                                                                          //
                   aExponentET,                                                                         //
                   oldWaterAvail,                                                                       //
                   monthlyRain,                                                                         //
                   potWaterLoss,                                                                        //
                   potentialET,
                   tempFac,                                                                             //
                   xAccPotWaterLoss,                                                                    //
                   changeSoilMoisture,                                                                  //
                   oldJulianDay,                                                                        //
                   dryDayInterp;                                                                        //
            double fieldCapacity = SiteVars.SoilFieldCapacity[site] * (double)SiteVars.SoilDepth[site]; // ClimateRegionData.SoilDepth[ecoregion];
            double wiltingPoint  = SiteVars.SoilWiltingPoint[site] * (double)SiteVars.SoilDepth[site];  // ClimateRegionData.SoilDepth[ecoregion];

            //double fieldCapacity = ClimateRegionData.FieldCapacity[ecoregion] * 100.0;
            //double wiltingPoint  = ClimateRegionData.WiltingPoint[ecoregion] * 100.0;

            //Initialize water content of soil in January to Field_Cap (mm)
            xFieldCap  = 10.0 * fieldCapacity;
            waterAvail = fieldCapacity;


            //Initialize Thornwaithe parameters:
            //
            //TE = temperature efficiency
            //aExponentET = exponent of evapotranspiration function
            //pot_et = potential evapotranspiration
            //aet = actual evapotranspiration
            //acc_pot_water_loss = accumulated potential water loss

            double actualET        = 0.0;
            double accPotWaterLoss = 0.0;
            double tempEfficiency  = 0.0;

            for (int i = 0; i < 12; i++)
            {
                tempFac = 0.2 * weather.MonthlyTemp[i];

                if (tempFac > 0.0)
                {
                    tempEfficiency += System.Math.Pow(tempFac, 1.514);
                }
            }

            aExponentET = 0.675 * System.Math.Pow(tempEfficiency, 3) -
                          77.1 * (tempEfficiency * tempEfficiency) +
                          17920.0 * tempEfficiency + 492390.0;
            aExponentET *= (0.000001);

            //Initialize the number of dry days and current day of year
            int    dryDays           = 0;
            double julianDay         = 15.0;
            double annualPotentialET = 0.0;


            for (int i = 0; i < 12; i++)
            {
                double daysInMonth = AnnualClimate.DaysInMonth(i, year);
                oldWaterAvail = waterAvail;
                monthlyRain   = weather.MonthlyPrecip[i];
                tempFac       = 10.0 * weather.MonthlyTemp[i];

                //Calc potential evapotranspiriation (potentialET) Thornwaite and Mather,
                //1957.  Climatology 10:83 - 311.
                if (tempFac > 0.0)
                {
                    potentialET = 1.6 * (System.Math.Pow((tempFac / tempEfficiency), aExponentET)) *
                                  AnnualClimate.LatitudeCorrection(i, PlugIn.Parameters.Latitude); //ClimateRegionData.Latitude[ecoregion]);
                }
                else
                {
                    potentialET = 0.0;
                }

                annualPotentialET += potentialET;

                //Calc potential water loss this month
                potWaterLoss = monthlyRain - potentialET;

                //If monthlyRain doesn't satisfy potentialET, add this month's potential
                //water loss to accumulated water loss from soil
                if (potWaterLoss < 0.0)
                {
                    accPotWaterLoss += potWaterLoss;
                    xAccPotWaterLoss = accPotWaterLoss * 10;

                    //Calc water retained in soil given so much accumulated potential
                    //water loss Pastor and Post. 1984.  Can. J. For. Res. 14:466:467.

                    waterAvail = fieldCapacity *
                                 System.Math.Exp((.000461 - 1.10559 / xFieldCap) * (-1.0 * xAccPotWaterLoss));

                    if (waterAvail < 0.0)
                    {
                        waterAvail = 0.0;
                    }

                    //changeSoilMoisture - during this month
                    changeSoilMoisture = waterAvail - oldWaterAvail;

                    //Calc actual evapotranspiration (AET) if soil water is drawn down
                    actualET += (monthlyRain - changeSoilMoisture);
                }

                //If monthlyRain satisfies potentialET, don't draw down soil water
                else
                {
                    waterAvail = oldWaterAvail + potWaterLoss;
                    if (waterAvail >= fieldCapacity)
                    {
                        waterAvail = fieldCapacity;
                    }
                    changeSoilMoisture = waterAvail - oldWaterAvail;

                    //If soil partially recharged, reduce accumulated potential
                    //water loss accordingly
                    accPotWaterLoss += changeSoilMoisture;

                    //If soil completely recharged, reset accumulated potential
                    //water loss to zero
                    if (waterAvail >= fieldCapacity)
                    {
                        accPotWaterLoss = 0.0;
                    }

                    //If soil water is not drawn upon, add potentialET to AET
                    actualET += potentialET;
                }

                oldJulianDay = julianDay;
                julianDay   += daysInMonth;
                dryDayInterp = 0.0;

                //Increment number of dry days, truncate
                //at end of growing season
                if ((julianDay > weather.BeginGrowing) && (oldJulianDay < weather.EndGrowing))
                {
                    if ((oldWaterAvail >= wiltingPoint) && (waterAvail >= wiltingPoint))
                    {
                        dryDayInterp += 0.0;  // NONE below wilting point
                    }
                    else if ((oldWaterAvail > wiltingPoint) && (waterAvail < wiltingPoint))
                    {
                        dryDayInterp = daysInMonth * (wiltingPoint - waterAvail) /
                                       (oldWaterAvail - waterAvail);
                        if ((oldJulianDay < weather.BeginGrowing) && (julianDay > weather.BeginGrowing))
                        {
                            if ((julianDay - weather.BeginGrowing) < dryDayInterp)
                            {
                                dryDayInterp = julianDay - weather.BeginGrowing;
                            }
                        }

                        if ((oldJulianDay < weather.EndGrowing) && (julianDay > weather.EndGrowing))
                        {
                            dryDayInterp = weather.EndGrowing - julianDay + dryDayInterp;
                        }

                        if (dryDayInterp < 0.0)
                        {
                            dryDayInterp = 0.0;
                        }
                    }
                    else if ((oldWaterAvail < wiltingPoint) && (waterAvail > wiltingPoint))
                    {
                        dryDayInterp = daysInMonth * (wiltingPoint - oldWaterAvail) /
                                       (waterAvail - oldWaterAvail);

                        if ((oldJulianDay < weather.BeginGrowing) && (julianDay > weather.BeginGrowing))
                        {
                            dryDayInterp = oldJulianDay + dryDayInterp - weather.BeginGrowing;
                        }

                        if (dryDayInterp < 0.0)
                        {
                            dryDayInterp = 0.0;
                        }

                        if ((oldJulianDay < weather.EndGrowing) && (julianDay > weather.EndGrowing))
                        {
                            if ((weather.EndGrowing - oldJulianDay) < dryDayInterp)
                            {
                                dryDayInterp = weather.EndGrowing - oldJulianDay;
                            }
                        }
                    }
                    else // ALL below wilting point
                    {
                        dryDayInterp = daysInMonth;

                        if ((oldJulianDay < weather.BeginGrowing) && (julianDay > weather.BeginGrowing))
                        {
                            dryDayInterp = julianDay - weather.BeginGrowing;
                        }

                        if ((oldJulianDay < weather.EndGrowing) && (julianDay > weather.EndGrowing))
                        {
                            dryDayInterp = weather.EndGrowing - oldJulianDay;
                        }
                    }

                    dryDays += (int)dryDayInterp;
                }
            }  //END MONTHLY CALCULATIONS

            //Convert AET from cm to mm
            //actualET *= 10.0;

            //Calculate AET multiplier
            //(used to be done in decomp)
            //float aetMf = min((double)AET,600.0);
            //AET_Mult = (-1. * aetMf) / (-1200. + aetMf);

            return(dryDays);
        }
Beispiel #6
0
        public static void Run(int year, int month, double liveBiomass, Site site, out double baseFlow, out double stormFlow, out double AET)
        {
            //Originally from h2olos.f of CENTURY model
            //Water Submodel for Century - written by Bill Parton
            //     Updated from Fortran 4 - rm 2/92
            //     Rewritten by Bill Pulliam - 9/94
            //     Rewritten by Melissa Lucash- 11/2014

            //PlugIn.ModelCore.UI.WriteLine("month={0}.", Main.Month);

            //...Initialize Local Variables
            double addToSoil    = 0.0;
            double bareSoilEvap = 0.0;

            baseFlow = 0.0;
            double relativeWaterContent = 0.0;
            double snow = 0.0;

            stormFlow = 0.0;
            double actualET          = 0.0;
            double remainingPET      = 0.0;
            double availableWaterMax = 0.0;  //amount of water available after precipitation and snowmelt (over-estimate of available water)
            //double availableWaterMin = 0.0;   //amount of water available after stormflow (runoff) evaporation and transpiration, but before baseflow/leaching (under-estimate of available water)
            double availableWater  = 0.0;    //amount of water deemed available to the trees, which will be the average between the max and min
            double priorWaterAvail = SiteVars.AvailableWater[site];
            double waterFull       = 0.0;

            //...Calculate external inputs
            IEcoregion ecoregion = PlugIn.ModelCore.Ecoregion[site];

            double litterBiomass    = (SiteVars.SurfaceStructural[site].Carbon + SiteVars.SurfaceMetabolic[site].Carbon) * 2.0;
            double deadBiomass      = SiteVars.SurfaceDeadWood[site].Carbon / 0.47;
            double soilWaterContent = SiteVars.SoilWaterContent[site];
            double liquidSnowpack   = SiteVars.LiquidSnowPack[site];

            H2Oinputs     = ClimateRegionData.AnnualWeather[ecoregion].MonthlyPrecip[month]; //rain + irract in cm;
            Precipitation = ClimateRegionData.AnnualWeather[ecoregion].MonthlyPrecip[month]; //rain + irract in cm;
            tave          = ClimateRegionData.AnnualWeather[ecoregion].MonthlyTemp[month];
            tmax          = ClimateRegionData.AnnualWeather[ecoregion].MonthlyMaxTemp[month];
            tmin          = ClimateRegionData.AnnualWeather[ecoregion].MonthlyMinTemp[month];
            pet           = ClimateRegionData.AnnualWeather[ecoregion].MonthlyPET[month];
            daysInMonth   = AnnualClimate.DaysInMonth(month, year);
            beginGrowing  = ClimateRegionData.AnnualWeather[ecoregion].BeginGrowing;
            endGrowing    = ClimateRegionData.AnnualWeather[ecoregion].EndGrowing;

            double wiltingPoint      = SiteVars.SoilWiltingPoint[site];
            double soilDepth         = SiteVars.SoilDepth[site];
            double fieldCapacity     = SiteVars.SoilFieldCapacity[site];
            double stormFlowFraction = SiteVars.SoilStormFlowFraction[site];
            double baseFlowFraction  = SiteVars.SoilBaseFlowFraction[site];
            double drain             = SiteVars.SoilDrain[site];


            //...Calculating snow pack first. Occurs when mean monthly air temperature is equal to or below freezing,
            //     precipitation is in the form of snow.

            if (tmin <= 0.0) // Use tmin to dictate whether it snows or rains.
            {
                snow            = H2Oinputs;
                H2Oinputs       = 0.0;
                liquidSnowpack += snow;  //only tracking liquidsnowpack (water equivalent) and not the actual amount of snow on the ground (i.e. not snowpack).
                //PlugIn.ModelCore.UI.WriteLine("Let it snow!! snow={0}, liquidsnowpack={1}.", snow, liquidSnowpack);
            }
            else
            {
                //PH: Accumulate precipitation and snowmelt before adding to soil so that interception and soil evaporation can come out first
                addToSoil += H2Oinputs;
                //soilWaterContent += H2Oinputs;
                //PlugIn.ModelCore.UI.WriteLine("Let it rain and add it to soil! rain={0}, soilWaterContent={1}.", H2Oinputs, soilWaterContent);
            }


            //...Then melt snow if there is snow on the ground and air temperature (tmax) is above minimum.
            if (liquidSnowpack > 0.0 && tmax > 0.0)
            {
                //...Calculate the amount of snow to melt:
                //This relationship ultimately derives from http://www.nps.gov/yose/planyourvisit/climate.htm which described the relationship between snow melting and air temp.
                //Documentation for the regression equation is in spreadsheet called WaterCalcs.xls by M. Lucash
                double snowMeltFraction = Math.Max((tmax * 0.05) + 0.024, 0.0);//This equation assumes a linear increase in the fraction of snow that melts as a function of air temp.

                if (snowMeltFraction > 1.0)
                {
                    snowMeltFraction = 1.0;
                }

                //PH:
                addToSoil += liquidSnowpack * snowMeltFraction;  //PH: Add melted snow to addToSoil. Amount of liquidsnowpack that melts = liquidsnowpack multiplied by the fraction that melts.
                //addToSoil = liquidSnowpack * snowMeltFraction;  //Amount of liquidsnowpack that melts = liquidsnowpack multiplied by the fraction that melts.

                //Subtracted melted snow from snowpack and add it to the soil
                liquidSnowpack = liquidSnowpack - addToSoil;
                //PH: Add to soil later.
                //soilWaterContent += addToSoil;
            }

            //Calculate the max amout of water available to trees, an over-estimate of the water available to trees.  It only reflects precip and melting of precip.
            //PH: available water may need to be calculated differently with my proposed changes, but I moved this variable down for now so that soilEvaporation comes out first.
            //availableWaterMax = soilWaterContent;

            //...Evaporate water from the snow pack (rewritten by Pulliam 9/94)
            //...Coefficient 0.87 relates to heat of fusion for ice vs. liquid water
            if (liquidSnowpack > 0.0)
            {
                //...Calculate cm of snow that remaining pet energy can evaporate:
                double evaporatedSnow = pet * 0.87;

                //...Don't evaporate more snow than actually exists:
                if (evaporatedSnow > liquidSnowpack)
                {
                    evaporatedSnow = liquidSnowpack;
                }

                liquidSnowpack = liquidSnowpack - evaporatedSnow;

                //...Decrement remaining pet by energy used to evaporate snow:

                //PH: CENTURY code divides evaporatedSnow by 0.87 so it matches the PET used to melt snow.
                remainingPET = pet - evaporatedSnow / 0.87;
                //remainingPET = pet - evaporatedSnow;

                if (remainingPET < 0.0)
                {
                    remainingPET = 0.0;
                }

                //Subtract evaporated snowfrom the soil water content
                //PH: Take evaporated snow out of snowmelt instead of soil...or is that double counting evaporation? Could this cause addToSoil to go negative?
                //It is already taken out of the snowpack, and is used to decrement PET so that it won't affect AET
                addToSoil -= evaporatedSnow;
                if (addToSoil < 0.0)
                {
                    addToSoil = 0.0;
                }
                //soilWaterContent -= evaporatedSnow;
            }

            // ********************************************************
            //...Calculate bare soil water loss and interception  when air temperature is above freezing and no snow cover.
            //...Mofified 9/94 to allow interception when t < 0 but no snow cover, Pulliam
            //PH: I moved this up to remove intercepted precipitation and bare soil evaporation from accumulated precipitation and snowmelt before it goes to the soil.
            if (liquidSnowpack <= 0.0)
            {
                //...Calculate total canopy cover and litter, put cap on effects:
                double standingBiomass = liveBiomass + deadBiomass;

                if (standingBiomass > 800.0)
                {
                    standingBiomass = 800.0;
                }
                if (litterBiomass > 400.0)
                {
                    litterBiomass = 400.0;
                }

                //...canopy interception, fraction of  precip (canopyIntercept):
                double canopyIntercept = ((0.0003 * litterBiomass) + (0.0006 * standingBiomass)) * OtherData.WaterLossFactor1;

                //...Bare soil evaporation, fraction of precip (bareSoilEvap):
                bareSoilEvap = 0.5 * System.Math.Exp((-0.002 * litterBiomass) - (0.004 * standingBiomass)) * OtherData.WaterLossFactor2;

                //...Calculate total surface evaporation losses, maximum allowable is 0.4 * pet. -rm 6/94
                remainingPET = pet;
                double soilEvaporation = System.Math.Min(((bareSoilEvap + canopyIntercept) * H2Oinputs), (0.4 * remainingPET));

                //Subtract soil evaporation from soil water content
                //PH: Subtract soilEvaporation from addToSoil so it won't drive down soil water.
                //PH: SoilEvaporation represents water that evaporates before reaching soil, so should not be subtracted from soil.
                addToSoil -= soilEvaporation;
                //soilWaterContent -= soilEvaporation;
            }

            //PH: Add liquid water to soil
            soilWaterContent += addToSoil;
            //Calculate the max amout of water available to trees, an over-estimate of the water available to trees.  It only reflects precip and melting of precip.
            //PH: Moved down so that soilEvaporation comes out first.
            availableWaterMax = soilWaterContent;
            // ********************************************************


            // Calculate actual evapotranspiration.  This equation is derived from the stand equation for calculating AET from PET
            //  Bergström, 1992

            // ********************************************************
            //PH: Moved up to take evapotranspiration out before excess drains away. This is different from the CENTURY approach, where evaporation is taken out of the add first, but is
            //less complex because it does not require partitioning the evaporation if evapotranspiration exceeds addToSoil.

            double waterEmpty = wiltingPoint * soilDepth;

            waterFull = soilDepth * fieldCapacity;  //units of cm


            if (soilWaterContent > waterFull)
            {
                actualET = remainingPET;
            }
            else
            {
                actualET = Math.Max(remainingPET * ((soilWaterContent - waterEmpty) / (waterFull - waterEmpty)), 0.0);
            }

            if (actualET < 0.0)
            {
                actualET = 0.0;
            }
            AET = actualET;
            // ********************************************************

            //PlugIn.ModelCore.UI.WriteLine("AET {0} = ", AET);

            //Subtract transpiration from soil water content
            soilWaterContent -= actualET;

            //Allow excess water to run off during storm events (stormflow)
            double waterMovement = 0.0;

            if (soilWaterContent > waterFull)
            {
                waterMovement    = Math.Max((soilWaterContent - waterFull), 0.0); // How much water should move during a storm event, which is based on how much water the soil can hold.
                soilWaterContent = waterFull;

                //...Compute storm flow.
                stormFlow = waterMovement * stormFlowFraction;

                // ********************************************************
                //Subtract stormflow from soil water
                //PH: I don't see why this should come out of soil water. It should come out of excess water
                //soilWaterContent -= stormFlow;
                //PlugIn.ModelCore.UI.WriteLine("Water Runs Off. stormflow={0}.", stormFlow);
                // ********************************************************
            }

            // ********************************************************
            //PH: add new variable to track excess water and calclulate baseFlow
            //PH: Remove stormFlow from from excess water
            waterMovement -= stormFlow;
            holdingTank   += waterMovement;
            // ********************************************************


            // ********************************************************
            //PH: moved up to take out soil evaporation and interception before water is added to soil.
            //...Calculate bare soil water loss and interception  when air temperature is above freezing and no snow cover.
            //...Mofified 9/94 to allow interception when t < 0 but no snow cover, Pulliam
            //if (liquidSnowpack <= 0.0)
            //{
            //...Calculate total canopy cover and litter, put cap on effects:
            //     double standingBiomass = liveBiomass + deadBiomass;
            //
            //     if (standingBiomass > 800.0) standingBiomass = 800.0;
            //     if (litterBiomass > 400.0) litterBiomass = 400.0;

            //...canopy interception, fraction of  precip (canopyIntercept):
            //double canopyIntercept = ((0.0003 * litterBiomass) + (0.0006 * standingBiomass)) * OtherData.WaterLossFactor1;

            //...Bare soil evaporation, fraction of precip (bareSoilEvap):
            //bareSoilEvap = 0.5 * System.Math.Exp((-0.002 * litterBiomass) - (0.004 * standingBiomass)) * OtherData.WaterLossFactor2;

            //...Calculate total surface evaporation losses, maximum allowable is 0.4 * pet. -rm 6/94
            //remainingPET = pet;
            //double soilEvaporation = System.Math.Min(((bareSoilEvap + canopyIntercept) * H2Oinputs), (0.4 * remainingPET));

            //Subtract soil evaporation from soil water content
            //soilWaterContent -= soilEvaporation;
            //}

            // Calculate actual evapotranspiration.  This equation is derived from the stand equation for calculating AET from PET
            //  Bergström, 1992

            //double waterEmpty = wiltingPoint * soilDepth;

            //if (soilWaterContent > waterFull)
            //    actualET = remainingPET;
            //else
            //{
            //    actualET = Math.Max(remainingPET * ((soilWaterContent - waterEmpty) / (waterFull - waterEmpty)), 0.0);
            //}

            //if (actualET < 0.0)
            //    actualET = 0.0;
            //AET = actualET;

            //PlugIn.ModelCore.UI.WriteLine("AET {0} = ", AET);

            //Subtract transpiration from soil water content
            //soilWaterContent -= actualET;
            // ********************************************************


            // ********************************************************
            //Leaching occurs. Drain baseflow fraction from holding tank.
            //PH: Now baseflow comes from holding tank.
            baseFlow = holdingTank * baseFlowFraction;
            //baseFlow = soilWaterContent * baseFlowFraction;

            //Subtract baseflow from soil water
            //PH: Subtract from holding tank instead. To not deplete soil water but still allow estimation of baseFlow.
            holdingTank -= baseFlow;
            //soilWaterContent -= baseFlow;
            // ********************************************************


            //Calculate the amount of available water after all the evapotranspiration and leaching has taken place (minimum available water)
            availableWater = Math.Max(soilWaterContent - waterEmpty, 0.0);

            //Calculate the final amount of available water to the trees, which is the average of the max and min
            //PH: availableWater is affected by my changes, and soilWaterContent should be higher now.  Therefore, I propose calculating using soilWaterContent directly instead
            //availableWater = soilWaterContent - waterEmpty;

            // Here calculate available water at the midpoint of the month
            //availableWater = (availableWaterMax + availableWaterMin)/ 2.0;

            // Compute the ratio of precipitation to PET
            double ratioPrecipPET = 0.0;

            if (pet > 0.0)
            {
                ratioPrecipPET = availableWater / pet;                            //assumes that the ratio is the amount of incoming precip divided by PET.
            }
            SiteVars.AnnualPPT_AET[site] += actualET;                             // RMS:  Currently using this to test AET by itself // Precipitation - actualET;
            SiteVars.AnnualClimaticWaterDeficit[site] += (pet - actualET) * 10.0; // Convert to mm, the standard definition
            //PlugIn.ModelCore.UI.WriteLine("Month={0}, PET={1}, AET={2}.", month, pet, actualET);

            SiteVars.LiquidSnowPack[site]   = liquidSnowpack;
            SiteVars.WaterMovement[site]    = waterMovement;
            SiteVars.AvailableWater[site]   = availableWater; //available to plants for growth
            SiteVars.SoilWaterContent[site] = soilWaterContent;
            SiteVars.SoilTemperature[site]  = CalculateSoilTemp(tmin, tmax, liveBiomass, litterBiomass, month);
            SiteVars.DecayFactor[site]      = CalculateDecayFactor((int)OtherData.WType, SiteVars.SoilTemperature[site], relativeWaterContent, ratioPrecipPET, month);
            SiteVars.AnaerobicEffect[site]  = CalculateAnaerobicEffect(drain, ratioPrecipPET, pet, tave);
            if (month == 0)
            {
                SiteVars.DryDays[site] = 0;
            }
            else
            {
                SiteVars.DryDays[site] += CalculateDryDays(month, beginGrowing, endGrowing, waterEmpty, availableWater, priorWaterAvail);
            }

            return;
        }
        public static void Run(int year, int month, double liveBiomass, Site site, out double baseFlow, out double stormFlow, out double AET)
        {
            //Originally from h2olos.f of CENTURY model
            //Water Submodel for Century - written by Bill Parton
            //     Updated from Fortran 4 - rm 2/92
            //     Rewritten by Bill Pulliam - 9/94
            //     Rewritten by Melissa Lucash- 11/2014

            //PlugIn.ModelCore.UI.WriteLine("month={0}.", Century.Month);

            //...Initialize Local Variables
            double addToSoil    = 0.0;
            double bareSoilEvap = 0.0;

            baseFlow = 0.0;
            double relativeWaterContent = 0.0;
            double snow = 0.0;

            stormFlow = 0.0;
            double actualET          = 0.0;
            double remainingPET      = 0.0;
            double availableWaterMax = 0.0;  //amount of water available after precipitation and snowmelt (over-estimate of available water)
            double availableWaterMin = 0.0;  //amount of water available after stormflow (runoff) evaporation and transpiration, but before baseflow/leaching (under-estimate of available water)
            double availableWater    = 0.0;  //amount of water deemed available to the trees, which will be the average between the max and min
            double priorWaterAvail   = SiteVars.AvailableWater[site];

            //...Calculate external inputs
            IEcoregion ecoregion = PlugIn.ModelCore.Ecoregion[site];

            double litterBiomass    = (SiteVars.SurfaceStructural[site].Carbon + SiteVars.SurfaceMetabolic[site].Carbon) * 2.0;
            double deadBiomass      = SiteVars.SurfaceDeadWood[site].Carbon / 0.47;
            double soilWaterContent = SiteVars.SoilWaterContent[site];
            double liquidSnowpack   = SiteVars.LiquidSnowPack[site];

            H2Oinputs = ClimateRegionData.AnnualWeather[ecoregion].MonthlyPrecip[month]; //rain + irract in cm;
            //PlugIn.ModelCore.UI.WriteLine("SoilWater. WaterInputs={0:0.00}, .", H2Oinputs);
            tave = ClimateRegionData.AnnualWeather[ecoregion].MonthlyTemp[month];
            //PlugIn.ModelCore.UI.WriteLine("SoilWater. AvgTemp={0:0.00}, .", tave);
            tmax         = ClimateRegionData.AnnualWeather[ecoregion].MonthlyMaxTemp[month];
            tmin         = ClimateRegionData.AnnualWeather[ecoregion].MonthlyMinTemp[month];
            pet          = ClimateRegionData.AnnualWeather[ecoregion].MonthlyPET[month];
            daysInMonth  = AnnualClimate.DaysInMonth(month, year);
            beginGrowing = ClimateRegionData.AnnualWeather[ecoregion].BeginGrowing;
            endGrowing   = ClimateRegionData.AnnualWeather[ecoregion].EndGrowing;

            double wiltingPoint      = SiteVars.SoilWiltingPoint[site];      //ClimateRegionData.WiltingPoint[ecoregion];
            double soilDepth         = SiteVars.SoilDepth[site];             // ClimateRegionData.SoilDepth[ecoregion];
            double fieldCapacity     = SiteVars.SoilFieldCapacity[site];     //ClimateRegionData.FieldCapacity[ecoregion];
            double stormFlowFraction = SiteVars.SoilStormFlowFraction[site]; // ClimateRegionData.StormFlowFraction[ecoregion];
            double baseFlowFraction  = SiteVars.SoilBaseFlowFraction[site];  //ClimateRegionData.BaseFlowFraction[ecoregion];
            double drain             = SiteVars.SoilDrain[site];             // ClimateRegionData.Drain[ecoregion];


            //...Calculating snow pack first. Occurs when mean monthly air temperature is equal to or below freezing,
            //     precipitation is in the form of snow.

            if (tmin <= 0.0) // Use tmin to dictate whether it snows or rains.
            {
                snow            = H2Oinputs;
                H2Oinputs       = 0.0;
                liquidSnowpack += snow;  //only tracking liquidsnowpack (water equivalent) and not the actual amount of snow on the ground (i.e. not snowpack).
                //PlugIn.ModelCore.UI.WriteLine("Let it snow!! snow={0}, liquidsnowpack={1}.", snow, liquidSnowpack);
            }
            else
            {
                soilWaterContent += H2Oinputs;
                //PlugIn.ModelCore.UI.WriteLine("Let it rain and add it to soil! rain={0}, soilWaterContent={1}.", H2Oinputs, soilWaterContent);
            }


            //...Then melt snow if there is snow on the ground and air temperature (tmax) is above minimum.
            if (liquidSnowpack > 0.0 && tmax > 0.0)
            {
                //...Calculate the amount of snow to melt:
                //This relationship ultimately derives from http://www.nps.gov/yose/planyourvisit/climate.htm which described the relationship between snow melting and air temp.
                //Documentation for the regression equation is in spreadsheet called WaterCalcs.xls by M. Lucash
                double snowMeltFraction = Math.Max((tmax * 0.05) + 0.024, 0.0);//This equation assumes a linear increase in the fraction of snow that melts as a function of air temp.

                if (snowMeltFraction > 1.0)
                {
                    snowMeltFraction = 1.0;
                }

                addToSoil = liquidSnowpack * snowMeltFraction; //Amount of liquidsnowpack that melts = liquidsnowpack multiplied by the fraction that melts.

                //Subtracted melted snow from snowpack and add it to the soil
                liquidSnowpack    = liquidSnowpack - addToSoil;
                soilWaterContent += addToSoil;
            }

            //Calculate the max amout of water available to trees, an over-estimate of the water available to trees.  It only reflects precip and melting of precip.
            availableWaterMax = soilWaterContent;

            //...Evaporate water from the snow pack (rewritten by Pulliam 9/94)
            //...Coefficient 0.87 relates to heat of fusion for ice vs. liquid water
            if (liquidSnowpack > 0.0)
            {
                //...Calculate cm of snow that remaining pet energy can evaporate:
                double evaporatedSnow = pet * 0.87;

                //...Don't evaporate more snow than actually exists:
                if (evaporatedSnow > liquidSnowpack)
                {
                    evaporatedSnow = liquidSnowpack;
                }

                liquidSnowpack = liquidSnowpack - evaporatedSnow;

                //...Decrement remaining pet by energy used to evaporate snow:
                remainingPET = pet - evaporatedSnow;

                if (remainingPET < 0.0)
                {
                    remainingPET = 0.0;
                }

                //Subtract evaporated snowfrom the soil water content
                soilWaterContent -= evaporatedSnow;
            }

            //Allow excess water to run off during storm events (stormflow)
            double waterFull = soilDepth * fieldCapacity;  //units of cm

            double waterMovement = 0.0;

            if (soilWaterContent > waterFull)
            {
                waterMovement    = Math.Max((soilWaterContent - waterFull), 0.0); // How much water should move during a storm event, which is based on how much water the soil can hold.
                soilWaterContent = waterFull;

                //...Compute storm flow.
                stormFlow = waterMovement * stormFlowFraction;

                //Subtract stormflow from soil water
                soilWaterContent -= stormFlow;
                //PlugIn.ModelCore.UI.WriteLine("Water Runs Off. stormflow={0}.", stormFlow);
            }

            //...Calculate bare soil water loss and interception  when air temperature is above freezing and no snow cover.
            //...Mofified 9/94 to allow interception when t < 0 but no snow cover, Pulliam
            if (liquidSnowpack <= 0.0)
            {
                //...Calculate total canopy cover and litter, put cap on effects:
                double standingBiomass = liveBiomass + deadBiomass;

                if (standingBiomass > 800.0)
                {
                    standingBiomass = 800.0;
                }
                if (litterBiomass > 400.0)
                {
                    litterBiomass = 400.0;
                }

                //...canopy interception, fraction of  precip (canopyIntercept):
                double canopyIntercept = ((0.0003 * litterBiomass) + (0.0006 * standingBiomass)) * OtherData.WaterLossFactor1;

                //...Bare soil evaporation, fraction of precip (bareSoilEvap):
                bareSoilEvap = 0.5 * System.Math.Exp((-0.002 * litterBiomass) - (0.004 * standingBiomass)) * OtherData.WaterLossFactor2;

                //...Calculate total surface evaporation losses, maximum allowable is 0.4 * pet. -rm 6/94
                remainingPET = pet;
                double soilEvaporation = System.Math.Min(((bareSoilEvap + canopyIntercept) * H2Oinputs), (0.4 * remainingPET));

                //Subtract soil evaporation from soil water content
                soilWaterContent -= soilEvaporation;
            }

            // Calculate actual evapotranspiration.  This equation is derived from the stand equation for calculating AET from PET
            //  Bergström, 1992

            double waterEmpty = wiltingPoint * soilDepth;

            if (soilWaterContent > waterFull)
            {
                actualET = remainingPET;
            }
            else
            {
                actualET = Math.Max(remainingPET * ((soilWaterContent - waterEmpty) / (waterFull - waterEmpty)), 0.0);
            }

            if (actualET < 0.0)
            {
                actualET = 0.0;
            }
            AET = actualET;

            //Subtract transpiration from soil water content
            soilWaterContent -= actualET;

            //Leaching occurs. Drain baseflow fraction from holding tank.
            baseFlow = soilWaterContent * baseFlowFraction;

            //Subtract baseflow from soil water
            soilWaterContent -= baseFlow;

            //Calculate the amount of available water after all the evapotranspiration and leaching has taken place (minimum available water)
            availableWaterMin = Math.Max(soilWaterContent - waterEmpty, 0.0);

            //Calculate the final amount of available water to the trees, which is the average of the max and min
            availableWater = (availableWaterMax + availableWaterMin) / 2.0;

            // Compute the ratio of precipitation to PET
            double ratioPrecipPET = 0.0;

            if (pet > 0.0)
            {
                ratioPrecipPET = availableWater / pet;             //assumes that the ratio is the amount of incoming precip divided by PET.
            }
            SiteVars.AnnualPPT_AET[site]      = H2Oinputs - actualET;
            SiteVars.AnnualSoilMoisture[site] = pet - actualET;
            SiteVars.LiquidSnowPack[site]     = liquidSnowpack;
            SiteVars.WaterMovement[site]      = waterMovement;
            SiteVars.AvailableWater[site]     = availableWater; //available to plants for growth
            SiteVars.SoilWaterContent[site]   = soilWaterContent;
            SiteVars.SoilTemperature[site]    = CalculateSoilTemp(tmin, tmax, liveBiomass, litterBiomass, month);
            SiteVars.DecayFactor[site]        = CalculateDecayFactor((int)OtherData.WType, SiteVars.SoilTemperature[site], relativeWaterContent, ratioPrecipPET, month);
            SiteVars.AnaerobicEffect[site]    = CalculateAnaerobicEffect(drain, ratioPrecipPET, pet, tave);
            if (month == 0)
            {
                SiteVars.DryDays[site] = 0;
            }
            else
            {
                SiteVars.DryDays[site] += CalculateDryDays(month, beginGrowing, endGrowing, waterEmpty, availableWater, priorWaterAvail);
            }

            return;
        }