//---------------------------------------------------------------------
 public static int CalculateDomAgeAgeOnly(Site site)
 {
     Dictionary<int, int> ageDictionary = new Dictionary<int, int>();
     foreach (ISpeciesCohorts sppCohorts in SiteVars.AgeCohorts[site])
     {
         foreach (ICohort cohort in sppCohorts)
         {
             int age = cohort.Age;
             if (ageDictionary.ContainsKey(age))
             {
                 ageDictionary[age] = ageDictionary[age] + 1;
             }
             else
             {
                 ageDictionary[age] = 1;
             }
         }
     }
     int domAge = 0;
     int maxValue = 0;
     foreach (var i in ageDictionary)
     {
         if (i.Value > maxValue)
         {
             domAge = i.Key;
             maxValue = i.Value;
         }
     }
     return domAge;
 }
        //---------------------------------------------------------------------

        /// <summary>
        /// Computes fire effects on litter, coarse woody debris, mineral soil, and charcoal.
        ///   No effects on soil organic matter (negligible according to Johnson et al. 2001).
        /// </summary>
        public static void ReduceLayers(byte severity, Site site)
        {
            //PlugIn.ModelCore.UI.WriteLine("   Calculating fire induced layer reductions...");
        
            double litterLossMultiplier = ReductionsTable[severity].LitterReduction;
            
            // Structural litter first
            
            double carbonLoss = SiteVars.SurfaceStructural[site].Carbon * litterLossMultiplier;
            double nitrogenLoss = SiteVars.SurfaceStructural[site].Nitrogen * litterLossMultiplier;
            double summaryNLoss = nitrogenLoss;
            
            SiteVars.SurfaceStructural[site].Carbon -= carbonLoss;
            SiteVars.SourceSink[site].Carbon        += carbonLoss;
            SiteVars.FireCEfflux[site]               += carbonLoss;
            
            SiteVars.SurfaceStructural[site].Nitrogen -= nitrogenLoss;
            SiteVars.SourceSink[site].Nitrogen += nitrogenLoss;
            SiteVars.FireNEfflux[site] += nitrogenLoss;
            
            // Metabolic litter

            carbonLoss = SiteVars.SurfaceMetabolic[site].Carbon * litterLossMultiplier;
            nitrogenLoss = SiteVars.SurfaceMetabolic[site].Nitrogen * litterLossMultiplier;
            summaryNLoss += nitrogenLoss;
            
            SiteVars.SurfaceMetabolic[site].Carbon  -= carbonLoss;
            SiteVars.SourceSink[site].Carbon        += carbonLoss;
            SiteVars.FireCEfflux[site]               += carbonLoss;
            
            SiteVars.SurfaceMetabolic[site].Nitrogen -= nitrogenLoss;
            SiteVars.SourceSink[site].Nitrogen        += nitrogenLoss;
            SiteVars.FireNEfflux[site] += nitrogenLoss;
            
            // Surface dead wood

            double woodLossMultiplier = ReductionsTable[severity].WoodReduction;
            
            carbonLoss   = SiteVars.SurfaceDeadWood[site].Carbon * woodLossMultiplier;
            nitrogenLoss = SiteVars.SurfaceDeadWood[site].Nitrogen * woodLossMultiplier;
            summaryNLoss += nitrogenLoss;
            
            SiteVars.SurfaceDeadWood[site].Carbon   -= carbonLoss;
            SiteVars.SourceSink[site].Carbon        += carbonLoss;
            SiteVars.FireCEfflux[site]               += carbonLoss;
            
            SiteVars.SurfaceDeadWood[site].Nitrogen -= nitrogenLoss;
            SiteVars.SourceSink[site].Nitrogen        += nitrogenLoss;
            SiteVars.FireNEfflux[site] += nitrogenLoss;

            //SiteVars.MineralN[site] += summaryNLoss * 0.01;  Need to substract N loss from Mineral N pool. -ML
            SiteVars.MineralN[site] -= summaryNLoss * 0.01;

        }
        //New method for calculating N limits, called from Century.cs Run method before calling Grow
        //Iterates through cohorts, assigning each a N gathering efficiency based on fine root biomass
        //and N tolerance.
        public static void CalculateNLimits(Site site)
        {
            // Iterate through the first time, assigning each cohort un un-normalized N multiplier
            double NMultTotal=0.0;
            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
                foreach (ICohort cohort in speciesCohorts)
                {
                    int Ntolerance = SpeciesData.NTolerance[cohort.Species];

                    //NMultiplier is a measure of how much N a cohort can gather relative to other cohorts
                    double NMultiplier = CalculateNMultiplier(cohort.Biomass, Ntolerance);
                    NMultTotal += NMultiplier;
                    Dictionary<int,double> newEntry = new Dictionary<int,double>();
                    newEntry.Add(cohort.Age,NMultiplier);

                    if (CohortNlimits.ContainsKey(cohort.Species.Index))
                    {
                        CohortNlimits[cohort.Species.Index].Add(cohort.Age,NMultiplier);
                    }
                    else
                    {
                        CohortNlimits.Add(cohort.Species.Index,newEntry);
                    }
                }

            double availableN = SiteVars.MineralN[site];  // g/m2

            //Iterate through a second time now that we have total N multiplier
            //Divide through by total and multiply by total available N so each cohort has a max N value
            //and the sum of cohort max N values is the site available N

            double totalNUptake = 0.0;
            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                foreach (ICohort cohort in speciesCohorts)
                {
                    double NMultiplier = CohortNlimits[cohort.Species.Index][cohort.Age];
                    double Nfrac = NMultiplier / NMultTotal;
                    CohortNlimits[cohort.Species.Index][cohort.Age] = Nfrac * availableN;
                    totalNUptake += Nfrac * availableN;
                }
            }
            if (totalNUptake > availableN)
            {
                totalNUptake = availableN;
                //PlugIn.ModelCore.Log.WriteLine("   ERROR:  Total max N uptake = {0:0.000}, availableN = {1:0.000}.", totalNUptake, availableN);
                //throw new ApplicationException("Error: Max N uptake > availableN.  See AvailableN.cs");
            }

            return;
        }
        //--------------------------------------------------------------------------
        public static void Leach(Site site, double baseFlow, double stormFlow)
        {
            //  double minlch, double frlech[3], double stream[8], double basef, double stormf)
            //Originally from leach.f of CENTURY model
            //...This routine computes the leaching of inorganic nitrogen (potential for use with phosphorus, and sulfur)
            //...Written 2/92 -rm. Revised on 12/11 by ML
            // ML left out leaching intensity factor.  Cap on MAX leaching (MINLECH/OMLECH3) is poorly defined in CENTURY manual. Added a NO3frac factor to account
            //for the fact that only NO3 (not NH4) is leached from soils.

            //...Called From:   SIMSOM

            //...amtlea:    amount leached
            //...linten:    leaching intensity
            //...strm:      storm flow
            //...base:      base flow

            //Outputs:
            //minerl and stream are recomputed
            IEcoregion ecoregion = PlugIn.ModelCore.Ecoregion[site];
            double waterMove = SiteVars.WaterMovement[site];

            double amtNLeached = 0.0;

            //PlugIn.ModelCore.UI.WriteLine("WaterMove={0:0}, ", waterMove);

             //...waterMove > 0. indicates a saturated water flow out of layer lyr
            if (waterMove > 0.0 && SiteVars.MineralN[site] > 0.0)
            {
                double textureEffect = OtherData.MineralLeachIntercept + OtherData.MineralLeachSlope * EcoregionData.PercentSand[ecoregion];
                //double leachIntensity = (1.0 - (OtherData.OMLeachWater - waterMove) / OtherData.OMLeachWater);
                //amtNLeached = textureEffect * SiteVars.MineralN[site] * OtherData.NfracLeachWater * OtherData.NO3frac;
                amtNLeached = textureEffect * SiteVars.MineralN[site] *  OtherData.NO3frac;

                //PlugIn.ModelCore.UI.WriteLine("amtNLeach={0:0.0}, textureEffect={1:0.0}, waterMove={2:0.0}, MineralN={3:0.00}", amtNLeached, textureEffect, waterMove, SiteVars.MineralN[site]);
            }

            double totalNleached = (baseFlow * amtNLeached) + (stormFlow * amtNLeached);

            SiteVars.MineralN[site] -= totalNleached;
            //PlugIn.ModelCore.UI.WriteLine("AfterSoilWaterLeaching. totalNLeach={0:0.0}, MineralN={1:0.00}", totalNleached, SiteVars.MineralN[site]);

            SiteVars.Stream[site].Nitrogen += totalNleached;
            SiteVars.MonthlyStreamN[site][Century.Month] += totalNleached;
            //PlugIn.ModelCore.UI.WriteLine("AfterSoilWaterLeaching. totalNLeach={0:0.0}, MineralN={1:0.00}", totalNleached, SiteVars.MineralN[site]);

            return;
        }
