diff --git a/VectoCore/Models/Connector/Ports/ITnPort.cs b/VectoCore/Models/Connector/Ports/ITnPort.cs index f3df1dd0c8f1a4d839150851878c5068618e3450..0d6ddba5b82b143d8d717d83c0dac6d754e51f96 100644 --- a/VectoCore/Models/Connector/Ports/ITnPort.cs +++ b/VectoCore/Models/Connector/Ports/ITnPort.cs @@ -26,6 +26,6 @@ namespace TUGraz.VectoCore.Models.Connector.Ports /// <param name="dt">[s]</param> /// <param name="torque">[Nm]</param> /// <param name="angularVelocity">[rad/s]</param> - IResponse Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, RadianPerSecond angularVelocity); + IResponse Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, PerSecond angularVelocity); } } \ No newline at end of file diff --git a/VectoCore/Models/Simulation/Cockpit/IEngineCockpit.cs b/VectoCore/Models/Simulation/Cockpit/IEngineCockpit.cs index e1844efc5802e3a74f000371116d53f1dcd0b502..52b57d8e718cae9a2553c4058d93250ae8100d58 100644 --- a/VectoCore/Models/Simulation/Cockpit/IEngineCockpit.cs +++ b/VectoCore/Models/Simulation/Cockpit/IEngineCockpit.cs @@ -10,6 +10,6 @@ namespace TUGraz.VectoCore.Models.Simulation.Cockpit /// <summary> /// [rad/s] The current engine speed. /// </summary> - RadianPerSecond EngineSpeed(); + PerSecond EngineSpeed(); } } \ No newline at end of file diff --git a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs index c1de88ce572721a10032d879c1f97f1ce32d63b0..dfa97107416ce81cbb05c1bcdd477385b5aa37a7 100644 --- a/VectoCore/Models/Simulation/Impl/VehicleContainer.cs +++ b/VectoCore/Models/Simulation/Impl/VehicleContainer.cs @@ -29,7 +29,7 @@ namespace TUGraz.VectoCore.Models.Simulation.Impl #region IEngineCockpit - public RadianPerSecond EngineSpeed() + public PerSecond EngineSpeed() { if (_engine == null) { throw new VectoException("no engine available!"); diff --git a/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs b/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs index 0b7def405135caeb627585e41dcde7ffe9c5bed4..954e88b367d921828278c720988c8edb835a1f2f 100644 --- a/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs +++ b/VectoCore/Models/SimulationComponent/Data/CombustionEngineData.cs @@ -75,7 +75,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data /// <summary> /// [rad/s] /// </summary> - public RadianPerSecond IdleSpeed + public PerSecond IdleSpeed { get { return _data.Body.IdleSpeed.RPMtoRad(); } protected set { _data.Body.IdleSpeed = (double) value.ConvertTo().Rounds.Per.Minute; } diff --git a/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs b/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs index 056f46fde552ec3d6e6cb83851745af54fc1dc89..a1e4032d5a4a9070912bc18e1267e7841a2506b1 100644 --- a/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs +++ b/VectoCore/Models/SimulationComponent/Data/DrivingCycleData.cs @@ -178,7 +178,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data /// [rad/s] If "n" is defined VECTO uses that instead of the /// calculated engine speed value. /// </summary> - public RadianPerSecond EngineSpeed { get; set; } + public PerSecond EngineSpeed { get; set; } /// <summary> /// [-] Gear input. Overwrites the gear shift model. @@ -249,7 +249,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data AdditionalAuxPowerDemand = row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(), EngineSpeed = - row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<RadianPerSecond>(), + row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(), Gear = row.ParseDoubleOrGetDefault(Fields.Gear), AirSpeedRelativeToVehicle = row.ParseDoubleOrGetDefault(Fields.AirSpeedRelativeToVehicle) @@ -311,7 +311,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(), Gear = row.ParseDoubleOrGetDefault(Fields.Gear), EngineSpeed = - row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<RadianPerSecond>(), + row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(), AirSpeedRelativeToVehicle = row.ParseDoubleOrGetDefault(Fields.AirSpeedRelativeToVehicle) .SI() @@ -365,7 +365,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data foreach (DataRow row in table.Rows) { var entry = new DrivingCycleEntry { EngineSpeed = - row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<RadianPerSecond>(), + row.ParseDoubleOrGetDefault(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(), AdditionalAuxPowerDemand = row.ParseDoubleOrGetDefault(Fields.AdditionalAuxPowerDemand).SI().Kilo.Watt.Cast<Watt>(), AuxiliarySupplyPower = AuxSupplyPowerReader.Read(row) diff --git a/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs b/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs index cf39c70c9908d34f8237f2da12d26dc1e5181b65..f1ef8218c1b7f94cb5820c1446974730e96d65ab 100644 --- a/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs +++ b/VectoCore/Models/SimulationComponent/Data/Engine/FuelConsumptionMap.cs @@ -26,7 +26,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine try { var entry = new FuelConsumptionEntry { EngineSpeed = - row.ParseDouble(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<RadianPerSecond>(), + row.ParseDouble(Fields.EngineSpeed).SI().Rounds.Per.Minute.Cast<PerSecond>(), Torque = row.ParseDouble(Fields.Torque).SI<NewtonMeter>(), FuelConsumption = row.ParseDouble(Fields.FuelConsumption).SI().Gramm.Per.Hour.ConvertTo().Kilo.Gramm.Per.Second @@ -61,7 +61,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine /// <param name="engineSpeed">[rad/sec]</param> /// <param name="torque">[Nm]</param> /// <returns>[kg/s]</returns> - public SI GetFuelConsumption(NewtonMeter torque, RadianPerSecond engineSpeed) + public SI GetFuelConsumption(NewtonMeter torque, PerSecond engineSpeed) { // delauney map needs is initialised with rpm, therefore the engineSpeed has to be converted. return @@ -93,7 +93,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine /// <summary> /// engine speed [rad/s] /// </summary> - public RadianPerSecond EngineSpeed { get; set; } + public PerSecond EngineSpeed { get; set; } /// <summary> /// Torque [Nm] diff --git a/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs b/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs index c78dd738df4dac7cb84255575b81fc82884fb326..23817dfd7f9340ae61192d9c270148cdf9076587 100644 --- a/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs +++ b/VectoCore/Models/SimulationComponent/Data/Engine/FullLoadCurve.cs @@ -9,267 +9,268 @@ using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Engine { - /// <summary> - /// Represents the Full load curve. - /// </summary> - public class FullLoadCurve : SimulationComponentData - { - [JsonProperty] private List<FullLoadCurveEntry> _entries; - - public static FullLoadCurve ReadFromFile(string fileName) - { - var data = VectoCSVFile.Read(fileName); - - //todo Contract.Requires<VectoException>(data.Columns.Count != 4, "FullLoadCurve Data File must consist of 4 columns."); - if (data.Columns.Count != 4) { - throw new VectoException("FullLoadCurve Data File must consist of 4 columns."); - } - - //todo Contract.Requires<VectoException>(data.Rows.Count < 2, "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); - if (data.Rows.Count < 2) { - throw new VectoException( - "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); - } - - List<FullLoadCurveEntry> entries; - if (HeaderIsValid(data.Columns)) { - entries = CreateFromColumnNames(data); - } else { - var log = LogManager.GetLogger<FullLoadCurve>(); - log.WarnFormat( - "FullLoadCurve: Header Line is not valid. Expected: '{0}, {1}, {2}, {3}', Got: '{4}'. Falling back to column index.", - Fields.EngineSpeed, Fields.TorqueFullLoad, Fields.TorqueDrag, Fields.PT1, - string.Join(", ", data.Columns.Cast<DataColumn>().Select(c => c.ColumnName).Reverse())); - - entries = CreateFromColumnIndizes(data); - } - - return new FullLoadCurve { _entries = entries }; - } - - private static bool HeaderIsValid(DataColumnCollection columns) - { - Contract.Requires(columns != null); - return columns.Contains(Fields.EngineSpeed) - && columns.Contains(Fields.TorqueDrag) - && columns.Contains(Fields.TorqueFullLoad) - && columns.Contains(Fields.PT1); - } - - private static List<FullLoadCurveEntry> CreateFromColumnNames(DataTable data) - { - Contract.Requires(data != null); - return (from DataRow row in data.Rows - select new FullLoadCurveEntry { - EngineSpeed = row.ParseDouble(Fields.EngineSpeed).RPMtoRad(), - TorqueFullLoad = row.ParseDouble(Fields.TorqueFullLoad).SI<NewtonMeter>(), - TorqueDrag = row.ParseDouble(Fields.TorqueDrag).SI<NewtonMeter>(), - PT1 = row.ParseDouble(Fields.PT1).SI<Second>() - }).ToList(); - } - - private static List<FullLoadCurveEntry> CreateFromColumnIndizes(DataTable data) - { - Contract.Requires(data != null); - return (from DataRow row in data.Rows - select new FullLoadCurveEntry { - EngineSpeed = row.ParseDouble(0).RPMtoRad(), - TorqueFullLoad = row.ParseDouble(1).SI<NewtonMeter>(), - TorqueDrag = row.ParseDouble(2).SI<NewtonMeter>(), - PT1 = row.ParseDouble(3).SI<Second>() - }).ToList(); - } - - /// <summary> - /// [rad/s] => [Nm] - /// </summary> - /// <param name="angularFrequency">[rad/s]</param> - /// <returns>[Nm]</returns> - public NewtonMeter FullLoadStationaryTorque(RadianPerSecond angularFrequency) - { - var idx = FindIndex(angularFrequency); - return VectoMath.Interpolate((double) _entries[idx - 1].EngineSpeed, (double) _entries[idx].EngineSpeed, - (double) _entries[idx - 1].TorqueFullLoad, (double) _entries[idx].TorqueFullLoad, - (double) angularFrequency).SI<NewtonMeter>(); - } - - /// <summary> - /// [rad/s] => [W] - /// </summary> - /// <param name="angularFrequency">[rad/s]</param> - /// <returns>[W]</returns> - public Watt FullLoadStationaryPower(RadianPerSecond angularFrequency) - { - return Formulas.TorqueToPower(FullLoadStationaryTorque(angularFrequency), angularFrequency); - } - - /// <summary> - /// [rad/s] => [Nm] - /// </summary> - /// <param name="angularFrequency">[rad/s]</param> - /// <returns>[Nm]</returns> - public NewtonMeter DragLoadStationaryTorque(RadianPerSecond angularFrequency) - { - var idx = FindIndex(angularFrequency); - return VectoMath.Interpolate((double) _entries[idx - 1].EngineSpeed, (double) _entries[idx].EngineSpeed, - (double) _entries[idx - 1].TorqueDrag, (double) _entries[idx].TorqueDrag, - (double) angularFrequency).SI<NewtonMeter>(); - } - - /// <summary> - /// [rad/s] => [W]. - /// </summary> - /// <param name="angularFrequency">[rad/s]</param> - /// <returns>[W]</returns> - public Watt DragLoadStationaryPower(RadianPerSecond angularFrequency) - { - Contract.Requires(angularFrequency.HasEqualUnit(new SI().Radian.Per.Second)); - Contract.Ensures(Contract.Result<SI>().HasEqualUnit(new SI().Watt)); - - return Formulas.TorqueToPower(DragLoadStationaryTorque(angularFrequency), angularFrequency); - } - - /// <summary> - /// [rad/s] => [-] - /// </summary> - /// <param name="angularFrequency">[rad/s]</param> - /// <returns>[-]</returns> - public double PT1(SI angularFrequency) - { - Contract.Requires(angularFrequency.HasEqualUnit(new SI().Radian.Per.Second)); - Contract.Ensures(Contract.Result<SI>().HasEqualUnit(new SI())); - - var idx = FindIndex(angularFrequency); - return VectoMath.Interpolate((double) _entries[idx - 1].EngineSpeed, (double) _entries[idx].EngineSpeed, - (double) _entries[idx - 1].PT1, (double) _entries[idx].PT1, - (double) angularFrequency); - } - - /// <summary> - /// [rad/s] => index. Get item index for engineSpeed. - /// </summary> - /// <param name="engineSpeed">[rad/s]</param> - /// <returns>index</returns> - protected int FindIndex(SI engineSpeed) - { - Contract.Requires(engineSpeed.HasEqualUnit(new SI().Radian.Per.Second)); - - int idx; - if (engineSpeed < _entries[0].EngineSpeed) { - Log.ErrorFormat("requested rpm below minimum rpm in FLD curve - extrapolating. n: {0}, rpm_min: {1}", - engineSpeed.ConvertTo().Rounds.Per.Minute, _entries[0].EngineSpeed.ConvertTo().Rounds.Per.Minute); - idx = 1; - } else { - idx = _entries.FindIndex(x => x.EngineSpeed > engineSpeed); - } - if (idx <= 0) { - idx = engineSpeed > _entries[0].EngineSpeed ? _entries.Count - 1 : 1; - } - return idx; - } - - private static class Fields - { - /// <summary> - /// [rpm] engine speed - /// </summary> - public const string EngineSpeed = "engine speed"; - - /// <summary> - /// [Nm] full load torque - /// </summary> - public const string TorqueFullLoad = "full load torque"; - - /// <summary> - /// [Nm] motoring torque - /// </summary> - public const string TorqueDrag = "motoring torque"; - - /// <summary> - /// [s] time constant - /// </summary> - public const string PT1 = "PT1"; - } - - private class FullLoadCurveEntry - { - /// <summary> - /// [rad/s] engine speed - /// </summary> - public RadianPerSecond EngineSpeed { get; set; } - - /// <summary> - /// [Nm] full load torque - /// </summary> - public NewtonMeter TorqueFullLoad { get; set; } - - /// <summary> - /// [Nm] motoring torque - /// </summary> - public NewtonMeter TorqueDrag { get; set; } - - /// <summary> - /// [s] PT1 time constant - /// </summary> - public Second PT1 { get; set; } - - #region Equality members - - protected bool Equals(FullLoadCurveEntry other) - { - Contract.Requires(other != null); - return EngineSpeed.Equals(other.EngineSpeed) - && TorqueFullLoad.Equals(other.TorqueFullLoad) - && TorqueDrag.Equals(other.TorqueDrag) - && PT1.Equals(other.PT1); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) { - return false; - } - if (ReferenceEquals(this, obj)) { - return true; - } - return obj.GetType() == GetType() && Equals((FullLoadCurveEntry) obj); - } - - public override int GetHashCode() - { - var hashCode = EngineSpeed.GetHashCode(); - hashCode = (hashCode * 397) ^ TorqueFullLoad.GetHashCode(); - hashCode = (hashCode * 397) ^ TorqueDrag.GetHashCode(); - hashCode = (hashCode * 397) ^ PT1.GetHashCode(); - return hashCode; - } - - #endregion - } - - #region Equality members - - protected bool Equals(FullLoadCurve other) - { - return _entries.SequenceEqual(other._entries); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) { - return false; - } - if (ReferenceEquals(this, obj)) { - return true; - } - return obj.GetType() == GetType() && Equals((FullLoadCurve) obj); - } - - public override int GetHashCode() - { - return (_entries != null ? _entries.GetHashCode() : 0); - } - - #endregion - } + /// <summary> + /// Represents the Full load curve. + /// </summary> + public class FullLoadCurve : SimulationComponentData + { + [JsonProperty] private List<FullLoadCurveEntry> _entries; + + public static FullLoadCurve ReadFromFile(string fileName) + { + var data = VectoCSVFile.Read(fileName); + + //todo Contract.Requires<VectoException>(data.Columns.Count != 4, "FullLoadCurve Data File must consist of 4 columns."); + if (data.Columns.Count != 4) { + throw new VectoException("FullLoadCurve Data File must consist of 4 columns."); + } + + //todo Contract.Requires<VectoException>(data.Rows.Count < 2, "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); + if (data.Rows.Count < 2) { + throw new VectoException( + "FullLoadCurve must consist of at least two lines with numeric values (below file header)"); + } + + List<FullLoadCurveEntry> entries; + if (HeaderIsValid(data.Columns)) { + entries = CreateFromColumnNames(data); + } else { + var log = LogManager.GetLogger<FullLoadCurve>(); + log.WarnFormat( + "FullLoadCurve: Header Line is not valid. Expected: '{0}, {1}, {2}, {3}', Got: '{4}'. Falling back to column index.", + Fields.EngineSpeed, Fields.TorqueFullLoad, Fields.TorqueDrag, Fields.PT1, + string.Join(", ", data.Columns.Cast<DataColumn>().Select(c => c.ColumnName).Reverse())); + + entries = CreateFromColumnIndizes(data); + } + + return new FullLoadCurve { _entries = entries }; + } + + private static bool HeaderIsValid(DataColumnCollection columns) + { + Contract.Requires(columns != null); + return columns.Contains(Fields.EngineSpeed) + && columns.Contains(Fields.TorqueDrag) + && columns.Contains(Fields.TorqueFullLoad) + && columns.Contains(Fields.PT1); + } + + private static List<FullLoadCurveEntry> CreateFromColumnNames(DataTable data) + { + Contract.Requires(data != null); + return (from DataRow row in data.Rows + select new FullLoadCurveEntry { + EngineSpeed = row.ParseDouble(Fields.EngineSpeed).RPMtoRad(), + TorqueFullLoad = row.ParseDouble(Fields.TorqueFullLoad).SI<NewtonMeter>(), + TorqueDrag = row.ParseDouble(Fields.TorqueDrag).SI<NewtonMeter>(), + PT1 = row.ParseDouble(Fields.PT1).SI<Second>() + }).ToList(); + } + + private static List<FullLoadCurveEntry> CreateFromColumnIndizes(DataTable data) + { + Contract.Requires(data != null); + return (from DataRow row in data.Rows + select new FullLoadCurveEntry { + EngineSpeed = row.ParseDouble(0).RPMtoRad(), + TorqueFullLoad = row.ParseDouble(1).SI<NewtonMeter>(), + TorqueDrag = row.ParseDouble(2).SI<NewtonMeter>(), + PT1 = row.ParseDouble(3).SI<Second>() + }).ToList(); + } + + /// <summary> + /// [rad/s] => [Nm] + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[Nm]</returns> + 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>(); + } + + /// <summary> + /// [rad/s] => [W] + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[W]</returns> + public Watt FullLoadStationaryPower(PerSecond angularVelocity) + { + return Formulas.TorqueToPower(FullLoadStationaryTorque(angularVelocity), angularVelocity); + } + + /// <summary> + /// [rad/s] => [Nm] + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[Nm]</returns> + 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>(); + } + + /// <summary> + /// [rad/s] => [W]. + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[W]</returns> + public Watt DragLoadStationaryPower(PerSecond angularVelocity) + { + Contract.Requires(angularVelocity.HasEqualUnit(new SI().Radian.Per.Second)); + Contract.Ensures(Contract.Result<SI>().HasEqualUnit(new SI().Watt)); + + return Formulas.TorqueToPower(DragLoadStationaryTorque(angularVelocity), angularVelocity); + } + + /// <summary> + /// [rad/s] => [-] + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[-]</returns> + public double PT1(PerSecond angularVelocity) + { + Contract.Requires(angularVelocity.HasEqualUnit(new SI().Radian.Per.Second)); + Contract.Ensures(Contract.Result<SI>().HasEqualUnit(new SI())); + + var idx = FindIndex(angularVelocity); + return VectoMath.Interpolate(_entries[idx - 1].EngineSpeed.Double(), + _entries[idx].EngineSpeed.Double(), + _entries[idx - 1].PT1.Double(), _entries[idx].PT1.Double(), + angularVelocity.Double()); + } + + /// <summary> + /// [rad/s] => index. Get item index for angularVelocity. + /// </summary> + /// <param name="angularVelocity">[rad/s]</param> + /// <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}", + angularVelocity.ConvertTo().Rounds.Per.Minute, _entries[0].EngineSpeed.ConvertTo().Rounds.Per.Minute); + idx = 1; + } else { + idx = _entries.FindIndex(x => x.EngineSpeed > angularVelocity); + } + if (idx <= 0) { + idx = angularVelocity > _entries[0].EngineSpeed ? _entries.Count - 1 : 1; + } + return idx; + } + + private static class Fields + { + /// <summary> + /// [rpm] engine speed + /// </summary> + public const string EngineSpeed = "engine speed"; + + /// <summary> + /// [Nm] full load torque + /// </summary> + public const string TorqueFullLoad = "full load torque"; + + /// <summary> + /// [Nm] motoring torque + /// </summary> + public const string TorqueDrag = "motoring torque"; + + /// <summary> + /// [s] time constant + /// </summary> + public const string PT1 = "PT1"; + } + + private class FullLoadCurveEntry + { + /// <summary> + /// [rad/s] engine speed + /// </summary> + public PerSecond EngineSpeed { get; set; } + + /// <summary> + /// [Nm] full load torque + /// </summary> + public NewtonMeter TorqueFullLoad { get; set; } + + /// <summary> + /// [Nm] motoring torque + /// </summary> + public NewtonMeter TorqueDrag { get; set; } + + /// <summary> + /// [s] PT1 time constant + /// </summary> + public Second PT1 { get; set; } + + #region Equality members + + protected bool Equals(FullLoadCurveEntry other) + { + Contract.Requires(other != null); + return EngineSpeed.Equals(other.EngineSpeed) + && TorqueFullLoad.Equals(other.TorqueFullLoad) + && TorqueDrag.Equals(other.TorqueDrag) + && PT1.Equals(other.PT1); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { + return false; + } + if (ReferenceEquals(this, obj)) { + return true; + } + return obj.GetType() == GetType() && Equals((FullLoadCurveEntry) obj); + } + + public override int GetHashCode() + { + var hashCode = EngineSpeed.GetHashCode(); + hashCode = (hashCode * 397) ^ TorqueFullLoad.GetHashCode(); + hashCode = (hashCode * 397) ^ TorqueDrag.GetHashCode(); + hashCode = (hashCode * 397) ^ PT1.GetHashCode(); + return hashCode; + } + + #endregion + } + + #region Equality members + + protected bool Equals(FullLoadCurve other) + { + return _entries.SequenceEqual(other._entries); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { + return false; + } + if (ReferenceEquals(this, obj)) { + return true; + } + return obj.GetType() == GetType() && Equals((FullLoadCurve) obj); + } + + public override int GetHashCode() + { + return (_entries != null ? _entries.GetHashCode() : 0); + } + + #endregion + } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index 4894579229b0440ea7870a21e5af3ac7101b5190..6ee631a1149a7a2500a0ac476c39f7d49c68ffc4 100644 --- a/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -13,410 +13,410 @@ using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.SimulationComponent.Impl { - /// <summary> - /// Component for a combustion engine. - /// </summary> - public class CombustionEngine : VectoSimulationComponent, ICombustionEngine, ITnOutPort, IMemento - { - public enum EngineOperationMode - { - Idle, - Drag, - FullDrag, - Load, - FullLoad, - Stopped, - Undef - } - - private const int EngineIdleSpeedStopThreshold = 100; - private const double MaxPowerExceededThreshold = 1.05; - private const double ZeroThreshold = 0.0001; - private const double FullLoadMargin = 0.01; - [NonSerialized] private readonly List<TimeSpan> _enginePowerCorrections = new List<TimeSpan>(); - - /// <summary> - /// Current state is computed in request method - /// </summary> - private EngineState _currentState = new EngineState(); - - private CombustionEngineData _data; - private EngineState _previousState = new EngineState(); - - public CombustionEngine(IVehicleContainer cockpit, CombustionEngineData data) - : base(cockpit) - { - _data = data; - - _previousState.OperationMode = EngineOperationMode.Idle; - _previousState.EnginePower = 0.SI<Watt>(); - _previousState.EngineSpeed = _data.IdleSpeed; - } - - #region IEngineCockpit - - RadianPerSecond IEngineCockpit.EngineSpeed() - { - return _previousState.EngineSpeed; - } - - #endregion - - #region IOutShaft - - public ITnOutPort OutShaft() - { - return this; - } - - #endregion - - #region ITnOutPort - - IResponse ITnOutPort.Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, RadianPerSecond engineSpeed) - { - _currentState.EngineSpeed = engineSpeed; - _currentState.AbsTime = absTime; - - var requestedPower = Formulas.TorqueToPower(torque, engineSpeed); - _currentState.EnginePowerLoss = InertiaPowerLoss(torque, engineSpeed); - var requestedEnginePower = requestedPower + _currentState.EnginePowerLoss; - - if (engineSpeed < (double) _data.IdleSpeed - EngineIdleSpeedStopThreshold) { - _currentState.OperationMode = EngineOperationMode.Stopped; - //todo: _currentState.EnginePowerLoss = enginePowerLoss; - } - - var currentGear = Cockpit.Gear(); - - _currentState.FullDragTorque = _data.GetFullLoadCurve(currentGear).DragLoadStationaryTorque(engineSpeed); - _currentState.FullDragPower = Formulas.TorqueToPower(_currentState.FullDragTorque, engineSpeed); - - ComputeFullLoadPower(currentGear, engineSpeed, dt); - - ValidatePowerDemand(requestedEnginePower); - - requestedEnginePower = LimitEnginePower(requestedEnginePower); - - UpdateEngineState(requestedEnginePower); - - _currentState.EnginePower = requestedEnginePower; //todo + _currentState.EnginePowerLoss; - _currentState.EngineTorque = Formulas.PowerToTorque(_currentState.EnginePower, - _currentState.EngineSpeed); - - //todo: use ResponseOverloadFail in case of overload - return new ResponseSuccess(); - } - - #endregion - - #region VectoSimulationComponent - - public override void CommitSimulationStep(IModalDataWriter writer) - { - writer[ModalResultField.PaEng] = (double) _currentState.EnginePowerLoss; - writer[ModalResultField.Pe_drag] = (double) _currentState.FullDragPower; - writer[ModalResultField.Pe_full] = (double) _currentState.DynamicFullLoadPower; - writer[ModalResultField.Pe_eng] = (double) _currentState.EnginePower; - - writer[ModalResultField.Tq_drag] = (double) _currentState.FullDragTorque; - writer[ModalResultField.Tq_full] = (double) _currentState.DynamicFullLoadTorque; - writer[ModalResultField.Tq_eng] = (double) _currentState.EngineTorque; - writer[ModalResultField.n] = (double) _currentState.EngineSpeed.ConvertTo().Rounds.Per.Minute; - - try { - writer[ModalResultField.FC] = - (double) - _data.ConsumptionMap.GetFuelConsumption(_currentState.EngineTorque, _currentState.EngineSpeed) - .ConvertTo() - .Gramm.Per.Hour; - } catch (VectoException ex) { - Log.WarnFormat("t: {0} - {1} n: {2} Tq: {3}", _currentState.AbsTime.TotalSeconds, ex.Message, - _currentState.EngineSpeed, _currentState.EngineTorque); - writer[ModalResultField.FC] = Double.NaN; - } - - _previousState = _currentState; - _currentState = new EngineState(); - } - - #endregion - - /// <summary> - /// Validates the requested power demand [W]. - /// </summary> - /// <param name="requestedEnginePower">[W]</param> - protected void ValidatePowerDemand(SI requestedEnginePower) - { - Contract.Requires(requestedEnginePower.HasEqualUnit(new SI().Watt)); - - if (_currentState.FullDragPower >= 0 && requestedEnginePower < 0) { - throw new VectoSimulationException(String.Format("t: {0} P_engine_drag > 0! n: {1} [1/min] ", - _currentState.AbsTime, _currentState.EngineSpeed.ConvertTo().Rounds.Per.Minute)); - } - if (_currentState.DynamicFullLoadPower <= 0 && requestedEnginePower > 0) { - throw new VectoSimulationException(String.Format("t: {0} P_engine_full < 0! n: {1} [1/min] ", - _currentState.AbsTime, _currentState.EngineSpeed.ConvertTo().Rounds.Per.Minute)); - } - } - - /// <summary> - /// [W] => [W] - /// </summary> - /// <param name="requestedEnginePower">[W]</param> - /// <returns>[W]</returns> - protected Watt LimitEnginePower(Watt requestedEnginePower) - { - if (requestedEnginePower > _currentState.DynamicFullLoadPower) { - if (requestedEnginePower / _currentState.DynamicFullLoadPower > MaxPowerExceededThreshold) { - _enginePowerCorrections.Add(_currentState.AbsTime); - Log.WarnFormat( - "t: {0} requested power > P_engine_full * 1.05 - corrected. P_request: {1} P_engine_full: {2}", - _currentState.AbsTime, requestedEnginePower, _currentState.DynamicFullLoadPower); - } - return _currentState.DynamicFullLoadPower; - } - if (requestedEnginePower < _currentState.FullDragPower) { - if (requestedEnginePower / _currentState.FullDragPower > MaxPowerExceededThreshold && - requestedEnginePower > -99999) { - _enginePowerCorrections.Add(_currentState.AbsTime); - Log.WarnFormat( - "t: {0} requested power < P_engine_drag * 1.05 - corrected. P_request: {1} P_engine_drag: {2}", - _currentState.AbsTime, requestedEnginePower, _currentState.FullDragPower); - } - return _currentState.FullDragPower; - } - return requestedEnginePower; - } - - /// <summary> - /// Updates the engine state dependend on the requested power [W]. - /// </summary> - /// <param name="requestedEnginePower">[W]</param> - protected void UpdateEngineState(Watt requestedEnginePower) - { - if (requestedEnginePower < -ZeroThreshold) { - _currentState.OperationMode = IsFullLoad(requestedEnginePower, _currentState.DynamicFullLoadPower) - ? EngineOperationMode.FullLoad - : EngineOperationMode.Load; - } else if (requestedEnginePower > ZeroThreshold) { - _currentState.OperationMode = IsFullLoad(requestedEnginePower, _currentState.FullDragPower) - ? EngineOperationMode.FullDrag - : EngineOperationMode.Drag; - } else { - // -ZeroThreshold <= requestedEnginePower <= ZeroThreshold - _currentState.OperationMode = EngineOperationMode.Idle; - } - } - - public IList<string> Warnings() - { - IList<string> retVal = new List<string>(); - retVal.Add(string.Format("Engine power corrected (>5%) in {0} time steps ", _enginePowerCorrections.Count)); - return retVal; - } - - /// <summary> - /// computes full load power from gear [-], angularFrequency [rad/s] and dt [s]. - /// </summary> - /// <param name="gear"></param> - /// <param name="angularFrequency">[rad/s]</param> - /// <param name="dt">[s]</param> - protected void ComputeFullLoadPower(uint gear, RadianPerSecond angularFrequency, TimeSpan dt) - { - if (dt.Ticks == 0) { - throw new VectoException("ComputeFullLoadPower cannot compute at time 0."); - } - - // TODO @@@quam: handle dynamic timesteps - if (!dt.TotalSeconds.IsEqual(1)) { - throw new VectoException("simulation steps other than 1s can not be handled ATM"); - } - - //_currentState.StationaryFullLoadPower = _data.GetFullLoadCurve(gear).FullLoadStationaryPower(rpm); - _currentState.StationaryFullLoadTorque = - _data.GetFullLoadCurve(gear).FullLoadStationaryTorque(angularFrequency); - _currentState.StationaryFullLoadPower = Formulas.TorqueToPower(_currentState.StationaryFullLoadTorque, - angularFrequency); - - var pt1 = _data.GetFullLoadCurve(gear).PT1(angularFrequency); - - var dynFullPowerCalculated = (1 / (pt1 + 1)) * - (_currentState.StationaryFullLoadPower + pt1 * _previousState.EnginePower); - _currentState.DynamicFullLoadPower = dynFullPowerCalculated < _currentState.StationaryFullLoadPower - ? dynFullPowerCalculated - : _currentState.StationaryFullLoadPower; - _currentState.DynamicFullLoadTorque = Formulas.PowerToTorque(_currentState.DynamicFullLoadPower, - angularFrequency); - } - - protected bool IsFullLoad(Watt requestedPower, Watt maxPower) - { - var testValue = requestedPower / maxPower - 1.0; - return testValue.Abs() < FullLoadMargin; - } - - /// <summary> - /// Calculates power loss. [W] - /// </summary> - /// <param name="torque">[Nm]</param> - /// <param name="engineSpeed">[rad/s]</param> - /// <returns>[W]</returns> - protected Watt InertiaPowerLoss(NewtonMeter torque, RadianPerSecond engineSpeed) - { - var deltaEngineSpeed = engineSpeed - _previousState.EngineSpeed; - var avgEngineSpeed = (_previousState.EngineSpeed + engineSpeed) / 2.0.SI<Second>(); - var result = _data.Inertia * deltaEngineSpeed * avgEngineSpeed; - return result.Cast<Watt>(); - } - - public class EngineState - { - public EngineOperationMode OperationMode { get; set; } - - /// <summary> - /// [s] - /// </summary> - public TimeSpan AbsTime { get; set; } - - /// <summary> - /// [W] - /// </summary> - public Watt EnginePower { get; set; } - - /// <summary> - /// [rad/s] - /// </summary> - public RadianPerSecond EngineSpeed { get; set; } - - /// <summary> - /// [W] - /// </summary> - public Watt EnginePowerLoss { get; set; } - - /// <summary> - /// [W] - /// </summary> - public Watt StationaryFullLoadPower { get; set; } - - /// <summary> - /// [W] - /// </summary> - public Watt DynamicFullLoadPower { get; set; } - - /// <summary> - /// [Nm] - /// </summary> - public NewtonMeter StationaryFullLoadTorque { get; set; } - - /// <summary> - /// [Nm] - /// </summary> - public NewtonMeter DynamicFullLoadTorque { get; set; } - - /// <summary> - /// [W] - /// </summary> - public Watt FullDragPower { get; set; } - - /// <summary> - /// [Nm] - /// </summary> - public NewtonMeter FullDragTorque { get; set; } - - /// <summary> - /// [Nm] - /// </summary> - public NewtonMeter EngineTorque { get; set; } - - #region Equality members - - protected bool Equals(EngineState other) - { - return OperationMode == other.OperationMode - && Equals(EnginePower, other.EnginePower) - && Equals(EngineSpeed, other.EngineSpeed) - && Equals(EnginePowerLoss, other.EnginePowerLoss) - && AbsTime.Equals(other.AbsTime); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) { - return false; - } - if (ReferenceEquals(this, obj)) { - return true; - } - var other = obj as EngineState; - return other != null && Equals(other); - } - - public override int GetHashCode() - { - unchecked { - var hashCode = (int) OperationMode; - hashCode = (hashCode * 397) ^ (EnginePower != null ? EnginePower.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (EngineSpeed != null ? EngineSpeed.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (EnginePowerLoss != null ? EnginePowerLoss.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ AbsTime.GetHashCode(); - return hashCode; - } - } - - #endregion - } - - #region Equality members - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) { - return false; - } - if (ReferenceEquals(this, obj)) { - return true; - } - return obj.GetType() == GetType() && Equals((CombustionEngine) obj); - } - - protected bool Equals(CombustionEngine other) - { - return Equals(_data, other._data) - && Equals(_previousState, other._previousState) - && Equals(_currentState, other._currentState); - } - - public override int GetHashCode() - { - unchecked { - var hashCode = base.GetHashCode(); - hashCode = (hashCode * 397) ^ (_data != null ? _data.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (_previousState != null ? _previousState.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ (_currentState != null ? _currentState.GetHashCode() : 0); - return hashCode; - } - } - - #endregion - - #region IMemento - - public string Serialize() - { - var mem = new { Data = _data, PreviousState = _previousState }; - return Memento.Serialize(mem); - } - - public void Deserialize(string data) - { - var mem = new { Data = _data, PreviousState = _previousState }; - mem = Memento.Deserialize(data, mem); - - _data = mem.Data; - _previousState = mem.PreviousState; - } - - #endregion - } + /// <summary> + /// Component for a combustion engine. + /// </summary> + public class CombustionEngine : VectoSimulationComponent, ICombustionEngine, ITnOutPort, IMemento + { + public enum EngineOperationMode + { + Idle, + Drag, + FullDrag, + Load, + FullLoad, + Stopped, + Undef + } + + private const int EngineIdleSpeedStopThreshold = 100; + private const double MaxPowerExceededThreshold = 1.05; + private const double ZeroThreshold = 0.0001; + private const double FullLoadMargin = 0.01; + [NonSerialized] private readonly List<TimeSpan> _enginePowerCorrections = new List<TimeSpan>(); + + /// <summary> + /// Current state is computed in request method + /// </summary> + private EngineState _currentState = new EngineState(); + + private CombustionEngineData _data; + private EngineState _previousState = new EngineState(); + + public CombustionEngine(IVehicleContainer cockpit, CombustionEngineData data) + : base(cockpit) + { + _data = data; + + _previousState.OperationMode = EngineOperationMode.Idle; + _previousState.EnginePower = 0.SI<Watt>(); + _previousState.EngineSpeed = _data.IdleSpeed; + } + + #region IEngineCockpit + + PerSecond IEngineCockpit.EngineSpeed() + { + return _previousState.EngineSpeed; + } + + #endregion + + #region IOutShaft + + public ITnOutPort OutShaft() + { + return this; + } + + #endregion + + #region ITnOutPort + + IResponse ITnOutPort.Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, PerSecond engineSpeed) + { + _currentState.EngineSpeed = engineSpeed; + _currentState.AbsTime = absTime; + + var requestedPower = Formulas.TorqueToPower(torque, engineSpeed); + _currentState.EnginePowerLoss = InertiaPowerLoss(torque, engineSpeed); + var requestedEnginePower = requestedPower + _currentState.EnginePowerLoss; + + if (engineSpeed < (double) _data.IdleSpeed - EngineIdleSpeedStopThreshold) { + _currentState.OperationMode = EngineOperationMode.Stopped; + //todo: _currentState.EnginePowerLoss = enginePowerLoss; + } + + var currentGear = Cockpit.Gear(); + + _currentState.FullDragTorque = _data.GetFullLoadCurve(currentGear).DragLoadStationaryTorque(engineSpeed); + _currentState.FullDragPower = Formulas.TorqueToPower(_currentState.FullDragTorque, engineSpeed); + + ComputeFullLoadPower(currentGear, engineSpeed, dt); + + ValidatePowerDemand(requestedEnginePower); + + requestedEnginePower = LimitEnginePower(requestedEnginePower); + + UpdateEngineState(requestedEnginePower); + + _currentState.EnginePower = requestedEnginePower; //todo + _currentState.EnginePowerLoss; + _currentState.EngineTorque = Formulas.PowerToTorque(_currentState.EnginePower, + _currentState.EngineSpeed); + + //todo: use ResponseOverloadFail in case of overload + return new ResponseSuccess(); + } + + #endregion + + #region VectoSimulationComponent + + public override void CommitSimulationStep(IModalDataWriter writer) + { + writer[ModalResultField.PaEng] = (double) _currentState.EnginePowerLoss; + writer[ModalResultField.Pe_drag] = (double) _currentState.FullDragPower; + writer[ModalResultField.Pe_full] = (double) _currentState.DynamicFullLoadPower; + writer[ModalResultField.Pe_eng] = (double) _currentState.EnginePower; + + writer[ModalResultField.Tq_drag] = (double) _currentState.FullDragTorque; + writer[ModalResultField.Tq_full] = (double) _currentState.DynamicFullLoadTorque; + writer[ModalResultField.Tq_eng] = (double) _currentState.EngineTorque; + writer[ModalResultField.n] = (double) _currentState.EngineSpeed.ConvertTo().Rounds.Per.Minute; + + try { + writer[ModalResultField.FC] = + (double) + _data.ConsumptionMap.GetFuelConsumption(_currentState.EngineTorque, _currentState.EngineSpeed) + .ConvertTo() + .Gramm.Per.Hour; + } catch (VectoException ex) { + Log.WarnFormat("t: {0} - {1} n: {2} Tq: {3}", _currentState.AbsTime.TotalSeconds, ex.Message, + _currentState.EngineSpeed, _currentState.EngineTorque); + writer[ModalResultField.FC] = Double.NaN; + } + + _previousState = _currentState; + _currentState = new EngineState(); + } + + #endregion + + /// <summary> + /// Validates the requested power demand [W]. + /// </summary> + /// <param name="requestedEnginePower">[W]</param> + protected void ValidatePowerDemand(SI requestedEnginePower) + { + Contract.Requires(requestedEnginePower.HasEqualUnit(new SI().Watt)); + + if (_currentState.FullDragPower >= 0 && requestedEnginePower < 0) { + throw new VectoSimulationException(String.Format("t: {0} P_engine_drag > 0! n: {1} [1/min] ", + _currentState.AbsTime, _currentState.EngineSpeed.ConvertTo().Rounds.Per.Minute)); + } + if (_currentState.DynamicFullLoadPower <= 0 && requestedEnginePower > 0) { + throw new VectoSimulationException(String.Format("t: {0} P_engine_full < 0! n: {1} [1/min] ", + _currentState.AbsTime, _currentState.EngineSpeed.ConvertTo().Rounds.Per.Minute)); + } + } + + /// <summary> + /// [W] => [W] + /// </summary> + /// <param name="requestedEnginePower">[W]</param> + /// <returns>[W]</returns> + protected Watt LimitEnginePower(Watt requestedEnginePower) + { + if (requestedEnginePower > _currentState.DynamicFullLoadPower) { + if (requestedEnginePower / _currentState.DynamicFullLoadPower > MaxPowerExceededThreshold) { + _enginePowerCorrections.Add(_currentState.AbsTime); + Log.WarnFormat( + "t: {0} requested power > P_engine_full * 1.05 - corrected. P_request: {1} P_engine_full: {2}", + _currentState.AbsTime, requestedEnginePower, _currentState.DynamicFullLoadPower); + } + return _currentState.DynamicFullLoadPower; + } + if (requestedEnginePower < _currentState.FullDragPower) { + if (requestedEnginePower / _currentState.FullDragPower > MaxPowerExceededThreshold && + requestedEnginePower > -99999) { + _enginePowerCorrections.Add(_currentState.AbsTime); + Log.WarnFormat( + "t: {0} requested power < P_engine_drag * 1.05 - corrected. P_request: {1} P_engine_drag: {2}", + _currentState.AbsTime, requestedEnginePower, _currentState.FullDragPower); + } + return _currentState.FullDragPower; + } + return requestedEnginePower; + } + + /// <summary> + /// Updates the engine state dependend on the requested power [W]. + /// </summary> + /// <param name="requestedEnginePower">[W]</param> + protected void UpdateEngineState(Watt requestedEnginePower) + { + if (requestedEnginePower < -ZeroThreshold) { + _currentState.OperationMode = IsFullLoad(requestedEnginePower, _currentState.DynamicFullLoadPower) + ? EngineOperationMode.FullLoad + : EngineOperationMode.Load; + } else if (requestedEnginePower > ZeroThreshold) { + _currentState.OperationMode = IsFullLoad(requestedEnginePower, _currentState.FullDragPower) + ? EngineOperationMode.FullDrag + : EngineOperationMode.Drag; + } else { + // -ZeroThreshold <= requestedEnginePower <= ZeroThreshold + _currentState.OperationMode = EngineOperationMode.Idle; + } + } + + public IList<string> Warnings() + { + IList<string> retVal = new List<string>(); + retVal.Add(string.Format("Engine power corrected (>5%) in {0} time steps ", _enginePowerCorrections.Count)); + return retVal; + } + + /// <summary> + /// computes full load power from gear [-], angularVelocity [rad/s] and dt [s]. + /// </summary> + /// <param name="gear"></param> + /// <param name="angularVelocity">[rad/s]</param> + /// <param name="dt">[s]</param> + protected void ComputeFullLoadPower(uint gear, PerSecond angularVelocity, TimeSpan dt) + { + if (dt.Ticks == 0) { + throw new VectoException("ComputeFullLoadPower cannot compute at time 0."); + } + + // TODO @@@quam: handle dynamic timesteps + if (!dt.TotalSeconds.IsEqual(1)) { + throw new VectoException("simulation steps other than 1s can not be handled ATM"); + } + + //_currentState.StationaryFullLoadPower = _data.GetFullLoadCurve(gear).FullLoadStationaryPower(rpm); + _currentState.StationaryFullLoadTorque = + _data.GetFullLoadCurve(gear).FullLoadStationaryTorque(angularVelocity); + _currentState.StationaryFullLoadPower = Formulas.TorqueToPower(_currentState.StationaryFullLoadTorque, + angularVelocity); + + var pt1 = _data.GetFullLoadCurve(gear).PT1(angularVelocity); + + var dynFullPowerCalculated = (1 / (pt1 + 1)) * + (_currentState.StationaryFullLoadPower + pt1 * _previousState.EnginePower); + _currentState.DynamicFullLoadPower = dynFullPowerCalculated < _currentState.StationaryFullLoadPower + ? dynFullPowerCalculated + : _currentState.StationaryFullLoadPower; + _currentState.DynamicFullLoadTorque = Formulas.PowerToTorque(_currentState.DynamicFullLoadPower, + angularVelocity); + } + + protected bool IsFullLoad(Watt requestedPower, Watt maxPower) + { + var testValue = requestedPower / maxPower - 1.0; + return testValue.Abs() < FullLoadMargin; + } + + /// <summary> + /// Calculates power loss. [W] + /// </summary> + /// <param name="torque">[Nm]</param> + /// <param name="angularVelocity">[rad/s]</param> + /// <returns>[W]</returns> + protected Watt InertiaPowerLoss(NewtonMeter torque, PerSecond angularVelocity) + { + var deltaEngineSpeed = angularVelocity - _previousState.EngineSpeed; + var avgEngineSpeed = (_previousState.EngineSpeed + angularVelocity) / 2.0.SI<Second>(); + var result = _data.Inertia * deltaEngineSpeed * avgEngineSpeed; + return result.Cast<Watt>(); + } + + public class EngineState + { + public EngineOperationMode OperationMode { get; set; } + + /// <summary> + /// [s] + /// </summary> + public TimeSpan AbsTime { get; set; } + + /// <summary> + /// [W] + /// </summary> + public Watt EnginePower { get; set; } + + /// <summary> + /// [rad/s] + /// </summary> + public PerSecond EngineSpeed { get; set; } + + /// <summary> + /// [W] + /// </summary> + public Watt EnginePowerLoss { get; set; } + + /// <summary> + /// [W] + /// </summary> + public Watt StationaryFullLoadPower { get; set; } + + /// <summary> + /// [W] + /// </summary> + public Watt DynamicFullLoadPower { get; set; } + + /// <summary> + /// [Nm] + /// </summary> + public NewtonMeter StationaryFullLoadTorque { get; set; } + + /// <summary> + /// [Nm] + /// </summary> + public NewtonMeter DynamicFullLoadTorque { get; set; } + + /// <summary> + /// [W] + /// </summary> + public Watt FullDragPower { get; set; } + + /// <summary> + /// [Nm] + /// </summary> + public NewtonMeter FullDragTorque { get; set; } + + /// <summary> + /// [Nm] + /// </summary> + public NewtonMeter EngineTorque { get; set; } + + #region Equality members + + protected bool Equals(EngineState other) + { + return OperationMode == other.OperationMode + && Equals(EnginePower, other.EnginePower) + && Equals(EngineSpeed, other.EngineSpeed) + && Equals(EnginePowerLoss, other.EnginePowerLoss) + && AbsTime.Equals(other.AbsTime); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { + return false; + } + if (ReferenceEquals(this, obj)) { + return true; + } + var other = obj as EngineState; + return other != null && Equals(other); + } + + public override int GetHashCode() + { + unchecked { + var hashCode = (int) OperationMode; + hashCode = (hashCode * 397) ^ (EnginePower != null ? EnginePower.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (EngineSpeed != null ? EngineSpeed.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (EnginePowerLoss != null ? EnginePowerLoss.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ AbsTime.GetHashCode(); + return hashCode; + } + } + + #endregion + } + + #region Equality members + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) { + return false; + } + if (ReferenceEquals(this, obj)) { + return true; + } + return obj.GetType() == GetType() && Equals((CombustionEngine) obj); + } + + protected bool Equals(CombustionEngine other) + { + return Equals(_data, other._data) + && Equals(_previousState, other._previousState) + && Equals(_currentState, other._currentState); + } + + public override int GetHashCode() + { + unchecked { + var hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ (_data != null ? _data.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (_previousState != null ? _previousState.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (_currentState != null ? _currentState.GetHashCode() : 0); + return hashCode; + } + } + + #endregion + + #region IMemento + + public string Serialize() + { + var mem = new { Data = _data, PreviousState = _previousState }; + return Memento.Serialize(mem); + } + + public void Deserialize(string data) + { + var mem = new { Data = _data, PreviousState = _previousState }; + mem = Memento.Deserialize(data, mem); + + _data = mem.Data; + _previousState = mem.PreviousState; + } + + #endregion + } } \ No newline at end of file diff --git a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyAuxiliary.cs b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyAuxiliary.cs index 1521adbf49f3faaf93dbf719be7b4242fbb7ff9d..3bdd91f98268f3e96c1e196bc83dd826b56f8bef 100644 --- a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyAuxiliary.cs +++ b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyAuxiliary.cs @@ -48,7 +48,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl #region ITnOutPort - IResponse ITnOutPort.Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, RadianPerSecond engineSpeed) + IResponse ITnOutPort.Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, PerSecond engineSpeed) { if (_outPort == null) { Log.ErrorFormat("{0} cannot handle incoming request - no outport available", absTime); diff --git a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyGearbox.cs b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyGearbox.cs index a7a01d930a843d1a7c70b6f1bbaf84c77f7e594a..81c7b1fdcab71372a119539ed41f21f193beaf7f 100644 --- a/VectoCore/Models/SimulationComponent/Impl/EngineOnlyGearbox.cs +++ b/VectoCore/Models/SimulationComponent/Impl/EngineOnlyGearbox.cs @@ -51,7 +51,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl #region ITnOutPort - IResponse ITnOutPort.Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, RadianPerSecond engineSpeed) + IResponse ITnOutPort.Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, PerSecond engineSpeed) { if (_outPort == null) { Log.ErrorFormat("{0} cannot handle incoming request - no outport available", absTime); diff --git a/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs b/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs index 0611b0706f2f29b728178d52a22a3fa59ad86189..638a0f0c4235ed4f3617daa2dd54bbea738fe523 100644 --- a/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs +++ b/VectoCore/Models/SimulationComponent/Impl/Gearbox.cs @@ -40,7 +40,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl #region ITnOutPort - IResponse ITnOutPort.Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, RadianPerSecond engineSpeed) + IResponse ITnOutPort.Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, PerSecond engineSpeed) { throw new NotImplementedException(); } diff --git a/VectoCore/Utils/DoubleExtensionMethods.cs b/VectoCore/Utils/DoubleExtensionMethods.cs index f9344a4b180bb517c56760a7bf5619bcb5705c70..1daa25650ce682602be33a5de5f4a6afaad90726 100644 --- a/VectoCore/Utils/DoubleExtensionMethods.cs +++ b/VectoCore/Utils/DoubleExtensionMethods.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.Contracts; namespace TUGraz.VectoCore.Utils { @@ -7,50 +6,45 @@ namespace TUGraz.VectoCore.Utils { public const double Tolerance = 0.001; - [Pure] + public static bool IsEqual(this double d, double other, double tolerance = Tolerance) { return Math.Abs(d - other) > -tolerance; } - [Pure] public static bool IsSmaller(this double d, double other, double tolerance = Tolerance) { return d - other < tolerance; } - [Pure] public static bool IsSmallerOrEqual(this double d, double other, double tolerance = Tolerance) { return d - other <= tolerance; } - [Pure] public static bool IsGreater(this double d, double other, double tolerance = Tolerance) { return other.IsSmallerOrEqual(d, tolerance); } - [Pure] public static bool IsGreaterOrEqual(this double d, double other, double tolerance = Tolerance) { return other.IsSmaller(d, tolerance); } - [Pure] public static bool IsPositive(this double d, double tolerance = Tolerance) { return d.IsGreaterOrEqual(0.0, tolerance); } /// <summary> - /// Converts the double-value from rounds per minute to the SI Unit RadianPerSecond + /// Converts the double-value from rounds per minute to the SI Unit PerSecond /// </summary> /// <param name="d"></param> /// <returns></returns> - public static RadianPerSecond RPMtoRad(this double d) + public static PerSecond RPMtoRad(this double d) { - return d.SI().Rounds.Per.Minute.ConvertTo().Radian.Per.Second.Cast<RadianPerSecond>(); + return d.SI().Rounds.Per.Minute.ConvertTo().Radian.Per.Second.Cast<PerSecond>(); } /// <summary> @@ -65,9 +59,9 @@ namespace TUGraz.VectoCore.Utils /// <summary> /// Gets the special SI class of the number. /// </summary> - public static T SI<T>(this double d) where T : SIBase<T> + public static T SI<T>(this double d) where T : SIBase<T>, new() { - return (T) Activator.CreateInstance(typeof (T), d); + return SIBase<T>.Create(d); } } } \ No newline at end of file diff --git a/VectoCore/Utils/Formulas.cs b/VectoCore/Utils/Formulas.cs index 2548cd6d5a20870db1e54a68a36d2a8a4de110f0..1dfe96caaef92dd32107f51052b42accfe10d2dc 100644 --- a/VectoCore/Utils/Formulas.cs +++ b/VectoCore/Utils/Formulas.cs @@ -6,22 +6,22 @@ namespace TUGraz.VectoCore.Utils /// [Nm], [rad/s] => [W]. Calculates the power from torque and angular velocity. /// </summary> /// <param name="torque">[Nm]</param> - /// <param name="angularFrequency">[rad/s]</param> + /// <param name="angularVelocity">[rad/s]</param> /// <returns>power [W]</returns> - public static Watt TorqueToPower(NewtonMeter torque, RadianPerSecond angularFrequency) + public static Watt TorqueToPower(NewtonMeter torque, PerSecond angularVelocity) { - return torque * angularFrequency; + return torque * angularVelocity; } /// <summary> /// [W], [rad/s] => [Nm]. Calculates the torque from power and angular velocity. /// </summary> /// <param name="power">[W]</param> - /// <param name="angularFrequency">[rad/s]</param> + /// <param name="angularVelocity">[rad/s]</param> /// <returns>torque [Nm]</returns> - public static NewtonMeter PowerToTorque(Watt power, RadianPerSecond angularFrequency) + public static NewtonMeter PowerToTorque(Watt power, PerSecond angularVelocity) { - return power / angularFrequency; + return power / angularVelocity; } } } \ No newline at end of file diff --git a/VectoCore/Utils/IntExtensionMethods.cs b/VectoCore/Utils/IntExtensionMethods.cs index 489b3b86fa07a5819fc8f2e5d791632e8e8003c8..0075574fbf4b245c924c328da76a9b59cec7f012 100644 --- a/VectoCore/Utils/IntExtensionMethods.cs +++ b/VectoCore/Utils/IntExtensionMethods.cs @@ -6,13 +6,13 @@ namespace TUGraz.VectoCore.Utils public static class IntExtensionMethods { /// <summary> - /// Converts the value from rounds per minute to the SI Unit RadianPerSecond + /// Converts the value from rounds per minute to the SI Unit PerSecond /// </summary> /// <param name="d"></param> /// <returns></returns> - public static RadianPerSecond RPMtoRad(this double d) + public static PerSecond RPMtoRad(this int d) { - return d.SI().Rounds.Per.Minute.ConvertTo().Radian.Per.Second.Cast<RadianPerSecond>(); + return d.SI().Rounds.Per.Minute.ConvertTo().Radian.Per.Second.Cast<PerSecond>(); } /// <summary> @@ -30,9 +30,9 @@ namespace TUGraz.VectoCore.Utils /// </summary> /// <param name="d"></param> /// <returns></returns> - public static T SI<T>(this int d) where T : SIBase<T> + public static T SI<T>(this int d) where T : SIBase<T>, new() { - return (T) Activator.CreateInstance(typeof (T), d); + return SIBase<T>.Create(d); } } } \ No newline at end of file diff --git a/VectoCore/Utils/SI.cs b/VectoCore/Utils/SI.cs index 34bd0cc54a862da1387a4afffccd9bd84086a0c3..1e29afeb4c497eb566e36e40f5b0635fe6413e9b 100644 --- a/VectoCore/Utils/SI.cs +++ b/VectoCore/Utils/SI.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; +using System.Dynamic; using System.Linq; using System.Runtime.Serialization; using TUGraz.VectoCore.Exceptions; @@ -10,56 +11,72 @@ namespace TUGraz.VectoCore.Utils { public class MeterPerSecond : SIBase<MeterPerSecond> { - public MeterPerSecond(double val = 0) : base(val, new SI().Meter.Per.Second) {} - } - - public class Radian : SIBase<Radian> - { - public Radian(double val = 0) : base(val, new SI().Radian) {} + public MeterPerSecond() : this(0) {} + protected MeterPerSecond(double val) : base(val, new SI().Meter.Per.Second) {} } public class Second : SIBase<Second> { - public Second(double val = 0) : base(val, new SI().Second) {} + public Second() : this(0) {} + protected Second(double val) : base(val, new SI().Second) {} } public class Watt : SIBase<Watt> { - public Watt(double val = 0) : base(val, new SI().Watt) {} + public Watt() : this(0) {} + protected Watt(double val) : base(val, new SI().Watt) {} - public static RadianPerSecond operator /(Watt watt, NewtonMeter newtonMeter) + public static PerSecond operator /(Watt watt, NewtonMeter newtonMeter) { - return ((watt as SI) / newtonMeter).Cast<RadianPerSecond>(); + return ((watt as SI) / newtonMeter).Cast<PerSecond>(); } - public static NewtonMeter operator /(Watt watt, RadianPerSecond radianPerSecond) + public static NewtonMeter operator /(Watt watt, PerSecond perSecond) { - return ((watt as SI) / radianPerSecond).Cast<NewtonMeter>(); + return ((watt as SI) / perSecond).Cast<NewtonMeter>(); } } - public class RadianPerSecond : SIBase<RadianPerSecond> + public class PerSecond : SIBase<PerSecond> { - public RadianPerSecond(double val = 0) : base(val, new SI().Radian.Per.Second) {} - - public static Watt operator *(RadianPerSecond radianPerSecond, NewtonMeter newtonMeter) - { - return ((radianPerSecond as SI) * newtonMeter).Cast<Watt>(); - } + public PerSecond() : this(0) {} + protected PerSecond(double val) : base(val, new SI().Radian.Per.Second) {} } public class RoundsPerMinute : SIBase<RoundsPerMinute> { - public RoundsPerMinute(double val = 0) : base(val, new SI().Rounds.Per.Minute) {} + public RoundsPerMinute() : this(0) {} + protected RoundsPerMinute(double val) : base(val, new SI().Rounds.Per.Minute) {} + } + + + public class Newton : SIBase<Newton> + { + protected Newton(double val) : base(val, new SI().Newton) {} + + public Newton() : this(0) {} + } + + public class Radian : SIBase<Radian> + { + public Radian() : this(0) {} + + public Radian(double val) : base(val, new SI().Radian) {} } public class NewtonMeter : SIBase<NewtonMeter> { - public NewtonMeter(double val = 0) : base(val, new SI().Newton.Meter) {} + public NewtonMeter() : this(0) {} + protected NewtonMeter(double val) : base(val, new SI().Newton.Meter) {} - public static Watt operator *(NewtonMeter newtonMeter, RadianPerSecond radianPerSecond) + public static Watt operator *(NewtonMeter newtonMeter, PerSecond perSecond) { - return ((newtonMeter as SI) * radianPerSecond).Cast<Watt>(); + return ((newtonMeter as SI) * perSecond).Cast<Watt>(); + } + + public static Watt operator *(PerSecond perSecond, NewtonMeter newtonMeter) + { + return ((perSecond as SI) * newtonMeter).Cast<Watt>(); } public static Second operator /(NewtonMeter newtonMeter, Watt watt) @@ -68,14 +85,16 @@ namespace TUGraz.VectoCore.Utils } } - public class Newton : SIBase<Newton> - { - public Newton(double val = 0) : base(val, new SI().Newton) {} - } - public abstract class SIBase<T> : SI where T : SIBase<T> + public abstract class SIBase<T> : SI where T : SIBase<T>, new() { - protected SIBase(double val = 0) : base(val) {} + public static T Create(double val) + { + return new T { Val = val }; + } + + protected SIBase() {} + protected SIBase(double val) : base(val) {} protected SIBase(double val, SI unit) : base(val, unit) {} #region Operators @@ -153,16 +172,15 @@ namespace TUGraz.VectoCore.Utils #endregion } - [DataContract] - public class SI + public class SI : IComparable { [DataMember] protected readonly string[] Denominator; [DataMember] protected readonly int Exponent; [DataMember] protected readonly string[] Numerator; [DataMember] protected readonly bool Reciproc; [DataMember] protected readonly bool Reverse; - [DataMember] protected readonly double Val; + [DataMember] protected double Val; public SI(double val = 0.0) { @@ -278,9 +296,12 @@ namespace TUGraz.VectoCore.Utils /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> - public T Cast<T>() where T : SIBase<T> + public T Cast<T>() where T : SIBase<T>, new() { - var t = (T) Activator.CreateInstance(typeof (T), Val); + var t = SIBase<T>.Create(Val); + if (!HasEqualUnit(t)) { + throw new VectoException(string.Format("SI Unit Conversion failed: From {0} to {1}", this, t)); + } Contract.Assert(HasEqualUnit(t), string.Format("SI Unit Conversion failed: From {0} to {1}", this, t)); return t; } @@ -427,12 +448,12 @@ namespace TUGraz.VectoCore.Utils [DebuggerHidden] public SI Radian { - get { return new SI(this, fromUnit: "rad", toUnit: "rad"); } + get { return new SI(this); } } public SI GradientPercent { - get { return new SI(this, factor: Math.Atan(Val) / Val, fromUnit: "%", toUnit: "rad"); } + get { return new SI(this, factor: Math.Atan(Val) / Val, fromUnit: "%", toUnit: ""); } } /// <summary> @@ -441,7 +462,7 @@ namespace TUGraz.VectoCore.Utils [DebuggerHidden] public SI Rounds { - get { return new SI(this, 2 * Math.PI, toUnit: "rad"); } + get { return new SI(this, 2 * Math.PI); } } /// <summary> @@ -701,6 +722,16 @@ namespace TUGraz.VectoCore.Utils } } + int IComparable.CompareTo(object obj) + { + var si = (obj as SI); + if (si == null || this > si) { + return 1; + } + + return this < si ? -1 : 0; + } + public static bool operator ==(SI left, SI right) { return Equals(left, right); diff --git a/VectoCore/Utils/VectoMath.cs b/VectoCore/Utils/VectoMath.cs index b000e637a67e80dc6b43205f0170da2a310d8a04..71a8fb44a3a0e8bc681175a8b94db1da1f51ac87 100644 --- a/VectoCore/Utils/VectoMath.cs +++ b/VectoCore/Utils/VectoMath.cs @@ -11,19 +11,25 @@ namespace TUGraz.VectoCore.Utils return (xint - x1) * (y2 - y1) / (x2 - x1) + y1; } - public static T Abs<T>(T si) where T : SIBase<T> + public static SI Abs(SI si) { - return (T) si.Abs(); + return si.Abs(); } - public static T Min<T>(T c1, T c2) where T : SIBase<T> + + public static T Abs<T>(T si) where T : SIBase<T>, new() + { + return si.Abs().Cast<T>(); + } + + public static T Min<T>(T c1, T c2) where T : IComparable { - return c1 <= c2 ? c1 : c2; + return c1.CompareTo(c2) <= 0 ? c1 : c2; } - public static T Max<T>(T c1, T c2) where T : SIBase<T> + public static T Max<T>(T c1, T c2) where T : IComparable { - return c1 >= c2 ? c1 : c2; + return c1.CompareTo(c2) >= 0 ? c1 : c2; } } } \ No newline at end of file diff --git a/VectoCoreTest/Models/Simulation/DrivingCycleTests.cs b/VectoCoreTest/Models/Simulation/DrivingCycleTests.cs index eee3b16b6b6b9d9d1bdf570aa92078ed4de97217..35d4f7f444d6702a42e9ce00314e7fd64c073b71 100644 --- a/VectoCoreTest/Models/Simulation/DrivingCycleTests.cs +++ b/VectoCoreTest/Models/Simulation/DrivingCycleTests.cs @@ -10,91 +10,91 @@ using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Tests.Models.Simulation { - [TestClass] - public class DrivingCycleTests - { - [TestMethod] - public void TestEngineOnly() - { - var container = new VehicleContainer(); + [TestClass] + public class DrivingCycleTests + { + [TestMethod] + public void TestEngineOnly() + { + var container = new VehicleContainer(); - var cycleData = DrivingCycleData.ReadFromFileEngineOnly(@"TestData\Cycles\Coach Engine Only.vdri"); - var cycle = new EngineOnlyDrivingCycle(container, cycleData); + var cycleData = DrivingCycleData.ReadFromFileEngineOnly(@"TestData\Cycles\Coach Engine Only.vdri"); + var cycle = new EngineOnlyDrivingCycle(container, cycleData); - var outPort = new MockTnOutPort(); - var inPort = cycle.InShaft(); - var cycleOut = cycle.OutPort(); + var outPort = new MockTnOutPort(); + var inPort = cycle.InShaft(); + var cycleOut = cycle.OutPort(); - inPort.Connect(outPort); + inPort.Connect(outPort); - var absTime = new TimeSpan(); - var dt = TimeSpan.FromSeconds(1); + var absTime = new TimeSpan(); + var dt = TimeSpan.FromSeconds(1); - var response = cycleOut.Request(absTime, dt); - Assert.IsInstanceOfType(response, typeof (ResponseSuccess)); + var response = cycleOut.Request(absTime, dt); + Assert.IsInstanceOfType(response, typeof (ResponseSuccess)); - var dataWriter = new TestModalDataWriter(); - container.CommitSimulationStep(dataWriter); + var dataWriter = new TestModalDataWriter(); + container.CommitSimulationStep(dataWriter); - Assert.AreEqual(absTime, outPort.AbsTime); - Assert.AreEqual(dt, outPort.Dt); - Assert.AreEqual(600.0.RPMtoRad(), outPort.AngularFrequency); - Assert.AreEqual(0.SI<NewtonMeter>(), outPort.Torque); - } + Assert.AreEqual(absTime, outPort.AbsTime); + Assert.AreEqual(dt, outPort.Dt); + Assert.AreEqual(600.0.RPMtoRad(), outPort.AngularVelocity); + Assert.AreEqual(0.SI<NewtonMeter>(), outPort.Torque); + } - [TestMethod] - public void Test_TimeBased_FirstCycle() - { - var container = new VehicleContainer(); + [TestMethod] + public void Test_TimeBased_FirstCycle() + { + var container = new VehicleContainer(); - var cycleData = DrivingCycleData.ReadFromFileTimeBased(@"TestData\Cycles\Coach time based.vdri"); - var cycle = new TimeBasedDrivingCycle(container, cycleData); + var cycleData = DrivingCycleData.ReadFromFileTimeBased(@"TestData\Cycles\Coach time based.vdri"); + var cycle = new TimeBasedDrivingCycle(container, cycleData); - var outPort = new MockDriverDemandOutPort(); + var outPort = new MockDriverDemandOutPort(); - var inPort = cycle.InPort(); - var cycleOut = cycle.OutPort(); + var inPort = cycle.InPort(); + var cycleOut = cycle.OutPort(); - inPort.Connect(outPort); + inPort.Connect(outPort); - var absTime = new TimeSpan(); - var dt = TimeSpan.FromSeconds(1); + var absTime = new TimeSpan(); + var dt = TimeSpan.FromSeconds(1); - var response = cycleOut.Request(absTime, dt); - Assert.IsInstanceOfType(response, typeof (ResponseSuccess)); + var response = cycleOut.Request(absTime, dt); + Assert.IsInstanceOfType(response, typeof (ResponseSuccess)); - Assert.AreEqual(absTime, outPort.AbsTime); - Assert.AreEqual(dt, outPort.Dt); - Assert.AreEqual(0.0.SI<MeterPerSecond>(), outPort.Velocity); - Assert.AreEqual((-0.020237973).SI().GradientPercent.Cast<Radian>(), outPort.Gradient); - } + Assert.AreEqual(absTime, outPort.AbsTime); + Assert.AreEqual(dt, outPort.Dt); + Assert.AreEqual(0.0.SI<MeterPerSecond>(), outPort.Velocity); + Assert.AreEqual((-0.020237973).SI().GradientPercent.Cast<Radian>(), outPort.Gradient); + } - [TestMethod] - public void Test_TimeBased_TimeFieldMissing() - { - var container = new VehicleContainer(); + [TestMethod] + public void Test_TimeBased_TimeFieldMissing() + { + var container = new VehicleContainer(); - var cycleData = DrivingCycleData.ReadFromFileTimeBased(@"TestData\Cycles\Cycle time field missing.vdri"); - var cycle = new TimeBasedDrivingCycle(container, cycleData); + var cycleData = DrivingCycleData.ReadFromFileTimeBased(@"TestData\Cycles\Cycle time field missing.vdri"); + var cycle = new TimeBasedDrivingCycle(container, cycleData); - var outPort = new MockDriverDemandOutPort(); + var outPort = new MockDriverDemandOutPort(); - var inPort = cycle.InPort(); - var cycleOut = cycle.OutPort(); + var inPort = cycle.InPort(); + var cycleOut = cycle.OutPort(); - inPort.Connect(outPort); + inPort.Connect(outPort); - var dataWriter = new TestModalDataWriter(); - var absTime = new TimeSpan(); - var dt = TimeSpan.FromSeconds(1); + var dataWriter = new TestModalDataWriter(); + var absTime = new TimeSpan(); + var dt = TimeSpan.FromSeconds(1); - while (cycleOut.Request(absTime, dt) is ResponseSuccess) { - Assert.AreEqual(absTime, outPort.AbsTime); - Assert.AreEqual(dt, outPort.Dt); - container.CommitSimulationStep(dataWriter); + while (cycleOut.Request(absTime, dt) is ResponseSuccess) { + Assert.AreEqual(absTime, outPort.AbsTime); + Assert.AreEqual(dt, outPort.Dt); + container.CommitSimulationStep(dataWriter); - absTime += dt; - } - } - } + absTime += dt; + } + } + } } \ No newline at end of file diff --git a/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs b/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs index 9ba69f9cdf8ea0d23c3826618a656bc0c731252b..dd0e4772335baa21345738d91082a6de59a9d8d9 100644 --- a/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs +++ b/VectoCoreTest/Models/SimulationComponent/CombustionEngineTest.cs @@ -65,7 +65,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var absTime = new TimeSpan(seconds: 0, minutes: 0, hours: 0); var dt = new TimeSpan(seconds: 1, minutes: 0, hours: 0); - var torque = new NewtonMeter(); + var torque = 0.SI<NewtonMeter>(); var engineSpeed = 600.0.RPMtoRad(); var dataWriter = new TestModalDataWriter(); diff --git a/VectoCoreTest/Models/SimulationComponent/MockPorts.cs b/VectoCoreTest/Models/SimulationComponent/MockPorts.cs index 46bd7188f3a2b00464b48a1c29a02e387a710530..d0f5b37d5036f6495ec5591da8cf7b89e8a1c5a6 100644 --- a/VectoCoreTest/Models/SimulationComponent/MockPorts.cs +++ b/VectoCoreTest/Models/SimulationComponent/MockPorts.cs @@ -6,41 +6,41 @@ using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Tests.Models.SimulationComponent { - public class MockTnOutPort : ITnOutPort - { - public TimeSpan AbsTime { get; set; } - public TimeSpan Dt { get; set; } - public NewtonMeter Torque { get; set; } - public RadianPerSecond AngularFrequency { get; set; } + public class MockTnOutPort : ITnOutPort + { + public TimeSpan AbsTime { get; set; } + public TimeSpan Dt { get; set; } + public NewtonMeter Torque { get; set; } + public PerSecond AngularVelocity { get; set; } - public IResponse Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, RadianPerSecond angularFrequency) - { - AbsTime = absTime; - Dt = dt; - Torque = torque; - AngularFrequency = angularFrequency; - LogManager.GetLogger(GetType()).DebugFormat("Request: absTime: {0}, dt: {1}, torque: {3}, engineSpeed: {4}", - absTime, dt, torque, angularFrequency); - return new ResponseSuccess(); - } - } + public IResponse Request(TimeSpan absTime, TimeSpan dt, NewtonMeter torque, PerSecond angularVelocity) + { + AbsTime = absTime; + Dt = dt; + Torque = torque; + AngularVelocity = angularVelocity; + LogManager.GetLogger(GetType()).DebugFormat("Request: absTime: {0}, dt: {1}, torque: {3}, engineSpeed: {4}", + absTime, dt, torque, angularVelocity); + return new ResponseSuccess(); + } + } - public class MockDriverDemandOutPort : IDriverDemandOutPort - { - public TimeSpan AbsTime { get; set; } - public TimeSpan Dt { get; set; } - public MeterPerSecond Velocity { get; set; } - public Radian Gradient { get; set; } + public class MockDriverDemandOutPort : IDriverDemandOutPort + { + public TimeSpan AbsTime { get; set; } + public TimeSpan Dt { get; set; } + public MeterPerSecond Velocity { get; set; } + public Radian Gradient { get; set; } - public IResponse Request(TimeSpan absTime, TimeSpan dt, MeterPerSecond velocity, Radian gradient) - { - AbsTime = absTime; - Dt = dt; - Velocity = velocity; - Gradient = gradient; - LogManager.GetLogger(GetType()).DebugFormat("Request: absTime: {0}, dt: {1}, velocity: {3}, gradient: {4}", - absTime, dt, velocity, gradient); - return new ResponseSuccess(); - } - } + public IResponse Request(TimeSpan absTime, TimeSpan dt, MeterPerSecond velocity, Radian gradient) + { + AbsTime = absTime; + Dt = dt; + Velocity = velocity; + Gradient = gradient; + LogManager.GetLogger(GetType()).DebugFormat("Request: absTime: {0}, dt: {1}, velocity: {3}, gradient: {4}", + absTime, dt, velocity, gradient); + return new ResponseSuccess(); + } + } } \ No newline at end of file diff --git a/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs b/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs index 7f0de7ba188499ef119b8e5b93fa57dfff8ba21a..ccf538dc5bb6b319518905b68208073c59924226 100644 --- a/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs +++ b/VectoCoreTest/Models/SimulationComponentData/FullLoadCurveTest.cs @@ -94,7 +94,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData public void Test_FileRead_NoHeader() { var curve = FullLoadCurve.ReadFromFile(@"TestData\Components\FullLoadCurve no header.vfld"); - var result = curve.FullLoadStationaryTorque(1.SI<RadianPerSecond>()); + var result = curve.FullLoadStationaryTorque(1.SI<PerSecond>()); Assert.AreNotEqual((double) result, 0.0); } diff --git a/VectoCoreTest/Utils/DoubleExtensionMethodTest.cs b/VectoCoreTest/Utils/DoubleExtensionMethodTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..21d802107dedb390e230f7ed1e36d8b7e2d2525e --- /dev/null +++ b/VectoCoreTest/Utils/DoubleExtensionMethodTest.cs @@ -0,0 +1,29 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Tests.Utils +{ + [TestClass] + public class DoubleExtensionMethodTest + { + [TestMethod] + public void DoubleExtensions_SI_Test() + { + var val = 600.0.RPMtoRad(); + Assert.AreEqual(600 / 60 * 2 * Math.PI, val.Double()); + + Assert.IsTrue(0.0.SI<PerSecond>().HasEqualUnit(val)); + + var val2 = 1200.0.SI().Rounds.Per.Minute.ConvertTo().Radian.Per.Second.Cast<PerSecond>(); + val = val * 2; + Assert.AreEqual(val, val2); + + val2 = val2 / 2; + val = val / 2; + Assert.AreEqual(val, val2); + Assert.AreEqual(600.SI().Rounds.Per.Minute.Cast<PerSecond>(), val2); + Assert.AreEqual(600.SI().Rounds.Per.Minute.Cast<PerSecond>().Double(), val2.Double()); + } + } +} \ No newline at end of file diff --git a/VectoCoreTest/Utils/SITest.cs b/VectoCoreTest/Utils/SITest.cs index f14296368771e870f3426150a79ad36f87f7a3bd..8f989c45390a273c599cc07243fc82d4786901b3 100644 --- a/VectoCoreTest/Utils/SITest.cs +++ b/VectoCoreTest/Utils/SITest.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.TestTools.UnitTesting; using TUGraz.VectoCore.Utils; @@ -21,7 +22,7 @@ namespace TUGraz.VectoCore.Tests.Utils public void TestSI() { var si = new SI(); - Assert.AreEqual(0.0, (double) si); + Assert.AreEqual(0.0, si.Double()); Assert.AreEqual("0 [-]", si.ToString()); Assert.IsTrue(si.HasEqualUnit(new SI())); @@ -29,7 +30,7 @@ namespace TUGraz.VectoCore.Tests.Utils Assert.AreEqual("5 [W]", si2.ToString()); var si3 = 2.SI().Radian.Per.Second; - Assert.AreEqual("2 [rad/s]", si3.ToString()); + Assert.AreEqual("2 [1/s]", si3.ToString()); var si4 = si2 * si3; Assert.AreEqual("10 [W/s]", si4.ToString()); @@ -38,15 +39,15 @@ namespace TUGraz.VectoCore.Tests.Utils var kg = 5.0.SI().Kilo.Gramm; - Assert.AreEqual(5.0, (double) kg); + Assert.AreEqual(5.0, kg.Double()); Assert.AreEqual("5 [kg]", kg.ToString()); kg = kg.ConvertTo().Kilo.Gramm.Value(); - Assert.AreEqual(5.0, (double) kg); + Assert.AreEqual(5.0, kg.Double()); Assert.AreEqual("5 [kg]", kg.ToString()); kg = kg.ConvertTo().Gramm.Value(); - Assert.AreEqual(5000, (double) kg); + Assert.AreEqual(5000, kg.Double()); Assert.AreEqual("5000 [g]", kg.ToString()); var x = 5.SI(); @@ -64,5 +65,35 @@ namespace TUGraz.VectoCore.Tests.Utils var y = 2.SI(); Assert.AreEqual((2 * 5).SI(), y * x); } + + [TestMethod] + [SuppressMessage("ReSharper", "SuggestVarOrType_SimpleTypes")] + public void SITests_Addition_Subtraction() + { + var v1 = 600.SI<NewtonMeter>(); + var v2 = 455.SI<NewtonMeter>(); + NewtonMeter v3 = v1 + v2; + + NewtonMeter v4 = v1 - v2; + + var v5 = v1 * v2; + Assert.IsTrue(v5.HasEqualUnit(0.SI().Square.Newton.Meter)); + Assert.AreEqual(v1.Double() * v2.Double(), v5.Double()); + + var v6 = v1 / v2; + Assert.IsTrue(v6.HasEqualUnit(0.SI())); + Assert.AreEqual(v1.Double() / v2.Double(), v6.Double()); + + var t = 10.SI<NewtonMeter>(); + var angVelo = 5.SI<PerSecond>(); + + Watt w = t * angVelo; + Watt w1 = angVelo * t; + + NewtonMeter t1 = w / angVelo; + + PerSecond angVelo1 = w / t; + Second sec = t / w; + } } } \ No newline at end of file diff --git a/VectoCoreTest/Utils/VectoMathTest.cs b/VectoCoreTest/Utils/VectoMathTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..2c163c8e67055adf5aba109fb75ee29ec58e5c41 --- /dev/null +++ b/VectoCoreTest/Utils/VectoMathTest.cs @@ -0,0 +1,37 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TUGraz.VectoCore.Utils; + +namespace TUGraz.VectoCore.Tests.Utils +{ + [TestClass] + public class VectoMathTest + { + [TestMethod] + public void VectoMath_Min() + { + var smaller = 0.SI(); + var bigger = 5.SI(); + var negative = -10.SI(); + var positive = 10.SI(); + Assert.AreEqual(smaller, VectoMath.Min(smaller, bigger)); + + Assert.AreEqual(bigger, VectoMath.Max(smaller, bigger)); + + Assert.AreEqual(positive, VectoMath.Abs(negative)); + Assert.AreEqual(positive, VectoMath.Abs(positive)); + + + var smallerWatt = 0.SI<Watt>(); + var biggerWatt = 5.SI<Watt>(); + var negativeWatt = -10.SI<Watt>(); + var positiveWatt = 10.SI<Watt>(); + Assert.AreEqual(smallerWatt, VectoMath.Min(smallerWatt, biggerWatt)); + + Assert.AreEqual(biggerWatt, VectoMath.Max(smallerWatt, biggerWatt)); + + + Assert.AreEqual(positiveWatt, VectoMath.Abs(negativeWatt)); + Assert.AreEqual(positiveWatt, VectoMath.Abs(positiveWatt)); + } + } +} \ No newline at end of file diff --git a/VectoCoreTest/VectoCoreTest.csproj b/VectoCoreTest/VectoCoreTest.csproj index 1d6412df1d2f694efb63bbd900b0c89461604ef6..ed4d8f934625633628f7390b38ab1221e5a17596 100644 --- a/VectoCoreTest/VectoCoreTest.csproj +++ b/VectoCoreTest/VectoCoreTest.csproj @@ -86,6 +86,8 @@ <Compile Include="Utils\SITest.cs" /> <Compile Include="Utils\DelauneyMapTest.cs" /> <Compile Include="Utils\TestModalDataWriter.cs" /> + <Compile Include="Utils\DoubleExtensionMethodTest.cs" /> + <Compile Include="Utils\VectoMathTest.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\VectoCore\VectoCore.csproj">