Path: blob/main/src/utils/emissions/HelpersPHEMlight.cpp
169678 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2013-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 HelpersPHEMlight.cpp14/// @author Daniel Krajzewicz15/// @author Michael Behrisch16/// @author Nikolaus Furian17/// @date Sat, 20.04.201318///19// Helper methods for PHEMlight-based emission computation20/****************************************************************************/21#include <config.h>2223#include <limits>24#include <cmath>25#ifdef INTERNAL_PHEM26#include "PHEMCEPHandler.h"27#include "PHEMConstants.h"28#endif29#include <foreign/PHEMlight/cpp/Constants.h>30#include <utils/common/StringUtils.h>31#include <utils/options/OptionsCont.h>3233#include "EnergyParams.h"34#include "HelpersPHEMlight.h"3536// idle speed is usually given in rpm (but may depend on electrical consumers). Actual speed depends on the gear so this number is only a rough estimate37#define IDLE_SPEED (10 / 3.6)3839// ===========================================================================40// method definitions41// ===========================================================================42HelpersPHEMlight::HelpersPHEMlight() :43PollutantsInterface::Helper("PHEMlight", PHEMLIGHT_BASE, -1),44myIndex(PHEMLIGHT_BASE) {45}464748HelpersPHEMlight::~HelpersPHEMlight() {49for (const auto& cep : myCEPs) {50delete cep.second;51}52}535455SUMOEmissionClass56HelpersPHEMlight::getClassByName(const std::string& eClass, const SUMOVehicleClass vc) {57if (eClass == "unknown" && !myEmissionClassStrings.hasString("unknown")) {58myEmissionClassStrings.addAlias("unknown", getClassByName("PC_G_EU4", vc));59}60if (eClass == "default" && !myEmissionClassStrings.hasString("default")) {61myEmissionClassStrings.addAlias("default", getClassByName("PC_G_EU4", vc));62}63if (myEmissionClassStrings.hasString(eClass)) {64return myEmissionClassStrings.get(eClass);65}66if (eClass.size() < 6) {67throw InvalidArgument("Unknown emission class '" + eClass + "'.");68}69int index = myIndex++;70const std::string type = eClass.substr(0, 3);71if (type == "HDV" || type == "LB_" || type == "RB_" || type == "LSZ" || eClass.find("LKW") != std::string::npos) {72index |= PollutantsInterface::HEAVY_BIT;73}74myEmissionClassStrings.insert(eClass, index);75#ifdef INTERNAL_PHEM76if (type == "HDV" || type == "LCV" || type == "PC_" || !PHEMCEPHandler::getHandlerInstance().Load(index, eClass)) {77#endif78myVolumetricFuel = OptionsCont::getOptions().getBool("emissions.volumetric-fuel");79std::vector<std::string> phemPath;80phemPath.push_back(OptionsCont::getOptions().getString("phemlight-path") + "/");81if (getenv("PHEMLIGHT_PATH") != nullptr) {82phemPath.push_back(std::string(getenv("PHEMLIGHT_PATH")) + "/");83}84if (getenv("SUMO_HOME") != nullptr) {85phemPath.push_back(std::string(getenv("SUMO_HOME")) + "/data/emissions/PHEMlight/");86}87myHelper.setCommentPrefix("c");88myHelper.setPHEMDataV("V4");89myHelper.setclass(eClass);90if (!myCEPHandler.GetCEP(phemPath, &myHelper)) {91myEmissionClassStrings.remove(eClass, index);92myIndex--;93throw InvalidArgument("File for PHEM emission class " + eClass + " not found.\n" + myHelper.getErrMsg());94}95myCEPs[index] = myCEPHandler.getCEPS().find(myHelper.getgClass())->second;96#ifdef INTERNAL_PHEM97}98#endif99myEmissionClassStrings.addAlias(StringUtils::to_lower_case(eClass), index);100return index;101}102103104SUMOEmissionClass105HelpersPHEMlight::getClass(const SUMOEmissionClass base, const std::string& vClass, const std::string& fuel, const std::string& eClass, const double weight) const {106std::string eClassOffset = "0";107if (eClass.length() == 5 && eClass.substr(0, 4) == "Euro") {108if (eClass[4] >= '0' && eClass[4] <= '6') {109eClassOffset = eClass.substr(4, 1);110}111}112std::string desc;113if (vClass == "Passenger") {114desc = "PKW_";115if (fuel == "Gasoline") {116desc += "G_";117} else if (fuel == "Diesel") {118desc += "D_";119} else if (fuel == "HybridGasoline") {120desc = "H_" + desc + "G_";121} else if (fuel == "HybridDiesel") {122desc = "H_" + desc + "G_";123}124desc += "EU" + eClassOffset;125} else if (vClass == "Moped") {126desc = "KKR_G_EU" + eClassOffset;127} else if (vClass == "Motorcycle") {128desc = "MR_G_EU" + eClassOffset;129if (fuel == "Gasoline2S") {130desc += "_2T";131} else {132desc += "_4T";133}134} else if (vClass == "Delivery") {135desc = "LNF_";136if (fuel == "Gasoline") {137desc += "G_";138} else if (fuel == "Diesel") {139desc += "D_";140}141desc += "EU" + eClassOffset + "_I";142if (weight > 1305.) {143desc += "I";144if (weight > 1760.) {145desc += "I";146}147}148} else if (vClass == "UrbanBus") {149desc = "LB_D_EU" + eClassOffset;150} else if (vClass == "Coach") {151desc = "RB_D_EU" + eClassOffset;152} else if (vClass == "Truck") {153desc = "Solo_LKW_D_EU" + eClassOffset + "_I";154if (weight > 1305.) {155desc += "I";156}157} else if (vClass == "Trailer") {158desc = "LSZ_D_EU" + eClassOffset;159}160if (myEmissionClassStrings.hasString(desc)) {161return myEmissionClassStrings.get(desc);162}163return base;164}165166167std::string168HelpersPHEMlight::getAmitranVehicleClass(const SUMOEmissionClass c) const {169const std::string name = myEmissionClassStrings.getString(c);170if (name.find("KKR_") != std::string::npos) {171return "Moped";172} else if (name.find("RB_") != std::string::npos) {173return "Coach";174} else if (name.find("LB_") != std::string::npos) {175return "UrbanBus";176} else if (name.find("LNF_") != std::string::npos) {177return "Delivery";178} else if (name.find("LSZ_") != std::string::npos) {179return "Trailer";180} else if (name.find("MR_") != std::string::npos) {181return "Motorcycle";182} else if (name.find("LKW_") != std::string::npos) {183return "Truck";184}185return "Passenger";186}187188189std::string190HelpersPHEMlight::getFuel(const SUMOEmissionClass c) const {191const std::string name = myEmissionClassStrings.getString(c);192std::string fuel = "Gasoline";193if (name.find("_D_") != std::string::npos) {194fuel = "Diesel";195}196if (name.find("H_") != std::string::npos) {197fuel = "Hybrid" + fuel;198}199return fuel;200}201202203int204HelpersPHEMlight::getEuroClass(const SUMOEmissionClass c) const {205const std::string name = myEmissionClassStrings.getString(c);206if (name.find("_EU1") != std::string::npos) {207return 1;208} else if (name.find("_EU2") != std::string::npos) {209return 2;210} else if (name.find("_EU3") != std::string::npos) {211return 3;212} else if (name.find("_EU4") != std::string::npos) {213return 4;214} else if (name.find("_EU5") != std::string::npos) {215return 5;216} else if (name.find("_EU6") != std::string::npos) {217return 6;218}219return 0;220}221222223double224HelpersPHEMlight::getWeight(const SUMOEmissionClass c) const {225const std::string name = myEmissionClassStrings.getString(c);226if (name.find("LNF_") != std::string::npos) {227if (name.find("_III") != std::string::npos) {228return 2630.;229} else if (name.find("_II") != std::string::npos) {230return 1532.;231} else if (name.find("_I") != std::string::npos) {232return 652.;233}234}235if (name.find("Solo_LKW_") != std::string::npos) {236if (name.find("_II") != std::string::npos) {237return 8398.;238} else if (name.find("_I") != std::string::npos) {239return 18702.;240}241}242return -1.;243}244245246double247HelpersPHEMlight::getEmission(const PHEMCEP* oldCep, PHEMlightdll::CEP* currCep, const std::string& e, const double p, const double v) const {248if (oldCep != nullptr) {249return oldCep->GetEmission(e, p, v);250}251return currCep->GetEmission(e, p, v, &myHelper);252}253254255double256HelpersPHEMlight::getModifiedAccel(const SUMOEmissionClass c, const double v, const double a, const double slope, const EnergyParams* /* param */) const {257PHEMlightdll::CEP* currCep = myCEPs.count(c) == 0 ? 0 : myCEPs.find(c)->second;258if (currCep != nullptr) {259return v == 0.0 ? 0.0 : MIN2(a, currCep->GetMaxAccel(v, slope));260}261return a;262}263264265double266HelpersPHEMlight::getCoastingDecel(const SUMOEmissionClass c, const double v, const double a, const double slope, const EnergyParams* /* param */) const {267return myCEPs.count(c) == 0 ? 0. : myCEPs.find(c)->second->GetDecelCoast(v, a, slope);268}269270271double272HelpersPHEMlight::compute(const SUMOEmissionClass c, const PollutantsInterface::EmissionType e, const double v, const double a, const double slope, const EnergyParams* param) const {273if (param != nullptr && param->isEngineOff()) {274return 0.;275}276const double corrSpeed = MAX2(0.0, v);277double power = 0.;278#ifdef INTERNAL_PHEM279const PHEMCEP* const oldCep = PHEMCEPHandler::getHandlerInstance().GetCep(c);280if (oldCep != nullptr) {281if (v > IDLE_SPEED && a < oldCep->GetDecelCoast(corrSpeed, a, slope, 0)) {282// coasting without power use only works if the engine runs above idle speed and283// the vehicle does not accelerate beyond friction losses284return 0;285}286power = oldCep->CalcPower(corrSpeed, a, slope);287}288#else289const PHEMCEP* const oldCep = 0;290#endif291PHEMlightdll::CEP* currCep = myCEPs.count(c) == 0 ? 0 : myCEPs.find(c)->second;292if (currCep != nullptr) {293const double corrAcc = getModifiedAccel(c, corrSpeed, a, slope, param);294if (currCep->getFuelType() != PHEMlightdll::Constants::strBEV &&295corrAcc < currCep->GetDecelCoast(corrSpeed, corrAcc, slope) &&296corrSpeed > PHEMlightdll::Constants::ZERO_SPEED_ACCURACY) {297// the IDLE_SPEED fix above is now directly in the decel coast calculation.298return 0;299}300power = currCep->CalcPower(corrSpeed, corrAcc, slope);301}302const std::string& fuelType = oldCep != nullptr ? oldCep->GetVehicleFuelType() : currCep->getFuelType();303switch (e) {304case PollutantsInterface::CO:305return getEmission(oldCep, currCep, "CO", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;306case PollutantsInterface::CO2:307if (oldCep != nullptr) {308return getEmission(oldCep, currCep, "FC", power, corrSpeed) * 3.15 / SECONDS_PER_HOUR * 1000.;309}310return currCep->GetCO2Emission(getEmission(nullptr, currCep, "FC", power, corrSpeed),311getEmission(nullptr, currCep, "CO", power, corrSpeed),312getEmission(nullptr, currCep, "HC", power, corrSpeed), &myHelper) / SECONDS_PER_HOUR * 1000.;313case PollutantsInterface::HC:314return getEmission(oldCep, currCep, "HC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;315case PollutantsInterface::NO_X:316return getEmission(oldCep, currCep, "NOx", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;317case PollutantsInterface::PM_X:318return getEmission(oldCep, currCep, "PM", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;319case PollutantsInterface::FUEL: {320if (myVolumetricFuel && fuelType == PHEMlightdll::Constants::strDiesel) { // divide by average diesel density of 836 g/l321return getEmission(oldCep, currCep, "FC", power, corrSpeed) / 836. / SECONDS_PER_HOUR * 1000.;322}323if (myVolumetricFuel && fuelType == PHEMlightdll::Constants::strGasoline) { // divide by average gasoline density of 742 g/l324return getEmission(oldCep, currCep, "FC", power, corrSpeed) / 742. / SECONDS_PER_HOUR * 1000.;325}326if (fuelType == PHEMlightdll::Constants::strBEV) {327return 0.;328}329return getEmission(oldCep, currCep, "FC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.; // still in mg even if myVolumetricFuel is set!330}331case PollutantsInterface::ELEC:332if (fuelType == PHEMlightdll::Constants::strBEV) {333return getEmission(oldCep, currCep, "FC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;334}335return 0;336}337// should never get here338return 0.;339}340341342/****************************************************************************/343344345