1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo 2# Copyright (C) 2013-2019 German Aerospace Center (DLR) and others. 3# This program and the accompanying materials 4# are made available under the terms of the Eclipse Public License v2.0 5# which accompanies this distribution, and is available at 6# http://www.eclipse.org/legal/epl-v20.html 7# SPDX-License-Identifier: EPL-2.0 8 9# @file demand.py 10# @author Daniel Krajzewicz 11# @date 2013-10-10 12# @version $Id$ 13 14from __future__ import absolute_import 15from __future__ import print_function 16import random 17import sumolib 18import os 19import subprocess 20import math 21import tempfile 22 23 24PIVOT__PEAK = 10000 25 26 27class LinearChange: 28 29 def __init__(self, beginFlow, endFlow, beginTime, endTime): 30 self.beginFlow = beginFlow / 3600. 31 self.endFlow = endFlow / 3600. 32 self.beginTime = beginTime 33 self.endTime = endTime 34 35 def depart(self, t): 36 return random.random() < ( 37 self.beginFlow + (self.endFlow - self.beginFlow) / (self.endTime - self.beginTime) * (t - self.beginTime)) 38 39 40class WaveComposition: 41 42 def __init__(self, offset, curves): 43 self.offset = offset 44 self.curves = curves 45 46 def depart(self, t): 47 v = self.offset 48 for c in self.curves: 49 dt = t - c[3] 50 v = v + \ 51 c[0] * math.sin(2 * math.pi * dt * c[2]) + \ 52 c[1] * math.cos(2 * math.pi * dt * c[2]) 53 v = v / 3600. 54 return random.random() < v 55 56 57class Vehicle: 58 59 def __init__(self, id, depart, fromEdge, toEdge, vType, via=None): 60 self.id = id 61 self.depart = depart 62 self.fromEdge = fromEdge 63 self.toEdge = toEdge 64 self.vType = vType 65 self._via = via 66 67 68class Stream: 69 70 def __init__(self, sid, validFrom, validUntil, numberModel, 71 departEdgeModel, arrivalEdgeModel, vTypeModel, via=None): 72 self.sid = sid 73 self._numberModel = numberModel 74 self._departEdgeModel = departEdgeModel 75 self._arrivalEdgeModel = arrivalEdgeModel 76 self._vTypeModel = vTypeModel 77 self._validFrom = validFrom 78 self._validUntil = validUntil 79 self._via = via 80 81 def getVehicleDepartures(self, b, e, sampleFactor=None, seenRatio=None): 82 if self._validFrom is not None and self._validUntil is not None and ( 83 e < self._validFrom or b > self._validUntil): 84 return [] 85 ret = [] 86 for i in range(b, e): 87 if self._validFrom is not None and self._validUntil is not None and ( 88 i < self._validFrom or i > self._validUntil): 89 continue 90 depart = i 91 if sampleFactor is not None: 92 off = i % (sampleFactor * 24) 93 if not off < sampleFactor: 94 continue 95 depart = sampleFactor * int(i / (24 * sampleFactor)) + off 96 if isinstance(self._numberModel, int) or isinstance(self._numberModel, float): 97 if random.random() < float(self._numberModel) / 3600.: 98 ret.append(depart) 99 elif self._numberModel.depart(i): 100 ret.append(depart) 101 return ret 102 103 def getFrom(self, what, i, number): 104 if isinstance(what, str): 105 return what 106 if isinstance(what, int): 107 return what 108 if isinstance(what, float): 109 return what 110 if isinstance(what, list): 111 return what[i % len(what)] 112 if isinstance(what, dict): 113 r = random.random() 114 s = 0 115 for k in what: 116 s = s + what[k] 117 if s > r: 118 return k 119 return None 120 return what.get() 121 122 def toVehicles(self, b, e, offset=0, sampleFactor=None, seenRatio=None): 123 vehicles = [] 124 departures = self.getVehicleDepartures(b, e, sampleFactor, seenRatio) 125 number = len(departures) 126 for i, d in enumerate(departures): 127 fromEdge = self.getFrom(self._departEdgeModel, i, number) 128 toEdge = self.getFrom(self._arrivalEdgeModel, i, number) 129 vType = self.getFrom(self._vTypeModel, i, number) 130 sid = self.sid 131 if sid is None: 132 sid = fromEdge + "_to_" + toEdge + "_" + str(i) 133 vehicles.append( 134 Vehicle(sid + "#" + str(i + offset), int(d), fromEdge, toEdge, vType, self._via)) 135 return vehicles 136 137 138class Demand: 139 140 def __init__(self): 141 self.streams = [] 142 143 def addStream(self, s): 144 self.streams.append(s) 145 146 def build(self, b, e, netName="net.net.xml", routesName="input_routes.rou.xml", sampleFactor=None): 147 vehicles = [] 148 for s in self.streams: 149 vehicles.extend(s.toVehicles(b, e, len(vehicles), sampleFactor)) 150 fdo = tempfile.NamedTemporaryFile(mode="w", delete=False) 151 fdo.write("<routes>\n") 152 for v in sorted(vehicles, key=lambda veh: veh.depart): 153 via = "" 154 if v._via is not None: 155 via = ' via="%s"' % v._via 156 if v.vType == "pedestrian": 157 fdo.write(' <person id="%s" depart="%s" type="pedestrian"><walk from="%s" to="%s"/></person>\n' % 158 (v.id, v.depart, v.fromEdge, v.toEdge)) 159 else: 160 fdo.write(' <trip id="%s" depart="%s" from="%s" to="%s" type="%s" %s/>\n' % 161 (v.id, v.depart, v.fromEdge, v.toEdge, v.vType, via)) 162 fdo.write("</routes>") 163 fdo.close() 164 duarouter = sumolib.checkBinary("duarouter") 165 print("netName > %s" % netName) 166 print("routesName > %s" % routesName) 167 # aeh, implicitly setting --no-warnings is not nice, is it?; and the 168 # need to dump generated vtypes to a temporary file as well 169 subprocess.call([duarouter, "-v", "-n", netName, "-t", fdo.name, "-o", routesName, 170 "--no-warnings", "--additional-files", "vtypes.add.xml", "--vtype-output", "tmp.add.xml"]) 171 os.remove(fdo.name) 172