LCOV - code coverage report
Current view: top level - src/utils/emissions - HelpersPHEMlight.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 45.6 % 158 72
Test Date: 2024-12-21 15:45:41 Functions: 61.5 % 13 8

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2013-2024 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    HelpersPHEMlight.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Michael Behrisch
      17              : /// @author  Nikolaus Furian
      18              : /// @date    Sat, 20.04.2013
      19              : ///
      20              : // Helper methods for PHEMlight-based emission computation
      21              : /****************************************************************************/
      22              : #include <config.h>
      23              : 
      24              : #include <limits>
      25              : #include <cmath>
      26              : #ifdef INTERNAL_PHEM
      27              : #include "PHEMCEPHandler.h"
      28              : #include "PHEMConstants.h"
      29              : #endif
      30              : #include <foreign/PHEMlight/cpp/Constants.h>
      31              : #include <utils/common/StringUtils.h>
      32              : #include <utils/options/OptionsCont.h>
      33              : 
      34              : #include "EnergyParams.h"
      35              : #include "HelpersPHEMlight.h"
      36              : 
      37              : // 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 estimate
      38              : #define IDLE_SPEED (10 / 3.6)
      39              : 
      40              : // ===========================================================================
      41              : // method definitions
      42              : // ===========================================================================
      43        56178 : HelpersPHEMlight::HelpersPHEMlight() :
      44              :     PollutantsInterface::Helper("PHEMlight", PHEMLIGHT_BASE, -1),
      45       112356 :     myIndex(PHEMLIGHT_BASE) {
      46        56178 : }
      47              : 
      48              : 
      49       112356 : HelpersPHEMlight::~HelpersPHEMlight() {
      50       112465 :     for (const auto& cep : myCEPs) {
      51          109 :         delete cep.second;
      52              :     }
      53       112356 : }
      54              : 
      55              : 
      56              : SUMOEmissionClass
      57          156 : HelpersPHEMlight::getClassByName(const std::string& eClass, const SUMOVehicleClass vc) {
      58          216 :     if (eClass == "unknown" && !myEmissionClassStrings.hasString("unknown")) {
      59           60 :         myEmissionClassStrings.addAlias("unknown", getClassByName("PC_G_EU4", vc));
      60              :     }
      61          156 :     if (eClass == "default" && !myEmissionClassStrings.hasString("default")) {
      62            0 :         myEmissionClassStrings.addAlias("default", getClassByName("PC_G_EU4", vc));
      63              :     }
      64              :     if (myEmissionClassStrings.hasString(eClass)) {
      65           47 :         return myEmissionClassStrings.get(eClass);
      66              :     }
      67          109 :     if (eClass.size() < 6) {
      68            0 :         throw InvalidArgument("Unknown emission class '" + eClass + "'.");
      69              :     }
      70          109 :     int index = myIndex++;
      71          109 :     const std::string type = eClass.substr(0, 3);
      72          218 :     if (type == "HDV" || type == "LB_" || type == "RB_" || type == "LSZ" || eClass.find("LKW") != std::string::npos) {
      73            0 :         index |= PollutantsInterface::HEAVY_BIT;
      74              :     }
      75          218 :     myEmissionClassStrings.insert(eClass, index);
      76              : #ifdef INTERNAL_PHEM
      77          109 :     if (type == "HDV" || type == "LCV" || type == "PC_" || !PHEMCEPHandler::getHandlerInstance().Load(index, eClass)) {
      78              : #endif
      79          109 :         myVolumetricFuel = OptionsCont::getOptions().getBool("emissions.volumetric-fuel");
      80              :         std::vector<std::string> phemPath;
      81          109 :         phemPath.push_back(OptionsCont::getOptions().getString("phemlight-path") + "/");
      82          109 :         if (getenv("PHEMLIGHT_PATH") != nullptr) {
      83            0 :             phemPath.push_back(std::string(getenv("PHEMLIGHT_PATH")) + "/");
      84              :         }
      85          109 :         if (getenv("SUMO_HOME") != nullptr) {
      86          218 :             phemPath.push_back(std::string(getenv("SUMO_HOME")) + "/data/emissions/PHEMlight/");
      87              :         }
      88          109 :         myHelper.setCommentPrefix("c");
      89          109 :         myHelper.setPHEMDataV("V4");
      90          109 :         myHelper.setclass(eClass);
      91          109 :         if (!myCEPHandler.GetCEP(phemPath, &myHelper)) {
      92            0 :             myEmissionClassStrings.remove(eClass, index);
      93            0 :             myIndex--;
      94            0 :             throw InvalidArgument("File for PHEM emission class " + eClass + " not found.\n" + myHelper.getErrMsg());
      95              :         }
      96          109 :         myCEPs[index] = myCEPHandler.getCEPS().find(myHelper.getgClass())->second;
      97              : #ifdef INTERNAL_PHEM
      98          109 :     }
      99              : #endif
     100          109 :     myEmissionClassStrings.addAlias(StringUtils::to_lower_case(eClass), index);
     101          109 :     return index;
     102              : }
     103              : 
     104              : 
     105              : SUMOEmissionClass
     106            0 : HelpersPHEMlight::getClass(const SUMOEmissionClass base, const std::string& vClass, const std::string& fuel, const std::string& eClass, const double weight) const {
     107            0 :     std::string eClassOffset = "0";
     108            0 :     if (eClass.length() == 5 && eClass.substr(0, 4) == "Euro") {
     109            0 :         if (eClass[4] >= '0' && eClass[4] <= '6') {
     110            0 :             eClassOffset = eClass.substr(4, 1);
     111              :         }
     112              :     }
     113              :     std::string desc;
     114            0 :     if (vClass == "Passenger") {
     115              :         desc = "PKW_";
     116            0 :         if (fuel == "Gasoline") {
     117              :             desc += "G_";
     118            0 :         } else if (fuel == "Diesel") {
     119              :             desc += "D_";
     120            0 :         } else if (fuel == "HybridGasoline") {
     121            0 :             desc = "H_" + desc + "G_";
     122            0 :         } else if (fuel == "HybridDiesel") {
     123            0 :             desc = "H_" + desc + "G_";
     124              :         }
     125            0 :         desc += "EU" + eClassOffset;
     126            0 :     } else if (vClass == "Moped") {
     127            0 :         desc = "KKR_G_EU" + eClassOffset;
     128            0 :     } else if (vClass == "Motorcycle") {
     129            0 :         desc = "MR_G_EU" + eClassOffset;
     130            0 :         if (fuel == "Gasoline2S") {
     131              :             desc += "_2T";
     132              :         } else {
     133              :             desc += "_4T";
     134              :         }
     135            0 :     } else if (vClass == "Delivery") {
     136              :         desc = "LNF_";
     137            0 :         if (fuel == "Gasoline") {
     138              :             desc += "G_";
     139            0 :         } else if (fuel == "Diesel") {
     140              :             desc += "D_";
     141              :         }
     142            0 :         desc += "EU" + eClassOffset + "_I";
     143            0 :         if (weight > 1305.) {
     144              :             desc += "I";
     145            0 :             if (weight > 1760.) {
     146              :                 desc += "I";
     147              :             }
     148              :         }
     149            0 :     } else if (vClass == "UrbanBus") {
     150            0 :         desc = "LB_D_EU" + eClassOffset;
     151            0 :     } else if (vClass == "Coach") {
     152            0 :         desc = "RB_D_EU" + eClassOffset;
     153            0 :     } else if (vClass == "Truck") {
     154            0 :         desc = "Solo_LKW_D_EU" + eClassOffset + "_I";
     155            0 :         if (weight > 1305.) {
     156              :             desc += "I";
     157              :         }
     158            0 :     } else if (vClass == "Trailer") {
     159            0 :         desc = "LSZ_D_EU" + eClassOffset;
     160              :     }
     161              :     if (myEmissionClassStrings.hasString(desc)) {
     162            0 :         return myEmissionClassStrings.get(desc);
     163              :     }
     164              :     return base;
     165              : }
     166              : 
     167              : 
     168              : std::string
     169            0 : HelpersPHEMlight::getAmitranVehicleClass(const SUMOEmissionClass c) const {
     170            0 :     const std::string name = myEmissionClassStrings.getString(c);
     171            0 :     if (name.find("KKR_") != std::string::npos) {
     172            0 :         return "Moped";
     173            0 :     } else if (name.find("RB_") != std::string::npos) {
     174            0 :         return "Coach";
     175            0 :     } else if (name.find("LB_") != std::string::npos) {
     176            0 :         return "UrbanBus";
     177            0 :     } else if (name.find("LNF_") != std::string::npos) {
     178            0 :         return "Delivery";
     179            0 :     } else if (name.find("LSZ_") != std::string::npos) {
     180            0 :         return "Trailer";
     181            0 :     } else if (name.find("MR_") != std::string::npos) {
     182            0 :         return "Motorcycle";
     183            0 :     } else if (name.find("LKW_") != std::string::npos) {
     184            0 :         return "Truck";
     185              :     }
     186            0 :     return "Passenger";
     187              : }
     188              : 
     189              : 
     190              : std::string
     191            0 : HelpersPHEMlight::getFuel(const SUMOEmissionClass c) const {
     192            0 :     const std::string name = myEmissionClassStrings.getString(c);
     193            0 :     std::string fuel = "Gasoline";
     194            0 :     if (name.find("_D_") != std::string::npos) {
     195              :         fuel = "Diesel";
     196              :     }
     197            0 :     if (name.find("H_") != std::string::npos) {
     198            0 :         fuel = "Hybrid" + fuel;
     199              :     }
     200            0 :     return fuel;
     201              : }
     202              : 
     203              : 
     204              : int
     205            0 : HelpersPHEMlight::getEuroClass(const SUMOEmissionClass c) const {
     206            0 :     const std::string name = myEmissionClassStrings.getString(c);
     207            0 :     if (name.find("_EU1") != std::string::npos) {
     208              :         return 1;
     209            0 :     } else if (name.find("_EU2") != std::string::npos) {
     210              :         return 2;
     211            0 :     } else if (name.find("_EU3") != std::string::npos) {
     212              :         return 3;
     213            0 :     } else if (name.find("_EU4") != std::string::npos) {
     214              :         return 4;
     215            0 :     } else if (name.find("_EU5") != std::string::npos) {
     216              :         return 5;
     217            0 :     } else if (name.find("_EU6") != std::string::npos) {
     218            0 :         return 6;
     219              :     }
     220              :     return 0;
     221              : }
     222              : 
     223              : 
     224              : double
     225          122 : HelpersPHEMlight::getWeight(const SUMOEmissionClass c) const {
     226          122 :     const std::string name = myEmissionClassStrings.getString(c);
     227          122 :     if (name.find("LNF_") != std::string::npos) {
     228            0 :         if (name.find("_III") != std::string::npos) {
     229              :             return 2630.;
     230            0 :         } else if (name.find("_II") != std::string::npos) {
     231              :             return 1532.;
     232            0 :         } else if (name.find("_I") != std::string::npos) {
     233              :             return 652.;
     234              :         }
     235              :     }
     236          122 :     if (name.find("Solo_LKW_") != std::string::npos) {
     237            0 :         if (name.find("_II") != std::string::npos) {
     238              :             return 8398.;
     239            0 :         } else if (name.find("_I") != std::string::npos) {
     240              :             return 18702.;
     241              :         }
     242              :     }
     243              :     return -1.;
     244              : }
     245              : 
     246              : 
     247              : double
     248      1051968 : HelpersPHEMlight::getEmission(const PHEMCEP* oldCep, PHEMlightdll::CEP* currCep, const std::string& e, const double p, const double v) const {
     249      1051968 :     if (oldCep != nullptr) {
     250            0 :         return oldCep->GetEmission(e, p, v);
     251              :     }
     252      1051968 :     return currCep->GetEmission(e, p, v, &myHelper);
     253              : }
     254              : 
     255              : 
     256              : double
     257       942732 : HelpersPHEMlight::getModifiedAccel(const SUMOEmissionClass c, const double v, const double a, const double slope, const EnergyParams* /* param */) const {
     258       942732 :     PHEMlightdll::CEP* currCep = myCEPs.count(c) == 0 ? 0 : myCEPs.find(c)->second;
     259       942732 :     if (currCep != nullptr) {
     260       942732 :         return v == 0.0 ? 0.0 : MIN2(a, currCep->GetMaxAccel(v, slope));
     261              :     }
     262              :     return a;
     263              : }
     264              : 
     265              : 
     266              : double
     267         2358 : HelpersPHEMlight::getCoastingDecel(const SUMOEmissionClass c, const double v, const double a, const double slope, const EnergyParams* /* param */) const {
     268         2358 :     return myCEPs.count(c) == 0 ? 0. : myCEPs.find(c)->second->GetDecelCoast(v, a, slope);
     269              : }
     270              : 
     271              : 
     272              : double
     273       963452 : HelpersPHEMlight::compute(const SUMOEmissionClass c, const PollutantsInterface::EmissionType e, const double v, const double a, const double slope, const EnergyParams* param) const {
     274       963452 :     if (param != nullptr && param->isEngineOff()) {
     275              :         return 0.;
     276              :     }
     277              :     const double corrSpeed = MAX2(0.0, v);
     278              :     double power = 0.;
     279              : #ifdef INTERNAL_PHEM
     280       942732 :     const PHEMCEP* const oldCep = PHEMCEPHandler::getHandlerInstance().GetCep(c);
     281       942732 :     if (oldCep != nullptr) {
     282            0 :         if (v > IDLE_SPEED && a < oldCep->GetDecelCoast(corrSpeed, a, slope, 0)) {
     283              :             // coasting without power use only works if the engine runs above idle speed and
     284              :             // the vehicle does not accelerate beyond friction losses
     285              :             return 0;
     286              :         }
     287            0 :         power = oldCep->CalcPower(corrSpeed, a, slope);
     288              :     }
     289              : #else
     290              :     const PHEMCEP* const oldCep = 0;
     291              : #endif
     292       942732 :     PHEMlightdll::CEP* currCep = myCEPs.count(c) == 0 ? 0 : myCEPs.find(c)->second;
     293       942732 :     if (currCep != nullptr) {
     294       942732 :         const double corrAcc = getModifiedAccel(c, corrSpeed, a, slope, param);
     295      1885464 :         if (currCep->getFuelType() != PHEMlightdll::Constants::strBEV &&
     296       942732 :                 corrAcc < currCep->GetDecelCoast(corrSpeed, corrAcc, slope) &&
     297        25536 :                 corrSpeed > PHEMlightdll::Constants::ZERO_SPEED_ACCURACY) {
     298              :             // the IDLE_SPEED fix above is now directly in the decel coast calculation.
     299              :             return 0;
     300              :         }
     301       920472 :         power = currCep->CalcPower(corrSpeed, corrAcc, slope);
     302              :     }
     303       920472 :     const std::string& fuelType = oldCep != nullptr ? oldCep->GetVehicleFuelType() : currCep->getFuelType();
     304       920472 :     switch (e) {
     305              :         case PollutantsInterface::CO:
     306       131496 :             return getEmission(oldCep, currCep, "CO", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     307       131496 :         case PollutantsInterface::CO2:
     308       131496 :             if (oldCep != nullptr) {
     309            0 :                 return getEmission(oldCep, currCep, "FC", power, corrSpeed) * 3.15 / SECONDS_PER_HOUR * 1000.;
     310              :             }
     311       262992 :             return currCep->GetCO2Emission(getEmission(nullptr, currCep, "FC", power, corrSpeed),
     312              :                                            getEmission(nullptr, currCep, "CO", power, corrSpeed),
     313       131496 :                                            getEmission(nullptr, currCep, "HC", power, corrSpeed), &myHelper) / SECONDS_PER_HOUR * 1000.;
     314              :         case PollutantsInterface::HC:
     315       131496 :             return getEmission(oldCep, currCep, "HC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     316              :         case PollutantsInterface::NO_X:
     317       131496 :             return getEmission(oldCep, currCep, "NOx", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     318              :         case PollutantsInterface::PM_X:
     319       131496 :             return getEmission(oldCep, currCep, "PM", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     320       131496 :         case PollutantsInterface::FUEL: {
     321       131496 :             if (myVolumetricFuel && fuelType == PHEMlightdll::Constants::strDiesel) { // divide by average diesel density of 836 g/l
     322            0 :                 return getEmission(oldCep, currCep, "FC", power, corrSpeed) / 836. / SECONDS_PER_HOUR * 1000.;
     323              :             }
     324       131496 :             if (myVolumetricFuel && fuelType == PHEMlightdll::Constants::strGasoline) { // divide by average gasoline density of 742 g/l
     325            0 :                 return getEmission(oldCep, currCep, "FC", power, corrSpeed) / 742. / SECONDS_PER_HOUR * 1000.;
     326              :             }
     327       131496 :             if (fuelType == PHEMlightdll::Constants::strBEV) {
     328              :                 return 0.;
     329              :             }
     330       131496 :             return getEmission(oldCep, currCep, "FC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.; // still in mg even if myVolumetricFuel is set!
     331              :         }
     332       131496 :         case PollutantsInterface::ELEC:
     333       131496 :             if (fuelType == PHEMlightdll::Constants::strBEV) {
     334            0 :                 return getEmission(oldCep, currCep, "FC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     335              :             }
     336              :             return 0;
     337              :     }
     338              :     // should never get here
     339              :     return 0.;
     340              : }
     341              : 
     342              : 
     343              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1