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