Exemplo n.º 5
0
        //---------------------------------------------------------------------
        public static double GetOrganicCarbon(Site site)
        {
            double totalC =

                    SiteVars.SurfaceStructural[site].Carbon
                    + SiteVars.SoilStructural[site].Carbon
                    + SiteVars.SurfaceMetabolic[site].Carbon
                    + SiteVars.SoilMetabolic[site].Carbon

                    + SiteVars.SOM1surface[site].Carbon
                    + SiteVars.SOM1soil[site].Carbon
                    + SiteVars.SOM2[site].Carbon
                    + SiteVars.SOM3[site].Carbon
                    ;

            return totalC;
        }
        //---------------------------------------------------------------------
        // Number of age classes, an indicator of structural complexity.
        public static int GetAgeRichness(Site site)
        {
            int age_richness = 0;
            List<int> ages = new List<int>();
            if (SiteVars.Cohorts[site] == null)
                return 0;
            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                foreach (ICohort cohort in speciesCohorts)
                {
                    if (!ages.Contains(cohort.Age))
                    {
                        ages.Add(cohort.Age);
                        age_richness++;
                    }
                }
            }

            return age_richness;
        }
        //---------------------------------------------------------------------
        //Use E = Hprime / ln S   where S apparently is # species)
        //where Hprime = -sum (pI * ln(pI))   where pI is proportion of individuals found in Ith species
        //from Magurran, A.  1988.  Ecological diversity and its measurements.  Princeton, NJ: Princeton University Press.  Pp 35-37)
        //Return E * 100 to fit within uint range
        public static int GetAgeEvenness(Site site)
        {
            double E = 0;
            double Hprime = 0;
            double proportion=0;
            int evenness = 0;
            int total_count = 0;
            Dictionary<int, int> cohort_counts = new Dictionary<int, int>();
            if (SiteVars.Cohorts[site] == null)
                return 0;
            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                foreach (ICohort cohort in speciesCohorts)
                {
                    total_count++;
                    if (!cohort_counts.ContainsKey((int) cohort.Age))
                    {
                        cohort_counts.Add((int) cohort.Age, 1);
                    }
                    else
                    {
                        cohort_counts[(int) cohort.Age]++;
                    }
                }
            }

            foreach (KeyValuePair<int,int> cohortIter in cohort_counts)
            {
                proportion = (double)cohortIter.Value / (double)total_count;
                Hprime += proportion * System.Math.Log(proportion);
            }
            Hprime = - Hprime;
            E = Hprime / System.Math.Log(cohort_counts.Count);
            evenness = (int)(E * 100.0);

            return evenness;
        }
        //---------------------------------------------------------------------
        public static ushort GetSppMaxAge(Site site, ISpecies spp)
        {
            if (!site.IsActive)
                return 0;

            if (SiteVars.Cohorts[site] == null)
            {
                PlugIn.ModelCore.UI.WriteLine("Cohort are null.");
                return 0;
            }
            ushort max = 0;

            foreach (ISpeciesCohorts sppCohorts in SiteVars.Cohorts[site])
            {
                if (sppCohorts.Species == spp)
                {
                    //ModelCore.UI.WriteLine("cohort spp = {0}, compare species = {1}.", sppCohorts.Species.Name, spp.Name);
                    foreach (ICohort cohort in sppCohorts)
                        if (cohort.Age > max)
                            max = cohort.Age;
                }
            }
            return max;
        }
 //---------------------------------------------------------------------
 public static int GetSppRichness(Site site)
 {
     //return total count of species
     int count = 0;
     if (SiteVars.Cohorts[site] == null)
         return 0;
     foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
     {
         count++;
     }
     return count;
 }
Exemplo n.º 10
0
        //---------------------------------------------------------------------

        private int CalcFuelType(Site site,
                                        IEnumerable<IFuelType> FuelTypes,
                                        IEnumerable<IDisturbanceType> DisturbanceTypes)
        {

            double[] forTypValue = new double[100];  //Maximum of 100 fuel types
            double sumConifer = 0.0;
            double sumDecid = 0.0;
            IEcoregion ecoregion = modelCore.Ecoregion[(ActiveSite) site];

            foreach(ISpecies species in modelCore.Species)
            {

                ISpeciesCohorts speciesCohorts = (Landis.Library.BiomassCohorts.ISpeciesCohorts) SiteVars.Cohorts[site][species];

                if(speciesCohorts == null)
                    continue;

                foreach(IFuelType ftype in FuelTypes)
                {
                    if(ftype.Ecoregions[ecoregion.Index])
                    {

                        if(ftype[species.Index] != 0)
                        {
                            int sppValue = 0;

                            foreach(ICohort cohort in speciesCohorts)
                                if(cohort.Age >= ftype.MinAge && cohort.Age <= ftype.MaxAge)
                                    sppValue += cohort.Biomass;

                            //modelCore.UI.WriteLine("sppVaue={0}, spp={1}, cohortB={2}.", sppValue, cohort.Species.Name, cohort.Biomass);

                            if(ftype[species.Index] == -1)
                                forTypValue[ftype.Index] -= sppValue;

                            if(ftype[species.Index] == 1)
                                forTypValue[ftype.Index] += sppValue;
                        }
                    }
                }

            }



            int finalFuelType = 0;
            int decidFuelType = 0;
            int coniferFuelType = 0;
            int openFuelType = 0;
            int slashFuelType = 0;
            double maxValue = 0.0;
            double maxDecidValue = 0.0;
            double maxConiferValue = 0.0;
            double maxConPlantValue = 0.0;
            double maxOpenValue = 0.0;
            double maxSlashValue = 0.0;

            //Set the PERCENT CONIFER DOMINANCE:
            int coniferDominance = 0;
            int hardwoodDominance = 0;


            //First accumulate data for the various Base Fuel Types:
            foreach(IFuelType ftype in FuelTypes)
            {
                if(ftype != null)
                {

                    // Sum for the Conifer and Deciduous dominance
                    if ((ftype.BaseFuel == BaseFuelType.Conifer || ftype.BaseFuel == BaseFuelType.ConiferPlantation)
                        && forTypValue[ftype.Index] > 0)
                    {
                        sumConifer += forTypValue[ftype.Index];
                    }

                    //This is calculated for the mixed types:
                    if ((ftype.BaseFuel == BaseFuelType.Deciduous)
                        && forTypValue[ftype.Index] > 0)
                    {
                        sumDecid += forTypValue[ftype.Index];
                    }

                    // CONIFER
                    if(forTypValue[ftype.Index] > maxConiferValue && ftype.BaseFuel == BaseFuelType.Conifer)
                    {

                        maxConiferValue = forTypValue[ftype.Index];
                        if(maxConiferValue > maxConPlantValue)
                            coniferFuelType = ftype.Index;
                    }
                    // CONIFER PLANTATION
                    if (forTypValue[ftype.Index] > maxConPlantValue && ftype.BaseFuel == BaseFuelType.ConiferPlantation)
                    {

                        maxConPlantValue = forTypValue[ftype.Index];
                        if(maxConPlantValue > maxConiferValue)
                            coniferFuelType = ftype.Index;
                    }

                    // OPEN
                    if (forTypValue[ftype.Index] > maxOpenValue && ftype.BaseFuel == BaseFuelType.Open)
                    {

                        maxOpenValue = forTypValue[ftype.Index];
                        openFuelType = ftype.Index;
                    }

                    // SLASH
                    if (forTypValue[ftype.Index] > maxSlashValue && ftype.BaseFuel == BaseFuelType.Slash)
                    {

                        maxSlashValue = forTypValue[ftype.Index];
                        slashFuelType = ftype.Index;
                    }

                    // DECIDUOUS
                    if(forTypValue[ftype.Index] > maxDecidValue && ftype.BaseFuel == BaseFuelType.Deciduous)
                    {

                        maxDecidValue = forTypValue[ftype.Index];
                        decidFuelType = ftype.Index;
                    }

                }
            }

            // Rules indicating a CONIFER cell
            if (maxConiferValue >= maxConPlantValue
                && maxConiferValue >= maxDecidValue
                && maxConiferValue >= maxOpenValue
                && maxConiferValue >= maxSlashValue)
            {
                maxValue = maxConiferValue;
            }

            // Rules indicating a DECIDUOUS cell
            else if (maxDecidValue >= maxConiferValue
                    && maxDecidValue >= maxConPlantValue
                    && maxDecidValue >= maxOpenValue
                    && maxDecidValue >= maxSlashValue)
            {
                maxValue = maxDecidValue;
            }

            // Rules indicating a CONIFER PLANTATION cell
            else if (maxConPlantValue >= maxConiferValue
                    && maxConPlantValue >= maxDecidValue
                    && maxConPlantValue >= maxOpenValue
                    && maxConPlantValue >= maxSlashValue)
            {
                maxValue = maxConPlantValue;
                finalFuelType = coniferFuelType;
                decidFuelType = 0;
                sumConifer = 100;
                sumDecid = 0;
            }

            // Rules indicating a SLASH cell
            else if (maxSlashValue >= maxConiferValue
                    && maxSlashValue >= maxConPlantValue
                    && maxSlashValue >= maxDecidValue
                    && maxSlashValue >= maxOpenValue)
            {
                maxValue = maxSlashValue;
                finalFuelType = slashFuelType;
                decidFuelType = 0;
                sumConifer = 0;
                sumDecid = 0;
            }

            // Rules indicating an OPEN (typically grass) cell
            else if (maxOpenValue >= maxConiferValue
                    && maxOpenValue >= maxConPlantValue
                    && maxOpenValue >= maxDecidValue
                    && maxOpenValue >= maxSlashValue)
            {
                maxValue = maxOpenValue;
                finalFuelType = openFuelType;
                decidFuelType = 0;
                sumConifer = 0;
                sumDecid = 0;
            }


            //Set the PERCENT DOMINANCE values:
            if (sumConifer > 0 || sumDecid > 0)
            {
                coniferDominance = (int)((sumConifer / (sumConifer + sumDecid) * 100) + 0.5);
                hardwoodDominance = (int)((sumDecid / (sumConifer + sumDecid) * 100) + 0.5);
                if (hardwoodDominance < hardwoodMax)
                {
                    coniferDominance = 100;
                    hardwoodDominance = 0;
                    finalFuelType = coniferFuelType;
                    decidFuelType = 0;
                }
                if (coniferDominance < hardwoodMax)
                {
                    coniferDominance = 0;
                    hardwoodDominance = 100;
                    finalFuelType = decidFuelType;
                    decidFuelType = 0;
                }
                if (hardwoodDominance > hardwoodMax && coniferDominance > hardwoodMax)
                    finalFuelType = coniferFuelType;
            }

            //---------------------------------------------------------------------
            // Next check the disturbance types.  This will override any other existing fuel type.
            foreach(DisturbanceType slash in DisturbanceTypes)
            {
                //if (SiteVars.HarvestCohortsKilled != null && SiteVars.HarvestCohortsKilled[site] > 0)
                //{
                    if (SiteVars.TimeOfLastHarvest != null &&
                        (modelCore.CurrentTime - SiteVars.TimeOfLastHarvest[site] <= slash.MaxAge))
                    {
                        foreach (string pName in slash.PrescriptionNames)
                        {
                            if (SiteVars.HarvestPrescriptionName != null && SiteVars.HarvestPrescriptionName[site].Trim() == pName.Trim())
                            {
                                finalFuelType = slash.FuelIndex; //Name;
                                decidFuelType = 0;
                                coniferDominance = 0;
                                hardwoodDominance = 0;
                            }
                        }
                    }
                //}
                //Check for fire severity effects of fuel type
                if (SiteVars.FireSeverity != null && SiteVars.FireSeverity[site] > 0)
                {
                    if (SiteVars.TimeOfLastFire != null &&
                        (modelCore.CurrentTime - SiteVars.TimeOfLastFire[site] <= slash.MaxAge))
                    {
                        foreach (string pName in slash.PrescriptionNames)
                        {
                            if (pName.StartsWith("FireSeverity"))
                            {
                                if((pName.Substring((pName.Length - 1), 1)).ToString() == SiteVars.FireSeverity[site].ToString())
                                {
                                    finalFuelType = slash.FuelIndex; //Name;
                                    decidFuelType = 0;
                                    coniferDominance = 0;
                                    hardwoodDominance = 0;
                                }
                            }
                        }
                    }
                }
                //Check for wind severity effects of fuel type
                if (SiteVars.WindSeverity != null && SiteVars.WindSeverity[site] > 0)
                {
                    if (SiteVars.TimeOfLastWind != null &&
                        (modelCore.CurrentTime - SiteVars.TimeOfLastWind[site] <= slash.MaxAge))
                    {
                        foreach (string pName in slash.PrescriptionNames)
                        {
                            if (pName.StartsWith("WindSeverity"))
                            {
                                if ((pName.Substring((pName.Length - 1), 1)).ToString() == SiteVars.WindSeverity[site].ToString())
                                {
                                    finalFuelType = slash.FuelIndex; //Name;
                                    decidFuelType = 0;
                                    coniferDominance = 0;
                                    hardwoodDominance = 0;
                                }
                            }
                        }
                    }
                }
            }

            SiteVars.PercentConifer[site]   = coniferDominance;
            SiteVars.PercentHardwood[site]  = hardwoodDominance;

            SiteVars.FuelType[site]         = finalFuelType;
            SiteVars.DecidFuelType[site]    = decidFuelType;

            return finalFuelType;

        }
