diff --git a/VectoCore/VectoCore/InputData/Reader/ShiftPolygonReader.cs b/VectoCore/VectoCore/InputData/Reader/ShiftPolygonReader.cs index 72ded465a88707b7aa3c65f27b39386b9212c8c2..c38b34afd9a052c9193f07ec184f20c98e42616f 100644 --- a/VectoCore/VectoCore/InputData/Reader/ShiftPolygonReader.cs +++ b/VectoCore/VectoCore/InputData/Reader/ShiftPolygonReader.cs @@ -88,37 +88,21 @@ namespace TUGraz.VectoCore.InputData.Reader private static List<ShiftPolygon.ShiftPolygonEntry> CreateFromColumnNames(DataTable data, string columnName) { return (from DataRow row in data.Rows - select new ShiftPolygon.ShiftPolygonEntry { - Torque = row.ParseDouble(Fields.Torque).SI<NewtonMeter>(), - AngularSpeed = row.ParseDouble(columnName).RPMtoRad(), - }).ToList(); + select new ShiftPolygon.ShiftPolygonEntry(row.SI<NewtonMeter>(Fields.Torque), + row.ParseDouble(columnName).RPMtoRad())).ToList(); } private static List<ShiftPolygon.ShiftPolygonEntry> CreateFromColumnIndizes(DataTable data, int column) { return (from DataRow row in data.Rows - select - new ShiftPolygon.ShiftPolygonEntry { - Torque = row.ParseDouble(0).SI<NewtonMeter>(), - AngularSpeed = row.ParseDouble(column).RPMtoRad(), - }).ToList(); + select new ShiftPolygon.ShiftPolygonEntry(row.SI<NewtonMeter>(0), row.ParseDouble(column).RPMtoRad())) + .ToList(); } public static class Fields { - /// <summary> - /// [Nm] torque - /// </summary> public const string Torque = "engine torque"; - - /// <summary> - /// [rpm] threshold for upshift - /// </summary> public const string AngularSpeedUp = "upshift rpm"; - - /// <summary> - /// [rpm] threshold for downshift - /// </summary> public const string AngularSpeedDown = "downshift rpm"; } } diff --git a/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs b/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs index 633b2fc76fe3e3a398f9719b29c982ac89ba918e..fb8d082597fc48c6facd9e7043d97200948cd510 100644 --- a/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs +++ b/VectoCore/VectoCore/Models/Declaration/DeclarationData.cs @@ -96,7 +96,6 @@ namespace TUGraz.VectoCore.Models.Declaration return 1; } - public static class Driver { public static class LookAhead @@ -177,6 +176,7 @@ namespace TUGraz.VectoCore.Models.Declaration //public static readonly PerSecond TorqueConverterSpeedLimit = 1600.RPMtoRad(); public static readonly double TorqueConverterSecondGearThreshold = 1.8; + public static readonly Second PowershiftShiftTime = 0.8.SI<Second>(); /// <summary> @@ -233,11 +233,7 @@ namespace TUGraz.VectoCore.Models.Declaration if (gearIdx > 0) { downShift = new[] { p2, downshiftCorr.P1, downshiftCorr.P2 }.Select( - point => new ShiftPolygon.ShiftPolygonEntry() { - AngularSpeed = point.X.SI<PerSecond>(), - Torque = point.Y.SI<NewtonMeter>() - }) - .ToList(); + point => new ShiftPolygon.ShiftPolygonEntry(point.Y.SI<NewtonMeter>(), point.X.SI<PerSecond>())).ToList(); downShift[0].Torque = maxDragTorque; } @@ -258,11 +254,7 @@ namespace TUGraz.VectoCore.Models.Declaration // ReSharper restore InconsistentNaming upShift = IntersectShiftPolygon(new[] { p4, p7, p5 }, new[] { p2p, p6p, p3pExt }) - .Select(point => new ShiftPolygon.ShiftPolygonEntry() { - AngularSpeed = point.X.SI<PerSecond>(), - Torque = point.Y.SI<NewtonMeter>() - }) - .ToList(); + .Select(point => new ShiftPolygon.ShiftPolygonEntry(point.Y.SI<NewtonMeter>(), point.X.SI<PerSecond>())).ToList(); upShift[0].Torque = maxDragTorque; return new ShiftPolygon(downShift, upShift); } @@ -398,12 +390,11 @@ namespace TUGraz.VectoCore.Models.Declaration var p2corr = new Point((maxTorque.Value() - edge.OffsetXY) / edge.SlopeXY, maxTorque.Value()); var downshift = new[] { - new ShiftPolygon.ShiftPolygonEntry { AngularSpeed = DownshiftPRM, Torque = maxDragTorque }, - new ShiftPolygon.ShiftPolygonEntry { AngularSpeed = DownshiftPRM, Torque = maxTorque } + new ShiftPolygon.ShiftPolygonEntry(maxDragTorque, DownshiftPRM), + new ShiftPolygon.ShiftPolygonEntry(maxTorque, DownshiftPRM) }; var upshift = new[] { p0, p1, p2corr }.Select( - pt => - new ShiftPolygon.ShiftPolygonEntry { AngularSpeed = pt.X.SI<PerSecond>(), Torque = pt.Y.SI<NewtonMeter>() }); + pt => new ShiftPolygon.ShiftPolygonEntry(pt.Y.SI<NewtonMeter>(), pt.X.SI<PerSecond>())); return new ShiftPolygon(first ? new List<ShiftPolygon.ShiftPolygonEntry>() : downshift.ToList(), last ? new List<ShiftPolygon.ShiftPolygonEntry>() : upshift.ToList()); @@ -415,11 +406,11 @@ namespace TUGraz.VectoCore.Models.Declaration var data = VectoCSVFile.ReadStream(RessourceHelper.ReadStream(resourceId), source: resourceId); var characteristicTorque = (from DataRow row in data.Rows select - new TorqueConverterEntry() { - SpeedRatio = row.ParseDouble(TorqueConverterDataReader.Fields.SpeedRatio), - Torque = row.ParseDouble(TorqueConverterDataReader.Fields.CharacteristicTorque).SI<NewtonMeter>(), - TorqueRatio = row.ParseDouble(TorqueConverterDataReader.Fields.TorqueRatio) - }).ToArray(); + new TorqueConverterEntry() { + SpeedRatio = row.ParseDouble(TorqueConverterDataReader.Fields.SpeedRatio), + Torque = row.ParseDouble(TorqueConverterDataReader.Fields.CharacteristicTorque).SI<NewtonMeter>(), + TorqueRatio = row.ParseDouble(TorqueConverterDataReader.Fields.TorqueRatio) + }).ToArray(); foreach (var torqueConverterEntry in characteristicTorque) { torqueConverterEntry.SpeedRatio = torqueConverterEntry.SpeedRatio * ratio; torqueConverterEntry.TorqueRatio = torqueConverterEntry.TorqueRatio / ratio; diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/ShiftPolygon.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/ShiftPolygon.cs index 3069a63cae0014d01309ab42e67e4641f9f4dcd4..e093882dbd00e62e485f5c2da0d9af8fddc8b57e 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/ShiftPolygon.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/Gearbox/ShiftPolygon.cs @@ -1,191 +1,198 @@ -/* -* This file is part of VECTO. -* -* Copyright © 2012-2016 European Union -* -* Developed by Graz University of Technology, -* Institute of Internal Combustion Engines and Thermodynamics, -* Institute of Technical Informatics -* -* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved -* by the European Commission - subsequent versions of the EUPL (the "Licence"); -* You may not use VECTO except in compliance with the Licence. -* You may obtain a copy of the Licence at: -* -* https://joinup.ec.europa.eu/community/eupl/og_page/eupl -* -* Unless required by applicable law or agreed to in writing, VECTO -* distributed under the Licence is distributed on an "AS IS" basis, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the Licence for the specific language governing permissions and -* limitations under the Licence. -* -* Authors: -* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology -* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology -* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology -* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology -* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology -* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology -*/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; -using System.Linq; -using TUGraz.VectoCommon.Models; -using TUGraz.VectoCommon.Utils; - -namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox -{ - [CustomValidation(typeof(ShiftPolygon), "ValidateShiftPolygon")] - public class ShiftPolygon : SimulationComponentData - { - private readonly List<ShiftPolygonEntry> _upShiftPolygon; - private readonly List<ShiftPolygonEntry> _downShiftPolygon; - - internal ShiftPolygon(List<ShiftPolygonEntry> downshift, List<ShiftPolygonEntry> upShift) - { - _upShiftPolygon = upShift; - _downShiftPolygon = downshift; - } - - - public ReadOnlyCollection<ShiftPolygonEntry> Upshift - { - get { return _upShiftPolygon.AsReadOnly(); } - } - - public ReadOnlyCollection<ShiftPolygonEntry> Downshift - { - get { return _downShiftPolygon.AsReadOnly(); } - } - - public bool IsBelowDownshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) - { - var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); - if (section.Item2.AngularSpeed < inAngularVelocity) { - return false; - } - return IsLeftOf(inAngularVelocity, inTorque, section); - } - - public bool IsBelowUpshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) - { - var section = Upshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); - if (section.Item2.AngularSpeed < inAngularVelocity) { - return false; - } - return IsLeftOf(inAngularVelocity, inTorque, section); - } - - public bool IsAboveDownshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) - { - var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); - - if (section.Item2.AngularSpeed < inAngularVelocity) { - return true; - } - return IsRightOf(inAngularVelocity, inTorque, section); - } - - public bool IsAboveUpshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) - { - var section = Upshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); - - if (section.Item2.AngularSpeed < inAngularVelocity) { - return true; - } - return IsRightOf(inAngularVelocity, inTorque, section); - } - - /// <summary> - /// Tests if current power request is on the left side of the shiftpolygon segment - /// </summary> - /// <param name="angularSpeed">The angular speed.</param> - /// <param name="torque">The torque.</param> - /// <param name="segment">Edge of the shift polygon</param> - /// <returns><c>true</c> if current power request is on the left side of the shiftpolygon segment; otherwise, <c>false</c>.</returns> - /// <remarks>Computes a simplified cross product for the vectors: from--X, from--to and checks - /// if the z-component is positive (which means that X was on the right side of from--to).</remarks> - protected static bool IsLeftOf(PerSecond angularSpeed, NewtonMeter torque, - Tuple<ShiftPolygonEntry, ShiftPolygonEntry> segment) - { - var abX = segment.Item2.AngularSpeed.Value() - segment.Item1.AngularSpeed.Value(); - var abY = segment.Item2.Torque.Value() - segment.Item1.Torque.Value(); - var acX = angularSpeed.Value() - segment.Item1.AngularSpeed.Value(); - var acY = torque.Value() - segment.Item1.Torque.Value(); - var z = abX * acY - abY * acX; - return z.IsGreater(0); - } - - /// <summary> - /// Tests if current power request is on the left side of the shiftpolygon segment - /// </summary> - /// <param name="angularSpeed">The angular speed.</param> - /// <param name="torque">The torque.</param> - /// <param name="segment">Edge of the shift polygon</param> - /// <returns><c>true</c> if current power request is on the left side of the shiftpolygon segment; otherwise, <c>false</c>.</returns> - /// <remarks>Computes a simplified cross product for the vectors: from--X, from--to and checks - /// if the z-component is negative (which means that X was on the left side of from--to).</remarks> - protected static bool IsRightOf(PerSecond angularSpeed, NewtonMeter torque, - Tuple<ShiftPolygonEntry, ShiftPolygonEntry> segment) - { - var abX = segment.Item2.AngularSpeed.Value() - segment.Item1.AngularSpeed.Value(); - var abY = segment.Item2.Torque.Value() - segment.Item1.Torque.Value(); - var acX = angularSpeed.Value() - segment.Item1.AngularSpeed.Value(); - var acY = torque.Value() - segment.Item1.Torque.Value(); - var z = abX * acY - abY * acX; - return z.IsSmaller(0); - } - - - // ReSharper disable once UnusedMember.Global -- used via validation - public static ValidationResult ValidateShiftPolygon(ShiftPolygon shiftPolygon, ValidationContext validationContext) - { - var validationService = - validationContext.GetService(typeof(VectoValidationModeServiceContainer)) as VectoValidationModeServiceContainer; - var gbxType = validationService != null ? validationService.GearboxType : null; - - if (gbxType == null || gbxType.Value.AutomaticTransmission()) { - return ValidationResult.Success; - } - - return shiftPolygon.Downshift.Pairwise(Tuple.Create) - .Any( - downshiftLine => - shiftPolygon.Upshift.Any(upshiftEntry => IsLeftOf(upshiftEntry.AngularSpeed, upshiftEntry.Torque, downshiftLine))) - ? new ValidationResult("upshift line has to be right of the downshift line!") - : ValidationResult.Success; - } - - - [DebuggerDisplay("{Torque}, {AngularSpeed}")] - public class ShiftPolygonEntry - { - /// <summary> - /// [Nm] engine torque - /// </summary> - public NewtonMeter Torque { get; set; } - - /// <summary> - /// [1/s] angular velocity threshold - /// </summary> - public PerSecond AngularSpeed { get; set; } - } - - public NewtonMeter InterpolateDownshift(PerSecond inAngularVelocity) - { - var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); - - if (section.Item1.AngularSpeed.IsEqual(section.Item2.AngularSpeed)) { - // vertical line - return double.MaxValue.SI<NewtonMeter>(); - } - return VectoMath.Interpolate(section.Item1.AngularSpeed, section.Item2.AngularSpeed, section.Item1.Torque, - section.Item2.Torque, inAngularVelocity); - } - } +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Linq; +using TUGraz.VectoCommon.Models; +using TUGraz.VectoCommon.Utils; + +namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox +{ + [CustomValidation(typeof(ShiftPolygon), "ValidateShiftPolygon")] + public class ShiftPolygon : SimulationComponentData + { + private readonly List<ShiftPolygonEntry> _upShiftPolygon; + private readonly List<ShiftPolygonEntry> _downShiftPolygon; + + internal ShiftPolygon(List<ShiftPolygonEntry> downshift, List<ShiftPolygonEntry> upShift) + { + _upShiftPolygon = upShift; + _downShiftPolygon = downshift; + } + + public ReadOnlyCollection<ShiftPolygonEntry> Upshift + { + get { return _upShiftPolygon.AsReadOnly(); } + } + + public ReadOnlyCollection<ShiftPolygonEntry> Downshift + { + get { return _downShiftPolygon.AsReadOnly(); } + } + + public bool IsBelowDownshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) + { + var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); + if (section.Item2.AngularSpeed < inAngularVelocity) { + return false; + } + return IsLeftOf(inAngularVelocity, inTorque, section); + } + + public bool IsBelowUpshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) + { + var section = Upshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); + if (section.Item2.AngularSpeed < inAngularVelocity) { + return false; + } + return IsLeftOf(inAngularVelocity, inTorque, section); + } + + public bool IsAboveDownshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) + { + var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); + + if (section.Item2.AngularSpeed < inAngularVelocity) { + return true; + } + return IsRightOf(inAngularVelocity, inTorque, section); + } + + public bool IsAboveUpshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) + { + var section = Upshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); + + if (section.Item2.AngularSpeed < inAngularVelocity) { + return true; + } + return IsRightOf(inAngularVelocity, inTorque, section); + } + + /// <summary> + /// Tests if current power request is on the left side of the shiftpolygon segment + /// </summary> + /// <param name="angularSpeed">The angular speed.</param> + /// <param name="torque">The torque.</param> + /// <param name="segment">Edge of the shift polygon</param> + /// <returns><c>true</c> if current power request is on the left side of the shiftpolygon segment; otherwise, <c>false</c>.</returns> + /// <remarks>Computes a simplified cross product for the vectors: from--X, from--to and checks + /// if the z-component is positive (which means that X was on the right side of from--to).</remarks> + public static bool IsLeftOf(PerSecond angularSpeed, NewtonMeter torque, + Tuple<ShiftPolygonEntry, ShiftPolygonEntry> segment) + { + if (segment.Item1.AngularSpeed < angularSpeed && segment.Item2.AngularSpeed < angularSpeed) + return false; + + var abX = segment.Item2.AngularSpeed.Value() - segment.Item1.AngularSpeed.Value(); + var abY = segment.Item2.Torque.Value() - segment.Item1.Torque.Value(); + var acX = angularSpeed.Value() - segment.Item1.AngularSpeed.Value(); + var acY = torque.Value() - segment.Item1.Torque.Value(); + var z = abX * acY - abY * acX; + return z.IsGreater(0); + } + + /// <summary> + /// Tests if current power request is on the left side of the shiftpolygon segment + /// </summary> + /// <param name="angularSpeed">The angular speed.</param> + /// <param name="torque">The torque.</param> + /// <param name="segment">Edge of the shift polygon</param> + /// <returns><c>true</c> if current power request is on the left side of the shiftpolygon segment; otherwise, <c>false</c>.</returns> + /// <remarks>Computes a simplified cross product for the vectors: from--X, from--to and checks + /// if the z-component is negative (which means that X was on the left side of from--to).</remarks> + public static bool IsRightOf(PerSecond angularSpeed, NewtonMeter torque, + Tuple<ShiftPolygonEntry, ShiftPolygonEntry> segment) + { + if (segment.Item1.AngularSpeed > angularSpeed && segment.Item2.AngularSpeed > angularSpeed) + return false; + + var abX = segment.Item2.AngularSpeed.Value() - segment.Item1.AngularSpeed.Value(); + var abY = segment.Item2.Torque.Value() - segment.Item1.Torque.Value(); + var acX = angularSpeed.Value() - segment.Item1.AngularSpeed.Value(); + var acY = torque.Value() - segment.Item1.Torque.Value(); + var z = abX * acY - abY * acX; + return z.IsSmaller(0); + } + + // ReSharper disable once UnusedMember.Global -- used via validation + public static ValidationResult ValidateShiftPolygon(ShiftPolygon shiftPolygon, ValidationContext validationContext) + { + var validationService = + validationContext.GetService(typeof(VectoValidationModeServiceContainer)) as VectoValidationModeServiceContainer; + var gbxType = validationService != null ? validationService.GearboxType : null; + + if (gbxType == null || gbxType.Value.AutomaticTransmission()) { + return ValidationResult.Success; + } + + return shiftPolygon.Downshift.Pairwise(Tuple.Create).Any(downshiftLine => + shiftPolygon.Upshift.Any(upshiftEntry => IsLeftOf(upshiftEntry.AngularSpeed, upshiftEntry.Torque, downshiftLine))) + ? new ValidationResult("upshift line has to be right of the downshift line!") + : ValidationResult.Success; + } + + [DebuggerDisplay("{Torque}, {AngularSpeed}")] + public class ShiftPolygonEntry + { + public ShiftPolygonEntry(NewtonMeter torque, PerSecond angularSpeed) + { + Torque = torque; + AngularSpeed = angularSpeed; + } + + /// <summary> + /// [Nm] engine torque + /// </summary> + public NewtonMeter Torque { get; set; } + + /// <summary> + /// [1/s] angular velocity threshold + /// </summary> + public PerSecond AngularSpeed { get; set; } + } + + public NewtonMeter InterpolateDownshift(PerSecond inAngularVelocity) + { + var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); + + if (section.Item1.AngularSpeed.IsEqual(section.Item2.AngularSpeed)) { + // vertical line + return double.MaxValue.SI<NewtonMeter>(); + } + return VectoMath.Interpolate(section.Item1.AngularSpeed, section.Item2.AngularSpeed, section.Item1.Torque, + section.Item2.Torque, inAngularVelocity); + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCore/Utils/DataTableExtensionMethods.cs b/VectoCore/VectoCore/Utils/DataTableExtensionMethods.cs index 7afba99f94bfa6b3f46ae6e063cecb5c3b2ce5ef..2b83816cf8ea0d0cd52af4fec2f8675a05f73d49 100644 --- a/VectoCore/VectoCore/Utils/DataTableExtensionMethods.cs +++ b/VectoCore/VectoCore/Utils/DataTableExtensionMethods.cs @@ -1,102 +1,113 @@ -/* -* This file is part of VECTO. -* -* Copyright © 2012-2016 European Union -* -* Developed by Graz University of Technology, -* Institute of Internal Combustion Engines and Thermodynamics, -* Institute of Technical Informatics -* -* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved -* by the European Commission - subsequent versions of the EUPL (the "Licence"); -* You may not use VECTO except in compliance with the Licence. -* You may obtain a copy of the Licence at: -* -* https://joinup.ec.europa.eu/community/eupl/og_page/eupl -* -* Unless required by applicable law or agreed to in writing, VECTO -* distributed under the Licence is distributed on an "AS IS" basis, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the Licence for the specific language governing permissions and -* limitations under the Licence. -* -* Authors: -* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology -* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology -* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology -* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology -* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology -* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology -*/ - -using System; -using System.Collections.Generic; -using System.Data; -using System.Globalization; -using TUGraz.VectoCommon.Exceptions; -using TUGraz.VectoCommon.Utils; - -namespace TUGraz.VectoCore.Utils -{ - public static class DataTableExtensionMethods - { - public static double ParseDoubleOrGetDefault(this DataRow row, string columnName, - double defaultValue = default(double)) - { - if (row.Table.Columns.Contains(columnName)) { - double result; - if (double.TryParse(row.Field<string>(columnName), NumberStyles.Any, CultureInfo.InvariantCulture, out result)) { - return result; - } - } - return defaultValue; - } - - public static double ParseDouble(this DataRow row, int columnIndex) - { - return row.ParseDouble(row.Table.Columns[columnIndex]); - } - - public static double ParseDouble(this DataRow row, string columnName) - { - if (!row.Table.Columns.Contains(columnName)) { - throw new KeyNotFoundException(string.Format("Column {0} was not found in DataRow.", columnName)); - } - return row.ParseDouble(row.Table.Columns[columnName]); - } - - public static double ParseDouble(this DataRow row, DataColumn column) - { - try { - return row.Field<string>(column).ToDouble(); - } catch (IndexOutOfRangeException e) { - throw new VectoException(string.Format("Field {0} was not found in DataRow.", column), e); - } catch (NullReferenceException e) { - throw new VectoException(string.Format("Field {0} must not be null.", column), e); - } catch (FormatException e) { - throw new VectoException(string.Format("Field {0} is not in a valid number format: {1}", column, - row.Field<string>(column)), e); - } catch (OverflowException e) { - throw new VectoException(string.Format("Field {0} has a value too high or too low: {1}", column, - row.Field<string>(column)), e); - } catch (ArgumentNullException e) { - throw new VectoException(string.Format("Field {0} contains null which cannot be converted to a number.", column), e); - } catch (Exception e) { - throw new VectoException(string.Format("Field {0}: {1}", column, e.Message), e); - } - } - - public static bool ParseBoolean(this DataRow row, string columnName) - { - if (!row.Table.Columns.Contains(columnName)) { - throw new KeyNotFoundException(string.Format("Column {0} was not found in DataRow.", columnName)); - } - return row.Field<string>(row.Table.Columns[columnName]).ToBoolean(); - } - - public static IEnumerable<T> Values<T>(this DataColumn column) - { - return column.Table.AsEnumerable().Select(r => r.Field<T>(column)); - } - } +/* +* This file is part of VECTO. +* +* Copyright © 2012-2016 European Union +* +* Developed by Graz University of Technology, +* Institute of Internal Combustion Engines and Thermodynamics, +* Institute of Technical Informatics +* +* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved +* by the European Commission - subsequent versions of the EUPL (the "Licence"); +* You may not use VECTO except in compliance with the Licence. +* You may obtain a copy of the Licence at: +* +* https://joinup.ec.europa.eu/community/eupl/og_page/eupl +* +* Unless required by applicable law or agreed to in writing, VECTO +* distributed under the Licence is distributed on an "AS IS" basis, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the Licence for the specific language governing permissions and +* limitations under the Licence. +* +* Authors: +* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology +* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology +* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology +* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology +* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology +* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology +*/ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Globalization; +using TUGraz.VectoCommon.Exceptions; +using TUGraz.VectoCommon.Utils; + +namespace TUGraz.VectoCore.Utils +{ + public static class DataTableExtensionMethods + { + public static double ParseDoubleOrGetDefault(this DataRow row, string columnName, + double defaultValue = default(double)) + { + if (row.Table.Columns.Contains(columnName)) { + double result; + if (double.TryParse(row.Field<string>(columnName), NumberStyles.Any, CultureInfo.InvariantCulture, out result)) { + return result; + } + } + return defaultValue; + } + + public static double ParseDouble(this DataRow row, int columnIndex) + { + return row.ParseDouble(row.Table.Columns[columnIndex]); + } + + public static T SI<T>(this DataRow row, int columnIndex) where T : SIBase<T> + { + return row.ParseDouble(row.Table.Columns[columnIndex]).SI<T>(); + } + + public static T SI<T>(this DataRow row, string columnName) where T : SIBase<T> + { + return row.ParseDouble(columnName).SI<T>(); + } + + public static double ParseDouble(this DataRow row, string columnName) + { + if (!row.Table.Columns.Contains(columnName)) { + throw new KeyNotFoundException(string.Format("Column {0} was not found in DataRow.", columnName)); + } + return row.ParseDouble(row.Table.Columns[columnName]); + } + + public static double ParseDouble(this DataRow row, DataColumn column) + { + try { + return row.Field<string>(column).ToDouble(); + } catch (IndexOutOfRangeException e) { + throw new VectoException(string.Format("Field {0} was not found in DataRow.", column), e); + } catch (NullReferenceException e) { + throw new VectoException(string.Format("Field {0} must not be null.", column), e); + } catch (FormatException e) { + throw new VectoException(string.Format("Field {0} is not in a valid number format: {1}", column, + row.Field<string>(column)), e); + } catch (OverflowException e) { + throw new VectoException(string.Format("Field {0} has a value too high or too low: {1}", column, + row.Field<string>(column)), e); + } catch (ArgumentNullException e) { + throw new VectoException(string.Format("Field {0} contains null which cannot be converted to a number.", column), + e); + } catch (Exception e) { + throw new VectoException(string.Format("Field {0}: {1}", column, e.Message), e); + } + } + + public static bool ParseBoolean(this DataRow row, string columnName) + { + if (!row.Table.Columns.Contains(columnName)) { + throw new KeyNotFoundException(string.Format("Column {0} was not found in DataRow.", columnName)); + } + return row.Field<string>(row.Table.Columns[columnName]).ToBoolean(); + } + + public static IEnumerable<T> Values<T>(this DataColumn column) + { + return column.Table.AsEnumerable().Select(r => r.Field<T>(column)); + } + } } \ No newline at end of file diff --git a/VectoCore/VectoCoreTest/Models/Declaration/ShiftPolygonTest.cs b/VectoCore/VectoCoreTest/Models/Declaration/ShiftPolygonTest.cs index 96344a77f16f41fe558d8abd6244295affd1c6f2..1686fe09d4467ae9610160df0941d4196950dcce 100644 --- a/VectoCore/VectoCoreTest/Models/Declaration/ShiftPolygonTest.cs +++ b/VectoCore/VectoCoreTest/Models/Declaration/ShiftPolygonTest.cs @@ -292,7 +292,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration } [TestCase] - public void CompueShiftPolygonDeclarationTest() + public void ComputeShiftPolygonDeclarationTest() { var engineFile = @"TestData\Components\40t_Long_Haul_Truck.veng"; var gearboxFile = @"TestData\Components\40t_Long_Haul_Truck.vgbx"; @@ -372,9 +372,8 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration Assert.AreEqual(0, shiftPolygons.Last().Upshift.Count); } - [TestCase] - public void CompueShiftPolygonATDeclarationTest() + public void ComputeShiftPolygonATDeclarationTest() { var engineFile = @"TestData\Components\40t_Long_Haul_Truck.veng"; var gearboxFile = @"TestData\Components\40t_Long_Haul_Truck.vgbx"; @@ -423,7 +422,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration } [TestCase] - public void CompueShiftPolygonDeclarationTestConfidentialEngine() + public void ComputeShiftPolygonDeclarationTestConfidentialEngine() { //var engineFldFile = @"E:\QUAM\Downloads\EngineFLD\Map_375c_BB1390_modTUG_R49_375c_BB1386.vfld"; var engineFldFile = @"E:\QUAM\tmp\scania_fullload_shiftpolygon-test.csv"; @@ -439,7 +438,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration var gearboxData = new JSONGearboxDataV6(JSONInputDataFactory.ReadFile(gearboxFile), gearboxFile); - var engineData = new CombustionEngineData() { IdleSpeed = 509.RPMtoRad(), }; @@ -453,7 +451,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration } engineData.FullLoadCurves = fullLoadCurves; - var shiftPolygons = new List<ShiftPolygon>(); var downshiftTransformed = new List<List<Point>>(); var downshiftOrig = new List<List<Point>>(); @@ -508,10 +505,12 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration 0.421, 4.18, 600), TestCase(@"class2_12t_Pmax_low\130kW_Diesel_example.vfld", @"class2_12t_Pmax_low\delivery_12t_example.vgbx", 0.421, 4.18, 600), - TestCase(@"class5_40t_baseline\12L-324kW.vfld", @"class5_40t_baseline\tractor_12gear_example.vgbx", 0.421, 2.64, 600), + TestCase(@"class5_40t_baseline\12L-324kW.vfld", @"class5_40t_baseline\tractor_12gear_example.vgbx", 0.421, 2.64, + 600), TestCase(@"class5_40t_iaxle_long\12L-324kW.vfld", @"class5_40t_iaxle_long\tractor_12gear_example.vgbx", 0.421, 2.31, 600), - TestCase(@"class5_40t_iaxle_short\12L-324kW.vfld", @"class5_40t_iaxle_short\tractor_12gear_example.vgbx", 0.421, 3.71, + TestCase(@"class5_40t_iaxle_short\12L-324kW.vfld", @"class5_40t_iaxle_short\tractor_12gear_example.vgbx", 0.421, + 3.71, 600), TestCase(@"class5_40t_Pmax_high\13-9-L-375kW.vfld", @"class5_40t_Pmax_high\tractor_12gear_example.vgbx", 0.421, 2.64, 600), @@ -540,7 +539,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration } engineData.FullLoadCurves = fullLoadCurves; - var shiftPolygons = new List<ShiftPolygon>(); var downshiftTransformed = new List<List<Point>>(); var upshiftOrig = new List<List<Point>>(); @@ -548,7 +546,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration shiftPolygons.Add( DeclarationData.Gearbox.ComputeShiftPolygon(gearboxData.Type, i, fullLoadCurves[(uint)(i + 1)], gearboxData.Gears, engineData, axlegearRatio, rdyn.SI<Meter>()) - ); + ); List<Point> tmp1, tmp2, tmp3; ComputShiftPolygonPoints(i, fullLoadCurves[(uint)(i + 1)], gearboxData.Gears, engineData, axlegearRatio, rdyn.SI<Meter>(), out tmp1, out tmp2, out tmp3); @@ -627,5 +625,53 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration downshiftTransformed = new[] { p2p, p6p, p3pExt }.ToList(); } + + /// <summary> + /// VECTO-517 Shiftpolygon is considered invalid + /// </summary> + [TestCase] + public void ShiftCurve_ShiftPolygon_Validation_Test() + { + var vgbs = new[] { + "-50,685,1537", + "550,685,1537", + "678,763,1537", + "1080,1008,2092", + "1200,1081,2092", + "1200,1081,2092", + "3000,1081,2092" + }; + + var shiftPolygon = + ShiftPolygonReader.Create( + VectoCSVFile.ReadStream( + InputDataHelper.InputDataAsStream("engine torque,downshift rpm [rpm],upshift rpm [rpm] ", vgbs))); + + var results = shiftPolygon.Validate(ExecutionMode.Engineering, GearboxType.MT, false); + Assert.IsFalse(results.Any(), string.Join("\n", results.Select(r => r.ErrorMessage))); + } + + [ + TestCase(false, 650, 400), + TestCase(true, 400, 500), + TestCase(false, 900, 400), + TestCase(false, 1200, 400), + TestCase(true, 600, 900), + TestCase(false, 1000, 900), + TestCase(false, 1200, 900), + TestCase(true, 300, 1300), + TestCase(true, 900, 1300), + TestCase(false, 1200, 1250), + TestCase(false, 1200, 1600), + ] + public void IsLeftOf_Test(bool result, double speed, double torque) + { + var segment = Tuple.Create( + new ShiftPolygon.ShiftPolygonEntry(550.SI<NewtonMeter>(), 685.RPMtoRad()), + new ShiftPolygon.ShiftPolygonEntry(1200.SI<NewtonMeter>(), 1080.RPMtoRad()) + ); + + Assert.AreEqual(result, ShiftPolygon.IsLeftOf(speed.RPMtoRad(), torque.SI<NewtonMeter>(), segment)); + } } } \ No newline at end of file