Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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 marouter_main.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Laura Bieker
18 : /// @author Michael Behrisch
19 : /// @date Thu, 06 Jun 2002
20 : ///
21 : // Main for MAROUTER
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #ifdef HAVE_VERSION_H
26 : #include <version.h>
27 : #endif
28 :
29 : #include <iostream>
30 : #include <string>
31 : #include <limits.h>
32 : #include <ctime>
33 : #include <vector>
34 : #include <xercesc/sax/SAXException.hpp>
35 : #include <xercesc/sax/SAXParseException.hpp>
36 : #include <utils/common/FileHelpers.h>
37 : #include <utils/common/StringUtils.h>
38 : #include <utils/common/MsgHandler.h>
39 : #include <utils/common/UtilExceptions.h>
40 : #include <utils/common/SystemFrame.h>
41 : #include <utils/common/RandHelper.h>
42 : #include <utils/common/ToString.h>
43 : #include <utils/distribution/Distribution_Points.h>
44 : #include <utils/iodevices/OutputDevice.h>
45 : #include <utils/iodevices/OutputDevice_String.h>
46 : #include <utils/options/Option.h>
47 : #include <utils/options/OptionsCont.h>
48 : #include <utils/options/OptionsIO.h>
49 : #include <utils/router/RouteCostCalculator.h>
50 : #include <utils/router/DijkstraRouter.h>
51 : #include <utils/router/AStarRouter.h>
52 : #include <utils/router/CHRouter.h>
53 : #include <utils/router/CHRouterWrapper.h>
54 : #include <utils/router/CCHRouter.h>
55 : #include <utils/xml/XMLSubSys.h>
56 : #include <od/ODCell.h>
57 : #include <od/ODDistrict.h>
58 : #include <od/ODDistrictCont.h>
59 : #include <od/ODDistrictHandler.h>
60 : #include <od/ODMatrix.h>
61 : #include <router/ROCCHMetrics.h>
62 : #include <router/ROEdge.h>
63 : #include <router/ROLoader.h>
64 : #include <router/RONet.h>
65 : #include <router/RORoute.h>
66 : #include <router/RORoutable.h>
67 :
68 : #include "ROMAFrame.h"
69 : #include "ROMAAssignments.h"
70 : #include "ROMAEdgeBuilder.h"
71 : #include "ROMARouteHandler.h"
72 : #include "ROMAEdge.h"
73 :
74 :
75 : // ===========================================================================
76 : // Method implementation
77 : // ===========================================================================
78 : /* -------------------------------------------------------------------------
79 : * data processing methods
80 : * ----------------------------------------------------------------------- */
81 : /**
82 : * loads the net
83 : * The net is in this meaning made up by the net itself and the dynamic
84 : * weights which may be supplied in a separate file
85 : */
86 : void
87 112 : initNet(RONet& net, ROLoader& loader, OptionsCont& oc) {
88 : // load the net
89 112 : ROMAEdgeBuilder builder;
90 112 : ROEdge::setGlobalOptions(oc.getBool("weights.interpolate"));
91 112 : loader.loadNet(net, builder);
92 : // load the weights when wished/available
93 218 : if (oc.isSet("weight-files")) {
94 0 : loader.loadWeights(net, "weight-files", oc.getString("weight-attribute"), false, oc.getBool("weights.expand"));
95 : }
96 218 : if (oc.isSet("lane-weight-files")) {
97 0 : loader.loadWeights(net, "lane-weight-files", oc.getString("weight-attribute"), true, oc.getBool("weights.expand"));
98 : }
99 112 : }
100 :
101 :
102 : double
103 32 : getTravelTime(const ROEdge* const edge, const ROVehicle* const /* veh */, double /* time */) {
104 32 : return edge->getLength() / edge->getSpeedLimit();
105 : }
106 :
107 :
108 : /**
109 : * Computes all pair shortest paths, saving them
110 : */
111 : void
112 1 : computeAllPairs(RONet& net, OptionsCont& oc) {
113 2 : OutputDevice::createDeviceByOption("all-pairs-output");
114 1 : OutputDevice& outFile = OutputDevice::getDeviceByOption("all-pairs-output");
115 : // build the router
116 : typedef DijkstraRouter<ROEdge, ROVehicle> Dijkstra;
117 1 : Dijkstra router(ROEdge::getAllEdges(), oc.getBool("ignore-errors"), &getTravelTime);
118 1 : ConstROEdgeVector into;
119 1 : const int numInternalEdges = net.getInternalEdgeNumber();
120 1 : const int numTotalEdges = (int)net.getEdgeNumber();
121 8 : for (int i = numInternalEdges; i < numTotalEdges; i++) {
122 : const Dijkstra::EdgeInfo& ei = router.getEdgeInfo(i);
123 7 : if (!ei.edge->isInternal()) {
124 7 : router.compute(ei.edge, nullptr, nullptr, 0, into);
125 7 : double fromEffort = router.getEffort(ei.edge, nullptr, 0);
126 56 : for (int j = numInternalEdges; j < numTotalEdges; j++) {
127 49 : double heuTT = router.getEdgeInfo(j).effort - fromEffort;
128 49 : outFile << heuTT;
129 : /*
130 : if (heuTT >
131 : ei.edge->getDistanceTo(router.getEdgeInfo(j).edge)
132 : && router.getEdgeInfo(j).traveltime != std::numeric_limits<double>::max()
133 : ) {
134 : std::cout << " heuristic failure: from=" << ei.edge->getID() << " to=" << router.getEdgeInfo(j).edge->getID()
135 : << " fromEffort=" << fromEffort << " heuTT=" << heuTT << " airDist=" << ei.edge->getDistanceTo(router.getEdgeInfo(j).edge) << "\n";
136 : }
137 : */
138 : }
139 : }
140 : }
141 2 : }
142 :
143 :
144 : /**
145 : * Computes the routes saving them
146 : */
147 : void
148 79 : computeRoutes(RONet& net, OptionsCont& oc, ODMatrix& matrix) {
149 : // build the router
150 : SUMOAbstractRouter<ROEdge, ROVehicle>* router = nullptr;
151 79 : const std::string measure = oc.getString("weight-attribute");
152 82 : const std::string routingAlgorithm = oc.getString("routing-algorithm");
153 79 : const double priorityFactor = oc.getFloat("weights.priority-factor");
154 79 : SUMOTime begin = string2time(oc.getString("begin"));
155 79 : SUMOTime end = string2time(oc.getString("end"));
156 158 : if (oc.isDefault("begin") && matrix.getBegin() >= 0) {
157 : begin = matrix.getBegin();
158 : }
159 158 : if (oc.isDefault("end")) {
160 77 : end = matrix.getEnd() >= 0 ? matrix.getEnd() : SUMOTime_MAX;
161 : }
162 79 : DijkstraRouter<ROEdge, ROVehicle>::Operation ttOp = oc.getInt("paths") > 1 ? &ROMAAssignments::getPenalizedTT : &ROEdge::getTravelTimeStatic;
163 79 : if (measure == "traveltime" && priorityFactor == 0) {
164 78 : if (routingAlgorithm == "dijkstra") {
165 142 : router = new DijkstraRouter<ROEdge, ROVehicle>(ROEdge::getAllEdges(), oc.getBool("ignore-errors"), ttOp, nullptr, false, nullptr, net.hasPermissions());
166 7 : } else if (routingAlgorithm == "astar") {
167 0 : router = new AStarRouter<ROEdge, ROVehicle, ROMapMatcher>(ROEdge::getAllEdges(), oc.getBool("ignore-errors"), ttOp, nullptr, net.hasPermissions());
168 7 : } else if (routingAlgorithm == "CH" && !net.hasPermissions()) {
169 3 : const SUMOTime weightPeriod = (oc.isSet("weight-files") ?
170 3 : string2time(oc.getString("weight-period")) :
171 : SUMOTime_MAX);
172 6 : router = new CHRouter<ROEdge, ROVehicle>(ROEdge::getAllEdges(), oc.getBool("ignore-errors"), &ROEdge::getTravelTimeStatic, SVC_IGNORING, weightPeriod, net.hasPermissions(), false);
173 4 : } else if (routingAlgorithm == "CHWrapper" || routingAlgorithm == "CH") {
174 : // use CHWrapper instead of CH if the net has permissions
175 0 : const SUMOTime weightPeriod = (oc.isSet("weight-files") ?
176 0 : string2time(oc.getString("weight-period")) :
177 : SUMOTime_MAX);
178 : router = new CHRouterWrapper<ROEdge, ROVehicle>(
179 0 : ROEdge::getAllEdges(), oc.getBool("ignore-errors"), &ROEdge::getTravelTimeStatic,
180 0 : begin, end, weightPeriod, net.hasPermissions(), oc.getInt("routing-threads"));
181 4 : } else if (routingAlgorithm == "CCH") {
182 : // One weight-independent hierarchy over the union graph and one
183 : // customized metric for the default vehicle's type, filled from
184 : // the travel times of the current assignment iteration.
185 : // ROMAAssignments resets the router after every iteration (where
186 : // CH rebuilds its whole hierarchy); the reset hook flags the
187 : // metric for re-customization at the next query, so every
188 : // iteration routes on exactly the travel times a Dijkstra query
189 : // would see. Like CH, the metric cannot follow the k shortest
190 : // path penalties (see ROMAFrame::checkOptions).
191 4 : ROCCHGraph* cchGraph = new ROCCHGraph(ROEdge::getAllEdges());
192 4 : ROCCHMetrics::init(cchGraph, &ROEdge::getTravelTimeStatic, begin, SUMOTime_MAX);
193 : SUMOAbstractRouter<ROEdge, ROVehicle>* fallback = new AStarRouter<ROEdge, ROVehicle, ROMapMatcher>(
194 8 : ROEdge::getAllEdges(), oc.getBool("ignore-errors"), &ROEdge::getTravelTimeStatic, nullptr, net.hasPermissions());
195 : router = new CCHRouter<ROEdge, ROVehicle, ROCCHGraph>(
196 : cchGraph, &ROCCHMetrics::get, &ROEdge::getTravelTimeStatic,
197 8 : oc.getBool("ignore-errors"), fallback, nullptr, &ROCCHMetrics::reset);
198 : } else {
199 0 : throw ProcessError(TLF("Unknown routing Algorithm '%'!", routingAlgorithm));
200 : }
201 : } else {
202 : DijkstraRouter<ROEdge, ROVehicle>::Operation op;
203 1 : if (measure == "traveltime") {
204 0 : if (ROEdge::initPriorityFactor(priorityFactor)) {
205 : op = &ROEdge::getTravelTimeStaticPriorityFactor;
206 : } else {
207 : op = &ROEdge::getTravelTimeStatic;
208 : }
209 1 : } else if (measure == "CO") {
210 : op = &ROEdge::getEmissionEffort<PollutantsInterface::CO>;
211 1 : } else if (measure == "CO2") {
212 : op = &ROEdge::getEmissionEffort<PollutantsInterface::CO2>;
213 1 : } else if (measure == "PMx") {
214 : op = &ROEdge::getEmissionEffort<PollutantsInterface::PM_X>;
215 1 : } else if (measure == "HC") {
216 : op = &ROEdge::getEmissionEffort<PollutantsInterface::HC>;
217 1 : } else if (measure == "NOx") {
218 : op = &ROEdge::getEmissionEffort<PollutantsInterface::NO_X>;
219 1 : } else if (measure == "fuel") {
220 : op = &ROEdge::getEmissionEffort<PollutantsInterface::FUEL>;
221 1 : } else if (measure == "electricity") {
222 : op = &ROEdge::getEmissionEffort<PollutantsInterface::ELEC>;
223 1 : } else if (measure == "noise") {
224 : op = &ROEdge::getNoiseEffort;
225 : } else {
226 : op = &ROEdge::getStoredEffort;
227 : }
228 1 : if (measure != "traveltime" && !net.hasLoadedEffort()) {
229 6 : WRITE_WARNINGF(TL("No weight data was loaded for attribute '%'."), measure);
230 : }
231 2 : router = new DijkstraRouter<ROEdge, ROVehicle>(ROEdge::getAllEdges(), oc.getBool("ignore-errors"), op, ttOp, false, nullptr, net.hasPermissions());
232 : }
233 : try {
234 : const RORouterProvider provider(router, nullptr, nullptr, nullptr);
235 : // prepare the output
236 79 : net.openOutput(oc);
237 : // process route definitions
238 156 : if (oc.isSet("timeline")) {
239 16 : matrix.applyCurve(matrix.parseTimeLine(oc.getStringVector("timeline"), oc.getBool("timeline.day-in-hours")));
240 : }
241 78 : matrix.sortByBeginTime();
242 157 : bool haveOutput = OutputDevice::createDeviceByOption("netload-output", "meandata");
243 77 : ROVehicle defaultVehicle(SUMOVehicleParameter(), nullptr, net.getVehicleTypeSecure(DEFAULT_VTYPE_ID), &net);
244 231 : ROMAAssignments a(begin, end, oc.getBool("additive-traffic"), oc.getFloat("weight-adaption"),
245 77 : oc.getInt("max-alternatives"), oc.getBool("capacities.default"), net, matrix, *router,
246 385 : haveOutput ? &OutputDevice::getDeviceByOption("netload-output") : nullptr);
247 77 : a.resetFlows();
248 : #ifdef HAVE_FOX
249 : // this is just to init the CHRouter with the default vehicle
250 77 : router->reset(&defaultVehicle);
251 77 : const int maxNumThreads = oc.getInt("routing-threads");
252 98 : while ((int)net.getThreadPool().size() < maxNumThreads) {
253 21 : new RONet::WorkerThread(net.getThreadPool(), provider);
254 : }
255 : #endif
256 77 : std::string assignMethod = oc.getString("assignment-method");
257 77 : if (assignMethod == "UE") {
258 2 : WRITE_WARNING(TL("Deterministic user equilibrium ('UE') is not implemented yet, using stochastic method ('SUE')."));
259 : assignMethod = "SUE";
260 : }
261 77 : if (assignMethod == "incremental") {
262 142 : a.incremental(oc.getInt("max-iterations"), oc.getBool("verbose"));
263 6 : } else if (assignMethod == "SUE") {
264 24 : a.sue(oc.getInt("max-iterations"), oc.getInt("max-inner-iterations"),
265 31 : oc.getInt("paths"), oc.getFloat("paths.penalty"), oc.getFloat("tolerance"), oc.getString("route-choice-method"));
266 : }
267 : // update path costs and output
268 : OutputDevice* dev = net.getRouteOutput();
269 77 : if (dev != nullptr) {
270 76 : std::vector<std::string> tazParamKeys;
271 152 : if (oc.isSet("taz-param")) {
272 2 : tazParamKeys = oc.getStringVector("taz-param");
273 : }
274 : std::map<SUMOTime, std::string> sortedOut;
275 : SUMOTime lastEnd = -1;
276 : int num = 0;
277 2125 : for (const ODCell* const c : matrix.getCells()) {
278 2049 : if (c->begin >= end || c->end <= begin ||
279 4098 : c->pathsVector.empty() || c->pathsVector.front()->getEdgeVector().empty()) {
280 3 : continue;
281 : }
282 2046 : if (lastEnd >= 0 && lastEnd <= c->begin) {
283 282 : for (std::map<SUMOTime, std::string>::const_iterator desc = sortedOut.begin(); desc != sortedOut.end(); ++desc) {
284 141 : dev->writePreformattedTag(desc->second);
285 : }
286 : sortedOut.clear();
287 : }
288 2046 : if (c->departures.empty()) {
289 2013 : const SUMOTime b = MAX2(begin, c->begin);
290 2013 : const SUMOTime e = MIN2(end, c->end);
291 2013 : const int numVehs = int(c->vehicleNumber * (double)(e - b) / (double)(c->end - c->begin));
292 2013 : OutputDevice_String od(1);
293 4026 : od.openTag(SUMO_TAG_FLOW).writeAttr(SUMO_ATTR_ID, oc.getString("prefix") + toString(num++));
294 2013 : od.writeAttr(SUMO_ATTR_BEGIN, time2string(b)).writeAttr(SUMO_ATTR_END, time2string(e));
295 2013 : od.writeAttr(SUMO_ATTR_NUMBER, numVehs);
296 2013 : matrix.writeDefaultAttrs(od, oc.getBool("ignore-vehicle-type"), c);
297 2013 : od.openTag(SUMO_TAG_ROUTE_DISTRIBUTION);
298 4612 : for (const std::shared_ptr<RORoute>& r : c->pathsVector) {
299 2599 : r->setCosts(router->recomputeCosts(r->getEdgeVector(), &defaultVehicle, begin));
300 5198 : r->writeXMLDefinition(od, nullptr, true, true, false, false);
301 : }
302 2013 : od.closeTag();
303 2013 : od.closeTag();
304 2013 : sortedOut[c->begin] += od.getString();
305 2013 : } else {
306 66 : const bool ignoreType = oc.getBool("ignore-vehicle-type");
307 1394 : for (auto deps : c->departures) {
308 1361 : if (deps.first >= end || deps.first < begin) {
309 : continue;
310 : }
311 4876 : const std::string routeDistId = c->origin + "_" + c->destination + "_" + time2string(c->begin) + "_" + time2string(c->end);
312 2484 : for (const SUMOVehicleParameter& veh : deps.second) {
313 1265 : OutputDevice_String od(1);
314 :
315 1271 : veh.write(od, OptionsCont::getOptions(), SUMO_TAG_VEHICLE, ignoreType || veh.vtypeid == DEFAULT_VTYPE_ID ? "" : veh.vtypeid);
316 1265 : od.openTag(SUMO_TAG_ROUTE_DISTRIBUTION);
317 3586 : for (const std::shared_ptr<RORoute>& r : c->pathsVector) {
318 2321 : r->setCosts(router->recomputeCosts(r->getEdgeVector(), &defaultVehicle, begin));
319 4642 : r->writeXMLDefinition(od, nullptr, true, true, false, false);
320 : }
321 2530 : od.closeTag();
322 1265 : if (!tazParamKeys.empty()) {
323 22 : od.openTag(SUMO_TAG_PARAM).writeAttr(SUMO_ATTR_KEY, tazParamKeys[0]).writeAttr(SUMO_ATTR_VALUE, c->origin).closeTag();
324 11 : if (tazParamKeys.size() > 1) {
325 22 : od.openTag(SUMO_TAG_PARAM).writeAttr(SUMO_ATTR_KEY, tazParamKeys[1]).writeAttr(SUMO_ATTR_VALUE, c->destination).closeTag();
326 : }
327 : }
328 1265 : od.closeTag();
329 1265 : sortedOut[deps.first] += od.getString();
330 1265 : }
331 : }
332 : }
333 2046 : if (c->end > lastEnd) {
334 : lastEnd = c->end;
335 : }
336 : }
337 1337 : for (std::map<SUMOTime, std::string>::const_iterator desc = sortedOut.begin(); desc != sortedOut.end(); ++desc) {
338 1261 : dev->writePreformattedTag(desc->second);
339 : }
340 : haveOutput = true;
341 76 : }
342 1 : if (!haveOutput) {
343 2 : throw ProcessError(TL("No output file given."));
344 : }
345 : // end the processing
346 76 : net.cleanup();
347 84 : } catch (ProcessError&) {
348 3 : net.cleanup();
349 3 : throw;
350 3 : }
351 76 : }
352 :
353 :
354 : /* -------------------------------------------------------------------------
355 : * main
356 : * ----------------------------------------------------------------------- */
357 : int
358 118 : main(int argc, char** argv) {
359 118 : OptionsCont& oc = OptionsCont::getOptions();
360 118 : oc.setApplicationDescription(TL("Import O/D-matrices for macroscopic traffic assignment to generate SUMO routes."));
361 236 : oc.setApplicationName("marouter", "Eclipse SUMO marouter " VERSION_STRING);
362 : int ret = 0;
363 : RONet* net = nullptr;
364 : try {
365 118 : XMLSubSys::init();
366 118 : ROMAFrame::fillOptions();
367 118 : OptionsIO::setArgs(argc, argv);
368 118 : OptionsIO::getOptions();
369 118 : if (oc.processMetaOptions(argc < 2)) {
370 6 : SystemFrame::close();
371 7 : return 0;
372 : }
373 112 : SystemFrame::checkOptions(oc);
374 371 : XMLSubSys::setValidation(oc.getString("xml-validation"), oc.getString("xml-validation.net"), oc.getString("xml-validation.routes"));
375 112 : MsgHandler::initOutputOptions();
376 112 : if (!ROMAFrame::checkOptions()) {
377 0 : throw ProcessError();
378 : }
379 112 : RandHelper::initRandGlobal();
380 : // load data
381 112 : ROLoader loader(oc, false, false);
382 112 : net = new RONet();
383 112 : initNet(*net, loader, oc);
384 218 : if (oc.isSet("all-pairs-output")) {
385 1 : computeAllPairs(*net, oc);
386 1 : if (net->getDistricts().empty()) {
387 1 : delete net;
388 1 : SystemFrame::close();
389 : if (ret == 0) {
390 : std::cout << "Success." << std::endl;
391 : }
392 : return ret;
393 : }
394 : }
395 108 : if (net->getDistricts().empty()) {
396 16 : WRITE_WARNING(TL("No districts loaded, will use edge ids!"));
397 : }
398 : // load districts
399 108 : ODDistrictCont districts;
400 108 : districts.makeDistricts(net->getDistricts());
401 : // load the matrix
402 108 : ODMatrix matrix(districts, oc.getFloat("scale"));
403 108 : matrix.loadMatrix(oc);
404 90 : ROMARouteHandler handler(matrix);
405 90 : matrix.loadRoutes(oc, handler);
406 90 : if (matrix.getNumLoaded() == matrix.getNumDiscarded()) {
407 6 : throw ProcessError(TL("No valid vehicles loaded."));
408 : }
409 113 : if (MsgHandler::getErrorInstance()->wasInformed() && !oc.getBool("ignore-errors")) {
410 16 : throw ProcessError(TL("Loading failed."));
411 : }
412 79 : MsgHandler::getErrorInstance()->clear();
413 316 : WRITE_MESSAGE(toString(matrix.getNumLoaded() - matrix.getNumDiscarded()) + " valid vehicles loaded (total seen: " + toString(matrix.getNumLoaded()) + ").");
414 :
415 : // build routes and parse the incremental rates if the incremental method is choosen.
416 : try {
417 79 : computeRoutes(*net, oc, matrix);
418 3 : } catch (XERCES_CPP_NAMESPACE::SAXParseException& e) {
419 0 : WRITE_ERROR(toString(e.getLineNumber()));
420 : ret = 1;
421 0 : } catch (XERCES_CPP_NAMESPACE::SAXException& e) {
422 0 : WRITE_ERROR(XMLSubSys::transcode(e.getMessage()));
423 : ret = 1;
424 0 : }
425 76 : if (MsgHandler::getErrorInstance()->wasInformed() || ret != 0) {
426 0 : throw ProcessError();
427 : }
428 225 : } catch (const ProcessError& e) {
429 105 : if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {
430 70 : WRITE_ERROR(e.what());
431 : }
432 35 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
433 : ret = 1;
434 35 : }
435 :
436 111 : delete net;
437 111 : SystemFrame::close();
438 111 : if (ret == 0) {
439 : std::cout << "Success." << std::endl;
440 : }
441 : return ret;
442 : }
443 :
444 :
445 : /****************************************************************************/
|