diff --git a/VectoCore/VectoCore/InputData/Reader/ComponentData/ElectricMotorMapReader.cs b/VectoCore/VectoCore/InputData/Reader/ComponentData/ElectricMotorMapReader.cs index 11a6fa9eb43056ee72fa505a125de89467736788..b664b31c50cbaf2e8448f2dfdf0c3ff73c783076 100644 --- a/VectoCore/VectoCore/InputData/Reader/ComponentData/ElectricMotorMapReader.cs +++ b/VectoCore/VectoCore/InputData/Reader/ComponentData/ElectricMotorMapReader.cs @@ -36,16 +36,19 @@ namespace TUGraz.VectoCore.InputData.Reader.ComponentData { } var entries = (from DataRow row in data.Rows select CreateEntry(row)).ToList(); - - var speeds = entries.Select(x => x.MotorSpeed).Distinct().Where(x => x.IsGreater(0)).ToList(); - var minSpeed = speeds.OrderBy(x => x).First(); - var torques = entries.Where(x => x.MotorSpeed.IsEqual(minSpeed)).ToList(); + var entriesZero = GetEntriesAtZeroRpm(entries); var delaunayMap = new DelaunayMap("ElectricMotorEfficiencyMap Mechanical to Electric"); var retVal = new EfficiencyMapNew(delaunayMap); - foreach (var entry in torques) { - delaunayMap.AddPoint(-entry.Torque.Value() * count, - 0, retVal.GetDelaunayZValue(entry)); + + foreach (var entry in entriesZero.OrderBy(x => x.Torque)) { + try { + delaunayMap.AddPoint(-entry.Torque.Value() * count, + 0, + retVal.GetDelaunayZValue(entry) * count); + } catch (Exception e) { + throw new VectoException($"EfficiencyMap - Entry {entry}: {e.Message}", e); + } } foreach (var entry in entries.Where(x => x.MotorSpeed.IsGreater(0)).OrderBy(x => x.MotorSpeed) @@ -63,6 +66,65 @@ namespace TUGraz.VectoCore.InputData.Reader.ComponentData { return retVal; } + private static List<EfficiencyMap.Entry> GetEntriesAtZeroRpm(List<EfficiencyMap.Entry> entries) + { + // find entries at first grid point above 0. em-speed might vary slightly, + // so apply clustering, and use distance between first clusters to select all entries at lowest speed grid point + + const int numEntriesExtrapolationFitting = 4; + + var speeds = new MeanShiftClustering(){ClusterCount = 100}.FindClusters(entries.Select(x => x.MotorSpeed.AsRPM).ToArray(), 10) + .Where(x => x > 0).ToList(); + var lowerSpeed = speeds.First().RPMtoRad() / 2.0; + var upperSpeed = speeds.First().RPMtoRad() + (speeds[1] - speeds.First()).RPMtoRad() / 2.0; + + //entries at lowest speed gridpoint + var torquesMinRpm = entries.Where(x => x.MotorSpeed.IsBetween(lowerSpeed, upperSpeed)).OrderBy(x => x.Torque).ToList(); + // entries at 0 rpm grid point + var torquesZeroRpm = entries.Where(x => x.MotorSpeed.IsEqual(0)).OrderBy(x => x.Torque).ToList(); + + + var entriesZero = new List<EfficiencyMap.Entry>(); + var avgSpeed = torquesMinRpm.Average(x => x.MotorSpeed.Value()).SI<PerSecond>(); + var torquesZeroMin = torquesZeroRpm.Min(x => x.Torque); + // if at 0 rpm a torque entry below the min torque at min speed is present, extrapolate to this torque + if (torquesZeroMin.IsSmaller(torquesMinRpm.Min(x => x.Torque))) { + // extrapolate entry at 0 rpm with min torque + var negTorque = torquesMinRpm.Where(x => x.Torque <= 0).OrderBy(x => x.Torque).ToList(); + if (negTorque.Count < 2) { + throw new VectoException( + "Failed to generate electrip power map - at least two negative entries are required"); + } + + var (k, d) = VectoMath.LeastSquaresFitting(negTorque.Take(numEntriesExtrapolationFitting), x => x.Torque.Value(), + x => x.PowerElectrical.Value()); + var extrapolatedPwr = (torquesZeroMin.Value() * k + d).SI<Watt>(); + entriesZero.Add(new EfficiencyMap.Entry(avgSpeed, torquesZeroMin, extrapolatedPwr)); + } + // copy all entries in-between + foreach (var entry in torquesMinRpm) { + entriesZero.Add(new EfficiencyMap.Entry(avgSpeed, entry.Torque, entry.PowerElectrical)); + } + + // if at 0 rpm a torque entry above the max torqe at min speed is present, extrapolate to this torque + var torquesZeroMax = torquesZeroRpm.Max(x => x.Torque); + if (torquesZeroMax.IsGreater(torquesMinRpm.Max(x => x.Torque))) { + // extrapolate entry at 0 rpm with max torque + var posTorque = torquesMinRpm.Where(x => x.Torque >= 0).OrderBy(x => x.Torque).Reverse().ToList(); + if (posTorque.Count < 2) { + throw new VectoException( + "Failed to generate electrip power map - at least two positive entries are required"); + } + + var (k, d) = VectoMath.LeastSquaresFitting(posTorque.Take(numEntriesExtrapolationFitting), x => x.Torque.Value(), + x => x.PowerElectrical.Value()); + var extrapolatedPwr = (torquesZeroMax.Value() * k + d).SI<Watt>(); + entriesZero.Add(new EfficiencyMap.Entry(avgSpeed, torquesZeroRpm.Max(x => x.Torque), extrapolatedPwr)); + } + + return entriesZero; + } + private static EfficiencyMap.Entry CreateEntry(DataRow row) { return new EfficiencyMap.Entry( diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/EfficiencyMap.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/EfficiencyMap.cs index d6f5cdc1164d2dcc14dcf7d115914fe53a4fc886..823cc7d8563d7e63eb422fc1e24d4004ce94d7d0 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/EfficiencyMap.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/EfficiencyMap.cs @@ -145,7 +145,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data.ElectricMotor return null; } - if (avgSpeed.IsGreaterOrEqual(MaxSpeed)) { + if (avgSpeed.IsEqual(0.RPMtoRad()) || avgSpeed.IsGreaterOrEqual(MaxSpeed)) { return 0.SI<NewtonMeter>(); } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/ElectricMotorData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/ElectricMotorData.cs index b10be1b872608b53624e211d9141f6e023087fbf..1a276f55493eb561a0713ec4ffbbd2ded564b986 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/ElectricMotorData.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/ElectricMotorData.cs @@ -60,7 +60,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public NewtonMeter EfficiencyMapLookupTorque(Volt voltage, Watt electricPower, PerSecond avgSpeed, NewtonMeter maxEmTorque) { - if (avgSpeed.IsGreaterOrEqual(MaxSpeed)) { + if (avgSpeed.IsEqual(0.RPMtoRad()) || avgSpeed.IsGreaterOrEqual(MaxSpeed)) { return 0.SI<NewtonMeter>(); } var (a, b) = GetSection(voltage); diff --git a/VectoCore/VectoCoreTest/FileIO/JsonReadHybridTest.cs b/VectoCore/VectoCoreTest/FileIO/JsonReadHybridTest.cs index ea60ff7aae91bd7d6fd9a574374cdbc49e299a99..b28414a2301194d355efe0067c310c09c1886631 100644 --- a/VectoCore/VectoCoreTest/FileIO/JsonReadHybridTest.cs +++ b/VectoCore/VectoCoreTest/FileIO/JsonReadHybridTest.cs @@ -200,7 +200,7 @@ namespace TUGraz.VectoCore.Tests.FileIO var pwrMap = ElectricMotorMapReader.Create(pwr, 2); Assert.AreEqual(0, pwrMap.LookupElectricPower(-0.RPMtoRad(), -800.SI<NewtonMeter>()).ElectricalPower.Value(), 1e-3); - Assert.AreEqual(-147.2105, pwrMap.LookupElectricPower(1.RPMtoRad(), -800.SI<NewtonMeter>()).ElectricalPower.Value(), 1e-3); + Assert.AreEqual(-208.04255, pwrMap.LookupElectricPower(1.RPMtoRad(), -800.SI<NewtonMeter>()).ElectricalPower.Value(), 1e-3); Assert.AreEqual(-16412.2624, pwrMap.LookupElectricPower(120.RPMtoRad(), -800.SI<NewtonMeter>()).ElectricalPower.Value(), 1e-3);