public void AddIngrowth(int year, OrganonStand stand, OrganonStandDensity standDensity) { List <int> remainingIngrowthYears = this.IngrowthByYear.Keys.Where(key => key > this.yearOfMostRecentIngrowthAdded).ToList(); if ((remainingIngrowthYears.Count < 1) || (remainingIngrowthYears[0] > year)) { // no ingrowth in this simulation step return; } int ingrowthYear = remainingIngrowthYears[0]; Debug.Assert((remainingIngrowthYears.Count == 1) || (remainingIngrowthYears[1] > year)); // for now, assume only one ingrowth measurement per simulation timestep float fixedPlotExpansionFactor = this.GetTreesPerAcreExpansionFactor(); foreach (PspTreeMeasurementSeries tree in this.IngrowthByYear[ingrowthYear]) { Trees treesOfSpecies = stand.TreesBySpecies[tree.Species]; Debug.Assert(treesOfSpecies.Capacity > treesOfSpecies.Count); float dbhInInches = Constant.InchesPerCentimeter * tree.DbhInCentimetersByYear.Values[0]; float heightInFeet = TestConstant.FeetPerMeter * TreeRecord.EstimateHeightInMeters(tree.Species, dbhInInches); treesOfSpecies.Add(tree.Tag, dbhInInches, heightInFeet, tree.EstimateInitialCrownRatio(standDensity), fixedPlotExpansionFactor); } this.yearOfMostRecentIngrowthAdded = ingrowthYear; }
public RandomGuessing(OrganonStand stand, OrganonConfiguration configuration, Objective objective, HeuristicParameters parameters) : base(stand, configuration, objective, parameters) { this.CentralSelectionPercentage = parameters.ProportionalPercentage; this.Iterations = 4 * stand.GetTreeRecordCount(); this.SelectionPercentageWidth = 20.0F; }
public TabuSearch(OrganonStand stand, OrganonConfiguration organonConfiguration, Objective objective, TabuParameters parameters) : base(stand, organonConfiguration, objective, parameters) { this.EscapeAfter = parameters.EscapeAfter; this.EscapeDistance = parameters.EscapeDistance; this.Iterations = parameters.Iterations; //this.Jump = parameters.Jump; this.MaximumTenure = parameters.MaximumTenure; this.Tenure = parameters.Tenure; }
public GreatDeluge(OrganonStand stand, OrganonConfiguration organonConfiguration, Objective objective, HeuristicParameters parameters) : base(stand, organonConfiguration, objective, parameters) { int treeRecords = stand.GetTreeRecordCount(); this.ChangeToExchangeAfter = Int32.MaxValue; this.FinalMultiplier = Constant.MonteCarloDefault.DelugeFinalMultiplier; this.IntitialMultiplier = Constant.MonteCarloDefault.DelugeInitialMultiplier; this.Iterations = Constant.MonteCarloDefault.IterationMultiplier * treeRecords; this.LowerWaterAfter = (int)(Constant.MonteCarloDefault.ReheatAfter * treeRecords + 0.5F); this.LowerWaterBy = Constant.MonteCarloDefault.DelugeLowerWaterBy; this.MoveType = MoveType.OneOpt; this.RainRate = null; this.StopAfter = Constant.MonteCarloDefault.StopAfter * treeRecords; }
public void AccumulateGrowthAndMortality(OrganonStand stand) { foreach (Trees treesOfSpecies in stand.TreesBySpecies.Values) { float[] totalDbhGrowthInInches = this.TotalDbhGrowthInInches.GetOrAdd(treesOfSpecies.Species, treesOfSpecies.Capacity); float[] totalHeightGrowthInFeet = this.TotalHeightGrowthInFeet.GetOrAdd(treesOfSpecies.Species, treesOfSpecies.Capacity); float[] totalDeadExpansionFactor = this.TotalDeadExpansionFactor.GetOrAdd(treesOfSpecies.Species, treesOfSpecies.Capacity); for (int treeIndex = 0; treeIndex < treesOfSpecies.Count; ++treeIndex) { totalDbhGrowthInInches[treeIndex] += treesOfSpecies.DbhGrowth[treeIndex]; totalHeightGrowthInFeet[treeIndex] += treesOfSpecies.HeightGrowth[treeIndex]; totalDeadExpansionFactor[treeIndex] += treesOfSpecies.DeadExpansionFactor[treeIndex]; } } }
public RecordTravel(OrganonStand stand, OrganonConfiguration organonConfiguration, Objective objective, HeuristicParameters parameters) : base(stand, organonConfiguration, objective, parameters) { int treeRecordCount = stand.GetTreeRecordCount(); this.Alpha = Constant.MonteCarloDefault.RecordTravelAlpha; this.ChangeToExchangeAfter = Int32.MaxValue; this.FixedDeviation = 0.0F; this.FixedIncrease = 0.0F; this.IncreaseAfter = (int)(Constant.MonteCarloDefault.ReheatAfter * treeRecordCount + 0.5F); this.Iterations = Constant.MonteCarloDefault.IterationMultiplier * treeRecordCount; this.MoveType = MoveType.OneOpt; this.RelativeDeviation = 0.0F; this.RelativeIncrease = Constant.MonteCarloDefault.RecordTravelRelativeIncrease; this.StopAfter = Constant.MonteCarloDefault.StopAfter * treeRecordCount; }
public void StandGrowthPerformance() { int thinningPeriod = 4; int runs = 4; // 1 warmup run + measured runs int trees = 300; #if DEBUG runs = 1; // do only functional validation of test on DEBUG builds to reduce test execution time trees = 10; #endif PlotsWithHeight nelder = PublicApi.GetNelder(); OrganonConfiguration configuration = PublicApi.CreateOrganonConfiguration(new OrganonVariantNwo()); configuration.Treatments.Harvests.Add(new ThinByIndividualTreeSelection(thinningPeriod)); OrganonStand stand = nelder.ToOrganonStand(configuration, 20, 130.0F, trees); stand.PlantingDensityInTreesPerHectare = TestConstant.NelderReplantingDensityInTreesPerHectare; Objective landExpectationValue = new Objective() { IsLandExpectationValue = true, PlanningPeriods = 9 }; HeuristicParameters defaultParameters = new HeuristicParameters(); TimeSpan runtime = TimeSpan.Zero; for (int run = 0; run < runs; ++run) { // after warmup: 3 runs * 300 trees = 900 measured growth simulations on i7-3770 (4th gen, Sandy Bridge) // dispersion of 5 runs min mean median max // .NET 5.0 with removed tree compaction 1.67 1.72 1.72 1.82 Hero hero = new Hero(stand, configuration, landExpectationValue, defaultParameters) { IsStochastic = false, MaximumIterations = 2 }; hero.RandomizeTreeSelection(TestConstant.Default.SelectionPercentage); if (run > 0) { // skip first run as a warmup run runtime += hero.Run(); } } this.TestContext !.WriteLine(runtime.TotalSeconds.ToString()); }
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); }
public SimulatedAnnealing(OrganonStand stand, OrganonConfiguration organonConfiguration, Objective objective, HeuristicParameters parameters) : base(stand, organonConfiguration, objective, parameters) { int treeRecords = stand.GetTreeRecordCount(); this.Alpha = Constant.MonteCarloDefault.AnnealingAlpha; this.ChangeToExchangeAfter = Int32.MaxValue; this.FinalProbability = 0.0F; this.InitialProbability = 0.0F; this.Iterations = Constant.MonteCarloDefault.IterationMultiplier * treeRecords; this.IterationsPerTemperature = Constant.MonteCarloDefault.AnnealingIterationsPerTemperature; this.MoveType = MoveType.OneOpt; this.ProbabilityWindowLength = Constant.MonteCarloDefault.AnnealingAveragingWindowLength; this.ReheatAfter = (int)(Constant.MonteCarloDefault.ReheatAfter * treeRecords + 0.5F); this.ReheatBy = Constant.MonteCarloDefault.AnnealingReheadBy; // float temperatureSteps = (float)(defaultIterations / this.IterationsPerTemperature); // this.Alpha = 1.0F / MathF.Pow(this.InitialAcceptProbability / this.FinalAcceptProbability, 1.0F / temperatureSteps); }
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 TestStandDensity(OrganonStand stand, OrganonVariant variant) : base(stand, variant) { }
public Hero(OrganonStand stand, OrganonConfiguration organonConfiguration, Objective objective, HeuristicParameters parameters) : base(stand, organonConfiguration, objective, parameters) { this.IsStochastic = false; this.MaximumIterations = Constant.HeuristicDefault.HeroMaximumIterations; }
public void NelderHero() { int thinningPeriod = 4; int treeCount = 100; int expectedTreesSelectedWithFiaVolume = 0; float minObjectiveFunctionWithFiaVolume = 4.154F; // USk$/ha float minObjectiveFunctionWithScaledVolume = 3.454F; // USk$/ha float minThinnedMbfWithFiaVolume = 0.0F; #if DEBUG treeCount = 48; expectedTreesSelectedWithFiaVolume = 0; minObjectiveFunctionWithFiaVolume = 1.790F; // USk$/ha minObjectiveFunctionWithScaledVolume = 1.357F; // USk$/ha minThinnedMbfWithFiaVolume = 0.0F; #endif PlotsWithHeight nelder = PublicApi.GetNelder(); OrganonConfiguration configuration = new OrganonConfiguration(new OrganonVariantNwo()); configuration.Treatments.Harvests.Add(new ThinByIndividualTreeSelection(thinningPeriod)); OrganonStand stand = nelder.ToOrganonStand(configuration, 20, 130.0F, treeCount); stand.PlantingDensityInTreesPerHectare = TestConstant.NelderReplantingDensityInTreesPerHectare; Objective landExpectationValue = new Objective() { IsLandExpectationValue = true, PlanningPeriods = 9 }; Hero hero = new Hero(stand, configuration, landExpectationValue, new HeuristicParameters() { UseScaledVolume = false }) { //IsStochastic = true, MaximumIterations = 10 }; //hero.RandomizeTreeSelection(TestConstant.Default.SelectionPercentage); hero.CurrentTrajectory.SetTreeSelection(0, thinningPeriod); hero.Run(); this.Verify(hero); int[] treeSelection = hero.BestTrajectory.IndividualTreeSelectionBySpecies[FiaCode.PseudotsugaMenziesii]; int treesSelected = treeSelection.Sum() / thinningPeriod; if (hero.BestTrajectory.UseScaledVolume) { this.TestContext !.WriteLine("best objective: {0} observed, near {1} expected", hero.BestObjectiveFunction, minObjectiveFunctionWithScaledVolume); Assert.IsTrue(hero.BestObjectiveFunction > minObjectiveFunctionWithScaledVolume); Assert.IsTrue(hero.BestObjectiveFunction < 1.02F * minObjectiveFunctionWithScaledVolume); } else { // expected standing volume, MBF, for no trees harvested // 0.122, 0.971, 3.18, 6.749, 11.56, 17.54, 24.56, 32.44, 40.97, 49.98 // expected NPV, US$/ha, for no trees harvested // -90.96, 61.62, 373.49, 781.90, 1228.99, 1674.68, 2085.93, 2438.25, 2717.76, 2918.96 this.TestContext !.WriteLine("best objective: {0} observed, near {1} expected", hero.BestObjectiveFunction, minObjectiveFunctionWithFiaVolume); Assert.IsTrue(hero.BestObjectiveFunction > minObjectiveFunctionWithFiaVolume); Assert.IsTrue(hero.BestObjectiveFunction < 1.01F * minObjectiveFunctionWithFiaVolume); Assert.IsTrue(hero.BestTrajectory.ThinningVolume.ScribnerTotal[thinningPeriod] >= minThinnedMbfWithFiaVolume); Assert.IsTrue(hero.BestTrajectory.ThinningVolume.ScribnerTotal[thinningPeriod] <= 1.01 * minThinnedMbfWithFiaVolume); Assert.IsTrue(treesSelected == expectedTreesSelectedWithFiaVolume); } }
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); }
public void NelderOtherHeuristics() { int thinningPeriod = 4; int treeCount = 75; #if DEBUG treeCount = 25; #endif PlotsWithHeight nelder = PublicApi.GetNelder(); OrganonConfiguration configuration = OrganonTest.CreateOrganonConfiguration(new OrganonVariantNwo()); configuration.Treatments.Harvests.Add(new ThinByIndividualTreeSelection(thinningPeriod)); OrganonStand stand = nelder.ToOrganonStand(configuration, 20, 130.0F, treeCount); stand.PlantingDensityInTreesPerHectare = TestConstant.NelderReplantingDensityInTreesPerHectare; Objective landExpectationValue = new Objective() { IsLandExpectationValue = true, PlanningPeriods = 9 }; Objective volume = new Objective() { PlanningPeriods = landExpectationValue.PlanningPeriods }; HeuristicParameters defaultParameters = new HeuristicParameters() { UseScaledVolume = false }; GeneticParameters geneticParameters = new GeneticParameters(treeCount) { PopulationSize = 7, MaximumGenerations = 5, UseScaledVolume = defaultParameters.UseScaledVolume }; GeneticAlgorithm genetic = new GeneticAlgorithm(stand, configuration, landExpectationValue, geneticParameters); TimeSpan geneticRuntime = genetic.Run(); GreatDeluge deluge = new GreatDeluge(stand, configuration, volume, defaultParameters) { RainRate = 5, LowerWaterAfter = 9, StopAfter = 10 }; deluge.RandomizeTreeSelection(TestConstant.Default.SelectionPercentage); TimeSpan delugeRuntime = deluge.Run(); RecordTravel recordTravel = new RecordTravel(stand, configuration, landExpectationValue, defaultParameters) { StopAfter = 10 }; recordTravel.RandomizeTreeSelection(TestConstant.Default.SelectionPercentage); TimeSpan recordRuntime = recordTravel.Run(); SimulatedAnnealing annealer = new SimulatedAnnealing(stand, configuration, volume, defaultParameters) { Iterations = 100 }; annealer.RandomizeTreeSelection(TestConstant.Default.SelectionPercentage); TimeSpan annealerRuntime = annealer.Run(); TabuParameters tabuParameters = new TabuParameters() { UseScaledVolume = defaultParameters.UseScaledVolume }; TabuSearch tabu = new TabuSearch(stand, configuration, landExpectationValue, tabuParameters) { Iterations = 7, //Jump = 2, MaximumTenure = 5 }; tabu.RandomizeTreeSelection(TestConstant.Default.SelectionPercentage); TimeSpan tabuRuntime = tabu.Run(); ThresholdAccepting thresholdAcceptor = new ThresholdAccepting(stand, configuration, volume, defaultParameters); thresholdAcceptor.IterationsPerThreshold.Clear(); thresholdAcceptor.Thresholds.Clear(); thresholdAcceptor.IterationsPerThreshold.Add(10); thresholdAcceptor.Thresholds.Add(1.0F); thresholdAcceptor.RandomizeTreeSelection(TestConstant.Default.SelectionPercentage); TimeSpan acceptorRuntime = thresholdAcceptor.Run(); RandomGuessing random = new RandomGuessing(stand, configuration, volume, defaultParameters) { Iterations = 4 }; TimeSpan randomRuntime = random.Run(); configuration.Treatments.Harvests.Clear(); configuration.Treatments.Harvests.Add(new ThinByPrescription(thinningPeriod)); PrescriptionParameters prescriptionParameters = new PrescriptionParameters() { Maximum = 60.0F, Minimum = 50.0F, Step = 10.0F, UseScaledVolume = defaultParameters.UseScaledVolume }; PrescriptionEnumeration enumerator = new PrescriptionEnumeration(stand, configuration, landExpectationValue, prescriptionParameters); TimeSpan enumerationRuntime = enumerator.Run(); // heuristics assigned to volume optimization this.Verify(deluge); this.Verify(annealer); this.Verify(thresholdAcceptor); this.Verify(random); // heuristics assigned to net present value optimization this.Verify(genetic); this.Verify(enumerator); this.Verify(recordTravel); this.Verify(tabu); HeuristicSolutionDistribution distribution = new HeuristicSolutionDistribution(1, thinningPeriod, treeCount); distribution.AddRun(annealer, annealerRuntime, defaultParameters); distribution.AddRun(deluge, delugeRuntime, defaultParameters); distribution.AddRun(thresholdAcceptor, acceptorRuntime, defaultParameters); distribution.AddRun(genetic, geneticRuntime, defaultParameters); distribution.AddRun(enumerator, enumerationRuntime, defaultParameters); distribution.AddRun(recordTravel, recordRuntime, defaultParameters); distribution.AddRun(tabu, tabuRuntime, defaultParameters); distribution.AddRun(random, randomRuntime, defaultParameters); distribution.OnRunsComplete(); }
public OrganonStand ToOrganonStand(OrganonConfiguration configuration, int ageInYears, float siteIndex, int maximumTreesInStand) { if (maximumTreesInStand < 1) { throw new ArgumentOutOfRangeException(nameof(maximumTreesInStand)); } // copy trees from plot to Organon stand with default crown ratios // For now, when the stand size is limited this just copies the first n trees encountered rather than subsampling the plot. // Can move this to a Trees.CopyFrom() and Trees.ChangeUnits() if needed. Stand plotAtAge = this.byAge[ageInYears]; int maximumTreesToCopy = Math.Min(plotAtAge.GetTreeRecordCount(), maximumTreesInStand); int treesCopied = 0; StringBuilder plotIDsAsString = new StringBuilder(); foreach (int plotID in this.plotIDs) { plotIDsAsString.Append(plotID.ToString(CultureInfo.InvariantCulture)); } OrganonStand stand = new OrganonStand(ageInYears, siteIndex) { Name = plotIDsAsString.ToString() }; foreach (Trees plotTreesOfSpecies in plotAtAge.TreesBySpecies.Values) { if (stand.TreesBySpecies.TryGetValue(plotTreesOfSpecies.Species, out Trees? standTreesOfSpecies) == false) { int minimumSize = Math.Min(maximumTreesToCopy - treesCopied, plotTreesOfSpecies.Count); standTreesOfSpecies = new Trees(plotTreesOfSpecies.Species, minimumSize, Units.English); stand.TreesBySpecies.Add(plotTreesOfSpecies.Species, standTreesOfSpecies); } for (int treeIndex = 0; treeIndex < plotTreesOfSpecies.Count; ++treeIndex) { int tag = plotTreesOfSpecies.Tag[treeIndex]; float dbhInInches = Constant.InchesPerCentimeter * plotTreesOfSpecies.Dbh[treeIndex]; float heightInFeet = Constant.FeetPerMeter * plotTreesOfSpecies.Height[treeIndex]; float liveExpansionFactor = Constant.HectaresPerAcre * plotTreesOfSpecies.LiveExpansionFactor[treeIndex]; if (Single.IsNaN(dbhInInches) || (dbhInInches <= 0.0F) || Single.IsNaN(heightInFeet) || (heightInFeet <= 0.0F)) { throw new NotSupportedException("Tree " + tag + " has a missing, zero, or negative height or diameter at age " + ageInYears + "."); } standTreesOfSpecies.Add(tag, dbhInInches, heightInFeet, defaultCrownRatio, liveExpansionFactor); if (++treesCopied >= maximumTreesToCopy) { break; // break inner for loop } } if (++treesCopied >= maximumTreesToCopy) { break; // break foreach } } // estimate crown ratio if (configuration.Variant.TreeModel == TreeModel.OrganonSwo) { // TODO: if needed, add support for old index for NWO and SMC Pacific madrone throw new NotImplementedException("Old tree index not computed."); } stand.EnsureSiteIndicesSet(configuration.Variant); stand.SetRedAlderSiteIndexAndGrowthEffectiveAge(); stand.SetSdiMax(configuration); float defaultOldIndex = 0.0F; OrganonStandDensity density = new OrganonStandDensity(stand, configuration.Variant); foreach (Trees treesOfSpecies in stand.TreesBySpecies.Values) { // initialize crown ratio from Organon variant for (int treeIndex = 0; treeIndex < treesOfSpecies.Count; ++treeIndex) { float dbhInInches = treesOfSpecies.Dbh[treeIndex]; float heightInFeet = treesOfSpecies.Height[treeIndex]; float crownCompetitionFactorLarger = density.GetCrownCompetitionFactorLarger(dbhInInches); float heightToCrownBase = configuration.Variant.GetHeightToCrownBase(treesOfSpecies.Species, heightInFeet, dbhInInches, crownCompetitionFactorLarger, density.BasalAreaPerAcre, stand.SiteIndex, stand.HemlockSiteIndex, defaultOldIndex); float crownRatio = (heightInFeet - heightToCrownBase) / heightInFeet; Debug.Assert(crownRatio >= 0.0F); Debug.Assert(crownRatio <= 1.0F); treesOfSpecies.CrownRatio[treeIndex] = crownRatio; } // initialize crown ratio from FVS-PN dubbing // https://www.fs.fed.us/fmsc/ftp/fvs/docs/overviews/FVSpn_Overview.pdf, section 4.3.1 // https://sourceforge.net/p/open-fvs/code/HEAD/tree/trunk/pn/crown.f#l67 // for live > 1.0 inch DBH // estimated crown ratio = d0 + d1 * 100.0 * SDI / SDImax // PSME d0 = 5.666442, d1 = -0.025199 if ((stand.TreesBySpecies.Count != 1) || (treesOfSpecies.Species != FiaCode.PseudotsugaMenziesii)) { throw new NotImplementedException(); } // FVS-PN crown ratio dubbing for Douglas-fir // Resulted in 0.28% less volume than Organon NWO on Malcolm Knapp Nelder 1 at stand age 70. // float qmd = stand.GetQuadraticMeanDiameter(); // float reinekeSdi = density.TreesPerAcre * MathF.Pow(0.1F * qmd, 1.605F); // float reinekeSdiMax = MathF.Exp((stand.A1 - Constant.NaturalLogOf10) / stand.A2); // float meanCrownRatioFvs = 5.666442F - 0.025199F * 100.0F * reinekeSdi / reinekeSdiMax; // Debug.Assert(meanCrownRatioFvs >= 0.0F); // Debug.Assert(meanCrownRatioFvs <= 10.0F); // FVS uses a 0 to 10 range, so 10 = 100% crown ratio // float weibullA = 0.0F; // float weibullB = -0.012061F + 1.119712F * meanCrownRatioFvs; // float weibullC = 3.2126F; // int[] dbhOrder = treesOfSpecies.GetDbhSortOrder(); // for (int treeIndex = 0; treeIndex < treesOfSpecies.Count; ++treeIndex) // { // float dbhFraction = (float)dbhOrder[treeIndex] / (float)treesOfSpecies.Count; // float fvsCrownRatio = weibullA + weibullB * MathV.Pow(-1.0F * MathV.Ln(1.0F - dbhFraction), 1.0F / weibullC); // Debug.Assert(fvsCrownRatio >= 0.0F); // Debug.Assert(fvsCrownRatio <= 10.0F); // treesOfSpecies.CrownRatio[treeIndex] = 0.1F * fvsCrownRatio; // } } // used for checking sensitivity to data order // Ordering not currently advantageous, so disabled for now. // foreach (Trees treesOfSpecies in stand.TreesBySpecies.Values) // { // treesOfSpecies.SortByDbh(); // } return(stand); }