Exemplo n.º 11
0
        private static double GetTotalSoilNitrogen(Site site)
        {
            double totalsoilN =

                    +SiteVars.MineralN[site]

                   //+ SiteVars.SurfaceDeadWood[site].Nitrogen
                   //+ SiteVars.SoilDeadWood[site].Nitrogen

                    + SiteVars.SurfaceStructural[site].Nitrogen
                    + SiteVars.SoilStructural[site].Nitrogen
                    + SiteVars.SurfaceMetabolic[site].Nitrogen
            +SiteVars.SoilMetabolic[site].Nitrogen

            + SiteVars.SOM1surface[site].Nitrogen
            + SiteVars.SOM1soil[site].Nitrogen
            + SiteVars.SOM2[site].Nitrogen
            + SiteVars.SOM3[site].Nitrogen;
                    ;

            return totalsoilN;
        }
 //---------------------------------------------------------------------
 public static int GetCohortCount(Site site)
 {
     //return total count of cohorts
     int count = 0;
     if (SiteVars.Cohorts[site] == null)
         return 0;
     foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
     {
         foreach (ICohort cohort in speciesCohorts)
         {
             count++;
         }
     }
     return count;
 }
        //---------------------------------------------------------------------
        public static int GetAvgAge(Site site)
        {
            if (SiteVars.Cohorts[site] == null)
                return 0;
            int avg = 0;
            int sum = 0;
            int count = 0;

            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                foreach (ICohort cohort in speciesCohorts)
                {
                    sum += cohort.Age;
                    count++;
                }
            }

            if (count == 0)
            {
                return 0;
            }

            avg = (int)(sum / count);
            return avg;
        }
        //---------------------------------------------------------------------
        public static void ResetAnnualValues(Site site)
        {
            // Reset these accumulators to zero:
            SiteVars.CohortLeafN[site] = 0.0;
            SiteVars.CohortLeafC[site] = 0.0;
            SiteVars.CohortWoodN[site] = 0.0;
            SiteVars.CohortWoodC[site] = 0.0;
            SiteVars.GrossMineralization[site] = 0.0;
            SiteVars.AGNPPcarbon[site] = 0.0;
            SiteVars.BGNPPcarbon[site] = 0.0;
            SiteVars.LitterfallC[site] = 0.0;

            SiteVars.Stream[site]          = new Layer(LayerName.Other, LayerType.Other);
            SiteVars.SourceSink[site]      = new Layer(LayerName.Other, LayerType.Other);

            SiteVars.SurfaceDeadWood[site].NetMineralization = 0.0;
            SiteVars.SurfaceStructural[site].NetMineralization = 0.0;
            SiteVars.SurfaceMetabolic[site].NetMineralization = 0.0;

            SiteVars.SoilDeadWood[site].NetMineralization = 0.0;
            SiteVars.SoilStructural[site].NetMineralization = 0.0;
            SiteVars.SoilMetabolic[site].NetMineralization = 0.0;

            SiteVars.SOM1surface[site].NetMineralization = 0.0;
            SiteVars.SOM1soil[site].NetMineralization = 0.0;
            SiteVars.SOM2[site].NetMineralization = 0.0;
            SiteVars.SOM3[site].NetMineralization = 0.0;
            SiteVars.AnnualNEE[site] = 0.0;
            SiteVars.Nvol[site] = 0.0;
            SiteVars.AnnualNEE[site] = 0.0;
            SiteVars.TotalNuptake[site] = 0.0;
            SiteVars.ResorbedN[site] = 0.0;
            SiteVars.FrassC[site] = 0.0;
            SiteVars.LAI[site] = 0.0;
            SiteVars.AgeMortality[site] = 0.0;

            //SiteVars.FireEfflux[site] = 0.0;
        }
        // Calculates how much N a cohort gets, based on the amount of N available.
        public static void SetMineralNallocation(Site site)
        {
            AvailableN.CohortMineralNallocation = new Dictionary<int, Dictionary<int, double>>();

               double availableN = SiteVars.MineralN[site];  // g/m2
               Math.Max(availableN, 0.01);

            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                foreach (ICohort cohort in speciesCohorts)
                {
                    int cohortAddYear = GetAddYear(cohort);
                    if (Century.MonthCnt == 11)
                        cohortAddYear--;
                    //PlugIn.ModelCore.UI.WriteLine("SETMineralNallocation: year={0}, mo={1}, species={2}, cohortAge={3}, cohortAddYear={4}.", PlugIn.ModelCore.CurrentTime, Century.Month, cohort.Species.Name, cohort.Age, cohortAddYear);

                    double Nfraction = 0.05;  //even a new cohort gets a little love
                    Dictionary<int, double> cohortDict = new Dictionary<int,double>();

                    if (AvailableN.CohortMineralNfraction.TryGetValue(cohort.Species.Index, out cohortDict))
                        cohortDict.TryGetValue(cohortAddYear, out Nfraction);

                    double Nallocation = Nfraction * availableN;
                    //PlugIn.ModelCore.UI.WriteLine("  NallocationlimitedbymineralN={0:0.00}, Nfraction={1:0.00}, availableN={2:0.00}.", Nallocation, Nfraction, availableN);

                    if (Double.IsNaN(Nallocation) || Double.IsNaN(Nfraction) || Double.IsNaN(availableN))
                    {
                        PlugIn.ModelCore.UI.WriteLine("  LIMIT N CALCULATION = NaN!  ");
                        PlugIn.ModelCore.UI.WriteLine("  Nallocation={0:0.00}, Nfraction={1:0.00}, availableN={2:0.00}.", Nallocation, Nfraction, availableN);
                    }

                    Dictionary<int, double> newEntry = new Dictionary<int, double>();
                    newEntry.Add(cohortAddYear, Nallocation);

                    if (CohortMineralNallocation.ContainsKey(cohort.Species.Index))
                    {
                        CohortMineralNallocation[cohort.Species.Index].Add(cohortAddYear, Nallocation);
                    }
                    else
                    {
                        CohortMineralNallocation.Add(cohort.Species.Index, newEntry);
                    }
                }
            }
            /*if (totalNUptake > availableN)
            {
                totalNUptake = availableN;
                //PlugIn.ModelCore.UI.WriteLine("   ERROR:  Total max N uptake = {0:0.000}, availableN = {1:0.000}.", totalNUptake, availableN);
                //throw new ApplicationException("Error: Max N uptake > availableN.  See AvailableN.cs");
            }
            SiteVars.TotalNuptake[site] = totalNUptake;*/
        }
        public static void Run(int year, int month, double liveBiomass, Site site)
        {
            //PlugIn.ModelCore.Log.WriteLine("year = {0}, month = {1}", year, month);

            //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

            //...Description of variables
            //
            //   deadBiomass        the average monthly standing dead biomass(gm/m..2)
            //   soilDepth          depth of the ith soil layer(cm)
            //   fieldCapacity      the field capacity of the ith soil layer(fraction)
            //   litterBiomass      the average monthly litter biomass(gm/m..2)
            //   liveBiomass        the average monthly live plant biomass(gm/m..2)
            //   waterMovement      the index for water movement(0-no flow,1-satruated flow)
            //   soilWaterContent   the soil water content of the ith soil layer(cm h2o)
            //   asnow              the snow pack water contint(cm-h2o)
            //   avh2o (1)          NA water available to plants for growth
            //   avh2o (2)          NA water available to plants for survival
            //                      (available water in the whole soil profile)
            //   availableWater     available water in current soil layer
            //   wiltingPoint       the wilting point of the  ith soil layer(fraction)
            //   transpLossFactor   the weight factor for transpiration water loss
            //   totalEvaporated               the water evaporated from the  soil and vegetation(cm/mon)
            //   evaporatedSnow             snow evaporated
            //   inputs             rain + irrigation
            //   H2Oinputs            inputs which are water (not converted to snow)
            //   nlayer             NA number of soil layers with water available for plant survival
            //   nlaypg             NA number of soil layers with water available for plant growth
            //   remainingPET       remaining pet, updated after each incremental h2o loss
            //   potentialEvapTop               the potential evaporation rate from the top  soil layer (cm/day)
            //   rain               the total monthly rainfall (cm/month)
            //   relativeWaterContent        the relative water content of the ith soil layer(0-1)
            //   liquidSnowpack               the liquid water in the snow pack
            //   tav                average monthly air temperature (2m-        //)
            //   tran               transpriation water loss(cm/mon)
            //   transpirationLoss  transpiration water loss

            //...Initialize Local Variables
            double addToSoil = 0.0;
            double bareSoilEvap = 0.0;
            double baseFlow = 0.0;
            double totalEvaporated = 0.0;
            double evaporativeLoss = 0.0;
            double potentialTrans = 0.0;
            double relativeWaterContent = 0.0;
            //double rwc1 = 0.0;
            double snow = 0.0;
            double liquidSnowpack = 0.0;
            double stormFlow = 0.0;
            //double tot = 0.0;
            //double tot2 = 0.0;
            //double totalAvailableWater = 0.0;
            double tran = 0.0;
            //double transpirationLoss = 0.0;
            double transpiration = 0.01;

            //...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 * 2.0;
            double soilWaterContent = SiteVars.SoilWaterContent[site] ;

            double H2Oinputs        = EcoregionData.AnnualWeather[ecoregion].MonthlyPrecip[month]; //rain + irract;
            double tave             = EcoregionData.AnnualWeather[ecoregion].MonthlyTemp[month];
            double tmax             = EcoregionData.AnnualWeather[ecoregion].MonthlyMaxTemp[month];
            double tmin             = EcoregionData.AnnualWeather[ecoregion].MonthlyMinTemp[month];
            double pet              = EcoregionData.AnnualWeather[ecoregion].MonthlyPET[month];
            //double soilTemp         = tave;

            double wiltingPoint     = EcoregionData.WiltingPoint[ecoregion];
            double soilDepth        = EcoregionData.SoilDepth[ecoregion];
            double fieldCapacity    = EcoregionData.FieldCapacity[ecoregion];
            double stormFlowFraction= EcoregionData.StormFlowFraction[ecoregion];
            double baseFlowFraction = EcoregionData.BaseFlowFraction[ecoregion];
            double drain            = EcoregionData.Drain[ecoregion];

            deadBiomass = 0.0;

            //...Throughout, uses remainingPET as remaining energy for pet after
            //     each melting and evaporation step.  Initially calculated
            //     pet is not modified.  Pulliam 9/94
            double remainingPET = pet;

            //...Determine the snow pack, melt snow, and evaporate from the snow pack
            //...When mean monthly air temperature is below freezing,
            //     precipitation is in the form of snow.
            if (tave < 0.0)
            {
                snow = H2Oinputs; //snow + inputs;
                H2Oinputs = 0.0;
            }

            //...Melt snow if air temperature is above minimum (tmelt(1))
            if (tave > OtherData.TMelt1)
            {
                //...Calculate the amount of snow to melt:
                double snowMelt = OtherData.TMelt2 * (tave - OtherData.TMelt1);

                if (snowMelt > snow)
                    snowMelt = snow;
                snow = snow - snowMelt;

                //..Melted snow goes to snow pack and drains excess
                //  addToSoil rain-on-snow  and melted snow to snowpack liquid (liquidSnowpack):
                if (tave > 0.0 && snow > 0.0)
                    liquidSnowpack = H2Oinputs;

                liquidSnowpack = liquidSnowpack + snowMelt;

                //...Drain snowpack to 5% liquid content (weight/weight), excess to soil:
                if (liquidSnowpack > (0.05 * snow))
                {
                    addToSoil = liquidSnowpack - 0.05 * snow;
                    liquidSnowpack = liquidSnowpack - addToSoil;
                }
            }

            //...Evaporate water from the snow pack (rewritten Pulliam 9/94 to
            //     evaporate from both snow aqnd liquidSnowpack in proportion)
            //...Coefficient 0.87 relates to heat of fusion for ice vs. liquid water
            //     wasn't modified as snow pack is at least 95% ice.
            if (snow > 0)
            {
                //...Calculate cm of snow that remaining pet energy can evaporate:
                double evaporatedSnow = remainingPET * 0.87;

                //...Calculate total snowpack water, ice + liquid:
                double totalSnowpack = snow + liquidSnowpack;

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

                //...Take evaporatedSnow from snow and liquidSnowpack in proportion:
                snow = snow - evaporatedSnow * (snow / totalSnowpack);
                liquidSnowpack = liquidSnowpack - evaporatedSnow * (liquidSnowpack/totalSnowpack);

                //...addToSoil evaporated snow to evaporation accumulator (totalEvaporated):
                totalEvaporated = evaporatedSnow;

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

                if (remainingPET < 0.0)
                    remainingPET = 0.0;
            }

            //...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 (snow <= 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;
                //PlugIn.ModelCore.Log.WriteLine("bareSoilEvap={0}, litterBiomass={1}, standingBiomass={2}.", bareSoilEvap, litterBiomass, standingBiomass);

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

                //...Calculate remaining water to addToSoil to soil and potential
                //     transpiration as remaining pet:
                addToSoil = H2Oinputs - evaporativeLoss;
                transpiration = remainingPET - evaporativeLoss;
                //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, evaporativeLoss={2:0.0}, addToSoil={3:0.0}, remainingPET={4:0.0}", year, month, evaporativeLoss, addToSoil, remainingPET);

            }

            // **************************************************************************
            //...Determine potential transpiration water loss (transpiration, cm/mon) as a
            //     function of precipitation and live biomass.
            //...If temperature is less than 2C turn off transpiration. -rm 6/94
            if (tave < 2.0)
                potentialTrans = 0.0;
            else
                potentialTrans = remainingPET * 0.65 * (1.0 - System.Math.Exp(-0.020 * liveBiomass));

            if (potentialTrans < transpiration)
                transpiration = potentialTrans;

            if (transpiration < 0.0) transpiration = 0.01;

            // **************************************************************************
            //...Calculate the potential evaporation rate from the top soil layer
            //     (potentialEvapTop-cm/day).  This is not actually taken out until after
            //     transpiration losses
            double potentialEvapTop = remainingPET - transpiration - evaporativeLoss;

            //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, PotentialEvapTop={2:0.0}, remainingPET={3:0.0}, transpiration={4:0.0}, evaporativeLoss={5:0.0}.", year, month, potentialEvapTop, remainingPET, transpiration, evaporativeLoss);
            if (potentialEvapTop < 0.0) potentialEvapTop = 0.0;

            // **************************************************************************
            //...Transpire water from added water first, before passing water
            //     on to soil.  This is necessary for a monthly time step to
            //     give plants in wet climates adequate access to water for
            //     transpiration. -rm 6/94, Pulliam 9/94
            tran = System.Math.Min((transpiration - 0.01), addToSoil);
            transpiration = transpiration - tran;
            //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, tran={2:0.0}, transpiration={3:0.0}, addToSoil={4:0.00}.", year, month, tran, transpiration, addToSoil);
            addToSoil = addToSoil - tran;

            //...Add water to the soil
            //...Changed to add base flow and storm flow.  -rm 2/92

            //...addToSoil water to layer:

            soilWaterContent += addToSoil;
            //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, soilWaterContent={2:0.0}, addToSoil={3:0.0}.", year, month, soilWaterContent,addToSoil);

             //...Calculate field capacity of soil, drain soil, pass excess
             //     on to waterMovement:
            double waterFull = soilDepth * fieldCapacity;
            double waterMovement = 0.0;

            if (soilWaterContent  > waterFull)
            {
                //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, soilWaterContent > waterFull", year, month);
                 waterMovement  = soilWaterContent  - waterFull;
                 soilWaterContent  = waterFull;

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

            //...Compute base flow and stream flow for H2O.
            //...Put water draining out bottom that doesn't go to stormflow
            //     into nlayer+1 holding tank:
            double drainedWater = addToSoil - stormFlow;

            //...Drain baseflow fraction from holding tank:
            baseFlow     = drainedWater * baseFlowFraction;
            drainedWater = drainedWater - baseFlow;

            //...Streamflow = stormflow + baseflow:
            double streamFlow = stormFlow + baseFlow;

            //...Save soilWaterContent before transpiration for future use:
            double asimx = soilWaterContent;

            // **************************************************************************
            //...Calculate transpiration water loss from each layer
            //...This section was completely rewritten by Pulliam, though it
            //     should still do the same thing.  9/94

            //...Calculate available water in layer, soilWaterContent minus wilting point:
            double availableWater = soilWaterContent - wiltingPoint  * soilDepth;
            //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, availableWater={2:0.0}, soilWaterContent={3:0.0}.", year, month, availableWater, soilWaterContent);

            if (availableWater < 0.0)
                availableWater = 0.0;

            //...Calculate available water weighted by transpiration loss depth
            //      distribution factors:
            double availableWaterWeighted  = availableWater * OtherData.TranspirationLossFactor;

            //...Calculate the actual transpiration water loss(tran-cm/mon)
            //...Also rewritten by Pulliam 9/94, should do the same thing
            //...Update potential transpiration to be no greater than available water:
            transpiration = System.Math.Min(availableWater, transpiration);

            //...Transpire water from layer:

            if(availableWaterWeighted > 0.0)
            {
                //...Calculate available water in layer j:
                //for( j = 0; j < nlayer; j++)
                 availableWater = soilWaterContent - wiltingPoint * soilDepth ;
                 //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, availableWater={2:0.0}, SWC={3:0.0}", year, month, availableWater, soilWaterContent);

                 if (availableWater < 0.0) availableWater = 0.0;

                 //...Calculate transpiration loss from layer, using weighted
                 //     water availabilities:
                 double transpirationLoss = (transpiration * availableWaterWeighted ) / availableWaterWeighted;

                 if (transpirationLoss > availableWater)
                    transpirationLoss = availableWater;

                 soilWaterContent  -= transpirationLoss;
                 availableWater    -= transpirationLoss;
                 //tran = tran + transpirationLoss;  // ????
                 relativeWaterContent  = ((soilWaterContent /soilDepth) - wiltingPoint ) / (fieldCapacity-wiltingPoint);

                //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, TranspirationLoss={2:0.0}, availableWater={3:0.0}, soilwaterContent={4:0.00}, relativeWaterContent={5:0.00}.", year, month, transpirationLoss, availableWater, soilWaterContent, relativeWaterContent);
                //...Sum up water available to plants for GROWTH:
                 //if (j <= nlaypg)
                 // avh2o[1] = avh2o[1] + avinj;

                 //...Sum up water available to plants for SURVIVAL:
                 // avh2o[2] = avh2o[2] + avinj;

                 //...Calculate parameter of H2O accumulation in top 2 soil layers:
                 // if (j <= 2)
                 //totalAvailableWater = totalAvailableWater + avinj;
            }

            // **************************************************************************
            //...Evaporate water from the top layer
            //...Rewritten by Pulliam, should still do the same thing 9/94

            //...Minimum relative water content for top layer to evaporate:
            double fwlos = 0.25;

            //...Fraction of water content between fwlos and field capacity:
            double evmt = (relativeWaterContent - fwlos) / (1.0 - fwlos);
            //PlugIn.ModelCore.Log.WriteLine("evmt={0:0.0}, relativeWaterContent={1:0.00}, fwlos={2}.", evmt,relativeWaterContent,fwlos);

            if (evmt <= 0.01) evmt = 0.01;

            //...Evaporation loss from layer 1:
            double evapLossTop = evmt * potentialEvapTop * bareSoilEvap * 0.10;

            double topWater = soilWaterContent - wiltingPoint * soilDepth;
            if (topWater < 0.0)         topWater       = 0.0;  //topWater = max evaporative loss from surface
            if (evapLossTop > topWater) evapLossTop = topWater;

            //...Update available water pools minus evaporation from top layer
            availableWater   -= evapLossTop;
            soilWaterContent -= evapLossTop;

            //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, evapLossTop={2:0.00}, SWC={3:0.00}", year, month, evapLossTop, soilWaterContent);
            //totalEvaporated += evapLossTop;

            //...Recalculate relative Water Content to estimate mid-month water content
            double avhsm = (soilWaterContent + relativeWaterContent * asimx) / (1.0 + relativeWaterContent);
            relativeWaterContent = ((avhsm / soilDepth) - wiltingPoint) / (fieldCapacity - wiltingPoint);

            //RMS: PlugIn.ModelCore.Log.WriteLine("Yr={0},Mo={1}, soilwaterContent={2:0.00}, relativeWaterContent={3:0.00}.", year, month,soilWaterContent, relativeWaterContent);

            // Compute the ratio of precipitation to PET
            double ratioPrecipPET = 0.0;
            if(pet > 0.0)  ratioPrecipPET = (availableWater + H2Oinputs) / pet;

            //PlugIn.ModelCore.Log.WriteLine("ratioPrecipPET={0}, totalAvailableH20={1}, H2Oinputs={2}, pet={3}.", ratioPrecipPET, totalAvailableWater, H2Oinputs, pet);

            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);

            //SiteVars.StormFlow[site] = stormFlow;

            //Leach(site, stormFlow, baseFlow);

            //PlugIn.ModelCore.Log.WriteLine("availH2O={0}, soilH2O={1}, wiltP={2}, soilCM={3}", availableWater, soilWaterContent, wiltingPoint, soilDepth);
            //PlugIn.ModelCore.Log.WriteLine("   yr={0}, mo={1}, DecayFactor={2:0.00}, Anaerobic={3:0.00}.", year, month, SiteVars.DecayFactor[site], SiteVars.AnaerobicEffect[site]);

            return;
        }
