Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/emissions/HelpersPHEMlight.cpp
169678 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2013-2025 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
HelpersPHEMlight::HelpersPHEMlight() :
44
PollutantsInterface::Helper("PHEMlight", PHEMLIGHT_BASE, -1),
45
myIndex(PHEMLIGHT_BASE) {
46
}
47
48
49
HelpersPHEMlight::~HelpersPHEMlight() {
50
for (const auto& cep : myCEPs) {
51
delete cep.second;
52
}
53
}
54
55
56
SUMOEmissionClass
57
HelpersPHEMlight::getClassByName(const std::string& eClass, const SUMOVehicleClass vc) {
58
if (eClass == "unknown" && !myEmissionClassStrings.hasString("unknown")) {
59
myEmissionClassStrings.addAlias("unknown", getClassByName("PC_G_EU4", vc));
60
}
61
if (eClass == "default" && !myEmissionClassStrings.hasString("default")) {
62
myEmissionClassStrings.addAlias("default", getClassByName("PC_G_EU4", vc));
63
}
64
if (myEmissionClassStrings.hasString(eClass)) {
65
return myEmissionClassStrings.get(eClass);
66
}
67
if (eClass.size() < 6) {
68
throw InvalidArgument("Unknown emission class '" + eClass + "'.");
69
}
70
int index = myIndex++;
71
const std::string type = eClass.substr(0, 3);
72
if (type == "HDV" || type == "LB_" || type == "RB_" || type == "LSZ" || eClass.find("LKW") != std::string::npos) {
73
index |= PollutantsInterface::HEAVY_BIT;
74
}
75
myEmissionClassStrings.insert(eClass, index);
76
#ifdef INTERNAL_PHEM
77
if (type == "HDV" || type == "LCV" || type == "PC_" || !PHEMCEPHandler::getHandlerInstance().Load(index, eClass)) {
78
#endif
79
myVolumetricFuel = OptionsCont::getOptions().getBool("emissions.volumetric-fuel");
80
std::vector<std::string> phemPath;
81
phemPath.push_back(OptionsCont::getOptions().getString("phemlight-path") + "/");
82
if (getenv("PHEMLIGHT_PATH") != nullptr) {
83
phemPath.push_back(std::string(getenv("PHEMLIGHT_PATH")) + "/");
84
}
85
if (getenv("SUMO_HOME") != nullptr) {
86
phemPath.push_back(std::string(getenv("SUMO_HOME")) + "/data/emissions/PHEMlight/");
87
}
88
myHelper.setCommentPrefix("c");
89
myHelper.setPHEMDataV("V4");
90
myHelper.setclass(eClass);
91
if (!myCEPHandler.GetCEP(phemPath, &myHelper)) {
92
myEmissionClassStrings.remove(eClass, index);
93
myIndex--;
94
throw InvalidArgument("File for PHEM emission class " + eClass + " not found.\n" + myHelper.getErrMsg());
95
}
96
myCEPs[index] = myCEPHandler.getCEPS().find(myHelper.getgClass())->second;
97
#ifdef INTERNAL_PHEM
98
}
99
#endif
100
myEmissionClassStrings.addAlias(StringUtils::to_lower_case(eClass), index);
101
return index;
102
}
103
104
105
SUMOEmissionClass
106
HelpersPHEMlight::getClass(const SUMOEmissionClass base, const std::string& vClass, const std::string& fuel, const std::string& eClass, const double weight) const {
107
std::string eClassOffset = "0";
108
if (eClass.length() == 5 && eClass.substr(0, 4) == "Euro") {
109
if (eClass[4] >= '0' && eClass[4] <= '6') {
110
eClassOffset = eClass.substr(4, 1);
111
}
112
}
113
std::string desc;
114
if (vClass == "Passenger") {
115
desc = "PKW_";
116
if (fuel == "Gasoline") {
117
desc += "G_";
118
} else if (fuel == "Diesel") {
119
desc += "D_";
120
} else if (fuel == "HybridGasoline") {
121
desc = "H_" + desc + "G_";
122
} else if (fuel == "HybridDiesel") {
123
desc = "H_" + desc + "G_";
124
}
125
desc += "EU" + eClassOffset;
126
} else if (vClass == "Moped") {
127
desc = "KKR_G_EU" + eClassOffset;
128
} else if (vClass == "Motorcycle") {
129
desc = "MR_G_EU" + eClassOffset;
130
if (fuel == "Gasoline2S") {
131
desc += "_2T";
132
} else {
133
desc += "_4T";
134
}
135
} else if (vClass == "Delivery") {
136
desc = "LNF_";
137
if (fuel == "Gasoline") {
138
desc += "G_";
139
} else if (fuel == "Diesel") {
140
desc += "D_";
141
}
142
desc += "EU" + eClassOffset + "_I";
143
if (weight > 1305.) {
144
desc += "I";
145
if (weight > 1760.) {
146
desc += "I";
147
}
148
}
149
} else if (vClass == "UrbanBus") {
150
desc = "LB_D_EU" + eClassOffset;
151
} else if (vClass == "Coach") {
152
desc = "RB_D_EU" + eClassOffset;
153
} else if (vClass == "Truck") {
154
desc = "Solo_LKW_D_EU" + eClassOffset + "_I";
155
if (weight > 1305.) {
156
desc += "I";
157
}
158
} else if (vClass == "Trailer") {
159
desc = "LSZ_D_EU" + eClassOffset;
160
}
161
if (myEmissionClassStrings.hasString(desc)) {
162
return myEmissionClassStrings.get(desc);
163
}
164
return base;
165
}
166
167
168
std::string
169
HelpersPHEMlight::getAmitranVehicleClass(const SUMOEmissionClass c) const {
170
const std::string name = myEmissionClassStrings.getString(c);
171
if (name.find("KKR_") != std::string::npos) {
172
return "Moped";
173
} else if (name.find("RB_") != std::string::npos) {
174
return "Coach";
175
} else if (name.find("LB_") != std::string::npos) {
176
return "UrbanBus";
177
} else if (name.find("LNF_") != std::string::npos) {
178
return "Delivery";
179
} else if (name.find("LSZ_") != std::string::npos) {
180
return "Trailer";
181
} else if (name.find("MR_") != std::string::npos) {
182
return "Motorcycle";
183
} else if (name.find("LKW_") != std::string::npos) {
184
return "Truck";
185
}
186
return "Passenger";
187
}
188
189
190
std::string
191
HelpersPHEMlight::getFuel(const SUMOEmissionClass c) const {
192
const std::string name = myEmissionClassStrings.getString(c);
193
std::string fuel = "Gasoline";
194
if (name.find("_D_") != std::string::npos) {
195
fuel = "Diesel";
196
}
197
if (name.find("H_") != std::string::npos) {
198
fuel = "Hybrid" + fuel;
199
}
200
return fuel;
201
}
202
203
204
int
205
HelpersPHEMlight::getEuroClass(const SUMOEmissionClass c) const {
206
const std::string name = myEmissionClassStrings.getString(c);
207
if (name.find("_EU1") != std::string::npos) {
208
return 1;
209
} else if (name.find("_EU2") != std::string::npos) {
210
return 2;
211
} else if (name.find("_EU3") != std::string::npos) {
212
return 3;
213
} else if (name.find("_EU4") != std::string::npos) {
214
return 4;
215
} else if (name.find("_EU5") != std::string::npos) {
216
return 5;
217
} else if (name.find("_EU6") != std::string::npos) {
218
return 6;
219
}
220
return 0;
221
}
222
223
224
double
225
HelpersPHEMlight::getWeight(const SUMOEmissionClass c) const {
226
const std::string name = myEmissionClassStrings.getString(c);
227
if (name.find("LNF_") != std::string::npos) {
228
if (name.find("_III") != std::string::npos) {
229
return 2630.;
230
} else if (name.find("_II") != std::string::npos) {
231
return 1532.;
232
} else if (name.find("_I") != std::string::npos) {
233
return 652.;
234
}
235
}
236
if (name.find("Solo_LKW_") != std::string::npos) {
237
if (name.find("_II") != std::string::npos) {
238
return 8398.;
239
} else if (name.find("_I") != std::string::npos) {
240
return 18702.;
241
}
242
}
243
return -1.;
244
}
245
246
247
double
248
HelpersPHEMlight::getEmission(const PHEMCEP* oldCep, PHEMlightdll::CEP* currCep, const std::string& e, const double p, const double v) const {
249
if (oldCep != nullptr) {
250
return oldCep->GetEmission(e, p, v);
251
}
252
return currCep->GetEmission(e, p, v, &myHelper);
253
}
254
255
256
double
257
HelpersPHEMlight::getModifiedAccel(const SUMOEmissionClass c, const double v, const double a, const double slope, const EnergyParams* /* param */) const {
258
PHEMlightdll::CEP* currCep = myCEPs.count(c) == 0 ? 0 : myCEPs.find(c)->second;
259
if (currCep != nullptr) {
260
return v == 0.0 ? 0.0 : MIN2(a, currCep->GetMaxAccel(v, slope));
261
}
262
return a;
263
}
264
265
266
double
267
HelpersPHEMlight::getCoastingDecel(const SUMOEmissionClass c, const double v, const double a, const double slope, const EnergyParams* /* param */) const {
268
return myCEPs.count(c) == 0 ? 0. : myCEPs.find(c)->second->GetDecelCoast(v, a, slope);
269
}
270
271
272
double
273
HelpersPHEMlight::compute(const SUMOEmissionClass c, const PollutantsInterface::EmissionType e, const double v, const double a, const double slope, const EnergyParams* param) const {
274
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
const PHEMCEP* const oldCep = PHEMCEPHandler::getHandlerInstance().GetCep(c);
281
if (oldCep != nullptr) {
282
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
power = oldCep->CalcPower(corrSpeed, a, slope);
288
}
289
#else
290
const PHEMCEP* const oldCep = 0;
291
#endif
292
PHEMlightdll::CEP* currCep = myCEPs.count(c) == 0 ? 0 : myCEPs.find(c)->second;
293
if (currCep != nullptr) {
294
const double corrAcc = getModifiedAccel(c, corrSpeed, a, slope, param);
295
if (currCep->getFuelType() != PHEMlightdll::Constants::strBEV &&
296
corrAcc < currCep->GetDecelCoast(corrSpeed, corrAcc, slope) &&
297
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
power = currCep->CalcPower(corrSpeed, corrAcc, slope);
302
}
303
const std::string& fuelType = oldCep != nullptr ? oldCep->GetVehicleFuelType() : currCep->getFuelType();
304
switch (e) {
305
case PollutantsInterface::CO:
306
return getEmission(oldCep, currCep, "CO", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
307
case PollutantsInterface::CO2:
308
if (oldCep != nullptr) {
309
return getEmission(oldCep, currCep, "FC", power, corrSpeed) * 3.15 / SECONDS_PER_HOUR * 1000.;
310
}
311
return currCep->GetCO2Emission(getEmission(nullptr, currCep, "FC", power, corrSpeed),
312
getEmission(nullptr, currCep, "CO", power, corrSpeed),
313
getEmission(nullptr, currCep, "HC", power, corrSpeed), &myHelper) / SECONDS_PER_HOUR * 1000.;
314
case PollutantsInterface::HC:
315
return getEmission(oldCep, currCep, "HC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
316
case PollutantsInterface::NO_X:
317
return getEmission(oldCep, currCep, "NOx", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
318
case PollutantsInterface::PM_X:
319
return getEmission(oldCep, currCep, "PM", power, corrSpeed) / SECONDS_PER_HOUR * 1000.;
320
case PollutantsInterface::FUEL: {
321
if (myVolumetricFuel && fuelType == PHEMlightdll::Constants::strDiesel) { // divide by average diesel density of 836 g/l
322
return getEmission(oldCep, currCep, "FC", power, corrSpeed) / 836. / SECONDS_PER_HOUR * 1000.;
323
}
324
if (myVolumetricFuel && fuelType == PHEMlightdll::Constants::strGasoline) { // divide by average gasoline density of 742 g/l
325
return getEmission(oldCep, currCep, "FC", power, corrSpeed) / 742. / SECONDS_PER_HOUR * 1000.;
326
}
327
if (fuelType == PHEMlightdll::Constants::strBEV) {
328
return 0.;
329
}
330
return getEmission(oldCep, currCep, "FC", power, corrSpeed) / SECONDS_PER_HOUR * 1000.; // still in mg even if myVolumetricFuel is set!
331
}
332
case PollutantsInterface::ELEC:
333
if (fuelType == PHEMlightdll::Constants::strBEV) {
334
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
/****************************************************************************/
344
345