Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2016-2024 German Aerospace Center (DLR) and others.
4 : // PHEMlight module
5 : // Copyright (C) 2016-2023 Technische Universitaet Graz, https://www.tugraz.at/
6 : // This program and the accompanying materials are made available under the
7 : // terms of the Eclipse Public License 2.0 which is available at
8 : // https://www.eclipse.org/legal/epl-2.0/
9 : // This Source Code may also be made available under the following Secondary
10 : // Licenses when the conditions for such availability set forth in the Eclipse
11 : // Public License 2.0 are satisfied: GNU General Public License, version 2
12 : // or later which is available at
13 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
14 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
15 : /****************************************************************************/
16 : /// @file Correction.cpp
17 : /// @author Martin Dippold
18 : /// @author Michael Behrisch
19 : /// @date July 2016
20 : ///
21 : //
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <fstream>
26 : #include <utils/common/StringUtils.h>
27 : #include "Correction.h"
28 : #include "Helpers.h"
29 : #include "Constants.h"
30 :
31 :
32 : namespace PHEMlightdllV5 {
33 :
34 4 : Correction::Correction(const std::vector<std::string>& dataPath)
35 4 : : privateDataPath(dataPath) {
36 4 : setUseDet(false);
37 4 : setUseTNOx(false);
38 4 : setAmbTemp(20);
39 4 : setYear(2022);
40 4 : setVehMileage(-1);
41 4 : setDETFilePath("Deterioration.det");
42 4 : setVMAFilePath("Mileage.vma");
43 4 : setTNOxFilePath("NOxCor.tno");
44 4 : }
45 :
46 36 : const bool& Correction::getUseDet() const {
47 36 : return privateUseDet;
48 : }
49 :
50 6 : void Correction::setUseDet(const bool& value) {
51 6 : privateUseDet = value;
52 6 : }
53 :
54 4 : const std::string& Correction::getDETFilePath() const {
55 4 : return privateDETFilePath;
56 : }
57 :
58 4 : void Correction::setDETFilePath(const std::string& value) {
59 4 : privateDETFilePath = value;
60 4 : }
61 :
62 4 : const std::string& Correction::getVMAFilePath() const {
63 4 : return privateVMAFilePath;
64 : }
65 :
66 4 : void Correction::setVMAFilePath(const std::string& value) {
67 4 : privateVMAFilePath = value;
68 4 : }
69 :
70 0 : const int& Correction::getYear() const {
71 0 : return privateYear;
72 : }
73 :
74 6 : void Correction::setYear(const int& value) {
75 6 : privateYear = value;
76 6 : }
77 :
78 12 : const double& Correction::getVehMileage() const {
79 12 : return privateVehMileage;
80 : }
81 :
82 8 : void Correction::setVehMileage(const double& value) {
83 8 : privateVehMileage = value;
84 8 : }
85 :
86 2 : bool Correction::ReadDet(std::string& ErrMSG) {
87 : //Read Detoriation file
88 2 : if (!ReadDETFile(ErrMSG)) {
89 : return false;
90 : }
91 :
92 2 : if (!ReadVMAFile(ErrMSG)) {
93 : return false;
94 : }
95 :
96 : // Return value
97 : return true;
98 : }
99 :
100 2 : bool Correction::ReadDETFile(std::string& ErrMSG) {
101 : //Open file
102 2 : std::ifstream detReader;
103 4 : for (const std::string& p : privateDataPath) {
104 12 : detReader.open((p + getDETFilePath()).c_str());
105 4 : if (detReader.good()) {
106 : break;
107 : }
108 : }
109 2 : if (!detReader.good()) {
110 0 : ErrMSG = "File does not exist! (" + getDETFilePath() + ")";
111 0 : return false;
112 : }
113 :
114 : //**** DET Datei einlesen ****
115 : try {
116 2 : detReader >> DETdata;
117 0 : } catch (...) {
118 0 : ErrMSG = "Error during file read! (" + getDETFilePath() + ")";
119 : return false;
120 0 : }
121 :
122 : // Return value
123 : return true;
124 2 : }
125 :
126 2 : bool Correction::ReadVMAFile(std::string& ErrMSG) {
127 : //Open file
128 2 : std::ifstream vmaReader;
129 4 : for (const std::string& p : privateDataPath) {
130 12 : vmaReader.open((p + getVMAFilePath()).c_str());
131 4 : if (vmaReader.good()) {
132 : break;
133 : }
134 : }
135 2 : if (!vmaReader.good()) {
136 0 : ErrMSG = "File does not exist! (" + getVMAFilePath() + ")";
137 0 : return false;
138 : }
139 :
140 : //**** VMA Datei einlesen ****
141 : try {
142 2 : vmaReader >> VMAdata;
143 0 : } catch (...) {
144 0 : ErrMSG = "Error during file read! (" + getVMAFilePath() + ")";
145 : return false;
146 0 : }
147 :
148 : // Return value
149 : return true;
150 2 : }
151 :
152 2 : bool Correction::IniDETfactor(Helpers* Helper) {
153 : //Initialise
154 2 : DETFactors = std::map<std::string, double>();
155 :
156 4 : if (DETdata.at("Vehicle").contains(Helper->getvClass())) {
157 4 : if (DETdata.at("Vehicle").at(Helper->getvClass()).at("PropulsionClass").contains(Helper->getpClass())) {
158 10 : for (const auto& it : DETdata.at("Vehicle").at(Helper->getvClass()).at("PropulsionClass").at(Helper->getpClass()).at("Emission").items()) {
159 18 : std::string EUclass = StringUtils::replace(Helper->geteClass(), "EU", "EURO ");
160 :
161 : //PC special classes check (ab, c, d, d-Temp). If available use otherwise use "EURO 6" if available
162 6 : if ((Helper->getvClass() == Constants::strPKW || Helper->getvClass() == Constants::strLNF) && EUclass.length() > 6) {
163 0 : std::string EUclassShort = EUclass.substr(0, 6);
164 :
165 0 : if (!it.value().at("EUClass").contains(EUclass) && it.value().at("EUClass").contains(EUclassShort)) {
166 : EUclass = EUclassShort;
167 : }
168 : }
169 :
170 : //Store in upper case because retrieval will be upper case as well
171 6 : std::string key = it.key();
172 14 : std::transform(key.begin(), key.end(), key.begin(), [](char c) { return (char)::toupper(c); });
173 : //Get the factor
174 12 : if (it.value().at("EUClass").contains(EUclass)) {
175 12 : const std::vector<double>& Mileage = it.value().at("Mileage");
176 6 : const std::vector<double>& Factor = it.value().at("EUClass").at(EUclass);
177 6 : if (getVehMileage() < 0) {
178 2 : setVehMileage(GetMileage(Helper));
179 : }
180 :
181 6 : for (int i = 1; i < (int)Mileage.size(); i++) {
182 6 : if (i == 1 && Mileage[i] > getVehMileage()) {
183 6 : DETFactors.insert(std::make_pair(key, Factor[0]));
184 6 : break;
185 : }
186 0 : else if (i == (int)Mileage.size() - 1 && getVehMileage() > Mileage[i]) {
187 0 : DETFactors.insert(std::make_pair(key, Factor[i]));
188 0 : break;
189 : }
190 0 : else if (getVehMileage() < Mileage[i]) {
191 0 : DETFactors.insert(std::make_pair(key, Interpolate(getVehMileage(), Mileage[i - 1], Mileage[i], Factor[i - 1], Factor[i])));
192 0 : break;
193 : }
194 : }
195 6 : }
196 : else {
197 0 : DETFactors.insert(std::make_pair(key, 1));
198 : }
199 : }
200 : }
201 : }
202 :
203 : //Return value
204 2 : return true;
205 : }
206 :
207 2 : double Correction::GetMileage(Helpers* Helper) {
208 : // Initialise
209 : double Mileage = 0;
210 :
211 4 : if (VMAdata.at("Vehicle").contains(Helper->getvClass())) {
212 4 : if (VMAdata.at("Vehicle").at(Helper->getvClass()).at("PropulsionClass").contains(Helper->getpClass())) {
213 2 : std::string Sclass = "0";
214 : //C# TO C++ CONVERTER NOTE: The following 'switch' operated on a string variable and was converted to C++ 'if-else' logic:
215 : // switch (Helper.sClass)
216 : //ORIGINAL LINE: case "":
217 2 : if (Helper->getsClass() == "") {
218 : Sclass = "0";
219 : }
220 : //ORIGINAL LINE: case "I":
221 0 : else if (Helper->getsClass() == "I") {
222 : Sclass = "1";
223 : }
224 : //ORIGINAL LINE: case "II":
225 0 : else if (Helper->getsClass() == "II") {
226 : Sclass = "2";
227 : }
228 : //ORIGINAL LINE: case "III":
229 0 : else if (Helper->getsClass() == "III") {
230 : Sclass = "3";
231 : }
232 :
233 4 : if (VMAdata.at("Vehicle").at(Helper->getvClass()).at("PropulsionClass").at(Helper->getpClass()).at("SizeClass").contains(Sclass)) {
234 4 : const nlohmann::json& sclassJson = VMAdata.at("Vehicle").at(Helper->getvClass()).at("PropulsionClass").at(Helper->getpClass()).at("SizeClass").at(Sclass);
235 6 : std::string EUclass = StringUtils::replace(Helper->geteClass(), "EU", "EURO ");
236 :
237 : //PC special classes check (ab, c, d, d-Temp). If available use otherwise use "EURO 6" if available
238 2 : if ((Helper->getvClass() == Constants::strPKW || Helper->getvClass() == Constants::strLNF) && EUclass.length() > 6) {
239 0 : std::string EUclassShort = EUclass.substr(0, 6);
240 :
241 0 : if (!sclassJson.at("EUClass").contains(EUclass) && sclassJson.at("EUClass").contains(EUclassShort)) {
242 : EUclass = EUclassShort;
243 : }
244 : }
245 :
246 : if (sclassJson.contains(EUclass)) {
247 : //Calculate Mileage
248 0 : const std::vector<double>& Factor = sclassJson.at(EUclass);
249 0 : int AnzYear = getYear() - 2020 + 1;
250 0 : Mileage = Factor[0] * std::pow(AnzYear, 3) + Factor[1] * std::pow(AnzYear, 2) + Factor[2] * AnzYear + Factor[3];
251 :
252 : //Check calculated mileage
253 0 : if (Mileage < 0) {
254 : Mileage = 0;
255 : }
256 0 : }
257 : }
258 : }
259 : }
260 :
261 :
262 : //Return value
263 2 : return Mileage;
264 : }
265 :
266 36 : const bool& Correction::getUseTNOx() const {
267 36 : return privateUseTNOx;
268 : }
269 :
270 6 : void Correction::setUseTNOx(const bool& value) {
271 6 : privateUseTNOx = value;
272 6 : }
273 :
274 4 : const std::string& Correction::getTNOxFilePath() const {
275 4 : return privateTNOxFilePath;
276 : }
277 :
278 4 : void Correction::setTNOxFilePath(const std::string& value) {
279 4 : privateTNOxFilePath = value;
280 4 : }
281 :
282 3 : const double& Correction::getAmbTemp() const {
283 3 : return privateAmbTemp;
284 : }
285 :
286 6 : void Correction::setAmbTemp(const double& value) {
287 6 : privateAmbTemp = value;
288 6 : }
289 :
290 2 : const double& Correction::getTNOxFactor() const {
291 2 : return privateTNOxFactor;
292 : }
293 :
294 3 : void Correction::setTNOxFactor(const double& value) {
295 3 : privateTNOxFactor = value;
296 3 : }
297 :
298 2 : bool Correction::ReadTNOx(std::string& ErrMSG) {
299 : //Open file
300 2 : std::ifstream tnoxReader;
301 4 : for (const std::string& p : privateDataPath) {
302 12 : tnoxReader.open((p + getTNOxFilePath()).c_str());
303 4 : if (tnoxReader.good()) {
304 : break;
305 : }
306 : }
307 2 : if (!tnoxReader.good()) {
308 0 : ErrMSG = "File does not exist! (" + getTNOxFilePath() + ")";
309 0 : return false;
310 : }
311 :
312 : //**** VMA Datei einlesen ****
313 : try {
314 2 : tnoxReader >> TNOxdata;
315 0 : } catch (...) {
316 0 : ErrMSG = "Error during file read! (" + getTNOxFilePath() + ")";
317 : return false;
318 0 : }
319 :
320 : // Return value
321 : return true;
322 2 : }
323 :
324 2 : bool Correction::IniTNOxfactor(Helpers* Helper) {
325 : //Initialise
326 2 : setTNOxFactor(1);
327 :
328 : //Calculation only for diesel vehicles
329 2 : if (Helper->getpClass() != Constants::strDiesel) {
330 : return true;
331 : }
332 :
333 2 : if (TNOxdata.at("Vehicle").contains(Helper->getvClass())) {
334 3 : std::string EUclass = StringUtils::replace(Helper->geteClass(), "EU", "EURO ");
335 :
336 : //PC special classes check (ab, c, d, d-Temp). If available use otherwise use "EURO 6" if available
337 1 : if ((Helper->getvClass() == Constants::strPKW || Helper->getvClass() == Constants::strLNF) && EUclass.length() > 6) {
338 0 : std::string EUclassShort = EUclass.substr(0, 6);
339 :
340 0 : if (!TNOxdata.at("Vehicle").at(Helper->getvClass()).at("EUClass").contains(EUclass) && TNOxdata.at("Vehicle").at(Helper->getvClass()).at("EUClass").contains(EUclassShort)) {
341 : EUclass = EUclassShort;
342 : }
343 : }
344 :
345 :
346 2 : if (TNOxdata.at("Vehicle").at(Helper->getvClass()).at("EUClass").contains(EUclass)) {
347 : //Check/set temperature borders, because calculation is a straight function
348 2 : const nlohmann::json& EUclassJson = TNOxdata.at("Vehicle").at(Helper->getvClass()).at("EUClass").at(EUclass);
349 1 : const double m = EUclassJson.at("m");
350 1 : const double c = EUclassJson.at("c");
351 1 : const double tb0 = EUclassJson.at("TB")[0];
352 1 : if (getAmbTemp() < tb0) {
353 0 : setTNOxFactor(m + c * tb0);
354 : }
355 2 : else if (getAmbTemp() > EUclassJson.at("TB")[1]) {
356 0 : setTNOxFactor(1);
357 : }
358 : else {
359 1 : setTNOxFactor(m + c * getAmbTemp());
360 : }
361 : }
362 : }
363 :
364 : //Return value
365 : return true;
366 : }
367 :
368 0 : double Correction::Interpolate(double px, double p1, double p2, double e1, double e2) {
369 0 : if (p2 == p1) {
370 : return e1;
371 : }
372 :
373 0 : return e1 + (px - p1) / (p2 - p1) * (e2 - e1);
374 : }
375 :
376 : }
|