public void VolumeFia() { int treeCount = 42; // TODO: TSHE, THPL, ... Trees trees = new Trees(FiaCode.PseudotsugaMenziesii, treeCount, Units.English); float[] fiaMerchantableCubicFeetPerAcre = new float[treeCount]; float[] fiaScribnerBoardFeetPerAcre = new float[treeCount]; float merchantableCubicFeetPerAcre = 0.0F; float merchantableCubicMetersPerHectare = 0.0F; float totalCylinderCubicMeterVolumePerAcre = 0.0F; float totalScribnerBoardFeetPerAcre = 0.0F; for (int treeIndex = 0; treeIndex < treeCount; ++treeIndex) { // create trees with a range of expansion factors to catch errors in expansion factor management float treeRatio = (float)treeIndex / (float)treeCount; TreeRecord tree = new TreeRecord(treeIndex, trees.Species, (float)treeIndex, 1.0F - 0.75F * treeRatio, 0.6F + treeIndex); trees.Add(tree.Tag, tree.DbhInInches, tree.HeightInFeet, tree.CrownRatio, tree.LiveExpansionFactor); float dbhInMeters = TestConstant.MetersPerInch * tree.DbhInInches; float heightInMeters = Constant.MetersPerFoot * tree.HeightInFeet; float treeSizedCylinderCubicMeterVolumePerAcre = tree.LiveExpansionFactor * 0.25F * MathF.PI * dbhInMeters * dbhInMeters * heightInMeters; fiaMerchantableCubicFeetPerAcre[treeIndex] = tree.LiveExpansionFactor * FiaVolume.GetMerchantableCubicFeet(trees, treeIndex); merchantableCubicFeetPerAcre += fiaMerchantableCubicFeetPerAcre[treeIndex]; fiaScribnerBoardFeetPerAcre[treeIndex] = tree.LiveExpansionFactor * FiaVolume.GetScribnerBoardFeet(trees, treeIndex); totalScribnerBoardFeetPerAcre += fiaScribnerBoardFeetPerAcre[treeIndex]; merchantableCubicMetersPerHectare += OsuVolume.GetCubicVolume(trees, treeIndex); // taper coefficient should be in the vicinity of 0.3 for larger trees, but this is not well defined for small trees // Lower bound can be made more stringent if necessary. Assert.IsTrue(fiaMerchantableCubicFeetPerAcre[treeIndex] >= 0.0); Assert.IsTrue(fiaMerchantableCubicFeetPerAcre[treeIndex] <= 0.4 * Constant.CubicFeetPerCubicMeter * treeSizedCylinderCubicMeterVolumePerAcre); Assert.IsTrue(fiaScribnerBoardFeetPerAcre[treeIndex] >= 0.0); Assert.IsTrue(fiaScribnerBoardFeetPerAcre[treeIndex] <= 6.5 * 0.4 * Constant.CubicFeetPerCubicMeter * treeSizedCylinderCubicMeterVolumePerAcre); totalCylinderCubicMeterVolumePerAcre += treeSizedCylinderCubicMeterVolumePerAcre; } float totalCylinderCubicFeetVolumePerAcre = Constant.CubicFeetPerCubicMeter * totalCylinderCubicMeterVolumePerAcre; Assert.IsTrue(merchantableCubicFeetPerAcre >= 0.05 * totalCylinderCubicFeetVolumePerAcre); Assert.IsTrue(merchantableCubicFeetPerAcre <= 0.35 * totalCylinderCubicFeetVolumePerAcre); Assert.IsTrue(merchantableCubicFeetPerAcre >= 0.5 * Constant.HectaresPerAcre * Constant.CubicFeetPerCubicMeter * merchantableCubicMetersPerHectare); Assert.IsTrue(merchantableCubicMetersPerHectare <= 0.35 * Constant.AcresPerHectare * totalCylinderCubicMeterVolumePerAcre); Assert.IsTrue(totalScribnerBoardFeetPerAcre >= 1.75 * 0.35 * totalCylinderCubicFeetVolumePerAcre); Assert.IsTrue(totalScribnerBoardFeetPerAcre <= 6.5 * 0.40 * totalCylinderCubicFeetVolumePerAcre); // check SIMD 128 result against scalar float totalScribnerBoardFeetPerAcre128 = FiaVolume.GetScribnerBoardFeetPerAcre(trees); float simdScalarScribnerDifference = totalScribnerBoardFeetPerAcre - totalScribnerBoardFeetPerAcre128; Assert.IsTrue(MathF.Abs(simdScalarScribnerDifference) < 0.004 * totalScribnerBoardFeetPerAcre); }
public void VolumePerformance() { int iterations = 1025; // 5 warmup run + measured runs int treeCount = 100 * 1000; #if DEBUG iterations = 25; // do only functional validation of test on DEBUG builds to reduce test execution time treeCount = 125; #endif Trees trees = new Trees(FiaCode.PseudotsugaMenziesii, treeCount, Units.English); float expansionFactor = 0.5F; for (int treeIndex = 0; treeIndex < treeCount; ++treeIndex) { float dbhInInches = (float)(treeIndex % 36 + 4); trees.Add(treeIndex, dbhInInches, 16.0F * MathF.Sqrt(dbhInInches) + 4.5F, 0.01F * (float)(treeIndex % 100), expansionFactor); } FiaVolume volume = new FiaVolume(); Stopwatch runtime = new Stopwatch(); float accumulatedBoardFeetPerAcre = 0.0F; for (int iteration = 0; iteration < iterations; ++iteration) { if (iteration > 20) { // skip first few runs as warmup runs runtime.Start(); } // 1000 runs * 10,000 trees = 10M volumes on i7-3770 // .NET standard 2.0 + MathF: 12.854s -> 686k trees/core-s // RS616 pow() -> exp(): 13.254s -> 754k // MathV scalar: 9.451s -> 1.06M // reuse log10(DBH): 8.897s -> 1.12M // aggressive inlining: 7.998s -> 1.20M // internal loop: 7.785s -> 1.28M // single species: 7.619s -> 1.31M // VEX 128: 2.063s -> 4.85M float standBoardFeetPerAcre = FiaVolume.GetScribnerBoardFeetPerAcre(trees); #if DEBUG Assert.IsTrue(standBoardFeetPerAcre > 35.0F * 1000.0F * expansionFactor); Assert.IsTrue(standBoardFeetPerAcre < 40.0F * 1000.0F * expansionFactor); #endif accumulatedBoardFeetPerAcre += standBoardFeetPerAcre; } runtime.Stop(); this.TestContext !.WriteLine(runtime.Elapsed.TotalSeconds.ToString()); }
public WriteHarvestSchedule() { this.fiaVolume = new FiaVolume(); this.Format = DataFormat.Long; }