public void Calculate_Establishment(IEcoregionPnETVariables pnetvars, IEcoregionPnET ecoregion, float PAR, IHydrology hydrology)
        {
            foreach (ISpeciesPNET spc in PlugIn.SpeciesPnET.AllSpecies)
            {

                if (pnetvars.Tmin > spc.PsnTMin)
                {
                    float frad = (float)Math.Pow(Cohort.ComputeFrad(PAR, spc.HalfSat), spc.EstRad);

                    float PressureHead = hydrology.GetPressureHead(ecoregion);

                    float fwater = (float)Math.Pow(Cohort.ComputeFWater(spc.H2, spc.H3, spc.H4, PressureHead), spc.EstMoist);

                    float pest = 1 - (float)Math.Pow(1.0 - (frad * fwater), Timestep);
                    if (!spc.PreventEstablishment)
                    {
                        if (pest > _pest[spc])
                        {
                            _pest[spc] = pest;
                            _fwater[spc] = fwater;
                            _frad[spc] = frad;

                            if (pest > (float)PlugIn.ContinuousUniformRandom())
                            {
                                if (HasEstablished(spc) == false)
                                {
                                    _hasEstablished.Add(spc);
                                }

                            }

                        }
                    }
                    if (establishment_siteoutput != null)
                    {

                        establishment_siteoutput.Add(((int)pnetvars.Year).ToString() + "," + spc.Name + "," + pest + "," + fwater + "," + frad + "," + HasEstablished(spc));

                        // TODO: win time by reducing calls to write
                        establishment_siteoutput.Write();
                    }
                }
            }
        }
