diff --git a/VectoCore/VectoCore/InputData/Reader/ComponentData/AuxiliaryDataReader.cs b/VectoCore/VectoCore/InputData/Reader/ComponentData/AuxiliaryDataReader.cs index c99c624a8e2cbfe5d2484c225bc691ac287c5bd9..60541a651f4fa6df57cd4fd137a5036e36ab77e6 100644 --- a/VectoCore/VectoCore/InputData/Reader/ComponentData/AuxiliaryDataReader.cs +++ b/VectoCore/VectoCore/InputData/Reader/ComponentData/AuxiliaryDataReader.cs @@ -67,9 +67,9 @@ namespace TUGraz.VectoCore.InputData.Reader.ComponentData private static void FillFromColumnNames(DataTable table, DelaunayMap map) { var data = table.Rows.Cast<DataRow>().Select(row => new { - AuxiliarySpeed = DataTableExtensionMethods.ParseDouble(row, (string)Fields.AuxSpeed).RPMtoRad(), - MechanicalPower = DataTableExtensionMethods.ParseDouble(row, (string)Fields.MechPower).SI().Kilo.Watt.Cast<Watt>(), - SupplyPower = DataTableExtensionMethods.ParseDouble(row, (string)Fields.SupplyPower).SI().Kilo.Watt.Cast<Watt>() + AuxiliarySpeed = row.ParseDouble(Fields.AuxSpeed).RPMtoRad(), + MechanicalPower = row.ParseDouble(Fields.MechPower).SI().Kilo.Watt.Cast<Watt>(), + SupplyPower = row.ParseDouble(Fields.SupplyPower).SI().Kilo.Watt.Cast<Watt>() }); foreach (var d in data) { map.AddPoint(d.AuxiliarySpeed.Value(), d.SupplyPower.Value(), d.MechanicalPower.Value()); diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMapReader.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMapReader.cs index 34357faf66a489d3ab3e32ff2ac63aa82dc4ad31..fe85cbcdb6ed35785d938e57cedfa77b841a6107 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMapReader.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMapReader.cs @@ -8,7 +8,7 @@ using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine { - public class FuelConsumptionMapReader + public static class FuelConsumptionMapReader { public static FuelConsumptionMap ReadFromFile(string fileName) { @@ -34,10 +34,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine foreach (DataRow row in data.Rows) { try { var entry = headerValid ? CreateFromColumNames(row) : CreateFromColumnIndizes(row); - - // Delaunay map works only as expected, when the angularVelocity is in rpm. - delaunayMap.AddPoint(entry.Torque.Value(), - headerValid ? DataTableExtensionMethods.ParseDouble(row, (string)Fields.EngineSpeed) : row.ParseDouble(0), + delaunayMap.AddPoint(entry.Torque.Value(), headerValid ? row.ParseDouble(Fields.EngineSpeed) : row.ParseDouble(0), entry.FuelConsumption.Value()); } catch (Exception e) { throw new VectoException(string.Format("Line {0}: {1}", data.Rows.IndexOf(row), e.Message), e); @@ -67,10 +64,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine private static FuelConsumptionMap.FuelConsumptionEntry CreateFromColumNames(DataRow row) { return new FuelConsumptionMap.FuelConsumptionEntry( - engineSpeed: row.ParseDouble((string)Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(), - torque: row.ParseDouble((string)Fields.Torque).SI<NewtonMeter>(), + engineSpeed: row.ParseDouble(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(), + torque: row.ParseDouble(Fields.Torque).SI<NewtonMeter>(), fuelConsumption: - row.ParseDouble((string)Fields.FuelConsumption) + row.ParseDouble(Fields.FuelConsumption) .SI() .Gramm.Per.Hour.ConvertTo() .Kilo.Gramm.Per.Second.Cast<KilogramPerSecond>() diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs index 2640bba7283ed30a1adb68e42e732a619f38ea20..00590a23c5d4363003f16ca1c8e245b52d5527ac 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/TransmissionLossMap.cs @@ -29,12 +29,9 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.Data; using System.Diagnostics; -using System.Linq; using TUGraz.VectoCommon.Exceptions; using TUGraz.VectoCommon.Models; using TUGraz.VectoCommon.Utils; @@ -69,10 +66,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox _lossMap = new DelaunayMap("TransmissionLossMap " + GearName); _invertedLossMap = new DelaunayMap("TransmissionLossMapInv. " + GearName); foreach (var entry in _entries) { - _lossMap.AddPoint(entry.InputSpeed.ConvertTo().Rounds.Per.Minute.Value(), - (entry.InputTorque - entry.TorqueLoss).Value(), entry.TorqueLoss.Value()); - _invertedLossMap.AddPoint(entry.InputSpeed.ConvertTo().Rounds.Per.Minute.Value(), entry.InputTorque.Value(), - entry.TorqueLoss.Value()); + _lossMap.AddPoint(entry.InputSpeed.Value(), (entry.InputTorque - entry.TorqueLoss).Value(), entry.TorqueLoss.Value()); + _invertedLossMap.AddPoint(entry.InputSpeed.Value(), entry.InputTorque.Value(), entry.TorqueLoss.Value()); } _lossMap.Triangulate(); @@ -88,12 +83,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox public LossMapResult GetTorqueLoss(PerSecond outAngularVelocity, NewtonMeter outTorque) { var result = new LossMapResult(); - var torqueLoss = _lossMap.Interpolate(outAngularVelocity.ConvertTo().Rounds.Per.Minute.Value() * _ratio, - outTorque.Value() / _ratio); + var torqueLoss = _lossMap.Interpolate(outAngularVelocity.Value() * _ratio, outTorque.Value() / _ratio); if (!torqueLoss.HasValue) { - torqueLoss = _lossMap.Extrapolate(outAngularVelocity.ConvertTo().Rounds.Per.Minute.Value() * _ratio, - outTorque.Value() / _ratio); + torqueLoss = _lossMap.Extrapolate(outAngularVelocity.Value() * _ratio, outTorque.Value() / _ratio); result.Extrapolated = true; } @@ -120,14 +113,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox /// <returns>Torque needed at output side (towards the wheels).</returns> public NewtonMeter GetOutTorque(PerSecond inAngularVelocity, NewtonMeter inTorque, bool allowExtrapolation = false) { - var torqueLoss = _invertedLossMap.Interpolate(inAngularVelocity.ConvertTo().Rounds.Per.Minute.Value(), - inTorque.Value()); + var torqueLoss = _invertedLossMap.Interpolate(inAngularVelocity.Value(), inTorque.Value()); if (torqueLoss.HasValue) { return (inTorque - torqueLoss.Value.SI<NewtonMeter>()) / _ratio; } if (allowExtrapolation) { - torqueLoss = _invertedLossMap.Extrapolate(inAngularVelocity.ConvertTo().Rounds.Per.Minute.Value(), inTorque.Value()); + torqueLoss = _invertedLossMap.Extrapolate(inAngularVelocity.Value(), inTorque.Value()); return (inTorque - torqueLoss.Value.SI<NewtonMeter>()) / _ratio; } diff --git a/VectoCore/VectoCore/Utils/ContextStopWatch.cs b/VectoCore/VectoCore/Utils/ContextStopWatch.cs new file mode 100644 index 0000000000000000000000000000000000000000..7814035b04daceb8901c04ab43e9a76e9a9dd453 --- /dev/null +++ b/VectoCore/VectoCore/Utils/ContextStopWatch.cs @@ -0,0 +1,29 @@ +using System; +using System.Diagnostics; + +namespace TUGraz.VectoCore.Utils +{ + /// <summary> + /// StopWatch which works like an IDisposable-Context. + /// Usage: using(new ContextStopWatch("main")) { ... } + /// </summary> + public class ContextStopWatch : Stopwatch, IDisposable + { + private readonly string _name; + + public ContextStopWatch(string name = null) + { + Start(); + _name = name; + } + + public void Dispose() + { + Stop(); + if (_name != null) + Console.WriteLine("{0}: {1}", _name, Elapsed); + else + Console.WriteLine(Elapsed); + } + } +} \ No newline at end of file diff --git a/VectoCore/VectoCore/Utils/DelaunayMap.cs b/VectoCore/VectoCore/Utils/DelaunayMap.cs index 54036b56b7e8f64c39dc4d9a2e8e2c5d7c6c7ab3..5cc64203cbc07ccaf2770f9f34b63c45fb2648b4 100644 --- a/VectoCore/VectoCore/Utils/DelaunayMap.cs +++ b/VectoCore/VectoCore/Utils/DelaunayMap.cs @@ -45,11 +45,15 @@ namespace TUGraz.VectoCore.Utils { public sealed class DelaunayMap : LoggingObject { - internal readonly ICollection<Point> Points = new HashSet<Point>(); + internal ICollection<Point> Points = new HashSet<Point>(); private List<Triangle> _triangles = new List<Triangle>(); private Edge[] _convexHull; private readonly string _mapName; + private double _minY; + private double _minX; + private double _maxY; + private double _maxX; public DelaunayMap(string name) { @@ -78,10 +82,14 @@ namespace TUGraz.VectoCore.Utils SanitycheckInputPoints(); // The "supertriangle" encompasses all triangulation points. - // This is just a helper triangle which initializes the algorithm and will be removed later. - const int superTriangleScalingFactor = 10; - var max = Points.Max(point => Math.Max(Math.Abs(point.X), Math.Abs(point.Y))) * superTriangleScalingFactor; - var superTriangle = new Triangle(new Point(max, 0), new Point(0, max), new Point(-max, -max)); + // This is just a helper triangle which initializes the algorithm and will be removed in the end of the algorithm. + _maxX = Points.Max(p => p.X); + _maxY = Points.Max(p => p.Y); + _minX = Points.Min(p => p.X); + _minY = Points.Min(p => p.Y); + Points = + Points.Select(p => new Point((p.X - _minX) / (_maxX - _minX), (p.Y - _minY) / (_maxY - _minY), p.Z)).ToList(); + var superTriangle = new Triangle(new Point(-1, -1), new Point(4, -1), new Point(-1, 4)); var triangles = new List<Triangle> { superTriangle }; var pointCount = 0; @@ -146,8 +154,7 @@ namespace TUGraz.VectoCore.Utils public void DrawGraph() { - const int max = 100000; - var superTriangle = new Triangle(new Point(max, 0), new Point(0, max), new Point(-max, -max)); + var superTriangle = new Triangle(new Point(-1, -1), new Point(4, -1), new Point(-1, 4)); DrawGraph(0, _triangles, superTriangle, Points.ToArray()); } @@ -215,6 +222,10 @@ namespace TUGraz.VectoCore.Utils /// null if interpolation has failed.</returns> public double? Interpolate(double x, double y) { + if (!_triangles.Any()) + throw new VectoException("Interpolation not possible. Call DelaunayMap.Triangulate first."); + x = (x - _minX) / (_maxX - _minX); + y = (y - _minY) / (_maxY - _minY); var tr = _triangles.Find(triangle => triangle.IsInside(x, y, exact: true)) ?? _triangles.Find(triangle => triangle.IsInside(x, y, exact: false)); @@ -234,6 +245,8 @@ namespace TUGraz.VectoCore.Utils /// <returns></returns> public double Extrapolate(double x, double y) { + x = (x - _minX) / (_maxX - _minX); + y = (y - _minY) / (_maxY - _minY); var point = new Point(x, y); // get nearest point on convex hull diff --git a/VectoCore/VectoCoreTest/Models/SimulationComponentData/AuxiliaryTypeHelperTest.cs b/VectoCore/VectoCoreTest/Models/SimulationComponentData/AuxiliaryTypeHelperTest.cs index 6e611b978cd4a4f2f902a2cd60b42a68ab133685..3643a4570a5db4ba3584c66200308ee42c7122dc 100644 --- a/VectoCore/VectoCoreTest/Models/SimulationComponentData/AuxiliaryTypeHelperTest.cs +++ b/VectoCore/VectoCoreTest/Models/SimulationComponentData/AuxiliaryTypeHelperTest.cs @@ -29,8 +29,8 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using TUGraz.VectoCommon.Models; using TUGraz.VectoCore.Configuration; using TUGraz.VectoCore.Models.Declaration; diff --git a/VectoCore/VectoCoreTest/Utils/AssertHelper.cs b/VectoCore/VectoCoreTest/Utils/AssertHelper.cs index 5e3a4487004afbfc430090909f81b5fad1fb4f66..ed7cbd9b2321cb54d63aa6bd382a70339544cbd2 100644 --- a/VectoCore/VectoCoreTest/Utils/AssertHelper.cs +++ b/VectoCore/VectoCoreTest/Utils/AssertHelper.cs @@ -29,10 +29,10 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Diagnostics; using System.Globalization; -using Microsoft.VisualStudio.TestTools.UnitTesting; using TUGraz.VectoCommon.Utils; namespace TUGraz.VectoCore.Tests.Utils @@ -73,29 +73,35 @@ namespace TUGraz.VectoCore.Tests.Utils } [DebuggerHidden] - public static void AreRelativeEqual(double expected, SI actual, + public static void AreRelativeEqual(double? expected, SI actual, double toleranceFactor = DoubleExtensionMethods.ToleranceFactor) { - AreRelativeEqual(expected, actual.Value(), toleranceFactor: toleranceFactor); + if (expected.HasValue) { + AreRelativeEqual(expected.Value, actual.Value(), toleranceFactor: toleranceFactor); + } else { + Assert.IsNull(actual, "Both Values have to be null or not null."); + } } [DebuggerHidden] - public static void AreRelativeEqual(double expected, double actual, string message = null, + public static void AreRelativeEqual(double? expected, double? actual, string message = null, double toleranceFactor = DoubleExtensionMethods.ToleranceFactor) { if (!string.IsNullOrWhiteSpace(message)) { - message = "\n_eng_avg" + message; + message = "\n" + message; } else { message = ""; } - if (double.IsNaN(expected)) { - Assert.IsTrue(double.IsNaN(actual), + Assert.IsFalse(expected.HasValue ^ actual.HasValue, "Both Values have to be null or not null."); + + if (double.IsNaN(expected.Value)) { + Assert.IsTrue(double.IsNaN(actual.Value), string.Format("Actual value is not NaN. Expected: {0}, Actual: {1}{2}", expected, actual, message)); return; } - var ratio = expected == 0 ? Math.Abs(actual) : Math.Abs(actual / expected - 1); + var ratio = expected == 0 ? Math.Abs(actual.Value) : Math.Abs(actual.Value / expected.Value - 1); Assert.IsTrue(ratio < toleranceFactor, string.Format(CultureInfo.InvariantCulture, "Given values are not equal. Expected: {0}, Actual: {1}, Difference: {3} (Tolerance Factor: {2}){4}", expected, actual, toleranceFactor, expected - actual, message)); diff --git a/VectoCore/VectoCoreTest/Utils/DelauneyMapTest.cs b/VectoCore/VectoCoreTest/Utils/DelauneyMapTest.cs index 6319a472f2e9ad519c685b0f6a7cc02555a1c132..7d0732987fa4412c0583003741f596ce708c45c7 100644 --- a/VectoCore/VectoCoreTest/Utils/DelauneyMapTest.cs +++ b/VectoCore/VectoCoreTest/Utils/DelauneyMapTest.cs @@ -29,9 +29,12 @@ * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology */ -using System; using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; +using System.Linq; using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Tests.Utils @@ -51,7 +54,7 @@ namespace TUGraz.VectoCore.Tests.Utils var result = map.Interpolate(0.25, 0.25); - AssertHelper.AreRelativeEqual(0, result.Value); + AssertHelper.AreRelativeEqual(0, result); } [TestMethod] @@ -65,21 +68,21 @@ namespace TUGraz.VectoCore.Tests.Utils map.Triangulate(); // fixed points - AssertHelper.AreRelativeEqual(0, map.Interpolate(0, 0).Value); - AssertHelper.AreRelativeEqual(1, map.Interpolate(1, 0).Value); - AssertHelper.AreRelativeEqual(2, map.Interpolate(0, 1).Value); + AssertHelper.AreRelativeEqual(0, map.Interpolate(0, 0)); + AssertHelper.AreRelativeEqual(1, map.Interpolate(1, 0)); + AssertHelper.AreRelativeEqual(2, map.Interpolate(0, 1)); // interpolations - AssertHelper.AreRelativeEqual(0.5, map.Interpolate(0.5, 0).Value); - AssertHelper.AreRelativeEqual(1, map.Interpolate(0, 0.5).Value); - AssertHelper.AreRelativeEqual(1.5, map.Interpolate(0.5, 0.5).Value); + AssertHelper.AreRelativeEqual(0.5, map.Interpolate(0.5, 0)); + AssertHelper.AreRelativeEqual(1, map.Interpolate(0, 0.5)); + AssertHelper.AreRelativeEqual(1.5, map.Interpolate(0.5, 0.5)); - AssertHelper.AreRelativeEqual(0.25, map.Interpolate(0.25, 0).Value); - AssertHelper.AreRelativeEqual(0.5, map.Interpolate(0, 0.25).Value); - AssertHelper.AreRelativeEqual(0.75, map.Interpolate(0.25, 0.25).Value); + AssertHelper.AreRelativeEqual(0.25, map.Interpolate(0.25, 0)); + AssertHelper.AreRelativeEqual(0.5, map.Interpolate(0, 0.25)); + AssertHelper.AreRelativeEqual(0.75, map.Interpolate(0.25, 0.25)); - AssertHelper.AreRelativeEqual(0.75, map.Interpolate(0.75, 0).Value); - AssertHelper.AreRelativeEqual(1.5, map.Interpolate(0, 0.75).Value); + AssertHelper.AreRelativeEqual(0.75, map.Interpolate(0.75, 0)); + AssertHelper.AreRelativeEqual(1.5, map.Interpolate(0, 0.75)); // extrapolation (should fail) Assert.IsNull(map.Interpolate(1, 1)); @@ -99,24 +102,24 @@ namespace TUGraz.VectoCore.Tests.Utils map.Triangulate(); // fixed points - AssertHelper.AreRelativeEqual(0, map.Interpolate(0, 0).Value); - AssertHelper.AreRelativeEqual(1, map.Interpolate(1, 0).Value); - AssertHelper.AreRelativeEqual(2, map.Interpolate(0, 1).Value); - AssertHelper.AreRelativeEqual(3, map.Interpolate(1, 1).Value); + AssertHelper.AreRelativeEqual(0, map.Interpolate(0, 0)); + AssertHelper.AreRelativeEqual(1, map.Interpolate(1, 0)); + AssertHelper.AreRelativeEqual(2, map.Interpolate(0, 1)); + AssertHelper.AreRelativeEqual(3, map.Interpolate(1, 1)); // interpolations - AssertHelper.AreRelativeEqual(0.5, map.Interpolate(0.5, 0).Value); - AssertHelper.AreRelativeEqual(1, map.Interpolate(0, 0.5).Value); - AssertHelper.AreRelativeEqual(2, map.Interpolate(1, 0.5).Value); - AssertHelper.AreRelativeEqual(2.5, map.Interpolate(0.5, 1).Value); + AssertHelper.AreRelativeEqual(0.5, map.Interpolate(0.5, 0)); + AssertHelper.AreRelativeEqual(1, map.Interpolate(0, 0.5)); + AssertHelper.AreRelativeEqual(2, map.Interpolate(1, 0.5)); + AssertHelper.AreRelativeEqual(2.5, map.Interpolate(0.5, 1)); - AssertHelper.AreRelativeEqual(1.5, map.Interpolate(0.5, 0.5).Value); + AssertHelper.AreRelativeEqual(1.5, map.Interpolate(0.5, 0.5)); - AssertHelper.AreRelativeEqual(0.75, map.Interpolate(0.25, 0.25).Value); - AssertHelper.AreRelativeEqual(2.25, map.Interpolate(0.75, 0.75).Value); + AssertHelper.AreRelativeEqual(0.75, map.Interpolate(0.25, 0.25)); + AssertHelper.AreRelativeEqual(2.25, map.Interpolate(0.75, 0.75)); - AssertHelper.AreRelativeEqual(1.75, map.Interpolate(0.25, 0.75).Value); - AssertHelper.AreRelativeEqual(1.25, map.Interpolate(0.75, 0.25).Value); + AssertHelper.AreRelativeEqual(1.75, map.Interpolate(0.25, 0.75)); + AssertHelper.AreRelativeEqual(1.25, map.Interpolate(0.75, 0.25)); // extrapolation (should fail) AssertHelper.Exception<VectoException>(() => map.Interpolate(1.5, 0.5), "Interpolation failed."); @@ -168,5 +171,43 @@ namespace TUGraz.VectoCore.Tests.Utils AssertHelper.Exception<VectoException>(() => { map.Triangulate(); }, "TEST: Input Data for Delaunay map contains duplicates! \n1 / 1"); } + + [TestMethod] + public void Test_Delaunay_NormalOperation() + { + foreach (var factors in new[] { + Tuple.Create(1.0, 1.0), + Tuple.Create(1.0, 0.04), + Tuple.Create(1.0, 0.1), + Tuple.Create(1.0, 0.01), + Tuple.Create(1.0, 0.0001) + }) { + var xfactor = factors.Item1; + var yfactor = factors.Item2; + + var map = new DelaunayMap("TEST"); + var points = + File.ReadAllLines(@"TestData\Components\40t_Long_Haul_Truck.vmap") + .Skip(1) + .Select(s => { + var p = s.Split(',').ToDouble().ToList(); + return new Point(p[0] * xfactor, p[1] * yfactor, p[2]); + }) + .ToList(); + + points.ForEach(p => map.AddPoint(p.X, p.Y, p.Z)); + map.Triangulate(); + + // test fixed points + foreach (var p in points) { + AssertHelper.AreRelativeEqual(p.Z, map.Interpolate(p.X, p.Y)); + } + map.DrawGraph(); + + // test one arbitrary point in the middle + AssertHelper.AreRelativeEqual(37681, map.Interpolate(1500 * xfactor, 1300 * yfactor), + string.Format("{0}, {1}", xfactor, yfactor)); + } + } } } \ No newline at end of file