Exemplo n.º 17
0
        //---------------------------------------------------------------------

        private int CalcFuelType(Site site,
                                        IEnumerable<IFuelType> FuelTypes,
                                        IEnumerable<IDisturbanceType> DisturbanceTypes)
        {

            double[] forTypValue = new double[100];  //Maximum of 100 fuel types
            double sumConifer = 0.0;
            double sumDecid = 0.0;

            ISpeciesDataset SpeciesDataset = modelCore.Species;
            foreach(ISpecies species in SpeciesDataset)
            {

                // This is the new algorithm, based on where a cohort is within it's age range.
                // This algorithm is less biased towards older cohorts.
                ISpeciesCohorts speciesCohorts = SiteVars.Cohorts[site][species];

                if(speciesCohorts == null)
                    continue;

                foreach(IFuelType ftype in FuelTypes)
                {

                    if(ftype[species.Index] != 0)
                    {
                        double sppValue = 0.0;

                        foreach(ICohort cohort in speciesCohorts)
                        {
                            double cohortValue =0.0;


                            if(cohort.Age >= ftype.MinAge && cohort.Age <= ftype.MaxAge)
                            {
                                // Adjust max range age to the spp longevity
                                double maxAge = System.Math.Min(ftype.MaxAge, (double) species.Longevity);

                                // The fuel type range must be at least 5 years:
                                double ftypeRange = System.Math.Max(1.0, maxAge - (double) ftype.MinAge);

                                // The cohort age relative to the fuel type range:
                                double relativeCohortAge = System.Math.Max(1.0, (double) cohort.Age - ftype.MinAge);

                                cohortValue = relativeCohortAge / ftypeRange * fuelCoefs[species.Index];

                                // Use the one cohort with the largest value:
                                //sppValue += System.Math.Max(sppValue, cohortValue);  // A BUG, should be...
                                sppValue = System.Math.Max(sppValue, cohortValue);
                            }
                        }

                        if(ftype[species.Index] == -1)
                            forTypValue[ftype.FuelIndex] -= sppValue;
                        if(ftype[species.Index] == 1)
                            forTypValue[ftype.FuelIndex] += sppValue;
                    }
                }

            }

            int finalFuelType = 0;
            int decidFuelType = 0;
            double maxValue = 0.0;
            double maxDecidValue = 0.0;

            //Set the PERCENT CONIFER DOMINANCE:
            int coniferDominance = 0;
            int hardwoodDominance = 0;


            //First accumulate data for the BASE fuel types:
            foreach(IFuelType ftype in FuelTypes)
            {
                if(ftype != null)
                {

                    if ((ftype.BaseFuel == BaseFuelType.Conifer || ftype.BaseFuel == BaseFuelType.ConiferPlantation)
                        && forTypValue[ftype.FuelIndex] > 0)
                    {
                        sumConifer += forTypValue[ftype.FuelIndex];
                    }

                    //This is calculated for the mixed types:
                    if ((ftype.BaseFuel == BaseFuelType.Deciduous)
                        && forTypValue[ftype.FuelIndex] > 0)
                    {
                        sumDecid += forTypValue[ftype.FuelIndex];
                    }

                    if(forTypValue[ftype.FuelIndex] > maxValue)
                    {
                        maxValue = forTypValue[ftype.FuelIndex];
                        finalFuelType = ftype.FuelIndex;
                    }

                    if(ftype.BaseFuel == BaseFuelType.Deciduous && forTypValue[ftype.FuelIndex] > maxDecidValue)
                    {
                        maxDecidValue = forTypValue[ftype.FuelIndex];
                        decidFuelType = ftype.FuelIndex;
                    }

                }
            }

            // Next, use rules to modify the conifer and deciduous dominance:


            foreach(IFuelType ftype in FuelTypes)
            {
                if(ftype != null)
                {

                    if(ftype.FuelIndex == finalFuelType && ftype.BaseFuel == BaseFuelType.ConiferPlantation)
                    {
                        decidFuelType = 0;
                        sumConifer = 100;
                        sumDecid = 0;
                    }

                    // a SLASH type
                    else if(ftype.FuelIndex == finalFuelType && ftype.BaseFuel == BaseFuelType.Slash)
                    {
                        //maxValue = maxSlashValue;
                        //finalFuelType = slashFuelType;
                        //decidFuelType = 0;
                        sumConifer = 0;
                        sumDecid = 0;
                    }

            // an OPEN type
                    else if(ftype.FuelIndex == finalFuelType && ftype.BaseFuel == BaseFuelType.Open)
                    {
                        //maxValue = maxOpenValue;
                        //finalFuelType = openFuelType;
                        //decidFuelType = 0;
                        sumConifer = 0;
                        sumDecid = 0;
                    }

                }
            }
            //Set the PERCENT DOMINANCE values:
            if (sumConifer > 0 || sumDecid > 0)
            {
                coniferDominance = (int)((sumConifer / (sumConifer + sumDecid) * 100) + 0.5);
                hardwoodDominance = (int)((sumDecid / (sumConifer + sumDecid) * 100) + 0.5);
                if (hardwoodDominance < hardwoodMax)
                {
                    coniferDominance = 100;
                    hardwoodDominance = 0;
                }
                if (coniferDominance < hardwoodMax)
                {
                    coniferDominance = 0;
                    hardwoodDominance = 100;
                    finalFuelType = decidFuelType;
                }
            }

            //---------------------------------------------------------------------
            // Next check the disturbance types.  This will override any other existing fuel type.
            foreach(DisturbanceType slash in DisturbanceTypes)
            {
                //if (SiteVars.HarvestCohortsKilled != null && SiteVars.HarvestCohortsKilled[site] > 0)
                //{
                    if (SiteVars.TimeOfLastHarvest != null &&
                        (modelCore.CurrentTime - SiteVars.TimeOfLastHarvest[site] <= slash.MaxAge))
                    {
                        foreach (string pName in slash.PrescriptionNames)
                        {
                            if (SiteVars.HarvestPrescriptionName != null && SiteVars.HarvestPrescriptionName[site].Trim() == pName.Trim())
                            {
                                finalFuelType = slash.FuelIndex; //Name;
                                decidFuelType = 0;
                                coniferDominance = 0;
                                hardwoodDominance = 0;
                            }
                        }
                    }
                //}
                //Check for fire severity effects of fuel type
                if (SiteVars.FireSeverity != null && SiteVars.FireSeverity[site] > 0)
                {
                    if (SiteVars.TimeOfLastFire != null &&
                        (modelCore.CurrentTime - SiteVars.TimeOfLastFire[site] <= slash.MaxAge))
                    {
                        foreach (string pName in slash.PrescriptionNames)
                        {
                            if (pName.StartsWith("FireSeverity"))
                            {
                                if((pName.Substring((pName.Length - 1), 1)).ToString() == SiteVars.FireSeverity[site].ToString())
                                {
                                    finalFuelType = slash.FuelIndex; //Name;
                                    decidFuelType = 0;
                                    coniferDominance = 0;
                                    hardwoodDominance = 0;
                                }
                            }
                        }
                    }
                }
                //Check for wind severity effects of fuel type
                if (SiteVars.WindSeverity != null && SiteVars.WindSeverity[site] > 0)
                {
                    if (SiteVars.TimeOfLastWind != null &&
                        (modelCore.CurrentTime - SiteVars.TimeOfLastWind[site] <= slash.MaxAge))
                    {
                        foreach (string pName in slash.PrescriptionNames)
                        {
                            if (pName.StartsWith("WindSeverity"))
                            {
                                if ((pName.Substring((pName.Length - 1), 1)).ToString() == SiteVars.WindSeverity[site].ToString())
                                {
                                    finalFuelType = slash.FuelIndex; //Name;
                                    decidFuelType = 0;
                                    coniferDominance = 0;
                                    hardwoodDominance = 0;
                                }
                            }
                        }
                    }
                }
            }

            //Assign Percent Conifer:
            SiteVars.PercentConifer[site] = coniferDominance;
            SiteVars.PercentHardwood[site] = hardwoodDominance;

            SiteVars.FuelType[site] = finalFuelType;
            SiteVars.DecidFuelType[site] = decidFuelType;

            return finalFuelType;

        }
 public static int GetMaxAge(Site site)
 {
     if (SiteVars.Cohorts[site] == null)
         return 0;
     int max = 0;
     foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
     {
         int maxSpeciesAge = GetMaxAge(speciesCohorts.Species, site);
         if (maxSpeciesAge > max)
             max = maxSpeciesAge;
     }
     return max;
 }
        //---------------------------------------------------------------------
        public static int GetMinAge(ISpecies species, Site site)
        {
            if (SiteVars.Cohorts[site] == null)
            {
                PlugIn.ModelCore.UI.WriteLine("Cohort are null.");
                return 0;
            }
            int min = 32767;//maxof uint

            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                if (speciesCohorts.Species == species)
                    foreach (ICohort cohort in speciesCohorts)
                    {
                        if (cohort.Age < min)
                            min = (int) cohort.Age;
                    }
            }
            return min;
        }
 /*public static uint GetMaxAge(ISpecies species, ActiveSite site) //Cohorts speciesCohorts)
 {
     //if (speciesCohorts == null)
     //    return 0;
     uint max = 0;
     foreach (ICohort cohort in speciesCohorts)
     {
         //  First cohort is the oldest
         max = cohort.Age;
         break;
     }
     return max;
 }*/
 //---------------------------------------------------------------------
 public static int GetMinAge(Site site)
 {
     if (SiteVars.Cohorts[site] == null)
         return 0;
     int min = 32767;//maxof uint
     foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
     {
         int minSpeciesAge = GetMinAge(speciesCohorts.Species, site); //Cohorts);
         if (minSpeciesAge < min)
             min = minSpeciesAge;
     }
     return min;
 }
        public static int GetMedianAge(ISpecies species, Site site)
        {
            if (SiteVars.Cohorts[site] == null)
                return 0;
            int median = 0;
            double dbl_median = 0.0;

            List<int> cohort_ages = new List<int>();
            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                if(speciesCohorts.Species == species)
                    foreach (ICohort cohort in speciesCohorts)
                    {
                        cohort_ages.Add((int) cohort.Age);
                    }
            }
            int count = cohort_ages.Count;
            if (count == 0)
            {
                return 0;
            }

            else if (count == 1)
            {
                return cohort_ages[0];
            }

            cohort_ages.Sort();//sorts in ascending order

            if (count % 2 == 0)
            {
                dbl_median = (cohort_ages[count / 2] + cohort_ages[(count / 2) - 1]) / 2.0;
                median = (int)dbl_median;
            }
            else
            {
                median = cohort_ages[count / 2];
            }
            return median;
        }
        //---------------------------------------------------------------------
        public static int GetMaxAge(ISpecies species, Site site)
        {
            if (SiteVars.Cohorts[site] == null)
            {
                PlugIn.ModelCore.UI.WriteLine("Cohort are null.");
                return 0;
            }
            int max = 0;

            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                if(speciesCohorts.Species == species)
                    foreach (ICohort cohort in speciesCohorts)
                    {
                        if (cohort.Age > max)
                            max = (int) cohort.Age;
                    }
            }
            return max;
        }
        //---------------------------------------------------------------------
        private int CalcForestType(List<IForestType> forestTypes, Site site)
        {
            int forTypeCnt = 0;

            double[] forTypValue = new double[forestTypes.Count];

            foreach(ISpecies species in modelCore.Species)
            {
                double sppValue = 0.0;

                if (SiteVars.Cohorts[site] == null)
                    break;

                sppValue = Util.ComputeBiomass(SiteVars.Cohorts[site][species]);

                forTypeCnt = 0;
                foreach(IForestType ftype in forestTypes)
                {
                    if(ftype[species.Index] != 0)
                    {
                        if(ftype[species.Index] == -1)
                            forTypValue[forTypeCnt] -= sppValue;
                        if(ftype[species.Index] == 1)
                            forTypValue[forTypeCnt] += sppValue;
                    }
                    forTypeCnt++;
                }
            }

            int finalForestType = 0;
            double maxValue = 0.0;
            forTypeCnt = 0;
            foreach(IForestType ftype in forestTypes)
            {
                if(forTypValue[forTypeCnt]>maxValue)
                {
                    maxValue = forTypValue[forTypeCnt];
                    finalForestType = forTypeCnt;
                }
                forTypeCnt++;
            }
            return finalForestType;
        }
        public static void Run(int year, int month, double liveBiomass, Site site, out double baseFlow, out double stormFlow)
        {
            //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

            //...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 = EcoregionData.AnnualWeather[ecoregion].MonthlyPrecip[month]; //rain + irract in cm;
            //PlugIn.ModelCore.UI.WriteLine("SoilWater. WaterInputs={0:0.00}, .", H2Oinputs);
            tave = EcoregionData.AnnualWeather[ecoregion].MonthlyTemp[month];
            //PlugIn.ModelCore.UI.WriteLine("SoilWater. AvgTemp={0:0.00}, .", tave);
            tmax = EcoregionData.AnnualWeather[ecoregion].MonthlyMaxTemp[month];
            tmin = EcoregionData.AnnualWeather[ecoregion].MonthlyMinTemp[month];
            pet = EcoregionData.AnnualWeather[ecoregion].MonthlyPET[month];

            double wiltingPoint = EcoregionData.WiltingPoint[ecoregion];
            double soilDepth = EcoregionData.SoilDepth[ecoregion];
            double fieldCapacity = EcoregionData.FieldCapacity[ecoregion];
            double stormFlowFraction = EcoregionData.StormFlowFraction[ecoregion];
            double baseFlowFraction = EcoregionData.BaseFlowFraction[ecoregion];
            double drain = EcoregionData.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:

                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.
                //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

               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
            //http://www.civil.utah.edu/~mizukami/coursework/cveen7920/ETMeasurement.pdf

            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;

            //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 + H2Oinputs) / pet; //old ratio used in previous versions of LANDIS-Century
            if (pet > 0.0) ratioPrecipPET = H2Oinputs / pet;  //assumes that the ratio is the amount of incoming precip divided by PET.

            //SiteVars.NumberDryDays[site] = numberDryDays; //Calculated above using method below.
            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);

            return;
        }
        //---------------------------------------------------------------------
        /// <summary>
        /// Computes fire effects on litter, coarse woody debris, mineral soil, and charcoal.
        ///   No effects on soil organic matter (negligible according to Johnson et al. 2001).
        /// </summary>
        public static void ReduceLayers(string prescriptionName, Site site)
        {
            PlugIn.ModelCore.UI.WriteLine("   Calculating harvest induced layer reductions...");

            double litterLossMultiplier = 0.0;
            double woodLossMultiplier = 0.0;

            bool found = false;
            foreach (HarvestReductions prescription in ReductionsTable)
            {
                if (SiteVars.HarvestPrescriptionName != null && SiteVars.HarvestPrescriptionName[site].Trim() == prescription.PrescriptionName.Trim())
                {
                    litterLossMultiplier = prescription.LitterReduction;
                    woodLossMultiplier = prescription.WoodReduction;
                    found = true;
                }
            }
            if (!found) return;

            // Structural litter first

            double carbonLoss = SiteVars.SurfaceStructural[site].Carbon * litterLossMultiplier;
            double nitrogenLoss = SiteVars.SurfaceStructural[site].Nitrogen * litterLossMultiplier;
            double summaryNLoss = nitrogenLoss;

            SiteVars.SurfaceStructural[site].Carbon -= carbonLoss;
            SiteVars.SourceSink[site].Carbon        += carbonLoss;
            SiteVars.FireCEfflux[site]               += carbonLoss;

            SiteVars.SurfaceStructural[site].Nitrogen -= nitrogenLoss;
            SiteVars.SourceSink[site].Nitrogen += nitrogenLoss;
            SiteVars.FireNEfflux[site] += nitrogenLoss;

            // Metabolic litter

            carbonLoss = SiteVars.SurfaceMetabolic[site].Carbon * litterLossMultiplier;
            nitrogenLoss = SiteVars.SurfaceMetabolic[site].Nitrogen * litterLossMultiplier;
            summaryNLoss += nitrogenLoss;

            SiteVars.SurfaceMetabolic[site].Carbon  -= carbonLoss;
            SiteVars.SourceSink[site].Carbon        += carbonLoss;
            SiteVars.FireCEfflux[site]               += carbonLoss;

            SiteVars.SurfaceMetabolic[site].Nitrogen -= nitrogenLoss;
            SiteVars.SourceSink[site].Nitrogen        += nitrogenLoss;
            SiteVars.FireNEfflux[site] += nitrogenLoss;

            // Surface dead wood

            //double woodLossMultiplier = ReductionsTable[severity].WoodReduction;

            carbonLoss   = SiteVars.SurfaceDeadWood[site].Carbon * woodLossMultiplier;
            nitrogenLoss = SiteVars.SurfaceDeadWood[site].Nitrogen * woodLossMultiplier;
            summaryNLoss += nitrogenLoss;

            SiteVars.SurfaceDeadWood[site].Carbon   -= carbonLoss;
            SiteVars.SourceSink[site].Carbon        += carbonLoss;
            SiteVars.FireCEfflux[site]               += carbonLoss;

            SiteVars.SurfaceDeadWood[site].Nitrogen -= nitrogenLoss;
            SiteVars.SourceSink[site].Nitrogen        += nitrogenLoss;
            SiteVars.FireNEfflux[site] += nitrogenLoss;

            SiteVars.MineralN[site] += summaryNLoss * 0.01;
        }
 public static uint GetVarAge(ISpecies species, Site site)
 {
     if (SiteVars.Cohorts[site] == null)
         return 0;
     int avg = GetAvgAge(species, site); //speciesCohorts);
     double sum = 0;
     int count = 0;
     foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
     {
         if(speciesCohorts.Species == species)
             foreach (ICohort cohort in speciesCohorts)
             {
                 sum += System.Math.Pow(cohort.Age - avg, 2);
                 count++;
             }
     }
     if (count <= 1)
         return 0;
     return (uint)System.Math.Round((sum / (count - 1)));
 }
 //---------------------------------------------------------------------
 public static int GetStdDevAge(Site site)
 {
     if (SiteVars.Cohorts[site] == null)
         return 0;
     int std_dev = (int)System.Math.Sqrt(GetVarAge(site));
     return std_dev;
 }
        //---------------------------------------------------------------------
        // Method for calculating Mineral N allocation, called from Century.cs Run method before calling Grow
        // Iterates through cohorts, assigning each a portion of mineral N based on coarse root biomass.  Uses an exponential function to "distribute"
        // the N more evenly between spp. so that the ones with the most woody biomass don't get all the N (L122).
        public static void CalculateMineralNfraction(Site site)
        {
            AvailableN.CohortMineralNfraction = new Dictionary<int, Dictionary<int, double>>();
            double NAllocTotal = 0.0;

            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                foreach (ICohort cohort in speciesCohorts)
                {
                    int cohortAddYear = GetAddYear(cohort);
                    //PlugIn.ModelCore.UI.WriteLine("CALCMineralNfraction: year={0}, mo={1}, species={2}, cohortAge={3}, cohortAddYear={4}.", PlugIn.ModelCore.CurrentTime, Century.Month, cohort.Species.Name, cohort.Age, cohortAddYear);

                    //Nallocation is a measure of how much N a cohort can gather relative to other cohorts
                    //double Nallocation = Roots.CalculateFineRoot(cohort.LeafBiomass);
                    double Nallocation = 1- Math.Exp((-Roots.CalculateCoarseRoot(cohort.WoodBiomass)*0.02));

                    if (Nallocation <= 0.0) //PlugIn.ModelCore.CurrentTime == 0)
                        Nallocation = Math.Max(Nallocation, cohort.WoodBiomass * 0.01);

                    //PlugIn.ModelCore.UI.WriteLine("Species = {0}, Age = {1}, Nallocation = {2}, WoodBiomass = {3}, LeafBioMass = {4}", cohort.Species.Name, cohort.Age, Nallocation, cohort.WoodBiomass, cohort.LeafBiomass);
                    NAllocTotal += Nallocation;
                    Dictionary<int, double> newEntry = new Dictionary<int, double>();
                    newEntry.Add(cohortAddYear, Nallocation);

                    if (CohortMineralNfraction.ContainsKey(cohort.Species.Index))
                    {
                        CohortMineralNfraction[cohort.Species.Index].Add(cohortAddYear, Nallocation);
                    }
                    else
                    {
                        CohortMineralNfraction.Add(cohort.Species.Index, newEntry);
                    }
                }

            }

            // Next relativize
            foreach (ISpeciesCohorts speciesCohorts in SiteVars.Cohorts[site])
            {
                //PlugIn.ModelCore.UI.WriteLine(" SpeciesCohorts = {0}", speciesCohorts.Species.Name);
                foreach (ICohort cohort in speciesCohorts)
                {
                    int cohortAddYear = GetAddYear(cohort);
                    //PlugIn.ModelCore.UI.WriteLine("CALC2MineralNfraction: year={0}, mo={1}, species={2}, cohortAge={3}, cohortAddYear={4}.", PlugIn.ModelCore.CurrentTime, Century.Month, cohort.Species.Name, cohort.Age, cohortAddYear);
                    double Nallocation = CohortMineralNfraction[cohort.Species.Index][cohortAddYear];
                    double relativeNallocation = Nallocation / NAllocTotal;
                    CohortMineralNfraction[cohort.Species.Index][cohortAddYear] = relativeNallocation;

                    //PlugIn.ModelCore.UI.WriteLine("  Nallocation={0:0.00}, NAllocTotal={1:0.00}, relativeNallocation={2:0.00}.", Nallocation, NAllocTotal, relativeNallocation);

                    if (Double.IsNaN(relativeNallocation) || Double.IsNaN(Nallocation) || Double.IsNaN(NAllocTotal))
                    {
                        PlugIn.ModelCore.UI.WriteLine("  N ALLOCATION CALCULATION = NaN!  ");
                        PlugIn.ModelCore.UI.WriteLine("  Nallocation={0:0.00}, NAllocTotal={1:0.00}, relativeNallocation={2:0.00}.", Nallocation, NAllocTotal, relativeNallocation);
                        PlugIn.ModelCore.UI.WriteLine("  Wood={0:0.00}, Leaf={1:0.00}.", cohort.WoodBiomass, cohort.LeafBiomass);
                    }
                    //PlugIn.ModelCore.UI.WriteLine("Yr={0},Mo={1}. MineralNfraction={2:0.00}", PlugIn.ModelCore.CurrentTime, Century.Month, CohortMineralNfraction[cohort.Species.Index][cohortAddYear]);
                }
            }
        }
        // ---------------------------------------------------------------------
        // This method calculates the initial rate of spread for a specific site.
        // See below for the method that estimates the initial rate of spread for a broad area.
        
        public static double InitialRateOfSpread(double ISI, ISeasonParameters season, Site site, bool secondRegionMap)
        {   

            
            int fuelIndex = SiteVars.CFSFuelType[site];
            if (secondRegionMap)
                fuelIndex = SiteVars.CFSFuelType2[site];
            int PC = SiteVars.PercentConifer[site];
            int PH = SiteVars.PercentHardwood[site];
            int PDF = SiteVars.PercentDeadFir[site];
            
            //PlugIn.ModelCore.Log.WriteLine("Fuel Type Code = {0}.", siteFuelType.ToString());
            
            double RSI = 0.0;  
            
            //if (Event.FuelTypeParms[fuelIndex].BaseFuel == BaseFuelType.Conifer ||
                //Event.FuelTypeParms[fuelIndex].BaseFuel == BaseFuelType.ConiferPlantation)
            if (Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.C1 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.C2 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.C3 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.C4 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.C5 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.C6 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.C7)
            {
                double a = Event.FuelTypeParms[fuelIndex].A;
                double b = Event.FuelTypeParms[fuelIndex].B;
                double c = Event.FuelTypeParms[fuelIndex].C;
                
                double percentHard = (double) PH / 100.0;
                double percentConi = (double) PC / 100.0;
                
                RSI = CalculateRSI(a, b, c, ISI);
                

                if (PDF > 0)
                {
                    if (PH > 0 && season.LeafStatus == LeafOnOff.LeafOn) //M-4
                    {
                        a = 140 * Math.Exp((-1) * 35.5 / (double) PDF);
                        b = 0.0404;
                        c = 3.02 * Math.Exp((-1) * 0.00714 * (double) PDF);
                    }
                    else  //M-3
                    {
                        a = 170 * Math.Exp((-1) * 35 / (double) PDF);
                        b = 0.082 * Math.Exp((-1) * 36 / (double)PDF);
                        c = 1.698 - (0.00303 * PDF);
                    }
                    double MRSI = CalculateRSI(a, b, c, ISI);
                    if (MRSI > RSI)
                        RSI = MRSI;
                }
                
                // These are the classic MIXED CONIFER + DECIDUOUS
                else if (PH > 0)
                {
                    double RSIconifer = RSI;
                    int dIndex = SiteVars.DecidFuelType[site]; //(int)FuelTypeCode.D1;
                    double RSIdecid = CalculateRSI(Event.FuelTypeParms[dIndex].A, Event.FuelTypeParms[dIndex].B, Event.FuelTypeParms[dIndex].C, ISI);

                    if (season.LeafStatus == LeafOnOff.LeafOn)  //M-2
                    {
                        RSI = ((1 - percentHard) * RSIconifer) + (0.2 * percentHard * RSIdecid);
                    }
                    else  //M-1
                    {
                        RSI = ((1 - percentHard) * RSIconifer) + (percentHard * RSIdecid);
                    }

                    //PlugIn.ModelCore.Log.WriteLine("Calculating ROSi for a MIXED type. PH={0}, PC={1}, LeafStatus={2}.", PH, PC, season.LeafStatus);
                    //PlugIn.ModelCore.Log.WriteLine("  RSIcon={0:0.0}, RSIdecid={1:0.0}, RSImix={2:0.000}.", RSIconifer, RSIdecid, RSI);

                }
            }
                

            //if (Event.FuelTypeParms[fuelIndex].BaseFuel == BaseFuelType.Open)
            if (Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.O1a || Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.O1b)
            //siteFuelType == FuelTypeCode.O1a)
            {
                double a, b, c;
                int percentCuring = season.PercentCuring;
                
                if(season.NameOfSeason == SeasonName.Spring)  //O1a
                {
                    a = 190; //Event.FuelTypeParms[(int)FuelTypeCode.O1a].A;
                    b = 0.0310; //Event.FuelTypeParms[(int)FuelTypeCode.O1a].B;
                    c = 1.4; //Event.FuelTypeParms[(int)FuelTypeCode.O1a].C;
                }
                else   //O1b
                {
                    a = 250; //Event.FuelTypeParms[(int)FuelTypeCode.O1b].A;
                    b = 0.0350; //Event.FuelTypeParms[(int)FuelTypeCode.O1b].B;
                    c = 1.7; //Event.FuelTypeParms[(int)FuelTypeCode.O1b].C;
                }
                
                double CF = (0.02 * percentCuring) - 1.0;
                RSI = CalculateRSI(a, b, c, ISI);
                if(percentCuring > 50) 
                    RSI *= CF;
                else
                    RSI = 0;
                if (PDF > 0)
                {
                    if (season.LeafStatus == LeafOnOff.LeafOn) //M-4
                    {
                        a = 140 * Math.Exp((-1) * 35.5 / (double)PDF);
                        b = 0.0404;
                        c = 3.02 * Math.Exp((-1) * 0.00714 * (double)PDF);
                    }
                    else //M-3
                    {
                        a = 170 * (Math.Exp(((-1) * 35) / (double)PDF));
                        b = 0.082 * (Math.Exp(((-1) * 36) / (double)PDF));
                        c = 1.698 - (0.00303 * (double)PDF);
                    }
                    double MRSI = CalculateRSI(a, b, c, ISI);
                    if (MRSI > RSI)
                        RSI = MRSI;
                }
            }
            
            //if(Event.FuelTypeParms[fuelIndex].BaseFuel == BaseFuelType.NoFuel || fuelIndex == 0)
            if (Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.NoFuel || fuelIndex == 0)
            //siteFuelType == FuelTypeCode.NoFuel)
            {
                if (PDF > 0)
                {
                    double a=0, b=0, c=0;
                    if (season.LeafStatus == LeafOnOff.LeafOn) //M-4
                    {
                        a = 140 * Math.Exp((-1) * 35.5 / (double)PDF);
                        b = 0.0404;
                        c = 3.02 * Math.Exp((-1) * 0.00714 * (double)PDF);
                    }
                    else //M-3
                    {
                        a = 170 * (Math.Exp(((-1) * 35) / (double)PDF));
                        b = 0.082 * (Math.Exp(((-1) * 36) / (double)PDF));
                        c = 1.698 - (0.00303 * (double)PDF);
                    }
                    RSI = CalculateRSI(a, b, c, ISI);
                }

                else
                {
                    return 0;
                }
            }

            //if( Event.FuelTypeParms[fuelIndex].BaseFuel == BaseFuelType.Slash || 
                //Event.FuelTypeParms[fuelIndex].BaseFuel == BaseFuelType.Deciduous)
            if (Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.S1 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.S2 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.S3 ||
                Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.D1)
            {
                //PlugIn.ModelCore.Log.WriteLine("Calculating ROSi for a DECIDUOUS or SLASH type.");

                double a = Event.FuelTypeParms[fuelIndex].A;
                double b = Event.FuelTypeParms[fuelIndex].B;
                double c = Event.FuelTypeParms[fuelIndex].C;
                    
                RSI = CalculateRSI(a, b, c, ISI);

                //if (Event.FuelTypeParms[fuelIndex].BaseFuel == BaseFuelType.Deciduous 
                if(Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.D1 
                    && season.LeafStatus == LeafOnOff.LeafOn)
                //if(siteFuelType == FuelTypeCode.D1 && season.LeafStatus == LeafOnOff.LeafOn)
                    RSI *= 0.2;
                
                if (PDF > 0)
                {
                    //if (Event.FuelTypeParms[fuelIndex].BaseFuel == BaseFuelType.Deciduous
                    if (Event.FuelTypeParms[fuelIndex].SurfaceFuel == SurfaceFuelType.D1 
                        && season.LeafStatus == LeafOnOff.LeafOn)  //M-4
                    //siteFuelType == FuelTypeCode.D1) 
                    {
                        a = 140 * Math.Exp((-1) * 35.5 / (double) PDF);
                        b = 0.0404;
                        c = 3.02 * Math.Exp((-1) * 0.00714 * (double) PDF);
                    }
                    else //M-3
                    {
                        a = 170 * (Math.Exp(((-1) * 35) / (double) PDF));
                        b = 0.082 * (Math.Exp(((-1) * 36 )/ (double) PDF));
                        c = 1.698 - (0.00303 * (double) PDF);
                    }
                    double MRSI = CalculateRSI(a, b, c, ISI);
                    if (MRSI > RSI)
                        RSI = MRSI;
                }
            }

            
            return RSI;
        }
 public static int GetStdDevAge(ISpecies species, Site site)
 {
     if (SiteVars.Cohorts[site] == null)
         return 0;
     int std_dev = (int)System.Math.Round(System.Math.Sqrt(GetVarAge(species, site)),0);
     return std_dev;
 }