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(); } } } }
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); }
/*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); }