Exemplo n.º 1
0
        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;
        }
Exemplo n.º 2
0
 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;
 }
Exemplo n.º 3
0
 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;
 }
Exemplo n.º 4
0
        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;
        }
Exemplo n.º 5
0
 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];
         }
     }
 }
Exemplo n.º 6
0
        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;
        }
Exemplo n.º 7
0
        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());
        }
Exemplo n.º 8
0
        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);
        }
Exemplo n.º 9
0
        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);
        }
Exemplo n.º 10
0
        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
            }
        }
Exemplo n.º 11
0
 public TestStandDensity(OrganonStand stand, OrganonVariant variant)
     : base(stand, variant)
 {
 }
Exemplo n.º 12
0
 public Hero(OrganonStand stand, OrganonConfiguration organonConfiguration, Objective objective, HeuristicParameters parameters)
     : base(stand, organonConfiguration, objective, parameters)
 {
     this.IsStochastic      = false;
     this.MaximumIterations = Constant.HeuristicDefault.HeroMaximumIterations;
 }
Exemplo n.º 13
0
        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);
            }
        }
Exemplo n.º 14
0
        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);
        }
Exemplo n.º 15
0
        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();
        }
Exemplo n.º 16
0
        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);
        }