LCOV - code coverage report
Current view: top level - src/utils/emissions - HelpersPHEMlight.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 69 161 42.9 %
Date: 2024-05-04 15:27:10 Functions: 7 13 53.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       45820 : HelpersPHEMlight::HelpersPHEMlight() :
      44             :     PollutantsInterface::Helper("PHEMlight", PHEMLIGHT_BASE, -1),
      45       91640 :     myIndex(PHEMLIGHT_BASE) {
      46       45820 : }
      47             : 
      48             : 
      49       91640 : HelpersPHEMlight::~HelpersPHEMlight() {
      50       91753 :     for (const auto& cep : myCEPs) {
      51         113 :         delete cep.second;
      52             :     }
      53      183280 : }
      54             : 
      55             : 
      56             : SUMOEmissionClass
      57         160 : HelpersPHEMlight::getClassByName(const std::string& eClass, const SUMOVehicleClass vc) {
      58         220 :     if (eClass == "unknown" && !myEmissionClassStrings.hasString("unknown")) {
      59          60 :         myEmissionClassStrings.addAlias("unknown", getClassByName("PC_G_EU4", vc));
      60             :     }
      61         160 :     if (eClass == "default" && !myEmissionClassStrings.hasString("default")) {
      62           0 :         myEmissionClassStrings.addAlias("default", getClassByName("PC_G_EU4", vc));
      63             :     }
      64         160 :     if (myEmissionClassStrings.hasString(eClass)) {
      65          47 :         return myEmissionClassStrings.get(eClass);
      66             :     }
      67         113 :     if (eClass.size() < 6) {
      68           0 :         throw InvalidArgument("Unknown emission class '" + eClass + "'.");
      69             :     }
      70         113 :     int index = myIndex++;
      71         113 :     const std::string type = eClass.substr(0, 3);
      72         565 :     if (type == "HDV" || type == "LB_" || type == "RB_" || type == "LSZ" || eClass.find("LKW") != std::string::npos) {
      73           0 :         index |= PollutantsInterface::HEAVY_BIT;
      74             :     }
      75         339 :     myEmissionClassStrings.insert(eClass, index);
      76             : #ifdef INTERNAL_PHEM
      77         339 :     if (type == "HDV" || type == "LCV" || type == "PC_" || !PHEMCEPHandler::getHandlerInstance().Load(index, eClass)) {
      78             : #endif
      79         226 :         myVolumetricFuel = OptionsCont::getOptions().getBool("emissions.volumetric-fuel");
      80             :         std::vector<std::string> phemPath;
      81         226 :         phemPath.push_back(OptionsCont::getOptions().getString("phemlight-path") + "/");
      82         113 :         if (getenv("PHEMLIGHT_PATH") != nullptr) {
      83           0 :             phemPath.push_back(std::string(getenv("PHEMLIGHT_PATH")) + "/");
      84             :         }
      85         113 :         if (getenv("SUMO_HOME") != nullptr) {
      86         226 :             phemPath.push_back(std::string(getenv("SUMO_HOME")) + "/data/emissions/PHEMlight/");
      87             :         }
      88         113 :         myHelper.setCommentPrefix("c");
      89         113 :         myHelper.setPHEMDataV("V4");
      90         113 :         myHelper.setclass(eClass);
      91         113 :         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         113 :         myCEPs[index] = myCEPHandler.getCEPS().find(myHelper.getgClass())->second;
      97             : #ifdef INTERNAL_PHEM
      98         113 :     }
      99             : #endif
     100         113 :     myEmissionClassStrings.addAlias(StringUtils::to_lower_case(eClass), index);
     101         113 :     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           0 :     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           0 : HelpersPHEMlight::getWeight(const SUMOEmissionClass c) const {
     226           0 :     const std::string name = myEmissionClassStrings.getString(c);
     227           0 :     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           0 :     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           0 :             return 18702.;
     241             :         }
     242             :     }
     243             :     return -1.;
     244             : }
     245             : 
     246             : 
     247             : double
     248     1068896 : HelpersPHEMlight::getEmission(const PHEMCEP* oldCep, PHEMlightdll::CEP* currCep, const std::string& e, const double p, const double v) const {
     249     1068896 :     if (oldCep != nullptr) {
     250           0 :         return oldCep->GetEmission(e, p, v);
     251             :     }
     252     1068896 :     return currCep->GetEmission(e, p, v, &myHelper);
     253             : }
     254             : 
     255             : 
     256             : double
     257      960120 : HelpersPHEMlight::getModifiedAccel(const SUMOEmissionClass c, const double v, const double a, const double slope, const EnergyParams* /* param */) const {
     258      960120 :     PHEMlightdll::CEP* currCep = myCEPs.count(c) == 0 ? 0 : myCEPs.find(c)->second;
     259      960120 :     if (currCep != nullptr) {
     260      960120 :         return v == 0.0 ? 0.0 : MIN2(a, currCep->GetMaxAccel(v, slope));
     261             :     }
     262             :     return a;
     263             : }
     264             : 
     265             : 
     266             : double
     267        4716 : HelpersPHEMlight::getCoastingDecel(const SUMOEmissionClass c, const double v, const double a, const double slope, const EnergyParams* /* param */) const {
     268        4716 :     return myCEPs.count(c) == 0 ? 0. : myCEPs.find(c)->second->GetDecelCoast(v, a, slope);
     269             : }
     270             : 
     271             : 
     272             : double
     273      980840 : HelpersPHEMlight::compute(const SUMOEmissionClass c, const PollutantsInterface::EmissionType e, const double v, const double a, const double slope, const EnergyParams* param) const {
     274      980840 :     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      960120 :     const PHEMCEP* const oldCep = PHEMCEPHandler::getHandlerInstance().GetCep(c);
     281      960120 :     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      960120 :     PHEMlightdll::CEP* currCep = myCEPs.count(c) == 0 ? 0 : myCEPs.find(c)->second;
     293      960120 :     if (currCep != nullptr) {
     294      960120 :         const double corrAcc = getModifiedAccel(c, corrSpeed, a, slope, param);
     295     1920240 :         if (currCep->getFuelType() != PHEMlightdll::Constants::strBEV &&
     296      960120 :                 corrAcc < currCep->GetDecelCoast(corrSpeed, corrAcc, slope) &&
     297       28280 :                 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      935284 :         power = currCep->CalcPower(corrSpeed, corrAcc, slope);
     302             :     }
     303      935284 :     const std::string& fuelType = oldCep != nullptr ? oldCep->GetVehicleFuelType() : currCep->getFuelType();
     304      935284 :     switch (e) {
     305             :         case PollutantsInterface::CO:
     306      133612 :             return getEmission(oldCep, currCep, "CO", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     307      133612 :         case PollutantsInterface::CO2:
     308      133612 :             if (oldCep != nullptr) {
     309           0 :                 return getEmission(oldCep, currCep, "FC", power, corrSpeed) * 3.15 / SECONDS_PER_HOUR * 1000.;
     310             :             }
     311      267224 :             return currCep->GetCO2Emission(getEmission(nullptr, currCep, "FC", power, corrSpeed),
     312             :                                            getEmission(nullptr, currCep, "CO", power, corrSpeed),
     313      133612 :                                            getEmission(nullptr, currCep, "HC", power, corrSpeed), &myHelper) / SECONDS_PER_HOUR * 1000.;
     314             :         case PollutantsInterface::HC:
     315      133612 :             return getEmission(oldCep, currCep, "HC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     316             :         case PollutantsInterface::NO_X:
     317      133612 :             return getEmission(oldCep, currCep, "NOx", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     318             :         case PollutantsInterface::PM_X:
     319      133612 :             return getEmission(oldCep, currCep, "PM", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
     320      133612 :         case PollutantsInterface::FUEL: {
     321      133612 :             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      133612 :             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      133612 :             if (fuelType == PHEMlightdll::Constants::strBEV) {
     328             :                 return 0.;
     329             :             }
     330      133612 :             return getEmission(oldCep, currCep, "FC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.; // still in mg even if myVolumetricFuel is set!
     331             :         }
     332      133612 :         case PollutantsInterface::ELEC:
     333      133612 :             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 1.14