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

Skip to content
Snippets Groups Projects
Commit a7ebc210 authored by Michael KRISPER's avatar Michael KRISPER
Browse files

fuelconsumption map changed to si units

parent bdf77162
No related branches found
No related tags found
No related merge requests found
......@@ -8,16 +8,6 @@ using TUGraz.VectoCore.Utils;
namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
{
/// <summary>
/// Three columns
/// One header line
/// At least four lines with numeric values (below file header)
/// The map must cover the full engine range between full load and motoring curve. Extrapolation is not possible!
/// Columns:
/// * engine speed [1/min]
/// * engine torque [Nm]
/// * Fuel Consumption [g/h]
/// </summary>
[JsonObject(MemberSerialization.Fields)]
public class FuelConsumptionMap : SimulationComponentData
{
......@@ -30,8 +20,19 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
private class FuelConsumptionEntry
{
/// <summary>
/// engine speed [rad/s]
/// </summary>
public double EngineSpeed { get; set; }
/// <summary>
/// Torque [Nm]
/// </summary>
public double Torque { get; set; }
/// <summary>
/// Fuel consumption [g/s]
/// </summary>
public double FuelConsumption { get; set; }
#region Equality members
......@@ -46,7 +47,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((FuelConsumptionEntry) obj);
return Equals((FuelConsumptionEntry)obj);
}
public override int GetHashCode()
......@@ -54,8 +55,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
unchecked
{
var hashCode = EngineSpeed.GetHashCode();
hashCode = (hashCode*397) ^ Torque.GetHashCode();
hashCode = (hashCode*397) ^ FuelConsumption.GetHashCode();
hashCode = (hashCode * 397) ^ Torque.GetHashCode();
hashCode = (hashCode * 397) ^ FuelConsumption.GetHashCode();
return hashCode;
}
}
......@@ -80,16 +81,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
{
var entry = new FuelConsumptionEntry
{
EngineSpeed = row.ParseDouble(Fields.EngineSpeed),
EngineSpeed = row.ParseDouble(Fields.EngineSpeed) / Units.RPMPerRadiant,
Torque = row.ParseDouble(Fields.Torque),
FuelConsumption = row.ParseDouble(Fields.FuelConsumption)
FuelConsumption = row.ParseDouble(Fields.FuelConsumption) * 1 / Units.SecondsPerHour
};
if (entry.FuelConsumption < 0)
throw new ArgumentOutOfRangeException("FuelConsumption < 0");
fuelConsumptionMap._entries.Add(entry);
fuelConsumptionMap._fuelMap.AddPoint(entry.EngineSpeed, entry.Torque, entry.FuelConsumption);
// the delauney map works as expected, when the original engine speed field is used.
fuelConsumptionMap._fuelMap.AddPoint(entry.EngineSpeed * Units.RPMPerRadiant, entry.Torque, entry.FuelConsumption);
}
catch (Exception e)
{
......@@ -106,9 +109,15 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
return fuelConsumptionMap;
}
/// <summary>
/// Calculates the fuel consumption based on the given fuel map.
/// </summary>
/// <param name="engineSpeed">Engine speed (n) in [rad/sec].</param>
/// <param name="torque">Torque (T) in [Nm].</param>
/// <returns></returns>
public double GetFuelConsumption(double engineSpeed, double torque)
{
return _fuelMap.Interpolate(engineSpeed, torque);
return _fuelMap.Interpolate(engineSpeed * Units.RPMPerRadiant, torque);
}
#region Equality members
......@@ -124,14 +133,14 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((FuelConsumptionMap) obj);
return Equals((FuelConsumptionMap)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_entries != null ? _entries.GetHashCode() : 0)*397) ^
return ((_entries != null ? _entries.GetHashCode() : 0) * 397) ^
(_fuelMap != null ? _fuelMap.GetHashCode() : 0);
}
}
......
using System;
using System.Collections.Generic;
using System.Linq;
using Common.Logging;
using Newtonsoft.Json;
using System.Collections.Generic;
using TUGraz.VectoCore.Exceptions;
namespace TUGraz.VectoCore.Utils
......@@ -22,16 +23,13 @@ namespace TUGraz.VectoCore.Utils
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 superTriangle = CalculateSuperTriangle();
var triangles = new List<Triangle> { superTriangle };
foreach (var point in _points)
{
// 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));
......@@ -47,7 +45,7 @@ namespace TUGraz.VectoCore.Utils
var newTriangles = convexHullEdges.Select(edge => new Triangle(edge.P1, edge.P2, point));
triangles.AddRange(newTriangles);
triangles.AddRange(newTriangles);
}
_triangles = triangles.FindAll(t => !t.SharesVertexWith(superTriangle));
......@@ -62,11 +60,14 @@ namespace TUGraz.VectoCore.Utils
public double Interpolate(double x, double y)
{
var tr = _triangles.Find(triangle => triangle.IsInside(x, y, exact: true)) ??
_triangles.Find(triangle => triangle.IsInside(x, y, exact: false));
var tr = _triangles.Find(triangle => triangle.IsInside(x, y, exact: true));
if (tr == null)
throw new VectoException("Interpolation failed.");
{
LogManager.GetLogger(GetType()).Info("Exact search found no fitting triangle. Approximation will be used.");
tr = _triangles.Find(triangle => triangle.IsInside(x, y, exact: false));
if (tr == null)
throw new VectoException(string.Format("Interpolation failed. x: {0}, y: {1}", x, y));
}
var plane = new Plane(tr);
return (plane.W - plane.X * x - plane.Y * y) / plane.Z;
......@@ -98,225 +99,237 @@ namespace TUGraz.VectoCore.Utils
}
#endregion
}
public class Point
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public Point(double x, double y, double z)
private class Point
{
X = x;
Y = y;
Z = z;
}
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public static Point operator -(Point p1, Point p2)
{
return new Point(p1.X - p2.X, p1.Y - p2.Y, p1.Z - p2.Z);
}
public Point(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return string.Format("Point({0}, {1}, {2})", X, Y, Z);
}
public static Point operator -(Point p1, Point p2)
{
return new Point(p1.X - p2.X, p1.Y - p2.Y, p1.Z - p2.Z);
}
#region Equality members
protected bool Equals(Point other)
{
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
public override string ToString()
{
return string.Format("Point({0}, {1}, {2})", X, Y, Z);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((Point)obj);
}
#region Equality members
protected bool Equals(Point other)
{
//const double tolerance = 0.0001m;
//return Math.Abs(X - other.X) < tolerance
// && Math.Abs(X - other.X) < tolerance
// && Math.Abs(X - other.X) < tolerance;
public override int GetHashCode()
{
return unchecked((((X.GetHashCode() * 397) ^ Y.GetHashCode()) * 397) ^ Z.GetHashCode());
}
#endregion
}
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
}
public class Plane
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public double W { get; set; }
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((Point)obj);
}
public Plane(double x, double y, double z, double w)
{
X = x;
Y = y;
Z = z;
W = w;
public override int GetHashCode()
{
return unchecked((((X.GetHashCode() * 397) ^ Y.GetHashCode()) * 397) ^ Z.GetHashCode());
}
#endregion
}
public Plane(Triangle tr)
private class Plane
{
var ab = tr.P2 - tr.P1;
var ac = tr.P3 - tr.P1;
var cross = new Point(ab.Y * ac.Z - ab.Z * ac.Y,
ab.Z * ac.X - ab.X * ac.Z,
ab.X * ac.Y - ab.Y * ac.X);
X = cross.X;
Y = cross.Y;
Z = cross.Z;
W = tr.P1.X * cross.X + tr.P1.Y * cross.Y + tr.P1.Z * cross.Z;
}
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public double W { get; set; }
public override string ToString()
{
return string.Format("Plane({0}, {1}, {2}, {3})", X, Y, Z, W);
}
}
public Plane(double x, double y, double z, double w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public class Triangle
{
public Point P1 { get; set; }
public Point P2 { get; set; }
public Point P3 { get; set; }
public Plane(Triangle tr)
{
var ab = tr.P2 - tr.P1;
var ac = tr.P3 - tr.P1;
public Triangle(Point p1, Point p2, Point p3)
{
P1 = p1;
P2 = p2;
P3 = p3;
var cross = new Point(ab.Y * ac.Z - ab.Z * ac.Y,
ab.Z * ac.X - ab.X * ac.Z,
ab.X * ac.Y - ab.Y * ac.X);
X = cross.X;
Y = cross.Y;
Z = cross.Z;
W = tr.P1.X * cross.X + tr.P1.Y * cross.Y + tr.P1.Z * cross.Z;
}
public override string ToString()
{
return string.Format("Plane({0}, {1}, {2}, {3})", X, Y, Z, W);
}
}
public bool IsInside(double x, double y, bool exact = true)
private class Triangle
{
var p = new Point(x, y, 0);
public Point P1 { get; set; }
public Point P2 { get; set; }
public Point P3 { get; set; }
public Triangle(Point p1, Point p2, Point p3)
{
P1 = p1;
P2 = p2;
P3 = p3;
}
var v0 = P3 - P1;
var v1 = P2 - P1;
var v2 = p - P1;
public bool IsInside(double x, double y, bool exact = true)
{
//Barycentric Technique: http://www.blackpawn.com/texts/pointinpoly/default.html
var p = new Point(x, y, 0);
var dot00 = v0.X * v0.X + v0.Y * v0.Y;
var dot01 = v0.X * v1.X + v0.Y * v1.Y;
var dot02 = v0.X * v2.X + v0.Y * v2.Y;
var dot11 = v1.X * v1.X + v1.Y * v1.Y;
var dot12 = v1.X * v2.X + v1.Y * v2.Y;
var v0 = P3 - P1;
var v1 = P2 - P1;
var v2 = p - P1;
var invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
var dot00 = v0.X * v0.X + v0.Y * v0.Y;
var dot01 = v0.X * v1.X + v0.Y * v1.Y;
var dot02 = v0.X * v2.X + v0.Y * v2.Y;
var dot11 = v1.X * v1.X + v1.Y * v1.Y;
var dot12 = v1.X * v2.X + v1.Y * v2.Y;
if (exact)
return u >= 0 && v >= 0 && u + v <= 1;
var invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
return (u >= -0.001) && (v >= -0.001) && (u + v <= 1.001);
}
if (exact)
return u >= 0 && v >= 0 && u + v <= 1;
public bool ContainsInCircumcircle(Point p)
{
var p0 = P1 - p;
var p1 = P2 - p;
var p2 = P3 - p;
return u.IsPositive() && v.IsPositive() && (u + v).IsSmallerOrEqual(1);
}
var p0square = p0.X * p0.X + p0.Y * p0.Y;
var p1square = p1.X * p1.X + p1.Y * p1.Y;
var p2square = p2.X * p2.X + p2.Y * p2.Y;
public bool ContainsInCircumcircle(Point p)
{
var p0 = P1 - p;
var p1 = P2 - p;
var p2 = P3 - p;
var det01 = p0.X * p1.Y - p1.X * p0.Y;
var det12 = p1.X * p2.Y - p2.X * p1.Y;
var det20 = p2.X * p0.Y - p0.X * p2.Y;
var p0square = p0.X * p0.X + p0.Y * p0.Y;
var p1square = p1.X * p1.X + p1.Y * p1.Y;
var p2square = p2.X * p2.X + p2.Y * p2.Y;
var result = p0square * det12 + p1square * det20 + p2square * det01;
return result > 0;
}
var det01 = p0.X * p1.Y - p1.X * p0.Y;
var det12 = p1.X * p2.Y - p2.X * p1.Y;
var det20 = p2.X * p0.Y - p0.X * p2.Y;
public bool SharesVertexWith(Triangle t)
{
return (P1.Equals(t.P1) || P1.Equals(t.P2) || P1.Equals(t.P3)) ||
(P2.Equals(t.P1) || P2.Equals(t.P2) || P2.Equals(t.P3)) ||
(P3.Equals(t.P1) || P3.Equals(t.P2) || P3.Equals(t.P3));
}
public override string ToString()
{
return string.Format("Triangle({0}, {1}, {2})", P1, P2, P3);
}
var result = p0square * det12 + p1square * det20 + p2square * det01;
#region Equality members
return result.IsPositive();
}
protected bool Equals(Triangle other)
{
return Equals(P1, other.P1) && Equals(P2, other.P2) && Equals(P3, other.P3);
}
public bool Contains(Point p)
{
return (p.Equals(P1) || p.Equals(P2) || p.Equals(P3));
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Triangle)obj);
}
public bool SharesVertexWith(Triangle t)
{
return t.Contains(P1) || t.Contains(P2) || t.Contains(P3);
}
public override int GetHashCode()
{
unchecked
public override string ToString()
{
var hashCode = (P1 != null ? P1.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (P2 != null ? P2.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (P3 != null ? P3.GetHashCode() : 0);
return hashCode;
return string.Format("Triangle({0}, {1}, {2})", P1, P2, P3);
}
}
#endregion
#region Equality members
public IEnumerable<Edge> GetEdges()
{
yield return new Edge(P1, P2);
yield return new Edge(P2, P3);
yield return new Edge(P3, P1);
}
}
protected bool Equals(Triangle other)
{
return Equals(P1, other.P1) && Equals(P2, other.P2) && Equals(P3, other.P3);
}
public class Edge
{
public Point P1 { get; set; }
public Point P2 { get; set; }
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Triangle)obj);
}
public Edge(Point p1, Point p2)
{
P1 = p1;
P2 = p2;
public override int GetHashCode()
{
unchecked
{
var hashCode = (P1 != null ? P1.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 override string ToString()
private class Edge
{
return string.Format("Edge({0}, {1})", P1, P2);
}
public Point P1 { get; set; }
public Point P2 { get; set; }
#region Equality members
public Edge(Point p1, Point p2)
{
P1 = p1;
P2 = p2;
}
protected bool Equals(Edge other)
{
return Equals(P1, other.P1) && Equals(P2, other.P2)
|| Equals(P1, other.P2) && Equals(P1, other.P2);
}
public override string ToString()
{
return string.Format("Edge({0}, {1})", P1, P2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((Edge)obj);
}
#region Equality members
public override int GetHashCode()
{
return ((P1 != null ? P1.GetHashCode() : 0)) ^ (P2 != null ? P2.GetHashCode() : 0);
}
protected bool Equals(Edge other)
{
return Equals(P1, other.P1) && Equals(P2, other.P2)
|| Equals(P1, other.P2) && Equals(P1, other.P2);
}
#endregion
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == GetType() && Equals((Edge)obj);
}
public override int GetHashCode()
{
return ((P1 != null ? P1.GetHashCode() : 0)) ^ (P2 != null ? P2.GetHashCode() : 0);
}
#endregion
}
}
}
......@@ -4,31 +4,36 @@ namespace TUGraz.VectoCore.Utils
{
static class FloatingPointExtensionMethods
{
public const double TOLERANCE = 0.001;
public const double Tolerance = 0.001;
public static bool IsEqual(this double d, double other)
{
return Math.Abs(d - other) > TOLERANCE;
return Math.Abs(d - other) > Tolerance;
}
public static bool IsSmaller(this double d, double other)
{
return d - other < TOLERANCE;
return d - other < Tolerance;
}
public static bool IsSmallerOrEqual(this double d, double other)
{
return d - other <= TOLERANCE;
return d - other <= Tolerance;
}
public static bool IsBigger(this double d, double other)
public static bool IsGreater(this double d, double other)
{
return other.IsSmallerOrEqual(d);
}
public static bool IsBiggerOrEqual(this double d, double other)
public static bool IsGreaterOrEqual(this double d, double other)
{
return other.IsSmaller(d);
}
public static bool IsPositive(this double d)
{
return d.IsGreaterOrEqual(0.0);
}
}
}
\ No newline at end of file
using System;
namespace TUGraz.VectoCore.Utils
{
public static class Units
{
public const double SecondsPerMinute = 60.0;
public const double SecondsPerHour = 3600.0;
public const double RPMPerRadiant = 2.0 * Math.PI / SecondsPerMinute;
}
}
\ No newline at end of file
......@@ -75,6 +75,7 @@
<Compile Include="Models\SimulationComponent\Data\DrivingCycleData.cs" />
<Compile Include="Models\SimulationComponent\Data\Engine\FuelConsumptionMap.cs" />
<Compile Include="Models\SimulationComponent\Data\Engine\FullLoadCurve.cs" />
<Compile Include="Utils\Units.cs" />
<Compile Include="Models\SimulationComponent\Data\SimulationComponentData.cs" />
<Compile Include="Models\SimulationComponent\ICombustionEngine.cs" />
<Compile Include="Models\Connector\Ports\IInPort.cs" />
......
using System.IO;
using System;
using System.IO;
using System.Linq;
using System.Globalization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TUGraz.VectoCore.Exceptions;
using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine;
using TUGraz.VectoCore.Utils;
namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData
{
......@@ -32,9 +35,17 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData
for (var i = 1; i < lines.Count(); i++)
{
var entry = lines[i].Split(',').Select(x => double.Parse(x, CultureInfo.InvariantCulture)).ToArray();
try
{
Assert.AreEqual(entry[2] * 1 / Units.SecondsPerHour, map.GetFuelConsumption(entry[0] / Units.RPMPerRadiant, entry[1]), Tolerance,
string.Format("Line: {0}, n={1}, T={2}", (i + 2), entry[0], entry[1]));
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]));
}
catch (VectoException ex)
{
throw new VectoException(string.Format("Row {0}: Error in ConsumptionMap n={1}, T={2}: {3}",
i + 2, entry[0], entry[1], ex.Message));
}
}
}
}
......
......@@ -66,10 +66,10 @@ namespace TUGraz.VectoCore.Tests.Utils
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.");
AssertException<VectoException>(() => map.Interpolate(1, 1), "Interpolation failed. x: 1, y: 1");
AssertException<VectoException>(() => map.Interpolate(-1, -1), "Interpolation failed. x: -1, y: -1");
AssertException<VectoException>(() => map.Interpolate(1, -1), "Interpolation failed. x: 1, y: -1");
AssertException<VectoException>(() => map.Interpolate(-1, 1), "Interpolation failed. x: -1, y: 1");
}
......
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