Beispiel #2
0
        public static SortedList <float, float> CalculateMonthlySoilTemps(SortedList <float, float> depthTempDict, IEcoregionPnET Ecoregion, int daysOfWinter, float snowPack, IHydrology hydrology, float lastTempBelowSnow)
        {
            //SortedList<float, float> depthTempDict = new SortedList<float, float>();  //for permafrost



            //float lambAir = 0.023f;
            //float lambIce = 2.29f;
            //float omega = (float)(2 * Math.PI / 12.0);


            float[] snowResults = CalculateSnowDepth(daysOfWinter, snowPack);
            float   sno_dep     = snowResults[0];
            float   Psno_kg_m3  = snowResults[1];

            if (Ecoregion.Variables.Tave >= 0)
            {
                float fracAbove0 = Ecoregion.Variables.Tmax / (Ecoregion.Variables.Tmax - Ecoregion.Variables.Tmin);
                sno_dep = sno_dep * fracAbove0;
            }
            // from CLM model - https://escomp.github.io/ctsm-docs/doc/build/html/tech_note/Soil_Snow_Temperatures/CLM50_Tech_Note_Soil_Snow_Temperatures.html#soil-and-snow-thermal-properties
            // Eq. 85 - Jordan (1991)
            //float lambda_Snow = (float)(lambAir + ((0.0000775 * Psno_kg_m3) + (0.000001105 * Math.Pow(Psno_kg_m3, 2))) * (lambIce - lambAir)) * 3.6F * 24F; //(kJ/m/d/K) includes unit conversion from W to kJ
            //float damping = (float)Math.Sqrt(omega / (2.0F * lambda_Snow));
            float damping  = CalculateSnowDamping(Psno_kg_m3);
            float DRz_snow = (float)Math.Exp(-1.0F * sno_dep * damping); // Damping ratio for snow - adapted from Kang et al. (2000) and Liang et al. (2014)


            // Permafrost calculations - from "Soil thawing worksheet.xlsx"
            //
            //if (Ecoregion.Variables.Tave < minMonthlyAvgTemp)
            //    minMonthlyAvgTemp = Ecoregion.Variables.Tave;
            float porosity     = Ecoregion.Porosity / Ecoregion.RootingDepth;                                                                                                                                             //m3/m3
            float waterContent = hydrology.Water / Ecoregion.RootingDepth;                                                                                                                                                //m3/m3
            float ga           = 0.035F + 0.298F * (waterContent / porosity);
            float Fa           = ((2.0F / 3.0F) / (1.0F + ga * ((Constants.lambda_a / Constants.lambda_w) - 1.0F))) + ((1.0F / 3.0F) / (1.0F + (1.0F - 2.0F * ga) * ((Constants.lambda_a / Constants.lambda_w) - 1.0F))); // ratio of air temp gradient
            float Fs           = PressureHeadSaxton_Rawls.GetFs(Ecoregion.SoilType);
            float lambda_s     = PressureHeadSaxton_Rawls.GetLambda_s(Ecoregion.SoilType);
            float lambda_theta = (Fs * (1.0F - porosity) * lambda_s + Fa * (porosity - waterContent) * Constants.lambda_a + waterContent * Constants.lambda_w) / (Fs * (1.0F - porosity) + Fa * (porosity - waterContent) + waterContent); //soil thermal conductivity (kJ/m/d/K)
            float D            = lambda_theta / PressureHeadSaxton_Rawls.GetCTheta(Ecoregion.SoilType);                                                                                                                                    //m2/day
            float Dmonth       = D * Ecoregion.Variables.DaySpan;                                                                                                                                                                          // m2/month
            float ks           = Dmonth * 1000000F / (Ecoregion.Variables.DaySpan * (Constants.SecondsPerHour * 24));                                                                                                                      // mm2/s
            float d            = (float)Math.Pow((Constants.omega / (2.0F * Dmonth)), (0.5));

            float maxDepth    = Ecoregion.RootingDepth + Ecoregion.LeakageFrostDepth;
            float freezeDepth = maxDepth;
            float testDepth   = 0;
            //if (lastTempBelowSnow == float.MaxValue)
            //{
            //    //int mCount = Math.Min(12, data.Count());
            //    //float tSum = 0;
            //    //foreach (int z in Enumerable.Range(0, mCount))
            //    //{
            //     //   tSum += data[z].Tave;
            //    //}
            //    //float annualTavg = tSum / mCount;
            //    float tempBelowSnow = Ecoregion.Variables.Tave;
            //    if (sno_dep > 0)
            //    {
            //        tempBelowSnow = annualTavg + (Ecoregion.Variables.Tave - annualTavg) * DRz_snow;
            //    }
            //    lastTempBelowSnow = tempBelowSnow;
            //    while (testDepth <= (maxDepth / 1000.0))
            //    {
            //        float DRz = (float)Math.Exp(-1.0F * testDepth * d); // adapted from Kang et al. (2000) and Liang et al. (2014)
            //        float zTemp = annualTavg + (tempBelowSnow - annualTavg) * DRz;
            //        depthTempDict[testDepth] = zTemp;
            //        if ((zTemp <= 0) && (testDepth < freezeDepth))
            //            freezeDepth = testDepth;
            //        testDepth += 0.25F;
            //    }
            //}
            //else
            //{
            float tempBelowSnow = Ecoregion.Variables.Tave;

            if (sno_dep > 0)
            {
                tempBelowSnow = lastTempBelowSnow + (Ecoregion.Variables.Tave - lastTempBelowSnow) * DRz_snow;
            }
            lastTempBelowSnow = tempBelowSnow;
            while (testDepth <= (maxDepth / 1000.0))
            {
                float DRz   = (float)Math.Exp(-1.0F * testDepth * d);   // adapted from Kang et al. (2000) and Liang et al. (2014)
                float zTemp = depthTempDict[testDepth] + (tempBelowSnow - depthTempDict[testDepth]) * DRz;
                depthTempDict[testDepth] = zTemp;
                //if ((zTemp <= 0) && (testDepth < freezeDepth))
                //    freezeDepth = testDepth;
                if (testDepth == 0f)
                {
                    testDepth = 0.10f;
                }
                else if (testDepth == 0.10f)
                {
                    testDepth = 0.25f;
                }
                else
                {
                    testDepth += 0.25F;
                }
            }
            //}
            return(depthTempDict);
        }
        public bool CalculatePhotosynthesis(float PrecInByCanopyLayer, float LeakagePerCohort, IHydrology hydrology, ref float SubCanopyPar)
        {
            bool success = true;

            // Leaf area index for the subcanopy layer by index. Function of specific leaf weight SLWMAX and the depth of the canopy
            // Depth of the canopy is expressed by the mass of foliage above this subcanopy layer (i.e. slwdel * index/imax *fol)
            LAI[index] = (1 / (float)PlugIn.IMAX) * fol / (species.SLWmax - species.SLWDel * index * (1 / (float)PlugIn.IMAX) * fol);

            // Precipitation interception has a max in the upper canopy and decreases exponentially through the canopy
            //Interception[index] = PrecInByCanopyLayer * (float)(1 - Math.Exp(-1 * ecoregion.PrecIntConst * LAI[index]));
            //if (Interception[index] > PrecInByCanopyLayer) throw new System.Exception("Error adding water, PrecInByCanopyLayer = " + PrecInByCanopyLayer + " Interception[index] = " + Interception[index]);

            // Incoming precipitation
            //float waterIn = PrecInByCanopyLayer  - Interception[index]; //mm
            float waterIn = PrecInByCanopyLayer; //mm

            // Add incoming precipitation to soil moisture
            success = hydrology.AddWater(waterIn);
            if (success == false) throw new System.Exception("Error adding water, waterIn = " + waterIn + " water = " + hydrology.Water);

            // Instantaneous runoff (excess of porosity)
            float runoff =  Math.Max(hydrology.Water - ecoregion.Porosity, 0);
            Hydrology.RunOff += runoff;
            success = hydrology.AddWater(-1 * runoff);
            if (success == false) throw new System.Exception("Error adding water, Hydrology.RunOff = " + Hydrology.RunOff + " water = " + hydrology.Water);

            // Fast Leakage
            float leakage = Math.Max(LeakagePerCohort * (hydrology.Water - ecoregion.FieldCap), 0);
            Hydrology.Leakage += leakage;

            // Remove fast leakage
            success = hydrology.AddWater(-1 * leakage);
            if (success == false) throw new System.Exception("Error adding water, Hydrology.Leakage = " + Hydrology.Leakage + " water = " + hydrology.Water);

            // Maintenance respiration depends on biomass,  non soluble carbon and temperature
            MaintenanceRespiration[index] = (1 / (float)PlugIn.IMAX) * (float)Math.Min(NSC, ecoregion.Variables[Species.Name].MaintRespFTempResp * biomass);//gC //IMAXinverse

            // Subtract mainenance respiration (gC/mo)
            nsc -= MaintenanceRespiration[index];

            // Woody decomposition: do once per year to reduce unnescessary computation time so with the last subcanopy layer
            if (index == PlugIn.IMAX - 1)
            {
                // In the first month
                if (ecoregion.Variables.Month == (int)Constants.Months.January)
                {
                    float woodSenescence = Senescence();
                    addwoodydebris(woodSenescence, species.KWdLit);
                    lastWoodySenescence = woodSenescence;

                    // Release of nsc, will be added to biomass components next year
                    // Assumed that NSC will have a minimum concentration, excess is allocated to biomass
                    float Allocation = Math.Max(nsc - (species.DNSC * FActiveBiom * biomass), 0);
                    biomass += Allocation;
                    biomassmax = Math.Max(biomassmax, biomass);
                    nsc -= Allocation;

                    age++;
                }
            }

            // When LeafOn becomes false for the first time in a year
            if(ecoregion.Variables.Tmin < this.SpeciesPNET.PsnTMin)
            {
                if (leaf_on == true)
                {
                    leaf_on = false;
                    float foliageSenescence = FoliageSenescence();
                    addlitter(foliageSenescence, SpeciesPNET);
                    lastFoliageSenescence = foliageSenescence;
                }
            }
            else
            {
                leaf_on = true;
            }

            if (leaf_on)
            {
                // Foliage linearly increases with active biomass
                float IdealFol = (species.FracFol * FActiveBiom * biomass);

                // If the tree should have more filiage than it currently has
                if (IdealFol > fol)
                {
                    // Foliage allocation depends on availability of NSC (allows deficit at this time so no min nsc)
                    // carbon fraction of biomass to convert C to DW
                    float Folalloc = Math.Max(0, Math.Min(nsc, species.CFracBiomass * (IdealFol - fol))); // gC/mo

                    // Add foliage allocation to foliage
                    fol += Folalloc / species.CFracBiomass;// gDW

                    // Subtract from NSC
                    nsc -= Folalloc;
                }
            }

            //  Apply defoliation in month of june
            if ((PlugIn.ModelCore.CurrentTime > 0) && (ecoregion.Variables.Month == (int)Constants.Months.June))
            {
                if (DefolProp > 0)
                {
                    //Adjust defol prop for foliage longevity - defol only affects current foliage
                    float adjDefol = DefolProp * species.TOfol;
                    ReduceFoliage(adjDefol);
                    // Update LAI after defoliation
                    LAI[index] = (1 / (float)PlugIn.IMAX) * fol / (species.SLWmax - species.SLWDel * index * (1 / (float)PlugIn.IMAX) * fol);
                }

            }

            // Reduction factor for radiation on photosynthesis
            FRad[index] = ComputeFrad(SubCanopyPar, species.HalfSat);

            // Below-canopy PAR if updated after each subcanopy layer
            SubCanopyPar *= (float)Math.Exp(-species.K * LAI[index]);

            // Get pressure head given ecoregion and soil water content (latter in hydrology)
            float PressureHead = hydrology.GetPressureHead(ecoregion);

            // Reduction water for sub or supra optimal soil water content
            if(PlugIn.ModelCore.CurrentTime > 0)
                FWater[index] = ComputeFWater(species.H2, species.H3, species.H4, PressureHead);
            else // Ignore H2 parameter during spinup
                FWater[index] = ComputeFWater(0, species.H3, species.H4, PressureHead);

            // If trees are physiologically active
            if (leaf_on)
            {

                // Compute net psn from stress factors and reference net psn
                NetPsn[index] = (1 / (float)PlugIn.IMAX) * FWater[index] * FRad[index] * Fage * ecoregion.Variables[species.Name].FTempPSNRefNetPsn * fol;

                // Net foliage respiration depends on reference psn (AMAX)
                //float FTempRespDayRefResp = ecoregion.Variables[species.Name].FTempRespDay * ecoregion.Variables.DaySpan * ecoregion.Variables.Daylength * Constants.MC / Constants.billion * ecoregion.Variables[species.Name].Amax;
                //Subistitute 24 hours in place of DayLength because foliar respiration does occur at night.  FTempRespDay uses Tave temps reflecting both day and night temperatures.
                float FTempRespDayRefResp = ecoregion.Variables[species.Name].FTempRespDay * ecoregion.Variables.DaySpan * (Constants.SecondsPerHour * 24) * Constants.MC / Constants.billion * ecoregion.Variables[species.Name].Amax;

                // Actal foliage respiration (growth respiration)
                FolResp[index] = FWater[index] * FTempRespDayRefResp * fol / (float)PlugIn.IMAX;

                // Gross psn depends on net psn and foliage respiration
                GrossPsn[index] = NetPsn[index] + FolResp[index];

                // Transpiration depends on gross psn, water use efficiency (gCO2/mm water) and molecular weight (gC/gCO2)
                Transpiration[index] = Math.Min(hydrology.Water, GrossPsn[index] / ecoregion.Variables[Species.Name].WUE / ecoregion.Variables[Species.Name].DelAmax * Constants.MCO2_MC);

                // Subtract transpiration from hydrology
                success = hydrology.AddWater(-1 * Transpiration[index]);
                if (success == false) throw new System.Exception("Error adding water, Transpiration = " + Transpiration[index] + " water = " + hydrology.Water);

                // Add net psn to non soluble carbons
                nsc += NetPsn[index];

            }
            else
            {
                // Reset subcanopy layer values
                NetPsn[index] = 0;
                FolResp[index] = 0;
                GrossPsn[index] = 0;
                Transpiration[index] = 0;

            }

            if (index < PlugIn.IMAX - 1) index++;
            return success;
        }
        public Dictionary<ISpeciesPNET, float> Calculate_Establishment_Month(IEcoregionPnETVariables pnetvars, IEcoregionPnET ecoregion, float PAR, IHydrology hydrology)
        {
            Dictionary<ISpeciesPNET, float> estabDict = new Dictionary<ISpeciesPNET, float>();

            foreach (ISpeciesPNET spc in PlugIn.SpeciesPnET.AllSpecies)
            {
                if (pnetvars.Tmin > spc.PsnTMin)
                {
                    float frad = (float)Math.Pow(Cohort.ComputeFrad(PAR, spc.HalfSat), spc.EstRad);

                    float PressureHead = hydrology.GetPressureHead(ecoregion);

                    float fwater = (float)Math.Pow(Cohort.ComputeFWater(spc.H2, spc.H3, spc.H4, PressureHead), spc.EstMoist);

                    float pest = 1 - (float)Math.Pow(1.0 - (frad * fwater), Timestep);
                    estabDict[spc] = pest;
                    if (fwater < _fwater[spc])
                    {
                        _fwater[spc] = fwater;
                    }
                    if (frad < _frad[spc])
                    {
                        _frad[spc] = frad;
                    }

                    /*if (establishment_siteoutput != null)
                    {

                        establishment_siteoutput.Add(((int)pnetvars.Year).ToString() + "," + spc.Name + "," + pest + "," + fwater + "," + frad + "," + HasEstablished(spc));

                        // TODO: win time by reducing calls to write
                        establishment_siteoutput.Write();
                    }
                     * */
                }

            }
            return estabDict;
        }
        /*public static void Initialize(int timestep)
         * {
         *  Timestep = timestep;
         *
         *
         * }*/

        public Dictionary <ISpeciesPNET, float> Calculate_Establishment_Month(IEcoregionPnETVariables pnetvars, IEcoregionPnET ecoregion, float PAR, IHydrology hydrology, float minHalfSat, float maxHalfSat, bool invertPest)
        {
            Dictionary <ISpeciesPNET, float> estabDict = new Dictionary <ISpeciesPNET, float>();

            float halfSatRange = maxHalfSat - minHalfSat;

            foreach (ISpeciesPNET spc in PlugIn.SpeciesPnET.AllSpecies)
            {
                if (pnetvars.Tmin > spc.PsnTMin && pnetvars.Tmax < spc.PsnTMax)
                {
                    // Adjust HalfSat for CO2 effect
                    float halfSatIntercept = spc.HalfSat - 350 * spc.CO2HalfSatEff;
                    float adjHalfSat       = spc.CO2HalfSatEff * pnetvars.CO2 + halfSatIntercept;
                    float frad             = (float)(Math.Min(1.0, (Math.Pow(Cohort.ComputeFrad(PAR, adjHalfSat), 2) * (1 / (Math.Pow(spc.EstRad, 2))))));
                    float adjFrad          = frad;
                    // Optional adjustment to invert Pest based on relative halfSat
                    if (invertPest && halfSatRange > 0)
                    {
                        float frad_adj_int = (spc.HalfSat - minHalfSat) / halfSatRange;
                        float frad_slope   = (frad_adj_int * 2) - 1;
                        adjFrad = 1 - frad_adj_int + frad * frad_slope;
                    }

                    float PressureHead = hydrology.GetPressureHead(ecoregion);

                    float fwater = (float)(Math.Min(1.0, (Math.Pow(Cohort.ComputeFWater(spc.H1, spc.H2, spc.H3, spc.H4, PressureHead), 2) * (1 / (Math.Pow(spc.EstMoist, 2))))));

                    float pest = (float)Math.Min(1.0, adjFrad * fwater);
                    estabDict[spc] = pest;
                    _fwater[spc]   = fwater;
                    _frad[spc]     = adjFrad;
                }
            }
            return(estabDict);
        }
