diff --git a/VectoCore/VectoCore/InputData/FileIO/JSON/JSONInputDataFactory.cs b/VectoCore/VectoCore/InputData/FileIO/JSON/JSONInputDataFactory.cs index 656b79d22c4e4872186d4753210265fac1bcb168..075fe71dfeb1469afd34c3c1726f467cf96e1901 100644 --- a/VectoCore/VectoCore/InputData/FileIO/JSON/JSONInputDataFactory.cs +++ b/VectoCore/VectoCore/InputData/FileIO/JSON/JSONInputDataFactory.cs @@ -211,6 +211,8 @@ namespace TUGraz.VectoCore.InputData.FileIO.JSON return new JSONElectricMotorV1(json, filename, tolerateMissing); case 2: return new JSONElectricMotorV2(json, filename, tolerateMissing); + case 3: + return new JSONElectricMotorV3(json, filename, tolerateMissing); default: throw new VectoException("ElectricMotor-File: Unsupported FileVersion. Got {0}", version); } diff --git a/VectoCore/VectoCore/Models/Connector/Ports/Impl/Response.cs b/VectoCore/VectoCore/Models/Connector/Ports/Impl/Response.cs index 629cbeff730662d71ffd6d83286734172822603a..ab6c8ffcfd41a629788dcaddc54fc12d683463b1 100644 --- a/VectoCore/VectoCore/Models/Connector/Ports/Impl/Response.cs +++ b/VectoCore/VectoCore/Models/Connector/Ports/Impl/Response.cs @@ -130,7 +130,7 @@ namespace TUGraz.VectoCore.Models.Connector.Ports.Impl public class ResponseBatteryEmpty : AbstractResponse { - public ResponseBatteryEmpty(object source) : base(source) { } + public ResponseBatteryEmpty(object source, IElectricSystemResponse electricSupplyResponse) : base(source) { } } /// <summary> diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/ElectricMotorData.cs b/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/ElectricMotorData.cs index ac22dd9ba82f92af7d40f9cbb84d4486ec352295..b2429dfc56a5f113cea6bbddb4e9798a392ab9d4 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/ElectricMotorData.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Data/ElectricMotor/ElectricMotorData.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using TUGraz.VectoCommon.Utils; using TUGraz.VectoCore.Models.SimulationComponent.Data.ElectricMotor; @@ -59,27 +60,79 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Data public NewtonMeter LookupDragTorque(Volt voltage, PerSecond avgSpeed) { - return null; + var tuple = VoltageLevels.GetSection(x => voltage > x.Voltage); + + return VectoMath.Interpolate(tuple.Item1.Voltage, tuple.Item2.Voltage, + tuple.Item1.DragCurve.Lookup(avgSpeed), + tuple.Item2.DragCurve.Lookup(avgSpeed), voltage); } public NewtonMeter EfficiencyMapLookupTorque(Volt voltage, Watt electricPower, PerSecond avgSpeed, NewtonMeter maxEmTorque) { - return null; + if (avgSpeed.IsGreaterOrEqual(MaxSpeed)) { + return 0.SI<NewtonMeter>(); + } + var tuple = VoltageLevels.GetSection(x => voltage > x.Voltage); + var r1 = tuple.Item1.EfficiencyMap.LookupTorque(electricPower, avgSpeed, maxEmTorque); + var r2 = tuple.Item2.EfficiencyMap.LookupTorque(electricPower, avgSpeed, maxEmTorque); + + if (r1 == null && r2 == null) { + return null; + } + + var retVal = VectoMath.Interpolate(tuple.Item1.Voltage, tuple.Item2.Voltage, + r1, r2, voltage); + var elPwr = LookupElectricPower(voltage, avgSpeed, retVal, true); + if (elPwr.ElectricalPower != null && electricPower.IsEqual(elPwr.ElectricalPower)) { + return retVal; + } + + //return null; + throw new NotImplementedException("EfficientyMapLookupTorque"); } - public EfficiencyMap.EfficiencyResult LookupElectricPower(Volt voltage, PerSecond avgSpeed, NewtonMeter maxTorque, bool allowExtrapolation = false) + public EfficiencyMap.EfficiencyResult LookupElectricPower(Volt voltage, PerSecond avgSpeed, NewtonMeter torque, bool allowExtrapolation = false) { - return null; + var tuple = VoltageLevels.GetSection(x => voltage > x.Voltage); + + var r1 = tuple.Item1.EfficiencyMap.LookupElectricPower(avgSpeed, torque, allowExtrapolation); + var r2 = tuple.Item2.EfficiencyMap.LookupElectricPower(avgSpeed, torque, allowExtrapolation); + + if (r1 == null || r2 == null || r1.ElectricalPower == null || r2.ElectricalPower == null) { + return new EfficiencyMap.EfficiencyResult() { + ElectricalPower = null, + Speed = avgSpeed, + Torque = torque + }; + } + + var pwr = VectoMath.Interpolate(tuple.Item1.Voltage, tuple.Item2.Voltage, r1.ElectricalPower, + r2.ElectricalPower, voltage); + + return new EfficiencyMap.EfficiencyResult() { + ElectricalPower = pwr, + Extrapolated = r1.Extrapolated || r2.Extrapolated, + Speed = avgSpeed, + Torque = torque + }; } public NewtonMeter FullGenerationTorque(Volt voltage, PerSecond avgSpeed) { - return null; + var tuple = VoltageLevels.GetSection(x => voltage > x.Voltage); + + return VectoMath.Interpolate(tuple.Item1.Voltage, tuple.Item2.Voltage, + tuple.Item1.FullLoadCurve.FullGenerationTorque(avgSpeed), + tuple.Item2.FullLoadCurve.FullGenerationTorque(avgSpeed), voltage); } public NewtonMeter FullLoadDriveTorque(Volt voltage, PerSecond avgSpeed) { - return null; + var tuple = VoltageLevels.GetSection(x => voltage > x.Voltage); + + return VectoMath.Interpolate(tuple.Item1.Voltage, tuple.Item2.Voltage, + tuple.Item1.FullLoadCurve.FullLoadDriveTorque(avgSpeed), + tuple.Item2.FullLoadCurve.FullLoadDriveTorque(avgSpeed), voltage); } } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/Driver.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/Driver.cs index 02b8894c21f032a0b06176cf7413def1a8df516d..b08cd6f2391fe9cbfd88db78b7c4e53fe37b2d9e 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/Driver.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/Driver.cs @@ -177,6 +177,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl case ResponseGearShift r: retVal = r; break; + case ResponseBatteryEmpty _: + return response; default: throw new UnexpectedResponseException("DrivingAction Accelerate.", response); } @@ -299,6 +301,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl MaxDistance = r.Driver.Acceleration / 2 * r.DeltaT * r.DeltaT + DataBus.VehicleInfo.VehicleSpeed * r.DeltaT }; break; + case ResponseBatteryEmpty _: + return retVal; default: throw new UnexpectedResponseException("DrivingAction Accelerate after Overload", retVal); } @@ -317,7 +321,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl operatingPoint = limitedOperatingPoint; break; case ResponseBatteryEmpty _: - break; + return retVal; case ResponseEngineSpeedTooHigh r: nextOperatingPoint = SearchOperatingPoint(absTime, ds, gradient, operatingPoint.Acceleration, r); retVal = NextComponent.Request(absTime, nextOperatingPoint.SimulationInterval, nextOperatingPoint.Acceleration, gradient, false); @@ -506,6 +510,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl MaxDistance = r.Driver.Acceleration / 2 * r.DeltaT * r.DeltaT + DataBus.VehicleInfo.VehicleSpeed * r.DeltaT }; break; + case ResponseBatteryEmpty _: + return response; default: throw new UnexpectedResponseException("CoastOrRoll Action: unhandled response from powertrain.", response); } @@ -730,6 +736,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl MaxDistance = DataBus.VehicleInfo.VehicleSpeed * r.DeltaT + point.Acceleration / 2 * r.DeltaT * r.DeltaT }; break; + case ResponseBatteryEmpty _: + return response; default: throw new UnexpectedResponseException("DrivingAction Brake: first request.", response); } @@ -854,6 +862,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } break; + case ResponseBatteryEmpty _: + return retVal; default: throw new UnexpectedResponseException("DrivingAction Brake: request failed after braking power was found.", retVal); } diff --git a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ElectricMotor.cs b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ElectricMotor.cs index 71a401ef7173729b98d8078af00ccc655d4dd41c..427d74d382a08d640605a47438ac67f83e87b6dc 100644 --- a/VectoCore/VectoCore/Models/SimulationComponent/Impl/ElectricMotor.cs +++ b/VectoCore/VectoCore/Models/SimulationComponent/Impl/ElectricMotor.cs @@ -30,7 +30,8 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl protected internal Joule ThermalBuffer = 0.SI<Joule>(); protected internal bool DeRatingActive = false; - + private bool BatteryElectricPowertrain; + public Joule OverloadBuffer { get; } public NewtonMeter ContinuousTorque { get; } @@ -61,7 +62,7 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var peakElPwr = ModelData.EfficiencyData.LookupElectricPower(voltage, ModelData.OverloadTestSpeed, -ModelData.OverloadTorque, true) .ElectricalPower; - var peakPwrLoss = -peakElPwr + ModelData.OverloadTorque * ModelData.OverloadTestSpeed; // losses need to be positive + var peakPwrLoss = -peakElPwr - ModelData.OverloadTorque * ModelData.OverloadTestSpeed; // losses need to be positive OverloadBuffer = (peakPwrLoss - ContinuousPowerLoss) * ModelData.OverloadTime; if (OverloadBuffer.IsSmallerOrEqual(0) && !(container is SimplePowertrainContainer)) { @@ -116,6 +117,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl PreviousState.DrivetrainSpeed = outAngularVelocity; PreviousState.DrivetrainOutTorque = outTorque; + BatteryElectricPowertrain = !DataBus.PowertrainInfo.HasCombustionEngine && + DataBus.PowertrainInfo.ElectricMotorPositions.All(x => x.IsBatteryElectric()); + if (NextComponent == null) { return new ResponseSuccess(this) { Engine = { @@ -272,7 +276,10 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl var electricSupplyResponse = ElectricPower.Request(absTime, dt, electricPower, dryRun); - if (!dryRun && !DataBus.IsTestPowertrain && !emOff && !(electricSupplyResponse is ElectricSystemResponseSuccess)) { + //if (!dryRun && !DataBus.IsTestPowertrain && BatteryElectricPowertrain && electricSupplyResponse is ElectricSystemUnderloadResponse) { + // return new ResponseBatteryEmpty(this, electricSupplyResponse); + //} + if (NextComponent != null && !dryRun && !DataBus.IsTestPowertrain && !emOff && !(electricSupplyResponse is ElectricSystemResponseSuccess)) { if ( !avgEmSpeed.IsEqual(DataBus.HybridControllerInfo.ElectricMotorSpeed(Position) / ModelData.RatioADC)) { return new ResponseInvalidOperatingPoint(this); } @@ -304,8 +311,9 @@ namespace TUGraz.VectoCore.Models.SimulationComponent.Impl } else { if (remainingPower.IsEqual(0, Constants.SimulationSettings.LineSearchTolerance)) { - if (electricSupplyResponse.MaxPowerDrive.IsGreaterOrEqual(0)) { - retVal = new ResponseBatteryEmpty(this); + //if (electricSupplyResponse.MaxPowerDrive.IsGreaterOrEqual(0)) { + if (electricSupplyResponse is ElectricSystemUnderloadResponse) { + retVal = new ResponseBatteryEmpty(this, electricSupplyResponse); } else { retVal = new ResponseSuccess(this) { ElectricMotor = { diff --git a/VectoCore/VectoCoreTest/Integration/BatteryElectric/BatteryElectricTest.cs b/VectoCore/VectoCoreTest/Integration/BatteryElectric/BatteryElectricTest.cs index 9ae61f5b293ab54767a8b3c250aeba21cbdf5c08..7b5662db24142577f4b24522333e5aa65867cb60 100644 --- a/VectoCore/VectoCoreTest/Integration/BatteryElectric/BatteryElectricTest.cs +++ b/VectoCore/VectoCoreTest/Integration/BatteryElectric/BatteryElectricTest.cs @@ -324,7 +324,7 @@ namespace TUGraz.VectoCore.Tests.Integration.BatteryElectric [ TestCase("LongHaul", 2000, 0.8, 0, TestName = "PEV E3 DriveCycle LongHaul, SoC: 0.8 Payload: 2t P_auxEl: 0kW"), - TestCase("RegionalDelivery", 2000, 0.8, 0, TestName = "BPEVEV E3 DriveCycle RegionalDelivery, SoC: 0.8 Payload: 2t P_auxEl: 0kW"), + TestCase("RegionalDelivery", 2000, 0.8, 0, TestName = "PEV E3 DriveCycle RegionalDelivery, SoC: 0.8 Payload: 2t P_auxEl: 0kW"), TestCase("UrbanDelivery", 2000, 0.8, 0, TestName = "PEV E3 DriveCycle UrbanDelivery, SoC: 0.8 Payload: 2t P_auxEl: 0kW"), TestCase("Construction", 2000, 0.8, 0, TestName = "PEV E3 DriveCycle Construction, SoC: 0.8 Payload: 2t P_auxEl: 0kW"), TestCase("Urban", 2000, 0.8, 0, TestName = "PEV E3 DriveCycle Urban, SoC: 0.8 Payload: 2t P_auxEl: 0kW"), diff --git a/VectoCore/VectoCoreTest/Models/SimulationComponent/ElectricMotorTest.cs b/VectoCore/VectoCoreTest/Models/SimulationComponent/ElectricMotorTest.cs index c20be385c2562fc2c5a7c4e9266745cbad9723a4..f2f55a77788e1f88e3b6a5d197125d9ceeb435d5 100644 --- a/VectoCore/VectoCoreTest/Models/SimulationComponent/ElectricMotorTest.cs +++ b/VectoCore/VectoCoreTest/Models/SimulationComponent/ElectricMotorTest.cs @@ -16,6 +16,7 @@ using TUGraz.VectoCore.Models.Simulation.Impl; using TUGraz.VectoCore.Models.SimulationComponent; using TUGraz.VectoCore.Models.SimulationComponent.Impl; using TUGraz.VectoCore.OutputData; +using TUGraz.VectoCore.OutputData.FileIO; using TUGraz.VectoCore.Tests.Utils; using TUGraz.VectoCore.Utils; @@ -25,6 +26,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent public class ElectricMotorTest { public const string MotorFile = @"TestData\Hybrids\ElectricMotor\GenericEMotor.vem"; + public const string MotorFile_v2 = @"TestData\Hybrids\ElectricMotor\GenericEMotorV2.vem"; public const string BatFile = @"TestData\Hybrids\Battery\GenericBattery.vbat"; [OneTimeSetUp] @@ -60,6 +62,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var strategy = new MockHybridControl(); var battery = new MockBattery(); + container.BatteryInfo = battery; var motor = new ElectricMotor(container, data.First().Item2, strategy, PowertrainPosition.HybridP2); var es = new ElectricSystem(container); es.Connect(battery); @@ -106,6 +109,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var strategy = new MockHybridControl(); var battery = new MockBattery(); + container.BatteryInfo = battery; var motor = new ElectricMotor(container, data.First().Item2, strategy, PowertrainPosition.HybridP2); var es = new ElectricSystem(container); es.Connect(battery); @@ -152,6 +156,8 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var strategy = new MockHybridControl(); var battery = new MockBattery(); + container.BatteryInfo = battery; + var motor = new ElectricMotor(container, data.First().Item2, strategy, PowertrainPosition.HybridP2); var es = new ElectricSystem(container); es.Connect(battery); @@ -200,6 +206,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var strategy = new MockHybridControl(); var battery = new MockBattery(); + container.BatteryInfo = battery; var motor = new ElectricMotor(container, data.First().Item2, strategy, PowertrainPosition.HybridP2); var es = new ElectricSystem(container); es.Connect(battery); @@ -253,6 +260,8 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var es = new ElectricSystem(container); es.Connect(battery); + container.BatteryInfo = battery; + battery.Initialize(0.5); var motor = new ElectricMotor(container, data.First().Item2, strategy, PowertrainPosition.HybridP2); motor.Connect(es); @@ -313,6 +322,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent }; var batteryData = dao.CreateBatteryData(tmp, 0.8); var battery = new Battery(container, batteryData); + container.BatteryInfo = battery; var es = new ElectricSystem(container); es.Connect(battery); battery.Initialize(initialSoc); @@ -339,12 +349,11 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent public void TestElectricMotorThermalDeRating() { var initialSoc = 0.8; - var torque = 334.SI<NewtonMeter>(); + var torque = 300.SI<NewtonMeter>(); + var continuousTorque = 24.SI<NewtonMeter>(); var speed = 2000.RPMtoRad(); - - - var inputData = JSONInputDataFactory.ReadElectricMotorData(MotorFile, false); + var inputData = JSONInputDataFactory.ReadElectricMotorData(MotorFile_v2, false); var batInput = JSONInputDataFactory.ReadREESSData(BatFile, false); var dao = new EngineeringDataAdapter(); var electricMachine = new MockElectricMachinesInputData() @@ -370,10 +379,11 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var batteryData = dao.CreateBatteryData(tmp, 0.8); var runData = new VectoRunData() { + JobName = "EM-Derating", ElectricMachinesData = data }; - var modData = new ModalDataContainer(runData, null, null); + var modData = new ModalDataContainer(runData, new FileOutputWriter("debug.csv"), null); modData.AddElectricMotor(PowertrainPosition.HybridP2); var container = new VehicleContainer(ExecutionMode.Engineering, modData); new EngineOnlyGearboxInfo(container); @@ -382,6 +392,7 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var es = new ElectricSystem(container); es.Connect(battery); battery.Initialize(initialSoc); + var motor = new ElectricMotor(container, data.First().Item2, strategy, PowertrainPosition.HybridP2); motor.Connect(es); @@ -391,37 +402,52 @@ namespace TUGraz.VectoCore.Tests.Models.SimulationComponent var i = 0; - // energy buffer is empty - overload is available - for (; i <= 60; i++) { - var dt = 0.5.SI<Second>(); - var absTime = i * dt; - - var response = motor.Request(absTime, dt, torque, speed); - Assert.AreEqual(-334.23, response.ElectricMotor.MaxDriveTorque.Value(), 1e-2); - motor.CommitSimulationStep(absTime, dt, modData); - } + var t1 = 21; + var t2 = 13; + var t3 = 20; + + try { + // energy buffer is empty - overload is available + for (; i < t1; i++) { + var dt = 0.5.SI<Second>(); + var absTime = i * dt; + + var response = motor.Request(absTime, dt, torque, speed); + Assert.AreEqual(-334.23, response.ElectricMotor.MaxDriveTorque.Value(), 1e-2); + motor.CommitSimulationStep(absTime, dt, modData); + modData[ModalResultField.time] = absTime; + modData.CommitSimulationStep(); + } - // energy buffer reached - only P_cont is available + // energy buffer reached - only P_cont is available - strategy.ElectricShare = -torque * 0.5; - for (; i <= 73; i++) { - var dt = 0.5.SI<Second>(); - var absTime = i * dt; + strategy.ElectricShare = -continuousTorque; + for (; i < t1 + t2; i++) { + var dt = 0.5.SI<Second>(); + var absTime = i * dt; - var response = motor.Request(absTime, dt, torque * 0.5, speed); - Assert.AreEqual(-238.73, response.ElectricMotor.MaxDriveTorque.Value(), 1e-2); - motor.CommitSimulationStep(absTime, dt, modData); - } + var response = motor.Request(absTime, dt, continuousTorque, speed); + Assert.AreEqual(-100, response.ElectricMotor.MaxDriveTorque.Value(), 1e-2); + motor.CommitSimulationStep(absTime, dt, modData); + modData[ModalResultField.time] = absTime; + modData.CommitSimulationStep(); + } - // energy buffer below lower threshold - overload is available again - for (; i < 100; i++) { - var dt = 0.5.SI<Second>(); - var absTime = i * dt; + // energy buffer below lower threshold - overload is available again + for (; i < t1 + t2 + t3; i++) { + var dt = 0.5.SI<Second>(); + var absTime = i * dt; - var response = motor.Request(absTime, dt, torque * 0.5, speed); - Assert.AreEqual(-334.23, response.ElectricMotor.MaxDriveTorque.Value(), 1e-2); - motor.CommitSimulationStep(absTime, dt, modData); + var response = motor.Request(absTime, dt, torque * 0.5, speed); + Assert.AreEqual(-334.23, response.ElectricMotor.MaxDriveTorque.Value(), 1e-2); + motor.CommitSimulationStep(absTime, dt, modData); + modData[ModalResultField.time] = absTime; + modData.CommitSimulationStep(); + } + } finally { + modData.WriteModalResults = true; + modData.Finish(VectoRun.Status.Aborted); } } diff --git a/VectoCore/VectoCoreTest/VectoCoreTest.csproj b/VectoCore/VectoCoreTest/VectoCoreTest.csproj index 2572f74355af024a922237b63b58f9c65bc2c0f1..7cf855a0fa0a64311b940c707547f84c3c519500 100644 --- a/VectoCore/VectoCoreTest/VectoCoreTest.csproj +++ b/VectoCore/VectoCoreTest/VectoCoreTest.csproj @@ -634,6 +634,9 @@ <None Include="TestData\Hybrids\ElectricMotor\GenericDrag.vemd"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> + <None Include="TestData\Hybrids\ElectricMotor\GenericEMotorV2.vem"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </None> <None Include="TestData\Hybrids\ElectricMotor\GenericEMotor.vem"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None>