Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS has been phased out. To see alternatives please check here

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

Merge branch 'develop' of...

Merge branch 'develop' of https://webgate.ec.europa.eu/CITnet/stash/scm/~emquarima/vecto-sim into feature/VECTO-54-implement-software-tests-for-engine

Conflicts:
	VectoCore/Exceptions/VectoExceptions.cs
	VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs
	VectoCoreTest/VectoCoreTest.csproj
parents eb6c8ae5 1b4a042a
Branches
Tags
No related merge requests found
Showing
with 813 additions and 186 deletions
using System; using System;
using System.Collections.Generic; using TUGraz.VectoCore.Exceptions;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TUGraz.VectoCore.Exceptions namespace TUGraz.VectoCore.Exceptions
{ {
class VectoException : Exception
abstract class FileIOException : Exception
{
protected FileIOException(string message) : base(message)
{ {
public VectoException(string message) : base(message) { }
public VectoException(string message, Exception innerException) : base(message, innerException) { }
} }
protected FileIOException(string message, Exception inner) abstract class FileIOException : VectoException
: base(message, inner)
{ {
} protected FileIOException(string message) : base(message) { }
protected FileIOException(string message, Exception inner) : base(message, inner) { }
} }
class InvalidFileFormatException : FileIOException class InvalidFileFormatException : FileIOException
...@@ -27,9 +21,22 @@ namespace TUGraz.VectoCore.Exceptions ...@@ -27,9 +21,22 @@ namespace TUGraz.VectoCore.Exceptions
public InvalidFileFormatException(string message, Exception inner) : base(message) { } public InvalidFileFormatException(string message, Exception inner) : base(message) { }
} }
/// <summary>
/// Exception which gets thrown when the version of a file is not supported.
/// </summary>
class UnsupportedFileVersionException : FileIOException class UnsupportedFileVersionException : FileIOException
{ {
public UnsupportedFileVersionException(string message) : base(message) { } public UnsupportedFileVersionException(string message) : base(message) { }
public UnsupportedFileVersionException(string message, Exception inner) : base(message, inner) { } public UnsupportedFileVersionException(string message, Exception inner) : base(message, inner) { }
} }
/// <summary>
/// Exception which gets thrown when an error occurred during read of a vecto csv-file.
/// </summary>
class CSVReadException : FileIOException
{
public CSVReadException(string message) : base(message) { }
public CSVReadException(string message, Exception inner) : base(message, inner) { }
}
} }
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Mime;
using System.Runtime.CompilerServices;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog.Layouts;
using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.Exceptions;
using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine; using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine;
...@@ -45,6 +41,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -45,6 +41,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
/// </code> /// </code>
public class CombustionEngineData : SimulationComponentData public class CombustionEngineData : SimulationComponentData
{ {
/// <summary>
/// A class which represents the json data format for serializing and deserializing the EngineData files.
/// </summary>
public class Data public class Data
{ {
public class DataHeader public class DataHeader
...@@ -67,18 +66,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -67,18 +66,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
public class DataBody public class DataBody
{ {
public class DataFullLoadCurve
{
[JsonProperty(Required = Required.Always)]
public string Gears;
[JsonProperty(Required = Required.Always)]
public string Path;
}
[JsonProperty(Required = Required.Always)]
public IList<DataFullLoadCurve> FullLoadCurves;
[JsonProperty("SavedInDeclMode")] [JsonProperty("SavedInDeclMode")]
public bool SavedInDeclarationMode; public bool SavedInDeclarationMode;
...@@ -94,6 +81,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -94,6 +81,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
[JsonProperty(Required = Required.Always)] [JsonProperty(Required = Required.Always)]
public double Inertia; public double Inertia;
public class DataFullLoadCurve
{
[JsonProperty(Required = Required.Always)]
public string Path;
[JsonProperty(Required = Required.Always)]
public string Gears;
}
[JsonProperty(Required = Required.Always)]
public IList<DataFullLoadCurve> FullLoadCurves;
[JsonProperty(Required = Required.Always)] [JsonProperty(Required = Required.Always)]
public string FuelMap; public string FuelMap;
...@@ -165,10 +164,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -165,10 +164,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
private readonly Dictionary<string, FullLoadCurve> _fullLoadCurves = new Dictionary<string, FullLoadCurve>(); private readonly Dictionary<string, FullLoadCurve> _fullLoadCurves = new Dictionary<string, FullLoadCurve>();
public static CombustionEngineData ReadFromFile(string fileName) public static CombustionEngineData ReadFromFile(string fileName)
{ {
//todo: file exception handling: file not readable //todo: file exception handling: file not readable
...@@ -196,16 +191,21 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -196,16 +191,21 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
return combustionEngineData; return combustionEngineData;
} }
public string WriteToJson() public string ToJson()
{ {
_data.Header.Date = DateTime.Now; _data.Header.Date = DateTime.Now;
_data.Header.FileVersion = 2; _data.Header.FileVersion = 2;
_data.Header.AppVersion = "3.0.0"; // todo: get current app version! _data.Header.AppVersion = "3.0.0"; // todo: get current app version!
_data.Header.CreatedBy = ""; // todo: get current user _data.Header.CreatedBy = ""; // todo: get current user
_data.Body.SavedInDeclarationMode = false; //todo: get declaration mode setting _data.Body.SavedInDeclarationMode = false; //todo: get declaration mode setting
return JsonConvert.SerializeObject(_data); return JsonConvert.SerializeObject(_data, Formatting.Indented);
} }
public void WriteToFile(string fileName)
{
//todo handle file exceptions
File.WriteAllText(fileName, ToJson());
}
public FullLoadCurve GetFullLoadCurve(uint gear) public FullLoadCurve GetFullLoadCurve(uint gear)
{ {
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.IO; using TUGraz.VectoCore.Exceptions;
using TUGraz.VectoCore.Utils; using TUGraz.VectoCore.Utils;
namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
...@@ -32,27 +32,53 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine ...@@ -32,27 +32,53 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
public double FuelConsumption { get; set; } public double FuelConsumption { get; set; }
} }
private IList<FuelConsumptionEntry> entries; private IList<FuelConsumptionEntry> _entries = new List<FuelConsumptionEntry>();
private DelauneyMap _fuelMap = new DelauneyMap();
public FuelConsumptionMap(string fileName) private FuelConsumptionMap() { }
public static FuelConsumptionMap ReadFromFile(string fileName)
{ {
var data = VectoCSVReader.Read(fileName); var fuelConsumptionMap = new FuelConsumptionMap();
entries = new List<FuelConsumptionEntry>(); var data = VectoCSVFile.Read(fileName);
//todo: catch exceptions if value format is wrong. try
{
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
var entry = new FuelConsumptionEntry(); try
entry.EngineSpeed = row.GetDouble(Fields.EngineSpeed); {
entry.Torque = row.GetDouble(Fields.Torque); var entry = new FuelConsumptionEntry
entry.FuelConsumption = row.GetDouble(Fields.FuelConsumption); {
entries.Add(entry); EngineSpeed = row.GetDouble(Fields.EngineSpeed),
Torque = row.GetDouble(Fields.Torque),
FuelConsumption = row.GetDouble(Fields.FuelConsumption)
};
if (entry.FuelConsumption < 0)
throw new ArgumentOutOfRangeException("FuelConsumption < 0" + data.Rows.IndexOf(row));
fuelConsumptionMap._entries.Add(entry);
fuelConsumptionMap._fuelMap.AddPoint(entry.EngineSpeed, entry.Torque, entry.FuelConsumption);
}
catch (Exception e)
{
throw new VectoException(string.Format("Line {0}: {1}", data.Rows.IndexOf(row), e.Message), e);
}
}
}
catch (Exception e)
{
throw new VectoException(string.Format("File {0}: {1}", fileName, e.Message), e);
} }
fuelConsumptionMap._fuelMap.Triangulate();
return fuelConsumptionMap;
} }
public static FuelConsumptionMap ReadFromFile(string fileName) public double GetFuelConsumption(double engineSpeed, double torque)
{ {
return new FuelConsumptionMap(fileName); return _fuelMap.Interpolate(engineSpeed, torque);
} }
} }
} }
...@@ -39,7 +39,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine ...@@ -39,7 +39,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine
public static FullLoadCurve ReadFromFile(string fileName) public static FullLoadCurve ReadFromFile(string fileName)
{ {
var fullLoadCurve = new FullLoadCurve(); var fullLoadCurve = new FullLoadCurve();
var data = VectoCSVReader.Read(fileName); var data = VectoCSVFile.Read(fileName);
fullLoadCurve.entries = new List<FullLoadCurveEntry>(); fullLoadCurve.entries = new List<FullLoadCurveEntry>();
//todo: catch exceptions if value format is wrong. //todo: catch exceptions if value format is wrong.
......
...@@ -23,10 +23,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -23,10 +23,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
{ {
private static class Fields private static class Fields
{ {
public const string Pe = "Pe"; public const string PowerEngine = "Pe";
public const string Padd = "Padd"; public const string PowerAuxilaries = "Padd";
public const string Me = "Me"; public const string Torque = "Me";
public const string n = "n"; public const string EngineSpeed = "n";
} }
/// <summary> /// <summary>
...@@ -52,11 +52,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -52,11 +52,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
/// <summary> /// <summary>
/// Additional power demand (aux) (Optional). /// Additional power demand (aux) (Optional).
/// </summary> /// </summary>
public double Padd { get; set; } public double PowerAuxilaries { get; set; }
public static List<EngineOnlyDrivingCycle> ReadFromFile(string fileName) public static List<EngineOnlyDrivingCycle> ReadFromFile(string fileName)
{ {
var data = VectoCSVReader.Read(fileName); var data = VectoCSVFile.Read(fileName);
var cycles = new List<EngineOnlyDrivingCycle>(); var cycles = new List<EngineOnlyDrivingCycle>();
...@@ -64,15 +64,15 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -64,15 +64,15 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
var cycle = new EngineOnlyDrivingCycle(); var cycle = new EngineOnlyDrivingCycle();
cycle.EngineSpeed = row.GetDouble(Fields.n); cycle.EngineSpeed = row.GetDouble(Fields.EngineSpeed);
if (data.Columns.Contains(Fields.Pe)) if (data.Columns.Contains(Fields.PowerEngine))
cycle.PowerEngine = row.GetDouble(Fields.Pe); cycle.PowerEngine = row.GetDouble(Fields.PowerEngine);
else else
cycle.Torque = row.GetDouble(Fields.Me); cycle.Torque = row.GetDouble(Fields.Torque);
if (data.Columns.Contains(Fields.Padd)) if (data.Columns.Contains(Fields.PowerAuxilaries))
cycle.Padd = row.GetDouble(Fields.Padd); cycle.PowerAuxilaries = row.GetDouble(Fields.PowerAuxilaries);
cycles.Add(cycle); cycles.Add(cycle);
} }
......
using System; using System;
using System.Collections.Generic;
using System.Data; using System.Data;
using System.Diagnostics.Eventing.Reader; using System.Linq;
using System.Reflection; using System.Reflection;
using TUGraz.VectoCore.Utils;
namespace TUGraz.VectoCore.Models.SimulationComponent.Data namespace TUGraz.VectoCore.Models.SimulationComponent.Data
{ {
...@@ -12,25 +12,44 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -12,25 +12,44 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
public ModalResults() public ModalResults()
{ {
foreach (ModalResultField value in Enum.GetValues(typeof(ModalResultField))) foreach (ModalResultField value in Enum.GetValues(typeof(ModalResultField)))
Columns.Add(value.GetName(), value.GetDataType()); {
var col = new DataColumn(value.GetName(), value.GetDataType()) { Caption = value.GetCaption() };
Columns.Add(col);
}
} }
public ModalResults ReadFromFile(string fileName) public static ModalResults ReadFromFile(string fileName)
{ {
var modalResults = new ModalResults(); var modalResults = new ModalResults();
var data = VectoCSVReader.Read(fileName); var data = VectoCSVFile.Read(fileName);
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
var new_row = modalResults.NewRow(); var newRow = modalResults.NewRow();
foreach (DataColumn col in row.Table.Columns) foreach (DataColumn col in row.Table.Columns)
{ {
new_row.SetField(col, row.Field<object>(col)); // In cols FC-AUXc and FC-WHTCc can be a "-"
if ((col.ColumnName == ModalResultField.FCAUXc.GetName() ||
col.ColumnName == ModalResultField.FCWHTCc.GetName()) && row.Field<string>(col) == "-")
continue;
// In col FC can sometimes be a "ERROR"
if (col.ColumnName == ModalResultField.FC.GetName() && row.Field<string>(col) == "ERROR")
continue;
newRow.SetField(col.ColumnName, row.GetDouble(col.ColumnName));
} }
modalResults.Rows.Add(new_row); modalResults.Rows.Add(newRow);
} }
modalResults.Load(data.CreateDataReader());
return modalResults; return modalResults;
} }
public void WriteToFile(string fileName)
{
VectoCSVFile.Write(fileName, this);
}
} }
/// <summary> /// <summary>
...@@ -38,60 +57,239 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -38,60 +57,239 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
/// </summary> /// </summary>
public enum ModalResultField public enum ModalResultField
{ {
[ModalResultField(typeof(double))] time, // [s] Time step. /// <summary>
[ModalResultField(typeof(double))] n, // [1/min] Engine speed. /// Time step [s].
[ModalResultField(typeof(double))] Tq_eng, // [Nm] Engine torque. /// </summary>
[ModalResultField(typeof(double))] Tq_clutch, // [Nm] Torque at clutch (before clutch, engine-side) [ModalResultField(typeof(double), caption: "time [s]")]
[ModalResultField(typeof(double))] Tq_full, // [Nm] Full load torque time,
[ModalResultField(typeof(double))] Tq_drag, // [Nm] Motoring torque
[ModalResultField(typeof(double))] Pe_eng, // [kW] Engine power. /// <summary>
[ModalResultField(typeof(double))] Pe_full, // [kW] Engine full load power. /// Engine speed [1/min].
[ModalResultField(typeof(double))] Pe_drag, // [kW] Engine drag power. /// </summary>
[ModalResultField(typeof(double))] Pe_clutch, // [kW] Engine power at clutch (equals Pe minus loss due to rotational inertia Pa Eng). [ModalResultField(typeof(double), caption: "n [1/min]")]
[ModalResultField(typeof(double), "Pa")] PaEng, // [kW] Rotational acceleration power: Engine. n,
[ModalResultField(typeof(double))] Paux, // [kW] Total auxiliary power demand .
[ModalResultField(typeof(double))] FC, // [g/h] Fuel consumption from FC map.. /// <summary>
[ModalResultField(typeof(double), "FC-AUXc")] FCAUXc, // [g/h] Fuel consumption after Auxiliary-Start/Stop Correction. (Based on FC.) /// [Nm] Engine torque.
[ModalResultField(typeof(double), "FC-WHTCc")] FCWHTCc, // [g/h] Fuel consumption after WHTC Correction. (Based on FC-AUXc.) /// </summary>
[ModalResultField(typeof(double), caption: "Tq_eng [Nm]")]
Tq_eng,
[ModalResultField(typeof(double))] dist, // [km] Travelled distance.
[ModalResultField(typeof(double))] v_act, // [km/h] Actual vehicle speed. /// <summary>
[ModalResultField(typeof(double))] v_targ, // [km/h] Target vehicle speed. /// [Nm] Torque at clutch (before clutch, engine-side)
[ModalResultField(typeof(double))] acc, // [m/s2] Vehicle acceleration. /// </summary>
[ModalResultField(typeof(double))] grad, // [%] Road gradient. [ModalResultField(typeof(double), caption: "Tq_clutch [Nm]")]
[ModalResultField(typeof(double))] Gear, // [-] Gear. "0" = clutch opened / neutral. "0.5" = lock-up clutch is open (AT with torque converter only, see Gearbox) Tq_clutch,
[ModalResultField(typeof(double), "Ploss GB")] PlossGB, // [kW] Gearbox losses.
[ModalResultField(typeof(double), "Ploss Diff")] PlossDiff, // [kW] Losses in differential / axle transmission. /// <summary>
[ModalResultField(typeof(double), "Ploss Retarder")] PlossRetarder, // [kW] Retarder losses. /// [Nm] Full load torque
[ModalResultField(typeof(double), "Pa GB")] PaGB, // [kW] Rotational acceleration power: Gearbox. /// </summary>
[ModalResultField(typeof(double), "Pa Veh")] PaVeh, // [kW] Vehicle acceleration power. [ModalResultField(typeof(double), caption: "Tq_full [Nm]")]
[ModalResultField(typeof(double))] Proll, // [kW] Rolling resistance power demand. Tq_full,
[ModalResultField(typeof(double))] Pair, // [kW] Air resistance power demand.
[ModalResultField(typeof(double))] Pgrad, // [kW] Power demand due to road gradient. /// <summary>
[ModalResultField(typeof(double))] Pwheel, // [kW] Total power demand at wheel = sum of rolling, air, acceleration and road gradient resistance. /// [Nm] Motoring torque
[ModalResultField(typeof(double))] Pbrake, // [kW] Brake power. Drag power is included in Pe. /// </summary>
//[ModalResultField(typeof(double))] Paux_xxx, // [kW] Power demand of Auxiliary with ID xxx. See also Aux Dialog and Driving Cycle. [ModalResultField(typeof(double), caption: "Tq_drag [Nm]")]
[ModalResultField(typeof(double))] TCν, // [-] Torque converter speed ratio Tq_drag,
[ModalResultField(typeof(double), "TCµ")] TCmu, // [-] Torque converter torque ratio
[ModalResultField(typeof(double))] TC_M_Out, // [Nm] Torque converter output torque /// <summary>
[ModalResultField(typeof(double))] TC_n_Out, // [1/min] Torque converter output speed /// [kW] Engine power.
/// </summary>
[ModalResultField(typeof(double), caption: "Pe_eng [kW]")]
Pe_eng,
/// <summary>
/// [kW] Engine full load power.
/// </summary>
[ModalResultField(typeof(double), caption: "Pe_full [kW]")]
Pe_full,
/// <summary>
/// [kW] Engine drag power.
/// </summary>
[ModalResultField(typeof(double), caption: "Pe_drag [kW]")]
Pe_drag,
/// <summary>
/// [kW] Engine power at clutch (equals Pe minus loss due to rotational inertia Pa Eng).
/// </summary>
[ModalResultField(typeof(double), caption: "Pe_clutch [kW]")]
Pe_clutch,
/// <summary>
/// [kW] Rotational acceleration power: Engine.
/// </summary>
[ModalResultField(typeof(double), name: "Pa", caption: "Pa [Eng]")]
PaEng,
/// <summary>
/// [kW] Total auxiliary power demand .
/// </summary>
[ModalResultField(typeof(double), caption: "Paux [kW]")]
Paux,
/// <summary>
/// [g/h] Fuel consumption from FC map..
/// </summary>
[ModalResultField(typeof(double), caption: "FC [g/h]")]
FC,
/// <summary>
/// [g/h] Fuel consumption after Auxiliary-Start/Stop Correction. (Based on FC.)
/// </summary>
[ModalResultField(typeof(double), name: "FC-AUXc", caption: "FC-AUXc [g/h]")]
FCAUXc,
/// <summary>
/// [g/h] Fuel consumption after WHTC Correction. (Based on FC-AUXc.)
/// </summary>
[ModalResultField(typeof(double), name: "FC-WHTCc", caption: "FC-WHTCc [g/h]")]
FCWHTCc,
/// <summary>
/// [km] Travelled distance.
/// </summary>
[ModalResultField(typeof(double))]
dist,
/// <summary>
/// [km/h] Actual vehicle speed.
/// </summary>
[ModalResultField(typeof(double))]
v_act,
/// <summary>
/// [km/h] Target vehicle speed.
/// </summary>
[ModalResultField(typeof(double))]
v_targ,
/// <summary>
/// [m/s2] Vehicle acceleration.
/// </summary>
[ModalResultField(typeof(double))]
acc,
/// <summary>
/// [%] Road gradient.
/// </summary>
[ModalResultField(typeof(double))]
grad,
/// <summary>
/// [-] Gear. "0" = clutch opened / neutral. "0.5" = lock-up clutch is open (AT with torque converter only, see Gearbox)
/// </summary>
[ModalResultField(typeof(double))]
Gear,
/// <summary>
/// [kW] Gearbox losses.
/// </summary>
[ModalResultField(typeof(double), name: "Ploss GB")]
PlossGB,
/// <summary>
/// [kW] Losses in differential / axle transmission.
/// </summary>
[ModalResultField(typeof(double), name: "Ploss Diff")]
PlossDiff,
/// <summary>
/// [kW] Retarder losses.
/// </summary>
[ModalResultField(typeof(double), name: "Ploss Retarder")]
PlossRetarder,
/// <summary>
/// [kW] Rotational acceleration power: Gearbox.
/// </summary>
[ModalResultField(typeof(double), name: "Pa GB")]
PaGB,
/// <summary>
/// [kW] Vehicle acceleration power.
/// </summary>
[ModalResultField(typeof(double), name: "Pa Veh")]
PaVeh,
/// <summary>
/// [kW] Rolling resistance power demand.
/// </summary>
[ModalResultField(typeof(double))]
Proll,
/// <summary>
/// [kW] Air resistance power demand.
/// </summary>
[ModalResultField(typeof(double))]
Pair,
/// <summary>
/// [kW] Power demand due to road gradient.
/// </summary>
[ModalResultField(typeof(double))]
Pgrad,
/// <summary>
/// [kW] Total power demand at wheel = sum of rolling, air, acceleration and road gradient resistance.
/// </summary>
[ModalResultField(typeof(double))]
Pwheel,
/// <summary>
/// [kW] Brake power. Drag power is included in Pe.
/// </summary>
[ModalResultField(typeof(double))]
Pbrake,
/// <summary>
/// [kW] Power demand of Auxiliary with ID xxx. See also Aux Dialog and Driving Cycle.
/// </summary>
[ModalResultField(typeof(double))]
Paux_xxx,
/// <summary>
/// [-] Torque converter speed ratio
/// </summary>
[ModalResultField(typeof(double))]
TCν,
/// <summary>
/// [-] Torque converter torque ratio
/// </summary>
[ModalResultField(typeof(double), name: "TCµ")]
TCmu,
/// <summary>
/// [Nm] Torque converter output torque
/// </summary>
[ModalResultField(typeof(double))]
TC_M_Out,
/// <summary>
/// [1/min] Torque converter output speed
/// </summary>
[ModalResultField(typeof(double))]
TC_n_Out
} }
[AttributeUsage(AttributeTargets.Field)] [AttributeUsage(AttributeTargets.Field)]
class ModalResultFieldAttribute : Attribute class ModalResultFieldAttribute : Attribute
{ {
internal ModalResultFieldAttribute(Type fieldType, string name=null) public Type FieldType { get; private set; }
public string Name { get; private set; }
public string Caption { get; set; }
internal ModalResultFieldAttribute(Type fieldType, string name = null, string caption = null)
{ {
FieldType = fieldType; FieldType = fieldType;
Name = name; Name = name;
Caption = caption;
} }
public Type FieldType { get; private set; }
public string Name { get; private set; }
} }
public static class ModalResultFieldExtensions public static class ModalResultFieldExtensionMethods
{ {
public static Type GetDataType(this ModalResultField field) public static Type GetDataType(this ModalResultField field)
{ {
...@@ -103,6 +301,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data ...@@ -103,6 +301,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
return GetAttr(field).Name ?? field.ToString(); return GetAttr(field).Name ?? field.ToString();
} }
public static string GetCaption(this ModalResultField field)
{
return GetAttr(field).Caption ?? field.GetName() ?? field.ToString();
}
private static ModalResultFieldAttribute GetAttr(ModalResultField field) private static ModalResultFieldAttribute GetAttr(ModalResultField field)
{ {
return (ModalResultFieldAttribute)Attribute.GetCustomAttribute(ForValue(field), typeof(ModalResultFieldAttribute)); return (ModalResultFieldAttribute)Attribute.GetCustomAttribute(ForValue(field), typeof(ModalResultFieldAttribute));
......
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using TUGraz.VectoCore.Exceptions;
namespace TUGraz.VectoCore.Models.SimulationComponent.Data
{
/// <summary>
/// Class for Reading and Writing VECTO CSV Files.
/// </summary>
/// <remarks>
/// The following format applies to all CSV (Comma-separated values) Input Files used in VECTO:
/// List Separator: Comma ","
/// Decimal-Mark: Dot "."
/// Comments: "#" at the beginning of the comment line. Number and position of comment lines is not limited.
/// Header: One header line (not a comment line) at the beginning of the file.
/// All Combinations between max-format and min-format possible. Only "id"-field is used.
/// max: <id> (name) [unit], <id> (name) [unit], ...
/// min: id,id,...
/// </remarks>
public static class VectoCSVFile
{
private const char Separator = ',';
private const char Comment = '#';
/// <summary>
/// Reads a CSV file which is stored in Vecto-CSV-Format.
/// </summary>
/// <param name="fileName"></param>
/// <exception cref="FileIOException"></exception>
/// <returns>A DataTable which represents the CSV File.</returns>
public static DataTable Read(string fileName)
{
try
{
var lines = File.ReadAllLines(fileName);
var header = lines.First();
header = Regex.Replace(header, @"\[.*?\]", "");
header = Regex.Replace(header, @"\(.*?\)", "");
header = header.Replace("<", "");
header = header.Replace(">", "");
// or all in one regex (incl. trim):
// Regex.Replace(header, @"\s*\[.*?\]\s*|\s*\(.*?\)\s*|\s*<|>\s*|\s*(?=,)|(?<=,)\s*", "");
var cols = header.Split(Separator);
var table = new DataTable();
foreach (var col in cols)
table.Columns.Add(col.Trim(), typeof(string));
// skip header! --> begin with index 1
for (int i = 1; i < lines.Length; i++)
{
string line = lines[i];
//todo: do more sophisticated splitting of csv-columns (or use a good csv library!)
if (line.Contains(Comment))
line = line.Substring(0, line.IndexOf(Comment));
var cells = line.Split(Separator);
if (cells.Length != cols.Length)
throw new CSVReadException(string.Format("Line {0}: The number of values is not correct.", i));
try
{
table.Rows.Add(line.Split(Separator));
}
catch (InvalidCastException e)
{
throw new CSVReadException(string.Format("Line {0}: The data format of a value is not correct. {1}", i, e.Message), e);
}
}
return table;
}
catch (Exception e)
{
throw new VectoException(string.Format("File {0}: {1}", fileName, e.Message));
}
}
public static void Write(string fileName, DataTable table)
{
StringBuilder sb = new StringBuilder();
var header = table.Columns.Cast<DataColumn>().Select(col => col.Caption ?? col.ColumnName);
sb.AppendLine(string.Join(", ", header));
foreach (DataRow row in table.Rows)
{
List<string> formattedList = new List<string>();
foreach (var item in row.ItemArray)
{
var formattable = item as IFormattable;
formattedList.Add(formattable != null
? formattable.ToString("", CultureInfo.InvariantCulture)
: item.ToString());
}
var line = string.Join(Separator.ToString(), formattedList);
sb.AppendLine(line);
}
File.WriteAllText(fileName, sb.ToString());
}
}
}
\ No newline at end of file
using System.Data;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace TUGraz.VectoCore.Models.SimulationComponent.Data
{
public static class VectoCSVReader
{
/// <summary>
/// Reads a CSV file which is stored in Vecto-CSV-Format.
/// </summary>
/// <param name="fileName"></param>
/// <returns>A DataTable which represents the CSV File.</returns>
/// <remarks>
/// The following format applies to all CSV (Comma-separated values) Input Files used in VECTO:
/// List Separator: Comma ","
/// Decimal-Mark: Dot "."
/// Comments: "#" at the beginning of the comment line. Number and position of comment lines is not limited.
/// Header: One header line (not a comment line) at the beginning of the file.
/// All Combinations between max-format and min-format possible. Only "id"-field is used.
/// max: <id> (name) [unit], <id> (name) [unit], ...
/// min: id,id,...
/// </remarks>
public static DataTable Read(string fileName)
{
var lines = File.ReadAllLines(fileName);
var header = lines.First();
header = Regex.Replace(header, @"\[.*?\]", "");
header = Regex.Replace(header, @"\(.*?\)", "");
header = header.Replace("<", "");
header = header.Replace(">", "");
var cols = header.Split(',');
var table = new DataTable();
foreach (var col in cols)
table.Columns.Add(col.Trim(), typeof(string));
foreach (string line in lines.Skip(1))
{
//todo: do more sophisticated splitting of csv-columns (or use a good csv library!)
table.Rows.Add(line.Split(','));
}
return table;
}
}
}
\ No newline at end of file
using System;
using System.Data; using System.Data;
using System.Globalization;
using TUGraz.VectoCore.Exceptions;
namespace TUGraz.VectoCore.Utils namespace TUGraz.VectoCore.Utils
{ {
public static class DataRowExtensionMethods public static class DataRowExtensionMethods
{ {
/// <summary>
/// Gets the field of a DataRow as double.
/// </summary>
/// <param name="row"></param>
/// <param name="columnName">The name of the column.</param>
/// <returns>The value of the underlying DataRows column field as double.</returns>
public static double GetDouble(this DataRow row, string columnName) public static double GetDouble(this DataRow row, string columnName)
{ {
return double.Parse(row.Field<string>(columnName)); //todo ArgumentNullException?
try
{
return double.Parse(row.Field<string>(columnName), CultureInfo.InvariantCulture);
}
catch (IndexOutOfRangeException e)
{
throw new VectoException(string.Format("Field {0} was not found in DataRow.", columnName), e);
}
catch (NullReferenceException e)
{
throw new VectoException(string.Format("Field {0} must not be null.", columnName), e);
}
catch (FormatException e)
{
throw new VectoException(string.Format("Field {0} is not in a valid number format: {1}", columnName,
row.Field<string>(columnName)), e);
}
catch (OverflowException e)
{
throw new VectoException(string.Format("Field {0} has a value too high or too low: {1}", columnName,
row.Field<string>(columnName)), e);
}
catch (ArgumentNullException e)
{
throw new VectoException(string.Format("Field {0} contains null which cannot be converted to a number.", columnName), e);
}
catch (Exception e)
{
throw new VectoException(string.Format("Field {0}: {1}", columnName, e.Message), e);
}
} }
} }
} }
\ No newline at end of file
using System;
using System.Linq;
using System.Collections.Generic;
using TUGraz.VectoCore.Exceptions;
namespace TUGraz.VectoCore.Utils
{
class DelauneyMap
{
private readonly List<Point> _points = new List<Point>();
private List<Triangle> _triangles = new List<Triangle>();
public void AddPoint(double x, double y, double z)
{
_points.Add(new Point(x, y, z));
}
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));
// 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);
}
// 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);
// 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);
}
// Remove all triangles sharing a vertex with the supertriangle.
_triangles = triangles.Where(triangle => !triangle.SharesVertexWith(superTriangle)).ToList();
}
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));
if (tr == null)
throw new VectoException("Interpolation failed.");
var plane = new Plane(tr);
return (plane.W - plane.X * x - plane.Y * y) / plane.Z;
}
}
public class Point
{
protected bool Equals(Point other)
{
return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.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);
}
public override int GetHashCode()
{
return unchecked((((X.GetHashCode() * 397) ^ Y.GetHashCode()) * 397) ^ Z.GetHashCode());
}
public override string ToString()
{
return string.Format("Point({0}, {1}, {2})", X, Y, Z);
}
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public Point(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public static Point operator -(Point p1, Point p2)
{
return new Point(p1.X - p2.X, p1.Y - p2.Y, p1.Z - p2.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 Plane(double x, double y, double z, double w)
{
X = x;
Y = y;
Z = z;
W = w;
}
public Plane(Triangle tr)
{
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 override string ToString()
{
return string.Format("Plane({0}, {1}, {2}, {3})", X, Y, Z, W);
}
}
public class Triangle
{
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;
}
public override string ToString()
{
return string.Format("Triangle({0}, {1}, {2})", P1, P2, P3);
}
public bool IsInside(double x, double y, bool exact = true)
{
var p = new Point(x, y, 0);
var v0 = P3 - P1;
var v1 = P2 - P1;
var v2 = p - P1;
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 invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
if (exact)
return u >= 0 && v >= 0 && u + v <= 1;
return (u >= -0.001) && (v >= -0.001) && (u + v <= 1.001);
}
public bool ContainsInCircumcircle(Point p)
{
var p0 = P1 - p;
var p1 = P2 - p;
var p2 = P3 - p;
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 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 result = p0square * det12 + p1square * det20 + p2square * det01;
return result > 0;
}
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 class Edge
{
public Point P1 { get; set; }
public Point P2 { get; set; }
public Edge(Point p1, Point p2)
{
P1 = p1;
P2 = p2;
}
public override string ToString()
{
return string.Format("Edge({0}, {1})", P1, 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 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);
}
}
}
using System;
namespace TUGraz.VectoCore.Utils
{
static class FloatingPointExtensionMethods
{
public const double TOLERANCE = 0.001;
public static bool IsEqual(this double d, double other)
{
return Math.Abs(d - other) > TOLERANCE;
}
public static bool IsSmaller(this double d, double other)
{
return d - other < TOLERANCE;
}
public static bool IsSmallerOrEqual(this double d, double other)
{
return d - other <= TOLERANCE;
}
public static bool IsBigger(this double d, double other)
{
return other.IsSmallerOrEqual(d);
}
public static bool IsBiggerOrEqual(this double d, double other)
{
return other.IsSmaller(d);
}
}
}
\ No newline at end of file
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
<Compile Include="Models\SimulationComponent\Data\CombustionEngineData.cs" /> <Compile Include="Models\SimulationComponent\Data\CombustionEngineData.cs" />
<Compile Include="Models\SimulationComponent\Data\Engine\FuelConsumptionMap.cs" /> <Compile Include="Models\SimulationComponent\Data\Engine\FuelConsumptionMap.cs" />
<Compile Include="Models\SimulationComponent\Data\Engine\FullLoadCurve.cs" /> <Compile Include="Models\SimulationComponent\Data\Engine\FullLoadCurve.cs" />
<Compile Include="Models\SimulationComponent\Data\VectoCSVReader.cs" /> <Compile Include="Models\SimulationComponent\Data\VectoCSVFile.cs" />
<Compile Include="Models\SimulationComponent\ICombustionEngine.cs" /> <Compile Include="Models\SimulationComponent\ICombustionEngine.cs" />
<Compile Include="Models\Connector\Ports\IInPort.cs" /> <Compile Include="Models\Connector\Ports\IInPort.cs" />
<Compile Include="Models\SimulationComponent\IGearbox.cs" /> <Compile Include="Models\SimulationComponent\IGearbox.cs" />
...@@ -86,6 +86,8 @@ ...@@ -86,6 +86,8 @@
<Compile Include="Models\SimulationComponent\VectoSimulationComponent.cs" /> <Compile Include="Models\SimulationComponent\VectoSimulationComponent.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Utils\DataRowExtensionMethods.cs" /> <Compile Include="Utils\DataRowExtensionMethods.cs" />
<Compile Include="Utils\DelauneyMap.cs" />
<Compile Include="Utils\FloatingPointExtensionMethods.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />
......
...@@ -78,7 +78,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent ...@@ -78,7 +78,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent
{ {
var engineData = CombustionEngineData.ReadFromFile(TestContext.DataRow["EngineFile"].ToString()); var engineData = CombustionEngineData.ReadFromFile(TestContext.DataRow["EngineFile"].ToString());
var data = EngineOnlyDrivingCycle.ReadFromFile(TestContext.DataRow["CycleFile"].ToString()); var data = EngineOnlyDrivingCycle.ReadFromFile(TestContext.DataRow["CycleFile"].ToString());
var expectedResults = new ModalResults().ReadFromFile(TestContext.DataRow["ModalResultFile"].ToString()); var expectedResults = ModalResults.ReadFromFile(TestContext.DataRow["ModalResultFile"].ToString());
var engine = new CombustionEngine(engineData); var engine = new CombustionEngine(engineData);
var port = engine.OutShaft(); var port = engine.OutShaft();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment