LCOV - code coverage report
Current view: top level - src/tools - emissionsDrivingCycle_main.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 147 175 84.0 %
Date: 2024-04-26 15:39:30 Functions: 1 1 100.0 %

          Line data    Source code
       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             : /****************************************************************************/
      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        1992 : main(int argc, char** argv) {
      60        1992 :     OptionsCont& oc = OptionsCont::getOptions();
      61        1992 :     oc.setApplicationDescription(TL("Computes emissions by driving a time line using SUMO's emission models."));
      62        3984 :     oc.setApplicationName("emissionsDrivingCycle", "Eclipse SUMO emissionsDrivingCycle Version " VERSION_STRING);
      63             : 
      64             :     // add options
      65        1992 :     SystemFrame::addConfigurationOptions(oc);
      66        1992 :     oc.addOptionSubTopic("Input");
      67        3984 :     oc.doRegister("timeline-file", 't', new Option_FileName());
      68        3984 :     oc.addSynonyme("timeline", "timeline-file");
      69        3984 :     oc.addDescription("timeline-file", "Input", TL("Defines the file to read the driving cycle from."));
      70             : 
      71        1992 :     oc.doRegister("timeline-file.skip", new Option_Integer(0));
      72        3984 :     oc.addSynonyme("timeline.skip", "timeline-file.skip");
      73        3984 :     oc.addDescription("timeline-file.skip", "Input", TL("Skips the first NUM lines."));
      74             : 
      75        3984 :     oc.doRegister("timeline-file.separator", new Option_String(";"));
      76        3984 :     oc.addSynonyme("timeline.separator", "timeline-file.separator");
      77        3984 :     oc.addDescription("timeline-file.separator", "Input", TL("Defines the entry separator."));
      78             : 
      79        3984 :     oc.doRegister("netstate-file", 'n', new Option_FileName());
      80        3984 :     oc.addSynonyme("netstate", "netstate-file");
      81        3984 :     oc.addSynonyme("amitran", "netstate-file");
      82        3984 :     oc.addDescription("netstate-file", "Input", TL("Defines the netstate, route and trajectory files to read the driving cycles from."));
      83             : 
      84        3984 :     oc.doRegister("additional-files", new Option_FileName());
      85        3984 :     oc.addDescription("additional-files", "Input", TL("Load emission parameters (vTypes) from FILE(s)"));
      86             : 
      87        3984 :     oc.doRegister("emission-class", 'e', new Option_String("unknown"));
      88        3984 :     oc.addDescription("emission-class", "Input", TL("Defines for which emission class the emissions shall be generated. "));
      89             : 
      90        3984 :     oc.doRegister("vtype", new Option_String());
      91        3984 :     oc.addDescription("vtype", "Input", TL("Defines the vehicle type to use for emission parameters."));
      92             : 
      93        1992 :     oc.addOptionSubTopic("Processing");
      94        1992 :     oc.doRegister("compute-a", 'a', new Option_Bool(false));
      95        3984 :     oc.addDescription("compute-a", "Processing", TL("If set, the acceleration is computed instead of being read from the file. "));
      96             : 
      97        1992 :     oc.doRegister("compute-a.forward", new Option_Bool(false));
      98        3984 :     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        1992 :     oc.doRegister("compute-a.zero-correction", new Option_Bool(false));
     101        3984 :     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        1992 :     oc.doRegister("skip-first", 's', new Option_Bool(false));
     104        3984 :     oc.addDescription("skip-first", "Processing", TL("If set, the first line of the read file is skipped."));
     105             : 
     106        1992 :     oc.doRegister("kmh", new Option_Bool(false));
     107        3984 :     oc.addDescription("kmh", "Processing", TL("If set, the given speed is interpreted as being given in km/h."));
     108             : 
     109        1992 :     oc.doRegister("have-slope", new Option_Bool(false));
     110        3984 :     oc.addDescription("have-slope", "Processing", TL("If set, the fourth column is read and used as slope (in deg)."));
     111             : 
     112        1992 :     oc.doRegister("slope", new Option_Float(0));
     113        3984 :     oc.addDescription("slope", "Processing", TL("Sets a global slope (in deg) that is used if the file does not contain slope information."));
     114             : 
     115        1992 :     oc.addOptionSubTopic("Output");
     116        3984 :     oc.doRegister("output-file", 'o', new Option_String());
     117        3984 :     oc.addSynonyme("output", "output-file");
     118        3984 :     oc.addDescription("output", "Output", TL("Defines the file to write the emission cycle results into."));
     119             : 
     120        3984 :     oc.doRegister("output.attributes", new Option_StringVector());
     121        3984 :     oc.addDescription("output.attributes", "Output", TL("Defines the attributes to write."));
     122             : 
     123        3984 :     oc.doRegister("emission-output", new Option_FileName());
     124        3984 :     oc.addDescription("emission-output", "Output", TL("Save the emission values of each vehicle in XML"));
     125             : 
     126        3984 :     oc.doRegister("sum-output", new Option_FileName());
     127        3984 :     oc.addSynonyme("sum", "sum-output");
     128        3984 :     oc.addDescription("sum-output", "Output", TL("Save the aggregated and normed emission values of each vehicle in CSV"));
     129             : 
     130        1992 :     oc.addOptionSubTopic("Emissions");
     131        1992 :     oc.doRegister("emissions.volumetric-fuel", new Option_Bool(false));
     132        3984 :     oc.addDescription("emissions.volumetric-fuel", "Emissions", TL("Return fuel consumption values in (legacy) unit l instead of mg"));
     133             : 
     134        5976 :     oc.doRegister("phemlight-path", new Option_FileName(StringVector({ "./PHEMlight/" })));
     135        3984 :     oc.addDescription("phemlight-path", "Emissions", TL("Determines where to load PHEMlight definitions from"));
     136             : 
     137        1992 :     oc.doRegister("phemlight-year", new Option_Integer(0));
     138        3984 :     oc.addDescription("phemlight-year", "Emissions", TL("Enable fleet age modelling with the given reference year in PHEMlight5"));
     139             : 
     140        1992 :     oc.doRegister("phemlight-temperature", new Option_Float(INVALID_DOUBLE));
     141        3984 :     oc.addDescription("phemlight-temperature", "Emissions", TL("Set ambient temperature to correct NOx emissions in PHEMlight5"));
     142             : 
     143        3984 :     oc.doRegister("begin", new Option_String("0", "TIME"));
     144        3984 :     oc.addDescription("begin", "Processing", TL("Defines the begin time in seconds;"));
     145             : 
     146        3984 :     oc.doRegister("end", new Option_String("-1", "TIME"));
     147        3984 :     oc.addDescription("end", "Processing", TL("Defines the end time in seconds;"));
     148             : 
     149        1992 :     SystemFrame::addReportOptions(oc);
     150        1992 :     oc.doRegister("quiet", 'q', new Option_Bool(false));
     151        3984 :     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        1992 :         XMLSubSys::init();
     159        1992 :         OptionsIO::setArgs(argc, argv);
     160        1992 :         OptionsIO::getOptions();
     161        1992 :         if (oc.processMetaOptions(argc < 2)) {
     162           0 :             SystemFrame::close();
     163           0 :             return 0;
     164             :         }
     165             : 
     166        1992 :         quiet = oc.getBool("quiet");
     167        2000 :         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        2000 :         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::ostream* out = nullptr;
     174        3984 :         if (oc.isSet("output-file")) {
     175        3976 :             out = new std::ofstream(oc.getString("output-file").c_str());
     176             :         }
     177             :         long long int attributes = 0;
     178        3984 :         if (oc.isSet("output.attributes")) {
     179          18 :             for (std::string attrName : oc.getStringVector("output.attributes")) {
     180           6 :                 if (!SUMOXMLDefinitions::Attrs.hasString(attrName)) {
     181           0 :                     if (attrName == "all") {
     182             :                         attributes = std::numeric_limits<long long int>::max() - 1;
     183             :                     } else {
     184           0 :                         WRITE_ERRORF(TL("Unknown attribute '%' to write in output."), attrName);
     185             :                     }
     186             :                     continue;
     187             :                 }
     188           6 :                 int attr = SUMOXMLDefinitions::Attrs.get(attrName);
     189             :                 assert(attr < 63);
     190           6 :                 attributes |= ((long long int)1 << attr);
     191             :             }
     192             :         } else {
     193             :             attributes = ~(((long long int)1 << SUMO_ATTR_AMOUNT));
     194             :         }
     195        3984 :         OutputDevice::createDeviceByOption("emission-output", "emission-export", "emission_file.xsd");
     196             :         OutputDevice* xmlOut = nullptr;
     197        3984 :         if (oc.isSet("emission-output")) {
     198           8 :             xmlOut = &OutputDevice::getDeviceByOption("emission-output");
     199        1988 :         } else if (out == nullptr) {
     200             :             out = &std::cout;
     201             :         }
     202             :         std::ostream* sumOut = nullptr;
     203        3984 :         if (oc.isSet("sum-output")) {
     204           0 :             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        3984 :         SUMOEmissionClass emissionClass = PollutantsInterface::getClassByName(oc.getString("emission-class"));
     209        1992 :         std::unique_ptr<EnergyParams> energyParams;
     210             :         std::map<std::string, SUMOVTypeParameter*> vTypes;
     211        3980 :         if (oc.isSet("vtype") || oc.isSet("additional-files")) {
     212           8 :             if (!oc.isSet("additional-files")) {
     213           0 :                 throw ProcessError(TL("Option --vtype requires option --additional-files for loading vehicle types"));
     214             :             }
     215           8 :             if (!oc.isUsableFileList("additional-files")) {
     216           0 :                 throw ProcessError();
     217             :             }
     218          12 :             for (const std::string& file : oc.getStringVector("additional-files")) {
     219           4 :                 VTypesHandler typesHandler(file, vTypes);
     220           4 :                 if (!XMLSubSys::runParser(typesHandler, file)) {
     221           0 :                     throw ProcessError(TLF("Loading of % failed.", file));
     222             :                 }
     223           4 :             }
     224           8 :             if (!oc.isSet("vtype") && vTypes.size() != 1) {
     225           0 :                 throw ProcessError(TL("Vehicle type is not unique."));
     226             :             }
     227          16 :             const auto vTypeIt = oc.isSet("vtype") ? vTypes.find(oc.getString("vtype")) : vTypes.begin();
     228           4 :             if (vTypeIt == vTypes.end()) {
     229           0 :                 throw ProcessError(TLF("Vehicle type '%' is not defined.", oc.getString("vtype")));
     230             :             }
     231           8 :             if (oc.isDefault("emission-class")) {
     232           0 :                 emissionClass = vTypeIt->second->emissionClass;
     233             :             }
     234           8 :             energyParams = std::unique_ptr<EnergyParams>(new EnergyParams(vTypeIt->second));
     235             :         } else {
     236        3976 :             energyParams = std::unique_ptr<EnergyParams>(new EnergyParams(emissionClass));
     237             :         }
     238             : 
     239        1994 :         const bool computeA = oc.getBool("compute-a") || oc.getBool("compute-a.forward");
     240        5978 :         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        3984 :         if (oc.isSet("timeline-file")) {
     243        3976 :             int skip = oc.getBool("skip-first") ? 1 : oc.getInt("timeline-file.skip");
     244        1988 :             const bool inKMH = oc.getBool("kmh");
     245        1988 :             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        3976 :             LineReader lr(oc.getString("timeline-file"));
     252        1988 :             if (!lr.good()) {
     253           6 :                 throw ProcessError(TLF("Unreadable file '%'.", lr.getFileName()));
     254             :             }
     255     2345466 :             while (lr.hasMore()) {
     256     2343480 :                 std::string line = lr.readLine();
     257     2343480 :                 if (skip > 0) {
     258           0 :                     skip--;
     259             :                     continue;
     260             :                 }
     261     4686960 :                 StringTokenizer st(StringUtils::prune(line), oc.getString("timeline-file.separator"));
     262     2343480 :                 if (st.hasNext()) {
     263             :                     try {
     264     2343480 :                         double t = StringUtils::toDouble(st.next());
     265     2343480 :                         double v = 0;
     266     2343480 :                         if (st.hasNext()) {
     267     2343480 :                             v = StringUtils::toDouble(st.next());
     268             :                         } else {
     269           0 :                             v = t;
     270           0 :                             t = time;
     271             :                         }
     272     2343480 :                         if (inKMH) {
     273     2343480 :                             v /= 3.6;
     274             :                         }
     275     2343480 :                         double a = !computeA && st.hasNext() ? StringUtils::toDouble(st.next()) : TrajectoriesHandler::INVALID_VALUE;
     276     2343480 :                         double s = haveSlope && st.hasNext() ? StringUtils::toDouble(st.next()) : TrajectoriesHandler::INVALID_VALUE;
     277     4686960 :                         if (handler.writeEmissions(*out, "", emissionClass, energyParams.get(), attributes, t, v, a, s)) {
     278     2341494 :                             l += v;
     279     2341494 :                             totalA += a;
     280     2341494 :                             totalS += s;
     281     2341494 :                             time++;
     282             :                         }
     283           0 :                     } catch (EmptyData&) {
     284           0 :                         throw ProcessError(TLF("Missing an entry in line '%'.", line));
     285           0 :                     } catch (NumberFormatException&) {
     286           0 :                         throw ProcessError(TLF("Not numeric entry in line '%'.", line));
     287           0 :                     }
     288             :                 }
     289     2343480 :             }
     290        1986 :             if (!quiet) {
     291             :                 std::cout << "sums" << std::endl
     292             :                           << "length:" << l << std::endl;
     293             :             }
     294        1986 :             if (sumOut != nullptr) {
     295           0 :                 (*sumOut) << oc.getString("emission-class") << "," << lr.getFileName() << "," << time << ","
     296           0 :                           << (l / time * 3.6) << "," << (totalS / time) << "," << (totalA / time) << ",";
     297           0 :                 handler.writeNormedSums(*sumOut, "", l);
     298             :             }
     299        1988 :         }
     300        3980 :         if (oc.isSet("netstate-file")) {
     301           8 :             XMLSubSys::runParser(handler, oc.getString("netstate-file"));
     302             :         }
     303        1990 :         if (!quiet) {
     304        3980 :             handler.writeSums(std::cout, "");
     305             :         }
     306        1990 :         delete sumOut;
     307        1990 :         if (out != &std::cout) {
     308        1990 :             delete out;
     309             :         }
     310        3986 :     } catch (InvalidArgument& e) {
     311           0 :         MsgHandler::getErrorInstance()->inform(e.what());
     312           0 :         MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
     313             :         ret = 1;
     314           2 :     } catch (ProcessError& e) {
     315           8 :         if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {
     316           4 :             MsgHandler::getErrorInstance()->inform(e.what());
     317             :         }
     318           2 :         MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
     319             :         ret = 1;
     320             : #ifndef _DEBUG
     321           2 :     } catch (...) {
     322           0 :         MsgHandler::getErrorInstance()->inform("Quitting (on unknown error).", false);
     323             :         ret = 1;
     324             : #endif
     325           0 :     }
     326        1992 :     SystemFrame::close();
     327        1992 :     if (ret == 0 && !quiet) {
     328             :         std::cout << "Success." << std::endl;
     329             :     }
     330             :     return ret;
     331             : }
     332             : 
     333             : 
     334             : /****************************************************************************/

Generated by: LCOV version 1.14