/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2002-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file HelpersMMPEVEM.cpp14/// @author Kevin Badalian ([email protected])15/// @date 2021-1016///17// The MMP's emission model for electric vehicles.18// If you use this model for academic research, you are highly encouraged to19// cite our paper "Accurate physics-based modeling of electric vehicle energy20// consumption in the SUMO traffic microsimulator"21// (DOI: 10.1109/ITSC48978.2021.9564463).22// Teaching and Research Area Mechatronics in Mobile Propulsion (MMP), RWTH Aachen23/****************************************************************************/24#include <config.h>2526#include <utils/common/SUMOTime.h>27#include <utils/common/StringUtils.h>28#include <utils/geom/GeomHelper.h>29#include <utils/emissions/CharacteristicMap.h>30#include <utils/emissions/HelpersMMPEVEM.h>313233// ===========================================================================34// method definitions35// ===========================================================================36/**37* \brief Compute the power consumption of an EV powertrain in a certain state.38*39* The model assumes that the driver recuperates as much energy as possible,40* meaning that he perfectly employs the mechanical brakes such that only what41* lies beyond the motor's capabilities is dissipated. If the new state is42* invalid, that is the demanded acceleration exceeds what the electric motor43* can manage or the power loss map is not defined for a given point, the torque44* and/or power are capped to the motor's limits or the power loss is set to45* zero.46*47* \param[in] m Vehicle mass [kg]48* \param[in] r_wheel Wheel radius [m]49* \param[in] Theta Moment of inertia [kg*m^2]50* \param[in] c_rr Rolling resistance coefficient [1]51* \param[in] c_d Drag coefficient [1]52* \param[in] A_front Cross-sectional area of the front of the car [m^2]53* \param[in] i_gear Gear ratio (i_gear = n_motor/n_wheel) [1]54* \param[in] eta_gear Gear efficiency [1]55* \param[in] M_max Maximum torque of the EM [Nm]56* \param[in] P_max Maximum power of the EM [W]57* \param[in] M_recup_max Maximum recuperation torque [Nm]58* \param[in] P_recup_max Maximum recuperation power [W]59* \param[in] R_battery Internal battery resistance [Ohm]60* \param[in] U_battery_0 Nominal battery voltage [V]61* \param[in] P_const Constant power consumption of ancillary devices [W]62* \param[in] ref_powerLossMap Power loss map of the EM + inverter63* \param[in] dt Simulation timestep [s]64* \param[in] v Vehicle speed at the end of a timestep [m/s]65* \param[in] a Constant acceleration during a timestep [m/s^2]66* \param[in] alpha Constant incline during a timestep [deg]67* \param[out] ref_powerConsumption Power consumption during the last timestep68* [W]69* \returns true if the new state is valid, else false70*/71bool calcPowerConsumption(double m, double r_wheel, double Theta, double c_rr,72double c_d, double A_front, double i_gear, double eta_gear, double M_max,73double P_max, double M_recup_max, double P_recup_max, double R_battery,74double U_battery_0, double P_const,75const CharacteristicMap& ref_powerLossMap, double dt, double v, double a,76double alpha, double& ref_powerConsumption) {77const double EPS = 1e-6;78const double RHO_AIR = 1.204; // Air density [kg/m^3]79bool b_stateValid = true;8081// Mass factor [1]82const double e_i = 1.0 + Theta / (m * r_wheel * r_wheel);83// Average speed during the previous timestep84const double v_mean = v - 0.5 * a * dt;8586// Force required for the desired acceleration [N]87double F_a = m * a * e_i;88// Grade resistance [N]89double F_gr = m * GRAVITY * std::sin(DEG2RAD(alpha));90// Rolling resistance [N]91double F_rr = m * GRAVITY * std::cos(DEG2RAD(alpha)) * c_rr;92if (std::abs(v_mean) <= EPS) {93F_rr = 0;94}95// Drag [N]96double F_d = 0.5 * c_d * A_front * RHO_AIR * v_mean * v_mean;97// Tractive force [N]98const double F_tractive = F_a + F_gr + F_rr + F_d;99100// Speed of the motor [rpm]101const double n_motor = v_mean / (2 * M_PI * r_wheel) * 60 * i_gear;102// Angular velocity of the motor [1/s]103double omega_motor = 2 * M_PI * n_motor / 60;104if (omega_motor == 0) {105omega_motor = EPS;106}107108// Torque at the motor [Nm]. Please note that this model, like most real EVs,109// utilizes the EM to hold the vehicle on an incline rather than the brakes.110double M_motor = F_tractive * r_wheel / i_gear;111if (F_tractive < 0) {112M_motor *= eta_gear;113} else {114M_motor /= eta_gear;115}116// Engine power [W]117double P_motor = M_motor * omega_motor;118119// Cap torque or power if necessary120// Accelerating121if (M_motor >= 0) {122if (M_motor > M_max) {123M_motor = M_max;124P_motor = M_motor * omega_motor;125b_stateValid = false;126}127if (P_motor > P_max) {128P_motor = P_max;129M_motor = P_motor / omega_motor;130b_stateValid = false;131}132}133// Recuperating134else {135// Even when capping, the state is still valid here because the extra energy136// is assumed to go to the brakes137if (M_motor < -M_recup_max) {138M_motor = -M_recup_max;139P_motor = M_motor * omega_motor;140}141if (P_motor < -P_recup_max) {142P_motor = -P_recup_max;143M_motor = P_motor / omega_motor;144}145}146147// Power loss in the electric motor + inverter [W]148double P_loss_motor =149ref_powerLossMap.eval(std::vector<double> {n_motor, M_motor})[0];150if (std::isnan(P_loss_motor)) {151P_loss_motor = 0.0;152b_stateValid = false;153}154155// Power demand at the battery [W]156double P_battery = P_motor + P_loss_motor + P_const;157// Total power demand (including the power loss in the battery) [W]158double P_total = (U_battery_0 * U_battery_0) / (2.0 * R_battery)159- U_battery_0 * std::sqrt((U_battery_0 * U_battery_0160- 4.0 * R_battery * P_battery) / (4.0 * R_battery * R_battery));161162ref_powerConsumption = P_total;163return b_stateValid;164}165166167/**168* \brief Constructor169*/170HelpersMMPEVEM::HelpersMMPEVEM()171: PollutantsInterface::Helper("MMPEVEM", MMPEVEM_BASE, MMPEVEM_BASE + 1)172{ }173174175/**176* \brief Compute the amount of emitted pollutants for an emission class in a177* given state.178*179* This method returns 0 for all emission types but electric power consumption.180*181* \param[in] c An emission class182* \param[in] e An emission type183* \param[in] v Current vehicle velocity [m/s]184* \param[in] a Current acceleration of the vehicle [m/s^2]185* \param[in] slope Slope of the road at the vehicle's current position [deg]186* \param[in] ptr_energyParams Vehicle parameters187*188* \returns The electric power consumption [Wh/s] or 0 for all other emission189* types190*/191double HelpersMMPEVEM::compute(const SUMOEmissionClass c,192const PollutantsInterface::EmissionType e, const double v, const double a,193const double slope, const EnergyParams* ptr_energyParams) const {194if (e != PollutantsInterface::ELEC) {195return 0.0;196}197198// Extract all required parameters, default values taken from the VW_ID3199// Vehicle mass [kg]200const double m = ptr_energyParams->getTotalMass(getWeight(c), 0.);201// Wheel radius [m]202const double r_wheel = ptr_energyParams->getDoubleOptional(SUMO_ATTR_WHEELRADIUS, 0.3588);203// Internal moment of inertia [kgm^2]204const double Theta = ptr_energyParams->getDoubleOptional(SUMO_ATTR_INTERNALMOMENTOFINERTIA, 12.5);205// Rolling resistance coefficient206const double c_rr = ptr_energyParams->getDoubleOptional(SUMO_ATTR_ROLLDRAGCOEFFICIENT, 0.007);207// Air drag coefficient208const double c_d = ptr_energyParams->getDoubleOptional(SUMO_ATTR_AIRDRAGCOEFFICIENT, 0.26);209// Cross-sectional area of the front of the car [m^2]210const double A_front = ptr_energyParams->getDoubleOptional(SUMO_ATTR_FRONTSURFACEAREA, 2.36);211// Gear ratio [1]212const double i_gear = ptr_energyParams->getDoubleOptional(SUMO_ATTR_GEARRATIO, 10.);213// Gearbox efficiency [1]214const double eta_gear = ptr_energyParams->getDoubleOptional(SUMO_ATTR_GEAREFFICIENCY, 0.96);215// Maximum torque [Nm]216const double M_max = ptr_energyParams->getDoubleOptional(SUMO_ATTR_MAXIMUMTORQUE, 310.);217// Maximum power [W]218const double P_max = ptr_energyParams->getDoubleOptional(SUMO_ATTR_MAXIMUMPOWER, 107000.);219// Maximum recuperation torque [Nm]220const double M_recup_max = ptr_energyParams->getDoubleOptional(SUMO_ATTR_MAXIMUMRECUPERATIONTORQUE, 95.5);221// Maximum recuperation power [W]222const double P_recup_max = ptr_energyParams->getDoubleOptional(SUMO_ATTR_MAXIMUMRECUPERATIONPOWER, 42800.);223// Internal battery resistance [Ohm]224const double R_battery = ptr_energyParams->getDoubleOptional(SUMO_ATTR_INTERNALBATTERYRESISTANCE, 0.1142);225// Nominal battery voltage [V]226const double U_battery_0 = ptr_energyParams->getDoubleOptional(SUMO_ATTR_NOMINALBATTERYVOLTAGE, 396.);227// Constant power consumption [W]228const double P_const = ptr_energyParams->getDoubleOptional(SUMO_ATTR_CONSTANTPOWERINTAKE, 360.);229// Power loss map230const CharacteristicMap& ref_powerLossMap = ptr_energyParams->getCharacteristicMap(SUMO_ATTR_POWERLOSSMAP);231232double P = 0.0; // [W]233bool b_stateValid = calcPowerConsumption(m, r_wheel, Theta, c_rr, c_d,234A_front, i_gear, eta_gear, M_max, P_max, M_recup_max, P_recup_max,235R_battery, U_battery_0, P_const, ref_powerLossMap, TS, v, a, slope, P);236P /= 3600.0; // [Wh/s]237238if (b_stateValid) {239return P;240} else {241return std::nan("");242}243}244245246