public void Plot14ImmediateThin() { int thinPeriod = 1; int lastPeriod = 4; bool useScaledVolume = false; PlotsWithHeight plot14 = PublicApi.GetPlot14(); OrganonConfiguration configuration = OrganonTest.CreateOrganonConfiguration(new OrganonVariantNwo()); OrganonStand stand = plot14.ToOrganonStand(configuration, 30, 130.0F); stand.PlantingDensityInTreesPerHectare = TestConstant.Plot14ReplantingDensityInTreesPerHectare; configuration.Treatments.Harvests.Add(new ThinByPrescription(thinPeriod) { FromAbovePercentage = 0.0F, ProportionalPercentage = 30.0F, FromBelowPercentage = 0.0F }); OrganonStandTrajectory thinnedTrajectory = new OrganonStandTrajectory(stand, configuration, TimberValue.Default, lastPeriod, useScaledVolume); AssertNullable.IsNotNull(thinnedTrajectory.StandByPeriod[0]); Assert.IsTrue(thinnedTrajectory.StandByPeriod[0] !.GetTreeRecordCount() == 222); thinnedTrajectory.Simulate(); // verify thinned trajectory // 0 1 2 3 4 float[] minimumThinnedQmd = new float[] { 9.16F, 10.47F, 11.64F, 12.64F, 13.52F }; // in // 0 1 2 3 4 float[] minimumThinnedTopHeight = new float[] { 92.9F, 101.4F, 110.4F, 118.9F, 126.7F }; // ft float[] minimumThinnedVolume; if (thinnedTrajectory.UseScaledVolume) { minimumThinnedVolume = new float[] { 51.59F, 51.75F, 66.71F, 81.88F, 97.72F }; // Poudel 2018 + Scribner long log net MBF/ha } else { // 0 1 2 3 4 minimumThinnedVolume = new float[] { 43.74F, 49.00F, 68.86F, 87.95F, 105.8F }; // Browning 1977 (FIA) MBF/ha } PublicApi.Verify(thinnedTrajectory, minimumThinnedQmd, minimumThinnedTopHeight, minimumThinnedVolume, thinPeriod, lastPeriod, 65, 70, configuration.Variant.TimeStepInYears); PublicApi.Verify(thinnedTrajectory, minimumThinnedVolume, thinPeriod); Assert.IsTrue(thinnedTrajectory.GetFirstHarvestAge() == 30); }
private static void Verify(OrganonStandTrajectory trajectory, float[] minimumQmd, float[] minimumTopHeight, float[] minimumStandingVolumeScribner, int thinPeriod, int lastPeriod, int minTrees, int maxTrees, int timeStepInYears) { Assert.IsTrue(trajectory.BasalAreaRemoved.Length == lastPeriod + 1); Assert.IsTrue(trajectory.BasalAreaRemoved[0] == 0.0F); Assert.IsTrue(trajectory.HarvestPeriods == thinPeriod + 1); // BUGBUG: clean off by one semantic Assert.IsTrue(trajectory.ThinningVolume.ScribnerTotal[0] == 0.0F); Assert.IsTrue(trajectory.ThinningVolume.ScribnerTotal.Length == lastPeriod + 1); PublicApi.Verify(trajectory.IndividualTreeSelectionBySpecies, thinPeriod, minTrees, maxTrees); Assert.IsTrue(String.IsNullOrEmpty(trajectory.Name) == false); Assert.IsTrue(trajectory.PeriodLengthInYears == timeStepInYears); Assert.IsTrue(trajectory.PlanningPeriods == lastPeriod + 1); // BUGBUG: clean off by one semantic float qmdTolerance = 1.01F; float topHeightTolerance = 1.01F; float volumeTolerance = 1.01F; for (int periodIndex = 0; periodIndex < trajectory.PlanningPeriods; ++periodIndex) { Assert.IsTrue(trajectory.DensityByPeriod[periodIndex].BasalAreaPerAcre > 0.0F); Assert.IsTrue(trajectory.DensityByPeriod[periodIndex].BasalAreaPerAcre <= TestConstant.Maximum.TreeBasalAreaLarger); Assert.IsTrue(trajectory.StandingVolume.ScribnerTotal[periodIndex] > minimumStandingVolumeScribner[periodIndex]); Assert.IsTrue(trajectory.StandingVolume.ScribnerTotal[periodIndex] < volumeTolerance * minimumStandingVolumeScribner[periodIndex]); OrganonStand stand = trajectory.StandByPeriod[periodIndex] ?? throw new NotSupportedException("Stand information missing for period " + periodIndex + "."); float qmd = stand.GetQuadraticMeanDiameter(); float topHeight = stand.GetTopHeight(); int treeRecords = stand.GetTreeRecordCount(); Assert.IsTrue((stand.Name != null) && (trajectory.Name != null) && stand.Name.StartsWith(trajectory.Name)); Assert.IsTrue(qmd > minimumQmd[periodIndex]); Assert.IsTrue(qmd < qmdTolerance * minimumQmd[periodIndex]); Assert.IsTrue(topHeight > minimumTopHeight[periodIndex]); Assert.IsTrue(topHeight < topHeightTolerance * minimumTopHeight[periodIndex]); Assert.IsTrue(treeRecords > 0); Assert.IsTrue(treeRecords < 666); // TODO: check qmd against QMD from basal area } }
public void NelderTrajectory() { int expectedUnthinnedTreeRecordCount = 661; int lastPeriod = 9; bool useScaledVolume = false; PlotsWithHeight nelder = PublicApi.GetNelder(); OrganonConfiguration configuration = OrganonTest.CreateOrganonConfiguration(new OrganonVariantNwo()); OrganonStand stand = nelder.ToOrganonStand(configuration, 20, 130.0F); stand.PlantingDensityInTreesPerHectare = TestConstant.NelderReplantingDensityInTreesPerHectare; OrganonStandTrajectory unthinnedTrajectory = new OrganonStandTrajectory(stand, configuration, TimberValue.Default, lastPeriod, useScaledVolume); unthinnedTrajectory.Simulate(); foreach (Stand?unthinnedStand in unthinnedTrajectory.StandByPeriod) { AssertNullable.IsNotNull(unthinnedStand); Assert.IsTrue(unthinnedStand.GetTreeRecordCount() == expectedUnthinnedTreeRecordCount); } int thinPeriod = 3; configuration.Treatments.Harvests.Add(new ThinByPrescription(thinPeriod) { FromAbovePercentage = 20.0F, ProportionalPercentage = 15.0F, FromBelowPercentage = 10.0F }); OrganonStandTrajectory thinnedTrajectory = new OrganonStandTrajectory(stand, configuration, TimberValue.Default, lastPeriod, useScaledVolume); AssertNullable.IsNotNull(thinnedTrajectory.StandByPeriod[0]); Assert.IsTrue(thinnedTrajectory.StandByPeriod[0] !.GetTreeRecordCount() == expectedUnthinnedTreeRecordCount); thinnedTrajectory.Simulate(); for (int periodIndex = 0; periodIndex < thinPeriod; ++periodIndex) { Stand?unthinnedStand = thinnedTrajectory.StandByPeriod[periodIndex]; AssertNullable.IsNotNull(unthinnedStand); Assert.IsTrue(unthinnedStand.GetTreeRecordCount() == expectedUnthinnedTreeRecordCount); } int expectedThinnedTreeRecordCount = 328; // must be updated if prescription changes for (int periodIndex = thinPeriod; periodIndex < thinnedTrajectory.PlanningPeriods; ++periodIndex) { Stand?thinnedStand = thinnedTrajectory.StandByPeriod[periodIndex]; AssertNullable.IsNotNull(thinnedStand); Assert.IsTrue(thinnedStand.GetTreeRecordCount() == expectedThinnedTreeRecordCount); } // verify unthinned trajectory // 0 1 2 3 4 5 6 7 8 9 float[] minimumUnthinnedQmd = new float[] { 6.61F, 8.17F, 9.52F, 10.67F, 11.68F, 12.56F, 13.34F, 14.05F, 14.69F, 15.28F }; // in // 0 1 2 3 4 5 6 7 8 9 float[] minimumUnthinnedTopHeight = new float[] { 54.1F, 67.9F, 80.3F, 91.6F, 101.9F, 111.3F, 119.9F, 127.8F, 135.0F, 141.7F }; // ft float[] minimumUnthinnedVolume; if (unthinnedTrajectory.UseScaledVolume) { minimumUnthinnedVolume = new float[] { 9.758F, 19.01F, 31.07F, 47.24F, 62.21F, 75.09F, 89.64F, 103.7F, 116.5F, 128.9F }; // Poudel 2018 + Scribner long log net MBF/ha } else { // 0 1 2 3 4 5 6 7 8 9 minimumUnthinnedVolume = new float[] { 4.428F, 15.02F, 30.49F, 48.39F, 66.72F, 84.45F, 101.1F, 116.4F, 130.6F, 143.6F }; // FIA SV6x32 MBF/ha } PublicApi.Verify(unthinnedTrajectory, minimumUnthinnedQmd, minimumUnthinnedTopHeight, minimumUnthinnedVolume, 0, lastPeriod, 0, 0, configuration.Variant.TimeStepInYears); // verify thinned trajectory // 0 1 2 3 4 5 6 7 8 9 float[] minimumThinnedQmd = new float[] { 6.61F, 8.19F, 9.53F, 11.81F, 13.44F, 14.80F, 15.99F, 17.05F, 18.00F, 18.85F }; // in // 0 1 2 3 4 5 6 7 8 9 float[] minimumThinnedTopHeight = new float[] { 54.1F, 67.9F, 80.3F, 88.3F, 98.4F, 108.0F, 116.9F, 125.0F, 132.5F, 139.4F }; // ft float[] minimumThinnedVolume; if (thinnedTrajectory.UseScaledVolume) { minimumThinnedVolume = new float[] { 9.758F, 19.01F, 31.07F, 28.25F, 41.77F, 54.37F, 68.44F, 85.10F, 100.4F, 114.7F }; // Poudel 2018 + Scribner long log net MBF/ha } else { // 0 1 2 3 4 5 6 7 8 9 minimumThinnedVolume = new float[] { 4.428F, 15.02F, 30.50F, 30.64F, 47.26F, 64.81F, 82.54F, 99.87F, 116.3F, 131.7F }; // FIA MBF/ha } PublicApi.Verify(thinnedTrajectory, minimumThinnedQmd, minimumThinnedTopHeight, minimumThinnedVolume, thinPeriod, lastPeriod, 200, 400, configuration.Variant.TimeStepInYears); PublicApi.Verify(thinnedTrajectory, minimumThinnedVolume, thinPeriod); }