protected override void ProcessRecord() { PlotsWithHeight plot; if (this.ExpansionFactor.HasValue) { plot = new PlotsWithHeight(this.Plots !, this.ExpansionFactor.Value); } else { plot = new PlotsWithHeight(this.Plots !); } plot.Read(this.Xlsx !, this.XlsxSheet); OrganonConfiguration configuration = new OrganonConfiguration(OrganonVariant.Create(this.Model)); OrganonStand stand; if (this.Trees.HasValue) { stand = plot.ToOrganonStand(configuration, this.Age, this.SiteIndex, this.Trees.Value); } else { stand = plot.ToOrganonStand(configuration, this.Age, this.SiteIndex); } if (this.PlantingDensity.HasValue) { stand.PlantingDensityInTreesPerHectare = this.PlantingDensity.Value; } this.WriteObject(stand); }
protected override void ProcessRecord() { if (this.HarvestPeriods.Count < 1) { throw new ArgumentOutOfRangeException(nameof(this.HarvestPeriods)); } if ((this.PerturbBy < 0.0F) || (this.PerturbBy > 1.0F)) { throw new ArgumentOutOfRangeException(nameof(this.PerturbBy)); } if (this.PlanningPeriods.Count < 1) { throw new ArgumentOutOfRangeException(nameof(this.PlanningPeriods)); } if (this.ProportionalPercentage.Count < 1) { throw new ArgumentOutOfRangeException(nameof(this.ProportionalPercentage)); } Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); IList <TParameters> parameterCombinations = this.GetParameterCombinations(); int treeCount = this.Stand !.GetTreeRecordCount(); List <HeuristicSolutionDistribution> distributions = new List <HeuristicSolutionDistribution>(parameterCombinations.Count * this.HarvestPeriods.Count * this.PlanningPeriods.Count); for (int planningPeriodIndex = 0; planningPeriodIndex < this.PlanningPeriods.Count; ++planningPeriodIndex) { for (int harvestPeriodIndex = 0; harvestPeriodIndex < this.HarvestPeriods.Count; ++harvestPeriodIndex) { int planningPeriods = this.PlanningPeriods[planningPeriodIndex]; int harvestPeriods = this.HarvestPeriods[harvestPeriodIndex]; if (harvestPeriods >= planningPeriods) // minimum 10 years between thinning and final harvest (if five year time step) { continue; } for (int parameterIndex = 0; parameterIndex < parameterCombinations.Count; ++parameterIndex) { distributions.Add(new HeuristicSolutionDistribution(1, harvestPeriods, treeCount) { HarvestPeriodIndex = harvestPeriodIndex, ParameterIndex = parameterIndex, PlanningPeriodIndex = planningPeriodIndex }); } } } ParallelOptions parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = this.Threads }; int totalRuns = distributions.Count * this.BestOf; int runsCompleted = 0; Task runs = Task.Run(() => { Parallel.For(0, totalRuns, parallelOptions, (int iteration, ParallelLoopState loopState) => { if (loopState.ShouldExitCurrentIteration) { return; } int distributionIndex = iteration / this.BestOf; HeuristicSolutionDistribution distribution = distributions[distributionIndex]; OrganonConfiguration organonConfiguration = new OrganonConfiguration(OrganonVariant.Create(this.TreeModel)); organonConfiguration.Treatments.Harvests.Add(this.CreateHarvest(distribution.HarvestPeriodIndex)); Objective objective = new Objective() { IsLandExpectationValue = this.LandExpectationValue, PlanningPeriods = this.PlanningPeriods[distribution.PlanningPeriodIndex] }; TParameters runParameters = parameterCombinations[distribution.ParameterIndex]; Heuristic currentHeuristic = this.CreateHeuristic(organonConfiguration, objective, runParameters); if (runParameters.PerturbBy > 0.0F) { if ((runParameters.PerturbBy == 1.0F) || (distribution.EliteSolutions.NewIndividuals == 0)) { // minor optimization point: save a few time steps by by re-using pre-thin results // minor optimization point: save one loop over stand by skipping this for genetic algorithms currentHeuristic.RandomizeTreeSelection(runParameters.ProportionalPercentage); } else { // TODO: support initialization from unperturbed elite solutions // TODO: intialize genetic algorithm population from elite solutions? // TODO: how to define generation statistics? // TODO: more granular locking? lock (distributions) { currentHeuristic.RandomizeTreeSelectionFrom(runParameters.PerturbBy, distribution.EliteSolutions); } } } TimeSpan runTime = currentHeuristic.Run(); lock (distributions) { distribution.AddRun(currentHeuristic, runTime, runParameters); ++runsCompleted; } if (this.Stopping) { loopState.Stop(); } }); }); string name = this.GetName(); int sleepsSinceLastStatusUpdate = 0; while (runs.IsCompleted == false) { Thread.Sleep(TimeSpan.FromSeconds(1.0)); ++sleepsSinceLastStatusUpdate; if (runs.IsFaulted) { Debug.Assert(runs.Exception != null && runs.Exception.InnerException != null); // per https://stackoverflow.com/questions/20170527/how-to-correctly-rethrow-an-exception-of-task-already-in-faulted-state ExceptionDispatchInfo.Capture(runs.Exception.InnerException).Throw(); } if (sleepsSinceLastStatusUpdate > 30) { double fractionComplete = (double)runsCompleted / (double)totalRuns; double secondsElapsed = stopwatch.Elapsed.TotalSeconds; double secondsRemaining = secondsElapsed * (1.0 / fractionComplete - 1.0); this.WriteProgress(new ProgressRecord(0, name, String.Format(runsCompleted + " of " + totalRuns + " runs completed by " + this.Threads + " threads.")) { PercentComplete = (int)(100.0 * fractionComplete), SecondsRemaining = (int)Math.Round(secondsRemaining) }); sleepsSinceLastStatusUpdate = 0; } } runs.GetAwaiter().GetResult(); // propagate any exceptions since last IsFaulted check foreach (HeuristicSolutionDistribution distribution in distributions) { distribution.OnRunsComplete(); } stopwatch.Stop(); this.WriteObject(distributions); if (distributions.Count == 1) { this.WriteSingleDistributionSummary(distributions[0], stopwatch.Elapsed); } else { this.WriteMultipleDistributionSummary(distributions, stopwatch.Elapsed); } }