Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2013-2026 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 emissionsDrivingCycle_main.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Michael Behrisch
17 : /// @date Wed, 21.08.2013
18 : ///
19 : // Main for an emissions calculator
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 : #include <utils/common/MsgHandler.h>
32 : #include <utils/options/Option.h>
33 : #include <utils/options/OptionsCont.h>
34 : #include <utils/options/OptionsIO.h>
35 : #include <utils/common/UtilExceptions.h>
36 : #include <utils/common/SystemFrame.h>
37 : #include <utils/common/ToString.h>
38 : #include <utils/xml/XMLSubSys.h>
39 : #include <utils/common/FileHelpers.h>
40 : #include <utils/common/StringTokenizer.h>
41 : #include <utils/common/StringUtils.h>
42 : #include <utils/emissions/PollutantsInterface.h>
43 : #include <utils/emissions/EnergyParams.h>
44 : #include <utils/iodevices/OutputDevice.h>
45 : #include <utils/importio/LineReader.h>
46 : #include "TrajectoriesHandler.h"
47 : #include "VTypesHandler.h"
48 :
49 :
50 : // ===========================================================================
51 : // functions
52 : // ===========================================================================
53 :
54 :
55 : /* -------------------------------------------------------------------------
56 : * main
57 : * ----------------------------------------------------------------------- */
58 : int
59 998 : main(int argc, char** argv) {
60 998 : OptionsCont& oc = OptionsCont::getOptions();
61 998 : oc.setApplicationDescription(TL("Computes emissions by driving a time line using SUMO's emission models."));
62 1996 : oc.setApplicationName("emissionsDrivingCycle", "Eclipse SUMO emissionsDrivingCycle " VERSION_STRING);
63 :
64 : // add options
65 998 : SystemFrame::addConfigurationOptions(oc);
66 998 : oc.addOptionSubTopic("Input");
67 998 : oc.doRegister("timeline-file", 't', new Option_FileName());
68 1996 : oc.addSynonyme("timeline", "timeline-file");
69 1996 : oc.addDescription("timeline-file", "Input", TL("Defines the file to read the driving cycle from."));
70 :
71 998 : oc.doRegister("timeline-file.skip", new Option_Integer(0));
72 1996 : oc.addSynonyme("timeline.skip", "timeline-file.skip");
73 1996 : oc.addDescription("timeline-file.skip", "Input", TL("Skips the first NUM lines."));
74 :
75 1996 : oc.doRegister("timeline-file.separator", new Option_String(";"));
76 1996 : oc.addSynonyme("timeline.separator", "timeline-file.separator");
77 1996 : oc.addDescription("timeline-file.separator", "Input", TL("Defines the entry separator."));
78 :
79 998 : oc.doRegister("netstate-file", 'n', new Option_FileName());
80 1996 : oc.addSynonyme("netstate", "netstate-file");
81 1996 : oc.addSynonyme("amitran", "netstate-file");
82 1996 : oc.addDescription("netstate-file", "Input", TL("Defines the netstate, route and trajectory files to read the driving cycles from."));
83 :
84 998 : oc.doRegister("additional-files", new Option_FileName());
85 1996 : oc.addDescription("additional-files", "Input", TL("Load emission parameters (vTypes) from FILE(s)"));
86 :
87 1996 : oc.doRegister("emission-class", 'e', new Option_String("HBEFA4/default"));
88 1996 : oc.addDescription("emission-class", "Input", TL("Defines for which emission class the emissions shall be generated. "));
89 :
90 998 : oc.doRegister("vtype", new Option_String());
91 1996 : oc.addDescription("vtype", "Input", TL("Defines the vehicle type to use for emission parameters."));
92 :
93 998 : oc.addOptionSubTopic("Processing");
94 998 : oc.doRegister("compute-a", 'a', new Option_Bool(false));
95 1996 : oc.addDescription("compute-a", "Processing", TL("If set, the acceleration is computed instead of being read from the file. "));
96 :
97 998 : oc.doRegister("compute-a.forward", new Option_Bool(false));
98 1996 : oc.addDescription("compute-a.forward", "Processing", TL("If set, the acceleration for time t is computed from v(t+1) - v(t) instead of v(t) - v(t-1). "));
99 :
100 998 : oc.doRegister("compute-a.zero-correction", new Option_Bool(false));
101 1996 : oc.addDescription("compute-a.zero-correction", "Processing", TL("If set, the acceleration for time t is set to 0 if the speed is 0. "));
102 :
103 998 : oc.doRegister("skip-first", 's', new Option_Bool(false));
104 1996 : oc.addDescription("skip-first", "Processing", TL("If set, the first line of the read file is skipped."));
105 :
106 998 : oc.doRegister("kmh", new Option_Bool(false));
107 1996 : oc.addDescription("kmh", "Processing", TL("If set, the given speed is interpreted as being given in km/h."));
108 :
109 998 : oc.doRegister("have-slope", new Option_Bool(false));
110 1996 : oc.addDescription("have-slope", "Processing", TL("If set, the fourth column is read and used as slope (in deg)."));
111 :
112 998 : oc.doRegister("slope", new Option_Float(0));
113 1996 : oc.addDescription("slope", "Processing", TL("Sets a global slope (in deg) that is used if the file does not contain slope information."));
114 :
115 998 : oc.addOptionSubTopic("Output");
116 998 : oc.doRegister("output-file", 'o', new Option_String());
117 1996 : oc.addSynonyme("output", "output-file");
118 1996 : oc.addDescription("output", "Output", TL("Defines the file to write the emission cycle results into."));
119 :
120 998 : oc.doRegister("output.attributes", new Option_StringVector());
121 1996 : oc.addDescription("output.attributes", "Output", TL("Defines the attributes to write."));
122 :
123 998 : oc.doRegister("emission-output", new Option_FileName());
124 1996 : oc.addDescription("emission-output", "Output", TL("Save the emission values of each vehicle in XML"));
125 :
126 998 : oc.doRegister("sum-output", new Option_FileName());
127 1996 : oc.addSynonyme("sum", "sum-output");
128 1996 : oc.addDescription("sum-output", "Output", TL("Save the aggregated and normed emission values of each vehicle in CSV"));
129 :
130 998 : oc.addOptionSubTopic("Emissions");
131 998 : oc.doRegister("emissions.volumetric-fuel", new Option_Bool(false));
132 1996 : oc.addDescription("emissions.volumetric-fuel", "Emissions", TL("Return fuel consumption values in (legacy) unit l instead of mg"));
133 :
134 3992 : oc.doRegister("phemlight-path", new Option_FileName(StringVector({ "./PHEMlight/" })));
135 1996 : oc.addDescription("phemlight-path", "Emissions", TL("Determines where to load PHEMlight definitions from"));
136 :
137 998 : oc.doRegister("phemlight-year", new Option_Integer(0));
138 1996 : oc.addDescription("phemlight-year", "Emissions", TL("Enable fleet age modelling with the given reference year in PHEMlight5"));
139 :
140 998 : oc.doRegister("phemlight-temperature", new Option_Float(INVALID_DOUBLE));
141 1996 : oc.addDescription("phemlight-temperature", "Emissions", TL("Set ambient temperature to correct NOx emissions in PHEMlight5"));
142 :
143 1996 : oc.doRegister("begin", new Option_String("0", "TIME"));
144 1996 : oc.addDescription("begin", "Processing", TL("Defines the begin time in seconds;"));
145 :
146 1996 : oc.doRegister("end", new Option_String("-1", "TIME"));
147 1996 : oc.addDescription("end", "Processing", TL("Defines the end time in seconds;"));
148 :
149 998 : SystemFrame::addReportOptions(oc);
150 998 : oc.doRegister("quiet", 'q', new Option_Bool(false));
151 1996 : oc.addDescription("quiet", "Report", TL("Not writing anything."));
152 :
153 : // run
154 : int ret = 0;
155 : bool quiet = false;
156 : try {
157 : // initialise the application system (messaging, xml, options)
158 998 : XMLSubSys::init();
159 998 : OptionsIO::setArgs(argc, argv);
160 998 : OptionsIO::getOptions();
161 998 : if (oc.processMetaOptions(argc < 2)) {
162 0 : SystemFrame::close();
163 0 : return 0;
164 : }
165 :
166 998 : quiet = oc.getBool("quiet");
167 1002 : if (!oc.isSet("timeline-file") && !oc.isSet("netstate-file")) {
168 0 : throw ProcessError(TL("Either a timeline or a netstate / amitran file must be given."));
169 : }
170 1003 : if (!oc.isSet("output-file") && (oc.isSet("timeline-file") || !oc.isSet("emission-output"))) {
171 0 : throw ProcessError(TL("The output file must be given."));
172 : }
173 : std::unique_ptr<std::ostream> outOwner;
174 : std::ostream* out = nullptr;
175 1996 : if (oc.isSet("output-file")) {
176 1992 : outOwner.reset(new std::ofstream(oc.getString("output-file").c_str()));
177 : out = outOwner.get();
178 : }
179 : long long int attributes = 0;
180 1996 : if (oc.isSet("output.attributes")) {
181 15 : for (std::string attrName : oc.getStringVector("output.attributes")) {
182 5 : if (!SUMOXMLDefinitions::Attrs.hasString(attrName)) {
183 0 : if (attrName == "all") {
184 : attributes = std::numeric_limits<long long int>::max() - 1;
185 : } else {
186 0 : WRITE_ERRORF(TL("Unknown attribute '%' to write in output."), attrName);
187 : }
188 : continue;
189 : }
190 5 : int attr = SUMOXMLDefinitions::Attrs.get(attrName);
191 : assert(attr < 63);
192 5 : attributes |= ((long long int)1 << attr);
193 : }
194 : } else {
195 : attributes = ~(((long long int)1 << SUMO_ATTR_AMOUNT));
196 : }
197 1996 : OutputDevice::createDeviceByOption("emission-output", "emission-export", "emission_file.xsd");
198 : OutputDevice* xmlOut = nullptr;
199 1996 : if (oc.isSet("emission-output")) {
200 5 : xmlOut = &OutputDevice::getDeviceByOption("emission-output");
201 996 : } else if (out == nullptr) {
202 : out = &std::cout;
203 : }
204 : std::unique_ptr<std::ostream> sumOut(nullptr);
205 1996 : if (oc.isSet("sum-output")) {
206 0 : sumOut.reset(new std::ofstream(oc.getString("sum-output").c_str()));
207 : (*sumOut) << "Vehicle,Cycle,Time,Speed,Gradient,Acceleration,FC,FCel,CO2,NOx,CO,HC,PM" << std::endl;
208 : }
209 :
210 999 : SUMOEmissionClass emissionClass = PollutantsInterface::getClassByName(oc.getString("emission-class"));
211 998 : std::unique_ptr<EnergyParams> energyParams;
212 : std::map<std::string, SUMOVTypeParameter*> vTypes;
213 1994 : if (oc.isSet("vtype") || oc.isSet("additional-files")) {
214 4 : if (!oc.isSet("additional-files")) {
215 0 : throw ProcessError(TL("Option --vtype requires option --additional-files for loading vehicle types"));
216 : }
217 4 : if (!oc.isUsableFileList("additional-files")) {
218 0 : throw ProcessError();
219 : }
220 6 : for (const std::string& file : oc.getStringVector("additional-files")) {
221 2 : VTypesHandler typesHandler(file, vTypes);
222 2 : if (!XMLSubSys::runParser(typesHandler, file)) {
223 0 : throw ProcessError(TLF("Loading of % failed.", file));
224 : }
225 2 : }
226 4 : if (!oc.isSet("vtype") && vTypes.size() != 1) {
227 0 : throw ProcessError(TL("Vehicle type is not unique."));
228 : }
229 8 : const auto vTypeIt = oc.isSet("vtype") ? vTypes.find(oc.getString("vtype")) : vTypes.begin();
230 2 : if (vTypeIt == vTypes.end()) {
231 0 : throw ProcessError(TLF("Vehicle type '%' is not defined.", oc.getString("vtype")));
232 : }
233 4 : if (oc.isDefault("emission-class")) {
234 0 : emissionClass = vTypeIt->second->emissionClass;
235 : }
236 4 : energyParams = std::unique_ptr<EnergyParams>(new EnergyParams(vTypeIt->second));
237 : } else {
238 1992 : energyParams = std::unique_ptr<EnergyParams>(new EnergyParams());
239 : }
240 1000 : for (auto& vt : vTypes) {
241 2 : delete vt.second;
242 : }
243 :
244 999 : const bool computeA = oc.getBool("compute-a") || oc.getBool("compute-a.forward");
245 1997 : TrajectoriesHandler handler(computeA, oc.getBool("compute-a.forward"), oc.getBool("compute-a.zero-correction"), emissionClass, energyParams.get(), attributes, oc.getFloat("slope"), out, xmlOut);
246 :
247 1996 : if (oc.isSet("timeline-file")) {
248 1992 : int skip = oc.getBool("skip-first") ? 1 : oc.getInt("timeline-file.skip");
249 996 : const bool inKMH = oc.getBool("kmh");
250 996 : const bool haveSlope = oc.getBool("have-slope");
251 : double l = 0;
252 : double totalA = 0;
253 : double totalS = 0;
254 : int time = 0;
255 :
256 996 : LineReader lr(oc.getString("timeline-file"));
257 996 : if (!lr.good()) {
258 2 : throw ProcessError(TLF("Unreadable file '%'.", lr.getFileName()));
259 : }
260 1172793 : while (lr.hasMore()) {
261 1171798 : std::string line = lr.readLine();
262 1171798 : if (skip > 0) {
263 0 : skip--;
264 : continue;
265 : }
266 2343596 : StringTokenizer st(StringUtils::prune(line), oc.getString("timeline-file.separator"));
267 1171798 : if (st.hasNext()) {
268 : try {
269 1171798 : double t = StringUtils::toDouble(st.next());
270 1171798 : double v = 0;
271 1171798 : if (st.hasNext()) {
272 1171798 : v = StringUtils::toDouble(st.next());
273 : } else {
274 0 : v = t;
275 0 : t = time;
276 : }
277 1171798 : if (inKMH) {
278 1171798 : v /= 3.6;
279 : }
280 2343596 : double a = !computeA && st.hasNext() ? StringUtils::toDouble(st.next()) : TrajectoriesHandler::INVALID_VALUE;
281 2343538 : double s = haveSlope && st.hasNext() ? StringUtils::toDouble(st.next()) : TrajectoriesHandler::INVALID_VALUE;
282 2343596 : if (handler.writeEmissions(*out, "", emissionClass, energyParams.get(), attributes, t, v, a, s)) {
283 1170803 : l += v;
284 1170803 : totalA += a;
285 1170803 : totalS += s;
286 1170803 : time++;
287 : }
288 0 : } catch (EmptyData&) {
289 0 : throw ProcessError(TLF("Missing an entry in line '%'.", line));
290 0 : } catch (NumberFormatException&) {
291 0 : throw ProcessError(TLF("Not numeric entry in line '%'.", line));
292 0 : }
293 : }
294 1171798 : }
295 995 : if (!quiet) {
296 : std::cout << "sums" << std::endl
297 : << "length:" << l << std::endl;
298 : }
299 995 : if (sumOut != nullptr) {
300 0 : (*sumOut) << oc.getString("emission-class") << "," << lr.getFileName() << "," << time << ","
301 0 : << (l / time * 3.6) << "," << (totalS / time) << "," << (totalA / time) << ",";
302 0 : handler.writeNormedSums(*sumOut, "", l);
303 : }
304 996 : }
305 1994 : if (oc.isSet("netstate-file")) {
306 4 : XMLSubSys::runParser(handler, oc.getString("netstate-file"));
307 : }
308 997 : if (!quiet) {
309 1994 : handler.writeSums(std::cout, "");
310 : }
311 1997 : } catch (InvalidArgument& e) {
312 0 : MsgHandler::getErrorInstance()->inform(e.what());
313 0 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
314 : ret = 1;
315 1 : } catch (ProcessError& e) {
316 3 : if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {
317 2 : MsgHandler::getErrorInstance()->inform(e.what());
318 : }
319 1 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
320 : ret = 1;
321 : #ifndef _DEBUG
322 1 : } catch (...) {
323 0 : MsgHandler::getErrorInstance()->inform("Quitting (on unknown error).", false);
324 : ret = 1;
325 : #endif
326 0 : }
327 998 : SystemFrame::close();
328 998 : if (ret == 0 && !quiet) {
329 : std::cout << "Success." << std::endl;
330 : }
331 : return ret;
332 1996 : }
333 :
334 :
335 : /****************************************************************************/
|