diff --git a/VectoCore/Models/Simulation/Data/ModalResult.cs b/VectoCore/Models/Simulation/Data/ModalResult.cs index a8d6ec2f6f81abf3880cd53193b9285829e30e9e..9770721229538911f4bda608a04febccff13c3c4 100644 --- a/VectoCore/Models/Simulation/Data/ModalResult.cs +++ b/VectoCore/Models/Simulation/Data/ModalResult.cs @@ -14,8 +14,8 @@ namespace TUGraz.VectoCore.Models.Simulation.Data { public ModalResults() { - foreach (ModalResultField value in Enum.GetValues(typeof (ModalResultField))) { - var col = new DataColumn(value.GetName(), value.GetDataType()) {Caption = value.GetCaption()}; + foreach (ModalResultField value in Enum.GetValues(typeof(ModalResultField))) { + var col = new DataColumn(value.GetName(), value.GetDataType()) { Caption = value.GetCaption() }; Columns.Add(col); } } @@ -66,188 +66,188 @@ namespace TUGraz.VectoCore.Models.Simulation.Data /// Time step [s]. /// Midpoint of the simulated interval. /// </summary> - [ModalResultField(typeof (double), caption: "time [s]")] time, + [ModalResultField(typeof(double), caption: "time [s]")] time, /// <summary> /// Simulation interval around the current time step. [s] /// </summary> - [ModalResultField(typeof (double), "simulation_interval", "simulation_interval [s]")] simulationInterval, + [ModalResultField(typeof(double), "simulation_interval", "simulation_interval [s]")] simulationInterval, /// <summary> /// Engine speed [1/min]. /// </summary> - [ModalResultField(typeof (double), caption: "n [1/min]")] n, + [ModalResultField(typeof(double), caption: "n [1/min]")] n, /// <summary> /// [Nm] Engine torque. /// </summary> - [ModalResultField(typeof (double), caption: "Tq_eng [Nm]")] Tq_eng, + [ModalResultField(typeof(double), caption: "Tq_eng [Nm]")] Tq_eng, /// <summary> /// [Nm] Torque at clutch (before clutch, engine-side) /// </summary> - [ModalResultField(typeof (double), caption: "Tq_clutch [Nm]")] Tq_clutch, + [ModalResultField(typeof(double), caption: "Tq_clutch [Nm]")] Tq_clutch, /// <summary> /// [Nm] Full load torque /// </summary> - [ModalResultField(typeof (double), caption: "Tq_full [Nm]")] Tq_full, + [ModalResultField(typeof(double), caption: "Tq_full [Nm]")] Tq_full, /// <summary> /// [Nm] Motoring torque /// </summary> - [ModalResultField(typeof (double), caption: "Tq_drag [Nm]")] Tq_drag, + [ModalResultField(typeof(double), caption: "Tq_drag [Nm]")] Tq_drag, /// <summary> /// [kW] Engine power. /// </summary> - [ModalResultField(typeof (double), caption: "Pe_eng [kW]")] Pe_eng, + [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, + [ModalResultField(typeof(double), caption: "Pe_full [kW]")] Pe_full, /// <summary> /// [kW] Engine drag power. /// </summary> - [ModalResultField(typeof (double), caption: "Pe_drag [kW]")] Pe_drag, + [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, + [ModalResultField(typeof(double), caption: "Pe_clutch [kW]")] Pe_clutch, /// <summary> /// [kW] Rotational acceleration power: Engine. /// </summary> - [ModalResultField(typeof (double), "Pa", "Pa [Eng]")] PaEng, + [ModalResultField(typeof(double), "Pa", "Pa [Eng]")] PaEng, /// <summary> /// [kW] Total auxiliary power demand . /// </summary> - [ModalResultField(typeof (double), caption: "Paux [kW]")] Paux, + [ModalResultField(typeof(double), caption: "Paux [kW]")] Paux, /// <summary> /// [g/h] Fuel consumption from FC map.. /// </summary> - [ModalResultField(typeof (double), caption: "FC [g/h]")] FC, + [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), "FC-AUXc", "FC-AUXc [g/h]")] FCAUXc, + [ModalResultField(typeof(double), "FC-AUXc", "FC-AUXc [g/h]")] FCAUXc, /// <summary> /// [g/h] Fuel consumption after WHTC Correction. (Based on FC-AUXc.) /// </summary> - [ModalResultField(typeof (double), "FC-WHTCc", "FC-WHTCc [g/h]")] FCWHTCc, + [ModalResultField(typeof(double), "FC-WHTCc", "FC-WHTCc [g/h]")] FCWHTCc, /// <summary> /// [km] Travelled distance. /// </summary> - [ModalResultField(typeof (double))] dist, + [ModalResultField(typeof(double))] dist, /// <summary> /// [km/h] Actual vehicle speed. /// </summary> - [ModalResultField(typeof (double))] v_act, + [ModalResultField(typeof(double))] v_act, /// <summary> /// [km/h] Target vehicle speed. /// </summary> - [ModalResultField(typeof (double))] v_targ, + [ModalResultField(typeof(double))] v_targ, /// <summary> /// [m/s2] Vehicle acceleration. /// </summary> - [ModalResultField(typeof (double))] acc, + [ModalResultField(typeof(double))] acc, /// <summary> /// [%] Road gradient. /// </summary> - [ModalResultField(typeof (double))] grad, + [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, + [ModalResultField(typeof(double))] Gear, /// <summary> /// [kW] Gearbox losses. /// </summary> - [ModalResultField(typeof (double), "Ploss GB")] PlossGB, + [ModalResultField(typeof(double), "Ploss GB")] PlossGB, /// <summary> /// [kW] Losses in differential / axle transmission. /// </summary> - [ModalResultField(typeof (double), "Ploss Diff")] PlossDiff, + [ModalResultField(typeof(double), "Ploss Diff")] PlossDiff, /// <summary> /// [kW] Retarder losses. /// </summary> - [ModalResultField(typeof (double), "Ploss Retarder")] PlossRetarder, + [ModalResultField(typeof(double), "Ploss Retarder")] PlossRetarder, /// <summary> /// [kW] Rotational acceleration power: Gearbox. /// </summary> - [ModalResultField(typeof (double), "Pa GB")] PaGB, + [ModalResultField(typeof(double), "Pa GB")] PaGB, /// <summary> /// [kW] Vehicle acceleration power. /// </summary> - [ModalResultField(typeof (double), "Pa Veh")] PaVeh, + [ModalResultField(typeof(double), "Pa Veh")] PaVeh, /// <summary> /// [kW] Rolling resistance power demand. /// </summary> - [ModalResultField(typeof (double))] Proll, + [ModalResultField(typeof(double))] Proll, /// <summary> /// [kW] Air resistance power demand. /// </summary> - [ModalResultField(typeof (double))] Pair, + [ModalResultField(typeof(double))] Pair, /// <summary> /// [kW] Power demand due to road gradient. /// </summary> - [ModalResultField(typeof (double))] Pgrad, + [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, + [ModalResultField(typeof(double))] Pwheel, /// <summary> /// [kW] Brake power. Drag power is included in Pe. /// </summary> - [ModalResultField(typeof (double))] Pbrake, + [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, + [ModalResultField(typeof(double))] Paux_xxx, /// <summary> /// [-] Torque converter speed ratio /// </summary> - [ModalResultField(typeof (double))] TCν, + [ModalResultField(typeof(double))] TCν, /// <summary> /// [-] Torque converter torque ratio /// </summary> - [ModalResultField(typeof (double), "TCµ")] TCmu, + [ModalResultField(typeof(double), "TCµ")] TCmu, /// <summary> /// [Nm] Torque converter output torque /// </summary> - [ModalResultField(typeof (double))] TC_M_Out, + [ModalResultField(typeof(double))] TC_M_Out, /// <summary> /// [1/min] Torque converter output speed /// </summary> - [ModalResultField(typeof (double))] TC_n_Out + [ModalResultField(typeof(double))] TC_n_Out } @@ -280,17 +280,17 @@ namespace TUGraz.VectoCore.Models.Simulation.Data public static string GetCaption(this ModalResultField field) { - return GetAttr(field).Caption ?? field.GetName() ?? field.ToString(); + return GetAttr(field).Caption ?? field.GetName(); } private static ModalResultFieldAttribute GetAttr(ModalResultField field) { - return (ModalResultFieldAttribute) Attribute.GetCustomAttribute(ForValue(field), typeof (ModalResultFieldAttribute)); + return (ModalResultFieldAttribute)Attribute.GetCustomAttribute(ForValue(field), typeof(ModalResultFieldAttribute)); } private static MemberInfo ForValue(ModalResultField field) { - return typeof (ModalResultField).GetField(Enum.GetName(typeof (ModalResultField), field)); + return typeof(ModalResultField).GetField(Enum.GetName(typeof(ModalResultField), field)); } } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs b/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs index 7776f65385695791817291001d96432eb6140a5f..5591256eaa03183fad4d4f11786a7325d6577a40 100644 --- a/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs +++ b/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs @@ -69,7 +69,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public SI Displacement { get { return _data.Body.Displacement.SI().Cubic.Centi.Meter.ConvertTo().Cubic.Meter.Value(); } - protected set { _data.Body.Displacement = (double) value.ConvertTo().Cubic.Centi.Meter; } + protected set { _data.Body.Displacement = (double)value.ConvertTo().Cubic.Centi.Meter; } } /// <summary> @@ -78,7 +78,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public PerSecond IdleSpeed { get { return _data.Body.IdleSpeed.RPMtoRad(); } - protected set { _data.Body.IdleSpeed = (double) value.ConvertTo().Rounds.Per.Minute; } + protected set { _data.Body.IdleSpeed = (double)value.ConvertTo().Rounds.Per.Minute; } } /// <summary> @@ -87,7 +87,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public SI Inertia { get { return _data.Body.Inertia.SI().Kilo.Gramm.Square.Meter; } - protected set { _data.Body.Inertia = (double) value.ConvertTo().Kilo.Gramm.Square.Meter; } + protected set { _data.Body.Inertia = (double)value.ConvertTo().Kilo.Gramm.Square.Meter; } } /// <summary> @@ -96,7 +96,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public SI WHTCUrban { get { return _data.Body.WHTCUrban.SI().Gramm.Per.Kilo.Watt.Hour.ConvertTo().Kilo.Gramm.Per.Watt.Second.Value(); } - protected set { _data.Body.WHTCUrban = (double) value.ConvertTo().Gramm.Per.Kilo.Watt.Hour; } + protected set { _data.Body.WHTCUrban = (double)value.ConvertTo().Gramm.Per.Kilo.Watt.Hour; } } /// <summary> @@ -105,16 +105,16 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public SI WHTCRural { get { return _data.Body.WHTCRural.SI().Gramm.Per.Kilo.Watt.Hour.ConvertTo().Kilo.Gramm.Per.Watt.Second.Value(); } - protected set { _data.Body.WHTCRural = (double) value.ConvertTo().Gramm.Per.Kilo.Watt.Hour; } + protected set { _data.Body.WHTCRural = (double)value.ConvertTo().Gramm.Per.Kilo.Watt.Hour; } } /// <summary> - /// [g/Ws] + /// [kg/Ws] /// </summary> public SI WHTCMotorway { get { return _data.Body.WHTCMotorway.SI().Gramm.Per.Kilo.Watt.Hour.ConvertTo().Kilo.Gramm.Per.Watt.Second.Value(); } - protected set { _data.Body.WHTCMotorway = (double) value.ConvertTo().Gramm.Per.Kilo.Watt.Hour; } + protected set { _data.Body.WHTCMotorway = (double)value.ConvertTo().Gramm.Per.Kilo.Watt.Hour; } } [DataMember] @@ -168,7 +168,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public FullLoadCurve GetFullLoadCurve(uint gear) { var curve = _fullLoadCurves.FirstOrDefault(kv => kv.Key.Contains(gear)); - if (curve.Key.Equals(null)) { + if (curve.Key == null) { throw new KeyNotFoundException(string.Format("Gear '{0}' was not found in the FullLoadCurves.", gear)); } @@ -209,7 +209,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data if (obj.GetType() != GetType()) { return false; } - return Equals((DataHeader) obj); + return Equals((DataHeader)obj); } public override int GetHashCode() @@ -303,7 +303,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data if (obj.GetType() != GetType()) { return false; } - return Equals((DataFullLoadCurve) obj); + return Equals((DataFullLoadCurve)obj); } public override int GetHashCode() @@ -344,7 +344,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data if (obj.GetType() != GetType()) { return false; } - return Equals((DataBody) obj); + return Equals((DataBody)obj); } public override int GetHashCode() @@ -385,7 +385,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data if (obj.GetType() != GetType()) { return false; } - return Equals((Data) obj); + return Equals((Data)obj); } public override int GetHashCode() @@ -402,18 +402,18 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { - return sourceType == typeof (string) || base.CanConvertFrom(context, sourceType); + return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); } public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { - return value.GetType() == typeof (string) - ? new Range((string) value) + return value.GetType() == typeof(string) + ? new Range((string)value) : base.ConvertFrom(context, culture, value); } } - [TypeConverter(typeof (RangeConverter))] + [TypeConverter(typeof(RangeConverter))] private class Range { private readonly uint _end; @@ -456,13 +456,13 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data if (obj.GetType() != GetType()) { return false; } - return Equals((Range) obj); + return Equals((Range)obj); } public override int GetHashCode() { unchecked { - return (int) ((_start * 397) ^ _end); + return (int)((_start * 397) ^ _end); } } @@ -490,7 +490,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data if (obj.GetType() != GetType()) { return false; } - return Equals((CombustionEngineData) obj); + return Equals((CombustionEngineData)obj); } public override int GetHashCode() diff --git a/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs b/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs index a1e4032d5a4a9070912bc18e1267e7841a2506b1..73916c84b2246defc79e4930eb74e5193f377cd1 100644 --- a/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs +++ b/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs @@ -8,233 +8,233 @@ using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.SimulationComponent.Data { - public class DrivingCycleData : SimulationComponentData - { - public enum CycleType - { - EngineOnly, - TimeBased, - DistanceBased - } - - public List<DrivingCycleEntry> Entries { get; set; } - - public static DrivingCycleData ReadFromFileEngineOnly(string fileName) - { - return ReadFromFile(fileName, CycleType.EngineOnly); - } - - public static DrivingCycleData ReadFromFileDistanceBased(string fileName) - { - return ReadFromFile(fileName, CycleType.DistanceBased); - } - - public static DrivingCycleData ReadFromFileTimeBased(string fileName) - { - return ReadFromFile(fileName, CycleType.TimeBased); - } - - public static DrivingCycleData ReadFromFile(string fileName, CycleType type) - { - var log = LogManager.GetLogger<DrivingCycleData>(); - - var parser = CreateDataParser(type); - var data = VectoCSVFile.Read(fileName); - var entries = parser.Parse(data).ToList(); - - log.Info(string.Format("Data loaded. Number of Entries: {0}", entries.Count)); - - var cycle = new DrivingCycleData { Entries = entries }; - return cycle; - } - - private static IDataParser CreateDataParser(CycleType type) - { - switch (type) { - case CycleType.EngineOnly: - return new EngineOnlyDataParser(); - case CycleType.TimeBased: - return new TimeBasedDataParser(); + public class DrivingCycleData : SimulationComponentData + { + public enum CycleType + { + EngineOnly, + TimeBased, + DistanceBased + } + + public List<DrivingCycleEntry> Entries { get; set; } + + public static DrivingCycleData ReadFromFileEngineOnly(string fileName) + { + return ReadFromFile(fileName, CycleType.EngineOnly); + } + + public static DrivingCycleData ReadFromFileDistanceBased(string fileName) + { + return ReadFromFile(fileName, CycleType.DistanceBased); + } + + public static DrivingCycleData ReadFromFileTimeBased(string fileName) + { + return ReadFromFile(fileName, CycleType.TimeBased); + } + + public static DrivingCycleData ReadFromFile(string fileName, CycleType type) + { + var log = LogManager.GetLogger<DrivingCycleData>(); + + var parser = CreateDataParser(type); + var data = VectoCSVFile.Read(fileName); + var entries = parser.Parse(data).ToList(); + + log.Info(string.Format("Data loaded. Number of Entries: {0}", entries.Count)); + + var cycle = new DrivingCycleData { Entries = entries }; + return cycle; + } + + private static IDataParser CreateDataParser(CycleType type) + { + switch (type) { + case CycleType.EngineOnly: + return new EngineOnlyDataParser(); + case CycleType.TimeBased: + return new TimeBasedDataParser(); case CycleType.DistanceBased: return new DistanceBasedDataParser(); - default: - throw new ArgumentOutOfRangeException("type"); - } - } - - private static class Fields - { - /// <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> - // todo: implement additional aux as dictionary! - 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"; - } - - public class DrivingCycleEntry - { - /// <summary> - /// [m] Travelled distance used for distance-based cycles. If "t" - /// is also defined this column will be ignored. - /// </summary> - public double 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. - /// </summary> - public double Time { get; set; } - - /// <summary> - /// [m/s] Required except for Engine Only Mode calculations. - /// </summary> - public MeterPerSecond VehicleSpeed { get; set; } - - /// <summary> - /// [%] Optional. - /// </summary> - public double RoadGradient { 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. - /// </summary> - public double 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. - /// </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. - /// </summary> - public PerSecond EngineSpeed { get; set; } - - /// <summary> - /// [-] 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. - /// </summary> - public Watt AdditionalAuxPowerDemand { get; set; } - - /// <summary> - /// [m/s] 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. - /// </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. - /// </summary> - public NewtonMeter EngineTorque { get; set; } - - public bool Drag { get; set; } - } - - #region DataParser - - private interface IDataParser - { - IEnumerable<DrivingCycleEntry> Parse(DataTable table); - } - - /// <summary> - /// Reader for Auxiliary Supply Power. - /// </summary> - private static class AuxSupplyPowerReader - { - /// <summary> - /// [W]. Reads Auxiliary Supply Power (defined by Fields.AuxiliarySupplyPower-Prefix). - /// </summary> - public static Dictionary<string, Watt> Read(DataRow row) - { - return row.Table.Columns.Cast<DataColumn>(). - Where(col => col.ColumnName.StartsWith(Fields.AuxiliarySupplyPower)). - ToDictionary(col => col.ColumnName.Substring(Fields.AuxiliarySupplyPower.Length - 1), - col => row.ParseDouble(col).SI().Kilo.Watt.Cast<Watt>()); - } - } + default: + throw new ArgumentOutOfRangeException("type"); + } + } + + private static class Fields + { + /// <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> + // todo: implement additional aux as dictionary! + 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"; + } + + public class DrivingCycleEntry + { + /// <summary> + /// [m] Travelled distance used for distance-based cycles. If "t" + /// is also defined this column will be ignored. + /// </summary> + public double 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. + /// </summary> + public double Time { get; set; } + + /// <summary> + /// [m/s] Required except for Engine Only Mode calculations. + /// </summary> + public MeterPerSecond VehicleSpeed { get; set; } + + /// <summary> + /// [%] Optional. + /// </summary> + public double RoadGradient { 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. + /// </summary> + public double 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. + /// </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. + /// </summary> + public PerSecond EngineSpeed { get; set; } + + /// <summary> + /// [-] 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. + /// </summary> + public Watt AdditionalAuxPowerDemand { get; set; } + + /// <summary> + /// [m/s] 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. + /// </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. + /// </summary> + public NewtonMeter EngineTorque { get; set; } + + public bool Drag { get; set; } + } + + #region DataParser + + private interface IDataParser + { + IEnumerable<DrivingCycleEntry> Parse(DataTable table); + } + + /// <summary> + /// Reader for Auxiliary Supply Power. + /// </summary> + private static class AuxSupplyPowerReader + { + /// <summary> + /// [W]. Reads Auxiliary Supply Power (defined by Fields.AuxiliarySupplyPower-Prefix). + /// </summary> + public static Dictionary<string, Watt> Read(DataRow row) + { + return row.Table.Columns.Cast<DataColumn>(). + Where(col => col.ColumnName.StartsWith(Fields.AuxiliarySupplyPower)). + ToDictionary(col => col.ColumnName.Substring(Fields.AuxiliarySupplyPower.Length - 1), + col => row.ParseDouble(col).SI().Kilo.Watt.Cast<Watt>()); + } + } private class DistanceBasedDataParser : IDataParser { @@ -297,132 +297,132 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data } } - private class TimeBasedDataParser : IDataParser - { - public IEnumerable<DrivingCycleEntry> Parse(DataTable table) - { - ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray()); - - var entries = table.Rows.Cast<DataRow>().Select((row, index) => new DrivingCycleEntry { - Time = row.ParseDoubleOrGetDefault(Fields.Time, index), - VehicleSpeed = row.ParseDouble(Fields.VehicleSpeed).SI().Kilo.Meter.Per.Hour.Cast<MeterPerSecond>(), - RoadGradient = row.ParseDoubleOrGetDefault(Fields.RoadGradient), - 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) - .SI() - .Kilo.Meter.Per.Hour.Cast<MeterPerSecond>(), - WindYawAngle = row.ParseDoubleOrGetDefault(Fields.WindYawAngle), - AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row) - }).ToArray(); - - return entries; - } - - private static void ValidateHeader(string[] header) - { - var allowedCols = new[] { - Fields.Time, - Fields.VehicleSpeed, - Fields.RoadGradient, - Fields.EngineSpeed, - Fields.Gear, - Fields.AdditionalAuxPowerDemand, - Fields.AirSpeedRelativeToVehicle, - Fields.WindYawAngle - }; - - foreach ( - var col in - header.Where(col => !(allowedCols.Contains(col) || col.StartsWith(Fields.AuxiliarySupplyPower))) - ) { - throw new VectoException(string.Format("Column '{0}' is not allowed.", col)); - } - - if (!header.Contains(Fields.VehicleSpeed)) { - throw new VectoException(string.Format("Column '{0}' is missing.", Fields.VehicleSpeed)); - } - - if (header.Contains(Fields.AirSpeedRelativeToVehicle) ^ header.Contains(Fields.WindYawAngle)) { - throw new VectoException( - string.Format("Both Columns '{0}' and '{1}' must be defined, or none of them.", - Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle)); - } - } - } - - private class EngineOnlyDataParser : IDataParser - { - public IEnumerable<DrivingCycleEntry> Parse(DataTable table) - { - ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray()); - var absTime = new TimeSpan(0, 0, 0); - - foreach (DataRow row in table.Rows) { - var entry = new 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 = - Formulas.PowerToTorque(row.ParseDouble(Fields.EnginePower).SI().Kilo.Watt.Cast<Watt>(), - entry.EngineSpeed); - } - } - entry.Time = absTime.TotalSeconds; - absTime += new TimeSpan(0, 0, 1); - - yield return entry; - } - } - - private static void ValidateHeader(string[] header) - { - var allowedCols = new[] { - Fields.EngineTorque, - Fields.EnginePower, - Fields.EngineSpeed, - Fields.AdditionalAuxPowerDemand - }; - - foreach (var col in header.Where(col => !allowedCols.Contains(col))) { - throw new VectoException(string.Format("Column '{0}' is not allowed.", col)); - } - - if (!header.Contains(Fields.EngineSpeed)) { - throw new VectoException(string.Format("Column '{0}' is missing.", Fields.EngineSpeed)); - } - - if (!(header.Contains(Fields.EngineTorque) || header.Contains(Fields.EnginePower))) { - throw new VectoException( - string.Format("Columns missing: Either column '{0}' or column '{1}' must be defined.", - Fields.EngineTorque, Fields.EnginePower)); - } - - if (header.Contains(Fields.EngineTorque) && header.Contains(Fields.EnginePower)) { - LogManager.GetLogger<DrivingCycleData>() - .WarnFormat("Found column '{0}' and column '{1}': only column '{0}' will be used.", - Fields.EngineTorque, Fields.EnginePower); - } - } - } - - #endregion - } + private class TimeBasedDataParser : IDataParser + { + public IEnumerable<DrivingCycleEntry> Parse(DataTable table) + { + ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray()); + + var entries = table.Rows.Cast<DataRow>().Select((row, index) => new DrivingCycleEntry { + Time = row.ParseDoubleOrGetDefault(Fields.Time, index), + VehicleSpeed = row.ParseDouble(Fields.VehicleSpeed).SI().Kilo.Meter.Per.Hour.Cast<MeterPerSecond>(), + RoadGradient = row.ParseDoubleOrGetDefault(Fields.RoadGradient), + 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) + .SI() + .Kilo.Meter.Per.Hour.Cast<MeterPerSecond>(), + WindYawAngle = row.ParseDoubleOrGetDefault(Fields.WindYawAngle), + AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row) + }).ToArray(); + + return entries; + } + + private static void ValidateHeader(string[] header) + { + var allowedCols = new[] { + Fields.Time, + Fields.VehicleSpeed, + Fields.RoadGradient, + Fields.EngineSpeed, + Fields.Gear, + Fields.AdditionalAuxPowerDemand, + Fields.AirSpeedRelativeToVehicle, + Fields.WindYawAngle + }; + + foreach ( + var col in + header.Where(col => !(allowedCols.Contains(col) || col.StartsWith(Fields.AuxiliarySupplyPower))) + ) { + throw new VectoException(string.Format("Column '{0}' is not allowed.", col)); + } + + if (!header.Contains(Fields.VehicleSpeed)) { + throw new VectoException(string.Format("Column '{0}' is missing.", Fields.VehicleSpeed)); + } + + if (header.Contains(Fields.AirSpeedRelativeToVehicle) ^ header.Contains(Fields.WindYawAngle)) { + throw new VectoException( + string.Format("Both Columns '{0}' and '{1}' must be defined, or none of them.", + Fields.AirSpeedRelativeToVehicle, Fields.WindYawAngle)); + } + } + } + + private class EngineOnlyDataParser : IDataParser + { + public IEnumerable<DrivingCycleEntry> Parse(DataTable table) + { + ValidateHeader(table.Columns.Cast<DataColumn>().Select(col => col.ColumnName).ToArray()); + var absTime = new TimeSpan(0, 0, 0); + + foreach (DataRow row in table.Rows) { + var entry = new 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 = + Formulas.PowerToTorque(row.ParseDouble(Fields.EnginePower).SI().Kilo.Watt.Cast<Watt>(), + entry.EngineSpeed); + } + } + entry.Time = absTime.TotalSeconds; + absTime += new TimeSpan(0, 0, 1); + + yield return entry; + } + } + + private static void ValidateHeader(string[] header) + { + var allowedCols = new[] { + Fields.EngineTorque, + Fields.EnginePower, + Fields.EngineSpeed, + Fields.AdditionalAuxPowerDemand + }; + + foreach (var col in header.Where(col => !allowedCols.Contains(col))) { + throw new VectoException(string.Format("Column '{0}' is not allowed.", col)); + } + + if (!header.Contains(Fields.EngineSpeed)) { + throw new VectoException(string.Format("Column '{0}' is missing.", Fields.EngineSpeed)); + } + + if (!(header.Contains(Fields.EngineTorque) || header.Contains(Fields.EnginePower))) { + throw new VectoException( + string.Format("Columns missing: Either column '{0}' or column '{1}' must be defined.", + Fields.EngineTorque, Fields.EnginePower)); + } + + if (header.Contains(Fields.EngineTorque) && header.Contains(Fields.EnginePower)) { + LogManager.GetLogger<DrivingCycleData>() + .WarnFormat("Found column '{0}' and column '{1}': only column '{0}' will be used.", + Fields.EngineTorque, Fields.EnginePower); + } + } + } + + #endregion + } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs b/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs index acbcdf1f4e762ed500a13644d4e45cb9e8ffc928..c4397b22f5f11771bec01d5b6ff7b41d0246bf75 100644 --- a/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs +++ b/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs @@ -90,9 +90,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine public NewtonMeter FullLoadStationaryTorque(PerSecond angularVelocity) { var idx = FindIndex(angularVelocity); - return VectoMath.Interpolate((double) _entries[idx - 1].EngineSpeed, (double) _entries[idx].EngineSpeed, - (double) _entries[idx - 1].TorqueFullLoad, (double) _entries[idx].TorqueFullLoad, - (double) angularVelocity).SI<NewtonMeter>(); + return VectoMath.Interpolate((double)_entries[idx - 1].EngineSpeed, (double)_entries[idx].EngineSpeed, + (double)_entries[idx - 1].TorqueFullLoad, (double)_entries[idx].TorqueFullLoad, + (double)angularVelocity).SI<NewtonMeter>(); } /// <summary> @@ -113,9 +113,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine public NewtonMeter DragLoadStationaryTorque(PerSecond angularVelocity) { var idx = FindIndex(angularVelocity); - return VectoMath.Interpolate((double) _entries[idx - 1].EngineSpeed, (double) _entries[idx].EngineSpeed, - (double) _entries[idx - 1].TorqueDrag, (double) _entries[idx].TorqueDrag, - (double) angularVelocity).SI<NewtonMeter>(); + return VectoMath.Interpolate((double)_entries[idx - 1].EngineSpeed, (double)_entries[idx].EngineSpeed, + (double)_entries[idx - 1].TorqueDrag, (double)_entries[idx].TorqueDrag, + (double)angularVelocity).SI<NewtonMeter>(); } /// <summary> @@ -155,8 +155,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine /// <returns>index</returns> protected int FindIndex(PerSecond angularVelocity) { - Contract.Requires(angularVelocity.HasEqualUnit(new SI().Radian.Per.Second)); - int idx; if (angularVelocity < _entries[0].EngineSpeed) { Log.ErrorFormat("requested rpm below minimum rpm in FLD curve - extrapolating. n: {0}, rpm_min: {1}", @@ -235,7 +233,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine if (ReferenceEquals(this, obj)) { return true; } - return obj.GetType() == GetType() && Equals((FullLoadCurveEntry) obj); + return obj.GetType() == GetType() && Equals((FullLoadCurveEntry)obj); } public override int GetHashCode() @@ -265,7 +263,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine if (ReferenceEquals(this, obj)) { return true; } - return obj.GetType() == GetType() && Equals((FullLoadCurve) obj); + return obj.GetType() == GetType() && Equals((FullLoadCurve)obj); } public override int GetHashCode() @@ -306,7 +304,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine public PerSecond RatedSpeed() { - var max = new Tuple<PerSecond, Watt>(new PerSecond(), new Watt()); + var max = new Tuple<PerSecond, Watt>(0.SI<PerSecond>(), 0.SI<Watt>()); for (var idx = 1; idx < _entries.Count; idx++) { var currentMax = FindMaxPower(_entries[idx - 1], _entries[idx]); if (currentMax.Item2 > max.Item2) { diff --git a/VectoCore/Utils/DelauneyMap.cs b/VectoCore/Utils/DelauneyMap.cs index 53b38e28420231e494fe92a5a35d47bdd8a5b3ef..d759555529a17fc5f4620469bafbaabd93a11d4e 100644 --- a/VectoCore/Utils/DelauneyMap.cs +++ b/VectoCore/Utils/DelauneyMap.cs @@ -90,9 +90,9 @@ namespace TUGraz.VectoCore.Utils Z = z; } - public double X { get; set; } - public double Y { get; set; } - public double Z { get; set; } + public double X; + public double Y; + public double Z; public static Point operator -(Point p1, Point p2) { @@ -123,7 +123,7 @@ namespace TUGraz.VectoCore.Utils if (ReferenceEquals(this, obj)) { return true; } - return obj.GetType() == GetType() && Equals((Point) obj); + return obj.GetType() == GetType() && Equals((Point)obj); } public override int GetHashCode() @@ -164,10 +164,10 @@ namespace TUGraz.VectoCore.Utils W = tr.P1.X * cross.X + tr.P1.Y * cross.Y + tr.P1.Z * cross.Z; } - public double X { get; set; } - public double Y { get; set; } - public double Z { get; set; } - public double W { get; set; } + public double X; + public double Y; + public double Z; + public double W; public override string ToString() { @@ -184,9 +184,9 @@ namespace TUGraz.VectoCore.Utils P3 = p3; } - public Point P1 { get; set; } - public Point P2 { get; set; } - public Point P3 { get; set; } + public Point P1; + public Point P2; + public Point P3; public bool IsInside(double x, double y, bool exact = true) { @@ -284,7 +284,7 @@ namespace TUGraz.VectoCore.Utils if (obj.GetType() != GetType()) { return false; } - return Equals((Triangle) obj); + return Equals((Triangle)obj); } public override int GetHashCode() @@ -308,8 +308,8 @@ namespace TUGraz.VectoCore.Utils P2 = p2; } - public Point P1 { get; set; } - public Point P2 { get; set; } + public Point P1; + public Point P2; public override string ToString() { @@ -333,7 +333,7 @@ namespace TUGraz.VectoCore.Utils if (ReferenceEquals(this, obj)) { return true; } - return obj.GetType() == GetType() && Equals((Edge) obj); + return obj.GetType() == GetType() && Equals((Edge)obj); } public override int GetHashCode() @@ -362,7 +362,7 @@ namespace TUGraz.VectoCore.Utils if (obj.GetType() != GetType()) { return false; } - return Equals((DelauneyMap) obj); + return Equals((DelauneyMap)obj); } public override int GetHashCode() diff --git a/VectoCore/Utils/DoubleExtensionMethods.cs b/VectoCore/Utils/DoubleExtensionMethods.cs index 1daa25650ce682602be33a5de5f4a6afaad90726..75533dc59311a66c42ee2aa6a100813dbdcc0e73 100644 --- a/VectoCore/Utils/DoubleExtensionMethods.cs +++ b/VectoCore/Utils/DoubleExtensionMethods.cs @@ -52,14 +52,14 @@ namespace TUGraz.VectoCore.Utils /// </summary> public static SI SI(this double d) { - return (SI) d; + return (SI)d; } /// <summary> /// Gets the special SI class of the number. /// </summary> - public static T SI<T>(this double d) where T : SIBase<T>, new() + public static T SI<T>(this double d) where T : SIBase<T> { return SIBase<T>.Create(d); } diff --git a/VectoCore/Utils/IntExtensionMethods.cs b/VectoCore/Utils/IntExtensionMethods.cs index 0075574fbf4b245c924c328da76a9b59cec7f012..2381c3bdd317947adfe8422aa8ad22974398eeef 100644 --- a/VectoCore/Utils/IntExtensionMethods.cs +++ b/VectoCore/Utils/IntExtensionMethods.cs @@ -22,7 +22,7 @@ namespace TUGraz.VectoCore.Utils /// <returns></returns> public static SI SI(this int d) { - return (SI) d; + return (SI)d; } /// <summary> @@ -30,7 +30,7 @@ namespace TUGraz.VectoCore.Utils /// </summary> /// <param name="d"></param> /// <returns></returns> - public static T SI<T>(this int d) where T : SIBase<T>, new() + public static T SI<T>(this int d) where T : SIBase<T> { return SIBase<T>.Create(d); } diff --git a/VectoCore/Utils/SI.cs b/VectoCore/Utils/SI.cs index 82a168b371c360553dd1345147f699c56ad7568d..799e77ac12bf7f7babc6f42e1b0dd4acef98100e 100644 --- a/VectoCore/Utils/SI.cs +++ b/VectoCore/Utils/SI.cs @@ -4,27 +4,56 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.Serialization; +using Newtonsoft.Json; using TUGraz.VectoCore.Exceptions; namespace TUGraz.VectoCore.Utils { - public class MeterPerSecond : SIBase<MeterPerSecond> + public class Newton : SIBase<Newton> + { + static Newton() + { + Constructors.Add(typeof(Newton), val => new Newton(val)); + } + + [JsonConstructor] + private Newton(double val) : base(new SI(val).Newton) {} + } + + public class Radian : SIBase<Radian> { - public MeterPerSecond() : this(0) {} - protected MeterPerSecond(double val) : base(val, new SI().Meter.Per.Second) {} + static Radian() + { + Constructors.Add(typeof(Radian), val => new Radian(val)); + } + + [JsonConstructor] + private Radian(double val) : base(new SI(val).Radian) {} } + public class Second : SIBase<Second> { - public Second() : this(0) {} - protected Second(double val) : base(val, new SI().Second) {} + static Second() + { + Constructors.Add(typeof(Second), val => new Second(val)); + } + + [JsonConstructor] + private Second(double val) : base(new SI(val).Second) {} } public class Watt : SIBase<Watt> { - public Watt() : this(0) {} - protected Watt(double val) : base(val, new SI().Watt) {} + static Watt() + { + Constructors.Add(typeof(Watt), val => new Watt(val)); + } + + [JsonConstructor] + private Watt(double val) : base(new SI(val).Watt) {} public static PerSecond operator /(Watt watt, NewtonMeter newtonMeter) { @@ -39,33 +68,48 @@ namespace TUGraz.VectoCore.Utils public class PerSecond : SIBase<PerSecond> { - public PerSecond() : this(0) {} - protected PerSecond(double val) : base(val, new SI().Radian.Per.Second) {} + static PerSecond() + { + Constructors.Add(typeof(PerSecond), val => new PerSecond(val)); + } + + [JsonConstructor] + private PerSecond(double val) : base(new SI(val).Per.Second) {} } - public class RoundsPerMinute : SIBase<RoundsPerMinute> + public class MeterPerSecond : SIBase<MeterPerSecond> { - public RoundsPerMinute() : this(0) {} - protected RoundsPerMinute(double val) : base(val, new SI().Rounds.Per.Minute) {} + static MeterPerSecond() + { + Constructors.Add(typeof(MeterPerSecond), val => new MeterPerSecond(val)); + } + + [JsonConstructor] + private MeterPerSecond(double val) : base(new SI(val).Meter.Per.Second) {} } - public class Newton : SIBase<Newton> + public class RoundsPerMinute : SIBase<RoundsPerMinute> { - public Newton() : this(0) {} - protected Newton(double val) : base(val, new SI().Newton) {} - } + static RoundsPerMinute() + { + Constructors.Add(typeof(RoundsPerMinute), val => new RoundsPerMinute(val)); + } - public class Radian : SIBase<Radian> - { - public Radian() : this(0) {} - protected Radian(double val) : base(val, new SI().Radian) {} + [JsonConstructor] + private RoundsPerMinute(double val) : base(new SI(val).Rounds.Per.Minute) {} } + public class NewtonMeter : SIBase<NewtonMeter> { - public NewtonMeter() : this(0) {} - protected NewtonMeter(double val) : base(val, new SI().Newton.Meter) {} + static NewtonMeter() + { + Constructors.Add(typeof(NewtonMeter), val => new NewtonMeter(val)); + } + + [JsonConstructor] + private NewtonMeter(double val) : base(new SI(val).Newton.Meter) {} public static Watt operator *(NewtonMeter newtonMeter, PerSecond perSecond) { @@ -83,17 +127,23 @@ namespace TUGraz.VectoCore.Utils } } - - public abstract class SIBase<T> : SI where T : SIBase<T>, new() + public abstract class SIBase<T> : SI where T : SIBase<T> { + protected static Dictionary<Type, Func<double, T>> Constructors = + new Dictionary<Type, Func<double, T>>(); + public static T Create(double val) { - return new T { Val = val }; + RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); + return Constructors[typeof(T)](val); } - protected SIBase() {} - protected SIBase(double val) : base(val) {} - protected SIBase(double val, SI unit) : base(val, unit) {} + protected SIBase(Type type, Func<double, T> constructor) + { + Constructors[type] = constructor; + } + + protected SIBase(SI si) : base(si) {} #region Operators @@ -181,59 +231,20 @@ namespace TUGraz.VectoCore.Utils [DataMember] protected readonly Unit[] Numerator; [DataMember] protected readonly bool Reciproc; [DataMember] protected readonly bool Reverse; - [DataMember] protected double Val; + [DataMember] protected readonly double Val; [SuppressMessage("ReSharper", "InconsistentNaming")] protected enum Unit { - /// <summary> - /// kilo - /// </summary> k, - - /// <summary> - /// seconds - /// </summary> s, - - /// <summary> - /// meter - /// </summary> m, - - /// <summary> - /// gramm - /// </summary> g, - - /// <summary> - /// Watt - /// </summary> W, - - /// <summary> - /// Newton - /// </summary> N, - - /// <summary> - /// % - /// </summary> Percent, - - /// <summary> - /// minutes - /// </summary> min, - - /// <summary> - /// centi - /// </summary> c, - - /// <summary> - /// Hour - /// </summary> h } @@ -356,7 +367,7 @@ namespace TUGraz.VectoCore.Utils /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> - public T Cast<T>() where T : SIBase<T>, new() + public T Cast<T>() where T : SIBase<T> { var t = SIBase<T>.Create(Val); if (!HasEqualUnit(t)) { @@ -571,6 +582,8 @@ namespace TUGraz.VectoCore.Utils public static SI operator +(SI si1, SI si2) { + Contract.Requires(si1 != null); + Contract.Requires(si2 != null); if (!si1.HasEqualUnit(si2)) { throw new VectoException( string.Format("Operator '+' can only operate on SI Objects with the same unit. Got: {0} + {1}", si1, si2)); @@ -581,6 +594,8 @@ namespace TUGraz.VectoCore.Utils public static SI operator -(SI si1, SI si2) { + Contract.Requires(si1 != null); + Contract.Requires(si2 != null); if (!si1.HasEqualUnit(si2)) { throw new VectoException( string.Format("Operator '-' can only operate on SI Objects with the same unit. Got: {0} + {1}", si1, si2)); @@ -590,6 +605,8 @@ namespace TUGraz.VectoCore.Utils public static SI operator *(SI si1, SI si2) { + Contract.Requires(si1 != null); + Contract.Requires(si2 != null); var numerator = si1.Numerator.Concat(si2.Numerator); var denominator = si1.Denominator.Concat(si2.Denominator); return new SI(si1.Val * si2.Val, numerator, denominator); @@ -597,6 +614,8 @@ namespace TUGraz.VectoCore.Utils public static SI operator /(SI si1, SI si2) { + Contract.Requires(si1 != null); + Contract.Requires(si2 != null); var numerator = si1.Numerator.Concat(si2.Denominator); var denominator = si1.Denominator.Concat(si2.Numerator); return new SI(si1.Val / si2.Val, numerator, denominator); @@ -604,51 +623,62 @@ namespace TUGraz.VectoCore.Utils public static SI operator +(SI si1, double d) { + Contract.Requires(si1 != null); return new SI(si1.Val + d, si1); } public static SI operator +(double d, SI si1) { + Contract.Requires(si1 != null); return si1 + d; } public static SI operator -(SI si1, double d) { + Contract.Requires(si1 != null); return new SI(si1.Val - d, si1); } public static SI operator -(double d, SI si1) { + Contract.Requires(si1 != null); return new SI(d - si1.Val, si1); } public static SI operator -(SI si1) { + Contract.Requires(si1 != null); return 0 - si1; } public static SI operator *(SI si1, double d) { + Contract.Requires(si1 != null); return new SI(si1.Val * d, si1); } public static SI operator *(double d, SI si1) { + Contract.Requires(si1 != null); return new SI(d * si1.Val, si1); } public static SI operator /(SI si1, double d) { + Contract.Requires(si1 != null); return new SI(si1.Val / d, si1); } public static SI operator /(double d, SI si1) { + Contract.Requires(si1 != null); return new SI(d / si1.Val, si1); } public static bool operator <(SI si1, SI si2) { + Contract.Requires(si1 != null); + Contract.Requires(si2 != null); if (!si1.HasEqualUnit(si2)) { throw new VectoException( string.Format("Operator '<' can only operate on SI Objects with the same unit. Got: {0} + {1}", si1, si2)); @@ -658,6 +688,8 @@ namespace TUGraz.VectoCore.Utils public static bool operator >(SI si1, SI si2) { + Contract.Requires(si1 != null); + Contract.Requires(si2 != null); if (!si1.HasEqualUnit(si2)) { throw new VectoException( string.Format("Operator '>' can only operate on SI Objects with the same unit. Got: {0} + {1}", si1, si2)); @@ -667,6 +699,8 @@ namespace TUGraz.VectoCore.Utils public static bool operator <=(SI si1, SI si2) { + Contract.Requires(si1 != null); + Contract.Requires(si2 != null); if (!si1.HasEqualUnit(si2)) { throw new VectoException( string.Format("Operator '<=' can only operate on SI Objects with the same unit. Got: {0} + {1}", si1, si2)); @@ -676,6 +710,8 @@ namespace TUGraz.VectoCore.Utils public static bool operator >=(SI si1, SI si2) { + Contract.Requires(si1 != null); + Contract.Requires(si2 != null); if (!si1.HasEqualUnit(si2)) { throw new VectoException( string.Format("Operator '>=' can only operate on SI Objects with the same unit. Got: {0} + {1}", si1, si2)); @@ -685,21 +721,25 @@ namespace TUGraz.VectoCore.Utils public static bool operator <(SI si1, double d) { + Contract.Requires(si1 != null); return si1.Val < d; } public static bool operator >(SI si1, double d) { + Contract.Requires(si1 != null); return si1.Val > d; } public static bool operator <=(SI si1, double d) { + Contract.Requires(si1 != null); return si1.Val <= d; } public static bool operator >=(SI si1, double d) { + Contract.Requires(si1 != null); return si1.Val >= d; } @@ -756,7 +796,7 @@ namespace TUGraz.VectoCore.Utils /// </summary> public override string ToString() { - return string.Format("{0} [{1}]", Val, GetUnitString()); + return ToString(null); } public virtual string ToString(string format) @@ -764,6 +804,7 @@ namespace TUGraz.VectoCore.Utils if (string.IsNullOrEmpty(format)) { format = ""; } + return string.Format("{0:" + format + "} [{2}]", Val, format, GetUnitString()); } @@ -772,11 +813,11 @@ namespace TUGraz.VectoCore.Utils #region Equality members /// <summary> - /// Compares the Unit-Parts of two SI Units. + /// Compares the Unit-Parts of two SI Units. /// </summary> - [Pure] public bool HasEqualUnit(SI si) { + Contract.Requires(si != null); return ToBasicUnits() .Denominator.OrderBy(x => x) .SequenceEqual(si.ToBasicUnits().Denominator.OrderBy(x => x)) @@ -815,7 +856,7 @@ namespace TUGraz.VectoCore.Utils } if (!HasEqualUnit(si)) { - if (si.Numerator.Length + si.Denominator.Length <= Numerator.Length + Denominator.Length) { + if (si.Numerator.Length + si.Denominator.Length >= Numerator.Length + Denominator.Length) { return -1; } return 1; diff --git a/VectoCore/Utils/VectoMath.cs b/VectoCore/Utils/VectoMath.cs index 71a8fb44a3a0e8bc681175a8b94db1da1f51ac87..3ed5a2fbd0a999b5f26345bbb1bc8bb8acf4ea20 100644 --- a/VectoCore/Utils/VectoMath.cs +++ b/VectoCore/Utils/VectoMath.cs @@ -17,7 +17,7 @@ namespace TUGraz.VectoCore.Utils } - public static T Abs<T>(T si) where T : SIBase<T>, new() + public static T Abs<T>(T si) where T : SIBase<T> { return si.Abs().Cast<T>(); } diff --git a/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs b/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs index dd0e4772335baa21345738d91082a6de59a9d8d9..8091c1a9aaf96bc20691f6db0d191d2c062e4f64 100644 --- a/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; using System.Reflection; using Microsoft.VisualStudio.TestTools.UnitTesting; +using TUGraz.VectoCore.Exceptions; using TUGraz.VectoCore.Models.Simulation.Data; using TUGraz.VectoCore.Models.Simulation.Impl; using TUGraz.VectoCore.Models.SimulationComponent.Data; @@ -23,6 +25,24 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent AppDomain.CurrentDomain.SetData("DataDirectory", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); } + /// <summary> + /// Assert an expected Exception. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="func"></param> + /// <param name="message"></param> + public static void AssertException<T>(Action func, string message = null) where T : Exception + { + try { + func(); + Assert.Fail("Expected Exception {0}, but no exception occured.", typeof(T)); + } catch (T ex) { + if (message != null) { + Assert.AreEqual(message, ex.Message); + } + } + } + [TestMethod] public void TestEngineHasOutPort() { @@ -153,5 +173,38 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent engineData.WriteToFile("engineData test output.veng"); } + + [TestMethod] + public void Test_EngineData() + { + var engineData = CombustionEngineData.ReadFromFile(CoachEngine); + var motorway = engineData.WHTCMotorway; + Assert.AreEqual(motorway.Double(), 0); + Assert.IsTrue(motorway.HasEqualUnit(new SI().Kilo.Gramm.Per.Watt.Second.ConvertTo())); + + var rural = engineData.WHTCRural; + Assert.AreEqual(rural.Double(), 0); + Assert.IsTrue(rural.HasEqualUnit(new SI().Kilo.Gramm.Per.Watt.Second.ConvertTo())); + + var urban = engineData.WHTCUrban; + Assert.AreEqual(urban.Double(), 0); + Assert.IsTrue(urban.HasEqualUnit(new SI().Kilo.Gramm.Per.Watt.Second.ConvertTo())); + + var displace = engineData.Displacement; + Assert.AreEqual(0.01273, displace.Double()); + Assert.IsTrue(displace.HasEqualUnit(new SI().Cubic.Meter)); + + var inert = engineData.Inertia; + Assert.AreEqual(3.8, inert.Double(), 0.00001); + Assert.IsTrue(inert.HasEqualUnit(new SI().Kilo.Gramm.Square.Meter)); + + var idle = engineData.IdleSpeed; + Assert.AreEqual(58.6430628670095, idle.Double(), 0.000001); + Assert.IsTrue(idle.HasEqualUnit(0.SI<PerSecond>())); + + var flc0 = engineData.GetFullLoadCurve(0); + + AssertException<KeyNotFoundException>(() => { var flc10000 = engineData.GetFullLoadCurve(1000); }); + } } } \ No newline at end of file diff --git a/VectoCoreTest/Utils/DoubleExtensionMethodTest.cs b/VectoCoreTest/Utils/DoubleExtensionMethodTest.cs index 21d802107dedb390e230f7ed1e36d8b7e2d2525e..afcc4bf834ff5ab10add5a573d72b7a0c7d84d01 100644 --- a/VectoCoreTest/Utils/DoubleExtensionMethodTest.cs +++ b/VectoCoreTest/Utils/DoubleExtensionMethodTest.cs @@ -1,4 +1,5 @@ using System; +using System.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; using TUGraz.VectoCore.Utils; @@ -25,5 +26,44 @@ namespace TUGraz.VectoCore.Tests.Utils Assert.AreEqual(600.SI().Rounds.Per.Minute.Cast<PerSecond>(), val2); Assert.AreEqual(600.SI().Rounds.Per.Minute.Cast<PerSecond>().Double(), val2.Double()); } + + [TestMethod] + public void DoubleExtension_CompareTests() + { + Assert.IsTrue(0.0.IsEqual(0.0)); + Assert.IsTrue(1.0.IsGreater(0.0)); + Assert.IsTrue(1.0.IsGreaterOrEqual(1.0)); + Assert.IsTrue(1.0.IsPositive()); + Assert.IsTrue(0.0.IsSmaller(1.0)); + Assert.IsTrue(1.0.IsSmallerOrEqual(1.0)); + + + Assert.IsTrue(0.0.IsEqual(0.001)); + + Assert.IsTrue(1.002.IsGreater(1.0)); + Assert.IsTrue(1.001.IsGreater(1.0)); + Assert.IsTrue(1.0.IsGreater(1.0)); + Assert.IsFalse(0.999.IsGreater(1.0)); + + Assert.IsTrue(1.001.IsGreaterOrEqual(1.0)); + Assert.IsFalse(0.999.IsGreaterOrEqual(1.0)); + Assert.IsFalse(0.998.IsGreaterOrEqual(1.0)); + + Assert.IsTrue(0.001.IsPositive()); + Assert.IsTrue(0.0.IsPositive()); + Assert.IsTrue((-0.0009).IsPositive()); + Assert.IsFalse((-0.001).IsPositive()); + Assert.IsFalse((-0.002).IsPositive()); + + Assert.IsTrue(0.998.IsSmaller(1.0)); + Assert.IsTrue(0.999.IsSmaller(1.0)); + Assert.IsTrue(1.0.IsSmaller(1.0)); + Assert.IsFalse(1.0011.IsSmaller(1.0)); + + Assert.IsTrue(1.001.IsSmallerOrEqual(1.0)); + Assert.IsFalse(1.002.IsSmallerOrEqual(1.0)); + Assert.IsTrue(0.999.IsSmallerOrEqual(1.0)); + Assert.IsTrue(0.998.IsSmallerOrEqual(1.0)); + } } } \ No newline at end of file diff --git a/VectoCoreTest/Utils/SITest.cs b/VectoCoreTest/Utils/SITest.cs index 94325d67b9e5c293821c32d2461d040534d13769..5b89dd80ddb4e280bba615d7b4c352a04f74a3be 100644 --- a/VectoCoreTest/Utils/SITest.cs +++ b/VectoCoreTest/Utils/SITest.cs @@ -19,7 +19,7 @@ namespace TUGraz.VectoCore.Tests.Utils { try { func(); - Assert.Fail("Expected Exception {0}, but no exception occured.", typeof (T)); + Assert.Fail("Expected Exception {0}, but no exception occured.", typeof(T)); } catch (T ex) { if (message != null) { Assert.AreEqual(message, ex.Message); @@ -27,63 +27,64 @@ namespace TUGraz.VectoCore.Tests.Utils } } + [TestMethod] public void SI_TypicalUsageTest() { //mult var angularVelocity = 600.RPMtoRad(); var torque = 1500.SI<NewtonMeter>(); var power = angularVelocity * torque; - Assert.IsInstanceOfType(power, typeof (Watt)); - Assert.AreEqual(600 * 1500, power.Double()); + Assert.IsInstanceOfType(power, typeof(Watt)); + Assert.AreEqual(600.0 / 60 * 2 * Math.PI * 1500, power.Double()); var siStandardMult = power * torque; - Assert.IsInstanceOfType(siStandardMult, typeof (SI)); - Assert.AreEqual(600 * 1500 * 1500, siStandardMult.Double()); + Assert.IsInstanceOfType(siStandardMult, typeof(SI)); + Assert.AreEqual(600.0 / 60 * 2 * Math.PI * 1500 * 1500, siStandardMult.Double()); Assert.IsTrue(siStandardMult.HasEqualUnit(new SI().Watt.Newton.Meter)); //div var torque2 = power / angularVelocity; - Assert.IsInstanceOfType(torque2, typeof (NewtonMeter)); + Assert.IsInstanceOfType(torque2, typeof(NewtonMeter)); Assert.AreEqual(1500, torque2.Double()); var siStandardDiv = power / power; - Assert.IsInstanceOfType(siStandardMult, typeof (SI)); + Assert.IsInstanceOfType(siStandardMult, typeof(SI)); Assert.IsTrue(siStandardDiv.HasEqualUnit(new SI())); - Assert.AreEqual(600 * 1500 * 1500, siStandardMult.Double()); + Assert.AreEqual(600.0 / 60 * 2 * Math.PI * 1500 * 1500, siStandardMult.Double()); //add var angularVelocity2 = 400.SI<RoundsPerMinute>().Cast<PerSecond>(); var angVeloSum = angularVelocity + angularVelocity2; - Assert.IsInstanceOfType(angVeloSum, typeof (PerSecond)); - Assert.AreEqual(400 + 600, angVeloSum.Double()); + Assert.IsInstanceOfType(angVeloSum, typeof(PerSecond)); + Assert.AreEqual((400.0 + 600) / 60 * 2 * Math.PI, angVeloSum.Double(), 0.0000001); AssertException<VectoException>(() => { var x = 500.SI().Watt + 300.SI().Newton; }); //subtract var angVeloDiff = angularVelocity - angularVelocity2; - Assert.IsInstanceOfType(angVeloDiff, typeof (PerSecond)); - Assert.AreEqual(600 - 400, angVeloDiff.Double()); + Assert.IsInstanceOfType(angVeloDiff, typeof(PerSecond)); + Assert.AreEqual((600.0 - 400) / 60 * 2 * Math.PI, angVeloDiff.Double(), 0.0000001); //general si unit - var generalSIUnit = 60000.SI().Gramm.Per.Kilo.Watt.Hour.ConvertTo().Kilo.Gramm.Per.Watt.Second; - Assert.IsInstanceOfType(generalSIUnit, typeof (SI)); + var generalSIUnit = 3600000000.0.SI().Gramm.Per.Kilo.Watt.Hour.ConvertTo().Kilo.Gramm.Per.Watt.Second; + Assert.IsInstanceOfType(generalSIUnit, typeof(SI)); Assert.AreEqual(1, generalSIUnit.Double()); //type conversion - var engineSpeed = 600; + var engineSpeed = 600.0; var angularVelocity3 = engineSpeed.RPMtoRad(); // convert between units measures var angularVelocity4 = engineSpeed.SI().Rounds.Per.Minute.ConvertTo().Radian.Per.Second; // cast SI to specialized unit classes. - var angularVelocity5 = angularVelocity2.Cast<PerSecond>(); + var angularVelocity5 = angularVelocity4.Cast<PerSecond>(); Assert.AreEqual(angularVelocity3, angularVelocity5); Assert.AreEqual(angularVelocity3.Double(), angularVelocity4.Double()); - Assert.IsInstanceOfType(angularVelocity3, typeof (PerSecond)); - Assert.IsInstanceOfType(angularVelocity5, typeof (PerSecond)); - Assert.IsInstanceOfType(angularVelocity4, typeof (SI)); + Assert.IsInstanceOfType(angularVelocity3, typeof(PerSecond)); + Assert.IsInstanceOfType(angularVelocity5, typeof(PerSecond)); + Assert.IsInstanceOfType(angularVelocity4, typeof(SI)); // ConvertTo only allows conversion if the units are correct. @@ -154,6 +155,27 @@ namespace TUGraz.VectoCore.Tests.Utils { var v1 = 600.SI<NewtonMeter>(); var v2 = 455.SI<NewtonMeter>(); + + Assert.IsTrue(v1 > v2); + Assert.IsTrue(v2 < v1); + Assert.IsTrue(v1 >= v2); + Assert.IsTrue(v2 <= v1); + + Assert.IsFalse(v1 < v2); + Assert.IsFalse(v2 > v1); + Assert.IsFalse(v1 <= v2); + Assert.IsFalse(v2 >= v1); + + Assert.AreEqual(1, new SI().CompareTo(null)); + Assert.AreEqual(1, new SI().CompareTo("bla")); + Assert.AreEqual(-1, new SI().Meter.CompareTo(new SI().Kilo.Meter.Per.Hour)); + Assert.AreEqual(1, new SI().Newton.Meter.CompareTo(new SI().Meter)); + + Assert.AreEqual(0, 1.SI().CompareTo(1.SI())); + Assert.AreEqual(-1, 1.SI().CompareTo(2.SI())); + Assert.AreEqual(1, 2.SI().CompareTo(1.SI())); + + NewtonMeter v3 = v1 + v2; NewtonMeter v4 = v1 - v2; @@ -177,5 +199,18 @@ namespace TUGraz.VectoCore.Tests.Utils PerSecond angVelo1 = w / t; Second sec = t / w; } + + [TestMethod] + public void SI_SpecialUnits() + { + 2.SI<MeterPerSecond>(); + 1.SI<Second>(); + 2.SI<Watt>(); + 1.SI<PerSecond>(); + 2.SI<RoundsPerMinute>(); + 3.SI<Newton>(); + 4.SI<Radian>(); + 5.SI<NewtonMeter>(); + } } } \ No newline at end of file diff --git a/VectoCoreTest/Utils/VectoMathTest.cs b/VectoCoreTest/Utils/VectoMathTest.cs index 2c163c8e67055adf5aba109fb75ee29ec58e5c41..263cb79984068c8e9f51708c04e746449a1183c9 100644 --- a/VectoCoreTest/Utils/VectoMathTest.cs +++ b/VectoCoreTest/Utils/VectoMathTest.cs @@ -26,6 +26,7 @@ namespace TUGraz.VectoCore.Tests.Utils var negativeWatt = -10.SI<Watt>(); var positiveWatt = 10.SI<Watt>(); Assert.AreEqual(smallerWatt, VectoMath.Min(smallerWatt, biggerWatt)); + Assert.AreEqual(smallerWatt, VectoMath.Min(biggerWatt, smallerWatt)); Assert.AreEqual(biggerWatt, VectoMath.Max(smallerWatt, biggerWatt));