diff --git a/VectoCore/VectoCore/InputData/Reader/ComponentData/ElectricMotorMapReader.cs b/VectoCore/VectoCore/InputData/Reader/ComponentData/ElectricMotorMapReader.cs index 7b819b5d8a8deb2ef6724dfbc58c88fd5b2c6bed..3147743e01eac91690ebbaed4f2221cdfad11772 100644 --- a/VectoCore/VectoCore/InputData/Reader/ComponentData/ElectricMotorMapReader.cs +++ b/VectoCore/VectoCore/InputData/Reader/ComponentData/ElectricMotorMapReader.cs @@ -11,6 +11,69 @@ using TUGraz.VectoCore.Models.SimulationComponent.Data.Engine; using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.InputData.Reader.ComponentData { + + public static class ElectricMotorMapReaderNew + { + public static EfficiencyMap Create(Stream data, int count) + { + return Create(VectoCSVFile.ReadStream(data), count); + } + + public static EfficiencyMap Create(DataTable data, int count) + { + var headerValid = HeaderIsValid(data.Columns); + if (!headerValid) { + LoggingObject.Logger<FuelConsumptionMap>().Warn( + "Efficiency Map: Header Line is not valid. Expected: '{0}, {1}, {2}', Got: {3}. Falling back to column index.", + Fields.MotorSpeed, + Fields.Torque, + Fields.PowerElectrical, + data.Columns.Cast<DataColumn>().Select(c => c.ColumnName).Join()); + data.Columns[0].ColumnName = Fields.MotorSpeed; + data.Columns[1].ColumnName = Fields.Torque; + data.Columns[2].ColumnName = Fields.PowerElectrical; + } + var delaunayMap = new DelaunayMap("ElectricMotorEfficiencyMap Mechanical to Electric"); + var retVal = new EfficiencyMapNew(delaunayMap); + foreach (DataRow row in data.Rows) { + try { + var entry = CreateEntry(row); + delaunayMap.AddPoint(-entry.Torque.Value() * count, + entry.MotorSpeed.Value(), + retVal.GetDelaunayZValue(entry) * count); + } catch (Exception e) { + throw new VectoException($"EfficiencyMap - Line {data.Rows.IndexOf(row)}: {e.Message}", e); + } + } + + delaunayMap.Triangulate(); + return retVal; + } + + private static EfficiencyMap.Entry CreateEntry(DataRow row) + { + return new EfficiencyMap.Entry( + speed: row.ParseDouble(Fields.MotorSpeed).RPMtoRad(), + torque: row.ParseDouble(Fields.Torque).SI<NewtonMeter>(), + powerElectrical: row.ParseDouble(Fields.PowerElectrical).SI(Unit.SI.Kilo.Watt).Cast<Watt>()); + } + + + private static bool HeaderIsValid(DataColumnCollection columns) + { + return columns.Contains(Fields.MotorSpeed) && columns.Contains(Fields.Torque) && + columns.Contains(Fields.PowerElectrical); + } + + public static class Fields + { + public const string MotorSpeed = "n"; + public const string Torque = "T"; + public const string PowerElectrical = "P_el"; + } + } + + public static class ElectricMotorMapReader { public static EfficiencyMap Create(Stream data, int count) @@ -39,15 +102,9 @@ namespace TUGraz.VectoCore.InputData.Reader.ComponentData { try { var entry = CreateEntry(row); - if (entry.Torque.IsGreaterOrEqual(0)) { - delaunayMap.AddPoint(-entry.Torque.Value() * count, - entry.MotorSpeed.Value(), - -entry.PowerElectrical.Value() * count); - } else { - delaunayMap.AddPoint(-entry.Torque.Value() * count, - entry.MotorSpeed.Value(), - -entry.PowerElectrical.Value() * count); - } + delaunayMap.AddPoint(-entry.Torque.Value() * count, + entry.MotorSpeed.Value(), + -entry.PowerElectrical.Value() * count); } catch (Exception e) { diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/EfficiencyMap.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/EfficiencyMap.cs index ad4a03b385679a4282cdb8bd174029bbea5644db..bc3e3ec043b722e9bd3f6e50f489865b0aa8af24 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/EfficiencyMap.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/EfficiencyMap.cs @@ -9,9 +9,41 @@ using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Models.SimulationComponent.Data.ElectricMotor { + + public class EfficiencyMapNew : EfficiencyMap + { + private static readonly PerSecond speedOffset = 0.01.RPMtoRad(); + protected internal EfficiencyMapNew(DelaunayMap efficiencyMapMech2El) : base(efficiencyMapMech2El) { } + + public override EfficiencyResult LookupElectricPower(PerSecond angularSpeed, NewtonMeter torque, bool allowExtrapolation = false) + { + var result = new EfficiencyResult(); + result.Torque = torque; + result.Speed = angularSpeed; + var value = _efficiencyMapMech2El.Interpolate(torque, angularSpeed); + if (!value.IsNaN()) { + result.ElectricalPower = value.SI<NewtonMeter>() * (angularSpeed + speedOffset) + angularSpeed * torque; + return result; + } + if (allowExtrapolation) { + value = _efficiencyMapMech2El.Extrapolate(torque, angularSpeed); + result.ElectricalPower = value.SI<NewtonMeter>() * (angularSpeed + speedOffset) + angularSpeed * torque; + result.Extrapolated = true; + return result; + } + return result; + } + + public double GetDelaunayZValue(Entry entry) + { + return - ((entry.PowerElectrical - entry.MotorSpeed * entry.Torque) / + (entry.MotorSpeed + EfficiencyMapNew.speedOffset)).Value(); + } + } + public class EfficiencyMap : LoggingObject { - private readonly DelaunayMap _efficiencyMapMech2El; + protected readonly DelaunayMap _efficiencyMapMech2El; private PerSecond _maxSpeed; protected internal EfficiencyMap(DelaunayMap efficiencyMapMech2El) @@ -20,7 +52,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.ElectricMotor } - public EfficiencyResult LookupElectricPower(PerSecond angularSpeed, NewtonMeter torque, bool allowExtrapolation = false) + public virtual EfficiencyResult LookupElectricPower(PerSecond angularSpeed, NewtonMeter torque, bool allowExtrapolation = false) { var result = new EfficiencyResult(); result.Torque = torque; @@ -40,34 +72,6 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.ElectricMotor return result; } - public EfficiencyResult SearchMechanicalPower(Watt electricPower, PerSecond angularSpeed, - bool allowExtrapolation = false) - { - if (electricPower.IsEqual(0)) - { - return new EfficiencyResult - { - ElectricalPower = electricPower, - Speed = angularSpeed, - Torque = 0.SI<NewtonMeter>() - }; - } - var torque = electricPower / angularSpeed; - var response = LookupElectricPower(angularSpeed, torque, true); - var delta = response.ElectricalPower - electricPower; - torque = SearchAlgorithm.Search(torque, delta, torque * 0.1, - getYValue: result => ((EfficiencyResult)result).ElectricalPower - electricPower, - evaluateFunction: x => LookupElectricPower(angularSpeed, x, true), - criterion: result => (((EfficiencyResult)result).ElectricalPower - electricPower).Value()); - - return new EfficiencyResult - { - ElectricalPower = electricPower, - Speed = angularSpeed, - Torque = torque - }; - } - public string[] SerializedEntries { get { return _efficiencyMapMech2El.Entries.Select( diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs index ddd51308dc06011661cdd1c0595ae16d90034920..79f76c00a893cd75fa0965ea3240f9af8663f334 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/CombustionEngine.cs @@ -369,11 +369,11 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected virtual PerSecond GetEngineSpeedLimit(Second absTime) { - if (DataBus.GearboxInfo.Gear.Gear == 0 || !DataBus.ClutchInfo.ClutchClosed(absTime) || !DataBus.GearboxInfo.TCLocked) { + if (DataBus.GearboxInfo.Gear.Gear == 0 || !DataBus.ClutchInfo.ClutchClosed(absTime)) { return ModelData.FullLoadCurves[0].N95hSpeed; } - return VectoMath.Min(DataBus.GearboxInfo.GetGearData(DataBus.GearboxInfo.Gear.Gear).MaxSpeed, ModelData.FullLoadCurves[0].N95hSpeed); + return VectoMath.Min(DataBus.GearboxInfo.GetGearData(DataBus.GearboxInfo.Gear.Gear)?.MaxSpeed, ModelData.FullLoadCurves[0].N95hSpeed); } public virtual IResponse Initialize(NewtonMeter outTorque, PerSecond outAngularVelocity) diff --git a/VectoCore/VectoCoreTest/Models/SimulationComponentData/ElectricMotorEfficienyMapTest.cs b/VectoCore/VectoCoreTest/Models/SimulationComponentData/ElectricMotorEfficienyMapTest.cs index 02fb6a03f74f114462d8370efd1ade46096dc254..52e5fed8d3b78c39ae5a28e3db559903413a2251 100644 --- a/VectoCore/VectoCoreTest/Models/SimulationComponentData/ElectricMotorEfficienyMapTest.cs +++ b/VectoCore/VectoCoreTest/Models/SimulationComponentData/ElectricMotorEfficienyMapTest.cs @@ -1,9 +1,13 @@ -using System.IO; +using System; +using System.Data; +using System.IO; using System.Linq; using NUnit.Framework; using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.InputData.FileIO.JSON; using TUGraz.VectoCore.InputData.Reader.ComponentData; +using TUGraz.VectoCore.Models.SimulationComponent.Data.ElectricMotor; +using TUGraz.VectoCore.Utils; namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData { [TestFixture] @@ -66,5 +70,96 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponentData { Assert.IsNull(tq); } + + [TestCase(@"E:\QUAM\Downloads\VECTO-1437_E2_EM-inv_Interpolation\VECTO_model\vem_P_inverter_DC_90.vemo")] + public void TestInterpolationMethod_Proposal(string filename) + { + var speedOffset = 0.01.RPMtoRad(); + + var tbl = VectoCSVFile.Read(filename); + var delaunayMap = new DelaunayMap("ElectricMotorEfficiencyMap Test"); + foreach (DataRow row in tbl.Rows) { + var entry = new EfficiencyMap.Entry( + speed: row.ParseDouble("n").RPMtoRad(), + torque: row.ParseDouble("T").SI<NewtonMeter>(), + powerElectrical: row.ParseDouble("P_el").SI(Unit.SI.Kilo.Watt).Cast<Watt>()); + + delaunayMap.AddPoint(-entry.Torque.Value(), + (entry.MotorSpeed).Value(), + -((entry.PowerElectrical - entry.MotorSpeed * entry.Torque) / (entry.MotorSpeed + speedOffset)).Value()); + } + + delaunayMap.Triangulate(); + var emMap = new EfficiencyMap(delaunayMap); + + for (var n = 10.RPMtoRad(); n < 4000.RPMtoRad(); n += 10.RPMtoRad()) { + for (var tq = -2800.SI<NewtonMeter>(); tq <= 2800.SI<NewtonMeter>(); tq += 100.SI<NewtonMeter>()) { + if (tq.IsEqual(0)) { + continue; + } + try { + var pwr = emMap.LookupElectricPower(n, tq); + if (pwr.ElectricalPower == null) { + continue; + } + Console.WriteLine($"{pwr.Speed.AsRPM}, {pwr.Torque.Value()}, {((pwr.ElectricalPower.Value() * (pwr.Speed + speedOffset).Value()).SI<Watt>() + pwr.Speed * pwr.Torque).Value()}, {pwr.Extrapolated}, {(n * tq).Value()}"); + } catch (Exception e) { + Console.WriteLine(e.Message); + } + } + } + } + + [TestCase(@"E:\QUAM\Downloads\VECTO-1437_E2_EM-inv_Interpolation\VECTO_model\vem_P_inverter_DC_90.vemo")] + public void TestInterpolationMethod_Proposal2(string filename) + { + EfficiencyMap emMap; + using (var fs = File.OpenRead(filename)) { + emMap = ElectricMotorMapReaderNew.Create(fs, 1); + } + + for (var n = 10.RPMtoRad(); n < 4000.RPMtoRad(); n += 10.RPMtoRad()) { + for (var tq = -2800.SI<NewtonMeter>(); tq <= 2800.SI<NewtonMeter>(); tq += 100.SI<NewtonMeter>()) { + if (tq.IsEqual(0)) { + continue; + } + try { + var pwr = emMap.LookupElectricPower(n, tq); + if (pwr.ElectricalPower == null) { + continue; + } + Console.WriteLine($"{pwr.Speed.AsRPM}, {pwr.Torque.Value()}, {pwr.ElectricalPower.Value()}, {pwr.Extrapolated}, {(n * tq).Value()}"); + } catch (Exception e) { + Console.WriteLine(e.Message); + } + } + } + } + + [TestCase(@"E:\QUAM\Downloads\VECTO-1437_E2_EM-inv_Interpolation\VECTO_model\vem_P_inverter_DC_90.vemo")] + public void TestInterpolationMethod_Current(string filename) + { + EfficiencyMap emMap; + using (var fs = File.OpenRead(filename)) { + emMap = ElectricMotorMapReader.Create(fs, 1); + } + + for (var n = 10.RPMtoRad(); n < 4000.RPMtoRad(); n += 10.RPMtoRad()) { + for (var tq = -2800.SI<NewtonMeter>(); tq <= 2800.SI<NewtonMeter>(); tq += 100.SI<NewtonMeter>()) { + if (tq.IsEqual(0)) { + continue; + } + try { + var pwr = emMap.LookupElectricPower(n, tq); + if (pwr.ElectricalPower == null) { + continue; + } + Console.WriteLine($"{pwr.Speed.AsRPM}, {pwr.Torque.Value()}, {pwr.ElectricalPower.Value()}, {pwr.Extrapolated}, {(n * tq).Value()}"); + } catch (Exception e) { + Console.WriteLine(e.Message); + } + } + } + } } } \ No newline at end of file