Path: blob/main/src/tools/emissionsDrivingCycle_main.cpp
169665 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2013-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file emissionsDrivingCycle_main.cpp14/// @author Daniel Krajzewicz15/// @author Michael Behrisch16/// @date Wed, 21.08.201317///18// Main for an emissions calculator19/****************************************************************************/20#include <config.h>2122#ifdef HAVE_VERSION_H23#include <version.h>24#endif2526#include <iostream>27#include <string>28#include <ctime>29#include <memory>30#include <utils/common/MsgHandler.h>31#include <utils/options/Option.h>32#include <utils/options/OptionsCont.h>33#include <utils/options/OptionsIO.h>34#include <utils/common/UtilExceptions.h>35#include <utils/common/SystemFrame.h>36#include <utils/common/ToString.h>37#include <utils/xml/XMLSubSys.h>38#include <utils/common/FileHelpers.h>39#include <utils/common/StringTokenizer.h>40#include <utils/common/StringUtils.h>41#include <utils/emissions/PollutantsInterface.h>42#include <utils/emissions/EnergyParams.h>43#include <utils/iodevices/OutputDevice.h>44#include <utils/importio/LineReader.h>45#include "TrajectoriesHandler.h"46#include "VTypesHandler.h"474849// ===========================================================================50// functions51// ===========================================================================525354/* -------------------------------------------------------------------------55* main56* ----------------------------------------------------------------------- */57int58main(int argc, char** argv) {59OptionsCont& oc = OptionsCont::getOptions();60oc.setApplicationDescription(TL("Computes emissions by driving a time line using SUMO's emission models."));61oc.setApplicationName("emissionsDrivingCycle", "Eclipse SUMO emissionsDrivingCycle " VERSION_STRING);6263// add options64SystemFrame::addConfigurationOptions(oc);65oc.addOptionSubTopic("Input");66oc.doRegister("timeline-file", 't', new Option_FileName());67oc.addSynonyme("timeline", "timeline-file");68oc.addDescription("timeline-file", "Input", TL("Defines the file to read the driving cycle from."));6970oc.doRegister("timeline-file.skip", new Option_Integer(0));71oc.addSynonyme("timeline.skip", "timeline-file.skip");72oc.addDescription("timeline-file.skip", "Input", TL("Skips the first NUM lines."));7374oc.doRegister("timeline-file.separator", new Option_String(";"));75oc.addSynonyme("timeline.separator", "timeline-file.separator");76oc.addDescription("timeline-file.separator", "Input", TL("Defines the entry separator."));7778oc.doRegister("netstate-file", 'n', new Option_FileName());79oc.addSynonyme("netstate", "netstate-file");80oc.addSynonyme("amitran", "netstate-file");81oc.addDescription("netstate-file", "Input", TL("Defines the netstate, route and trajectory files to read the driving cycles from."));8283oc.doRegister("additional-files", new Option_FileName());84oc.addDescription("additional-files", "Input", TL("Load emission parameters (vTypes) from FILE(s)"));8586oc.doRegister("emission-class", 'e', new Option_String("HBEFA4/default"));87oc.addDescription("emission-class", "Input", TL("Defines for which emission class the emissions shall be generated. "));8889oc.doRegister("vtype", new Option_String());90oc.addDescription("vtype", "Input", TL("Defines the vehicle type to use for emission parameters."));9192oc.addOptionSubTopic("Processing");93oc.doRegister("compute-a", 'a', new Option_Bool(false));94oc.addDescription("compute-a", "Processing", TL("If set, the acceleration is computed instead of being read from the file. "));9596oc.doRegister("compute-a.forward", new Option_Bool(false));97oc.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). "));9899oc.doRegister("compute-a.zero-correction", new Option_Bool(false));100oc.addDescription("compute-a.zero-correction", "Processing", TL("If set, the acceleration for time t is set to 0 if the speed is 0. "));101102oc.doRegister("skip-first", 's', new Option_Bool(false));103oc.addDescription("skip-first", "Processing", TL("If set, the first line of the read file is skipped."));104105oc.doRegister("kmh", new Option_Bool(false));106oc.addDescription("kmh", "Processing", TL("If set, the given speed is interpreted as being given in km/h."));107108oc.doRegister("have-slope", new Option_Bool(false));109oc.addDescription("have-slope", "Processing", TL("If set, the fourth column is read and used as slope (in deg)."));110111oc.doRegister("slope", new Option_Float(0));112oc.addDescription("slope", "Processing", TL("Sets a global slope (in deg) that is used if the file does not contain slope information."));113114oc.addOptionSubTopic("Output");115oc.doRegister("output-file", 'o', new Option_String());116oc.addSynonyme("output", "output-file");117oc.addDescription("output", "Output", TL("Defines the file to write the emission cycle results into."));118119oc.doRegister("output.attributes", new Option_StringVector());120oc.addDescription("output.attributes", "Output", TL("Defines the attributes to write."));121122oc.doRegister("emission-output", new Option_FileName());123oc.addDescription("emission-output", "Output", TL("Save the emission values of each vehicle in XML"));124125oc.doRegister("sum-output", new Option_FileName());126oc.addSynonyme("sum", "sum-output");127oc.addDescription("sum-output", "Output", TL("Save the aggregated and normed emission values of each vehicle in CSV"));128129oc.addOptionSubTopic("Emissions");130oc.doRegister("emissions.volumetric-fuel", new Option_Bool(false));131oc.addDescription("emissions.volumetric-fuel", "Emissions", TL("Return fuel consumption values in (legacy) unit l instead of mg"));132133oc.doRegister("phemlight-path", new Option_FileName(StringVector({ "./PHEMlight/" })));134oc.addDescription("phemlight-path", "Emissions", TL("Determines where to load PHEMlight definitions from"));135136oc.doRegister("phemlight-year", new Option_Integer(0));137oc.addDescription("phemlight-year", "Emissions", TL("Enable fleet age modelling with the given reference year in PHEMlight5"));138139oc.doRegister("phemlight-temperature", new Option_Float(INVALID_DOUBLE));140oc.addDescription("phemlight-temperature", "Emissions", TL("Set ambient temperature to correct NOx emissions in PHEMlight5"));141142oc.doRegister("begin", new Option_String("0", "TIME"));143oc.addDescription("begin", "Processing", TL("Defines the begin time in seconds;"));144145oc.doRegister("end", new Option_String("-1", "TIME"));146oc.addDescription("end", "Processing", TL("Defines the end time in seconds;"));147148SystemFrame::addReportOptions(oc);149oc.doRegister("quiet", 'q', new Option_Bool(false));150oc.addDescription("quiet", "Report", TL("Not writing anything."));151152// run153int ret = 0;154bool quiet = false;155try {156// initialise the application system (messaging, xml, options)157XMLSubSys::init();158OptionsIO::setArgs(argc, argv);159OptionsIO::getOptions();160if (oc.processMetaOptions(argc < 2)) {161SystemFrame::close();162return 0;163}164165quiet = oc.getBool("quiet");166if (!oc.isSet("timeline-file") && !oc.isSet("netstate-file")) {167throw ProcessError(TL("Either a timeline or a netstate / amitran file must be given."));168}169if (!oc.isSet("output-file") && (oc.isSet("timeline-file") || !oc.isSet("emission-output"))) {170throw ProcessError(TL("The output file must be given."));171}172std::ostream* out = nullptr;173if (oc.isSet("output-file")) {174out = new std::ofstream(oc.getString("output-file").c_str());175}176long long int attributes = 0;177if (oc.isSet("output.attributes")) {178for (std::string attrName : oc.getStringVector("output.attributes")) {179if (!SUMOXMLDefinitions::Attrs.hasString(attrName)) {180if (attrName == "all") {181attributes = std::numeric_limits<long long int>::max() - 1;182} else {183WRITE_ERRORF(TL("Unknown attribute '%' to write in output."), attrName);184}185continue;186}187int attr = SUMOXMLDefinitions::Attrs.get(attrName);188assert(attr < 63);189attributes |= ((long long int)1 << attr);190}191} else {192attributes = ~(((long long int)1 << SUMO_ATTR_AMOUNT));193}194OutputDevice::createDeviceByOption("emission-output", "emission-export", "emission_file.xsd");195OutputDevice* xmlOut = nullptr;196if (oc.isSet("emission-output")) {197xmlOut = &OutputDevice::getDeviceByOption("emission-output");198} else if (out == nullptr) {199out = &std::cout;200}201std::ostream* sumOut = nullptr;202if (oc.isSet("sum-output")) {203sumOut = new std::ofstream(oc.getString("sum-output").c_str());204(*sumOut) << "Vehicle,Cycle,Time,Speed,Gradient,Acceleration,FC,FCel,CO2,NOx,CO,HC,PM" << std::endl;205}206207SUMOEmissionClass emissionClass = PollutantsInterface::getClassByName(oc.getString("emission-class"));208std::unique_ptr<EnergyParams> energyParams;209std::map<std::string, SUMOVTypeParameter*> vTypes;210if (oc.isSet("vtype") || oc.isSet("additional-files")) {211if (!oc.isSet("additional-files")) {212throw ProcessError(TL("Option --vtype requires option --additional-files for loading vehicle types"));213}214if (!oc.isUsableFileList("additional-files")) {215throw ProcessError();216}217for (const std::string& file : oc.getStringVector("additional-files")) {218VTypesHandler typesHandler(file, vTypes);219if (!XMLSubSys::runParser(typesHandler, file)) {220throw ProcessError(TLF("Loading of % failed.", file));221}222}223if (!oc.isSet("vtype") && vTypes.size() != 1) {224throw ProcessError(TL("Vehicle type is not unique."));225}226const auto vTypeIt = oc.isSet("vtype") ? vTypes.find(oc.getString("vtype")) : vTypes.begin();227if (vTypeIt == vTypes.end()) {228throw ProcessError(TLF("Vehicle type '%' is not defined.", oc.getString("vtype")));229}230if (oc.isDefault("emission-class")) {231emissionClass = vTypeIt->second->emissionClass;232}233energyParams = std::unique_ptr<EnergyParams>(new EnergyParams(vTypeIt->second));234} else {235energyParams = std::unique_ptr<EnergyParams>(new EnergyParams());236}237238const bool computeA = oc.getBool("compute-a") || oc.getBool("compute-a.forward");239TrajectoriesHandler handler(computeA, oc.getBool("compute-a.forward"), oc.getBool("compute-a.zero-correction"), emissionClass, energyParams.get(), attributes, oc.getFloat("slope"), out, xmlOut);240241if (oc.isSet("timeline-file")) {242int skip = oc.getBool("skip-first") ? 1 : oc.getInt("timeline-file.skip");243const bool inKMH = oc.getBool("kmh");244const bool haveSlope = oc.getBool("have-slope");245double l = 0;246double totalA = 0;247double totalS = 0;248int time = 0;249250LineReader lr(oc.getString("timeline-file"));251if (!lr.good()) {252throw ProcessError(TLF("Unreadable file '%'.", lr.getFileName()));253}254while (lr.hasMore()) {255std::string line = lr.readLine();256if (skip > 0) {257skip--;258continue;259}260StringTokenizer st(StringUtils::prune(line), oc.getString("timeline-file.separator"));261if (st.hasNext()) {262try {263double t = StringUtils::toDouble(st.next());264double v = 0;265if (st.hasNext()) {266v = StringUtils::toDouble(st.next());267} else {268v = t;269t = time;270}271if (inKMH) {272v /= 3.6;273}274double a = !computeA && st.hasNext() ? StringUtils::toDouble(st.next()) : TrajectoriesHandler::INVALID_VALUE;275double s = haveSlope && st.hasNext() ? StringUtils::toDouble(st.next()) : TrajectoriesHandler::INVALID_VALUE;276if (handler.writeEmissions(*out, "", emissionClass, energyParams.get(), attributes, t, v, a, s)) {277l += v;278totalA += a;279totalS += s;280time++;281}282} catch (EmptyData&) {283throw ProcessError(TLF("Missing an entry in line '%'.", line));284} catch (NumberFormatException&) {285throw ProcessError(TLF("Not numeric entry in line '%'.", line));286}287}288}289if (!quiet) {290std::cout << "sums" << std::endl291<< "length:" << l << std::endl;292}293if (sumOut != nullptr) {294(*sumOut) << oc.getString("emission-class") << "," << lr.getFileName() << "," << time << ","295<< (l / time * 3.6) << "," << (totalS / time) << "," << (totalA / time) << ",";296handler.writeNormedSums(*sumOut, "", l);297}298}299if (oc.isSet("netstate-file")) {300XMLSubSys::runParser(handler, oc.getString("netstate-file"));301}302if (!quiet) {303handler.writeSums(std::cout, "");304}305delete sumOut;306if (out != &std::cout) {307delete out;308}309} catch (InvalidArgument& e) {310MsgHandler::getErrorInstance()->inform(e.what());311MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);312ret = 1;313} catch (ProcessError& e) {314if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {315MsgHandler::getErrorInstance()->inform(e.what());316}317MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);318ret = 1;319#ifndef _DEBUG320} catch (...) {321MsgHandler::getErrorInstance()->inform("Quitting (on unknown error).", false);322ret = 1;323#endif324}325SystemFrame::close();326if (ret == 0 && !quiet) {327std::cout << "Success." << std::endl;328}329return ret;330}331332333/****************************************************************************/334335336