Line data Source code
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 emissionsMap_main.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Michael Behrisch
17 : /// @date Wed, 21.08.2013
18 : ///
19 : // Main for an emissions map writer
20 : /****************************************************************************/
21 : #include <config.h>
22 :
23 : #ifdef HAVE_VERSION_H
24 : #include <version.h>
25 : #endif
26 :
27 : #include <iostream>
28 : #include <string>
29 : #include <ctime>
30 : #include <memory>
31 : #if __cplusplus >= 201703L
32 : #if __has_include(<filesystem>)
33 : #include <filesystem>
34 : namespace fs = std::filesystem;
35 : #elif __has_include(<experimental/filesystem>)
36 : #include <experimental/filesystem>
37 : namespace fs = std::experimental::filesystem;
38 : #endif
39 : #endif
40 : #include <utils/common/MsgHandler.h>
41 : #include <utils/common/StringUtils.h>
42 : #include <utils/options/Option.h>
43 : #include <utils/options/OptionsCont.h>
44 : #include <utils/options/OptionsIO.h>
45 : #include <utils/common/UtilExceptions.h>
46 : #include <utils/emissions/EnergyParams.h>
47 : #include <utils/emissions/PollutantsInterface.h>
48 : #include <utils/common/SystemFrame.h>
49 : #include <utils/common/ToString.h>
50 : #include <utils/xml/XMLSubSys.h>
51 : #include <utils/common/FileHelpers.h>
52 : #include <utils/iodevices/OutputDevice.h>
53 : #include "VTypesHandler.h"
54 :
55 :
56 : // ===========================================================================
57 : // functions
58 : // ===========================================================================
59 951 : void single(const OptionsCont& oc, const std::string& of, const std::string& className, SUMOEmissionClass c,
60 : double vMin, double vMax, double vStep,
61 : double aMin, double aMax, double aStep,
62 : double sMin, double sMax, double sStep,
63 : bool verbose) {
64 951 : if (verbose) {
65 0 : WRITE_MESSAGEF(TL("Writing map of '%' into '%'."), className, of);
66 : }
67 951 : std::ofstream o(of.c_str());
68 951 : if (!o.good()) {
69 0 : throw ProcessError(TLF("Could not open file '%' for writing.", of));
70 : }
71 :
72 951 : std::unique_ptr<EnergyParams> energyParams;
73 : std::map<std::string, SUMOVTypeParameter*> vTypes;
74 1901 : if (oc.isSet("vtype") || oc.isSet("additional-files")) {
75 2 : if (!oc.isSet("additional-files")) {
76 0 : throw ProcessError(TL("Option --vtype requires option --additional-files for loading vehicle types"));
77 : }
78 2 : if (!oc.isUsableFileList("additional-files")) {
79 0 : throw ProcessError();
80 : }
81 3 : for (const std::string& file : oc.getStringVector("additional-files")) {
82 1 : VTypesHandler typesHandler(file, vTypes);
83 1 : if (!XMLSubSys::runParser(typesHandler, file)) {
84 0 : throw ProcessError(TLF("Loading of % failed.", file));
85 : }
86 1 : }
87 2 : if (!oc.isSet("vtype") && vTypes.size() != 1) {
88 0 : throw ProcessError(TL("Vehicle type is not unique."));
89 : }
90 4 : const auto vTypeIt = oc.isSet("vtype") ? vTypes.find(oc.getString("vtype")) : vTypes.begin();
91 1 : if (vTypeIt == vTypes.end()) {
92 0 : throw ProcessError(TLF("Vehicle type '%' is not defined.", oc.getString("vtype")));
93 : }
94 2 : if (oc.isDefault("emission-class")) {
95 0 : c = vTypeIt->second->emissionClass;
96 : }
97 2 : energyParams = std::unique_ptr<EnergyParams>(new EnergyParams(vTypeIt->second));
98 : } else {
99 1900 : energyParams = std::unique_ptr<EnergyParams>(new EnergyParams());
100 : }
101 7608 : for (double v = vMin; v <= vMax; v += vStep) {
102 66570 : for (double a = aMin; a <= aMax; a += aStep) {
103 119826 : for (double s = sMin; s <= sMax; s += sStep) {
104 59913 : const PollutantsInterface::Emissions result = PollutantsInterface::computeAll(c, v, a, s, energyParams.get());
105 59913 : o << v << ";" << a << ";" << s << ";" << "CO" << ";" << result.CO << std::endl;
106 59913 : o << v << ";" << a << ";" << s << ";" << "CO2" << ";" << result.CO2 << std::endl;
107 59913 : o << v << ";" << a << ";" << s << ";" << "HC" << ";" << result.HC << std::endl;
108 59913 : o << v << ";" << a << ";" << s << ";" << "PMx" << ";" << result.PMx << std::endl;
109 59913 : o << v << ";" << a << ";" << s << ";" << "NOx" << ";" << result.NOx << std::endl;
110 59913 : o << v << ";" << a << ";" << s << ";" << "fuel" << ";" << result.fuel << std::endl;
111 59913 : o << v << ";" << a << ";" << s << ";" << "electricity" << ";" << result.electricity << std::endl;
112 : }
113 : }
114 : }
115 951 : }
116 :
117 :
118 :
119 :
120 : int
121 951 : main(int argc, char** argv) {
122 951 : OptionsCont& oc = OptionsCont::getOptions();
123 951 : oc.setApplicationDescription(TL("Builds and writes an emissions map for SUMO's emission models."));
124 1902 : oc.setApplicationName("emissionsMap", "Eclipse SUMO emissionsMap " VERSION_STRING);
125 : // add options
126 951 : SystemFrame::addConfigurationOptions(oc);
127 951 : oc.addOptionSubTopic("Input");
128 951 : oc.addOptionSubTopic("Processing");
129 951 : oc.doRegister("iterate", 'i', new Option_Bool(false));
130 1902 : oc.addDescription("iterate", "Processing", TL("If set, maps for all available emissions are written."));
131 :
132 951 : oc.doRegister("emission-class", 'e', new Option_String());
133 1902 : oc.addDescription("emission-class", "Processing", TL("Defines the name of the emission class to generate the map for."));
134 :
135 951 : oc.doRegister("additional-files", new Option_FileName());
136 1902 : oc.addDescription("additional-files", "Input", TL("Load emission parameters (vTypes) from FILE(s)"));
137 :
138 951 : oc.doRegister("vtype", new Option_String());
139 1902 : oc.addDescription("vtype", "Input", TL("Defines the vehicle type to use for emission parameters."));
140 :
141 951 : oc.doRegister("v-min", new Option_Float(0.));
142 1902 : oc.addDescription("v-min", "Processing", TL("Defines the minimum velocity boundary of the map to generate (in m/s)."));
143 951 : oc.doRegister("v-max", new Option_Float(50.));
144 1902 : oc.addDescription("v-max", "Processing", TL("Defines the maximum velocity boundary of the map to generate (in m/s)."));
145 951 : oc.doRegister("v-step", new Option_Float(2.));
146 1902 : oc.addDescription("v-step", "Processing", TL("Defines the velocity step size (in m/s)."));
147 951 : oc.doRegister("a-min", new Option_Float(-4.));
148 1902 : oc.addDescription("a-min", "Processing", TL("Defines the minimum acceleration boundary of the map to generate (in m/s^2)."));
149 951 : oc.doRegister("a-max", new Option_Float(4.));
150 1902 : oc.addDescription("a-max", "Processing", TL("Defines the maximum acceleration boundary of the map to generate (in m/s^2)."));
151 951 : oc.doRegister("a-step", new Option_Float(.5));
152 1902 : oc.addDescription("a-step", "Processing", TL("Defines the acceleration step size (in m/s^2)."));
153 951 : oc.doRegister("s-min", new Option_Float(-10.));
154 1902 : oc.addDescription("s-min", "Processing", TL("Defines the minimum slope boundary of the map to generate (in deg)."));
155 951 : oc.doRegister("s-max", new Option_Float(10.));
156 1902 : oc.addDescription("s-max", "Processing", TL("Defines the maximum slope boundary of the map to generate (in deg)."));
157 951 : oc.doRegister("s-step", new Option_Float(1.));
158 1902 : oc.addDescription("s-step", "Processing", TL("Defines the slope step size (in deg)."));
159 :
160 951 : oc.addOptionSubTopic("Output");
161 951 : oc.doRegister("output-file", 'o', new Option_String());
162 1902 : oc.addSynonyme("output", "output-file");
163 1902 : oc.addDescription("output", "Output", TL("Defines the file (or the path if --iterate was set) to write the map(s) into."));
164 :
165 951 : oc.addOptionSubTopic("Emissions");
166 951 : oc.doRegister("emissions.volumetric-fuel", new Option_Bool(false));
167 1902 : oc.addDescription("emissions.volumetric-fuel", "Emissions", TL("Return fuel consumption values in (legacy) unit l instead of mg"));
168 :
169 3804 : oc.doRegister("phemlight-path", new Option_FileName(StringVector({ "./PHEMlight/" })));
170 1902 : oc.addDescription("phemlight-path", "Emissions", TL("Determines where to load PHEMlight definitions from"));
171 :
172 951 : oc.doRegister("phemlight-year", new Option_Integer(0));
173 1902 : oc.addDescription("phemlight-year", "Emissions", TL("Enable fleet age modelling with the given reference year in PHEMlight5"));
174 :
175 951 : oc.doRegister("phemlight-temperature", new Option_Float(INVALID_DOUBLE));
176 1902 : oc.addDescription("phemlight-temperature", "Emissions", TL("Set ambient temperature to correct NOx emissions in PHEMlight5"));
177 :
178 951 : SystemFrame::addReportOptions(oc);
179 :
180 : // run
181 : int ret = 0;
182 : try {
183 : // initialise the application system (messaging, xml, options)
184 951 : XMLSubSys::init();
185 951 : OptionsIO::setArgs(argc, argv);
186 951 : OptionsIO::getOptions();
187 951 : if (oc.processMetaOptions(argc < 2)) {
188 0 : SystemFrame::close();
189 : return 0;
190 : }
191 :
192 951 : double vMin = oc.getFloat("v-min");
193 951 : double vMax = oc.getFloat("v-max");
194 951 : double vStep = oc.getFloat("v-step");
195 951 : double aMin = oc.getFloat("a-min");
196 951 : double aMax = oc.getFloat("a-max");
197 951 : double aStep = oc.getFloat("a-step");
198 951 : double sMin = oc.getFloat("s-min");
199 951 : double sMax = oc.getFloat("s-max");
200 951 : double sStep = oc.getFloat("s-step");
201 1902 : if (!oc.getBool("iterate")) {
202 1902 : if (!oc.isSet("emission-class")) {
203 0 : throw ProcessError(TL("The emission class (-e) must be given."));
204 : }
205 1902 : if (!oc.isSet("output-file")) {
206 0 : throw ProcessError(TL("The output file (-o) must be given."));
207 : }
208 951 : const SUMOEmissionClass c = PollutantsInterface::getClassByName(oc.getString("emission-class"));
209 2853 : single(oc, oc.getString("output-file"), oc.getString("emission-class"),
210 951 : c, vMin, vMax, vStep, aMin, aMax, aStep, sMin, sMax, sStep, oc.getBool("verbose"));
211 : } else {
212 0 : if (!oc.isSet("output-file")) {
213 0 : oc.set("output-file", "./");
214 : }
215 : #if __cplusplus >= 201703L
216 : std::vector<std::string> phemPath;
217 0 : phemPath.push_back(OptionsCont::getOptions().getString("phemlight-path") + "/");
218 0 : if (getenv("PHEMLIGHT_PATH") != nullptr) {
219 0 : phemPath.push_back(std::string(getenv("PHEMLIGHT_PATH")) + "/");
220 : }
221 0 : if (getenv("SUMO_HOME") != nullptr) {
222 0 : phemPath.push_back(std::string(getenv("SUMO_HOME")) + "/data/emissions/PHEMlight/");
223 0 : phemPath.push_back(std::string(getenv("SUMO_HOME")) + "/data/emissions/PHEMlight5/");
224 : }
225 0 : for (const std::string& p : phemPath) {
226 : std::error_code ec;
227 0 : for (const auto& entry : fs::directory_iterator(p, ec)) {
228 0 : if (entry.path().extension() == ".veh") {
229 0 : if (entry.path().parent_path().filename().string().back() == '5') {
230 0 : PollutantsInterface::getClassByName("PHEMlight5/" + entry.path().filename().stem().stem().string());
231 : } else {
232 0 : PollutantsInterface::getClassByName("PHEMlight/" + entry.path().filename().stem().stem().string());
233 : }
234 : }
235 : }
236 : }
237 : #endif
238 0 : const std::vector<SUMOEmissionClass> classes = PollutantsInterface::getAllClasses();
239 0 : for (std::vector<SUMOEmissionClass>::const_iterator ci = classes.begin(); ci != classes.end(); ++ci) {
240 0 : SUMOEmissionClass c = *ci;
241 0 : single(oc, oc.getString("output-file") + PollutantsInterface::getName(c) + ".csv",
242 0 : PollutantsInterface::getName(c),
243 0 : c, vMin, vMax, vStep, aMin, aMax, aStep, sMin, sMax, sStep, oc.getBool("verbose"));
244 : }
245 0 : }
246 0 : } catch (InvalidArgument& e) {
247 0 : MsgHandler::getErrorInstance()->inform(e.what());
248 0 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
249 : ret = 1;
250 0 : } catch (ProcessError& e) {
251 0 : if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {
252 0 : MsgHandler::getErrorInstance()->inform(e.what());
253 : }
254 0 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
255 : ret = 1;
256 : #ifndef _DEBUG
257 0 : } catch (...) {
258 0 : MsgHandler::getErrorInstance()->inform("Quitting (on unknown error).", false);
259 : ret = 1;
260 : #endif
261 0 : }
262 951 : SystemFrame::close();
263 : if (ret == 0) {
264 : std::cout << "Success." << std::endl;
265 : }
266 : return ret;
267 1902 : }
268 :
269 :
270 : /****************************************************************************/
|