Eclipse SUMO - Simulation of Urban MObility
emissionsDrivingCycle_main.cpp
Go to the documentation of this file.
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 /****************************************************************************/
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>
32 #include <utils/options/Option.h>
37 #include <utils/common/ToString.h>
38 #include <utils/xml/XMLSubSys.h>
46 #include "TrajectoriesHandler.h"
47 #include "VTypesHandler.h"
48 
49 
50 // ===========================================================================
51 // functions
52 // ===========================================================================
53 
54 
55 /* -------------------------------------------------------------------------
56  * main
57  * ----------------------------------------------------------------------- */
58 int
59 main(int argc, char** argv) {
61  oc.setApplicationDescription(TL("Computes emissions by driving a time line using SUMO's emission models."));
62  oc.setApplicationName("emissionsDrivingCycle", "Eclipse SUMO emissionsDrivingCycle Version " VERSION_STRING);
63 
64  // add options
66  oc.addOptionSubTopic("Input");
67  oc.doRegister("timeline-file", 't', new Option_FileName());
68  oc.addSynonyme("timeline", "timeline-file");
69  oc.addDescription("timeline-file", "Input", TL("Defines the file to read the driving cycle from."));
70 
71  oc.doRegister("timeline-file.skip", new Option_Integer(0));
72  oc.addSynonyme("timeline.skip", "timeline-file.skip");
73  oc.addDescription("timeline-file.skip", "Input", TL("Skips the first NUM lines."));
74 
75  oc.doRegister("timeline-file.separator", new Option_String(";"));
76  oc.addSynonyme("timeline.separator", "timeline-file.separator");
77  oc.addDescription("timeline-file.separator", "Input", TL("Defines the entry separator."));
78 
79  oc.doRegister("netstate-file", 'n', new Option_FileName());
80  oc.addSynonyme("netstate", "netstate-file");
81  oc.addSynonyme("amitran", "netstate-file");
82  oc.addDescription("netstate-file", "Input", TL("Defines the netstate, route and trajectory files to read the driving cycles from."));
83 
84  oc.doRegister("additional-files", new Option_FileName());
85  oc.addDescription("additional-files", "Input", TL("Load emission parameters (vTypes) from FILE(s)"));
86 
87  oc.doRegister("emission-class", 'e', new Option_String("unknown"));
88  oc.addDescription("emission-class", "Input", TL("Defines for which emission class the emissions shall be generated. "));
89 
90  oc.doRegister("vtype", new Option_String());
91  oc.addDescription("vtype", "Input", TL("Defines the vehicle type to use for emission parameters."));
92 
93  oc.addOptionSubTopic("Processing");
94  oc.doRegister("compute-a", 'a', new Option_Bool(false));
95  oc.addDescription("compute-a", "Processing", TL("If set, the acceleration is computed instead of being read from the file. "));
96 
97  oc.doRegister("compute-a.forward", new Option_Bool(false));
98  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  oc.doRegister("compute-a.zero-correction", new Option_Bool(false));
101  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  oc.doRegister("skip-first", 's', new Option_Bool(false));
104  oc.addDescription("skip-first", "Processing", TL("If set, the first line of the read file is skipped."));
105 
106  oc.doRegister("kmh", new Option_Bool(false));
107  oc.addDescription("kmh", "Processing", TL("If set, the given speed is interpreted as being given in km/h."));
108 
109  oc.doRegister("have-slope", new Option_Bool(false));
110  oc.addDescription("have-slope", "Processing", TL("If set, the fourth column is read and used as slope (in deg)."));
111 
112  oc.doRegister("slope", new Option_Float(0));
113  oc.addDescription("slope", "Processing", TL("Sets a global slope (in deg) that is used if the file does not contain slope information."));
114 
115  oc.addOptionSubTopic("Output");
116  oc.doRegister("output-file", 'o', new Option_String());
117  oc.addSynonyme("output", "output-file");
118  oc.addDescription("output", "Output", TL("Defines the file to write the emission cycle results into."));
119 
120  oc.doRegister("output.attributes", new Option_StringVector());
121  oc.addDescription("output.attributes", "Output", TL("Defines the attributes to write."));
122 
123  oc.doRegister("emission-output", new Option_FileName());
124  oc.addDescription("emission-output", "Output", TL("Save the emission values of each vehicle in XML"));
125 
126  oc.doRegister("sum-output", new Option_FileName());
127  oc.addSynonyme("sum", "sum-output");
128  oc.addDescription("sum-output", "Output", TL("Save the aggregated and normed emission values of each vehicle in CSV"));
129 
130  oc.addOptionSubTopic("Emissions");
131  oc.doRegister("emissions.volumetric-fuel", new Option_Bool(false));
132  oc.addDescription("emissions.volumetric-fuel", "Emissions", TL("Return fuel consumption values in (legacy) unit l instead of mg"));
133 
134  oc.doRegister("phemlight-path", new Option_FileName(StringVector({ "./PHEMlight/" })));
135  oc.addDescription("phemlight-path", "Emissions", TL("Determines where to load PHEMlight definitions from"));
136 
137  oc.doRegister("phemlight-year", new Option_Integer(0));
138  oc.addDescription("phemlight-year", "Emissions", TL("Enable fleet age modelling with the given reference year in PHEMlight5"));
139 
140  oc.doRegister("phemlight-temperature", new Option_Float(INVALID_DOUBLE));
141  oc.addDescription("phemlight-temperature", "Emissions", TL("Set ambient temperature to correct NOx emissions in PHEMlight5"));
142 
143  oc.doRegister("begin", new Option_String("0", "TIME"));
144  oc.addDescription("begin", "Processing", TL("Defines the begin time in seconds;"));
145 
146  oc.doRegister("end", new Option_String("-1", "TIME"));
147  oc.addDescription("end", "Processing", TL("Defines the end time in seconds;"));
148 
150  oc.doRegister("quiet", 'q', new Option_Bool(false));
151  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  XMLSubSys::init();
159  OptionsIO::setArgs(argc, argv);
161  if (oc.processMetaOptions(argc < 2)) {
163  return 0;
164  }
165 
166  quiet = oc.getBool("quiet");
167  if (!oc.isSet("timeline-file") && !oc.isSet("netstate-file")) {
168  throw ProcessError(TL("Either a timeline or a netstate / amitran file must be given."));
169  }
170  if (!oc.isSet("output-file") && (oc.isSet("timeline-file") || !oc.isSet("emission-output"))) {
171  throw ProcessError(TL("The output file must be given."));
172  }
173  std::ostream* out = nullptr;
174  if (oc.isSet("output-file")) {
175  out = new std::ofstream(oc.getString("output-file").c_str());
176  }
177  long long int attributes = 0;
178  if (oc.isSet("output.attributes")) {
179  for (std::string attrName : oc.getStringVector("output.attributes")) {
180  if (!SUMOXMLDefinitions::Attrs.hasString(attrName)) {
181  if (attrName == "all") {
182  attributes = std::numeric_limits<long long int>::max() - 1;
183  } else {
184  WRITE_ERRORF(TL("Unknown attribute '%' to write in output."), attrName);
185  }
186  continue;
187  }
188  int attr = SUMOXMLDefinitions::Attrs.get(attrName);
189  assert(attr < 63);
190  attributes |= ((long long int)1 << attr);
191  }
192  } else {
193  attributes = ~(((long long int)1 << SUMO_ATTR_AMOUNT));
194  }
195  OutputDevice::createDeviceByOption("emission-output", "emission-export", "emission_file.xsd");
196  OutputDevice* xmlOut = nullptr;
197  if (oc.isSet("emission-output")) {
198  xmlOut = &OutputDevice::getDeviceByOption("emission-output");
199  } else if (out == nullptr) {
200  out = &std::cout;
201  }
202  std::ostream* sumOut = nullptr;
203  if (oc.isSet("sum-output")) {
204  sumOut = new std::ofstream(oc.getString("sum-output").c_str());
205  (*sumOut) << "Vehicle,Cycle,Time,Speed,Gradient,Acceleration,FC,FCel,CO2,NOx,CO,HC,PM" << std::endl;
206  }
207 
208  SUMOEmissionClass emissionClass = PollutantsInterface::getClassByName(oc.getString("emission-class"));
209  std::unique_ptr<EnergyParams> energyParams;
210  std::map<std::string, SUMOVTypeParameter*> vTypes;
211  if (oc.isSet("vtype") || oc.isSet("additional-files")) {
212  if (!oc.isSet("additional-files")) {
213  throw ProcessError(TL("Option --vtype requires option --additional-files for loading vehicle types"));
214  }
215  if (!oc.isUsableFileList("additional-files")) {
216  throw ProcessError();
217  }
218  for (const std::string& file : oc.getStringVector("additional-files")) {
219  VTypesHandler typesHandler(file, vTypes);
220  if (!XMLSubSys::runParser(typesHandler, file)) {
221  throw ProcessError(TLF("Loading of % failed.", file));
222  }
223  }
224  if (!oc.isSet("vtype") && vTypes.size() != 1) {
225  throw ProcessError(TL("Vehicle type is not unique."));
226  }
227  const auto vTypeIt = oc.isSet("vtype") ? vTypes.find(oc.getString("vtype")) : vTypes.begin();
228  if (vTypeIt == vTypes.end()) {
229  throw ProcessError(TLF("Vehicle type '%' is not defined.", oc.getString("vtype")));
230  }
231  if (oc.isDefault("emission-class")) {
232  emissionClass = vTypeIt->second->emissionClass;
233  }
234  energyParams = std::unique_ptr<EnergyParams>(new EnergyParams(vTypeIt->second));
235  } else {
236  energyParams = std::unique_ptr<EnergyParams>(new EnergyParams(emissionClass));
237  }
238 
239  const bool computeA = oc.getBool("compute-a") || oc.getBool("compute-a.forward");
240  TrajectoriesHandler handler(computeA, oc.getBool("compute-a.forward"), oc.getBool("compute-a.zero-correction"), emissionClass, energyParams.get(), attributes, oc.getFloat("slope"), out, xmlOut);
241 
242  if (oc.isSet("timeline-file")) {
243  int skip = oc.getBool("skip-first") ? 1 : oc.getInt("timeline-file.skip");
244  const bool inKMH = oc.getBool("kmh");
245  const bool haveSlope = oc.getBool("have-slope");
246  double l = 0;
247  double totalA = 0;
248  double totalS = 0;
249  int time = 0;
250 
251  LineReader lr(oc.getString("timeline-file"));
252  if (!lr.good()) {
253  throw ProcessError(TLF("Unreadable file '%'.", lr.getFileName()));
254  }
255  while (lr.hasMore()) {
256  std::string line = lr.readLine();
257  if (skip > 0) {
258  skip--;
259  continue;
260  }
261  StringTokenizer st(StringUtils::prune(line), oc.getString("timeline-file.separator"));
262  if (st.hasNext()) {
263  try {
264  double t = StringUtils::toDouble(st.next());
265  double v = 0;
266  if (st.hasNext()) {
267  v = StringUtils::toDouble(st.next());
268  } else {
269  v = t;
270  t = time;
271  }
272  if (inKMH) {
273  v /= 3.6;
274  }
275  double a = !computeA && st.hasNext() ? StringUtils::toDouble(st.next()) : TrajectoriesHandler::INVALID_VALUE;
276  double s = haveSlope && st.hasNext() ? StringUtils::toDouble(st.next()) : TrajectoriesHandler::INVALID_VALUE;
277  if (handler.writeEmissions(*out, "", emissionClass, energyParams.get(), attributes, t, v, a, s)) {
278  l += v;
279  totalA += a;
280  totalS += s;
281  time++;
282  }
283  } catch (EmptyData&) {
284  throw ProcessError(TLF("Missing an entry in line '%'.", line));
285  } catch (NumberFormatException&) {
286  throw ProcessError(TLF("Not numeric entry in line '%'.", line));
287  }
288  }
289  }
290  if (!quiet) {
291  std::cout << "sums" << std::endl
292  << "length:" << l << std::endl;
293  }
294  if (sumOut != nullptr) {
295  (*sumOut) << oc.getString("emission-class") << "," << lr.getFileName() << "," << time << ","
296  << (l / time * 3.6) << "," << (totalS / time) << "," << (totalA / time) << ",";
297  handler.writeNormedSums(*sumOut, "", l);
298  }
299  }
300  if (oc.isSet("netstate-file")) {
301  XMLSubSys::runParser(handler, oc.getString("netstate-file"));
302  }
303  if (!quiet) {
304  handler.writeSums(std::cout, "");
305  }
306  delete sumOut;
307  if (out != &std::cout) {
308  delete out;
309  }
310  } catch (InvalidArgument& e) {
312  MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
313  ret = 1;
314  } catch (ProcessError& e) {
315  if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {
317  }
318  MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
319  ret = 1;
320 #ifndef _DEBUG
321  } catch (...) {
322  MsgHandler::getErrorInstance()->inform("Quitting (on unknown error).", false);
323  ret = 1;
324 #endif
325  }
327  if (ret == 0 && !quiet) {
328  std::cout << "Success." << std::endl;
329  }
330  return ret;
331 }
332 
333 
334 /****************************************************************************/
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:305
#define TL(string)
Definition: MsgHandler.h:315
#define TLF(string,...)
Definition: MsgHandler.h:317
std::vector< std::string > StringVector
Definition of a vector of strings.
Definition: Option.h:42
int SUMOEmissionClass
@ SUMO_ATTR_AMOUNT
MSMeanData_Amitran.
const double INVALID_DOUBLE
invalid double
Definition: StdDefs.h:64
An upper class for objects with additional parameters.
Definition: EnergyParams.h:43
Retrieves a file linewise and reports the lines to a handler.
Definition: LineReader.h:48
bool good() const
Returns the information whether the stream is readable.
Definition: LineReader.cpp:232
bool readLine(LineHandler &lh)
Reads a single (the next) line from the file and reports it to the given LineHandler.
Definition: LineReader.cpp:68
bool hasMore() const
Returns whether another line may be read (the file was not read completely)
Definition: LineReader.cpp:52
std::string getFileName() const
Returns the name of the used file.
Definition: LineReader.cpp:174
static MsgHandler * getErrorInstance()
Returns the instance to add errors to.
Definition: MsgHandler.cpp:92
virtual void inform(std::string msg, bool addType=true)
adds a new error to the list
Definition: MsgHandler.cpp:154
A storage for options typed value containers)
Definition: OptionsCont.h:89
void addDescription(const std::string &name, const std::string &subtopic, const std::string &description)
Adds a description for an option.
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
void setApplicationName(const std::string &appName, const std::string &fullName)
Sets the application name.
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
void addSynonyme(const std::string &name1, const std::string &name2, bool isDeprecated=false)
Adds a synonyme for an options name (any order)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
void doRegister(const std::string &name, Option *o)
Adds an option under the given name.
Definition: OptionsCont.cpp:76
void setApplicationDescription(const std::string &appDesc)
Sets the application description.
void addOptionSubTopic(const std::string &topic)
Adds an option subtopic.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
bool processMetaOptions(bool missingOptions)
Checks for help and configuration output, returns whether we should exit.
bool isUsableFileList(const std::string &name) const
Checks whether the named option is usable as a file list (with at least a single file)
static void setArgs(int argc, char **argv)
Stores the command line arguments for later parsing.
Definition: OptionsIO.cpp:58
static void getOptions(const bool commandLineOnly=false)
Parses the command line arguments and loads the configuration.
Definition: OptionsIO.cpp:74
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
static bool createDeviceByOption(const std::string &optionName, const std::string &rootElement="", const std::string &schemaFile="")
Creates the device using the output definition stored in the named option.
static OutputDevice & getDeviceByOption(const std::string &name)
Returns the device described by the option.
static SUMOEmissionClass getClassByName(const std::string &eClass, const SUMOVehicleClass vc=SVC_IGNORING)
Checks whether the string describes a known vehicle class.
static SequentialStringBijection Attrs
The names of SUMO-XML attributes for use in netbuild.
int get(const std::string &str) const
bool hasNext()
returns the information whether further substrings exist
std::string next()
returns the next substring when it exists. Otherwise the behaviour is undefined
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
static std::string prune(const std::string &str)
Removes trailing and leading whitechars.
Definition: StringUtils.cpp:58
static void close()
Closes all of an applications subsystems.
static void addConfigurationOptions(OptionsCont &oc)
Adds configuration options to the given container.
Definition: SystemFrame.cpp:38
static void addReportOptions(OptionsCont &oc)
Adds reporting options to the given container.
Definition: SystemFrame.cpp:67
An XML-Handler for amitran and netstate trajectories.
void writeNormedSums(std::ostream &o, const std::string id, const double factor)
void writeSums(std::ostream &o, const std::string id)
static const int INVALID_VALUE
bool writeEmissions(std::ostream &o, const std::string id, const SUMOEmissionClass c, EnergyParams *params, long long int attributes, double t, double &v, double &a, double &s)
An XML-Handler for amitran and netstate trajectories.
Definition: VTypesHandler.h:38
static void init()
Initialises the xml-subsystem.
Definition: XMLSubSys.cpp:56
static bool runParser(GenericSAXHandler &handler, const std::string &file, const bool isNet=false, const bool isRoute=false, const bool isExternal=false, const bool catchExceptions=true)
Runs the given handler on the given file; returns if everything's ok.
Definition: XMLSubSys.cpp:148
int main(int argc, char **argv)