Beispiel #6
0
        /*public void Calculate_Establishment(IEcoregionPnETVariables pnetvars, IEcoregionPnET ecoregion, float PAR, IHydrology hydrology)
         * {
         *  foreach (ISpeciesPNET spc in PlugIn.SpeciesPnET.AllSpecies)
         *  {
         *
         *
         *      if (pnetvars.Tmin > spc.PsnTMin)
         *      {
         *          // Adjust HalfSat for CO2 effect
         *          float halfSatIntercept = spc.HalfSat - 350 * spc.CO2HalfSatEff;
         *          float adjHalfSat = spc.CO2HalfSatEff * pnetvars.CO2 + halfSatIntercept;
         *          float frad = (float)Math.Pow(Cohort.ComputeFrad(PAR, adjHalfSat), spc.EstRad);
         *
         *
         *          float PressureHead = hydrology.GetPressureHead(ecoregion);
         *
         *          float fwater = (float)Math.Pow(Cohort.ComputeFWater(spc.H1,spc.H2, spc.H3, spc.H4, PressureHead), spc.EstMoist);
         *
         *          float pest = 1 - (float)Math.Pow(1.0 - (frad * fwater), Timestep);
         *          if (!spc.PreventEstablishment)
         *          {
         *              if (pest > _pest[spc])
         *              {
         *                  _pest[spc] = pest;
         *                  _fwater[spc] = fwater;
         *                  _frad[spc] = frad;
         *
         *                  if (pest > (float)PlugIn.ContinuousUniformRandom())
         *                  {
         *                      if (HasEstablished(spc) == false)
         *                      {
         *                          _hasEstablished.Add(spc);
         *                      }
         *
         *                  }
         *
         *              }
         *          }
         *          if (establishment_siteoutput != null)
         *          {
         *
         *              establishment_siteoutput.Add(((int)pnetvars.Year).ToString() + "," + spc.Name + "," + pest + "," + fwater + "," + frad + "," + HasEstablished(spc));
         *
         *              // TODO: win time by reducing calls to write
         *              establishment_siteoutput.Write();
         *          }
         *      }
         *  }
         * }
         */

        public Dictionary <ISpeciesPNET, float> Calculate_Establishment_Month(IEcoregionPnETVariables pnetvars, IEcoregionPnET ecoregion, float PAR, IHydrology hydrology, float minHalfSat, float maxHalfSat, bool invertPest)
        {
            Dictionary <ISpeciesPNET, float> estabDict = new Dictionary <ISpeciesPNET, float>();
            //_fwater = new Dictionary<ISpeciesPNET, float>();
            //_pest = new Dictionary<ISpeciesPNET, float>();
            //_frad = new Dictionary<ISpeciesPNET, float>();
            float halfSatRange = maxHalfSat - minHalfSat;

            foreach (ISpeciesPNET spc in PlugIn.SpeciesPnET.AllSpecies)
            {
                if (pnetvars.Tmin > spc.PsnTMin && pnetvars.Tmax < spc.PsnTMax)
                {
                    // Adjust HalfSat for CO2 effect
                    float halfSatIntercept = spc.HalfSat - 350 * spc.CO2HalfSatEff;
                    float adjHalfSat       = spc.CO2HalfSatEff * pnetvars.CO2 + halfSatIntercept;
                    float frad             = (float)(Math.Min(1.0, (Math.Pow(Cohort.ComputeFrad(PAR, adjHalfSat), 2) * (1 / (Math.Pow(spc.EstRad, 2))))));
                    float adjFrad          = frad;
                    // Optional adjustment to invert Pest based on relative halfSat
                    if (invertPest && halfSatRange > 0)
                    {
                        float frad_adj_int = (spc.HalfSat - minHalfSat) / halfSatRange;
                        float frad_slope   = (frad_adj_int * 2) - 1;
                        adjFrad = 1 - frad_adj_int + frad * frad_slope;
                    }


                    float PressureHead = hydrology.GetPressureHead(ecoregion);

                    float fwater = (float)(Math.Min(1.0, (Math.Pow(Cohort.ComputeFWater(spc.H1, spc.H2, spc.H3, spc.H4, PressureHead), 2) * (1 / (Math.Pow(spc.EstMoist, 2))))));

                    //float pest = 1 - (float)Math.Pow(1.0 - (frad * fwater * spc.MaxPest), Timestep);
                    float pest = (float)Math.Min(1.0, adjFrad * fwater);
                    estabDict[spc] = pest;
                    _pest[spc]     = pest;
                    _fwater[spc]   = fwater;
                    _frad[spc]     = adjFrad;

                    /*if (fwater < _fwater[spc])
                     * {
                     *  _fwater[spc] = fwater;
                     * }
                     * if (frad < _frad[spc])
                     * {
                     *  _frad[spc] = frad;
                     * }
                     */
                    /*if (establishment_siteoutput != null)
                     * {
                     *
                     *  establishment_siteoutput.Add(((int)pnetvars.Year).ToString() + "," + spc.Name + "," + pest + "," + fwater + "," + frad + "," + HasEstablished(spc));
                     *
                     *  // TODO: win time by reducing calls to write
                     *  establishment_siteoutput.Write();
                     * }
                     * */
                }
            }
            return(estabDict);
        }