Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
Commit bdf77162 authored by Markus Quaritsch's avatar Markus Quaritsch
Browse files

Merge pull request #34 in VECTO/vecto-sim from...

Merge pull request #34 in VECTO/vecto-sim from ~EMKRISPMI/vecto-sim:feature/VECTO-82-write-test-for-delauney to develop

* commit 'b6753b9b':
  FuelConsumptionMap Tests
  added tolerance to double comparison in Tests
  Tests for DelauneyMap
  added test file for delauney map
  Delauney: added comments to the triangulate algorithm
  Delauney Map triangulate minor changes
  better readability of triangulation method
parents 8ad8375e b6753b9b
No related branches found
No related tags found
No related merge requests found
......@@ -86,7 +86,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
};
if (entry.FuelConsumption < 0)
throw new ArgumentOutOfRangeException("FuelConsumption < 0" + data.Rows.IndexOf(row));
throw new ArgumentOutOfRangeException("FuelConsumption < 0");
fuelConsumptionMap._entries.Add(entry);
fuelConsumptionMap._fuelMap.AddPoint(entry.EngineSpeed, entry.Torque, entry.FuelConsumption);
......
......@@ -7,7 +7,7 @@ using TUGraz.VectoCore.Exceptions;
namespace TUGraz.VectoCore.Utils
{
[JsonObject(MemberSerialization.Fields)]
class DelauneyMap
public class DelauneyMap
{
private readonly List<Point> _points = new List<Point>();
private List<Triangle> _triangles = new List<Triangle>();
......@@ -19,44 +19,45 @@ namespace TUGraz.VectoCore.Utils
public void Triangulate()
{
const int superTriangleScalingFactor = 10;
if (_points.Count < 3)
throw new ArgumentException(string.Format("Triangulations needs at least 3 Points. Got {0} Points.", _points.Count));
var superTriangle = CalculateSuperTriangle();
// The "supertriangle" encompasses all triangulation points.
// This triangle initializes the algorithm and will be removed later.
var max = _points.Max(point => Math.Max(Math.Abs(point.X), Math.Abs(point.Y))) * superTriangleScalingFactor;
var superTriangle = new Triangle(new Point(max, 0, 0), new Point(0, max, 0), new Point(-max, -max, 0));
var triangles = new List<Triangle> { superTriangle };
foreach (var point in _points)
{
var edges = new List<Edge>();
// If the actual vertex lies inside the circumcircle, then the three edges of the
// triangle are added to the edge buffer and the triangle is removed from list.
foreach (var containerTriangle in triangles.Where(triangle => triangle.ContainsInCircumcircle(point)).ToList())
{
edges.Add(new Edge(containerTriangle.P1, containerTriangle.P2));
edges.Add(new Edge(containerTriangle.P2, containerTriangle.P3));
edges.Add(new Edge(containerTriangle.P3, containerTriangle.P1));
triangles.Remove(containerTriangle);
}
// If the actual vertex lies inside a triangle, the edges of the triangle are
// added to the edge buffer and the triangle is removed from list.
var containerTriangles = triangles.FindAll(t => t.ContainsInCircumcircle(point));
triangles.RemoveAll(t => t.ContainsInCircumcircle(point));
// Remove duplicate edges. This leaves the convex hull of the edges.
// The edges in this convex hull are oriented counterclockwise!
var convexHullEdges = edges.GroupBy(edge => edge).Where(group => group.Count() == 1).SelectMany(group => group);
var convexHullEdges = containerTriangles.
SelectMany(t => t.GetEdges()).
GroupBy(edge => edge).
Where(group => group.Count() == 1).
SelectMany(group => group);
var newTriangles = convexHullEdges.Select(edge => new Triangle(edge.P1, edge.P2, point));
// Generate new counterclockwise oriented triangles filling the "hole" in
// the existing triangulation. These triangles all share the actual vertex.
var counterTriangles = convexHullEdges.Select(edge => new Triangle(edge.P1, edge.P2, point));
triangles.AddRange(counterTriangles);
triangles.AddRange(newTriangles);
}
// Remove all triangles sharing a vertex with the supertriangle.
_triangles = triangles.Where(triangle => !triangle.SharesVertexWith(superTriangle)).ToList();
_triangles = triangles.FindAll(t => !t.SharesVertexWith(superTriangle));
}
private Triangle CalculateSuperTriangle()
{
const int superTriangleScalingFactor = 10;
var max = _points.Max(point => Math.Max(Math.Abs(point.X), Math.Abs(point.Y))) * superTriangleScalingFactor;
return new Triangle(new Point(max, 0, 0), new Point(0, max, 0), new Point(-max, -max, 0));
}
public double Interpolate(double x, double y)
......@@ -84,14 +85,14 @@ namespace TUGraz.VectoCore.Utils
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((DelauneyMap) obj);
return Equals((DelauneyMap)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_points != null ? _points.GetHashCode() : 0)*397) ^
return ((_points != null ? _points.GetHashCode() : 0) * 397) ^
(_triangles != null ? _triangles.GetHashCode() : 0);
}
}
......@@ -132,12 +133,12 @@ namespace TUGraz.VectoCore.Utils
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((Point) obj);
return obj.GetType() == GetType() && Equals((Point)obj);
}
public override int GetHashCode()
{
return unchecked((((X.GetHashCode()*397) ^ Y.GetHashCode())*397) ^ Z.GetHashCode());
return unchecked((((X.GetHashCode() * 397) ^ Y.GetHashCode()) * 397) ^ Z.GetHashCode());
}
#endregion
}
......@@ -255,7 +256,7 @@ namespace TUGraz.VectoCore.Utils
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Triangle) obj);
return Equals((Triangle)obj);
}
public override int GetHashCode()
......@@ -263,14 +264,20 @@ namespace TUGraz.VectoCore.Utils
unchecked
{
var hashCode = (P1 != null ? P1.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (P2 != null ? P2.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (P3 != null ? P3.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (P2 != null ? P2.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (P3 != null ? P3.GetHashCode() : 0);
return hashCode;
}
}
#endregion
public IEnumerable<Edge> GetEdges()
{
yield return new Edge(P1, P2);
yield return new Edge(P2, P3);
yield return new Edge(P3, P1);
}
}
public class Edge
......@@ -301,7 +308,7 @@ namespace TUGraz.VectoCore.Utils
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((Edge) obj);
return obj.GetType() == GetType() && Equals((Edge)obj);
}
public override int GetHashCode()
......
using System.IO;
using System.Linq;
using System.Globalization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine;
namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData
{
[TestClass]
public class FuelConsumptionMapTest
{
private const double Tolerance = 0.0001;
[TestMethod]
public void TestFuelConsumption_FixedPoints()
{
var map = FuelConsumptionMap.ReadFromFile(@"TestData\Components\24t Coach.vmap");
var lines = File.ReadAllLines(@"TestData\Components\24t Coach.vmap").Skip(1).ToArray();
AssertMapValuesEqual(lines, map);
}
[TestMethod]
public void TestFuelConsumption_InterpolatedPoints()
{
var map = FuelConsumptionMap.ReadFromFile(@"TestData\Components\24t Coach.vmap");
var lines = File.ReadAllLines(@"TestData\Components\24t CoachInterpolated.vmap").Skip(1).ToArray();
AssertMapValuesEqual(lines, map);
}
private static void AssertMapValuesEqual(string[] lines, FuelConsumptionMap map)
{
for (var i = 1; i < lines.Count(); i++)
{
var entry = lines[i].Split(',').Select(x => double.Parse(x, CultureInfo.InvariantCulture)).ToArray();
Assert.AreEqual(entry[2], map.GetFuelConsumption(entry[0], entry[1]), Tolerance,
string.Format("Line: {0}, n={1}, T={2}", (i + 2), entry[0], entry[1]));
}
}
}
}
engine speed [1/min],torque [Nm],fuel consumption [g/h]
560,-74.5,628
560,100,2226.5
560,300,4246
560,500,6455
560,700,8495
560,900,10307
560,1090,12054
600,-74,729.5
600,100,2408.5
600,300,4428
600,500,6799.5
600,700,9057.5
600,900,11042.5
600,1100,13136
600,1241,14752.5
800,-74.5,939.5
800,100,3082.5
800,300,5653.5
800,500,8540
800,700,11572.5
800,900,14550.5
800,1100,17627
800,1300,20832.5
800,1500,23954.5
800,1695.5,27194
1000,-80,1432.5
1000,100,4414
1000,300,7580.5
1000,500,10776
1000,700,14159.5
1000,900,17914.5
1000,1100,21697
1000,1300,25366
1000,1500,29183.5
1000,1700,33134
1000,1900,37231.5
1000,2100,41740
1000,2250,45478
1200,-89.5,1653.5
1200,100,5102
1200,300,8774
1200,500,12648
1200,700,16880
1200,900,21396
1200,1100,25928.5
1200,1300,30305.5
1200,1500,34564.5
1200,1700,39194.5
1200,1900,44303
1200,2100,49349
1200,2250,53357.5
1400,-101.5,2153
1400,100,6224.5
1400,300,10433
1400,500,15123
1400,700,19905.5
1400,900,24690.5
1400,1100,29814.5
1400,1300,35141
1400,1500,40470
1400,1700,46323.5
1400,1900,52641.5
1400,2100,58451
1400,2250,62724.5
1600,-117.5,2604.5
1600,100,7439
1600,300,12253.5
1600,500,17482.5
1600,700,23010.5
1600,900,28762.5
1600,1100,34439.5
1600,1300,40037
1600,1500,46289
1600,1700,53386
1600,1900,60467
1600,2039.5,65217
1800,-132,3204.5
1800,100,9093
1800,300,14548.5
1800,500,20357
1800,700,26947.5
1800,900,33439.5
1800,1100,39728.5
1800,1300,46437.5
1800,1500,53616
1800,1700,61296.5
1800,1828.5,66365.5
2000,-150.5,4563.5
2000,100,11974.5
2000,300,17738.5
2000,500,23865.5
2000,700,30632
2000,900,38512.5
2000,1100,46927.5
2000,1276,53818
2100,-160,5235
2100,100,13401
2100,300,19364
2100,500,25655
2100,700,32315.5
2100,900,40680
2100,1050,48148
1100,2300,50884
1300,2300,59654.5
1100,2200,47951.5
1300,2200,56427.5
1500,2139.5,63796
1100,2000,43137.5
1300,2000,51372.5
1500,2000,59872
1700,1928.5,65744
1100,1800,38397
1300,1800,45572
1500,1800,53236.5
1700,1800,61088.5
800,1695.5,27194
900,1600,28324
1100,1600,33931.5
1300,1600,39946
1500,1600,46473
1700,1600,53594
900,1400,24814
1100,1400,29816.5
1300,1400,35088.5
1500,1400,40286
1700,1400,46311
1900,1376,53207
600,1241,14752.5
700,1200,16720
900,1200,21384.5
1100,1200,25855
1300,1200,30358
1500,1200,34892
1700,1200,40163.5
1900,1200,47048.5
580,1000,11655
700,1000,14043
900,1000,17939.5
1100,1000,21770.5
1300,1000,25385
1500,1000,29362
1700,1000,34004.5
1900,1000,39607.5
2050,1000,44240
580,800,9694.5
700,800,11550
900,800,14525.5
1100,800,17540
1300,800,20701.5
1500,800,24091
1700,800,28197.5
1900,800,32344.5
2050,800,34952.5
580,600,7858
700,600,9080
900,600,11206.5
1100,600,13499.5
1300,600,16084
1500,600,18825
1700,600,21760.5
1900,600,25235
2050,600,27995
580,400,5396.5
700,400,6259.5
900,400,8109.5
1100,400,9924.5
1300,400,11687
1500,400,13780.5
1700,400,16079
1900,400,18987.5
2050,400,21525.5
580,200,3277.5
700,200,3822
900,200,5124.5
1100,200,6430
1300,200,7520
1500,200,8906
1700,200,10723
1900,200,13299.5
2050,200,15577
580,0,1357.5
700,0,1669
900,0,2372
1100,0,3086
1300,0,3806.5
1500,0,4757.5
1700,0,5809
1900,0,7768
2050,0,9798.5
580,-148.5,0
680,-149,0
900,-154.5,0
1100,-169.5,0
1300,-191,0
1500,-219,0
1700,-249.5,0
1900,-282.5,0
2050,-310.5,0
\ No newline at end of file
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TUGraz.VectoCore.Exceptions;
using TUGraz.VectoCore.Utils;
namespace TUGraz.VectoCore.Tests.Utils
{
[TestClass]
public class DelauneyMapTest
{
private const double tolerance = 0.00001;
public static void AssertException<T>(Action func, string message) where T : Exception
{
try
{
func();
Assert.Fail();
}
catch (T ex)
{
Assert.AreEqual(message, ex.Message);
}
}
[TestMethod]
public void Test_Simple_DelauneyMap()
{
var map = new DelauneyMap();
map.AddPoint(0, 0, 0);
map.AddPoint(1, 0, 0);
map.AddPoint(0, 1, 0);
map.Triangulate();
var result = map.Interpolate(0.25, 0.25);
Assert.AreEqual(0, result, tolerance);
}
[TestMethod]
public void Test_DelauneyMapTriangle()
{
var map = new DelauneyMap();
map.AddPoint(0, 0, 0);
map.AddPoint(1, 0, 1);
map.AddPoint(0, 1, 2);
map.Triangulate();
// fixed points
Assert.AreEqual(0, map.Interpolate(0, 0), tolerance);
Assert.AreEqual(1, map.Interpolate(1, 0), tolerance);
Assert.AreEqual(2, map.Interpolate(0, 1), tolerance);
// interpolations
Assert.AreEqual(0.5, map.Interpolate(0.5, 0), tolerance);
Assert.AreEqual(1, map.Interpolate(0, 0.5), tolerance);
Assert.AreEqual(1.5, map.Interpolate(0.5, 0.5), tolerance);
Assert.AreEqual(0.25, map.Interpolate(0.25, 0), tolerance);
Assert.AreEqual(0.5, map.Interpolate(0, 0.25), tolerance);
Assert.AreEqual(0.75, map.Interpolate(0.25, 0.25), tolerance);
Assert.AreEqual(0.75, map.Interpolate(0.75, 0), tolerance);
Assert.AreEqual(1.5, map.Interpolate(0, 0.75), tolerance);
// extrapolation (should fail)
AssertException<VectoException>(() => map.Interpolate(1, 1), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(-1, -1), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(1, -1), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(-1, 1), "Interpolation failed.");
}
public void Test_DelauneyMapPlane()
{
var map = new DelauneyMap();
map.AddPoint(0, 0, 0);
map.AddPoint(1, 0, 1);
map.AddPoint(0, 1, 2);
map.AddPoint(1, 1, 3);
map.Triangulate();
// fixed points
Assert.AreEqual(0, map.Interpolate(0, 0), tolerance);
Assert.AreEqual(1, map.Interpolate(1, 0), tolerance);
Assert.AreEqual(2, map.Interpolate(0, 1), tolerance);
Assert.AreEqual(3, map.Interpolate(1, 1), tolerance);
// interpolations
Assert.AreEqual(0.5, map.Interpolate(0.5, 0), tolerance);
Assert.AreEqual(1, map.Interpolate(0, 0.5), tolerance);
Assert.AreEqual(2, map.Interpolate(1, 0.5), tolerance);
Assert.AreEqual(2.5, map.Interpolate(0.5, 1), tolerance);
Assert.AreEqual(1.5, map.Interpolate(0.5, 0.5), tolerance);
Assert.AreEqual(0.75, map.Interpolate(0.25, 0.25), tolerance);
Assert.AreEqual(2.25, map.Interpolate(0.75, 0.75), tolerance);
Assert.AreEqual(1.75, map.Interpolate(0.25, 0.75), tolerance);
Assert.AreEqual(1.25, map.Interpolate(0.75, 0.25), tolerance);
// extrapolation (should fail)
AssertException<VectoException>(() => map.Interpolate(1.5, 0.5), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(1.5, 1.5), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(0.5, 1.5), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(-0.5, 1.5), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(-0.5, 0.5), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(-1.5, -1.5), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(0.5, -0.5), "Interpolation failed.");
AssertException<VectoException>(() => map.Interpolate(-1.5, -0.5), "Interpolation failed.");
}
[TestMethod]
public void Test_Delauney_LessThan3Points()
{
DelauneyMap map;
try
{
map = new DelauneyMap();
map.Triangulate();
Assert.Fail();
}
catch (ArgumentException ex)
{
Assert.AreEqual("Triangulations needs at least 3 Points. Got 0 Points.", ex.Message);
}
try
{
map = new DelauneyMap();
map.AddPoint(0, 0, 0);
map.Triangulate();
Assert.Fail();
}
catch (ArgumentException ex)
{
Assert.AreEqual("Triangulations needs at least 3 Points. Got 1 Points.", ex.Message);
}
try
{
map = new DelauneyMap();
map.AddPoint(0, 0, 0);
map.AddPoint(0, 0, 0);
map.Triangulate();
Assert.Fail();
}
catch (ArgumentException ex)
{
Assert.AreEqual("Triangulations needs at least 3 Points. Got 2 Points.", ex.Message);
}
map = new DelauneyMap();
map.AddPoint(0, 0, 0);
map.AddPoint(1, 0, 0);
map.AddPoint(0, 1, 0);
map.Triangulate();
}
}
}
......@@ -69,6 +69,7 @@
</Choose>
<ItemGroup>
<Compile Include="Integration\EngineOnlyCycle\EngineOnlyCycleTest.cs" />
<Compile Include="Models\SimulationComponentData\FuelConsumptionMapTest.cs" />
<Compile Include="Models\SimulationComponentData\FullLoadCurveTest.cs" />
<Compile Include="Models\SimulationComponent\CombustionEngineTest.cs" />
<Compile Include="Models\Simulation\DrivingCycleTests.cs" />
......@@ -81,6 +82,7 @@
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Include="Utils\DelauneyMapTest.cs" />
<Compile Include="Utils\TestModalDataWriter.cs" />
</ItemGroup>
<ItemGroup>
......@@ -97,6 +99,9 @@
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<None Include="TestData\Components\24t CoachInterpolated.vmap">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="TestData\Components\FullLoadCurve insufficient columns.vfld">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment