public void WriteToCsv(StreamWriter writer, OrganonVariant variant, int year) { foreach (KeyValuePair <FiaCode, float[]> meanDbhInCmForSpecies in this.MeanDbhInCmBySpecies) { FiaCode species = meanDbhInCmForSpecies.Key; string speciesCode = species.ToFourLetterCode(); // BUGBUG: Huffman Peak hack if (species == FiaCode.AbiesConcolor) { speciesCode = FiaCode.AbiesProcera.ToFourLetterCode(); } else if (species == FiaCode.AbiesGrandis) { speciesCode = FiaCode.AbiesAmabalis.ToFourLetterCode(); } float[] deadExpansionFactors = this.DeadExpansionFactorBySpecies[species]; float[] liveExpansionFactors = this.LiveExpansionFactorBySpecies[species]; float[] maxDbh = this.MaxDbhInCmBySpecies[species]; float[] meanCrownRatios = this.MeanCrownRatioBySpecies[species]; float[] meanHeight = this.MeanHeightInMetersBySpecies[species]; float[] minDbh = this.MinDbhInCmBySpecies[species]; for (int quantile = 0; quantile < TestConstant.DbhQuantiles; ++quantile) { writer.WriteLine("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}", variant.TreeModel, year, speciesCode, quantile, meanDbhInCmForSpecies.Value[quantile], meanHeight[quantile], liveExpansionFactors[quantile], deadExpansionFactors[quantile], meanCrownRatios[quantile], minDbh[quantile], maxDbh[quantile]); } } }
private static float GetMortalityFertilizationAdjustment(FiaCode species, TreeModel treeModel, int simulationStep, OrganonTreatments treatments) { // fertilization mortality effects currently supported only for non-RAP Douglas-fir if ((treatments.FertilizationsPerformed < 1) || (species != FiaCode.PseudotsugaMenziesii) || (treeModel == TreeModel.OrganonRap)) { return(0.0F); } // non-RAP Douglas-fir // Hann 2003 Research Contribution 40, Table 37: Parameters for predicting fertlization response of 5-year mortality float c5 = 0.0000552859F; float PF2 = 1.5F; float PF3 = -0.5F; float XTIME = Constant.DefaultTimeStepInYears * (float)simulationStep; float FERTX1 = 0.0F; for (int treatmentIndex = 1; treatmentIndex < 5; ++treatmentIndex) { // BUGBUG: summation range doesn't match 13 or 18 year periods given in Hann 2003 Table 3 FERTX1 += treatments.PoundsOfNitrogenPerAcre[treatmentIndex] * MathV.Exp(PF3 / PF2 * (treatments.TimeStepsSinceFertilization[0] - treatments.TimeStepsSinceFertilization[treatmentIndex])); } float FERTADJ = c5 * MathV.Pow(treatments.PoundsOfNitrogenPerAcre[0] + FERTX1, PF2) * MathV.Exp(PF3 * (XTIME - treatments.TimeStepsSinceFertilization[0])); return(FERTADJ); }
protected override float GetHeightToLargestCrownWidth(FiaCode species, float HT, float CR) { float B1 = species switch { // Hann(1999) FS 45: 217-225 FiaCode.PseudotsugaMenziesii => 0.062000F, // Hann and Hanus(2001) FRL Research Contribution 34 FiaCode.AbiesGrandis => 0.028454F, // Hann and Hanus(2001) FRL Research Contribution 34 FiaCode.TsugaHeterophylla or FiaCode.ThujaPlicata or FiaCode.TaxusBrevifolia => 0.209806F, // Hann and Hanus(2001) FRL Research Contribution 34 FiaCode.ArbutusMenziesii or FiaCode.AcerMacrophyllum or FiaCode.QuercusGarryana or FiaCode.AlnusRubra or FiaCode.CornusNuttallii or FiaCode.Salix => 0.0F, _ => throw Trees.CreateUnhandledSpeciesException(species), }; float CL = CR * HT; float HLCW = HT - (1.0F - B1) * CL; return(HLCW); }
public override void GetHeightPredictionCoefficients(FiaCode species, out float B0, out float B1, out float B2) { switch (species) { // Hann, Bluhm, and Hibbs(2011) Forest Biometrics Research Paper 1 case FiaCode.AlnusRubra: B0 = 6.75650139F; B1 = -4.6252377F; B2 = -0.23208200F; break; // Hanus, Marshall, and Hann(1999) FRL Research Contribution 25 case FiaCode.PseudotsugaMenziesii: B0 = 7.262195456F; B1 = -5.899759104F; B2 = -0.287207389F; break; // Hanus, Marshall, and Hann(1999) FRL Research Contribution 25 case FiaCode.TsugaHeterophylla: B0 = 6.555344622F; B1 = -5.137174162F; B2 = -0.364550800F; break; // Hann and Hanus(2002) OSU Department of Forest Management Internal Report #2 case FiaCode.ThujaPlicata: B0 = 6.14817441F; B1 = -5.40092761F; B2 = -0.38922036F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.AcerMacrophyllum: B0 = 5.21462F; B1 = -2.70252F; B2 = -0.354756F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.CornusNuttallii: B0 = 4.49727F; B1 = -2.07667F; B2 = -0.388650F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.Salix: B0 = 4.88361F; B1 = -2.47605F; B2 = -0.309050F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } }
public static void ReduceExpansionFactors(OrganonConfiguration configuration, int simulationStep, OrganonStand stand, OrganonStandDensity densityBeforeGrowth) { foreach (Trees treesOfSpecies in stand.TreesBySpecies.Values) { FiaCode species = treesOfSpecies.Species; float fertilizationExponent = OrganonMortality.GetMortalityFertilizationAdjustment(species, configuration.Variant.TreeModel, simulationStep, configuration.Treatments); configuration.Variant.ReduceExpansionFactors(stand, densityBeforeGrowth, treesOfSpecies, fertilizationExponent); } }
public TreeRecord(int tag, FiaCode species, float dbhInInches, float crownRatio, float expansionFactor) { this.CrownRatio = crownRatio; this.DbhInInches = dbhInInches; this.HeightInFeet = TreeRecord.EstimateHeightInFeet(species, dbhInInches); this.LiveExpansionFactor = expansionFactor; this.Species = species; this.Tag = tag; }
public static float EstimateHeightInMeters(FiaCode species, float dbhInInches) { // height-diameter equations from // Ishii H, Reynolds JH, Ford ED, Shaw DC. 2000. Height growth and vertical development of an old-growth // Pseudotsuga-Tsuga forest in southwestern Washington State, U.S.A. Canadian Journal of Forest Resources // 30:17-24. http://faculty.washington.edu/joel/Papers/IshiiReynoldsetalCJFR2000.pdf // Figure 2: ABAM, PSME, TABR, THPL, and TSHE at Wind River / TT Munger Experimental Forest // Fujimori T, Kawanabe S, Hideki S, et al. 1976. Biomass and primary production in forests of three major // vegetation zones of the northwestern United States. Journal of the Japanese Forestry Society 58(10):360-373. // http://andrewsforest.oregonstate.edu/publications/800 // Figure 6: ABPR Goat Marsh Research Natural Area // // also // Curtis RO. 2015. Development of Top Heights and Corresponding Diameters in High Elevation Noble Fir Plantations. // Research Paper PNW-RP-603 Pacific Northwest Research Station USFS. https://www.fs.fed.us/pnw/pubs/pnw_rp603.pdf // ABPR 50 year site index: 24 m @ n = 1 // Franklin JF. ND. Abies procera. https://andrewsforest.oregonstate.edu/sites/default/files/lter/pubs/pdf/pub1168.pdf // ABPR class II 100 year site index: 36 m => 48 cm DBH under Ishii 2000 float dbhInCm = Constant.CentimetersPerInch * dbhInInches; var heightInM = species switch { FiaCode.AbiesAmabalis => 45.8F * (1.0F - MathF.Exp(-0.008F * MathF.Pow(dbhInCm, 1.36F))), FiaCode.AbiesProcera => dbhInCm / (0.6035F + 0.0095F * dbhInCm), FiaCode.PseudotsugaMenziesii => 60.1F * (1.0F - MathF.Exp(-0.007F * MathF.Pow(dbhInCm, 1.25F))), FiaCode.TaxusBrevifolia => 50.0F * (1.0F - MathF.Exp(-0.025F * MathF.Pow(dbhInCm, 0.71F))), FiaCode.ThujaPlicata => 68.5F * (1.0F - MathF.Exp(-0.009F * MathF.Pow(dbhInCm, 1.04F))), FiaCode.TsugaHeterophylla => 56.9F * (1.0F - MathF.Exp(-0.007F * MathF.Pow(dbhInCm, 1.29F))), // simple defaults for other conifers and willows FiaCode.AbiesConcolor or FiaCode.AbiesGrandis or FiaCode.CalocedrusDecurrens or FiaCode.NotholithocarpusDensiflorus or FiaCode.PinusLambertiana or FiaCode.PinusPonderosa or FiaCode.Salix => dbhInCm + 1.37F, // simple defaults for most hardwoods FiaCode.AcerMacrophyllum or FiaCode.AlnusRubra or FiaCode.ArbutusMenziesii or FiaCode.ChrysolepisChrysophyllaVarChrysophylla or FiaCode.CornusNuttallii or FiaCode.QuercusChrysolepis or FiaCode.QuercusGarryana or FiaCode.QuercusKelloggii => 0.8F * dbhInCm + 1.37F, _ => throw Trees.CreateUnhandledSpeciesException(species), }; ; if (heightInM < 1.372F) { // regression equations above may be inaccurate on small trees, resulting in heights below Organon 2.2.4's 4.5 foot minimum // For now, and for simplicity, force these trees to satisfy legacy Organon height requirements. heightInM = 1.372F; } return(heightInM); }
public TreeQuantiles(TestStand stand, PspStand pspStand, int measurementYear) : this() { float perTreeExpansionFactor = pspStand.GetTreesPerHectareExpansionFactor(); foreach (KeyValuePair <FiaCode, int[]> initialDbhQuantile in stand.InitialDbhQuantileBySpecies) { // accumulate stand state into quantiles int[] speciesQuantileCounts = new int[TestConstant.DbhQuantiles]; float[] speciesQuantileLiveExpansionFactor = new float[TestConstant.DbhQuantiles]; float[] speciesQuantileMaxDbh = new float[TestConstant.DbhQuantiles]; float[] speciesQuantileMeanDbh = new float[TestConstant.DbhQuantiles]; float[] speciesQuantileMinDbh = Enumerable.Repeat(Constant.CentimetersPerInch * TestConstant.Maximum.DiameterInInches, TestConstant.DbhQuantiles).ToArray(); Trees treesOfSpecies = stand.TreesBySpecies[initialDbhQuantile.Key]; for (int treeIndex = 0; treeIndex < treesOfSpecies.Count; ++treeIndex) { int quantile = initialDbhQuantile.Value[treeIndex]; int tag = treesOfSpecies.Tag[treeIndex]; PspTreeMeasurementSeries measurementSeries = pspStand.MeasurementsByTag[tag]; if (measurementSeries.DbhInCentimetersByYear.TryGetValue(measurementYear, out float dbh)) { speciesQuantileCounts[quantile] += 1; speciesQuantileLiveExpansionFactor[quantile] += perTreeExpansionFactor; speciesQuantileMaxDbh[quantile] = MathF.Max(speciesQuantileMaxDbh[quantile], dbh); speciesQuantileMeanDbh[quantile] += dbh; speciesQuantileMinDbh[quantile] = MathF.Min(speciesQuantileMinDbh[quantile], dbh); } } for (int quantile = 0; quantile < TestConstant.DbhQuantiles; ++quantile) { int quantileCount = speciesQuantileCounts[quantile]; if (quantileCount > 0) { speciesQuantileMeanDbh[quantile] = speciesQuantileMeanDbh[quantile] / (float)quantileCount; Debug.Assert(speciesQuantileMinDbh[quantile] / speciesQuantileMaxDbh[quantile] < 1.0001); Debug.Assert(speciesQuantileMinDbh[quantile] / speciesQuantileMeanDbh[quantile] < 1.0001); Debug.Assert(speciesQuantileMeanDbh[quantile] / speciesQuantileMaxDbh[quantile] < 1.0001); } } FiaCode species = initialDbhQuantile.Key; this.DeadExpansionFactorBySpecies.Add(species, new float[TestConstant.DbhQuantiles]); this.LiveExpansionFactorBySpecies.Add(species, speciesQuantileLiveExpansionFactor); this.MaxDbhInCmBySpecies.Add(species, speciesQuantileMaxDbh); this.MeanCrownRatioBySpecies.Add(species, new float[TestConstant.DbhQuantiles]); this.MeanDbhInCmBySpecies.Add(species, speciesQuantileMeanDbh); this.MeanHeightInMetersBySpecies.Add(species, new float[TestConstant.DbhQuantiles]); this.MinDbhInCmBySpecies.Add(species, speciesQuantileMinDbh); } }
private static FiaCode MaybeRemapToSupportedSpecies(FiaCode species, OrganonVariant variant) { if (variant.IsSpeciesSupported(species)) { return(species); } if (species == FiaCode.ChrysolepisChrysophyllaVarChrysophylla) { return(FiaCode.CornusNuttallii); } else { throw Trees.CreateUnhandledSpeciesException(species); } }
protected override float GetHeightToLargestCrownWidth(FiaCode species, float HT, float CR) { // DISTANCE ABOVE CROWN BASE TO LARGEST CROWN WIDTH float B1; float B2; switch (species) { // Hann, Bluhm, and Hibbs Red Alder Plantation Analysis case FiaCode.AlnusRubra: B1 = 0.63619616F; B2 = -1.2180562F; break; // Hann(1999) FS 45: 217-225 case FiaCode.PseudotsugaMenziesii: B1 = 0.062000F; B2 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.TsugaHeterophylla: case FiaCode.ThujaPlicata: B1 = 0.209806F; B2 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.AcerMacrophyllum: case FiaCode.CornusNuttallii: case FiaCode.Salix: B1 = 0.0F; B2 = 0.0F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float CL = CR * HT; float HLCW = HT - (1.0F - B1 * MathV.Exp(MathF.Pow(B2 * (1.0F - HT / 140.0F), 3))) * CL; return(HLCW); }
private void ParseRow(int rowIndex, string[] rowAsStrings) { if ((rowIndex == 0) || (rowAsStrings[Constant.Psp.ColumnIndex.Tag] == null)) { return; } int plot = Int32.Parse(rowAsStrings[Constant.Psp.ColumnIndex.Plot]); this.plotCount = Math.Max(this.plotCount, plot); int tag = Int32.Parse(rowAsStrings[Constant.Psp.ColumnIndex.Tag]); if (this.MeasurementsByTag.TryGetValue(tag, out PspTreeMeasurementSeries? tree) == false) { FiaCode species = FiaCodeExtensions.Parse(rowAsStrings[Constant.Psp.ColumnIndex.Species]); if (species == FiaCode.Alnus) { // remap Alnus viridis ssp sinuata to Alnus rubra as no Organon variant has support species = FiaCode.AlnusRubra; } tree = new PspTreeMeasurementSeries(tag, species); this.MeasurementsByTag.Add(tag, tree); } int status = Int32.Parse(rowAsStrings[Constant.Psp.ColumnIndex.Status]); int year = Int32.Parse(rowAsStrings[Constant.Psp.ColumnIndex.Year]); if (status < Constant.Psp.TreeStatus.Dead) // dead or not found trees lack diameter measurements { float dbhInCentimeters = float.Parse(rowAsStrings[Constant.Psp.ColumnIndex.Dbh]); Debug.Assert(dbhInCentimeters >= 5.0F); if (tree.DbhInCentimetersByYear.TryAdd(year, dbhInCentimeters) == false) { // in case of a conflict, use whichever DBH comes last for consistency with the code above. // For example, tree 8824 in RS39 has two 2013 records. tree.DbhInCentimetersByYear[year] = dbhInCentimeters; } } this.MeasurementYears.Add(year); }
protected override float GetLargestCrownWidth(FiaCode species, float MCW, float CR, float DBH, float HT) { float B1; float B2; float B3; switch (species) { // Hann(1997) FRL Research Contribution 17 case FiaCode.PseudotsugaMenziesii: B1 = 0.0F; B2 = 0.00436324F; B3 = 0.6020020F; break; // Hann(1997) FRL Research Contribution 17 case FiaCode.AbiesGrandis: B1 = 0.0F; B2 = 0.00308402F; B3 = 0.0F; break; // Hann(1997) FRL Research Contribution 17 // BUGBUG: all coefficients are zero case FiaCode.TsugaHeterophylla: B1 = 0.0F; B2 = 0.0F; B3 = 0.0F; break; // IC of Hann(1997) FRL Research Contribution 17 case FiaCode.ThujaPlicata: B1 = -0.2513890F; B2 = 0.006925120F; B3 = 0.985922F; break; // WH of Hann(1997) FRL Research Contribution 17 // BUGBUG: all coefficients are zero case FiaCode.TaxusBrevifolia: B1 = 0.0F; B2 = 0.0F; B3 = 0.0F; break; // Hann(1997) FRL Research Contribution 17 case FiaCode.ArbutusMenziesii: B1 = 0.118621F; B2 = 0.00384872F; B3 = 0.0F; break; // Hann(1997) FRL Research Contribution 17 case FiaCode.AcerMacrophyllum: B1 = 0.0F; B2 = 0.0F; B3 = 1.470180F; break; // Hann(1997) FRL Research Contribution 17 case FiaCode.QuercusGarryana: B1 = 0.3648110F; B2 = 0.0F; B3 = 0.0F; break; // Hann(1997) FRL Research Contribution 17 case FiaCode.AlnusRubra: B1 = 0.3227140F; B2 = 0.0F; B3 = 0.0F; break; // GC of Hann(1997) FRL Research Contribution 17 case FiaCode.CornusNuttallii: case FiaCode.Salix: B1 = 0.0F; B2 = 0.0F; B3 = 1.61440F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float CL = CR * HT; float LCW = MCW * MathV.Pow(CR, B1 + B2 * CL + B3 * (DBH / HT)); return(LCW); }
public PspTreeMeasurementSeries(int tag, FiaCode species) { this.DbhInCentimetersByYear = new SortedList <int, float>(Constant.Psp.DefaultNumberOfStandMeasurements); this.Species = species; this.Tag = tag; }
// OG only used for madrone public override float GetHeightToCrownBase(FiaCode species, float HT, float DBH, float CCFL, float BA, float siteIndex, float hemlockSiteIndex, float OG) { float B0; float B1; float B2; float B3; float B4; float B5; float B6 = 0.0F; switch (species) { // Hann and Hanus (2004) FS 34: 1193-2003 case FiaCode.PseudotsugaMenziesii: B0 = 6.18464679F; B1 = -0.00328764F; B2 = -0.00136555F; B3 = -1.19702220F; B4 = 3.17028263F; B5 = 0.0F; break; // Zumrawi and Hann (1989) FRL Research Paper 52 case FiaCode.AbiesGrandis: B0 = 1.04746F; B1 = -0.0066643F; B2 = -0.0067129F; B3 = 0.0F; B4 = 0.0F; B5 = 0.0F; break; // Johnson (2002) Willamette Industries Report case FiaCode.TsugaHeterophylla: B0 = 1.92682F; B1 = -0.00280478F; B2 = -0.0011939F; B3 = -0.513134F; B4 = 3.68901F; B5 = 0.00742219F; break; // Hann and Hanus(2002) OSU Department of Forest Management Internal Report #2 case FiaCode.ThujaPlicata: B0 = 4.49102006F; B1 = 0.0F; B2 = -0.00132412F; B3 = -1.01460531F; B4 = 0.0F; B5 = 0.01340624F; break; // Hanus, Hann, and Marshall (2000) FRL Research Contribution 29 case FiaCode.TaxusBrevifolia: B0 = 0.0F; B1 = 0.0F; B2 = 0.0F; B3 = 0.0F; B4 = 2.030940382F; B5 = 0.0F; break; // Hanus, Hann, and Marshall (2000) FRL Research Contribution 29 case FiaCode.ArbutusMenziesii: B0 = 2.955339267F; B1 = 0.0F; B2 = 0.0F; B3 = -0.798610738F; B4 = 3.095269471F; B5 = 0.0F; B6 = 0.700465646F; break; // BL Coefficients from Hanus, Hann, and Marshall (2000) FRL Research Contribution 29 case FiaCode.AcerMacrophyllum: B0 = 0.9411395642F; B1 = -0.00768402F; B2 = -0.005476131F; B3 = 0.0F; B4 = 0.0F; B5 = 0.0F; break; // Gould, Marshall, and Harrington (2008) West.J.Appl.For. 23: 26-33 case FiaCode.QuercusGarryana: B0 = 1.05786632F; B1 = 0.0F; B2 = -0.00183283F; B3 = -0.28644547F; B4 = 0.0F; B5 = 0.0F; break; // Hann and Hanus (2002) OSU Department of Forest Management Internal Report #1 case FiaCode.AlnusRubra: B0 = 0.56713781F; B1 = -0.010377976F; B2 = -0.002066036F; B3 = 0.0F; B4 = 1.39796223F; B5 = 0.0F; B6 = 0.0F; break; // Hanus, Hann, and Marshall (2000) FRL Research Contribution 29 case FiaCode.CornusNuttallii: case FiaCode.Salix: B0 = 0.0F; B1 = 0.0F; B2 = -0.005666559F; B3 = -0.745540494F; B4 = 0.0F; B5 = 0.038476613F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float HCB; if (species == FiaCode.TsugaHeterophylla) { // HCB = HT / (1.0F + MathV.Exp(B0 + B1 * HT + B2 * CCFL + B3 * MathV.Ln(BA) + B4 * (DBH / HT) + B5 * SI_2 + B6 * OG * OG)); HCB = HT / (1.0F + MathV.Exp(B0 + B1 * HT + B2 * CCFL + B3 * MathV.Ln(BA) + B4 * (DBH / HT) + B5 * hemlockSiteIndex)); } else { HCB = HT / (1.0F + MathV.Exp(B0 + B1 * HT + B2 * CCFL + B3 * MathV.Ln(BA) + B4 * (DBH / HT) + B5 * siteIndex + B6 * OG * OG)); } Debug.Assert(HCB >= 0.0F); Debug.Assert(HCB <= HT); return(HCB); }
protected override float GetCrownWidth(FiaCode species, float HLCW, float largestCrownWidth, float HT, float DBH, float XL) { float B1; float B2; float B3; switch (species) { // Hann(1999) FS 45: 217-225 case FiaCode.PseudotsugaMenziesii: B1 = 0.929973F; B2 = -0.135212F; B3 = -0.0157579F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.AbiesGrandis: B1 = 0.999291F; B2 = 0.0F; B3 = -0.0314603F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.TsugaHeterophylla: case FiaCode.ThujaPlicata: case FiaCode.TaxusBrevifolia: B1 = 0.629785F; B2 = 0.0F; B3 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.ArbutusMenziesii: B1 = 0.5F; B2 = 0.0F; B3 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.AcerMacrophyllum: B1 = 0.5F; B2 = 0.0F; B3 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.QuercusGarryana: B1 = 0.5F; B2 = 0.0F; B3 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.AlnusRubra: B1 = 0.5F; B2 = 0.0F; B3 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.CornusNuttallii: B1 = 0.5F; B2 = 0.0F; B3 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.Salix: B1 = 0.5F; B2 = 0.0F; B3 = 0.0F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float RP = (HT - XL) / (HT - HLCW); float RATIO = HT / DBH; if (species == FiaCode.PseudotsugaMenziesii) { if (RATIO > 50.0F) { RATIO = 50.0F; } } else if (species == FiaCode.AbiesGrandis) { if (RATIO > 31.0) { RATIO = 31.0F; } } float crownWidthMultiplier = MathV.Pow(RP, B1 + B2 * MathF.Sqrt(RP) + B3 * RATIO); Debug.Assert(crownWidthMultiplier >= 0.0F); Debug.Assert(crownWidthMultiplier <= 1.0F); float crownWidth = largestCrownWidth * crownWidthMultiplier; return(crownWidth); }
public override void GetHeightPredictionCoefficients(FiaCode species, out float B0, out float B1, out float B2) { switch (species) { // Hanus, Marshall, and Hann(1999) FRL Research Contribution 25 case FiaCode.PseudotsugaMenziesii: B0 = 7.262195456F; B1 = -5.899759104F; B2 = -0.287207389F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.AbiesGrandis: B0 = 7.42808F; B1 = -5.80832F; B2 = -0.240317F; break; // Hanus, Marshall, and Hann(1999) FRL Research Contribution 25 case FiaCode.TsugaHeterophylla: B0 = 6.555344622F; B1 = -5.137174162F; B2 = -0.364550800F; break; // Hann and Hanus(2002) OSU Department of Forest Management Internal Report #2 case FiaCode.ThujaPlicata: B0 = 6.14817441F; B1 = -5.40092761F; B2 = -0.38922036F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.TaxusBrevifolia: B0 = 9.30172F; B1 = -7.50951F; B2 = -0.100000F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.ArbutusMenziesii: B0 = 5.84487F; B1 = -3.84795F; B2 = -0.289213F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.AcerMacrophyllum: B0 = 5.21462F; B1 = -2.70252F; B2 = -0.354756F; break; // Gould, Marshall, and Harrington(2008) West.J.Appl.For. 23: 26-33 case FiaCode.QuercusGarryana: B0 = 4.69753118F; B1 = -3.51586969F; B2 = -0.57665068F; break; // Hann and Hanus(2002) OSU Department of Forest Management Internal Report #1 case FiaCode.AlnusRubra: B0 = 5.59759126F; B1 = -3.19942952F; B2 = -0.38783403F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.CornusNuttallii: B0 = 4.49727F; B1 = -2.07667F; B2 = -0.388650F; break; // Wang and Hann(1988) FRL Research Paper 51 case FiaCode.Salix: B0 = 4.88361F; B1 = -2.47605F; B2 = -0.309050F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } }
private void ParseRow(int rowIndex, string[] rowAsStrings) { if (rowIndex == 0) { // parse header for (int columnIndex = 0; columnIndex < rowAsStrings.Length; ++columnIndex) { string columnHeader = rowAsStrings[columnIndex]; if (String.IsNullOrWhiteSpace(columnHeader)) { break; } if (columnHeader.Equals("species", StringComparison.OrdinalIgnoreCase)) { this.speciesColumnIndex = columnIndex; } else if (columnHeader.Equals("plot", StringComparison.OrdinalIgnoreCase)) { this.plotColumnIndex = columnIndex; } else if (columnHeader.Equals("tree", StringComparison.OrdinalIgnoreCase)) { this.treeColumnIndex = columnIndex; } else if (columnHeader.Equals("age", StringComparison.OrdinalIgnoreCase)) { this.ageColumnIndex = columnIndex; } else if (columnHeader.StartsWith("dbh", StringComparison.OrdinalIgnoreCase)) { this.dbhColumnIndex = columnIndex; if (columnHeader.EndsWith("mm", StringComparison.Ordinal)) { this.dbhScaleFactor = 0.1F; // otherwise, assume cm } } else if (columnHeader.Equals("height", StringComparison.OrdinalIgnoreCase) || columnHeader.Equals("height, m", StringComparison.OrdinalIgnoreCase)) { this.heightColumnIndex = columnIndex; } else if (columnHeader.Equals("height, dm", StringComparison.OrdinalIgnoreCase)) { this.heightColumnIndex = columnIndex; this.heightScaleFactor = 0.1F; // otherwise, assume m } else if (columnHeader.StartsWith("expansion factor", StringComparison.OrdinalIgnoreCase)) { this.expansionFactorColumnIndex = columnIndex; } else if (columnHeader.Equals("treecond", StringComparison.OrdinalIgnoreCase)) { this.treeConditionColumnIndex = columnIndex; } else { // ignore column for now } } // check header if (this.speciesColumnIndex < 0) { throw new ArgumentOutOfRangeException(nameof(this.speciesColumnIndex), "Species column not found."); } if (this.plotColumnIndex < 0) { throw new ArgumentOutOfRangeException(nameof(this.plotColumnIndex), "Plot column not found."); } if (this.treeColumnIndex < 0) { throw new ArgumentOutOfRangeException(nameof(this.treeColumnIndex), "Tree number column not found."); } if (this.ageColumnIndex < 0) { throw new ArgumentOutOfRangeException(nameof(this.ageColumnIndex), "Tree age column not found."); } if (this.dbhColumnIndex < 0) { throw new ArgumentOutOfRangeException(nameof(this.dbhColumnIndex), "DBH column not found."); } if (this.heightColumnIndex < 0) { throw new ArgumentOutOfRangeException(nameof(this.heightColumnIndex), "Height column not found."); } if ((this.expansionFactorColumnIndex < 0) && (this.defaultExpansionFactor <= 0.0F)) { throw new ArgumentOutOfRangeException(nameof(this.expansionFactorColumnIndex), "Expansion factor column not found."); } return; } if (rowAsStrings[this.treeColumnIndex] == null) { return; // assume end of data in file } // filter by plot int plot = Int32.Parse(rowAsStrings[this.plotColumnIndex]); bool treeInPlotList = false; for (int idIndex = 0; idIndex < this.plotIDs.Count; ++idIndex) { if (plot == this.plotIDs[idIndex]) { treeInPlotList = true; break; } } if (treeInPlotList == false) { // tree is not in the list of plots specified return; } // exclude dead trees if (this.treeConditionColumnIndex >= 0) { if (String.IsNullOrWhiteSpace(rowAsStrings[this.treeConditionColumnIndex]) == false) { int treeCondition = Int32.Parse(rowAsStrings[this.treeConditionColumnIndex]); if (treeCondition == Constant.MalcolmKnapp.TreeCondition.Dead) { return; } } } // parse data int age = Int32.Parse(rowAsStrings[this.ageColumnIndex]); if (this.byAge.TryGetValue(age, out Stand? plotAtAge) == false) { plotAtAge = new Stand(); this.byAge.Add(age, plotAtAge); } FiaCode species = FiaCodeExtensions.Parse(rowAsStrings[this.speciesColumnIndex]); if (plotAtAge.TreesBySpecies.TryGetValue(species, out Trees? treesOfSpecies) == false) { treesOfSpecies = new Trees(species, 1, Units.Metric); plotAtAge.TreesBySpecies.Add(species, treesOfSpecies); } int tag = Int32.Parse(rowAsStrings[this.treeColumnIndex]); string dbhAsString = rowAsStrings[this.dbhColumnIndex]; float dbh = Single.NaN; if ((String.IsNullOrWhiteSpace(dbhAsString) == false) && (String.Equals(dbhAsString, "NA", StringComparison.OrdinalIgnoreCase) == false)) { dbh = this.dbhScaleFactor * Single.Parse(dbhAsString); } string heightAsString = rowAsStrings[this.heightColumnIndex]; float height = Single.NaN; if ((String.IsNullOrWhiteSpace(heightAsString) == false) && (String.Equals(heightAsString, "NA", StringComparison.OrdinalIgnoreCase) == false)) { height = this.heightScaleFactor * Single.Parse(heightAsString); } float expansionFactor = this.defaultExpansionFactor; if (this.expansionFactorColumnIndex >= 0) { expansionFactor = Single.Parse(rowAsStrings[this.expansionFactorColumnIndex]); } // add trees with placeholder crown ratio treesOfSpecies.Add(tag, dbh, height, this.defaultCrownRatio, expansionFactor); }
protected override float GetCrownWidth(FiaCode species, float HLCW, float largestCrownWidth, float HT, float DBH, float XL) { float B1; float B2; float B3; switch (species) { // Hann, Bluhm, and Hibbs Red Alder Plantation Analysis case FiaCode.AlnusRubra: B1 = 0.63420194F; B2 = 0.17649614F; B3 = -0.02315018F; break; // Hann(1999) FS 45: 217-225 case FiaCode.PseudotsugaMenziesii: B1 = 0.929973F; B2 = -0.135212F; B3 = -0.0157579F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.ThujaPlicata: case FiaCode.TsugaHeterophylla: B1 = 0.629785F; B2 = 0.0F; B3 = 0.0F; break; // Hann and Hanus(2001) FRL Research Contribution 34 case FiaCode.AcerMacrophyllum: case FiaCode.CornusNuttallii: case FiaCode.Salix: B1 = 0.5F; B2 = 0.0F; B3 = 0.0F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float RP = (HT - XL) / (HT - HLCW); float RATIO = HT / DBH; if (species == FiaCode.PseudotsugaMenziesii) { if (RATIO > 50.0F) { RATIO = 50.0F; } } float crownWidthMultiplier = MathV.Pow(RP, B1 + B2 * MathF.Sqrt(RP) + B3 * RATIO); Debug.Assert(crownWidthMultiplier >= 0.0F); Debug.Assert(crownWidthMultiplier <= 3.5F); // BUGBUG: red alder coefficients inherited from Fortran lead to negative powers of RP float crownWidth = largestCrownWidth * crownWidthMultiplier; return(crownWidth); }
protected static void Verify(ExpectedTreeChanges expectedGrowth, OrganonWarnings expectedWarnings, TestStand stand, OrganonVariant variant) { Assert.IsTrue(stand.AgeInYears >= 0); Assert.IsTrue(stand.AgeInYears <= TestConstant.Maximum.StandAgeInYears); Assert.IsTrue(stand.BreastHeightAgeInYears >= 0); Assert.IsTrue(stand.BreastHeightAgeInYears <= TestConstant.Maximum.StandAgeInYears); Assert.IsTrue(stand.NumberOfPlots >= 1); Assert.IsTrue(stand.NumberOfPlots <= 36); Assert.IsTrue(stand.TreesBySpecies.Count > 0); Assert.IsTrue(stand.GetTreeRecordCount() > 0); foreach (Trees treesOfSpecies in stand.TreesBySpecies.Values) { FiaCode species = treesOfSpecies.Species; Assert.IsTrue(Enum.IsDefined(typeof(FiaCode), species)); for (int treeIndex = 0; treeIndex < treesOfSpecies.Count; ++treeIndex) { // primary tree data float crownRatio = treesOfSpecies.CrownRatio[treeIndex]; Assert.IsTrue(crownRatio >= 0.0F); Assert.IsTrue(crownRatio <= 1.0F); float dbhInInches = treesOfSpecies.Dbh[treeIndex]; Assert.IsTrue(dbhInInches >= 0.0F); Assert.IsTrue(dbhInInches <= TestConstant.Maximum.DiameterInInches); float expansionFactor = treesOfSpecies.LiveExpansionFactor[treeIndex]; Assert.IsTrue(expansionFactor >= 0.0F); Assert.IsTrue(expansionFactor <= TestConstant.Maximum.ExpansionFactor); float heightInFeet = treesOfSpecies.Height[treeIndex]; Assert.IsTrue(heightInFeet >= 0.0F); Assert.IsTrue(heightInFeet <= TestConstant.Maximum.HeightInFeet); float deadExpansionFactor = treesOfSpecies.DeadExpansionFactor[treeIndex]; Assert.IsTrue(deadExpansionFactor >= 0.0F); Assert.IsTrue(deadExpansionFactor <= TestConstant.Maximum.ExpansionFactor); Assert.IsTrue(expansionFactor + deadExpansionFactor <= TestConstant.Maximum.ExpansionFactor); // diameter and height growth float diameterGrowthInInches = treesOfSpecies.DbhGrowth[treeIndex]; if (expectedGrowth.HasFlag(ExpectedTreeChanges.DiameterGrowth)) { Assert.IsTrue(diameterGrowthInInches > 0.0F, "{0}: {1} {2} did not grow in diameter.", variant.TreeModel, treesOfSpecies.Species, treeIndex); Assert.IsTrue(diameterGrowthInInches <= 0.1F * TestConstant.Maximum.DiameterInInches); } else if (expectedGrowth.HasFlag(ExpectedTreeChanges.DiameterGrowthOrNoChange)) { Assert.IsTrue(diameterGrowthInInches >= 0.0F); Assert.IsTrue(diameterGrowthInInches <= 0.1F * TestConstant.Maximum.DiameterInInches); } else { Assert.IsTrue(diameterGrowthInInches == 0.0F); } float heightGrowthInFeet = treesOfSpecies.HeightGrowth[treeIndex]; if (expectedGrowth.HasFlag(ExpectedTreeChanges.HeightGrowth)) { Assert.IsTrue(heightGrowthInFeet > 0.0F, "{0}: {1} {2} did not grow in height.", variant.TreeModel, treesOfSpecies.Species, treeIndex); Assert.IsTrue(heightGrowthInFeet <= 0.1F * TestConstant.Maximum.HeightInFeet); } else if (expectedGrowth.HasFlag(ExpectedTreeChanges.HeightGrowthOrNoChange)) { Assert.IsTrue(heightGrowthInFeet >= 0.0F, "{0}: {1} {2} decreased in height.", variant.TreeModel, treesOfSpecies.Species, treeIndex); Assert.IsTrue(heightGrowthInFeet <= 0.1F * TestConstant.Maximum.HeightInFeet); } else { Assert.IsTrue(heightGrowthInFeet == 0.0F); } // for now, ignore warnings on height exceeding potential height // Assert.IsTrue(stand.TreeWarnings[treeWarningIndex] == 0); } for (int treeIndex = treesOfSpecies.Count; treeIndex < treesOfSpecies.Capacity; ++treeIndex) { Assert.IsTrue(treesOfSpecies.CrownRatio[treeIndex] == 0.0F); Assert.IsTrue(treesOfSpecies.Dbh[treeIndex] == 0.0F); Assert.IsTrue(treesOfSpecies.DeadExpansionFactor[treeIndex] == 0.0F); Assert.IsTrue(treesOfSpecies.Height[treeIndex] == 0.0F); Assert.IsTrue(treesOfSpecies.LiveExpansionFactor[treeIndex] == 0.0F); } } Assert.IsTrue(stand.Warnings.BigSixHeightAbovePotential == false); Assert.IsTrue(stand.Warnings.LessThan50TreeRecords == expectedWarnings.HasFlag(OrganonWarnings.LessThan50TreeRecords)); Assert.IsTrue(stand.Warnings.HemlockSiteIndexOutOfRange == expectedWarnings.HasFlag(OrganonWarnings.HemlockSiteIndex)); Assert.IsTrue(stand.Warnings.OtherSpeciesBasalAreaTooHigh == false); Assert.IsTrue(stand.Warnings.SiteIndexOutOfRange == false); if (variant.TreeModel != TreeModel.OrganonSmc) { // for now, ignore SMC warning for breast height age < 10 Assert.IsTrue(stand.Warnings.TreesOld == false); } // for now, ignore stand.Warnings.TreesYoung }
public override float GetMaximumCrownWidth(FiaCode species, float D, float H) { float B0; float B1; float B2; float K; float PKDBH; switch (species) { // Hann, Bluhm, and Hibbs Red Alder Plantation Analysis case FiaCode.AlnusRubra: B0 = 2.320746348F; B1 = 6.661401926F; B2 = 0.0F; K = 0.6F; PKDBH = 999.99F; break; // Paine and Hann(1982) FRL Research Paper 46 case FiaCode.PseudotsugaMenziesii: B0 = 4.6198F; B1 = 1.8426F; B2 = -0.011311F; K = 1.0F; PKDBH = 81.45F; break; // Paine and Hann(1982) FRL Research Paper 46 case FiaCode.TsugaHeterophylla: B0 = 4.5652F; B1 = 1.4147F; B2 = 0.0F; K = 1.0F; PKDBH = 999.99F; break; // Smith(1966) Proc. 6th World Forestry Conference case FiaCode.ThujaPlicata: B0 = 4.0F; B1 = 1.65F; B2 = 0.0F; K = 1.0F; PKDBH = 999.99F; break; // Ek(1974) School of Natural Res., U.Wisc., Forestry Res. Notes. case FiaCode.AcerMacrophyllum: B0 = 4.0953F; B1 = 2.3849F; B2 = -0.011630F; K = 1.0F; PKDBH = 102.53F; break; // GC of Paine and Hann (1982) FRL Research Paper 46 // GC of Paine and Hann(1982) FRL Research Paper 46 case FiaCode.CornusNuttallii: case FiaCode.Salix: B0 = 2.9793895F; B1 = 1.5512443F; B2 = -0.01416129F; K = 1.0F; PKDBH = 54.77F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float DBH = D; if (DBH > PKDBH) { DBH = PKDBH; } float HT = H; float MCW; if (HT < 4.501F) { MCW = HT / 4.5F * B0; } else { MCW = B0 + B1 * MathV.Pow(DBH, K) + B2 * DBH * DBH; } return(MCW); }
protected override float GetLargestCrownWidth(FiaCode species, float MCW, float CR, float DBH, float HT) { float B0; float B1; float B2; float B3; switch (species) { // Hann, Bluhm, and Hibbs Red Alder Plantation Analysis case FiaCode.AlnusRubra: B0 = 0.78160725F; B1 = 0.44092737F; B2 = 0.0F; B3 = 0.0F; break; // Hann(1997) FRL Research Contribution 17 case FiaCode.PseudotsugaMenziesii: B0 = 1.0F; B1 = 0.0F; B2 = 0.00436324F; B3 = 0.6020020F; break; // Hann(1997) FRL Research Contribution 17 case FiaCode.TsugaHeterophylla: B0 = 1.0F; B1 = 0.0F; B2 = 0.0F; B3 = 0.0F; break; // IC of Hann(1997) FRL Research Contribution 17 case FiaCode.ThujaPlicata: B0 = 1.0F; B1 = -0.2513890F; B2 = 0.006925120F; B3 = 0.985922F; break; // Hann(1997) FRL Research Contribution 17 case FiaCode.AcerMacrophyllum: B0 = 1.0F; B1 = 0.0F; B2 = 0.0F; B3 = 1.47018F; break; // GC of Hann(1997) FRL Research Contribution 17 case FiaCode.CornusNuttallii: case FiaCode.Salix: B0 = 1.0F; B1 = 0.0F; B2 = 0.0F; B3 = 1.61440F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float CL = CR * HT; float LCW = B0 * MCW * MathV.Pow(CR, B1 + B2 * CL + B3 * (DBH / HT)); return(LCW); }
public static float EstimateHeightInFeet(FiaCode species, float dbhInInches) { return(TestConstant.FeetPerMeter * TreeRecord.EstimateHeightInMeters(species, dbhInInches)); }
// OG unused public override float GetHeightToCrownBase(FiaCode species, float HT, float DBH, float CCFL, float BA, float siteIndex, float hemlockSiteIndex, float OG) { float B0; float B1; float B2; float B3; float B4; float B5; // float B6 = 0.0F; // always zero float K = 0.0F; switch (species) { // Hann, Bluhm, and Hibbs (2011) Development and Evaluation of the Tree-Level Equations and Their Combined // Stand-Level Behavior in the Red Alder Plantation Version of Organon case FiaCode.AlnusRubra: B0 = 3.73113020F; B1 = -0.021546486F; B2 = -0.0016572840F; B3 = -1.0649544F; B4 = 7.47699601F; B5 = 0.0252953320F; K = 1.6F; break; // Hann and Hanus (2004) FS 34: 1193-2003 case FiaCode.PseudotsugaMenziesii: B0 = 6.18464679F; B1 = -0.00328764F; B2 = -0.00136555F; B3 = -1.19702220F; B4 = 3.17028263F; B5 = 0.0F; break; // Johnson (2002) Willamette Industries Report case FiaCode.TsugaHeterophylla: B0 = 1.92682F; B1 = -0.00280478F; B2 = -0.0011939F; B3 = -0.513134F; B4 = 3.68901F; B5 = 0.00742219F; break; // Hann and Hanus (2002) OSU Department of Forest Management Internal Report #2 case FiaCode.ThujaPlicata: B0 = 4.49102006F; B1 = 0.0F; B2 = -0.00132412F; B3 = -1.01460531F; B4 = 0.0F; B5 = 0.01340624F; break; // Hanus, Hann, and Marshall (2000) FRL Research Contribution 29 case FiaCode.AcerMacrophyllum: B0 = 0.9411395642F; B1 = -0.00768402F; B2 = -0.005476131F; B3 = 0.0F; B4 = 0.0F; B5 = 0.0F; break; // Hanus, Hann, and Marshall (2000) FRL Research Contribution 29 case FiaCode.CornusNuttallii: case FiaCode.Salix: B0 = 0.0F; B1 = 0.0F; B2 = -0.005666559F; B3 = -0.745540494F; B4 = 0.0F; B5 = 0.038476613F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float HCB; if (species == FiaCode.AlnusRubra) { // HCB = (HT - K) / (1.0F + MathV.Exp(B0 + B1 * HT + B2 * CCFL + B3 * MathV.Ln(BA) + B4 * (DBH / HT) + B5 * SI_1 + B6 * OG * OG)) + K; HCB = (HT - K) / (1.0F + MathV.Exp(B0 + B1 * HT + B2 * CCFL + B3 * MathV.Ln(BA) + B4 * (DBH / HT) + B5 * siteIndex)) + K; } else { float SITE = hemlockSiteIndex; if (species == FiaCode.TsugaHeterophylla) { SITE = 0.480F + (1.110F * (hemlockSiteIndex + 4.5F)); } // HCB = HT / (1.0F + MathV.Exp(B0 + B1 * HT + B2 * CCFL + B3 * MathV.Ln(BA) + B4 * (DBH / HT) + B5 * SITE + B6 * OG * OG)); HCB = HT / (1.0F + MathV.Exp(B0 + B1 * HT + B2 * CCFL + B3 * MathV.Ln(BA) + B4 * (DBH / HT) + B5 * SITE)); } Debug.Assert(HCB >= 0.0F); Debug.Assert(HCB <= HT); return(HCB); }
public override float GetMaximumCrownWidth(FiaCode species, float D, float H) { float B0; float B1; float B2; float PKDBH; switch (species) { // Paine and Hann(1982) FRL Research Paper 46 case FiaCode.PseudotsugaMenziesii: B0 = 4.6198F; B1 = 1.8426F; B2 = -0.011311F; PKDBH = 81.45F; break; // Paine and Hann(1982) FRL Research Paper 46 case FiaCode.AbiesGrandis: B0 = 6.1880F; B1 = 1.0069F; B2 = 0.0F; PKDBH = 999.99F; break; // Paine and Hann(1982) FRL Research Paper 46 case FiaCode.TsugaHeterophylla: B0 = 4.5652F; B1 = 1.4147F; B2 = 0.0F; PKDBH = 999.99F; break; // Smith(1966) Proc. 6th World Forestry Conference case FiaCode.ThujaPlicata: B0 = 4.0F; B1 = 1.65F; B2 = 0.0F; PKDBH = 999.99F; break; // WH of Paine and Hann(1982) FRL Research Paper 46 case FiaCode.TaxusBrevifolia: B0 = 4.5652F; B1 = 1.4147F; B2 = 0.0F; PKDBH = 999.99F; break; // Paine and Hann(1982) FRL Research Paper 46 case FiaCode.ArbutusMenziesii: B0 = 3.4298629F; B1 = 1.3532302F; B2 = 0.0F; PKDBH = 999.99F; break; // Ek(1974) School of Natural Res., U.Wisc., Forestry Res. Notes. case FiaCode.AcerMacrophyllum: B0 = 4.0953F; B1 = 2.3849F; B2 = -0.011630F; PKDBH = 102.53F; break; // Paine and Hann (1982) FRL Research Paper 46 case FiaCode.QuercusGarryana: B0 = 3.0785639F; B1 = 1.9242211F; B2 = 0.0F; PKDBH = 999.99F; break; // Smith(1966) Proc. 6th World Forestry Conference case FiaCode.AlnusRubra: B0 = 8.0F; B1 = 1.53F; B2 = 0.0F; PKDBH = 999.99F; break; // GC of Paine and Hann(1982) FRL Research Paper 46 case FiaCode.CornusNuttallii: case FiaCode.Salix: B0 = 2.9793895F; B1 = 1.5512443F; B2 = -0.01416129F; PKDBH = 54.77F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float DBH = D; if (DBH > PKDBH) { DBH = PKDBH; } float HT = H; float MCW; if (HT < 4.501F) { MCW = HT / 4.5F * B0; } else { MCW = B0 + B1 * DBH + B2 * DBH * DBH; } return(MCW); }
public TreeQuantiles(TestStand stand) : this() { foreach (KeyValuePair <FiaCode, int[]> initialDbhQuantile in stand.InitialDbhQuantileBySpecies) { // accumulate stand state into quantiles Trees treesOfSpecies = stand.TreesBySpecies[initialDbhQuantile.Key]; int[] quantileCounts = new int[TestConstant.DbhQuantiles]; float[] quantileDeadExpansionFactor = new float[TestConstant.DbhQuantiles]; float[] quantileLiveExpansionFactor = new float[TestConstant.DbhQuantiles]; float[] quantileMaxDbh = new float[TestConstant.DbhQuantiles]; float[] quantileMeanCrownRatio = new float[TestConstant.DbhQuantiles]; float[] quantileMeanDbh = new float[TestConstant.DbhQuantiles]; float[] quantileMeanHeight = new float[TestConstant.DbhQuantiles]; float[] quantileMinDbh = Enumerable.Repeat(Constant.CentimetersPerInch * TestConstant.Maximum.DiameterInInches, TestConstant.DbhQuantiles).ToArray(); for (int treeIndex = 0; treeIndex < initialDbhQuantile.Value.Length; ++treeIndex) { int quantile = initialDbhQuantile.Value[treeIndex]; float liveExpansionFactor = treesOfSpecies.LiveExpansionFactor[treeIndex]; if (liveExpansionFactor > 0.0F) { quantileCounts[quantile] += 1; quantileDeadExpansionFactor[quantile] += treesOfSpecies.DeadExpansionFactor[treeIndex]; quantileLiveExpansionFactor[quantile] += liveExpansionFactor; quantileMaxDbh[quantile] = MathF.Max(quantileMaxDbh[quantile], treesOfSpecies.Dbh[treeIndex]); quantileMeanCrownRatio[quantile] += liveExpansionFactor * treesOfSpecies.CrownRatio[treeIndex]; quantileMeanDbh[quantile] += liveExpansionFactor * treesOfSpecies.Dbh[treeIndex]; quantileMeanHeight[quantile] += liveExpansionFactor * treesOfSpecies.Height[treeIndex]; quantileMinDbh[quantile] = MathF.Min(quantileMinDbh[quantile], treesOfSpecies.Dbh[treeIndex]); } } // take averages and convert from Organon's English units to metric for (int quantile = 0; quantile < TestConstant.DbhQuantiles; ++quantile) { int quantileCount = quantileCounts[quantile]; if (quantileCount > 0) { float liveExpansionFactor = quantileLiveExpansionFactor[quantile]; quantileDeadExpansionFactor[quantile] = TestConstant.AcresPerHectare * quantileDeadExpansionFactor[quantile]; quantileLiveExpansionFactor[quantile] = TestConstant.AcresPerHectare * liveExpansionFactor; quantileMaxDbh[quantile] = Constant.CentimetersPerInch * quantileMaxDbh[quantile]; quantileMeanCrownRatio[quantile] /= liveExpansionFactor; quantileMeanDbh[quantile] = Constant.CentimetersPerInch * quantileMeanDbh[quantile] / liveExpansionFactor; quantileMeanHeight[quantile] = Constant.MetersPerFoot * quantileMeanHeight[quantile] / liveExpansionFactor; quantileMinDbh[quantile] = Constant.CentimetersPerInch * quantileMinDbh[quantile]; Debug.Assert(quantileMinDbh[quantile] / quantileMaxDbh[quantile] < 1.0001); Debug.Assert(quantileMinDbh[quantile] / quantileMeanDbh[quantile] < 1.0001); Debug.Assert(quantileMeanDbh[quantile] / quantileMaxDbh[quantile] < 1.0001); } } FiaCode species = initialDbhQuantile.Key; this.DeadExpansionFactorBySpecies.Add(species, quantileDeadExpansionFactor); this.LiveExpansionFactorBySpecies.Add(species, quantileLiveExpansionFactor); this.MaxDbhInCmBySpecies.Add(species, quantileMaxDbh); this.MeanCrownRatioBySpecies.Add(species, quantileMeanCrownRatio); this.MeanDbhInCmBySpecies.Add(species, quantileMeanDbh); this.MeanHeightInMetersBySpecies.Add(species, quantileMeanHeight); this.MinDbhInCmBySpecies.Add(species, quantileMinDbh); } }
protected override float GetMaximumHeightToCrownBase(FiaCode species, float HT, float CCFL) { float B0; float B1; float B2; float B3; float LIMIT; switch (species) { case FiaCode.PseudotsugaMenziesii: B0 = 0.96F; B1 = 0.26F; B2 = -0.34758F; B3 = 1.5F; LIMIT = 0.95F; break; case FiaCode.AbiesGrandis: B0 = 0.96F; B1 = 0.31F; B2 = -2.450718394F; B3 = 1.0F; LIMIT = 0.95F; break; case FiaCode.TsugaHeterophylla: B0 = 1.01F; B1 = 0.36F; B2 = -0.944528054F; B3 = 0.6F; LIMIT = 0.96F; break; case FiaCode.ThujaPlicata: B0 = 0.96F; B1 = 0.31F; B2 = -1.059636222F; B3 = 1.0F; LIMIT = 0.95F; break; case FiaCode.TaxusBrevifolia: B0 = 0.85F; B1 = 0.35F; B2 = -0.922868139F; B3 = 0.8F; LIMIT = 0.80F; break; case FiaCode.ArbutusMenziesii: B0 = 0.981F; B1 = 0.161F; B2 = -1.73666044F; B3 = 1.0F; LIMIT = 0.98F; break; case FiaCode.AcerMacrophyllum: B0 = 1.0F; B1 = 0.45F; B2 = -1.020016685F; B3 = 1.0F; LIMIT = 0.95F; break; case FiaCode.QuercusGarryana: B0 = 1.0F; B1 = 0.3F; B2 = -0.95634399F; B3 = 1.1F; LIMIT = 0.98F; break; case FiaCode.AlnusRubra: B0 = 0.93F; B1 = 0.18F; B2 = -0.928243505F; B3 = 1.0F; LIMIT = 0.92F; break; case FiaCode.CornusNuttallii: B0 = 1.0F; B1 = 0.45F; B2 = -1.020016685F; B3 = 1.0F; LIMIT = 0.95F; break; case FiaCode.Salix: B0 = 0.985F; B1 = 0.285F; B2 = -0.969750805F; B3 = 0.9F; LIMIT = 0.98F; break; default: throw Trees.CreateUnhandledSpeciesException(species); } float MAXBR = B0 - B1 * MathV.Exp(B2 * MathV.Pow(CCFL / 100.0F, B3)); if (MAXBR > LIMIT) { MAXBR = LIMIT; } float MAXHCB = MAXBR * HT; return(MAXHCB); }
public TestStand ToStand(OrganonConfiguration configuration, float siteIndex) { int firstPlotMeasurementYear = this.GetFirstMeasurementYear(); // populate Organon version of stand // Currently, PSP stands are assumed to have IsEvenAge = false, which causes Organon to require a stand age of // zero years be passed. TestStand stand = new TestStand(configuration.Variant, 0, siteIndex) { NumberOfPlots = this.plotCount }; foreach (KeyValuePair <FiaCode, int> speciesCount in this.CountTreesBySpecies()) { // skip any unsupported species as they should be remapped in following loops if (configuration.Variant.IsSpeciesSupported(speciesCount.Key) == false) { continue; } // metric PSP data is converted to English units for Organon below stand.TreesBySpecies.Add(speciesCount.Key, new Trees(speciesCount.Key, speciesCount.Value, Units.English)); } float fixedPlotExpansionFactor = this.GetTreesPerAcreExpansionFactor(); foreach (PspTreeMeasurementSeries tree in this.MeasurementsByTag.Values) { int firstTreeMeasurementYear = tree.GetFirstMeasurementYear(); Debug.Assert(firstTreeMeasurementYear >= firstPlotMeasurementYear); if (firstTreeMeasurementYear != firstPlotMeasurementYear) { // tree is ingrowth List <PspTreeMeasurementSeries> ingrowthForYear = this.IngrowthByYear.GetOrAdd(firstTreeMeasurementYear); ingrowthForYear.Add(tree); continue; } FiaCode species = PspStand.MaybeRemapToSupportedSpecies(tree.Species, configuration.Variant); Trees treesOfSpecies = stand.TreesBySpecies[species]; Debug.Assert(treesOfSpecies.Capacity > treesOfSpecies.Count); float dbhInInches = TestConstant.InchesPerCm * tree.DbhInCentimetersByYear[firstPlotMeasurementYear]; float heightInFeet = TreeRecord.EstimateHeightInFeet(species, dbhInInches); treesOfSpecies.Add(tree.Tag, dbhInInches, heightInFeet, TestConstant.Default.CrownRatio, fixedPlotExpansionFactor); } // estimate crown ratios OrganonStandDensity standDensity = new OrganonStandDensity(stand, configuration.Variant); Dictionary <FiaCode, int> indexBySpecies = new Dictionary <FiaCode, int>(); foreach (PspTreeMeasurementSeries tree in this.MeasurementsByTag.Values) { int firstTreeMeasurementYear = tree.GetFirstMeasurementYear(); if (firstTreeMeasurementYear != firstPlotMeasurementYear) { continue; } if (indexBySpecies.TryGetValue(tree.Species, out int treeIndex) == false) { treeIndex = 0; indexBySpecies.Add(tree.Species, treeIndex); } FiaCode species = PspStand.MaybeRemapToSupportedSpecies(tree.Species, configuration.Variant); Trees treesOfSpecies = stand.TreesBySpecies[species]; treesOfSpecies.CrownRatio[treeIndex] = tree.EstimateInitialCrownRatio(standDensity); indexBySpecies[tree.Species] = ++treeIndex; } // complete stand initialization stand.EnsureSiteIndicesSet(configuration.Variant); stand.SetQuantiles(); stand.SetRedAlderSiteIndexAndGrowthEffectiveAge(); stand.SetSdiMax(configuration); return(stand); }