Code development platform for open source projects from the European Union institutions :large_blue_circle: EU Login authentication by SMS will be completely phased out by mid-2025. To see alternatives please check here

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

added reading of pwheel and measured driving cycles.

parent ad6934f6
No related branches found
No related tags found
No related merge requests found
......@@ -39,27 +39,21 @@ namespace TUGraz.VectoCore.InputData.Reader
{
var cols = cycleData.Columns.Cast<DataColumn>().Select(c => c.ColumnName).ToArray();
try {
PWheelCycleDataParser.ValidateHeader(cols);
if (PWheelCycleDataParser.ValidateHeader(cols, false)) {
return CycleType.PWheel;
} catch (VectoException) {}
try {
MeasuredSpeedDataParser.ValidateHeader(cols);
}
if (MeasuredSpeedDataParser.ValidateHeader(cols, false)) {
return CycleType.MeasuredSpeed;
} catch (VectoException) {}
try {
EngineOnlyCycleDataParser.ValidateHeader(cols);
}
if (EngineOnlyCycleDataParser.ValidateHeader(cols, false)) {
return CycleType.EngineOnly;
} catch (VectoException) {}
try {
TimeBasedCycleDataParser.ValidateHeader(cols);
}
if (TimeBasedCycleDataParser.ValidateHeader(cols, false)) {
return CycleType.TimeBased;
} catch (VectoException) {}
try {
DistanceBasedCycleDataParser.ValidateHeader(cols);
}
if (DistanceBasedCycleDataParser.ValidateHeader(cols, false)) {
return CycleType.DistanceBased;
} catch (VectoException) {}
}
throw new VectoException("CycleFile Format is unknown.");
}
......@@ -114,6 +108,21 @@ namespace TUGraz.VectoCore.InputData.Reader
}
}
/// <summary>
/// Creates a cycle from a DataTable with automatic determination of Cycle Type.
/// </summary>
/// <param name="data">The cycle data.</param>
/// <param name="name">The name.</param>
/// <returns></returns>
public static DrivingCycleData ReadFromDataTable(DataTable data, string name)
{
if (data == null) {
Logger<DistanceBasedCycleDataParser>().Warn("Invalid data for DrivingCycle -- dataTable is null");
throw new VectoException("Invalid data for DrivingCycle -- dataTable is null");
}
return ReadFromDataTable(data, GetCycleType(data), name);
}
/// <summary>
/// Creates a cycle from a DataTable
/// </summary>
......@@ -123,8 +132,11 @@ namespace TUGraz.VectoCore.InputData.Reader
/// <returns></returns>
public static DrivingCycleData ReadFromDataTable(DataTable data, CycleType type, string name)
{
var parser = GetDataParser(type);
var entries = parser.Parse(data).ToList();
if (data == null) {
Logger<DistanceBasedCycleDataParser>().Warn("Invalid data for DrivingCycle -- dataTable is null");
throw new VectoException("Invalid data for DrivingCycle -- dataTable is null");
}
var entries = GetDataParser(type).Parse(data).ToList();
if (type == CycleType.DistanceBased) {
entries = FilterDrivingCycleEntries(entries);
......@@ -137,6 +149,7 @@ namespace TUGraz.VectoCore.InputData.Reader
return cycle;
}
private static List<DrivingCycleData.DrivingCycleEntry> FilterDrivingCycleEntries(
List<DrivingCycleData.DrivingCycleEntry> entries)
{
......@@ -209,104 +222,56 @@ namespace TUGraz.VectoCore.InputData.Reader
if (first.Distance.IsEqual(second.Distance)) {
return true;
}
var retVal = first.VehicleTargetSpeed == second.VehicleTargetSpeed;
retVal = retVal &&
first.RoadGradient.IsEqual(second.RoadGradient, Constants.SimulationSettings.DrivingCycleRoadGradientTolerance);
retVal = retVal && first.StoppingTime.IsEqual(0) && second.StoppingTime.IsEqual(0);
retVal = retVal && first.AdditionalAuxPowerDemand == second.AdditionalAuxPowerDemand;
retVal = retVal && first.AuxiliarySupplyPower.Count == second.AuxiliarySupplyPower.Count;
foreach (var key in first.AuxiliarySupplyPower.Keys) {
if (!second.AuxiliarySupplyPower.ContainsKey(key)) {
return false;
}
retVal = retVal && first.AuxiliarySupplyPower[key] == second.AuxiliarySupplyPower[key];
if (first.VehicleTargetSpeed != second.VehicleTargetSpeed) {
return false;
}
if (!first.RoadGradient.IsEqual(second.RoadGradient, Constants.SimulationSettings.DrivingCycleRoadGradientTolerance)) {
return false;
}
if (!(first.StoppingTime.IsEqual(0) && second.StoppingTime.IsEqual(0))) {
return false;
}
if (first.AdditionalAuxPowerDemand != second.AdditionalAuxPowerDemand) {
return false;
}
if (first.AuxiliarySupplyPower.Count != second.AuxiliarySupplyPower.Count) {
return false;
}
if (!first.AuxiliarySupplyPower.SequenceEqual(second.AuxiliarySupplyPower)) {
return false;
}
return retVal;
return true;
}
// todo MK-2016-01-19: move fields to resource file
private static class Fields
{
/// <summary>
/// [kW] the power at the wheel.
/// </summary>
public const string PWheel = "Pwheel";
/// <summary>
/// [m] Travelled distance used for distance-based cycles. If t is also defined this column will be ignored.
/// </summary>
public const string Distance = "s";
/// <summary>
/// [s] Used for time-based cycles. If neither this nor the distance s is defined the data will be interpreted as 1Hz.
/// </summary>
public const string Time = "t";
/// <summary>
/// [km/h] Required except for Engine Only Mode calculations.
/// </summary>
public const string VehicleSpeed = "v";
/// <summary>
/// [%] Optional.
/// </summary>
public const string RoadGradient = "grad";
/// <summary>
/// [s] Required for distance-based cycles. Not used in time based cycles. stop defines the time the vehicle spends in
/// stop phases.
/// </summary>
public const string StoppingTime = "stop";
/// <summary>
/// [kW] "Aux_xxx" Supply Power input for each auxiliary defined in the .vecto file , where xxx matches the ID of the
/// corresponding Auxiliary. ID's are not case sensitive and must not contain space or special characters.
/// </summary>
public const string AuxiliarySupplyPower = "Aux_";
/// <summary>
/// [rpm] If n is defined VECTO uses that instead of the calculated engine speed value.
/// </summary>
public const string EngineSpeed = "n";
/// <summary>
/// [-] Gear input. Overwrites the gear shift model.
/// </summary>
public const string Gear = "gear";
/// <summary>
/// [kW] This power input will be directly added to the engine power in addition to possible other auxiliaries. Also
/// used in Engine Only Mode.
/// </summary>
public const string AdditionalAuxPowerDemand = "Padd";
/// <summary>
/// [km/h] Only required if Cross Wind Correction is set to Vair and Beta Input.
/// </summary>
public const string AirSpeedRelativeToVehicle = "vair_res";
/// <summary>
/// [°] Only required if Cross Wind Correction is set to Vair and Beta Input.
/// </summary>
public const string WindYawAngle = "vair_beta";
/// <summary>
/// [kW] Effective engine power at clutch. Only required in Engine Only Mode. Alternatively torque Me can be defined.
/// Use DRAG to define motoring operation.
/// </summary>
public const string EnginePower = "Pe";
/// <summary>
/// [Nm] Effective engine torque at clutch. Only required in Engine Only Mode. Alternatively power Pe can be defined.
/// Use DRAG to define motoring operation.
/// </summary>
public const string EngineTorque = "Me";
}
#region DataParser
public interface ICycleDataParser
private interface ICycleDataParser
{
IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table);
}
......@@ -315,35 +280,31 @@ namespace TUGraz.VectoCore.InputData.Reader
{
public IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table)
{
if (table == null) {
Logger<DistanceBasedCycleDataParser>().Warn("Invalid data for DistanceBasedDrivingCycle -- null");
throw new VectoException("Invalid data for DistanceBasedDrivingCycle -- null");
}
ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray());
return table.Rows.Cast<DataRow>().Select(row => new DrivingCycleData.DrivingCycleEntry {
Distance = row.ParseDouble(Fields.Distance).SI<Meter>(),
VehicleTargetSpeed =
row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(),
VehicleTargetSpeed = row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(),
RoadGradientPercent = row.ParseDoubleOrGetDefault(Fields.RoadGradient),
RoadGradient =
VectoMath.InclinationToAngle(row.ParseDoubleOrGetDefault(Fields.RoadGradient) / 100.0),
StoppingTime =
(row.ParseDouble(Fields.StoppingTime)).SI<Second>(),
AdditionalAuxPowerDemand =
row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
EngineSpeed =
row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(),
RoadGradient = VectoMath.InclinationToAngle(row.ParseDoubleOrGetDefault(Fields.RoadGradient) / 100.0),
StoppingTime = row.ParseDouble(Fields.StoppingTime).SI<Second>(),
AdditionalAuxPowerDemand = row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
EngineSpeed = row.ParseDoubleOrGetDefault(Fields.EngineSpeed).RPMtoRad(),
Gear = row.ParseDoubleOrGetDefault(Fields.Gear),
AirSpeedRelativeToVehicle =
row.ParseDoubleOrGetDefault(Fields.AirSpeedRelativeToVehicle).KMPHtoMeterPerSecond(),
AirSpeedRelativeToVehicle = row.ParseDoubleOrGetDefault(Fields.AirSpeedRelativeToVehicle).KMPHtoMeterPerSecond(),
WindYawAngle = row.ParseDoubleOrGetDefault(Fields.WindYawAngle),
AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row)
});
}
public static void ValidateHeader(string[] header)
public static bool ValidateHeader(string[] header, bool throwExceptions = true)
{
var requiredCols = new[] {
Fields.VehicleSpeed,
Fields.Distance,
Fields.StoppingTime
};
var allowedCols = new[] {
Fields.Distance,
Fields.VehicleSpeed,
......@@ -356,27 +317,33 @@ namespace TUGraz.VectoCore.InputData.Reader
Fields.WindYawAngle
};
foreach (
var col in
header.Where(
col => !(allowedCols.Contains(col) || col.StartsWith(Fields.AuxiliarySupplyPower)))
) {
throw new VectoException("Column '{0}' is not allowed.", col);
}
header = header.Where(c => !c.StartsWith(Fields.AuxiliarySupplyPower)).ToArray();
if (!header.Contains(Fields.VehicleSpeed)) {
throw new VectoException("Column '{0}' is missing.", Fields.VehicleSpeed);
var diff = header.Except(allowedCols).ToList();
if (diff.Any()) {
if (throwExceptions) {
throw new VectoException("Column(s) not allowed: " + string.Join(", ", diff));
}
return false;
}
if (!header.Contains(Fields.Distance)) {
throw new VectoException("Column '{0}' is missing.", Fields.Distance);
diff = requiredCols.Except(header).ToList();
if (diff.Any()) {
if (throwExceptions) {
throw new VectoException("Column(s) missing: " + string.Join(", ", diff));
}
return false;
}
if (header.Contains(Fields.AirSpeedRelativeToVehicle) ^
header.Contains(Fields.WindYawAngle)) {
throw new VectoException("Both Columns '{0}' and '{1}' must be defined, or none of them.",
Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle);
if (throwExceptions) {
throw new VectoException("Both Columns, '{0}' and '{1}', must be defined, or none of them.",
Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle);
}
return false;
}
return true;
}
}
......@@ -388,18 +355,13 @@ namespace TUGraz.VectoCore.InputData.Reader
var entries = table.Rows.Cast<DataRow>().Select((row, index) => new DrivingCycleData.DrivingCycleEntry {
Time = row.ParseDoubleOrGetDefault(Fields.Time, index).SI<Second>(),
VehicleTargetSpeed =
row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(),
VehicleTargetSpeed = row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(),
RoadGradientPercent = row.ParseDoubleOrGetDefault(Fields.RoadGradient),
RoadGradient =
VectoMath.InclinationToAngle(row.ParseDoubleOrGetDefault(Fields.RoadGradient) / 100.0),
AdditionalAuxPowerDemand =
row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
RoadGradient = VectoMath.InclinationToAngle(row.ParseDoubleOrGetDefault(Fields.RoadGradient) / 100.0),
AdditionalAuxPowerDemand = row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
Gear = row.ParseDoubleOrGetDefault(Fields.Gear),
EngineSpeed =
row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(),
AirSpeedRelativeToVehicle =
row.ParseDoubleOrGetDefault(Fields.AirSpeedRelativeToVehicle).KMPHtoMeterPerSecond(),
EngineSpeed = row.ParseDoubleOrGetDefault(Fields.EngineSpeed).RPMtoRad(),
AirSpeedRelativeToVehicle = row.ParseDoubleOrGetDefault(Fields.AirSpeedRelativeToVehicle).KMPHtoMeterPerSecond(),
WindYawAngle = row.ParseDoubleOrGetDefault(Fields.WindYawAngle),
AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row)
}).ToArray();
......@@ -407,7 +369,7 @@ namespace TUGraz.VectoCore.InputData.Reader
return entries;
}
public static void ValidateHeader(string[] header)
public static bool ValidateHeader(string[] header, bool throwExceptions = true)
{
var allowedCols = new[] {
Fields.Time,
......@@ -417,24 +379,41 @@ namespace TUGraz.VectoCore.InputData.Reader
Fields.Gear,
Fields.AdditionalAuxPowerDemand,
Fields.AirSpeedRelativeToVehicle,
Fields.WindYawAngle
Fields.WindYawAngle,
};
var extraColumns =
header.Where(col => !(allowedCols.Contains(col) || col.StartsWith(Fields.AuxiliarySupplyPower))).ToList();
if (extraColumns.Any()) {
throw new VectoException("Columns not allowed: " + string.Join(", ", extraColumns));
var requiredCols = new[] {
Fields.VehicleSpeed,
};
header = header.Where(c => !c.StartsWith(Fields.AuxiliarySupplyPower)).ToArray();
var diff = header.Except(allowedCols).ToList();
if (diff.Any()) {
if (throwExceptions) {
throw new VectoException("Column(s) not allowed: " + string.Join(", ", diff));
}
return false;
}
if (!header.Contains(Fields.VehicleSpeed)) {
throw new VectoException("Column '{0}' is missing.", Fields.VehicleSpeed);
diff = requiredCols.Except(header).ToList();
if (diff.Any()) {
if (throwExceptions) {
throw new VectoException("Column(s) missing: " + string.Join(", ", diff));
}
return false;
}
if (header.Contains(Fields.AirSpeedRelativeToVehicle) ^
header.Contains(Fields.WindYawAngle)) {
throw new VectoException("Either both columns, '{0}' and '{1}', are defined, or none of them.",
Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle);
if (throwExceptions) {
throw new VectoException("Both Columns, '{0}' and '{1}', must be defined, or none of them.",
Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle);
}
return false;
}
return true;
}
}
......@@ -443,22 +422,21 @@ namespace TUGraz.VectoCore.InputData.Reader
public IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table)
{
ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray());
var absTime = 0.SI<Second>();
var absTime = 0.SI<Second>();
foreach (DataRow row in table.Rows) {
var entry = new DrivingCycleData.DrivingCycleEntry {
EngineSpeed =
row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(),
EngineSpeed = row.ParseDoubleOrGetDefault(Fields.EngineSpeed).RPMtoRad(),
AdditionalAuxPowerDemand =
row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row)
};
if (row.Table.Columns.Contains(Fields.EngineTorque)) {
if (row.Field<string>(Fields.EngineTorque).Equals("<DRAG>")) {
entry.Drag = true;
} else {
entry.EngineTorque =
row.ParseDouble(Fields.EngineTorque).SI<NewtonMeter>();
entry.EngineTorque = row.ParseDouble(Fields.EngineTorque).SI<NewtonMeter>();
}
} else {
if (row.Field<string>(Fields.EnginePower).Equals("<DRAG>")) {
......@@ -474,7 +452,7 @@ namespace TUGraz.VectoCore.InputData.Reader
}
}
public static void ValidateHeader(string[] header)
public static bool ValidateHeader(string[] header, bool throwExceptions = true)
{
var allowedCols = new[] {
Fields.EngineTorque,
......@@ -483,23 +461,39 @@ namespace TUGraz.VectoCore.InputData.Reader
Fields.AdditionalAuxPowerDemand
};
foreach (var col in header.Where(col => !allowedCols.Contains(col))) {
throw new VectoException("Column '{0}' is not allowed.", col);
var requiredCols = new[] {
Fields.EngineSpeed
};
var diff = header.Except(allowedCols).ToList();
if (diff.Any()) {
if (throwExceptions) {
throw new VectoException("Column(s) not allowed: " + string.Join(", ", diff));
}
return false;
}
if (!header.Contains(Fields.EngineSpeed)) {
throw new VectoException("Column '{0}' is missing.", Fields.EngineSpeed);
diff = requiredCols.Except(header).ToList();
if (diff.Any()) {
if (throwExceptions) {
throw new VectoException("Column(s) missing: " + string.Join(", ", diff));
}
return false;
}
if (!(header.Contains(Fields.EngineTorque) || header.Contains(Fields.EnginePower))) {
throw new VectoException("Columns missing: Either column '{0}' or column '{1}' must be defined.",
Fields.EngineTorque, Fields.EnginePower);
if (throwExceptions) {
throw new VectoException("Column(s) missing: Either column '{0}' or column '{1}' must be defined.",
Fields.EngineTorque, Fields.EnginePower);
}
return false;
}
if (header.Contains(Fields.EngineTorque) && header.Contains(Fields.EnginePower)) {
Logger<DrivingCycleDataReader>().Warn("Found column '{0}' and column '{1}': only column '{0}' will be used.",
Logger<DrivingCycleDataReader>().Warn("Found column '{0}' and column '{1}': Only column '{0}' will be used.",
Fields.EngineTorque, Fields.EnginePower);
}
return true;
}
}
......@@ -508,47 +502,19 @@ namespace TUGraz.VectoCore.InputData.Reader
public IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table)
{
ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray());
var absTime = 0.SI<Second>();
foreach (DataRow row in table.Rows) {
var entry = new DrivingCycleData.DrivingCycleEntry {
EngineSpeed =
row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(),
AdditionalAuxPowerDemand =
row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row)
};
if (row.Table.Columns.Contains(Fields.EngineTorque)) {
if (row.Field<string>(Fields.EngineTorque).Equals("<DRAG>")) {
entry.Drag = true;
} else {
entry.EngineTorque =
row.ParseDouble(Fields.EngineTorque).SI<NewtonMeter>();
}
} else {
if (row.Field<string>(Fields.EnginePower).Equals("<DRAG>")) {
entry.Drag = true;
} else {
entry.EngineTorque = row.ParseDouble(Fields.EnginePower).SI().Kilo.Watt.Cast<Watt>() / entry.EngineSpeed;
}
}
entry.Time = absTime;
absTime += 1.SI<Second>();
var entries = table.Rows.Cast<DataRow>().Select(row => new DrivingCycleData.DrivingCycleEntry {
Time = row.ParseDouble(Fields.Time).SI<Second>(),
PWheel = row.ParseDouble(Fields.PWheel).SI().Kilo.Watt.Cast<Watt>(),
Gear = row.ParseDouble(Fields.Gear),
EngineSpeed = row.ParseDouble(Fields.EngineSpeed).RPMtoRad(),
AdditionalAuxPowerDemand = row.ParseDouble(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
}).ToArray();
yield return entry;
}
return entries;
}
/// <summary>
/// Checks if the header is a valid PWheel Cycle File Header.
/// </summary>
/// <param name="header">The header.</param>
/// <exception cref="VectoException">
/// Column(s) '{0}' not allowed.
/// or
/// Column(s) '{0}' missing.
/// </exception>
public static void ValidateHeader(string[] header)
public static bool ValidateHeader(string[] header, bool throwExceptions = true)
{
var allowedCols = new[] {
Fields.Time,
......@@ -560,13 +526,20 @@ namespace TUGraz.VectoCore.InputData.Reader
var diff = header.Except(allowedCols).ToList();
if (diff.Any()) {
throw new VectoException("Column(s) '{0}' not allowed.", string.Join(", ", diff));
if (throwExceptions) {
throw new VectoException("Column(s) '{0}' not allowed.", string.Join(", ", diff));
}
return false;
}
diff = allowedCols.Except(header).ToList();
if (diff.Any()) {
throw new VectoException("Column(s) '{0}' missing.", string.Join(", ", diff));
if (throwExceptions) {
throw new VectoException("Column(s) '{0}' missing.", string.Join(", ", diff));
}
return false;
}
return true;
}
}
......@@ -575,47 +548,19 @@ namespace TUGraz.VectoCore.InputData.Reader
public IEnumerable<DrivingCycleData.DrivingCycleEntry> Parse(DataTable table)
{
ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray());
var absTime = 0.SI<Second>();
foreach (DataRow row in table.Rows) {
var entry = new DrivingCycleData.DrivingCycleEntry {
EngineSpeed =
row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(),
AdditionalAuxPowerDemand =
row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row)
};
if (row.Table.Columns.Contains(Fields.EngineTorque)) {
if (row.Field<string>(Fields.EngineTorque).Equals("<DRAG>")) {
entry.Drag = true;
} else {
entry.EngineTorque =
row.ParseDouble(Fields.EngineTorque).SI<NewtonMeter>();
}
} else {
if (row.Field<string>(Fields.EnginePower).Equals("<DRAG>")) {
entry.Drag = true;
} else {
entry.EngineTorque = row.ParseDouble(Fields.EnginePower).SI().Kilo.Watt.Cast<Watt>() / entry.EngineSpeed;
}
}
entry.Time = absTime;
absTime += 1.SI<Second>();
var entries = table.Rows.Cast<DataRow>().Select(row => new DrivingCycleData.DrivingCycleEntry {
Time = row.ParseDouble(Fields.Time).SI<Second>(),
VehicleTargetSpeed = row.ParseDouble(Fields.VehicleSpeed).KMPHtoMeterPerSecond(),
RoadGradient = VectoMath.InclinationToAngle(row.ParseDoubleOrGetDefault(Fields.RoadGradient) / 100.0),
Gear = row.ParseDouble(Fields.Gear),
AdditionalAuxPowerDemand = row.ParseDouble(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(),
}).ToArray();
yield return entry;
}
return entries;
}
/// <summary>
/// Check if the header is a valid Measured Speed Cycle header.
/// </summary>
/// <param name="header">The header.</param>
/// <exception cref="VectoException">
/// Column(s) not allowed.
/// or
/// Column(s) missing.
/// </exception>
public static void ValidateHeader(string[] header)
public static bool ValidateHeader(string[] header, bool throwExceptions = true)
{
var allowedCols = new[] {
Fields.Time,
......@@ -625,15 +570,24 @@ namespace TUGraz.VectoCore.InputData.Reader
Fields.AdditionalAuxPowerDemand
};
var requiredCols = allowedCols;
var diff = header.Except(allowedCols).ToList();
if (diff.Any()) {
throw new VectoException("Column(s) '{0}' not allowed.", string.Join(", ", diff));
if (throwExceptions) {
throw new VectoException("Column(s) '{0}' not allowed.", string.Join(", ", diff));
}
return false;
}
diff = allowedCols.Except(header).ToList();
diff = requiredCols.Except(header).ToList();
if (diff.Any()) {
throw new VectoException("Column(s) '{0}' missing.", string.Join(", ", diff));
if (throwExceptions) {
throw new VectoException("Column(s) '{0}' missing.", string.Join(", ", diff));
}
return false;
}
return true;
}
}
}
......
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using TUGraz.VectoCore.InputData.Reader.DataObjectAdaper;
using TUGraz.VectoCore.Models;
......@@ -30,23 +31,18 @@ namespace TUGraz.VectoCore.InputData.Reader.Impl
var driver = dao.CreateDriverData(InputDataProvider.DriverInputData);
var engineData = dao.CreateEngineData(InputDataProvider.EngineInputData);
foreach (var cycle in InputDataProvider.JobInputData().Cycles) {
var simulationRunData = new VectoRunData {
JobName = InputDataProvider.JobInputData().JobName,
EngineData = engineData,
GearboxData = dao.CreateGearboxData(InputDataProvider.GearboxInputData, engineData),
AxleGearData = dao.CreateAxleGearData(InputDataProvider.AxleGearInputData),
VehicleData = dao.CreateVehicleData(InputDataProvider.VehicleInputData),
DriverData = driver,
Aux = dao.CreateAuxiliaryData(InputDataProvider.AuxiliaryInputData()),
Retarder = dao.CreateRetarderData(InputDataProvider.RetarderInputData),
// TODO: distance or time-based cycle!
Cycle =
DrivingCycleDataReader.ReadFromDataTable(cycle.CycleData, CycleType.DistanceBased, cycle.Name),
IsEngineOnly = InputDataProvider.JobInputData().EngineOnlyMode
};
yield return simulationRunData;
}
return InputDataProvider.JobInputData().Cycles.Select(cycle => new VectoRunData {
JobName = InputDataProvider.JobInputData().JobName,
EngineData = engineData,
GearboxData = dao.CreateGearboxData(InputDataProvider.GearboxInputData, engineData),
AxleGearData = dao.CreateAxleGearData(InputDataProvider.AxleGearInputData),
VehicleData = dao.CreateVehicleData(InputDataProvider.VehicleInputData),
DriverData = driver,
Aux = dao.CreateAuxiliaryData(InputDataProvider.AuxiliaryInputData()),
Retarder = dao.CreateRetarderData(InputDataProvider.RetarderInputData),
Cycle = DrivingCycleDataReader.ReadFromDataTable(cycle.CycleData, cycle.Name),
IsEngineOnly = InputDataProvider.JobInputData().EngineOnlyMode
});
}
}
}
\ No newline at end of file
......@@ -46,28 +46,57 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
return _engineOnly ? BuildEngineOnly(data) : BuildFullPowertrain(data);
}
private VehicleContainer BuildEngineOnly(VectoRunData data)
{
var cycle = new EngineOnlyDrivingCycle(_container, data.Cycle);
var gearbox = new EngineOnlyGearbox(_container);
cycle.InPort().Connect(gearbox.OutPort());
var directAux = new Auxiliary(_container);
directAux.AddDirect(cycle);
gearbox.InPort().Connect(directAux.OutPort());
var engine = new EngineOnlyCombustionEngine(_container, data.EngineData);
directAux.InPort().Connect(engine.OutPort());
return _container;
}
private VehicleContainer BuildFullPowertrain(VectoRunData data)
{
_container.RunData = data;
IDrivingCycle cycle;
switch (data.Cycle.CycleType) {
case CycleType.EngineOnly:
throw new VectoSimulationException("Engine-Only cycle File for full PowerTrain not allowed!");
case CycleType.DistanceBased:
cycle = new DistanceBasedDrivingCycle(_container, data.Cycle);
break;
case CycleType.TimeBased:
cycle = new TimeBasedDrivingCycle(_container, data.Cycle);
break;
//case CycleType.PWheel:
// cycle = new PWheelDrivingCycle(_container, data.Cycle);
// break;
//case CycleType.MeasuredSpeed:
// cycle = new MeasuredSpeedDrivingCycle(_container, data.Cycle);
// break;
default:
throw new VectoSimulationException("Unhandled Cycle Type");
}
// cycle --> driver --> vehicle --> wheels --> axleGear --> retarder --> gearBox
var driver = AddComponent(cycle, new Driver(_container, data.DriverData, new DefaultDriverStrategy()));
var vehicle = AddComponent(driver, new Vehicle(_container, data.VehicleData));
var wheels = AddComponent(vehicle, new Wheels(_container, data.VehicleData.DynamicTyreRadius));
var brakes = AddComponent(wheels, new Brakes(_container));
var tmp = AddComponent(brakes, new AxleGear(_container, data.AxleGearData));
IPowerTrainComponent tmp;
if (data.Cycle.CycleType == CycleType.PWheel) {
tmp = new AxleGear(_container, data.AxleGearData);
//((PWheelDrivingCycle)cycle).InPort().Connect(tmp.OutPort());
} else {
tmp = AddComponent(brakes, new AxleGear(_container, data.AxleGearData));
}
switch (data.Retarder.Type) {
case RetarderData.RetarderType.Primary:
......@@ -120,7 +149,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
return _container;
}
protected IGearbox GetGearbox(VehicleContainer container, GearboxData data)
private IGearbox GetGearbox(VehicleContainer container, GearboxData data)
{
IShiftStrategy strategy;
switch (data.Type) {
......@@ -142,59 +171,39 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
return new Gearbox(container, data, strategy);
}
protected virtual IDriver AddComponent(IDrivingCycle prev, IDriver next)
private static IDriver AddComponent(IDrivingCycleInProvider prev, IDriver next)
{
prev.InPort().Connect(next.OutPort());
return next;
}
protected virtual IVehicle AddComponent(IDriver prev, IVehicle next)
private static IVehicle AddComponent(IDriverDemandInProvider prev, IVehicle next)
{
prev.InPort().Connect(next.OutPort());
return next;
}
protected virtual IWheels AddComponent(IFvInProvider prev, IWheels next)
private static IWheels AddComponent(IFvInProvider prev, IWheels next)
{
prev.InPort().Connect(next.OutPort());
return next;
}
protected virtual IPowerTrainComponent AddComponent(IWheels prev, IPowerTrainComponent next)
private static IPowerTrainComponent AddComponent(ITnInProvider prev, IPowerTrainComponent next)
{
prev.InPort().Connect(next.OutPort());
return next;
}
protected virtual IPowerTrainComponent AddComponent(IPowerTrainComponent prev, IPowerTrainComponent next)
private static IPowerTrainComponent AddComponent(IPowerTrainComponent prev, IPowerTrainComponent next)
{
prev.InPort().Connect(next.OutPort());
return next;
}
protected virtual void AddComponent(IPowerTrainComponent prev, ITnOutProvider next)
private static void AddComponent(ITnInProvider prev, ITnOutProvider next)
{
prev.InPort().Connect(next.OutPort());
}
private VehicleContainer BuildEngineOnly(VectoRunData data)
{
var cycle = new EngineOnlyDrivingCycle(_container, data.Cycle);
var gearbox = new EngineOnlyGearbox(_container);
cycle.InPort().Connect(gearbox.OutPort());
var directAux = new Auxiliary(_container);
directAux.AddDirect(cycle);
gearbox.InPort().Connect(directAux.OutPort());
var engine = new EngineOnlyCombustionEngine(_container, data.EngineData);
directAux.InPort().Connect(engine.OutPort());
return _container;
}
}
}
\ No newline at end of file
......@@ -14,6 +14,7 @@
* limitations under the Licence.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
......@@ -105,10 +106,25 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl
d.Cycle.Name + Constants.FileExtensions.CycleFile, mass, loading));
VectoRun run;
if (data.IsEngineOnly) {
run = new TimeRun(builder.Build(data));
} else {
run = new DistanceRun(builder.Build(data));
switch (data.Cycle.CycleType) {
case CycleType.DistanceBased:
run = new DistanceRun(builder.Build(data));
break;
case CycleType.EngineOnly:
run = new TimeRun(builder.Build(data));
break;
case CycleType.TimeBased:
run = new TimeRun(builder.Build(data));
break;
case CycleType.PWheel:
run = new TimeRun(builder.Build(data));
break;
case CycleType.MeasuredSpeed:
run = new TimeRun(builder.Build(data));
break;
default:
throw new ArgumentOutOfRangeException("CycleType unknown:" + data.Cycle.CycleType);
}
var validationErrors = run.Validate();
......
......@@ -15,11 +15,19 @@
*/
using System.Collections.Generic;
using System.Data;
using TUGraz.VectoCore.Utils;
namespace TUGraz.VectoCore.Models.SimulationComponent.Data
{
public enum CycleType
{
EngineOnly,
TimeBased,
DistanceBased,
PWheel,
MeasuredSpeed
}
public class DrivingCycleData : SimulationComponentData
{
internal DrivingCycleData() {}
......@@ -53,105 +61,82 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data
}
/// <summary>
/// [m] Travelled distance used for distance-based cycles. If "t"
/// is also defined this column will be ignored.
/// Travelled distance used for distance-based cycles. If "t" is also defined this column will be ignored.
/// </summary>
public Meter Distance { get; set; }
/// <summary>
/// [s] Used for time-based cycles. If neither this nor the distance
/// "s" is defined the data will be interpreted as 1Hz.
/// Used for time-based cycles. If neither this nor the distance. "s" is defined the data will be interpreted as 1Hz.
/// </summary>
public Second Time { get; set; }
/// <summary>
/// [m/s] Required except for Engine Only Mode calculations.
/// Required except for Engine Only Mode calculations.
/// </summary>
public MeterPerSecond VehicleTargetSpeed { get; set; }
/// <summary>
/// [rad] Optional.
/// Optional.
/// </summary>
public Radian RoadGradient { get; set; }
/// <summary>
/// [%] Optional.
/// [%] Optional.
/// </summary>
public double RoadGradientPercent { get; set; }
/// <summary>
/// [m] relative altitude of the driving cycle over distance
/// relative altitude of the driving cycle over distance
/// </summary>
public Meter Altitude { get; set; }
/// <summary>
/// [s] Required for distance-based cycles. Not used in time based
/// cycles. "stop" defines the time the vehicle spends in stop phases.
/// Required for distance-based cycles. Not used in time based cycles. "stop" defines the time the vehicle spends in stop phases.
/// </summary>
public Second StoppingTime { get; set; }
/// <summary>
/// [W] Supply Power input for each auxiliary defined in the
/// .vecto file where xxx matches the ID of the corresponding
/// Auxiliary. ID's are not case sensitive and must not contain
/// space or special characters.
/// Supply Power input for each auxiliary defined in the .vecto file where xxx matches the ID of the corresponding
/// Auxiliary. ID's are not case sensitive and must not contain space or special characters.
/// </summary>
public Dictionary<string, Watt> AuxiliarySupplyPower { get; set; }
/// <summary>
/// [rad/s] If "n" is defined VECTO uses that instead of the
/// calculated engine speed value.
/// If "n" is defined VECTO uses that instead of the calculated engine speed value.
/// </summary>
public PerSecond EngineSpeed { get; set; }
/// <summary>
/// [-] Gear input. Overwrites the gear shift model.
/// [-] Gear input. Overwrites the gear shift model.
/// </summary>
public double Gear { get; set; }
/// <summary>
/// [W] This power input will be directly added to the engine
/// power in addition to possible other auxiliaries. Also used in
/// Engine Only Mode.
/// This power input will be directly added to the engine power in addition to possible other auxiliaries. Also used in Engine Only Mode.
/// </summary>
public Watt AdditionalAuxPowerDemand { get; set; }
/// <summary>
/// [m/s] Only required if Cross Wind Correction is set to Vair and Beta Input.
/// Only required if Cross Wind Correction is set to Vair and Beta Input.
/// </summary>
public MeterPerSecond AirSpeedRelativeToVehicle { get; set; }
/// <summary>
/// [°] Only required if Cross Wind Correction is set to Vair and Beta Input.
/// [°] Only required if Cross Wind Correction is set to Vair and Beta Input.
/// </summary>
public double WindYawAngle { get; set; }
/// <summary>
/// [Nm] Effective engine torque at clutch. Only required in
/// Engine Only Mode. Alternatively power "Pe" can be defined.
/// Use "DRAG" to define motoring operation.
/// Effective engine torque at clutch. Only required in Engine Only Mode. Alternatively power "Pe" can be defined. Use "DRAG" to define motoring operation.
/// </summary>
public NewtonMeter EngineTorque { get; set; }
public bool Drag { get; set; }
}
#region DataParser
private interface IDataParser
{
IEnumerable<DrivingCycleEntry> Parse(DataTable table);
/// <summary>
/// Power on the Wheels (only used in PWheel Mode).
/// </summary>
public Watt PWheel { get; set; }
}
#endregion
}
public enum CycleType
{
EngineOnly,
TimeBased,
DistanceBased,
PWheel,
MeasuredSpeed
}
}
\ No newline at end of file
......@@ -27,6 +27,14 @@ namespace TUGraz.VectoCore.Utils
/// </remarks>
public static class SwitchExtension
{
/// <summary>
/// Switches on the type.
/// With ".Case" you can define single alternatives (only the first suitable case will be executed).
/// With ".If" you can define multiple type-conditionals (every suitable if will be executed)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="self">The self.</param>
/// <returns></returns>
[DebuggerHidden]
public static Switch<T> Switch<T>(this T self)
{
......@@ -69,6 +77,21 @@ namespace TUGraz.VectoCore.Utils
return this;
}
/// <summary>
/// Does the action if the type is fullfilled and continues the evaluation.
/// </summary>
/// <typeparam name="TFilter">The type of the filter.</typeparam>
/// <param name="action">The action.</param>
/// <returns></returns>
[DebuggerHidden]
public Switch<T> If<TFilter>(Action<TFilter> action) where TFilter : class
{
if (_value is TFilter) {
action(_value as TFilter);
}
return this;
}
[DebuggerHidden]
public void Default(Action action)
{
......
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TUGraz.VectoCore.InputData.FileIO.JSON;
using TUGraz.VectoCore.Models.Simulation.Impl;
using TUGraz.VectoCore.OutputData;
using TUGraz.VectoCore.OutputData.FileIO;
using TUGraz.VectoCore.Tests.Utils;
namespace TUGraz.VectoCore.Tests.Models.Simulation
{
......@@ -42,6 +47,33 @@ namespace TUGraz.VectoCore.Tests.Models.Simulation
[TestMethod]
public void Pwheel_Output_Modfile_Test()
{
var jobFile = @"TestData\Jobs\Pwheel.vecto";
var fileWriter = new FileOutputWriter(jobFile);
var sumWriter = new SummaryDataContainer(fileWriter);
var jobContainer = new JobContainer(sumWriter);
var inputData = JSONInputDataFactory.ReadJsonJob(jobFile);
var runsFactory = new SimulatorFactory(ExecutionMode.Engineering, inputData, fileWriter);
jobContainer.AddRuns(runsFactory);
jobContainer.Execute();
jobContainer.WaitFinished();
ResultFileHelper.TestSumFile(@"TestData\Results\EngineOnlyCycles\24t Coach.vsum", @"24t Coach.vsum");
ResultFileHelper.TestModFiles(new[] {
@"TestData\Results\EngineOnlyCycles\24t Coach_Engine Only1.vmod",
@"TestData\Results\EngineOnlyCycles\24t Coach_Engine Only2.vmod",
@"TestData\Results\EngineOnlyCycles\24t Coach_Engine Only3.vmod"
}, new[] {
@"TestData\Jobs\24t Coach EngineOnly_Engine Only1.vmod",
@"TestData\Jobs\24t Coach EngineOnly_Engine Only2.vmod",
@"TestData\Jobs\24t Coach EngineOnly_Engine Only3.vmod"
})
;
Assert.Fail("Test not implemented");
}
}
......
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TUGraz.VectoCore.Models.Simulation.Data;
......@@ -9,9 +10,13 @@ using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine;
using TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox;
using TUGraz.VectoCore.Utils;
#pragma warning disable 169
namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData
{
[TestClass]
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
public class CombustionEngineDataValidationTestClass
{
/// <summary>
......@@ -38,7 +43,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData
var data = new CombustionEngineData {
ModelName = "asdf",
Displacement = 5.SI().Cubic.Centi.Meter.Cast<CubicMeter>(),
Displacement = 6374.SI().Cubic.Centi.Meter.Cast<CubicMeter>(),
IdleSpeed = 560.RPMtoRad(),
Inertia = 1.SI<KilogramSquareMeter>(),
WHTCUrban = 1.SI().Gramm.Per.Kilo.Watt.Hour.Cast<KilogramPerWattSecond>(),
......@@ -189,6 +194,11 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData
[Required, ValidateObject] public DeepDataObject parent_sub_object = new DeepDataObject();
#endregion
private void just_to_remove_compiler_warnings()
{
private_parent_field = private_static_parent_field;
}
}
public class DataObject : ParentDataObject
......@@ -272,6 +282,11 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData
[Required, ValidateObject] public DeepDataObject sub_object = new DeepDataObject();
#endregion
private void just_to_remove_compiler_warnings()
{
private_field = private_static_field;
}
}
}
}
\ No newline at end of file
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