1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
2# Copyright (C) 2016-2019 German Aerospace Center (DLR) and others.
3# SUMOPy module
4# Copyright (C) 2012-2017 University of Bologna - DICAM
5# This program and the accompanying materials
6# are made available under the terms of the Eclipse Public License v2.0
7# which accompanies this distribution, and is available at
8# http://www.eclipse.org/legal/epl-v20.html
9# SPDX-License-Identifier: EPL-2.0
10
11# @file    prt-25-oldmergenode.py
12# @author  Joerg Schweizer
13# @date
14# @version $Id$
15
16"""
17This plugin provides methods to run and analyze PRT networks.
18
19
20"""
21import os
22import sys
23import numpy as np
24import random
25from agilepy.lib_base.processes import Process
26#from xml.sax import saxutils, parse, handler
27
28
29from coremodules.modules_common import *
30import agilepy.lib_base.classman as cm
31import agilepy.lib_base.arrayman as am
32import agilepy.lib_base.xmlman as xm
33#from agilepy.lib_base.misc import get_inversemap
34#from agilepy.lib_base.geometry import find_area
35#from agilepy.lib_base.processes import Process,CmlMixin,ff,call
36from coremodules.network.network import SumoIdsConf
37from coremodules.network.routing import edgedijkstra, get_mincostroute_edge2edge
38from coremodules.simulation import sumo
39from coremodules.simulation.sumo import traci
40#from coremodules.network import routing
41from coremodules.demand.demandbase import DemandobjMixin
42from coremodules.simulation.simulationbase import SimobjMixin
43
44from coremodules.demand.virtualpop import StageTypeMixin, StrategyMixin
45from coremodules.simulation import results as res
46
47BERTHSTATES = {'free': 0, 'waiting': 1, 'boarding': 2, 'alighting': 3}
48VEHICLESTATES = {'init': 0, 'waiting': 1, 'boarding': 2, 'alighting': 3,
49                 'emptytrip': 4, 'occupiedtrip': 5, 'forewarding': 6, 'await_forwarding': 7}
50LEADVEHICLESTATES = [VEHICLESTATES['boarding'], VEHICLESTATES['waiting'],
51                     VEHICLESTATES['emptytrip'], VEHICLESTATES['occupiedtrip']]
52STOPTYPES = {'person': 0, 'freight': 1, 'depot': 2, 'group': 3, 'mixed': 4}
53# def detect_entered_left(x,y):
54#    """
55#    returns the enter and left elemets of list x (before)
56#    and list y (after)
57#    """
58#    if len(x) == 0:
59#        if len(y) == 0:
60#            return
61
62
63class Mergenodes(am.ArrayObjman):
64    def __init__(self, ident, prtservices, **kwargs):
65        self._init_objman(ident=ident,
66                          parent=prtservices,
67                          name='Merge nodes',
68                          info='Contains information and methods for merging the flow of two vehicle streams, including platoons.',
69                          version=0.2,
70                          **kwargs)
71        self._init_attributes()
72
73    def _init_attributes(self):
74        #self.add(cm.ObjConf(PrtBerths('berths',self))   )
75
76        self.add(cm.AttrConf('time_update', 0.5,
77                             groupnames=['parameters'],
78                             name='Update time',
79                             info="Update time for merges.",
80                             unit='s',
81                             ))
82
83        net = self.get_scenario().net
84
85        self.add_col(am.IdsArrayConf('ids_node', net.nodes,
86                                     name='Node ID',
87                                     info='Network node ID.',
88                                     is_index=True,
89                                     ))
90
91        # self.add_col(am.IdsArrayConf('ids_edge_in1', net.edges,
92        #                            name = 'ID edge 1',
93        #                            info = 'ID of edge at entrance 1.',
94        #                            ))
95        if self.get_version() < 0.2:
96            self.delete('ids_mergenode_in1')
97            self.delete('ids_mergenode_in2')
98            self.delete('ids_mergenodes_out')
99        self.add_col(am.IdsArrayConf('ids_mergenode_in1', self,
100                                     name='ID mergenode in 1',
101                                     info='ID of upstream mergenode at incoming line 1.',
102                                     ))
103
104        self.add_col(am.ArrayConf('distances_mergenode_in1', 0.0,
105                                  groupnames=['parameters'],
106                                  name='Distance mergenode in 1',
107                                  info="Distance to mergenode at incoming line 1.",
108                                  unit='m',
109                                  ))
110
111        # self.add_col(am.IdsArrayConf('ids_edge2', net.edges,
112        #                            name = 'ID edge 2',
113        #                            info = 'ID of edge at entrance 2.',
114        #                            ))
115
116        self.add_col(am.IdsArrayConf('ids_mergenode_in2', self,
117                                     name='ID mergenode in 2',
118                                     info='ID of upstream mergenode at incoming line 2.',
119                                     ))
120        self.add_col(am.ArrayConf('distances_mergenode_in2', 0.0,
121                                  groupnames=['parameters'],
122                                  name='Distance mergenode in 2',
123                                  info="Distance to mergenode at incoming line 2.",
124                                  unit='m',
125                                  ))
126
127        self.add_col(am.IdlistsArrayConf('ids_mergenodes_out', net.nodes,
128                                         name='IDs mergenode out',
129                                         info='IDs of downstream mergenodes.',
130                                         ))
131
132        self.add_col(am.ArrayConf('are_station',  default=False,
133                                  groupnames=['parameters'],
134                                  name='is station',
135                                  info="Node is the exit node of a station the incoming edge of this node is a platform edge.",
136                                  ))
137
138        self.set_verion(0.2)
139
140    def make_from_net(self):
141        """
142        Make merge node database from network.
143        """
144        print 'Mergenodes.make_from_net'
145        self.clear()
146        id_prtmode = self.parent.id_prtmode
147
148        net = self.get_scenario().net
149        nodes = net.nodes
150        edges = net.edges
151        lanes = net.lanes
152        id_zippertype = nodes.types.choices['zipper']
153        #ids_ptstop = ptstops.get_ids()
154        #id_mode_prt = self.parent.id_prtmode
155
156        #ids_edges = net.lanes.ids_edge[ptstops.ids_lane[ids_ptstop]]
157        #ids_lanes = net.edges.ids_lanes[ids_edges]
158        #ids_lane = ptstops.ids_lane[ids_ptstop]
159        #edgelengths = net.edges.lengths
160
161        ids_node = nodes.get_ids()
162        for id_node, ids_edge_from, ids_edge_to, id_type in zip(
163            ids_node,
164            nodes.ids_incoming[ids_node],
165            nodes.ids_outgoing[ids_node],
166            nodes.types[ids_node],
167        ):
168
169            if id_type == id_zippertype:
170                # it is a zipper tpe node
171
172                # print 60*'-'
173                # print '  check node',id_node,id_type
174
175                if (len(ids_edge_from) == 2) & (len(ids_edge_to) == 1):
176
177                    # check accesslevels
178                    id_edge1, id_edge2 = ids_edge_from
179                    ids_lane1, ids_lane2 = edges.ids_lanes[ids_edge_from]
180                    al1 = lanes.get_accesslevel(ids_lane1, id_prtmode)
181                    al2 = lanes.get_accesslevel(ids_lane2, id_prtmode)
182                    # print '    2 in, one out => merge node, access: al1,al2',al1,al2
183                    if (al1 == 2) & (al2 == 2):
184                        # print '      +PRT merge with 2 PRT lines entering'
185
186                        id_node_up1, dist1 = self.search_upstream_merge(id_edge1, edges, lanes, id_prtmode)
187                        id_node_up2, dist2 = self.search_upstream_merge(id_edge2, edges, lanes, id_prtmode)
188
189                        ids_mergenode_out = self.search_downstream_merges(ids_edge_to[0], edges, lanes, id_prtmode)
190                        self.add_row(ids_node=id_node,
191                                     ids_mergenode_in1=id_node_up1,
192                                     distances_mergenode_in1=dist1,
193                                     ids_mergenode_in2=id_node_up2,
194                                     distances_mergenode_in2=dist2,
195                                     ids_mergenodes_out=ids_mergenode_out,
196                                     are_station=False,
197                                     )
198
199                elif (len(ids_edge_from) == 1) & (len(ids_edge_to) == 2):
200
201                    id_edge_from = ids_edge_from[0]
202                    al_in = lanes.get_accesslevel(edges.ids_lanes[id_edge_from], id_prtmode)
203                    # print '    one in, 2 out => diverge node, access al_in',al_in
204                    # check if node is outgoing node at a station
205                    if al_in == 1:
206                        # mixed access level of incoming edge=> platform
207                        id_node_up, dist = self.search_upstream_merge(id_edge_from, edges, lanes, id_prtmode)
208
209                        id_edge1, id_edge2 = ids_edge_to
210                        ids_lane1, ids_lane2 = edges.ids_lanes[ids_edge_to]
211
212                        al1 = lanes.get_accesslevel(ids_lane1, id_prtmode)
213                        al2 = lanes.get_accesslevel(ids_lane2, id_prtmode)
214                        # here we could also decide on the number of lanes
215                        # but this may not be robust in the future
216
217                        if (al1 == 1) & (al2 == 2):
218                            # print '      +share access level on outedge 1'
219                            ids_mergenode_out = self.search_downstream_merges(id_edge2, edges, lanes, id_prtmode)
220                            self.add_row(ids_node=id_node,
221                                         ids_mergenode_in1=id_node_up,
222                                         distances_mergenode_in1=dist,
223                                         ids_mergenodes_out=ids_mergenode_out,
224                                         are_station=True,
225                                         )
226
227                        elif (al1 == 2) & (al2 == 1):
228                            # print '      +share access level on outedge 2'
229                            ids_mergenode_out = self.search_downstream_merges(id_edge1, edges, lanes, id_prtmode)
230                            self.add_row(ids_node=id_node,
231                                         ids_mergenode_in1=id_node_up,
232                                         distances_mergenode_in1=dist,
233                                         ids_mergenodes_out=ids_mergenode_out,
234                                         are_station=True,
235                                         )
236
237    def get_scenario(self):
238        return self.parent.get_scenario()
239
240    def prepare_sim(self, process):
241        print 'Mergenodes.prepare_sim'
242        net = self.get_scenario().net
243        nodes = net.nodes
244        edges = net.edges
245        lanes = net.lanes
246        ids_edge_sumo = edges.ids_sumo
247
248        id_prtmode = self.parent.id_prtmode
249        ids = self.get_ids()
250        n_id_max = np.max(ids)+1
251        #self.ids_merge_to_ids_node_sumo = np.zeros(n_id_max,dtype = np.object)
252        #self.ids_merge_to_ids_node_sumo[ids] = nodes.ids_sumo[self.ids_node[ids]]
253
254        self.ids_merge_to_ids_edge_out_sumo = np.zeros(n_id_max, dtype=np.object)
255        #self.ids_node_to_ids_edge_out_sumo = np.zeros(n_id_max,dtype = np.object)
256        self.ids_node_to_ids_merge = np.zeros(np.max(self.ids_node[ids])+1, dtype=np.int32)
257        self.ids_node_to_ids_merge[self.ids_node[ids]] = ids
258
259        # this queue contains all vehicles on the outgoing edge
260        # it is used to identify whether a new vehicle has left the merge
261        self.ids_vehs_out_sumo = np.zeros(n_id_max, dtype=np.object)
262
263        # this queue contains all vehicles on line 1 between
264        # merge and id_node_in1
265        self.ids_vehs_in1 = np.zeros(n_id_max, dtype=np.object)
266        self.ids_vehs_in1_sumo = np.zeros(n_id_max, dtype=np.object)
267
268        # this queue contains all vehicles on line 2 between
269        # merge and id_node_in2
270        self.ids_vehs_in2 = np.zeros(n_id_max, dtype=np.object)
271        self.ids_vehs_in2_sumo = np.zeros(n_id_max, dtype=np.object)
272
273        # this queue contains the merged vehicles for the controlled range
274        self.ids_vehs_merged = np.zeros(n_id_max, dtype=np.object)
275        self.ids_vehs_merged_sumo = np.zeros(n_id_max, dtype=np.object)
276
277        # this queuse contains the line index (1 or 2, 0= not assigned)
278        self.lineinds_vehs_merged = np.zeros(n_id_max, dtype=np.object)
279
280        #self.ids_merge_to_ids_edge_out_sumo[ids] = edges.ids_sumo[self.ids_node[ids]]
281        ids_node = self.ids_node[ids]
282        for id_merge, id_node, ids_edge_out in zip(ids, ids_node, nodes.ids_outgoing[ids_node]):
283            if len(ids_edge_out) == 1:
284                # regular merge
285                self.ids_merge_to_ids_edge_out_sumo[id_merge] = ids_edge_sumo[ids_edge_out[0]]
286
287            elif len(ids_edge_out) == 2:
288                # one edge is PRT, the other ped access
289                # check accesslevels
290                id_edge1, id_edge2 = ids_edge_out
291                ids_lane1, ids_lane2 = edges.ids_lanes[ids_edge_out]
292                al1 = lanes.get_accesslevel(ids_lane1, id_prtmode)
293                al2 = lanes.get_accesslevel(ids_lane2, id_prtmode)
294                if al1 == 2:
295                    self.ids_merge_to_ids_edge_out_sumo[id_merge] = ids_edge_sumo[id_edge1]
296                    #self.ids_node_to_ids_edge_out_sumo[id_node] = ids_edge_sumo[id_edge1]
297                if al2 == 2:
298                    self.ids_merge_to_ids_edge_out_sumo[id_merge] = ids_edge_sumo[id_edge2]
299                    #self.ids_node_to_ids_edge_out_sumo[id_node] = ids_edge_sumo[id_edge2]
300
301            self.ids_vehs_out_sumo[id_merge] = set()
302
303            self.ids_vehs_in1[id_merge] = list()
304            self.ids_vehs_in1_sumo[id_merge] = list()
305            self.ids_vehs_in2[id_merge] = list()
306            self.ids_vehs_in2_sumo[id_merge] = list()
307
308            self.ids_vehs_merged[id_merge] = list()
309            self.ids_vehs_merged_sumo[id_merge] = list()
310            self.lineinds_vehs_merged[id_merge] = list()
311
312        return [(self.time_update.get_value(), self.process_step),
313                ]
314
315    def process_step(self, process):
316        simtime = process.simtime
317        print 79*'_'
318        print 'Mergenodes.process_step at', simtime
319        net = self.get_scenario().net
320        vehicles = self.parent.prtvehicles
321        ids = self.get_ids()
322        for id_merge, id_node, id_edge_out, ids_mergenode_out, ids_veh_out_sumo in\
323            zip(ids,
324                self.ids_node[ids],
325                self.ids_merge_to_ids_edge_out_sumo[ids],
326                self.ids_mergenodes_out[ids],
327                self.ids_vehs_out_sumo[ids],
328                ):
329            print '  '+60*'.'
330            print '  process id_merge,id_node', id_merge, id_node, net.nodes.ids_sumo[id_node]
331            if 0:
332                # print '  ids_veh_out_sumo',ids_veh_out_sumo
333                print '  ids_mergenode_out', ids_mergenode_out
334
335            # check for new vehicle arrivals/departures
336            ids_veh_sumo = set(traci.edge.getLastStepVehicleIDs(id_edge_out))
337            # print '    ids_veh_sumo_prev=',ids_veh_sumo_prev
338            # print '    ids_veh_sumo=',ids_veh_sumo
339
340            if ids_veh_out_sumo != ids_veh_sumo:
341                ids_veh_entered_sumo = list(ids_veh_sumo.difference(ids_veh_out_sumo))
342                ids_veh_entered = vehicles.get_ids_from_ids_sumo(ids_veh_entered_sumo)
343                #ids_veh_left = vehicles.get_ids_from_ids_sumo(list(ids_veh_sumo_prev.difference(ids_veh_sumo)))
344                # print '  ids_veh_entered',ids_veh_entered
345
346                ids_merge_out = self.ids_node_to_ids_merge[ids_mergenode_out]
347                # print '  ids_mergenode_out',ids_mergenode_out
348                # print '  ids_merge_out',ids_merge_out
349                ids_edge_mergeout_sumo = self.ids_merge_to_ids_edge_out_sumo[ids_merge_out]
350
351                for id_veh, id_veh_sumo in zip(ids_veh_entered, ids_veh_entered_sumo):
352                    # print '    >>entered vehicle',id_veh,id_veh_sumo
353                    route_sumo = traci.vehicle.getRoute(id_veh_sumo)
354                    # check which out mergenode are on the current route
355                    # of the vehicle and inform the respectine merges
356                    # then communicate vehicle with dist to merge
357                    for id_merge_out, id_edge_sumo in zip(ids_merge_out, ids_edge_mergeout_sumo):
358                        if id_edge_sumo in route_sumo:
359                            print '    enter veh %d from merge %d to merge %d on edge %s' % (
360                                id_veh, id_merge, id_merge_out, id_edge_sumo)
361                            self.enter_veh(id_veh, id_veh_sumo, id_merge, id_merge_out)
362                            # measure dist to merge with traci call or precalc
363                            break
364
365                self.ids_vehs_out_sumo[id_merge] = ids_veh_sumo
366
367    def enter_veh(self, id_veh, id_veh_sumo, id_merge_from, id_merge_to):
368        print 'enter_veh id_veh %d, id_merge_from %d to id_merge_to %d' % (id_veh, id_merge_from, id_merge_to)
369
370        # in id_merge_from: take vehicle out of merged queue and input queue
371
372        if id_veh in self.ids_vehs_merged[id_merge_from]:
373            # enterd veh should be in pole position in merge queue of merge_from
374
375            # pop if vehicles are properly merged
376            # id_veh_from = self.ids_vehs_merged[id_merge_from].pop()
377            # self.ids_vehs_merged_sumo.ids_vehs_merged[id_merge_from].pop()
378            # if id_veh_from != id_veh:
379            #    print 'WARNING in enter_veh: veh %d instead of veh %d in polepos of merge %d'%(id_veh_from,id_veh, id_merge_from)
380            #    return False
381            # lineind = self.lineinds_vehs_merged[id_merge_from].pop()
382            # for testing: just remove, no matter where
383            ind_pos = self.ids_vehs_merged[id_merge_from].index(id_veh)
384            lineind = self.lineinds_vehs_merged[id_merge_from][ind_pos]
385
386            self.ids_vehs_merged[id_merge_from].pop(ind_pos)  # remove(id_veh)
387            self.ids_vehs_merged_sumo[id_merge_from].pop(ind_pos)  # remove(id_veh_sumo)
388            self.lineinds_vehs_merged[id_merge_from].pop(ind_pos)
389
390            if lineind == 1:
391                self.ids_vehs_in1[id_merge_from].pop()
392                self.ids_vehs_in1_sumo[id_merge_from].pop()
393            if lineind == 2:
394                self.ids_vehs_in2[id_merge_from].pop()
395                self.ids_vehs_in2_sumo[id_merge_from].pop()
396            else:
397                pass
398        else:
399            # the entered vehicle is not in a merge queue
400            # probably a new vehicle at station
401
402            # just be sure that the vehicle is not in any queue
403            # but actually this cannot happen
404            if id_veh in self.ids_vehs_in1[id_merge_from]:
405                print 'WARNING in enter_veh: new veh %d should not be in inqueue 1' % id_veh
406                self.ids_vehs_in1[id_merge_from].remove(id_veh)
407                self.ids_vehs_in1_sumo[id_merge_from].remove(id_veh_sumo)
408
409            if id_veh in self.ids_vehs_in2[id_merge_from]:
410                print 'WARNING in enter_veh: new veh %d should not be in inqueue 1' % id_veh
411                self.ids_vehs_in2[id_merge_from].remove(id_veh)
412                self.ids_vehs_in2_sumo[id_merge_from].remove(id_veh_sumo)
413
414        # on which input line of merge id_merge_to does the vehicle approach?
415        if id_veh in self.ids_vehs_merged[id_merge_to]:
416            indpos = self.ids_vehs_merged[id_merge_to].index(id_veh)
417            lineind = self.lineinds_vehs_merged[id_merge_to][indpos]
418
419        elif self.ids_node_to_ids_merge[self.ids_mergenode_in1[id_merge_to]] == id_merge_from:
420            indpos = -1
421            lineind = 1
422        else:
423            indpos = -1
424            lineind = 2
425
426        if lineind == 1:
427            # from line 1
428            self.ids_vehs_in1[id_merge_to].append(id_veh)
429            self.ids_vehs_in1_sumo[id_merge_to].append(id_veh_sumo)
430            dist_mergenode = self.distances_mergenode_in1[id_merge_to]  # correct with pos
431
432        elif lineind == 2:
433            self.ids_vehs_in2[id_merge_to].append(id_veh)
434            self.ids_vehs_in2_sumo[id_merge_to].append(id_veh_sumo)
435            dist_mergenode = self.distances_mergenode_in2[id_merge_to]  # correct with pos
436
437        if indpos == -1:
438            # vehicle is new and must be merged into   ids_vehs_merged
439            # print '  merge veh %d arriving from in $d at dist %.2fm'%(id_veh,lineind,dist_mergenode)
440            print '  merge veh %d arriving from in %d at dist %.2fm' % (id_veh, lineind, dist_mergenode)
441            ids_vehs_merged = self.ids_vehs_merged[id_merge_to]
442            ids_vehs_merged_sumo = self.ids_vehs_merged_sumo[id_merge_to]
443            lineinds_vehs_merged = self.lineinds_vehs_merged[id_merge_to]
444            id_edge_merge_sumo = self.ids_merge_to_ids_edge_out_sumo[id_merge_to]
445            ind_pos_check = 0
446            is_insert_in_front = False
447            id_veh_merged = -1
448
449            print '  ids_vehs_merged_sumo', ids_vehs_merged_sumo
450            print '  lineinds_vehs_merged', lineinds_vehs_merged
451
452            for id_veh_merged, id_veh_merged_sumo in zip(ids_vehs_merged, ids_vehs_merged_sumo):
453                dist = traci.vehicle.getDrivingDistance(id_veh_merged_sumo, id_edge_merge_sumo, 0.0)
454                print '    check veh %d, dist %.2fm, dist_mergenode  %.2fm, ind_pos_check = %d' % (
455                    id_veh_merged, dist, dist_mergenode, ind_pos_check)
456                if dist > dist_mergenode:
457                    # distance to merge of this vehicle in queue is greater
458                    # than the new vehicle
459                    # inseri on copies? No break
460
461                    # isert in fron of currently checked vehicle
462                    is_insert_in_front = True
463                    break
464
465                ind_pos_check += 1
466
467            #ind_pos_check =-1
468            if is_insert_in_front:
469                ind_insert = ind_pos_check - 1
470                print '    insert veh %d behind veh %d, index %d' % (id_veh, id_veh_merged, ind_insert)
471            else:
472                ind_insert = ind_pos_check-1
473                print '    append veh %d behind veh %d, last index %d' % (id_veh, id_veh_merged, ind_insert)
474
475            ids_vehs_merged.insert(ind_insert, id_veh)
476            ids_vehs_merged_sumo.insert(ind_insert, id_veh_sumo)
477            lineinds_vehs_merged.insert(ind_insert, lineind)
478
479            # send control info to involved vehicles
480            # inform downstream merges
481
482    def search_upstream_merge(self, id_edge_start, edges, lanes, id_prtmode):
483        """
484        Searches next upstream merge node.
485        Returns id_node, id_mergeedge, id_platformedge
486
487        id_platformedge is positive if node is exit node
488        of a station platform.
489        """
490        # print 'search_upstream_merge',id_edge_start
491        length = edges.lengths[id_edge_start]
492        is_merge = False
493        id_edge_platform = -1
494        id_edge = id_edge_start
495        while (not is_merge):
496            ids_edge_incoming = edges.get_incoming(id_edge)
497            # print '    search id_edge,ids_edge_incoming',id_edge,ids_edge_incoming
498            if len(ids_edge_incoming) == 2:
499                #id_edge1, id_edge2 =  ids_edge_to
500                ids_lane1, ids_lane2 = edges.ids_lanes[ids_edge_incoming]
501                al1 = lanes.get_accesslevel(ids_lane1, id_prtmode)
502                al2 = lanes.get_accesslevel(ids_lane2, id_prtmode)
503                if (al1 > 0) & (al2 > 0):
504                    # print '    2 in, one out => it is a merge node'
505                    is_merge = True
506
507            if len(ids_edge_incoming) == 1:
508                ids_lane = edges.ids_lanes[ids_edge_incoming[0]]
509                al = lanes.get_accesslevel(ids_lane, id_prtmode)
510                if (al == 1):
511                    # print '    2 in, one out => it is a merge node'
512                    is_merge = True
513
514            if not is_merge:
515                id_edge = ids_edge_incoming[0]
516                length += edges.lengths[id_edge]
517
518            # print '    id_edge,is_merge',id_edge,is_merge
519        # print '  found node',edges.ids_fromnode[id_edge]
520        return edges.ids_fromnode[id_edge], length
521
522    def search_downstream_merges(self, id_edge_start, edges, lanes, id_prtmode):
523        """
524        Searches next downstream merge nodes.
525        Returns array of downstream merge node IDs
526
527        """
528
529        ids_edge = set([id_edge_start])
530        ids_mergenode = set()
531        # print 'search_downstream_merges',id_edge_start
532
533        is_cont = True
534        #n_cont = 20
535        while (len(ids_edge) > 0) & is_cont:
536            # print '  len(ids_edge)',len(ids_edge),ids_edge
537            ids_edge_new = set()
538
539            for id_edge in ids_edge:
540                ids_edge_outgoing = edges.get_outgoing(id_edge)
541                # print '  id_edge,ids_edge_outgoing',id_edge,ids_edge_outgoing
542                for id_downedge in ids_edge_outgoing:
543                    ids_lane = edges.ids_lanes[id_downedge]
544                    if lanes.get_accesslevel(ids_lane, id_prtmode) == 2:
545                        ids_downedge_incoming = edges.get_incoming(id_downedge)
546                        # print '    id_downedge,ids_downedge_incoming',id_downedge,ids_downedge_incoming
547                        is_merge = False
548
549                        if len(ids_downedge_incoming) == 2:
550                            ids_lane1, ids_lane2 = edges.ids_lanes[ids_downedge_incoming]
551                            al1 = lanes.get_accesslevel(ids_lane1, id_prtmode)
552                            al2 = lanes.get_accesslevel(ids_lane2, id_prtmode)
553                            # print '      check al1,al2',al1,al2,(al1 == 2) & (al2 == 2)
554                            if (al1 == 2) & (al2 == 2):  # real merges
555                                # print '      add mergenode',edges.ids_fromnode[id_downedge]
556                                ids_mergenode.add(edges.ids_fromnode[id_downedge])
557                                is_merge = True
558
559                        if not is_merge:  # len(ids_downedge_incoming) == 1:
560                            #ids_lane = edges.ids_lanes[ids_downedge_incoming]
561                            # if lanes.get_accesslevel(ids_lane1, id_prtmode) == 2:
562                            ids_edge_new.add(id_downedge)
563
564                        # else:
565                        #    print 'WARNING in search_downstream_merges: edge %d has %d incoming edges.'%(id_downedge, len(ids_downedge_incoming))
566
567            ids_edge = ids_edge_new.copy()
568            # print '    ids_edge_new',ids_edge_new,ids_edge,len(ids_edge)
569            # is_cont = n_cont>0 #id_edge_start not in ids_edge
570            #n_cont -= 1
571
572        # if not is_cont:
573        #    print '  endless!!id_edge_start,ids_edge',id_edge_start,ids_edge
574        # print '  ids_mergenode',ids_mergenode
575        return np.array(list(ids_mergenode), dtype=np.int32)
576
577
578class PrtBerths(am.ArrayObjman):
579
580    def __init__(self, ident, prtstops, **kwargs):
581        # print 'PrtVehicles vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
582        self._init_objman(ident=ident,
583                          parent=prtstops,
584                          name='PRT Berths',
585                          info='PRT Berths.',
586                          **kwargs)
587
588        self._init_attributes()
589
590    def _init_attributes(self):
591        #vtypes = self.get_scenario().demand.vtypes
592        net = self.get_scenario().net
593        self.add(cm.AttrConf('length_default', 4.0,
594                             groupnames=['parameters', 'options'],
595                             name='Default length',
596                             info='Default berth length.',
597                             unit='m',
598                             ))
599
600        self.add_col(am.IdsArrayConf('ids_prtstop', self.parent,
601                                     name='PRT stop ID',
602                                     info='PRT stop ID',
603                                     ))
604
605        # states now dynamic, see prepare_sim
606        # if hasattr(self,'states'):
607        #    self.delete('states')
608        # self.add_col(am.ArrayConf( 'states', default = BERTHSTATES['free'],
609        #                            dtype = np.int32,
610        #                            choices = BERTHSTATES,
611        #                            name = 'state',
612        #                            info = 'State of berth.',
613        #                            ))
614
615        self.add_col(am.ArrayConf('stoppositions', default=0.0,
616                                  dtype=np.float32,
617                                  name='Stop position',
618                                  info='Position on edge where vehicle nose stops.',
619                                  ))
620
621    def prepare_sim(self, process):
622        # print 'PrtBerths.prepare_sim'
623        ids = self.get_ids()
624        self.states = BERTHSTATES['free']*np.ones(np.max(ids)+1, dtype=np.int32)
625        self.ids_veh = -1*np.ones(np.max(ids)+1, dtype=np.int32)
626        return []  # berth has no update function
627
628    def get_scenario(self):
629        return self.parent.get_scenario()
630
631    def get_prtvehicles(self):
632        return self.parent.parent.prtvehicles
633
634    def make(self, id_stop, position_from=None, position_to=None,
635             n_berth=None,
636             offset_firstberth=0.0, offset_stoppos=-0.0):
637        stoplength = position_to-position_from
638        # print 'Berths.make',id_stop,stoplength
639
640        # TODO: let define berth number either explicitely or through stoplength
641
642        length_berth = self.length_default.get_value()
643        positions = position_from + offset_firstberth\
644            + np.arange(length_berth-offset_firstberth, stoplength+length_berth, length_berth) + offset_stoppos
645        n_berth = len(positions)
646
647        # force number of berth to be pair
648        if n_berth % 2 == 1:
649            positions = positions[1:]
650            n_berth -= 1
651
652        ids_berth = self.add_rows(n=n_berth,
653                                  stoppositions=positions,
654                                  ids_prtstop=id_stop * np.ones(n_berth, dtype=np.int32),
655                                  )
656        return ids_berth
657
658    def set_prtvehicles(self, prtvehicles):
659        """
660        Defines attributes which are linked with prtvehicles
661        """
662        self.add_col(am.IdsArrayConf('ids_veh_allocated', prtvehicles,
663                                     name='Alloc. veh ID',
664                                     info='ID of  vehicle which have allocated this berth. -1 means no allocation.',
665                                     ))
666
667
668class PrtStops(am.ArrayObjman):
669    def __init__(self, ident, prtservices, **kwargs):
670        self._init_objman(ident=ident,
671                          parent=prtservices,
672                          name='PRT stops',
673                          info='Contains information on PRT stops.',
674                          #xmltag = ('additional','busStop','stopnames'),
675                          version=0.1,
676                          **kwargs)
677        self._init_attributes()
678
679    def _init_attributes(self):
680        self.add(cm.ObjConf(PrtBerths('berths', self)))
681
682        berths = self.get_berths()
683        net = self.get_scenario().net
684
685        self.add(cm.AttrConf('time_update', 0.5,
686                             groupnames=['parameters'],
687                             name='Update time',
688                             info="Update time for station.",
689                             unit='s',
690                             ))
691
692        self.add(cm.AttrConf('time_kickout', 30.0,
693                             groupnames=['parameters'],
694                             name='Kickout time',
695                             info="Time to kick out empty vehicles after vehicles behing have been occupied with passengers.",
696                             unit='s',
697                             ))
698        self.add(cm.AttrConf('timeconst_flow', 0.98,
699                             groupnames=['parameters'],
700                             name='Flow time const',
701                             info="Constant to update the moving average flow.",
702                             ))
703
704        self.add(cm.AttrConf('stoplinegap', 12.0,
705                             groupnames=['parameters'],
706                             name='Stopline gap',
707                             unit='m',
708                             info="Distance between stopline, where vehicles get started, and the end of the lane.",
709                             ))
710
711        if hasattr(self, 'time_update_man'):
712            self.delete('time_update_man')
713            self.delete('timehorizon')
714
715        self.add_col(am.IdsArrayConf('ids_ptstop', net.ptstops,
716                                     name='ID PT stop',
717                                     info='ID of public transport stop. ',
718                                     ))
719
720        if hasattr(self, 'are_depot'):
721            self.delete('are_depot')
722
723        self.add_col(am.ArrayConf('types', default=STOPTYPES['person'],
724                                  dtype=np.int32,
725                                  perm='rw',
726                                  choices=STOPTYPES,
727                                  name='Type',
728                                  info='Type of stop.',
729                                  ))
730
731        self.add_col(am.IdlistsArrayConf('ids_berth_alight', berths,
732                                         #groupnames = ['_private'],
733                                         name='Alight berth IDs',
734                                         info="Alight berth IDs.",
735                                         ))
736
737        self.add_col(am.IdlistsArrayConf('ids_berth_board', berths,
738                                         #groupnames = ['_private'],
739                                         name='Board berth IDs',
740                                         info="Board berth IDs.",
741                                         ))
742
743        # self.add_col(am.ArrayConf( 'inds_berth_alight_allocated', default = 0,
744        #                            #groupnames = ['_private'],
745        #                            dtype = np.int32,
746        #                            perm = 'rw',
747        #                            name = 'Ind aberth lastalloc',
748        #                            info = 'Berth index of last allocated berth in alight zone.',
749        #                            ))
750
751        # self.add_col(am.ArrayConf( 'inds_berth_board_allocated', default = 0,
752        #                            #groupnames = ['_private'],
753        #                            dtype = np.int32,
754        #                            perm = 'rw',
755        #                            name = 'Ind bberth lastalloc',
756        #                            info = 'Berth index of last allocated berth in boarding zone.',
757        #                            ))
758
759    def get_edges(self, ids_prtstop):
760        net = self.get_scenario().net
761        return net.lanes.ids_edge[net.ptstops.ids_lane[self.ids_ptstop[ids_prtstop]]]
762
763    def get_berths(self):
764        return self.berths.get_value()
765
766    def get_scenario(self):
767        return self.parent.get_scenario()
768
769    def set_prtvehicles(self, prtvehicles):
770        self.get_berths().set_prtvehicles(prtvehicles)
771
772    def set_vehicleman(self, vehicleman):
773        self.add(cm.ObjConf(vehicleman, is_child=False, groups=['_private']))
774
775    def get_vehicleman(self):
776        return self.vehicleman.get_value()
777
778    def get_closest(self, coords):
779        """
780        Returns the closest prt stop for each coord in coords vector.
781        """
782        net = self.get_scenario().net
783        ptstops = net.ptstops
784        lanes = net.lanes
785        n = len(coords)
786        # print 'get_closest',n
787
788        #ids_stop = self.get_ids()
789
790        ids_prtstop = self.get_ids()
791        ids_ptstop = self.ids_ptstop[ids_prtstop]
792        coords_stop = ptstops.centroids[ids_ptstop]
793        ids_edge_stop = net.lanes.ids_edge[ptstops.ids_lane[ids_ptstop]]
794
795        inds_closest = np.zeros(n, dtype=np.int32)
796
797        i = 0
798        for coord in coords:
799            ind_closest = np.argmin(np.sum((coord-coords_stop)**2, 1))
800            inds_closest[i] = ind_closest
801            i += 1
802
803        ids_prtstop_closest = ids_prtstop[inds_closest]
804        ids_edge_closest = ids_edge_stop[inds_closest]
805
806        return ids_prtstop_closest, ids_edge_closest
807
808    def get_waitpositions(self, ids, is_alight=False, offset=-0.0):
809        """
810        Assign randomly a wait-position for each stop in ids
811
812        offset is wait position relative to the vehicle nose.
813        """
814        # print 'get_waitpositions min(ids),max(ids)',min(ids),is_alight,max(ids),offset
815        positions = np.zeros(len(ids), dtype=np.float32)
816        randint = random.randint
817        if is_alight:
818            ids_berths = self.ids_berth_alight[ids]
819        else:
820            ids_berths = self.ids_berth_board[ids]
821
822        stoppositions = self.get_berths().stoppositions
823        # print '  ids_berths',ids_berths
824        i = 0
825        for id_stop, ids_berth in zip(ids, ids_berths):
826            #ids_berth = ids_berths[id_stop]
827            ind_berth = randint(0, len(ids_berth)-1)
828
829            positions[i] = stoppositions[ids_berth[ind_berth]]
830            # print '  id_stop,ids_berth,posiions',id_stop,ids_berth,stoppositions[ids_berth[ind_berth]]
831            i += 1
832            #positions[i] = stoppositions[ids_berth[randint(0,len(ids_berth))]]
833        # for id_stop , pos in zip(ids, positions):
834        #    print '  id_stop %d, is_alight = %s, pos %.2fm'%(id_stop, is_alight ,pos)
835
836        return positions+offset
837
838    def prepare_sim(self, process):
839        print 'PrtStops.prepare_sim'
840        net = self.get_scenario().net
841        ptstops = net.ptstops
842        ids_edge_sumo = net.edges.ids_sumo
843
844        berths = self.get_berths()
845        lanes = net.lanes
846        ids_edge_sumo = net.edges.ids_sumo
847        ids = self.get_ids()
848        get_outgoing = net.edges.get_outgoing
849
850        # station management
851        self.ids_stop_to_ids_edge_sumo = np.zeros(np.max(ids)+1, dtype=np.object)
852        self.ids_stop_to_ids_edge = np.zeros(np.max(ids)+1, dtype=np.int32)
853
854        ids_stopedge = lanes.ids_edge[ptstops.ids_lane[self.ids_ptstop[ids]]]
855        # print '  ids,self.stoplines[ids]',ids,self.stoplines[ids]
856        self.ids_stop_to_ids_edge_sumo[ids] = ids_edge_sumo[ids_stopedge]
857        self.ids_stop_to_ids_edge[ids] = ids_stopedge
858
859        # Determine stopline position  where vehicles actually start
860        # running off the station
861
862        # final stop at one meter before end of stopedge
863        self.stoplines = np.zeros(np.max(ids)+1, dtype=np.float32)
864        stoplinegap = self.stoplinegap.get_value()
865        #self.stoplines[ids] = net.edges.lengths[ids_stopedge]-12.0
866        #stoplengths = net.edges.lengths[ids_stopedge]
867        for id_stop, ids_berth_board, length_stopedge in zip(ids, self.ids_berth_board[ids], net.edges.lengths[ids_stopedge]):
868            lastberthstoppos = berths.stoppositions[ids_berth_board][-1]
869            if (length_stopedge-lastberthstoppos) > stoplinegap+1:
870                self.stoplines[id_stop] = length_stopedge-stoplinegap
871            elif length_stopedge > lastberthstoppos:
872                self.stoplines[id_stop] = 0.5*(length_stopedge+lastberthstoppos)
873
874        self.ids_stop_to_ids_acceledge_sumo = np.zeros(np.max(ids)+1, dtype=np.object)
875        for id_stop, id_stopedge in zip(ids, ids_stopedge):
876            self.ids_stop_to_ids_acceledge_sumo[id_stop] = ids_edge_sumo[get_outgoing(id_stopedge)[0]]
877
878        self.id_edge_sumo_to_id_stop = {}
879        for id_stop, id_edge_sumo in zip(ids, self.ids_stop_to_ids_edge_sumo[ids]):
880            self.id_edge_sumo_to_id_stop[id_edge_sumo] = id_stop
881
882        self.inds_berth_alight_allocated = -1*np.ones(np.max(ids)+1, dtype=np.int32)
883        self.inds_berth_board_allocated = -1*np.ones(np.max(ids)+1, dtype=np.int32)
884        self.ids_vehs_alight_allocated = np.zeros(np.max(ids)+1, dtype=np.object)
885        self.ids_vehs_board_allocated = np.zeros(np.max(ids)+1, dtype=np.object)
886
887        # time when last platoon vehicle accumulation started
888        # -1 means no platoon accumulation takes place
889        self.times_plat_accumulate = -1*np.ones(np.max(ids)+1, dtype=np.int32)
890
891        self.ids_vehs_sumo_prev = np.zeros(np.max(ids)+1, dtype=np.object)
892        self.ids_vehs = np.zeros(np.max(ids)+1, dtype=np.object)
893        self.ids_vehs_toallocate = np.zeros(np.max(ids)+1, dtype=np.object)
894        #
895        self.times_lastboard = 10**4*np.ones(np.max(ids)+1, dtype=np.int32)
896
897        # for vehicle management
898        self.numbers_veh = np.zeros(np.max(ids)+1, dtype=np.int32)
899        self.numbers_person_wait = np.zeros(np.max(ids)+1, dtype=np.int32)
900        self.flows_person = np.zeros(np.max(ids)+1, dtype=np.float32)
901        self.ids_veh_lead = -1*np.ones(np.max(ids)+1, dtype=np.int32)
902        #self.ids_veh_lastdep = -1*np.ones(np.max(ids)+1,dtype = np.int32)
903        self.ids_vehs_prog = np.zeros(np.max(ids)+1, dtype=np.object)
904
905        # person management
906        self.ids_persons_sumo_prev = np.zeros(np.max(ids)+1, dtype=np.object)
907        #self.ids_persons_sumo_boarded = np.zeros(np.max(ids)+1,dtype = np.object)
908        self.waittimes_persons = np.zeros(np.max(ids)+1, dtype=np.object)
909        self.waittimes_tot = np.zeros(np.max(ids)+1, dtype=np.float32)
910
911        virtualpop = self.get_scenario().demand.virtualpop
912
913        ids_persons = virtualpop.get_ids()
914        stagelists = virtualpop.get_plans().stagelists
915        prttransits = virtualpop.get_plans().get_stagetable('prttransits')
916        id_person_to_origs_dests = {}
917
918        # create map from person id to various destination information
919        # TODO: needs to be improved for trip chains, move to prtservices
920        # idea: whu not scan prttransits?
921        for id_person, stagelist in zip(ids_persons, stagelists[virtualpop.ids_plan[ids_persons]]):
922            # print '  check dests of id_person',id_person
923            for stages, id_stage in stagelist:
924                if stages.ident == 'prttransits':
925                    id_fromedge_sumo = ids_edge_sumo[stages.ids_fromedge[id_stage]]
926                    id_toedge_sumo = ids_edge_sumo[stages.ids_toedge[id_stage]]
927                    data_orig_dest = (self.id_edge_sumo_to_id_stop[id_fromedge_sumo],
928                                      self.id_edge_sumo_to_id_stop[id_toedge_sumo],
929                                      id_fromedge_sumo,
930                                      id_toedge_sumo)
931
932                    id_person_sumo = virtualpop.get_id_sumo_from_id(id_person)
933                    if id_person_to_origs_dests.has_key(id_person_sumo):
934                        id_person_to_origs_dests[id_person_sumo].append(data_orig_dest)
935                    else:
936                        id_person_to_origs_dests[id_person_sumo] = [data_orig_dest]
937            # print '  prtdests = ',id_person_to_origs_dests[id_person_sumo]
938
939        # print '   id_person_to_origs_dests=\n',id_person_to_origs_dests
940        self.id_person_to_origs_dests = id_person_to_origs_dests
941
942        # this is only used for crazy person stage detection
943        # angles_stop =
944
945        # various initianilizations
946        for id_stop, id_edge_sumo in zip(ids, self.ids_stop_to_ids_edge_sumo[ids]):
947            # set allocation index to last possible berth
948            self.inds_berth_alight_allocated[id_stop] = len(self.ids_berth_alight[id_stop])
949            self.inds_berth_board_allocated[id_stop] = len(self.ids_berth_board[id_stop])
950
951            self.ids_vehs_alight_allocated[id_stop] = []
952            self.ids_vehs_board_allocated[id_stop] = []
953
954            self.ids_vehs_sumo_prev[id_stop] = set([])
955            self.ids_persons_sumo_prev[id_stop] = set([])
956            #self.ids_persons_sumo_boarded [id_stop] = []
957            self.waittimes_persons[id_stop] = {}
958            self.ids_vehs[id_stop] = []
959            self.ids_vehs_toallocate[id_stop] = []
960
961            self.ids_vehs_prog[id_stop] = []
962
963        #    traci.edge.subscribe(id_edge_sumo, [traci.constants.VAR_ARRIVED_VEHICLES_IDS])
964        updatedata_berth = berths.prepare_sim(process)
965
966        return [(self.time_update.get_value(), self.process_step),
967                ]+updatedata_berth
968
969    def process_step(self, process):
970        simtime = process.simtime
971        # print 79*'_'
972        # print 'PrtStops.process_step at',simtime
973        net = self.get_scenario().net
974        ptstops = net.ptstops
975        berths = self.get_berths()
976        lanes = net.lanes
977        ids_edge_sumo = net.edges.ids_sumo
978        vehicles = self.parent.prtvehicles
979        virtualpop = self.get_scenario().demand.virtualpop
980        ids = self.get_ids()
981
982        for id_stop, id_edge_sumo, ids_veh_sumo_prev, ids_person_sumo_prev in\
983            zip(ids, self.ids_stop_to_ids_edge_sumo[ids],
984                self.ids_vehs_sumo_prev[ids],
985                self.ids_persons_sumo_prev[ids]):
986            # print '  '+60*'.'
987            # print '  process id_stop,id_edge_sumo', id_stop,id_edge_sumo
988            if 0:
989
990                # print '    ids_berth_alight',self.ids_berth_alight[id_stop]
991                # print '    ids_berth_board',self.ids_berth_board[id_stop]
992                print '    ids_vehs', self.ids_vehs[id_stop]
993                print '    ids_vehs_toallocate', self.ids_vehs_toallocate[id_stop]
994                print '    ids_vehs_alight_allocated', self.ids_vehs_alight_allocated[id_stop]
995                print '    ids_vehs_board_allocated', self.ids_vehs_board_allocated[id_stop]
996                print '    id_veh_lead prt.%d' % self.ids_veh_lead[id_stop]
997                print '    ids_vehs_prog', self.ids_vehs_prog[id_stop]
998
999                print '    inds_berth_alight_allocated', self.inds_berth_alight_allocated[id_stop]
1000                print '    inds_berth_board_allocated', self.inds_berth_board_allocated[id_stop]
1001                print '    numbers_person_wait', self.numbers_person_wait[id_stop]
1002                # print '    flow_person',self.flows_person[id_stop]
1003                # print '    waittimes_persons',self.waittimes_persons[id_stop]
1004                print '    waittimes_tot', self.waittimes_tot[id_stop]
1005                # no longer print '    ids_persons_sumo_boarded',self.ids_persons_sumo_boarded[id_stop]
1006                print '    times_lastboard', self.times_lastboard[id_stop]
1007
1008            if 0:
1009                for id_veh_sumo in self.ids_vehs_sumo_prev[id_stop]:
1010                    print '    stopstate ', id_veh_sumo, bin(traci.vehicle.getStopState(id_veh_sumo))[
1011                        2:], traci.vehicle.getRoute(id_veh_sumo)
1012
1013            if 0:
1014                self.get_berthqueues(id_stop)
1015
1016            # check for new vehicle arrivals/departures
1017            ids_veh_sumo = set(traci.edge.getLastStepVehicleIDs(id_edge_sumo))
1018            # print '    ids_veh_sumo_prev=',ids_veh_sumo_prev
1019            # print '    ids_veh_sumo=',ids_veh_sumo
1020
1021            if ids_veh_sumo_prev != ids_veh_sumo:
1022                ids_veh_entered = vehicles.get_ids_from_ids_sumo(list(ids_veh_sumo.difference(ids_veh_sumo_prev)))
1023                ids_veh_left = vehicles.get_ids_from_ids_sumo(list(ids_veh_sumo_prev.difference(ids_veh_sumo)))
1024                for id_veh in ids_veh_entered:
1025                    self.enter(id_stop, id_veh)
1026
1027                for id_veh in ids_veh_left:
1028                    self.exit(id_stop, id_veh)
1029                self.ids_vehs_sumo_prev[id_stop] = ids_veh_sumo
1030                # print '    ids_veh_sumo_entered',ids_veh_sumo_entered
1031                # print '    ids_veh_sumo_left',ids_veh_sumo_left
1032
1033            # check whether allocated vehicles arrived at alighting berths
1034            ids_veh_remove = []
1035            for id_veh in self.ids_vehs_alight_allocated[id_stop]:
1036                # TODO: here we could also check vehicle position
1037                # print '   isStopped',vehicles.get_id_sumo(id_veh),traci.vehicle.isStopped(vehicles.get_id_sumo(id_veh))
1038                if traci.vehicle.isStopped(vehicles.get_id_sumo(id_veh)):
1039                    ids_veh_remove.append(id_veh)
1040                    id_berth_alight = vehicles.ids_berth[id_veh]
1041                    berths.ids_veh[id_berth_alight] = id_veh
1042                    berths.states[id_berth_alight] = BERTHSTATES['alighting']
1043                    vehicles.alight(id_veh)
1044
1045            for id_veh in ids_veh_remove:
1046                self.ids_vehs_alight_allocated[id_stop].remove(id_veh)
1047
1048            # check whether we can move vehicles from alighting to
1049            # boarding berths
1050
1051            # TODO: here we must check if berth in boarding zone are free
1052            # AND if they are occupied with empty vehicles, those
1053            # vehicles need to be kicked out...but only in case
1054            # new vehicles are waiting to be allocated
1055
1056            ids_berth_alight = self.ids_berth_alight[id_stop][::-1]
1057            ids_berth_board = self.ids_berth_board[id_stop][::-1]
1058
1059            if True:  # len(self.ids_vehs_alight_allocated[id_stop])==0:
1060                # all vehicles did arrive in alight position
1061
1062                # identify berth and vehicles ready to forward to boarding
1063                ids_veh_forward = []
1064                ids_berth_forward = []
1065                for id_berth_alight, id_veh in zip(
1066                    ids_berth_alight,
1067                    berths.ids_veh[ids_berth_alight],
1068                ):
1069                    # print '    check alight->board  for veh prt.%d'%id_veh,'at berth',id_berth_alight,berths.states[id_berth_alight], berths.states[id_berth_alight]==BERTHSTATES['free']
1070
1071                    if id_veh >= 0:  # is there a waiting vehicle
1072                        if vehicles.is_completed_alighting(id_veh):
1073                            ids_veh_forward.append(id_veh)
1074                            ids_berth_forward.append(id_berth_alight)
1075                        else:
1076                            # print '    vehicle has not finished alighting...'
1077                            # stop allocating berth in board zone
1078                            # to prevent allocations behind non-allocated vehicles
1079                            break
1080
1081                n_veh_alloc = len(ids_veh_forward)
1082
1083                if n_veh_alloc > 0:
1084
1085                    if self.inds_berth_board_allocated[id_stop] > n_veh_alloc:
1086                        queues = self.get_berthqueues(id_stop)
1087                    else:
1088                        queues = None
1089
1090                    # print '  found %d veh at id_stop=%d to berth alloc with alloc index %d'%(n_veh_alloc,id_stop, self.inds_berth_board_allocated [id_stop])
1091                    # if queues is not None:
1092                    #    print '    queues',queues
1093                    for id_berth_alight, id_veh in zip(
1094                        ids_berth_forward,
1095                        ids_veh_forward,
1096                    ):
1097
1098                        id_berth_board = self.allocate_board(id_stop, n_veh_alloc, queues)
1099                        # print '    try allocate id_veh=prt.%d for berth id_berth_board=%d'%(id_veh,id_berth_board)
1100                        if id_berth_board >= 0:
1101                            # print '     send vehicle id_veh %d to id_berth_board %d'%(id_veh,id_berth_board)#,berths.stoppositions[id_berth_board]
1102                            n_veh_alloc -= 1
1103                            berths.ids_veh[id_berth_alight] = -1
1104
1105                            berths.states[id_berth_alight] = BERTHSTATES['free']
1106
1107                            vehicles.control_stop_board(id_veh, id_stop, id_berth_board,
1108                                                        id_edge_sumo=self.ids_stop_to_ids_edge_sumo[id_stop],
1109                                                        position=berths.stoppositions[id_berth_board],
1110                                                        )
1111                            self.ids_vehs_board_allocated[id_stop].append(id_veh)
1112
1113            # if all allocated vehicles found their berth and all berths are free, then
1114            # reset  alight allocation index
1115            # print '  check for reset of alight allocation index'
1116            if len(self.ids_vehs_alight_allocated[id_stop]) == 0:
1117                if np.all(berths.states[ids_berth_alight] == BERTHSTATES['free']):
1118                    # print '    reset inds_berth_alight_allocated',self.inds_berth_alight_allocated[id_stop],'->',len(self.ids_berth_alight[id_stop])
1119                    self.inds_berth_alight_allocated[id_stop] = len(self.ids_berth_alight[id_stop])
1120
1121                    # try to allocate unallocated vehicles
1122                    ids_veh_remove = []
1123                    for id_veh in self.ids_vehs_toallocate[id_stop]:
1124                        id_berth = self.allocate_alight(id_stop)
1125                        if id_berth < 0:
1126                            # allocation failed
1127                            # do nothing, vehicle continues to wait for allocation
1128                            pass
1129                        else:
1130                            # command vehicle to go to berth for alighting
1131                            # print '     send waiting vehicle id_veh %d to id_berth_alight %d'%(id_veh,id_berth)#,berths.stoppositions[id_berth]
1132                            self.parent.prtvehicles.control_stop_alight(id_veh, id_stop, id_berth,
1133                                                                        id_edge_sumo=self.ids_stop_to_ids_edge_sumo[id_stop],
1134                                                                        position=self.get_berths(
1135                                                                        ).stoppositions[id_berth],
1136                                                                        )
1137                            self.ids_vehs_alight_allocated[id_stop].append(id_veh)
1138                            ids_veh_remove.append(id_veh)
1139
1140                    for id_veh in ids_veh_remove:
1141                        self.ids_vehs_toallocate[id_stop].remove(id_veh)
1142
1143            # check whether allocated vehicles arrived at boarding berths
1144            ids_veh_remove = []
1145            for id_veh in self.ids_vehs_board_allocated[id_stop]:
1146                # TODO: here we could also check vehicle position
1147                if traci.vehicle.isStopped(vehicles.get_id_sumo(id_veh)):
1148                    ids_veh_remove.append(id_veh)
1149                    id_berth_board = vehicles.ids_berth[id_veh]
1150                    berths.ids_veh[id_berth_board] = id_veh
1151                    berths.states[id_berth_board] = BERTHSTATES['boarding']
1152                    vehicles.board(id_veh,
1153                                   id_edge_sumo=self.ids_stop_to_ids_edge_sumo[id_stop])
1154
1155                    self.parent.vehicleman.indicate_trip_empty(id_veh, id_stop, simtime)
1156                    # vehicle could become potentially the lead vehicle
1157                    self.try_set_leadveh(id_stop, id_veh)
1158
1159            for id_veh in ids_veh_remove:
1160                self.ids_vehs_board_allocated[id_stop].remove(id_veh)
1161
1162            # if all allocated vehicles for board area
1163            # found their berth and all berths are free, then
1164            # reset  allocation index
1165            # print '  board berth rest check', len(self.ids_vehs_board_allocated[id_stop])==0,np.all(berths.states[ids_berth_board]==BERTHSTATES['free']),berths.states[ids_berth_board]
1166            if (self.inds_berth_board_allocated[id_stop] == 0) & (len(self.ids_vehs_board_allocated[id_stop]) == 0):
1167
1168                if np.all(berths.states[ids_berth_board] == BERTHSTATES['free']):
1169                    # print '    reset inds_berth_board_allocated to',len(self.ids_berth_board[id_stop])
1170                    self.inds_berth_board_allocated[id_stop] = len(self.ids_berth_board[id_stop])
1171
1172            # check for new person entering/left the station edge
1173            ids_person_sumo = set(traci.edge.getLastStepPersonIDs(id_edge_sumo))
1174
1175            n_enter = 0
1176            if ids_person_sumo_prev != ids_person_sumo:
1177
1178                if 0:
1179                    print '  change\n  id_person_sumo', ids_person_sumo
1180                    print '  ids_person_sumo_prev', ids_person_sumo_prev
1181                # print '  dir(traci.person)',dir(traci.person)
1182                # for id_person_sumo in ids_person_sumo:
1183                #    print '  id_person_sumo',id_person_sumo,traci.person.getRoadID(id_person_sumo),traci.person.getVehicle(id_person_sumo)
1184
1185                # deal with persons who left the edge
1186                # NEW: this is done later when loaded vehicles are investigated
1187
1188                #ids_person_sumo_left = ids_person_sumo_prev.difference(ids_person_sumo)
1189                # print '  ids_person_sumo_left',ids_person_sumo_left
1190                # for id_person_sumo in ids_person_sumo_left:
1191                #    print '  id_person_sumo_left pers',id_person_sumo,id_edge_sumo,traci.person.getRoadID(id_person_sumo),traci.person.getVehicle(id_person_sumo)
1192                #    #print '  ids_person_sumo',ids_person_sumo
1193                #    # tricky: if the person who left the edge id_edge_sumo
1194                #    # shows still id_edge_sumo then this person is in a vehicle
1195                #    if traci.person.getRoadID(id_person_sumo) == id_edge_sumo:
1196                #        #print '  person boarded: pers',id_person_sumo,traci.person.getLanePosition(id_person_sumo)
1197                #        self.ids_persons_sumo_boarded[id_stop].append(id_person_sumo)
1198                #        self.waittimes_tot[id_stop] -= simtime - self.waittimes_persons[id_stop][id_person_sumo]
1199                #        del self.waittimes_persons[id_stop][id_person_sumo]
1200                #        self.numbers_person_wait[id_stop] -= 1
1201
1202                # deal with persons who entered the edge
1203                ids_person_sumo_entered = ids_person_sumo.difference(ids_person_sumo_prev)
1204                for id_person_sumo in ids_person_sumo_entered:
1205                    # print '  entered id_person_sumo',id_person_sumo,traci.person.getRoadID(id_person_sumo)
1206                    if self.id_person_to_origs_dests.has_key(id_person_sumo):
1207                        id_edge_sumo_dests = self.id_person_to_origs_dests[id_person_sumo]
1208                        # check if person still has a PRT trip
1209
1210                        if len(id_edge_sumo_dests) > 0:
1211                            # check if next trip has origin edge equal to edge of this stop
1212                            if id_edge_sumo_dests[0][2] == id_edge_sumo:
1213                                # print '  add to waittimes_persons',id_person_sumo
1214                                self.waittimes_persons[id_stop][id_person_sumo] = simtime
1215                                n_enter += 1
1216
1217                                # communicate person entry to vehman
1218                                self.parent.vehicleman.note_person_entered(
1219                                    id_stop, id_person_sumo, id_edge_sumo_dests[0][1])
1220
1221                            # else:
1222                            #    print 'WARNING: person %s starts with % insted of %s.'%(id_person_sumo,id_edge_sumo_dests[0][2],id_edge_sumo)
1223
1224                self.numbers_person_wait[id_stop] += n_enter
1225                self.ids_persons_sumo_prev[id_stop] = ids_person_sumo
1226
1227            self.waittimes_tot += self.numbers_person_wait*self.time_update.get_value()
1228
1229            timeconst_flow = self.timeconst_flow.get_value()
1230            self.flows_person[id_stop] = timeconst_flow*self.flows_person[id_stop] + \
1231                (1.0-timeconst_flow)*float(n_enter)/self.time_update.get_value()
1232
1233            if 0:
1234                for id_person_sumo in ids_person_sumo_prev:
1235                    print '    ids_person_sumo=%s pos = %.2f ' % (
1236                        id_person_sumo, traci.person.getLanePosition(id_person_sumo))
1237                # nomore print '    ids_persons_sumo_boarded',self.ids_persons_sumo_boarded[id_stop]
1238
1239            # check if boarding is completed in load area,
1240            # starting with last vehicle
1241            ids_berth_board = self.ids_berth_board[id_stop][::-1]
1242            for id_berth_board, id_veh in zip(
1243                ids_berth_board,
1244                berths.ids_veh[ids_berth_board],
1245            ):
1246                if id_veh >= 0:  # is there a waiting vehicle
1247                    id_veh_sumo = vehicles.get_veh_if_completed_boarding(id_veh)
1248                    if id_veh_sumo:
1249                        id_person_sumo = self.init_trip_occupied(id_stop,
1250                                                                 id_berth_board,
1251                                                                 id_veh,
1252                                                                 id_veh_sumo,
1253                                                                 simtime)
1254                        if id_person_sumo is not None:
1255                            # do some statistics here
1256                            self.waittimes_tot[id_stop] -= simtime - self.waittimes_persons[id_stop][id_person_sumo]
1257                            del self.waittimes_persons[id_stop][id_person_sumo]
1258                            self.numbers_person_wait[id_stop] -= 1
1259
1260            # check if there are passengers in the vehicles which wait for
1261            # alight allocate
1262            # TODO: can be replaced by a single instruction
1263            n_pax = 0
1264            for id_veh in self.ids_vehs_alight_allocated[id_stop]+self.ids_vehs_toallocate[id_stop]:
1265                if vehicles.states[id_veh] == VEHICLESTATES['occupiedtrip']:
1266                    n_pax += 1
1267            # print '    n_pax' ,n_pax
1268            # check whether to foreward vehicles in boarding berth
1269
1270            # no foreward if all berth are free occupied vehicles
1271            if np.all(berths.states[ids_berth_board] == BERTHSTATES['free']):
1272                # print '    foreward all occupied id_stop,ids_berth_board',id_stop,ids_berth_board
1273                #self.foreward_boardzone(id_stop, ids_berth_board)
1274                self.times_lastboard[id_stop] = 10**4  # reset clock if all are free
1275
1276            # foreward if there are passengers in unallocated vehicles
1277            elif (self.numbers_person_wait[id_stop] == 0) & (n_pax > 0):
1278                # passengers arriving, no persons waiting
1279                # kick out immediately
1280                self.foreward_boardzone(id_stop, ids_berth_board, simtime)
1281
1282            # elif (self.numbers_person_wait[id_stop]>0) & (n_pax>0) & (self.times_lastboard[id_stop] == 10**4):
1283            elif ((self.numbers_person_wait[id_stop] > 0) | (n_pax > 0)) & (self.times_lastboard[id_stop] == 10**4):
1284                # passengers arriving but still persons boarding
1285                # reset kick-out counter
1286                self.times_lastboard[id_stop] = simtime
1287
1288            elif simtime - self.times_lastboard[id_stop] > self.time_kickout.get_value():
1289                # print '  call foreward_boardzone timeout',process.simtime,self.times_lastboard[id_stop],process.simtime - self.times_lastboard[id_stop]
1290                self.foreward_boardzone(id_stop, ids_berth_board, simtime)
1291
1292            # check whether a programmed vehicle can be started
1293            if self.types[id_stop] == STOPTYPES['group']:
1294                self.start_vehicles_platoon(id_stop, process)
1295            else:
1296                self.start_vehicles(id_stop, process)
1297
1298    def start_vehicles(self, id_stop, process):
1299        # print 'start_vehicles=\n',self.ids_vehs_prog[id_stop]
1300        i = 0
1301        vehicles = self.parent.prtvehicles
1302        ids_vehs_prog = self.ids_vehs_prog[id_stop]
1303        for time_start, id_veh, id_stop_target, is_started in ids_vehs_prog:
1304            if process.simtime > time_start:
1305                if not is_started:
1306                    if traci.vehicle.isStopped(vehicles.get_id_sumo(id_veh)):
1307                        route, duration = self.route_stop_to_stop(id_stop, id_stop_target)
1308                        self.parent.prtvehicles.reschedule_trip(id_veh,
1309                                                                route_sumo=self.get_scenario().net.edges.ids_sumo[route]
1310                                                                )
1311                        ids_vehs_prog[i][3] = True
1312            i += 1
1313
1314    def route_stop_to_stop(self, id_stop_from, id_stop_to):
1315        # route
1316        return self.parent.get_route(self.ids_stop_to_ids_edge[id_stop_from],
1317                                     self.ids_stop_to_ids_edge[id_stop_to]
1318                                     )
1319
1320    def start_vehicles_platoon(self, id_stop, process, timeout_platoon=40, n_platoon_max=8):
1321        # print 'start_vehicles_platoon id_stop, times_plat_accumulate',id_stop,self.times_plat_accumulate[id_stop]
1322        # print '  ids_vehs_prog=\n',self.ids_vehs_prog[id_stop]
1323
1324        if self.times_plat_accumulate[id_stop] < 0:
1325            # print '  accumulation has not even started'
1326            return
1327
1328        vehicles = self.parent.prtvehicles
1329        ids_vehs_prog = self.ids_vehs_prog[id_stop]
1330        inds_platoon = []
1331        i = 0  # len(ids_vehs_prog)
1332        id_veh_nextplatoon = -1
1333        for time_start, id_veh, id_stop_target, is_started in ids_vehs_prog:  # [::-1]:
1334
1335            if not is_started:
1336                if len(inds_platoon) == 0:
1337                    # check if first vehicle in platoon is stopped
1338                    if traci.vehicle.isStopped(vehicles.get_id_sumo(id_veh)):
1339                        inds_platoon.append(i)
1340                    else:
1341                        break
1342                else:
1343                    # append to platoon
1344                    inds_platoon.append(i)
1345
1346            i += 1
1347
1348        # print '  trigger platoon?', inds_platoon,len(inds_platoon),n_platoon_max,process.simtime - self.times_plat_accumulate[id_stop],timeout_platoon
1349        if len(inds_platoon) == 0:
1350            return
1351
1352        if (process.simtime - self.times_plat_accumulate[id_stop] > timeout_platoon)\
1353                | (len(inds_platoon) >= n_platoon_max):
1354
1355            # platoon release triggered
1356            self._trigger_platoon(id_stop, inds_platoon)
1357
1358    def _trigger_platoon(self, id_stop, inds_platoon):
1359        # print 'trigger_platoon inds_platoon',inds_platoon
1360        ids_vehs_prog = self.ids_vehs_prog[id_stop]
1361        self.times_plat_accumulate[id_stop] = -1
1362
1363        time_start, id_veh, id_stop_target, is_prog = ids_vehs_prog[inds_platoon[0]]
1364        self.parent.prtvehicles.reschedule_trip(id_veh, self.ids_stop_to_ids_edge_sumo[id_stop_target])
1365        ids_vehs_prog[inds_platoon[0]][3] = True
1366        # one vehicle platoon, no followers
1367        if len(inds_platoon) > 1:
1368            # try to concatenate followers
1369            for i in xrange(1, len(inds_platoon)):
1370                time_start_pre, id_veh_pre, id_stop_target_pre, is_prog_pre = ids_vehs_prog[inds_platoon[i-1]]
1371                time_start, id_veh, id_stop_target, is_prog = ids_vehs_prog[inds_platoon[i]]
1372                # print '    check prt.%d'%ids_vehs_prog[inds_platoon[i]][1],'with leader prt.%d'%ids_vehs_prog[inds_platoon[i-1]][1],id_stop_target == id_stop_target_pre
1373                if id_stop_target == id_stop_target_pre:
1374                    self.parent.prtvehicles.concatenate(id_veh, id_veh_pre)
1375
1376                self.parent.prtvehicles.reschedule_trip(id_veh, self.ids_stop_to_ids_edge_sumo[id_stop_target])
1377                ids_vehs_prog[inds_platoon[i]][3] = True
1378
1379    def try_set_leadveh(self, id_stop, id_veh):
1380
1381        if self.ids_veh_lead[id_stop] >= 0:
1382            # print 'try_set_leadveh leader already defined',id_stop,id_veh,self.ids_veh_lead[id_stop]
1383            return False
1384        else:
1385            ind_queue = self.ids_vehs[id_stop].index(id_veh)
1386            # print 'try_set_leadveh id_stop, id_veh,ind_queue',id_stop, 'prt.%d'%id_veh,ind_queue,len(self.ids_vehs[id_stop]),len(self.ids_vehs_prog[id_stop])
1387
1388            if ind_queue == 0:  # len(self.ids_vehs[id_stop])-1:
1389                # print  '  id_veh is new leader because last position',self.ids_vehs[id_stop].index(id_veh)
1390                # print  '  ids_vehs',self.ids_vehs[id_stop]
1391                self.set_leadveh(id_stop, id_veh)
1392                return True
1393
1394            elif len(self.ids_vehs_prog[id_stop]) == ind_queue:
1395                # print  '   id_veh is new leader because all vehicles in front are already programmed'
1396                self.set_leadveh(id_stop, id_veh)
1397                return True
1398            # elif len(self.ids_vehs_prog[id_stop])>0:
1399            #    if (self.ids_vehs[id_stop][ind_queue-1] == self.ids_vehs_prog[id_stop][-1][1]) :
1400            #        print  '   id_veh is new leader because next vehicle %d is programmed'%(self.ids_vehs[id_stop][ind_queue-1],)
1401            #        self.set_leadveh(id_stop, id_veh)
1402            #        return True
1403            #    else:
1404            #        return False
1405
1406            else:
1407                return False
1408
1409    def set_leadveh(self, id_stop, id_veh):
1410        # print 'set_leadveh id_stop=%d, prt.%d'%( id_stop,id_veh)
1411        self.ids_veh_lead[id_stop] = id_veh
1412
1413    def program_leadveh(self, id_stop, id_veh, id_stop_target, time_start):
1414        print 'program_leadveh prt.%d  from id_stop %d to id_stop_target %d at %d' % (
1415            id_veh, id_stop, id_stop_target, time_start), 'check leader', id_veh == self.ids_veh_lead[id_stop]
1416
1417        # check also if occupied in the meanwhile?? need to know emptytrip or not...
1418        if id_veh == self.ids_veh_lead[id_stop]:
1419            # check in vehman:if self.parent.prtvehicles.is_still_empty(id_veh):
1420
1421            if self.parent.prtvehicles.states[id_veh] == VEHICLESTATES['boarding']:
1422                id_berth_board = self.parent.prtvehicles.ids_berth[id_veh]
1423                self.init_trip_empty(id_stop, id_berth_board, id_veh, time_start, is_ask_vehman=False)
1424
1425            self.ids_veh_lead[id_stop] = -1
1426
1427            # format for programmed vehicle list:
1428            # [time_start, id_veh, id_stop_target,is_started]
1429            self.ids_vehs_prog[id_stop].append([time_start, id_veh, id_stop_target, False])
1430
1431            if self.types[id_stop] == STOPTYPES['group']:
1432                # in platoon mode...
1433                # set a stop for this vehicle if there are only started, programmed
1434                # vehicles in front , or no vehicle
1435
1436                # check if this vehicle needs to stop in front of the stopline
1437                # in order to hold up other vehicles in the platoon
1438                # print '    check stopline',len(self.ids_vehs_prog[id_stop])
1439                # print '     ids_vehs_prog ',self.ids_vehs_prog[id_stop]
1440                is_stop = True
1441                for i in range(len(self.ids_vehs_prog[id_stop])-2, -1, -1):
1442                    # print '    check prt.%d'%self.ids_vehs_prog[id_stop][i][1],'started',self.ids_vehs_prog[id_stop][i][3]
1443                    if not self.ids_vehs_prog[id_stop][i][3]:  # is already started
1444                        is_stop = False
1445                        break
1446
1447                if is_stop:
1448                    # make this vehicle stop at stopline and reset platoon timer
1449                    self.parent.prtvehicles.set_stop(
1450                        id_veh, self.ids_stop_to_ids_edge_sumo[id_stop], self.stoplines[id_stop])
1451                    self.times_plat_accumulate[id_stop] = time_start
1452
1453            # try make previous vehicle the lead  vehicle
1454            ind_queue = self.ids_vehs[id_stop].index(id_veh)
1455
1456            # print '  ind_queue,queuelen,ok',ind_queue,len(self.ids_vehs[id_stop]),len(self.ids_vehs[id_stop]) > ind_queue+1
1457            if len(self.ids_vehs[id_stop]) > ind_queue+1:
1458                id_veh_newlead = self.ids_vehs[id_stop][ind_queue+1]
1459                # print '  id_veh_newlead, state',id_veh_newlead, self.parent.prtvehicles.states[id_veh_newlead]
1460                if self.parent.prtvehicles.states[id_veh_newlead] in LEADVEHICLESTATES:
1461                    self.set_leadveh(id_stop, id_veh_newlead)
1462
1463            # print '  new lead veh prt.%d'%(self.ids_veh_lead[id_stop],)
1464            return True
1465
1466        else:
1467            return False
1468
1469    def init_trip_occupied(self, id_stop, id_berth, id_veh, id_veh_sumo, simtime):
1470        # TODO: actually a berth method??
1471        berths = self.get_berths()
1472
1473        #id_veh_sumo = self.parent.prtvehicles.get_id_sumo(id_veh)
1474        n_pax = traci.vehicle.getPersonNumber(id_veh_sumo)
1475        # print 'init_trip_occupied', id_stop, id_berth, 'veh=%s'%id_veh_sumo,'simtime',simtime,'n_pax',n_pax
1476
1477        # identify whic of the boarding persons is in the
1478        # vehicle which completed boarding
1479        dist_min = np.inf
1480        id_person_sumo_inveh = None
1481        stoppos = berths.stoppositions[id_berth]
1482
1483        for id_person_sumo in self.ids_persons_sumo_prev[id_stop]:
1484            # print '  check veh of person',id_person_sumo,traci.person.getVehicle(id_person_sumo),id_veh_sumo
1485            if traci.person.getVehicle(id_person_sumo) == id_veh_sumo:
1486                id_person_sumo_inveh = id_person_sumo
1487
1488            #d = abs(stoppos - traci.person.getLanePosition(id_person_sumo))
1489            # if d<dist_min:
1490            #    dist_min = d
1491            #    id_person_sumo_inveh = id_person_sumo
1492
1493        if id_person_sumo_inveh is not None:
1494            # print '  found person %s in veh %s'%(id_person_sumo_inveh,id_veh_sumo)
1495
1496            # program vehicle to person's destination
1497            # print '    found person,origs_dests',id_person_sumo_inveh,self.id_person_to_origs_dests[id_person_sumo_inveh]
1498            id_stop_orig, id_stop_dest, id_edge_sumo_from, id_edge_sumo_to = \
1499                self.id_person_to_origs_dests[id_person_sumo_inveh].pop(0)
1500            # print '    found person', id_person_sumo_inveh,'from', id_stop_orig, id_edge_sumo_from,' to' , id_edge_sumo_to, id_stop_dest
1501
1502            stopline = self._get_stopline(id_stop, simtime)
1503            # print '    simtime', simtime
1504
1505            self.parent.prtvehicles.init_trip_occupied(
1506                id_veh, self.ids_stop_to_ids_edge_sumo[id_stop],
1507                stopline,
1508            )
1509
1510            # self.ids_persons_sumo_boarded[id_stop].remove(id_person_sumo_inveh)
1511            self.times_lastboard[id_stop] = simtime
1512            berths.states[id_berth] = BERTHSTATES['free']
1513            berths.ids_veh[id_berth] = -1
1514            # self.ids_vehs_outset[id_stop].add(id_veh)
1515            self.try_set_leadveh(id_stop, id_veh)
1516            self.parent.vehicleman.init_trip_occupied(id_veh, id_stop, id_stop_dest, simtime)
1517            return id_person_sumo_inveh
1518
1519        else:
1520            print 'WARNING: on stop %d edge %s, berth %d no person found inside vehicle prt.%d' % (
1521                id_stop, self.ids_stop_to_ids_edge_sumo[id_stop], id_berth, id_veh)
1522            return None
1523
1524    def _get_stopline(self, id_stop, simtime):
1525        if self.types[id_stop] == STOPTYPES['group']:
1526            # print '    platooning...',id_stop,simtime
1527            if self.times_plat_accumulate[id_stop] < 0:  # len(self.ids_vehs_prog[id_stop]) == 0:
1528                # print '      first in platoon-> stop it at exit-line',simtime
1529                #stopline = self.stoplines[id_stop]
1530                # actually not clear who will arrive first at the stopline
1531                # therefore do not stop. Stop must be set duruing rescheduling
1532                stopline = None
1533                self.times_plat_accumulate[id_stop] = simtime
1534                # print '    times_plat_accumulate',self.times_plat_accumulate[id_stop],simtime
1535            else:
1536                # print '      not first, let it approach previous veh.'
1537                stopline = None
1538
1539        else:
1540            # print '    no platooning: all vehicles stop and wait for start'
1541            stopline = self.stoplines[id_stop]
1542        # print '    times_plat_accumulate',self.times_plat_accumulate[id_stop],'simtime',simtime
1543        # print '    stopline',stopline
1544        return stopline
1545
1546    def init_trip_empty(self, id_stop, id_berth, id_veh, simtime, is_ask_vehman=True):
1547        # print 'Stops.init_trip_empty  id_stop, id_berth, id_veh, is_ask_vehman', id_stop, id_berth, 'prt.%d'%id_veh, is_ask_vehman,'simtime',simtime
1548        # TODO: actually a berth method??
1549        berths = self.get_berths()
1550
1551        berths.states[id_berth] = BERTHSTATES['free']
1552        berths.ids_veh[id_berth] = -1
1553
1554        # print '  proceed to stopline',self.stoplines[id_stop]
1555        stopline = self._get_stopline(id_stop, simtime)
1556        self.parent.prtvehicles.init_trip_empty(
1557            id_veh,
1558            self.ids_stop_to_ids_edge_sumo[id_stop],
1559            stopline)
1560
1561        if is_ask_vehman:
1562            self.try_set_leadveh(id_stop, id_veh)
1563            #id_stop_target = self.parent.vehicleman.init_trip_empty(id_veh, id_stop)
1564            self.parent.vehicleman.init_trip_empty(id_veh, id_stop, simtime)
1565
1566            # print 'init_trip_empty for',id_veh,' from',id_stop,'to',id_stop_target,id_edge_sumo_target
1567            #self.parent.prtvehicles.init_trip_empty(id_veh, self.ids_stop_to_ids_edge_sumo[id_stop_target])
1568
1569        # else:
1570        #    self.parent.prtvehicles.init_trip_empty(id_veh, self.ids_stop_to_ids_edge_sumo[id_stop], -1)
1571
1572        # self.ids_vehs_outset[id_stop].add(id_veh)
1573
1574    def foreward_boardzone(self, id_stop,  ids_berth_board, simtime):
1575        # print 'foreward_boardzone',id_stop,ids_berth_board,'simtime',simtime
1576        berths = self.get_berths()
1577        #ids_berth_board = self.ids_berth_board[id_stop][::-1]
1578        # inds_o berths.states[ids_berth_board] != BERTHSTATES['free']
1579        for id_berth, state in zip(ids_berth_board, berths.states[ids_berth_board]):
1580            # print '    id_berth,boarding?,id_veh',id_berth, state== BERTHSTATES['boarding'],berths.ids_veh[id_berth]
1581            if state == BERTHSTATES['boarding']:
1582                self.init_trip_empty(id_stop, id_berth, berths.ids_veh[id_berth], simtime)
1583
1584        self.times_lastboard[id_stop] = 10**4  # reset last board counter
1585
1586    def enter(self, id_stop, id_veh):
1587        print 'enter id_stop, id_veh', id_stop, 'prt.%d' % id_veh
1588
1589        self.parent.prtvehicles.decatenate(id_veh)
1590
1591        self.ids_vehs[id_stop].append(id_veh)
1592
1593        # tell vehman that veh arrived
1594        #self.numbers_veh_arr[id_stop] -= 1
1595        self.parent.vehicleman.conclude_trip(id_veh, id_stop)
1596
1597        self.numbers_veh[id_stop] += 1
1598        id_berth = self.allocate_alight(id_stop)
1599        if id_berth < 0:
1600            print '  allocation failed, command vehicle to slow down and wait for allocation'
1601            self.ids_vehs_toallocate[id_stop].append(id_veh)
1602            self.parent.prtvehicles.control_slow_down(id_veh)
1603        else:
1604            # command vehicle to go to berth for alighting
1605            print '     send entering vehicle id_veh %d to id_berth_alight %d at pos %.2fm' % (
1606                id_veh, id_berth, self.get_berths().stoppositions[id_berth])
1607            self.parent.prtvehicles.control_stop_alight(id_veh, id_stop, id_berth,
1608                                                        id_edge_sumo=self.ids_stop_to_ids_edge_sumo[id_stop],
1609                                                        position=self.get_berths().stoppositions[id_berth],
1610                                                        )
1611            self.ids_vehs_alight_allocated[id_stop].append(id_veh)
1612
1613    def exit(self, id_stop, id_veh):
1614        # print 'exit prt.%d at stop %d'%(id_veh,id_stop)
1615        self.ids_vehs[id_stop].remove(id_veh)
1616        #id_stop_target = self.parent.vehicleman.start_trip(id_veh, id_stop)
1617        #self.parent.prtvehicles.reschedule_trip(id_veh, self.ids_stop_to_ids_edge_sumo[id_stop_target])
1618        #ind_veh = -1
1619        # print '  ids_vehs_prog=\n',self.ids_vehs_prog[id_stop]
1620        i = 0
1621        for time_start, id_veh_prog, id_stop_target, is_prog in self.ids_vehs_prog[id_stop]:
1622            if id_veh_prog == id_veh:
1623                self.ids_vehs_prog[id_stop].pop(i)
1624                break
1625            i = +1
1626
1627        # self.ids_vehs_prog[id_stop].remove(id_veh)
1628
1629        self.numbers_veh[id_stop] -= 1
1630
1631    def allocate_alight(self, id_stop):
1632
1633        # print 'allocate_alight',id_stop
1634        #self.inds_berth_alight_allocated [id_stop] = len(self.ids_berth_alight[id_stop])
1635        ind_berth = self.inds_berth_alight_allocated[id_stop]
1636
1637        if ind_berth == 0:
1638            # no free berth :(
1639            return -1
1640        else:
1641            ind_berth -= 1
1642            self.inds_berth_alight_allocated[id_stop] = ind_berth
1643            return self.ids_berth_alight[id_stop][ind_berth]
1644
1645    def allocate_board(self, id_stop, n_alloc, queues):
1646        """
1647        Return successive berth ID to be allocated for boarding
1648        at given stop ID.
1649        n_veh_forward is the number of vehicles which remain to be
1650        allocated.
1651        """
1652        # print 'allocate_alight',id_stop, n_alloc
1653        #self.inds_berth_alight_allocated [id_stop] = len(self.ids_berth_alight[id_stop])
1654        ind_berth = self.inds_berth_board_allocated[id_stop]
1655
1656        if ind_berth == 0:
1657            # no free berth :(
1658            return -1
1659        else:
1660            if queues is None:
1661
1662                # less or equal allocation positions in board zone
1663                # then vehicles to be allocated
1664                ind_berth -= 1
1665                # print '  allocate in order ind_berth=',ind_berth
1666                self.inds_berth_board_allocated[id_stop] = ind_berth
1667                return self.ids_berth_board[id_stop][ind_berth]
1668            else:
1669                # there are more allocation positions in board zone
1670                # then vehicles to be allocated
1671                ind_berth -= 1
1672                id_berth = self.ids_berth_board[id_stop][ind_berth]
1673                # print '  check queue, start with ind_berth=%d,n_alloc=%d, id_berth=%d, queue=%d, pos=%.2fm'%(ind_berth,n_alloc,id_berth,queues[id_berth],self.get_berths().stoppositions[id_berth]),queues[id_berth] == 0,ind_berth >= n_alloc
1674                while (queues[id_berth] == 0) & (ind_berth >= n_alloc):
1675                    ind_berth -= 1
1676                    id_berth = self.ids_berth_board[id_stop][ind_berth]
1677                    # print '  check queue, start with ind_berth=%d,n_alloc=%d, id_berth=%d, queue=%d, pos=%.2fm'%(ind_berth,n_alloc,id_berth,queues[id_berth],self.get_berths().stoppositions[id_berth]),queues[id_berth] == 0,ind_berth >= n_alloc
1678
1679                self.inds_berth_board_allocated[id_stop] = ind_berth
1680                return id_berth
1681
1682    def get_berthqueues(self, id_stop):
1683        # currently not used
1684        # print 'get_berthqueues',id_stop
1685        # TODO: use stop angle and person angle to detect waiting persons
1686        ids_berth_board = self.ids_berth_board[id_stop]
1687        stoppositions = self.get_berths().stoppositions[ids_berth_board]
1688        counters = np.zeros(len(stoppositions), dtype=np.int32)
1689        # print '  stoppositions',stoppositions
1690        for id_person_sumo in self.waittimes_persons[id_stop].keys():
1691            position = traci.person.getLanePosition(id_person_sumo)
1692            # print '    position',position
1693            dists = np.abs(stoppositions-position)
1694            # print '  dists',dists,np.any(dists<5)
1695            if np.any(dists < 0.8):
1696                ind_berth = np.argmin(dists)
1697                counters[ind_berth] += 1
1698
1699        queues = {}
1700        for id_berth, count in zip(ids_berth_board, counters):
1701            queues[id_berth] = count
1702
1703        # print '  queues=\n',queues
1704        return queues
1705
1706    def make_from_net(self):
1707        """
1708        Make prt stop database from PT stops in network.
1709        """
1710        print 'make_from_net'
1711        self.clear()
1712        net = self.get_scenario().net
1713        ptstops = net.ptstops
1714
1715        ids_ptstop = ptstops.get_ids()
1716        id_mode_prt = self.parent.id_prtmode
1717
1718        #ids_edges = net.lanes.ids_edge[ptstops.ids_lane[ids_ptstop]]
1719        #ids_lanes = net.edges.ids_lanes[ids_edges]
1720        ids_lane = ptstops.ids_lane[ids_ptstop]
1721        #edgelengths = net.edges.lengths
1722
1723        for id_stop, id_lane, position_from, position_to in zip(
1724            ids_ptstop,
1725            # ids_lanes,
1726            ids_lane,
1727            ptstops.positions_from[ids_ptstop],
1728            ptstops.positions_to[ids_ptstop],
1729        ):
1730            # get allowed modes of lane with index 1
1731            modes_allow = net.lanes.ids_modes_allow[id_lane]
1732            # print '  check id_stop, modes_allow, position_from, position_to',id_stop, modes_allow, position_from, position_to
1733            if id_mode_prt in modes_allow:
1734                self.make(id_stop,
1735                          position_from,
1736                          position_to)
1737
1738        self.parent.make_fstar(is_update=True)
1739        self.parent.make_times_stop_to_stop()
1740
1741    def make(self, id_ptstop, position_from, position_to):
1742        """
1743        Initialize a new prt stop and generate berth.
1744        """
1745        id_stop = self.add_row(ids_ptstop=id_ptstop)
1746        ids_berth = self.get_berths().make(id_stop, position_from=position_from,
1747                                           position_to=position_to)
1748        n_berth = len(ids_berth)
1749        n_berth_alight = int(0.5*n_berth)
1750        n_berth_board = n_berth-n_berth_alight
1751        self.ids_berth_alight[id_stop] = ids_berth[0:n_berth_alight]
1752        self.ids_berth_board[id_stop] = ids_berth[n_berth_alight:n_berth]
1753        return id_stop
1754
1755
1756class VehicleAdder(Process):
1757    def __init__(self,  vehicles, logger=None, **kwargs):
1758        print 'VehicleAdder.__init__', vehicles, vehicles.parent.get_ident()
1759        self._init_common('vehicleadder', name='Vehicle adder',
1760                          logger=logger,
1761                          info='Add vehicles to PRT stops of network.',
1762                          )
1763        self._vehicles = vehicles
1764
1765        attrsman = self.set_attrsman(cm.Attrsman(self))
1766
1767        self.n_vehicles = attrsman.add(cm.AttrConf('n_vehicles', kwargs.get('n_vehicles', -1),
1768                                                   groupnames=['options'],
1769                                                   perm='rw',
1770                                                   name='Number of vehicles',
1771                                                   info='Number of PRT vehicles to be added to the network. Use -1 to fill all present PRT stations.',
1772                                                   ))
1773
1774    def do(self):
1775        # print 'VehicleAdder.do'
1776        self._vehicles.add_to_net(n=self.n_vehicles)
1777
1778
1779class PrtVehicles(am.ArrayObjman):
1780
1781    def __init__(self, ident, prtservices, **kwargs):
1782        # print 'PrtVehicles vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
1783        self._init_objman(ident=ident,
1784                          parent=prtservices,
1785                          name='PRT Veh.',
1786                          info='PRT vehicle database. These are shared vehicles.',
1787                          **kwargs)
1788
1789        self._init_attributes()
1790
1791    def _init_attributes(self):
1792        vtypes = self.get_scenario().demand.vtypes
1793        net = self.get_scenario().net
1794
1795        # TODO: add/update vtypes here
1796        self.add_col(SumoIdsConf('Veh', xmltag='id'))
1797
1798        if not hasattr(self, 'ids_vtype'):
1799            id_vtype = self.make_vtype()
1800
1801            self.add_col(am.IdsArrayConf('ids_vtype', vtypes,
1802                                         id_default=id_vtype,
1803                                         groupnames=['state'],
1804                                         name='Veh. type',
1805                                         info='PRT vehicle type.',
1806                                         #xmltag = 'type',
1807                                         ))
1808
1809        self.add_col(am.ArrayConf('states', default=VEHICLESTATES['init'],
1810                                  dtype=np.int32,
1811                                  choices=VEHICLESTATES,
1812                                  name='state',
1813                                  info='State of vehicle.',
1814                                  ))
1815
1816        # self.add_col(am.IdsArrayConf( 'ids_targetprtstop', self.parent.prtstops,
1817        #                            groupnames = ['parameters'],
1818        #                            name = 'Target stop ID',
1819        #                            info = 'ID of current target PRT stop.',
1820        #                            ))
1821
1822        self.add_col(am.IdsArrayConf('ids_currentedge', net.edges,
1823                                     groupnames=['state'],
1824                                     name='Current edge ID',
1825                                     info='Edge ID of most recent reported position.',
1826                                     ))
1827
1828        # self.add_col(am.IdsArrayConf( 'ids_targetedge', net.edges,
1829        #                            groupnames = ['state'],
1830        #                            name = 'Target edge ID',
1831        #                            info = 'Target edge ID to be reached. This can be either intermediate target edges (), such as a compressor station.',
1832        #                            ))
1833
1834    def get_net(self):
1835        return self.parent.get_scenario().net
1836
1837    def make_vtype(self):
1838        id_vtype = self.get_scenario().demand.vtypes.add_vtype('PRT',
1839                                                               accel=2.5,
1840                                                               decel=2.5,
1841                                                               sigma=1.0,
1842                                                               length=3.5,
1843                                                               width=1.6,
1844                                                               height=1.7,
1845                                                               number_persons=1,
1846                                                               capacity_persons=1,
1847                                                               dist_min=0.5,
1848                                                               tau=0.8,
1849                                                               speed_max=10.0,
1850                                                               deviation_speed=0.01,  # slight deviation for better following
1851                                                               emissionclass='HBEFA3/zero',
1852                                                               id_mode=self.parent.id_prtmode,  # specifies mode for demand
1853                                                               color=np.array((255, 240, 0, 255), np.float32)/255.0,
1854                                                               shape_gui='evehicle',
1855                                                               times_boarding=1.5,
1856                                                               times_loading=20.0,
1857                                                               sublane_alignment_lat='center',
1858                                                               sublane_speed_max_lat=0.5,
1859                                                               sublane_gap_min_lat=0.24,
1860                                                               sublane_alignment_eager=1000000.0,
1861                                                               )
1862        return id_vtype
1863
1864    def concatenate(self, id_veh, id_veh_pre):
1865        # print 'concatenate prt.%d'%id_veh, 'prt.%d'%id_veh_pre
1866        id_veh_sumo = self.get_id_sumo(id_veh)
1867        traci.vehicle.setSpeedFactor(id_veh_sumo, 2.0)  # +random.uniform(-0.01,0.01)
1868        traci.vehicle.setImperfection(id_veh_sumo, 0.0)
1869        traci.vehicle.setAccel(id_veh_sumo, 3.5)
1870        traci.vehicle.setMinGap(id_veh_sumo, 0.01)
1871        traci.vehicle.setTau(id_veh_sumo, 0.2)
1872
1873    def decatenate(self, id_veh):
1874        # print 'decatenate prt.%d'%id_veh
1875        id_veh_sumo = self.get_id_sumo(id_veh)
1876        traci.vehicle.setSpeedFactor(id_veh_sumo, 1.0)  # +random.uniform(-0.01,0.01)
1877        traci.vehicle.setMinGap(id_veh_sumo, 0.5)
1878        traci.vehicle.setImperfection(id_veh_sumo, 1.0)
1879        traci.vehicle.setTau(id_veh_sumo, 0.8)
1880        traci.vehicle.setAccel(id_veh_sumo, 2.5)
1881
1882    def prepare_sim(self, process):
1883        print 'PrtVehicles.prepare_sim'
1884        net = self.get_scenario().net
1885        ptstops = net.ptstops
1886        lanes = net.lanes
1887        ids_edge_sumo = net.edges.ids_sumo
1888        ids = self.get_ids()
1889
1890        self.ids_berth = -1*np.ones(np.max(ids)+1, dtype=np.int32)
1891
1892        # for id_veh, id_veh_sumo in zip(ids,self.ids_sumo[ids]):
1893        #    traci.vehicle.subscribe(id_veh_sumo,
1894        #                            [   traci.constants.VAR_ROAD_ID,
1895        #                                traci.constants.VAR_POSITION,
1896        #                                traci.constants.VAR_STOPSTATE,
1897        #                                ])
1898        return []  # currentlu vehicles are not updated
1899
1900    def process_step(self, process):
1901        # print 'process_step',traci.vehicle.getSubscriptionResults()
1902        pass
1903        # VEHICLESTATES = {'init':0,'waiting':1,'boarding':2,'alighting':3,'emptytrip':4,'occupiedtrip':5,'forewarding':6}
1904
1905    def control_slow_down(self, id_veh, speed=1.0, time_slowdown=5):
1906        # print 'control_slow_down',self.get_id_sumo(id_veh)
1907        traci.vehicle.slowDown(self.get_id_sumo(id_veh), speed, time_slowdown)
1908
1909    def control_stop_alight(self, id_veh, id_stop, id_berth,
1910                            id_edge_sumo=None,
1911                            position=None,
1912                            ):
1913        id_veh_sumo = self.get_id_sumo(id_veh)
1914        p = traci.vehicle.getLanePosition(id_veh_sumo)
1915        # print 'control_stop_alight',id_veh_sumo,p,'->',position,'id_berth',id_berth
1916        #d = position - p
1917        #v = traci.vehicle.getSpeed(id_veh_sumo)
1918        #d_save = 1.0/(2*2.5)*(v**2)
1919        # print '  v=',v
1920        # print '  d,d_save',d,d_save
1921        self.states[id_veh] = VEHICLESTATES['forewarding']
1922        self.ids_berth[id_veh] = id_berth
1923        traci.vehicle.setStop(self.get_id_sumo(id_veh),
1924                              id_edge_sumo,
1925                              pos=position,
1926                              flags=0,
1927                              laneIndex=1,
1928                              )
1929
1930    def control_stop_board(self, id_veh, id_stop, id_berth,
1931                           id_edge_sumo=None,
1932                           position=None,
1933                           ):
1934
1935        # print 'control_stop_board',self.get_id_sumo(id_veh),id_stop, id_berth,id_edge_sumo,position
1936
1937        id_veh_sumo = self.get_id_sumo(id_veh)
1938        # print 'control_stop_board',id_veh_sumo,traci.vehicle.getLanePosition(id_veh_sumo),'->',position,id_berth
1939        self.ids_berth[id_veh] = id_berth
1940        self.states[id_veh] = VEHICLESTATES['forewarding']
1941        # print '  StopState=',bin(traci.vehicle.getStopState(id_veh_sumo ))[2:]
1942        if traci.vehicle.isStopped(id_veh_sumo):
1943            traci.vehicle.resume(id_veh_sumo)
1944
1945        traci.vehicle.setStop(id_veh_sumo,
1946                              id_edge_sumo,
1947                              startPos=position-4.0,
1948                              pos=position,
1949                              flags=2,  # park and trigger 1+2,#
1950                              laneIndex=1,
1951                              )
1952
1953    def alight(self, id_veh):
1954        # print 'alight',self.get_id_sumo(id_veh)
1955        # TODO: necessary to keep copy of state?
1956        self.states[id_veh] = VEHICLESTATES['alighting']
1957        # traci.vehicle.getStopState(self.get_id_sumo(id_veh))
1958        # VEHICLESTATES = {'init':0,'waiting':1,'boarding':2,'alighting'
1959
1960    def board(self, id_veh, id_edge_sumo=None, position=None):
1961        # print 'board',self.get_id_sumo(id_veh)
1962        # TODO: necessary to keep copy of state?
1963        self.states[id_veh] = VEHICLESTATES['boarding']
1964        #id_veh_sumo = self.get_id_sumo(id_veh)
1965        # print 'board',id_veh_sumo,'stopstate',bin(traci.vehicle.getStopState(id_veh_sumo ))[2:]
1966        # print '  ',dir(traci.vehicle)
1967        # traci.vehicle.getLastStepPersonIDs()
1968        # traci.vehicle.getStopState(self.get_id_sumo(id_veh))
1969        # VEHICLESTATES = {'init':0,'waiting':1,'boarding':2,'alighting'
1970        #traci.vehicle.setRoute(id_veh_sumo, [id_edge_sumo])
1971        # traci.vehicle.resume(id_veh_sumo)
1972
1973        # traci.vehicle.setStop(  self.get_id_sumo(id_veh),
1974        #                        traci.vehicle.getRoadID(id_veh_sumo),
1975        #                        pos = traci.vehicle.getLanePosition(id_veh_sumo),
1976        #                        flags= 2,#
1977        #                        laneIndex= 1,
1978        #                        )
1979        # print 'board ',id_veh_sumo, traci.vehicle.getStopState(id_veh_sumo )# bin(traci.vehicle.getStopState(id_veh_sumo ))[2:]
1980
1981    def set_stop(self, id_veh, id_edge_sumo, stopline):
1982        # print 'set_stop',self.get_id_sumo(id_veh),stopline
1983        traci.vehicle.setStop(self.get_id_sumo(id_veh),
1984                              id_edge_sumo,
1985                              pos=stopline,
1986                              laneIndex=1,
1987                              )
1988
1989    def is_completed_alighting(self, id_veh):
1990        # print 'is_completed_alighting',self.get_id_sumo(id_veh),self.states[id_veh],self.states[id_veh] == VEHICLESTATES['alighting'],traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)),type(traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)))
1991        if self.states[id_veh] == VEHICLESTATES['alighting']:
1992            if traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)) == 0:
1993                # print '  id_veh_sumo',self.get_id_sumo(id_veh),'completed alighting'
1994                self.states[id_veh] = VEHICLESTATES['await_forwarding']
1995                return True
1996            else:
1997                return False
1998        elif self.states[id_veh] == VEHICLESTATES['await_forwarding']:
1999            return True
2000        else:
2001            print 'WARNING: strange vehicle state %s while alighting prt.%d' % (self.states[id_veh], id_veh)
2002            return True
2003
2004    def is_completed_boarding(self, id_veh):
2005        # print 'is_completed_boarding',self.get_id_sumo(id_veh),self.states[id_veh],self.states[id_veh] == VEHICLESTATES['boarding'],traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)),type(traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)))
2006        if self.states[id_veh] == VEHICLESTATES['boarding']:
2007            if traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)) == 1:
2008                # print 'is_completed_boarding',self.get_id_sumo(id_veh),'completed boarding'
2009                self.states[id_veh] = VEHICLESTATES['waiting']
2010                return True
2011            else:
2012                False
2013
2014        else:
2015            return True
2016
2017    def get_veh_if_completed_boarding(self, id_veh):
2018        # print 'get_veh_if_completed_boarding',self.get_id_sumo(id_veh),self.states[id_veh],self.states[id_veh] == VEHICLESTATES['boarding'],traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)),type(traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)))
2019
2020        if self.states[id_veh] == VEHICLESTATES['boarding']:
2021            id_veh_sumo = self.get_id_sumo(id_veh)
2022            if traci.vehicle.getPersonNumber(id_veh_sumo) >= 1:
2023                # print 'get_veh_if_completed_boarding',id_veh_sumo,'completed boarding'
2024                self.states[id_veh] = VEHICLESTATES['waiting']
2025                return id_veh_sumo
2026            else:
2027                return ''
2028
2029        else:
2030            return ''
2031
2032    def init_trip_occupied(self, id_veh, id_edge_sumo, stopline=None):
2033        id_veh_sumo = self.get_id_sumo(id_veh)
2034        # print 'init_trip_occupied',self.get_id_sumo(id_veh),id_edge_sumo, stopline
2035        # print '  current route:',traci.vehicle.getRoute(id_veh_sumo)
2036        self.states[id_veh] = VEHICLESTATES['occupiedtrip']
2037
2038        # print '  StopState=',bin(traci.vehicle.getStopState(id_veh_sumo ))[2:]
2039        if traci.vehicle.isStopped(id_veh_sumo):
2040            traci.vehicle.resume(id_veh_sumo)
2041        #traci.vehicle.changeTarget(self.get_id_sumo(id_veh), id_edge_sumo_dest)
2042        #traci.vehicle.changeTarget(self.get_id_sumo(id_veh), id_edge_sumo_dest)
2043        if stopline is not None:
2044            traci.vehicle.setStop(id_veh_sumo,
2045                                  id_edge_sumo,
2046                                  pos=stopline,
2047                                  laneIndex=1,
2048                                  )
2049        else:
2050            speed_crawl = 1.0
2051            time_accel = 4.0
2052            #traci.vehicle.slowDown(id_veh_sumo, speed_crawl, time_accel)
2053
2054    def init_trip_empty(self, id_veh, id_edge_sumo, stopline=None):
2055        print 'Vehicles.init_trip_empty', self.get_id_sumo(id_veh), id_edge_sumo, stopline
2056        self.states[id_veh] = VEHICLESTATES['emptytrip']
2057        id_veh_sumo = self.get_id_sumo(id_veh)
2058        if traci.vehicle.isStopped(id_veh_sumo):
2059            traci.vehicle.resume(id_veh_sumo)
2060        #traci.vehicle.changeTarget(id_veh_sumo, id_edge_sumo_to)
2061        if stopline is not None:
2062            # if stopline>=0:
2063            # print '  Route=',traci.vehicle.getRoute(id_veh_sumo)
2064            print '  Position=', traci.vehicle.getLanePosition(id_veh_sumo), stopline
2065            # print '  StopState=',bin(traci.vehicle.getStopState(id_veh_sumo ))[2:]
2066
2067            traci.vehicle.setStop(id_veh_sumo,
2068                                  id_edge_sumo,
2069                                  pos=stopline,
2070                                  laneIndex=1,
2071                                  )
2072        else:
2073            speed_crawl = 1.0
2074            time_accel = 4.0
2075            #traci.vehicle.slowDown(id_veh_sumo, speed_crawl, time_accel)
2076
2077    def reschedule_trip(self, id_veh, id_edge_sumo_to=None, route_sumo=None):
2078        # print 'reschedule_trip',self.get_id_sumo(id_veh),id_edge_sumo_to,route_sumo
2079        id_veh_sumo = self.get_id_sumo(id_veh)
2080        if traci.vehicle.isStopped(id_veh_sumo):
2081            traci.vehicle.resume(id_veh_sumo)
2082
2083        if route_sumo is not None:
2084            # set entire route
2085            traci.vehicle.setRoute(id_veh_sumo, route_sumo)
2086        else:
2087            # set new target and let SUMO do the routing
2088            traci.vehicle.changeTarget(id_veh_sumo, id_edge_sumo_to)
2089
2090        #
2091        traci.vehicle.slowDown(id_veh_sumo, 13.0, 5.0)
2092
2093    def add_to_net(self, n=-1, length_veh_av=4.0):
2094        """
2095        Add n PRT vehicles to network
2096        If n = -1 then fill up stops with vehicles.
2097        """
2098        # print 'PrtVehicles.make',n,length_veh_av
2099        # self.clear()
2100        net = self.get_scenario().net
2101        ptstops = net.ptstops
2102        prtstops = self.parent.prtstops
2103        lanes = net.lanes
2104        ids_prtstop = prtstops.get_ids()
2105        ids_ptstop = prtstops.ids_ptstop[ids_prtstop]
2106        ids_veh = []
2107        n_stop = len(prtstops)
2108
2109        for id_prt, id_edge, pos_from, pos_to in zip(
2110            ids_prtstop,
2111            lanes.ids_edge[ptstops.ids_lane[ids_ptstop]],
2112            ptstops.positions_from[ids_ptstop],
2113            ptstops.positions_to[ids_ptstop],
2114        ):
2115            # TODO: here we can select depos or distribute a
2116            # fixed number of vehicles or put them into berth
2117            # print '  ',pos_to,pos_from,int((pos_to-pos_from)/length_veh_av)
2118            if n > 0:
2119                n_veh_per_stop = int(float(n)/n_stop+0.5)
2120            else:
2121                n_veh_per_stop = int((pos_to-pos_from)/length_veh_av)
2122            # print '  n,n_stop,n_veh_per_stop',n,n_stop,n_veh_per_stop
2123            for i in range(n_veh_per_stop):
2124                id_veh = self.add_row(ids_stop_target=id_prt,
2125                                      ids_currentedge=id_edge,
2126                                      )
2127
2128                self.ids_sumo[id_veh] = self.get_id_sumo(id_veh)
2129                ids_veh.append(id_veh)
2130
2131        return ids_veh
2132
2133    # def write_veh
2134    #
2135
2136    def get_scenario(self):
2137        return self.parent.get_scenario()
2138
2139    def get_vtypes(self):
2140        """
2141        Returns a set with all used PRT vehicle types.
2142        """
2143        # print 'Vehicles_individual.get_vtypes',self.cols.vtype
2144        return set(self.ids_vtype.get_value())
2145
2146    def get_id_sumo(self, id_veh):
2147        return 'prt.%s' % (id_veh)
2148
2149    def get_id_from_id_sumo(self, id_veh_sumo):
2150        if len(id_veh_sumo.split('.')) == 2:
2151            prefix, id_veh = id_veh_sumo.split('.')
2152            if prefix == 'prt':
2153                return int(id_veh)
2154            else:
2155                return -1
2156        return -1
2157
2158    def get_ids_from_ids_sumo(self, ids_veh_sumo):
2159        n = len(ids_veh_sumo)
2160        ids = np.zeros(n, np.int32)
2161        for i in xrange(n):
2162            ids[i] = self.get_id_from_id_sumo(ids_veh_sumo[i])
2163        return ids
2164
2165    def get_id_line_xml(self):
2166        return 'prt'
2167
2168
2169class PrtStrategy(StrategyMixin):
2170    def __init__(self, ident, parent=None,
2171                 name='Personal Rapid Transit Strategy',
2172                 info='With this strategy, the person uses Personla Rapid Transit as main transport mode.',
2173                 **kwargs):
2174
2175        self._init_objman(ident, parent, name=name, info=info, **kwargs)
2176        attrsman = self.set_attrsman(cm.Attrsman(self))
2177        # specific init
2178        self._init_attributes()
2179        self._init_constants()
2180
2181    def _init_attributes(self):
2182        # print 'StrategyMixin._init_attributes'
2183        pass
2184
2185    def _init_constants(self):
2186        #virtualpop = self.get_virtualpop()
2187        #stagetables = virtualpop.get_stagetables()
2188
2189        #self._walkstages = stagetables.get_stagetable('walks')
2190        #self._ridestages = stagetables.get_stagetable('rides')
2191        #self._activitystages = stagetables.get_stagetable('activities')
2192
2193        #self._plans = virtualpop.get_plans()
2194        #
2195        # print 'AutoStrategy._init_constants'
2196        # print dir(self)
2197        # self.get_attrsman().do_not_save_attrs(['_activitystages','_ridestages','_walkstages','_plans'])
2198
2199        modes = self.get_virtualpop().get_scenario().net.modes
2200        self._id_mode_bike = modes.get_id_mode('bicycle')
2201        self._id_mode_auto = modes.get_id_mode('passenger')
2202        self._id_mode_moto = modes.get_id_mode('motorcycle')
2203        self._id_mode_bus = modes.get_id_mode('bus')
2204        self.get_attrsman().do_not_save_attrs([
2205            '_id_mode_bike', '_id_mode_auto', '_id_mode_moto', '_id_mode_bus'
2206        ])
2207
2208    def set_prtservice(self, prtservice):
2209        #self.add( cm.ObjConf( prtservice, is_child = False,groups = ['_private']))
2210        self.prtservice = prtservice
2211        self.get_attrsman().do_not_save_attrs(['prtservice'])
2212
2213    def preevaluate(self, ids_person):
2214        """
2215        Preevaluation strategies for person IDs in vector ids_person.
2216
2217        Returns a preevaluation vector with a preevaluation value
2218        for each person ID. The values of the preevaluation vector are as follows:
2219            -1 : Strategy cannot be applied
2220            0  : Stategy can be applied, but the preferred mode is not used
2221            1  : Stategy can be applied, and preferred mode is part of the strategy
2222            2  : Strategy uses predomunantly preferred mode
2223
2224        """
2225        n_pers = len(ids_person)
2226        persons = self.get_virtualpop()
2227        preeval = np.zeros(n_pers, dtype=np.int32)
2228
2229        # TODO: here we could exclude by age or distance facilities-stops
2230
2231        # put 0 for persons whose preference is not public transport
2232        preeval[persons.ids_mode_preferred[ids_person] != self.prtservice.id_prtmode] = 0
2233
2234        # put 2 for persons with car access and who prefer cars
2235        preeval[persons.ids_mode_preferred[ids_person] == self.prtservice.id_prtmode] = 2
2236
2237        print '  PrtStrategy.preevaluate', len(np.flatnonzero(preeval))
2238        return preeval
2239
2240    def plan(self, ids_person, logger=None):
2241        """
2242        Generates a plan for these person according to this strategie.
2243        Overriden by specific strategy.
2244        """
2245        print 'PrtStrategy.pan', len(ids_person)
2246        #make_plans_private(self, ids_person = None, mode = 'passenger')
2247        # routing necessary?
2248        virtualpop = self.get_virtualpop()
2249        scenario = virtualpop.get_scenario()
2250        plans = virtualpop.get_plans()  # self._plans
2251        demand = scenario.demand
2252        #ptlines = demand.ptlines
2253
2254        walkstages = plans.get_stagetable('walks')
2255        prtstages = plans.get_stagetable('prttransits')
2256        activitystages = plans.get_stagetable('activities')
2257
2258        activities = virtualpop.get_activities()
2259        activitytypes = demand.activitytypes
2260        prtstops = self.prtservice.prtstops
2261
2262        net = scenario.net
2263        edges = net.edges
2264        lanes = net.lanes
2265        modes = net.modes
2266        landuse = scenario.landuse
2267        facilities = landuse.facilities
2268
2269        times_est_plan = plans.times_est
2270        # here we can determine edge weights for different modes
2271
2272        # this could be centralized to avoid redundance
2273        plans.prepare_stagetables(['walks', 'prttransits', 'activities'])
2274        # must be after preparation:
2275        times_stop_to_stop = self.prtservice.times_stop_to_stop
2276
2277        ids_person_act, ids_act_from, ids_act_to\
2278            = virtualpop.get_activities_from_pattern(0, ids_person=ids_person)
2279
2280        if len(ids_person_act) == 0:
2281            print 'WARNING in TrasitStrategy.plan: no eligible persons found.'
2282            return False
2283
2284        # temporary maps from ids_person to other parameters
2285        nm = np.max(ids_person_act)+1
2286        map_ids_plan = np.zeros(nm, dtype=np.int32)
2287        #ids_plan_act = virtualpop.add_plans(ids_person_act, id_strategy = self.get_id_strategy())
2288        map_ids_plan[ids_person_act] = virtualpop.add_plans(ids_person_act, id_strategy=self.get_id_strategy())
2289
2290        map_times = np.zeros(nm, dtype=np.int32)
2291        map_times[ids_person_act] = activities.get_times_end(ids_act_from, pdf='unit')
2292
2293        # set start time to plans (important!)
2294        plans.times_begin[map_ids_plan[ids_person_act]] = map_times[ids_person_act]
2295
2296        map_ids_fac_from = np.zeros(nm, dtype=np.int32)
2297        map_ids_fac_from[ids_person_act] = activities.ids_facility[ids_act_from]
2298
2299        n_plans = len(ids_person_act)
2300        print 'TrasitStrategy.plan n_plans=', n_plans
2301
2302        # make initial activity stage
2303        ids_edge_from = facilities.ids_roadedge_closest[map_ids_fac_from[ids_person_act]]
2304        poss_edge_from = facilities.positions_roadedge_closest[map_ids_fac_from[ids_person_act]]
2305        # this is the time when first activity starts
2306        # first activity is normally not simulated
2307
2308        names_acttype_from = activitytypes.names[activities.ids_activitytype[ids_act_from]]
2309        durations_act_from = activities.get_durations(ids_act_from)
2310        times_from = map_times[ids_person_act]-durations_act_from
2311        #times_from = activities.get_times_end(ids_act_from, pdf = 'unit')
2312
2313        for id_plan,\
2314            time,\
2315            id_act_from,\
2316            name_acttype_from,\
2317            duration_act_from,\
2318            id_edge_from,\
2319            pos_edge_from  \
2320            in zip(map_ids_plan[ids_person_act],
2321                   times_from,
2322                   ids_act_from,
2323                   names_acttype_from,
2324                   durations_act_from,
2325                   ids_edge_from,
2326                   poss_edge_from):
2327
2328            id_stage_act, time = activitystages.append_stage(
2329                id_plan, time,
2330                ids_activity=id_act_from,
2331                names_activitytype=name_acttype_from,
2332                durations=duration_act_from,
2333                ids_lane=edges.ids_lanes[id_edge_from][0],
2334                positions=pos_edge_from,
2335            )
2336
2337        ##
2338
2339        ind_act = 0
2340
2341        # main loop while there are persons performing
2342        # an activity at index ind_act
2343        while len(ids_person_act) > 0:
2344            ids_plan = map_ids_plan[ids_person_act]
2345
2346            times_from = map_times[ids_person_act]
2347
2348            names_acttype_to = activitytypes.names[activities.ids_activitytype[ids_act_to]]
2349            durations_act_to = activities.get_durations(ids_act_to)
2350
2351            ids_fac_from = map_ids_fac_from[ids_person_act]
2352            ids_fac_to = activities.ids_facility[ids_act_to]
2353
2354            centroids_from = facilities.centroids[ids_fac_from]
2355            centroids_to = facilities.centroids[ids_fac_to]
2356
2357            # origin edge and position
2358            ids_edge_from = facilities.ids_roadedge_closest[ids_fac_from]
2359            poss_edge_from = facilities.positions_roadedge_closest[ids_fac_from]
2360
2361            # destination edge and position
2362            ids_edge_to = facilities.ids_roadedge_closest[ids_fac_to]
2363            poss_edge_to = facilities.positions_roadedge_closest[ids_fac_to]
2364
2365            # find closest prt stop!!
2366            ids_stop_from, ids_stopedge_from = prtstops.get_closest(centroids_from)
2367            ids_stop_to, ids_stopedge_to = prtstops.get_closest(centroids_to)
2368
2369            poss_stop_from = prtstops.get_waitpositions(ids_stop_from, is_alight=False, offset=-0.5)
2370            poss_stop_to = prtstops.get_waitpositions(ids_stop_to, is_alight=True, offset=-0.5)
2371
2372            i = 0.0
2373            for id_person, id_plan, time_from, id_act_from, id_act_to, name_acttype_to, duration_act_to, id_edge_from, pos_edge_from, id_edge_to, pos_edge_to, id_stop_from, id_stopedge_from, pos_stop_from, id_stop_to, id_stopedge_to, pos_stop_to\
2374                    in zip(ids_person_act, ids_plan, times_from, ids_act_from, ids_act_to, names_acttype_to, durations_act_to,  ids_edge_from, poss_edge_from, ids_edge_to, poss_edge_to, ids_stop_from, ids_stopedge_from, poss_stop_from, ids_stop_to, ids_stopedge_to, poss_stop_to):
2375                n_pers = len(ids_person_act)
2376                if logger:
2377                    logger.progress(i/n_pers*100)
2378                i += 1.0
2379                print 79*'_'
2380                print '  id_plan=%d, id_person=%d, ' % (id_plan, id_person)
2381
2382                id_stage_walk1, time = walkstages.append_stage(id_plan, time_from,
2383                                                               id_edge_from=id_edge_from,
2384                                                               position_edge_from=pos_edge_from,
2385                                                               id_edge_to=id_stopedge_from,
2386                                                               position_edge_to=pos_stop_from,  # -7.0,
2387                                                               )
2388
2389                # take PRT
2390                # self.ids_edge_to_ids_prtstop
2391                id_stage_transit, time = prtstages.append_stage(
2392                    id_plan, time,
2393                    duration=times_stop_to_stop[id_stop_from, id_stop_to],
2394                    id_fromedge=id_stopedge_from,
2395                    id_toedge=id_stopedge_to,
2396                )
2397
2398                # walk from final prtstop to activity
2399                # print '    Stage for linktype %2d fromedge %s toedge %s'%(linktype, edges.ids_sumo[id_stopedge_to],edges.ids_sumo[id_edge_to] )
2400                id_stage_walk2, time = walkstages.append_stage(id_plan, time,
2401                                                               id_edge_from=id_stopedge_to,
2402                                                               position_edge_from=pos_stop_to,
2403                                                               id_edge_to=id_edge_to,
2404                                                               position_edge_to=pos_edge_to,
2405                                                               )
2406
2407                # update time for trips estimation for this plan
2408                plans.times_est[id_plan] += time-time_from
2409
2410                # define current end time without last activity duration
2411                plans.times_end[id_plan] = time
2412
2413                id_stage_act, time = activitystages.append_stage(
2414                    id_plan, time,
2415                    ids_to=id_act_to,
2416                    names_totype=name_acttype_to,
2417                    durations=duration_act_to,
2418                    ids_lane=edges.ids_lanes[id_edge_to][0],
2419                    positions=pos_edge_to,
2420                )
2421
2422                # store time for next iteration in case other activities are
2423                # following
2424                map_times[id_person] = time
2425
2426            # select persons and activities for next setp
2427            ind_act += 1
2428            ids_person_act, ids_act_from, ids_act_to\
2429                = virtualpop.get_activities_from_pattern(ind_act, ids_person=ids_person_act)
2430
2431
2432class PrtTransits(StageTypeMixin):
2433    def __init__(self, ident, stages,
2434                 name='Ride on PRT',
2435                 info='Ride on Personal Rapid Transit network.',
2436                 # **kwargs,
2437                 ):
2438
2439        print 'PrtTransits.__init__', ident, stages
2440        self.init_stagetable(ident,
2441                             stages, name=name,
2442                             info=info,
2443                             )
2444        self._init_attributes()
2445        self._init_constants()
2446
2447    def _init_attributes(self):
2448        edges = self.get_virtualpop().get_net().edges
2449
2450        self.add_col(am.IdsArrayConf('ids_fromedge', edges,
2451                                     groupnames=['parameters'],
2452                                     name='Edge ID from',
2453                                     info='Edge ID of departure PRT station.',
2454                                     ))
2455
2456        self.add_col(am.IdsArrayConf('ids_toedge', edges,
2457                                     groupnames=['parameters'],
2458                                     name='Edge ID to',
2459                                     info='Edge ID of destination PRT station.',
2460                                     ))
2461
2462    def set_prtservice(self, prtservice):
2463        self.add(cm.ObjConf(prtservice, is_child=False, groups=['_private']))
2464
2465    def get_prtservice(self):
2466        return self.prtservice.get_value()
2467
2468    def prepare_planning(self):
2469
2470        prtservice = self.get_prtservice()
2471        print 'prttransits.prepare_planning', prtservice.times_stop_to_stop
2472        if prtservice.times_stop_to_stop is None:
2473            prtservice.make_times_stop_to_stop()
2474        print prtservice.times_stop_to_stop
2475
2476    def append_stage(self, id_plan, time_start=-1.0,
2477                     duration=0.0,
2478                     id_fromedge=-1, id_toedge=-1, **kwargs):
2479        """
2480        Appends a PRT transit stage to plan id_plan.
2481
2482        """
2483        # print 'PrtTransits.append_stage',id_stage
2484
2485        id_stage, time_end = StageTypeMixin.append_stage(self,
2486                                                         id_plan,
2487                                                         time_start,
2488                                                         durations=duration,
2489                                                         ids_fromedge=id_fromedge,
2490                                                         ids_toedge=id_toedge,
2491                                                         )
2492
2493        # add this stage to the vehicle database
2494        # ind_ride gives the index of this ride (within the same plan??)
2495        #ind_ride = self.parent.get_individualvehicles().append_ride(id_veh, id_stage)
2496        return id_stage, time_end
2497
2498    def to_xml(self, id_stage, fd, indent=0):
2499        # <ride from="1/3to0/3" to="0/4to1/4" lines="train0"/>
2500        net = self.get_virtualpop().get_net()
2501        #ids_stoplane = net.ptstops.ids_lane
2502        #ids_laneedge = net.lanes.ids_edge
2503        ids_sumoedge = net.edges.ids_sumo
2504
2505        #ind = self.get_ind(id_stage)
2506        fd.write(xm.start('ride', indent=indent))
2507        fd.write(xm.num('from', ids_sumoedge[self.ids_fromedge[id_stage]]))
2508        fd.write(xm.num('to', ids_sumoedge[self.ids_toedge[id_stage]]))
2509        fd.write(xm.num('lines', 'prt'))
2510        # if self.cols.pos_edge_from[ind]>0:
2511        #    fd.write(xm.num('departPos', self.cols.pos_edge_from[ind]))
2512        # if self.cols.pos_edge_to[ind]>0:
2513        #    fd.write(xm.num('arrivalPos', self.cols.pos_edge_to[ind]))
2514
2515        fd.write(xm.stopit())  # ends stage
2516
2517
2518class VehicleMan(am.ArrayObjman):
2519    def __init__(self, ident, prtservices, **kwargs):
2520        self._init_objman(ident=ident,
2521                          parent=prtservices,
2522                          name='PRT vehicle management',
2523                          info='PRT vehicle management.',
2524                          #xmltag = ('additional','busStop','stopnames'),
2525                          version=0.0,
2526                          **kwargs)
2527
2528        self._init_attributes()
2529        self._init_constants()
2530
2531    def _init_attributes(self):
2532        self.add(cm.AttrConf('time_update', 2.0,
2533                             groupnames=['parameters'],
2534                             name='Man. update time',
2535                             info="Update time for vehicle management.",
2536                             unit='s',
2537                             ))
2538
2539        # self.time_update.set(5.0)
2540
2541        self.add(cm.AttrConf('time_update_flows', 10.0,
2542                             groupnames=['parameters'],
2543                             name='Flow update time',
2544                             info="Update time for flow estimations.",
2545                             unit='s',
2546                             ))
2547        # self.time_update.set(10.0)
2548        # if hasattr(self,'time_flowaverage'):
2549        #    self.delete('time_flowaverage')
2550
2551        self.add(cm.AttrConf('time_est_max', 1200,
2552                             groupnames=['parameters'],
2553                             name='prediction interval',
2554                             info="Prediction time range of vehicle management.",
2555                             unit='s',
2556                             ))
2557
2558        self.add(cm.AttrConf('time_search_occupied', 10.0,
2559                             groupnames=['parameters'],
2560                             name='Occupied search time',
2561                             info="Time interval for search of optimum departure time in case of an occupied vehicle trip.",
2562                             unit='s',
2563                             ))
2564
2565        self.add(cm.AttrConf('time_search_empty', 20.0,
2566                             groupnames=['parameters'],
2567                             name='Empty search time',
2568                             info="Time interval for search of optimum departure time in case of an empty vehicle trip.",
2569                             unit='s',
2570                             ))
2571
2572        self.add(cm.AttrConf('weight_demand', 0.01,
2573                             groupnames=['parameters'],
2574                             name='Demand weight',
2575                             info="Weight of current demand at stations when assigning empty vehicles trips.",
2576                             ))
2577
2578        self.add(cm.AttrConf('weight_flow', 1.0,
2579                             groupnames=['parameters'],
2580                             name='Flow weight',
2581                             info="Weight of flows (changes in demand over time) at stations when assigning empty vehicles trips.",
2582                             ))
2583
2584        self.add(cm.AttrConf('constant_timeweight', 0.005,
2585                             groupnames=['parameters'],
2586                             name='Time weight const.',
2587                             info="Constant for the exponential decay function weighting the time instance in the optimization function.",
2588                             unit='1/s',
2589                             ))
2590
2591    # def set_stops(self,vehicleman):
2592    #    self.add( cm.ObjConf( stops, is_child = False,groups = ['_private']))
2593
2594    def get_stops(self):
2595        return self.parent.prtstops
2596
2597    def get_vehicles(self):
2598        return self.parent.prtvehicles
2599
2600    def get_scenario(self):
2601        return self.parent.parent.get_scenario()
2602
2603    def prepare_sim(self, process):
2604        print 'VehicleMan.prepare_sim'
2605        net = self.get_scenario().net
2606        # station management
2607        # self.ids_stop_to_ids_edge_sumo = np.zeros(np.max(ids)+1,dtype = np.object)
2608
2609        self.ids_stop = self.get_stops().get_ids()
2610        n_stoparray = np.max(self.ids_stop)+1
2611        self.numbers_veh_arr = np.zeros(n_stoparray, dtype=np.int32)
2612
2613        self.n_est_max = self.time_est_max.get_value()/self.time_update_flows.get_value()
2614        self.inflows_sched = np.zeros((n_stoparray, self.n_est_max), dtype=np.int32)
2615        self.inflows_person = np.zeros(n_stoparray, dtype=np.int32)
2616        self.inflows_person_last = np.zeros(n_stoparray, dtype=np.int32)
2617        # vehicle management
2618        self.ids_veh = self.get_vehicles().get_ids()
2619        n_veharray = np.max(self.ids_veh)+1
2620
2621        # could go in vehicle array
2622        self.ids_stop_target = -1*np.ones(n_veharray, dtype=np.int32)
2623        self.ids_stop_current = -1*np.ones(n_veharray, dtype=np.int32)
2624        self.times_order = 10**6*np.ones(n_veharray, dtype=np.int32)
2625        # self.occupiedtrips_new = []#?? put this is also in general time scheme with more urgency??
2626        #self.emptytrips_new = []
2627
2628        # from veh arrays
2629        #self.are_emptytrips = np.zeros(n_veharray, dtype = np.bool)
2630
2631        # logging
2632        results = process.get_results()
2633        if results is not None:
2634            time_update_flows = self.time_update_flows.get_value()
2635            n_timesteps = int((process.duration-process.time_warmup)/time_update_flows+1)
2636            results.prtstopresults.init_recording(n_timesteps, time_update_flows)
2637
2638        self.log_inflows_temp = np.zeros(n_stoparray, dtype=np.int32)
2639
2640        return [(self.time_update.get_value(), self.process_step),
2641                (self.time_update_flows.get_value(), self.update_flows),
2642                ]
2643
2644    def update_flows(self, process):
2645        # print 'update flow prediction'
2646        self.inflows_sched[:, 0] = 0
2647        self.inflows_sched = np.roll(self.inflows_sched, -1)
2648        time_update_flows = self.time_update_flows.get_value()
2649
2650        if process.simtime > process.time_warmup:
2651            stops = self.get_stops()
2652            ids_stop = self.ids_stop
2653            prtstopresults = process.get_results().prtstopresults
2654            const_time = 1.0/time_update_flows  # np.array([1.0/time_update_flows],dtype=np.float32)
2655            timestep = (process.simtime - process.simtime_start - process.time_warmup)/time_update_flows
2656            prtstopresults.record(timestep, ids_stop,
2657                                  inflows_veh=np.array(const_time * self.log_inflows_temp[ids_stop], dtype=np.float32),
2658                                  inflows_veh_sched=np.array(
2659                                      const_time * self.inflows_sched[ids_stop, 0], dtype=np.float32),
2660                                  numbers_person_wait=stops.numbers_person_wait[ids_stop],
2661                                  waittimes_tot=stops.waittimes_tot[ids_stop],
2662                                  inflows_person=np.array(const_time * self.inflows_person[ids_stop], dtype=np.float32),
2663                                  #inflows_person = stops.flows_person[ids_stop],
2664                                  )
2665
2666        self.inflows_person_last = self.inflows_person.copy()
2667        self.inflows_person[:] = 0
2668        self.log_inflows_temp[:] = 0
2669
2670    def process_step(self, process):
2671        print 79*'M'
2672        print 'VehicleMan.process_step'
2673
2674        stops = self.get_stops()
2675        #vehicles = self.get_vehicles()
2676
2677        has_programmed = True
2678        while has_programmed:
2679            has_programmed = False
2680            ids_veh_lead = stops.ids_veh_lead[self.ids_stop]
2681            inds_valid = np.flatnonzero(ids_veh_lead >= 0)
2682            if len(inds_valid) > 0:
2683                has_programmed |= self.push_occupied_leadvehs(ids_veh_lead[inds_valid], process)
2684                has_programmed |= self.push_empty_leadvehs(ids_veh_lead[inds_valid], process)
2685                has_programmed |= self.pull_empty_leadvehs(ids_veh_lead[inds_valid], process)
2686            else:
2687                # no more leaders
2688                has_programmed = False
2689        # print '\n terminated vehicle man n_est_max',self.n_est_max
2690        # print '  inflows_sched=\n',self.inflows_sched
2691
2692    def note_person_entered(self, id_stop, id_person_sumo, id_stop_dest):
2693        # here just estimate person flows
2694        self.inflows_person[id_stop] += 1
2695
2696    def push_occupied_leadvehs(self, ids_veh_lead, process):
2697        n_timeslot_offset = 3
2698        n_searchint = int(self.time_search_occupied.get_value()/self.time_update_flows.get_value()+0.5)
2699        stops = self.get_stops()
2700        vehicles = self.get_vehicles()
2701
2702        inds_valid = np.flatnonzero(vehicles.states[ids_veh_lead] == VEHICLESTATES['occupiedtrip'])
2703        if len(inds_valid) == 0:
2704            return False
2705        # print 'push_occupied_leadvehs ids_veh_lead',ids_veh_lead[inds_valid]
2706        times_stop_to_stop = self.parent.times_stop_to_stop
2707        ids_stop_current = self.ids_stop_current[ids_veh_lead[inds_valid]]
2708        ids_stop_target = self.ids_stop_target[ids_veh_lead[inds_valid]]
2709        durations_est = times_stop_to_stop[ids_stop_current, ids_stop_target]
2710        # print '  duration_est, t_est_max',durations_est,self.n_est_max*self.time_update_flows.get_value()
2711
2712        inds_time_min = n_timeslot_offset+np.array(np.clip(np.array(1.0*durations_est/self.time_update_flows.get_value(
2713        ), dtype=np.int32), 0, self.n_est_max-n_searchint-n_timeslot_offset), dtype=np.int32)
2714        inds_time_max = inds_time_min+n_searchint
2715        # print '  inds_time_min unclipped',n_timeslot_offset+np.array(1.0*durations_est/self.time_update_flows.get_value(),dtype=np.int32)
2716        # print '  inds_time_min   clipped',inds_time_min
2717        # print '  inds_time_max',inds_time_max, self.inflows_sched.shape
2718
2719        for id_veh_lead, state, id_stop_current, id_stop_target, time_order, ind_min, ind_max, duration_est\
2720                in zip(
2721                    ids_veh_lead[inds_valid],
2722                    vehicles.states[ids_veh_lead[inds_valid]],
2723                    self.ids_stop_current[ids_veh_lead[inds_valid]],
2724                    self.ids_stop_target[ids_veh_lead[inds_valid]],
2725                    self.times_order[ids_veh_lead[inds_valid]],
2726                    inds_time_min,
2727                    inds_time_max,
2728                    durations_est,
2729                ):
2730
2731            # print '    check veh',id_veh_lead, state, 'id_stop_current',id_stop_current, 'id_stop_target',id_stop_target
2732
2733            #VEHICLESTATES = {'init':0,'waiting':1,'boarding':2,'alighting':3,'emptytrip':4,'occupiedtrip':5,'forewarding':6}
2734            # if state == VEHICLESTATES['occupiedtrip']:
2735
2736            #ids_stop = list(self.ids_stop)
2737            # ids_stop.remove(id_stop)
2738            #costs = (stops.numbers_person_wait[ids_stop]-stops.numbers_veh[ids_stop]-self.numbers_veh_arr[ids_stop])/self.parent.times_stop_to_stop[id_stop,ids_stop]
2739            #ids_stop [np.argmax(costs)]
2740            inds_time = np.arange(ind_min, ind_max)
2741            costs = self.inflows_sched[id_stop_target, inds_time]
2742            # print '    inds_time',inds_time
2743            # print '    inflows_sched',costs
2744            ind_time_depart = inds_time[np.argmin(costs)]
2745
2746            time_depart = process.simtime + ind_time_depart*self.time_update_flows.get_value()-duration_est
2747            # print '      ind_time_depart, time_arr, duration_est',ind_time_depart,process.simtime + ind_time_depart*self.time_update_flows.get_value(),duration_est
2748            # print '      time_order' ,time_order,'time_depart',time_depart,'delay',time_depart-time_order
2749
2750            stops.program_leadveh(id_stop_current, id_veh_lead, id_stop_target, time_depart)
2751            self.numbers_veh_arr[id_stop_target] += 1
2752            self.inflows_sched[id_stop_target, ind_time_depart] += 1
2753
2754        return True  # at least one vehicle assigned
2755
2756    def push_empty_leadvehs(self, ids_veh_lead, process):
2757        n_timeslot_offset = 3
2758        stops = self.get_stops()
2759        vehicles = self.get_vehicles()
2760        inds_valid = np.flatnonzero(vehicles.states[ids_veh_lead] == VEHICLESTATES['emptytrip'])
2761        if len(inds_valid) == 0:
2762            return False
2763
2764        # print 'push_empty_leadvehs ids_veh_lead',ids_veh_lead[inds_valid]
2765        times_stop_to_stop = self.parent.times_stop_to_stop
2766        ids_stop_current = self.ids_stop_current[ids_veh_lead[inds_valid]]
2767
2768        n_stop_target = len(self.ids_stop)
2769        ids_stop_target = self.ids_stop.reshape(n_stop_target, 1)
2770        flow_person_est = (stops.flows_person[ids_stop_target] *
2771                           self.time_update_flows.get_value()).reshape(n_stop_target, 1)
2772        n_searchint = int(self.time_search_empty.get_value()/self.time_update_flows.get_value()+0.5)
2773        inds_search_base = n_timeslot_offset + \
2774            np.arange(n_searchint, dtype=np.int32) * np.ones((n_stop_target, n_searchint), dtype=np.int32)
2775
2776        #self.numbers_veh = np.zeros(np.max(ids)+1, dtype = np.int32)
2777        #self.numbers_person_wait = np.zeros(np.max(ids)+1, dtype = np.int32)
2778        #self.flows_person = np.zeros(np.max(ids)+1, dtype = np.float32)
2779        #self.ids_veh_lead = -1*np.ones(np.max(ids)+1,dtype = np.int32)
2780        #self.ids_vehs_prog = np.zeros(np.max(ids)+1,dtype = np.object)
2781        is_started = False
2782        for id_veh_lead, id_stop_current, time_order, \
2783                in zip(
2784                    ids_veh_lead[inds_valid],
2785                    self.ids_stop_current[ids_veh_lead[inds_valid]],
2786                    self.times_order[ids_veh_lead[inds_valid]],
2787                ):
2788
2789            #id_stop_target = self.get_stop_emptytrip(id_stop_current)
2790
2791            durations_est = times_stop_to_stop[id_stop_current, ids_stop_target]
2792            ind_stop_current = np.flatnonzero(durations_est == 0)[0]
2793            # print '    check veh',id_veh_lead, 'id_stop_current',id_stop_current,'ind_stop_current',ind_stop_current
2794            inds_time_min = np.array(np.clip(np.array(1.0*durations_est/self.time_update_flows.get_value(),
2795                                                      dtype=np.int32), 0, self.n_est_max-n_searchint), dtype=np.int32)
2796            inds_search = inds_search_base + inds_time_min.reshape(n_stop_target, 1)
2797            timeweight = np.exp(-self.constant_timeweight.get_value() * inds_search*self.time_update_flows.get_value())
2798
2799            # print '  demand',stops.numbers_person_wait[ids_stop_target]
2800            if 0:
2801                print '  waittimes_tot', stops.waittimes_tot[ids_stop_target]
2802                print '  numbers_person_wait', stops.numbers_person_wait[ids_stop_target]
2803                print '  flow_person_est', flow_person_est
2804                print '  inflows_sched', self.inflows_sched[ids_stop_target, inds_search]
2805                print '  delta flow', (flow_person_est-self.inflows_sched[ids_stop_target, inds_search])
2806                print '  demandcomp', self.weight_demand.get_value() * stops.numbers_person_wait[ids_stop_target]
2807                print '  flowcomp', self.weight_flow.get_value(
2808                ) * (flow_person_est-self.inflows_sched[ids_stop_target, inds_search])
2809
2810                print '  flow_person_est', flow_person_est
2811
2812            # costs = (   self.weight_demand.get_value() * stops.waittimes_tot[ids_stop_target]\
2813            #            + self.weight_flow.get_value() * (flow_person_est-self.inflows_sched[ids_stop_target, inds_search])\
2814            #         )* timeweight
2815            costs = (self.weight_demand.get_value() * (stops.numbers_person_wait[ids_stop_target])
2816                     + self.weight_flow.get_value() * (flow_person_est-self.inflows_sched[ids_stop_target, inds_search])
2817                     ) * timeweight
2818
2819            #costs = np.exp(+self.inflows_sched[ids_stop_target, inds_search]-flow_person_est)*timeweight
2820            #costs = (self.inflows_sched[ids_stop_target, inds_search]-flow_person_est)*timeweight
2821            costs[ind_stop_current, :] = -999999
2822            if 0:
2823                # print '    flow_person_est',flow_person_#est
2824                print '    timeweight', timeweight
2825                print '    durations_est', durations_est
2826                print '    inds_search_base', inds_search_base
2827                print '    inds_search unclipped\n', inds_search_base + \
2828                    np.array(1.0*durations_est/self.time_update_flows.get_value(),
2829                             dtype=np.int32).reshape(n_stop_target, 1)
2830                print '    inds_search clipped  \n', inds_search
2831                print '    inds_time_min', inds_time_min
2832                print '    ind_stop_current', ind_stop_current, durations_est[ind_stop_current]
2833                print '    costs=\n', costs
2834            # constant_timeweight
2835
2836            #
2837            ind_target = np.argmax(costs)
2838            ind_stop_target = ind_target/n_searchint
2839            #ind_time_arrive = ind_target%n_searchint+inds_time_min[ind_stop_target]
2840            ind_time_delta = ind_target % n_searchint
2841            ind_time_arrive = inds_search[ind_stop_target, ind_time_delta]
2842            if 0:
2843                print '    ind_target,n_searchint,ind_stop_target,ind_time_delta', ind_target, n_searchint, ind_target/n_searchint, ind_time_delta
2844                print '    ind_delta_depart,c_min', costs[ind_stop_target, ind_time_delta]
2845                print '    inds_time_min,ind_time_arrive', inds_time_min[ind_stop_target], ind_time_arrive
2846
2847            id_stop_target = ids_stop_target[ind_stop_target][0]
2848            time_depart = process.simtime + ind_time_arrive * \
2849                self.time_update_flows.get_value()-durations_est[ind_stop_target]
2850            # print '\n     id_stop_target',id_stop_target
2851            # print '     time_order' ,time_order,'time_depart',time_depart,'delay',time_depart-time_order
2852
2853            stops.program_leadveh(id_stop_current, id_veh_lead, id_stop_target, time_depart)
2854            is_started = True
2855
2856            self.numbers_veh_arr[id_stop_target] += 1
2857            self.inflows_sched[id_stop_target, ind_time_arrive] += 1
2858
2859        return is_started
2860
2861    def pull_empty_leadvehs(self, ids_veh_lead, process):
2862
2863        n_timeslot_offset = 2
2864
2865        #self.numbers_veh = np.zeros(np.max(ids)+1, dtype = np.int32)
2866        #self.numbers_person_wait = np.zeros(np.max(ids)+1, dtype = np.int32)
2867
2868        # inds_valid = np.flatnonzero(    (vehicles.states[ids_veh_lead] == VEHICLESTATES['waiting'])\
2869        #                                & (stops.numbers_person_wait[self.ids_stop_current[ids_veh_lead]] == 0))
2870        # print 'pull_empty_leadvehs'
2871        stops = self.get_stops()
2872        vehicles = self.get_vehicles()
2873        times_stop_to_stop = self.parent.times_stop_to_stop
2874
2875        # get potential pull vehicles
2876        inds_valid = np.flatnonzero((vehicles.states[ids_veh_lead] == VEHICLESTATES['boarding'])
2877                                    & (stops.numbers_person_wait[self.ids_stop_current[ids_veh_lead]] == 0))
2878
2879        # print '  states',vehicles.states[ids_veh_lead], VEHICLESTATES['boarding']
2880        # print '  numbers_person_wait',stops.numbers_person_wait[self.ids_stop_current[ids_veh_lead]]
2881        # print '  &',(vehicles.states[ids_veh_lead] == VEHICLESTATES['boarding'])\
2882        #                               & (stops.numbers_person_wait[self.ids_stop_current[ids_veh_lead]] == 0)
2883        # print '   origins inds_valid',inds_valid
2884        if len(inds_valid) == 0:
2885            # print '    all stops busy'
2886            return False
2887
2888        ids_veh_lead = ids_veh_lead[inds_valid]
2889        ids_stop_current = self.ids_stop_current[ids_veh_lead]
2890
2891        # print '  available for pulling ids_veh_lead',ids_veh_lead
2892        # print '    ids_stop_current',ids_stop_current
2893        # get potential target station with demand
2894        demands = stops.numbers_person_wait[self.ids_stop]\
2895            - (0.5*stops.numbers_veh[self.ids_stop]
2896               + self.numbers_veh_arr[self.ids_stop])
2897        inds_valid = np.flatnonzero(demands > 0)
2898        # print '  targets inds_valid',inds_valid
2899        if len(inds_valid) == 0:
2900            # print '  no demand'
2901            return False
2902
2903        ids_stop_target = self.ids_stop[inds_valid]
2904        demands = demands[inds_valid]
2905        # print '  ids_stop_current',ids_stop_current
2906        # print '  ids_stop_target',ids_stop_target
2907        # print '  demands',demands
2908        # calculate cost matrix with id_stop_current in rows and id_stop_target
2909
2910        #n_origin = len(ids_stop_current)
2911        #n_targets = len(ids_stop_target)
2912        times = times_stop_to_stop[ids_stop_current, :][:, ids_stop_target]
2913        times[times == 0] = 99999
2914        timeweight = np.exp(-self.constant_timeweight.get_value() * times)
2915        #costs = times_stop_to_stop[ids_stop_current,:][:,ids_stop_target]
2916        #costs[costs == 0] = 99999
2917        # print '  timeweight\n',timeweight
2918
2919        costs = timeweight * demands
2920        #costs = np.zeros(costs.size, np.float32)-demands
2921
2922        inds_pull = np.argmax(costs, 1)
2923        # print '  costs\n',costs
2924        # print '  ->inds_pull,ids_stop_target',inds_pull,ids_stop_target[inds_pull]
2925
2926        durations_est = times_stop_to_stop[ids_stop_current, ids_stop_target[inds_pull]]
2927        # print '  duration_est, t_est_max',durations_est,self.n_est_max*self.time_update_flows.get_value()
2928
2929        inds_time_min = n_timeslot_offset+np.array(np.clip(np.array(1.0*durations_est/self.time_update_flows.get_value(
2930        ), dtype=np.int32), 0, self.n_est_max-1-n_timeslot_offset), dtype=np.int32)
2931        inds_time_max = inds_time_min+2
2932        # print '  inds_time_min',inds_time_min
2933        # print '  inds_time_max',inds_time_max
2934
2935        is_started = False
2936        for id_veh_lead, state, id_stop_current, id_stop_target, time_order, ind_min, ind_max, duration_est\
2937                in zip(
2938                    ids_veh_lead,
2939                    vehicles.states[ids_veh_lead],
2940                    ids_stop_current,
2941                    ids_stop_target[inds_pull],
2942                    self.times_order[ids_veh_lead],
2943                    inds_time_min,
2944                    inds_time_max,
2945                    durations_est,
2946                ):
2947
2948            # print '    check veh prt.%d'%(id_veh_lead), state, 'id_stop_current',id_stop_current, 'id_stop_target',id_stop_target
2949
2950            #VEHICLESTATES = {'init':0,'waiting':1,'boarding':2,'alighting':3,'emptytrip':4,'occupiedtrip':5,'forewarding':6}
2951            # if state == VEHICLESTATES['occupiedtrip']:
2952
2953            #ids_stop = list(self.ids_stop)
2954            # ids_stop.remove(id_stop)
2955            #costs = (stops.numbers_person_wait[ids_stop]-stops.numbers_veh[ids_stop]-self.numbers_veh_arr[ids_stop])/self.parent.times_stop_to_stop[id_stop,ids_stop]
2956            #ids_stop [np.argmax(costs)]
2957            inds_time = np.arange(ind_min, ind_max)
2958            costs = self.inflows_sched[id_stop_current, inds_time]
2959            ind_time_depart = inds_time[np.argmin(costs)]
2960
2961            self.inflows_sched[id_stop_target, ind_time_depart] += 1
2962            time_depart = process.simtime + ind_time_depart*self.time_update_flows.get_value()-duration_est
2963
2964            # this is a workarouned that vehicle does first reach the stopline
2965            # before it gets rescheduled...try with stoplinecheck
2966
2967            # if time_depart < process.simtime+15:
2968            #    time_depart = process.simtime+15
2969            # print '      ind_time_depart, time_arr, duration_est',ind_time_depart,process.simtime + ind_time_depart*self.time_update_flows.get_value(),duration_est
2970            # print '      time_order' ,time_order,'time_depart',time_depart,'delay',time_depart-time_order
2971
2972            stops.program_leadveh(id_stop_current, id_veh_lead, id_stop_target, time_depart)
2973            self.numbers_veh_arr[id_stop_target] += 1
2974            is_started = True
2975
2976        return is_started
2977
2978    def init_trip_occupied(self, id_veh, id_stop_from, id_stop_to, time_order):
2979        # print 'init_trip_occupied from',id_veh, id_stop_from, id_stop_to
2980        # search closest stop
2981        #self.are_emptytrips[id_veh] = False
2982
2983        self.ids_stop_current[id_veh] = id_stop_from
2984        self.ids_stop_target[id_veh] = id_stop_to
2985        self.times_order[id_veh] = time_order
2986        # print '  to stop',id_stop
2987        # return id_stop_to
2988
2989    def init_trip_empty(self, id_veh, id_stop, time_order):
2990        # print 'VehMan.init_trip_empty id_veh prt.%d,id_stop %d'%(id_veh,id_stop)
2991        # search closest stop
2992        self.ids_stop_current[id_veh] = id_stop
2993        self.times_order[id_veh] = time_order
2994        #self.are_emptytrips[id_veh] = True
2995        #id_stop_target = ids_stop[random.randint(0,len(ids_stop)-1)]
2996        # print '  to stop',id_stop
2997        # return id_stop_target
2998
2999    def indicate_trip_empty(self, id_veh, id_stop, time_order):
3000        # print 'indicate_trip_empty id_veh,id_stop',id_veh,id_stop
3001        # search closest stop
3002        self.ids_stop_current[id_veh] = id_stop
3003        self.times_order[id_veh] = time_order
3004
3005    def get_stop_emptytrip(self, id_stop):
3006        stops = self.get_stops()
3007        ids_stop = list(self.ids_stop)
3008        ids_stop.remove(id_stop)
3009        costs = (stops.numbers_person_wait[ids_stop]-stops.numbers_veh[ids_stop] -
3010                 self.numbers_veh_arr[ids_stop])/self.parent.times_stop_to_stop[id_stop, ids_stop]
3011
3012        return ids_stop[np.argmax(costs)]
3013
3014    def conclude_trip(self, id_veh, id_stop):
3015        self.ids_stop_target[id_veh] = -1
3016        self.numbers_veh_arr[id_stop] -= 1
3017
3018        # measures actually arrived vehicles at stop
3019        # accumulates counts over one flow measurement interval
3020        self.log_inflows_temp[id_stop] += 1
3021
3022
3023class PrtService(SimobjMixin, DemandobjMixin, cm.BaseObjman):
3024    def __init__(self, ident, simulation=None,
3025                 name='PRT service', info='PRT service',
3026                 **kwargs):
3027            # print 'PrtService.__init__',name
3028
3029        self._init_objman(ident=ident, parent=simulation,
3030                          name=name, info=info, **kwargs)
3031
3032        attrsman = self.set_attrsman(cm.Attrsman(self))
3033
3034        # make PRTservice a demand object as link
3035        self.get_scenario().demand.add_demandobject(obj=self)
3036
3037        self._init_attributes()
3038        self._init_constants()
3039
3040    def get_scenario(self):
3041        return self.parent.parent
3042
3043    def _init_attributes(self):
3044        # print 'PrtService._init_attributes',hasattr(self,'prttransit')
3045        attrsman = self.get_attrsman()
3046        scenario = self.get_scenario()
3047        # here we ged classes not vehicle type
3048        # specific vehicle type within a class will be generated later
3049        modechoices = scenario.net.modes.names.get_indexmap()
3050
3051        # print '  modechoices',modechoices
3052        self.id_prtmode = attrsman.add(am.AttrConf('id_prtmode',  modechoices['custom1'],
3053                                                   groupnames=['options'],
3054                                                   choices=modechoices,
3055                                                   name='Mode',
3056                                                   info='PRT transport mode (or vehicle class).',
3057                                                   ))
3058
3059        self.prtstops = attrsman.add(cm.ObjConf(PrtStops('prtstops', self)))
3060        self.mergenodes = attrsman.add(cm.ObjConf(Mergenodes('mergenodes', self)))
3061        self.prtvehicles = attrsman.add(cm.ObjConf(PrtVehicles('prtvehicles', self)))
3062        self.vehicleman = attrsman.add(cm.ObjConf(VehicleMan('vehicleman', self)))
3063
3064        # --------------------------------------------------------------------
3065        # prt transit table
3066        # attention: prttransits will be a child of virtual pop,
3067        # and a link from prt service
3068
3069        # if not hasattr(self,'prttransit'):
3070        virtualpop = self.get_scenario().demand.virtualpop
3071        prttransits = virtualpop.get_plans().add_stagetable('prttransits', PrtTransits)
3072
3073        # print '  prttransits =',prttransits
3074        # add attribute as link
3075        # self.prttransits =  attrsman.add(\
3076        #                        cm.ObjConf(prttransits,is_child = False ),
3077        #                        is_overwrite = False,)
3078        prttransits.set_prtservice(self)
3079
3080        prtstrategy = virtualpop.get_strategies().add_strategy('prt', PrtStrategy)
3081        # self.prttransits =  attrsman.add(\
3082        #                        cm.ObjConf(prttransits,is_child = False ),
3083        #                        is_overwrite = False,)
3084        prtstrategy.set_prtservice(self)
3085
3086        # temporary attrfix
3087        #prtserviceconfig = self.parent.get_attrsman().prtservice
3088        #prtserviceconfig.groupnames = []
3089        #prtserviceconfig.add_groupnames(['demand objects'])
3090
3091    def _init_constants(self):
3092        # print 'PrtService._init_constants',self,self.parent
3093        attrsman = self.get_attrsman()
3094        self.times_stop_to_stop = None
3095        self.fstar = None
3096        self._results = None
3097        attrsman.do_not_save_attrs(['times_stop_to_stop', 'fstar', '_results'])
3098
3099    def get_vtypes(self):
3100
3101        ids_vtypes = set(self.prtvehicles.ids_vtype.get_value())
3102        return ids_vtypes
3103
3104    def get_writexmlinfo(self, is_route=False):
3105        """
3106        Returns three array where the first array is the
3107        begin time of the first vehicle and the second array is the
3108        write function to be called for the respectice vehicle and
3109        the third array contains the vehicle ids
3110
3111        Method used to sort trips when exporting to route or trip xml file
3112        """
3113        print 'PRT.get_writexmlinfo'
3114
3115        # time of first PRT vehicle(s) to be inserted
3116        virtualpop = self.get_scenario().demand.virtualpop
3117        t_start = virtualpop.get_time_depart_first()
3118
3119        #t_start = 0.0
3120        # time betwenn insertion of consecutive vehicles at same stop
3121        t_delta = 10  # s
3122
3123        n_veh = len(self.prtvehicles)
3124        times_depart = np.zeros(n_veh, dtype=np.int32)
3125        writefuncs = np.zeros(n_veh, dtype=np.object)
3126        writefuncs[:] = self.write_prtvehicle_xml
3127        ids_veh = self.prtvehicles.get_ids()
3128
3129        id_edge_prev = -1
3130        i = 0
3131        t0 = t_start
3132        for id_edge in self.prtvehicles.ids_currentedge[ids_veh]:
3133                # print '  id_edge, t_start, id_edge_prev',id_edge, t0, id_edge_prev
3134            times_depart[i] = t0
3135            t0 += t_delta
3136            if id_edge != id_edge_prev:
3137                t0 = t_start
3138                id_edge_prev = 1*id_edge
3139            i += 1
3140
3141        return times_depart, writefuncs, ids_veh
3142
3143    def write_prtvehicle_xml(self,  fd, id_veh, time_begin, indent=2):
3144        # print 'write_prtvehicle_xml',id_veh, time_begin
3145        # TODO: actually this should go in prtvehicles
3146        #time_veh_wait_after_stop = 3600
3147        scenario = self.get_scenario()
3148        net = scenario.net
3149
3150        #lanes = net.lanes
3151        edges = net.edges
3152        #ind_ride = rides.get_inds(id_stage)
3153        #id_veh = rides.ids_veh[id_stage]
3154        prtvehicles = self.prtvehicles
3155        #ptstops = net.ptstops
3156        #prtstops = self.parent.prtstops
3157        #ids_prtstop = prtstops.get_ids()
3158        #ids_ptstop = prtstops.ids_ptstop[id_stop]
3159        # lanes.ids_edge[ptstops.ids_lane[ids_ptstop]],
3160        #id_lane_from = parking.ids_lane[id_parking_from]
3161        #laneindex_from =  lanes.indexes[id_lane_from]
3162        #pos_from = parking.positions[id_parking_from]
3163
3164        #id_parking_to = rides.ids_parking_to[id_stage]
3165        #id_lane_to = parking.ids_lane[id_parking_to]
3166        #laneindex_to =  lanes.indexes[id_lane_to]
3167        #pos_to = parking.positions[id_parking_to]
3168
3169        # write unique veh ID to prevent confusion with other veh declarations
3170        fd.write(xm.start('vehicle id="%s"' % prtvehicles.get_id_sumo(id_veh), indent+2))
3171
3172        fd.write(xm.num('depart', '%d' % time_begin))
3173        fd.write(xm.num('type', scenario.demand.vtypes.ids_sumo[prtvehicles.ids_vtype[id_veh]]))
3174        fd.write(xm.num('line', prtvehicles.get_id_line_xml()))
3175        fd.write(xm.stop())
3176
3177        # write route
3178        fd.write(xm.start('route', indent+4))
3179        # print '  edgeindex[ids_edge]',edgeindex[ids_edge]
3180        fd.write(xm.arr('edges', [edges.ids_sumo[prtvehicles.ids_currentedge[id_veh]]]))
3181
3182        # does not seem to have an effect, always starts at base????
3183        fd.write(xm.num('departPos', 'base'))
3184        #fd.write(xm.num('departLane', laneindex_from ))
3185        fd.write(xm.stopit())
3186
3187        # write depart stop
3188        # fd.write(xm.start('stop',indent+4))
3189        #fd.write(xm.num('lane', edges.ids_sumo[lanes.ids_edge[id_lane_from]]+'_%d'%laneindex_from ))
3190        #fd.write(xm.num('duration', time_veh_wait_after_stop))
3191        #fd.write(xm.num('startPos', pos_from ))
3192        #fd.write(xm.num('endPos', pos_from + parking.lengths[id_parking_from]))
3193        #fd.write(xm.num('triggered', "True"))
3194        # fd.write(xm.stopit())
3195
3196        fd.write(xm.end('vehicle', indent+2))
3197
3198    # def make_stops_and_vehicles(self, n_veh = -1):
3199    #    self.prtstops.make_from_net()
3200    #    self.prtvehicles.make(n_veh)
3201    #    self.make_times_stop_to_stop()
3202
3203    def prepare_sim(self, process):
3204        print 'prepare_sim', self.ident
3205        # print '  self.times_stop_to_stop',self.times_stop_to_stop
3206
3207        if self.fstar is None:
3208            self.make_fstar()
3209
3210        if self.times_stop_to_stop is None:
3211            print '  times_stop_to_stop'
3212            self.make_times_stop_to_stop()
3213
3214        updatedata = self.prtvehicles.prepare_sim(process)
3215        updatedata += self.prtstops.prepare_sim(process)
3216        updatedata += self.mergenodes.prepare_sim(process)
3217        updatedata += self.vehicleman.prepare_sim(process)
3218
3219        # print 'PrtService.prepare_sim updatedata',updatedata
3220        return updatedata
3221
3222    def make_fstar(self, is_update=False):
3223        if (self.fstar is None) | is_update:
3224            self.fstar = self.get_fstar()
3225            self.edgetimes = self.get_times(self.fstar)
3226
3227    def get_route(self, id_fromedge, id_toedge):
3228        """
3229        Centralized function to determin fastest route between
3230        PRT network edges.
3231        """
3232        duration, route = get_mincostroute_edge2edge(id_fromedge, id_toedge,
3233                                                     weights=self.edgetimes, fstar=self.fstar)
3234
3235        return route, duration
3236
3237    def make_times_stop_to_stop(self, fstar=None, times=None):
3238        print 'make_times_stop_to_stop'
3239        log = self.get_logger()
3240        if fstar is None:
3241            if self.fstar is None:
3242                self.make_fstar()
3243
3244            fstar = self.fstar  # get_fstar()
3245            times = self.edgetimes  # self.get_times(fstar)
3246
3247        if len(fstar) == 0:
3248            self.times_stop_to_stop = [[]]
3249            return
3250
3251        ids_prtstop = self.prtstops.get_ids()
3252        if len(ids_prtstop) == 0:
3253            self.get_logger().w('WARNING: no PRT stops, no plans. Generate them!')
3254            return
3255
3256        ids_edge = self.prtstops.get_edges(ids_prtstop)
3257
3258        # check if all PRT stop edges are in fstar
3259        ids_ptstop = self.prtstops.ids_ptstop[ids_prtstop]  # for debug only
3260        is_incomplete_fstar = False
3261        for id_edge, id_stop, id_ptstop in zip(ids_edge, ids_prtstop, ids_ptstop):
3262            # print '  Found PRT stop %d, PT stop %d with id_edge %d '%(id_stop,id_ptstop, id_edge)
3263            if not fstar.has_key(id_edge):
3264                print 'WARNING in make_times_stop_to_stop: PRT stop %d, PT stop %d has no id_edge %d in fstar' % (
3265                    id_stop, id_ptstop, id_edge)
3266                is_incomplete_fstar = True
3267
3268        # check if fstar is complete (all to edges are in keys)
3269        ids_fromedge_set = set(fstar.keys())
3270        ids_sumo = self.get_scenario().net.edges.ids_sumo
3271        for id_fromedge in ids_fromedge_set:
3272            if not ids_fromedge_set.issuperset(fstar[id_fromedge]):
3273                is_incomplete_fstar = True
3274                ids_miss = fstar[id_fromedge].difference(ids_fromedge_set)
3275                print 'WARNING in make_times_stop_to_stop: incomplete fstar of id_fromedge = %d, %s' % (
3276                    id_fromedge, ids_sumo[id_fromedge])
3277                for id_edge in ids_miss:
3278                    print '  missing', id_edge, ids_sumo[id_edge]
3279
3280        if is_incomplete_fstar:
3281            return
3282
3283        # print '  ids_prtstop,ids_edge',ids_prtstop,ids_edge
3284        n_elem = np.max(ids_prtstop)+1
3285        stop_to_stop = np.zeros((n_elem, n_elem), dtype=np.int32)
3286
3287        ids_edge_to_ids_prtstop = np.zeros(np.max(ids_edge)+1, dtype=np.int32)
3288        ids_edge_to_ids_prtstop[ids_edge] = ids_prtstop
3289
3290        ids_edge_target = set(ids_edge)
3291
3292        for id_stop, id_edge in zip(ids_prtstop, ids_edge):
3293            # print '    route for id_stop, id_edge',id_stop, id_edge
3294
3295            # remove origin from target
3296            ids_edge_target.discard(id_edge)
3297
3298            costs, routes = edgedijkstra(id_edge,
3299                                         ids_edge_target=ids_edge_target,
3300                                         weights=times, fstar=fstar
3301                                         )
3302
3303            # print '    ids_edge_target',ids_edge_target
3304            # print '    costs\n',   costs
3305            # print '    routes\n',   routes
3306            # for route in routes:
3307            #    if len(route)==0:
3308            #        print 'WARNING in make_times_stop_to_stop: empty route'
3309            #    else:
3310            #        print '    found route to id_edge, id_stop',route[-1],ids_edge_to_ids_prtstop[route[-1]],len(route)
3311
3312            if costs is not None:
3313                # TODO: could be vectorialized, but not so easy
3314                for id_edge_target in ids_edge_target:
3315                    #stop_to_stop[id_edge,id_edge_target] = costs[id_edge_target]
3316                    # print '     stop_orig,stop_target,costs ',ids_edge_to_ids_prtstop[id_edge],ids_edge_to_ids_prtstop[id_edge_target],costs[id_edge_target]
3317                    # print '     stop_orig,costs ',ids_edge_to_ids_prtstop[id_edge],ids_sumo[id_edge]
3318                    # print '     stop_target',ids_edge_to_ids_prtstop[id_edge_target],ids_sumo[id_edge_target]
3319                    # print '     costs ',costs[id_edge_target]
3320                    # stop_to_stop[ids_edge_to_ids_prtstop[[id_edge,id_edge_target]]]=costs[id_edge_target]
3321                    if id_edge_target in costs:
3322                        stop_to_stop[ids_edge_to_ids_prtstop[id_edge],
3323                                     ids_edge_to_ids_prtstop[id_edge_target]] = costs[id_edge_target]
3324                    else:
3325                        print 'WARNING in make_times_stop_to_stop: unreacle station id_fromedge = %d, %s' % (
3326                            id_edge_target, ids_sumo[id_edge_target])
3327                        is_incomplete_fstar = True
3328
3329                # put back origin to targets (probably not the best way)
3330                ids_edge_target.add(id_edge)
3331                # print '    ids_edge_target (all)',ids_edge_target
3332
3333            # print '    stop_to_stop',stop_to_stop
3334            # TODO: here we could also store the routes
3335
3336        if is_incomplete_fstar:
3337            return False
3338
3339        self.times_stop_to_stop = stop_to_stop
3340        self.ids_edge_to_ids_prtstop = ids_edge_to_ids_prtstop
3341        # print '  times_stop_to_stop=\n',self.times_stop_to_stop
3342        return True
3343
3344    def get_fstar(self):
3345        """
3346        Returns the forward star graph of the network as dictionary:
3347            fstar[id_fromedge] = set([id_toedge1, id_toedge2,...])
3348        """
3349        print 'get_fstar'
3350        net = self.get_scenario().net
3351        # prt mode
3352        id_mode = self.id_prtmode
3353
3354        #ids_edge = self.get_ids()
3355        #fstar = np.array(np.zeros(np.max(ids_edge)+1, np.obj))
3356        fstar = {}
3357        connections = net.connections
3358        lanes = net.lanes
3359
3360        #inds_con = connections.get_inds()
3361        #ids_fromlane = connections.ids_fromlane.get_value()[inds_con]
3362        #ids_tolane = connections.ids_tolane.get_value()[inds_con]
3363
3364        ids_con = connections.get_ids()
3365        ids_fromlane = connections.ids_fromlane[ids_con]
3366        ids_tolane = connections.ids_tolane[ids_con]
3367
3368        ids_mainmode_from = lanes.ids_mode[ids_fromlane]
3369        ids_mainmode_to = lanes.ids_mode[ids_tolane]
3370
3371        #ids_modes_allow_from = lanes.ids_modes_allow[ids_fromlane]
3372        #ids_modes_allow_to = lanes.ids_modes_allow[ids_tolane]
3373
3374        ids_fromedge = lanes.ids_edge[ids_fromlane]
3375        ids_toedge = lanes.ids_edge[ids_tolane]
3376        # print '  ids_fromedge',ids_fromedge
3377        # print '  ids_modes_allow',ids_modes_allow
3378
3379        for id_fromedge, id_toedge, id_mode_allow_from, id_mode_allow_to, id_fromlane, id_tolane in\
3380            zip(ids_fromedge, ids_toedge, ids_mainmode_from, ids_mainmode_to,
3381                ids_fromlane, ids_tolane):
3382
3383            if id_mode_allow_from == id_mode:
3384                if id_mode_allow_to == id_mode:
3385
3386                    if fstar.has_key(id_fromedge):
3387                        fstar[id_fromedge].add(id_toedge)
3388                    else:
3389                        fstar[id_fromedge] = set([id_toedge])
3390            # if id_fromedge == 14048:
3391            #    print '  id_fromedge, id_toedge',id_fromedge, id_toedge,fstar.has_key(id_fromedge)
3392            #    print '  id_fromlane, id_tolane ',id_fromlane, id_tolane
3393            #    print '  id_mode_allow_from, id_mode_allow_to',id_mode_allow_from, id_mode_allow_to
3394
3395        # for id_fromedge, id_toedge,ids_mode_allow_from,id_modes_allow_to  in\
3396        #                zip(ids_fromedge, ids_toedge, ids_modes_allow_from, ids_modes_allow_to):
3397        #    if len(ids_mode_allow_from)>0:
3398        #        if ids_mode_allow_from[-1] == id_mode:
3399        #            if len(id_modes_allow_to)>0:
3400        #                if id_modes_allow_to[-1] == id_mode:
3401        #
3402        #                    if fstar.has_key(id_fromedge):
3403        #                        fstar[id_fromedge].add(id_toedge)
3404        #                    else:
3405        #                        fstar[id_fromedge]=set([id_toedge])
3406
3407        return fstar
3408
3409    def get_times(self, fstar):
3410        """
3411        Returns freeflow travel times for all edges.
3412        The returned array represents the speed and the index corresponds to
3413        edge IDs.
3414
3415        """
3416        if len(fstar) == 0:
3417            return []
3418
3419        net = self.get_scenario().net
3420        #id_mode = net.modes.get_id_mode(mode)
3421        id_mode = self.id_prtmode
3422        # print 'get_times id_mode,is_check_lanes,speed_max',id_mode,is_check_lanes,speed_max
3423        ids_edge = np.array(fstar.keys(), dtype=np.int32)
3424
3425        times = np.array(np.zeros(np.max(ids_edge)+1, np.float32))
3426        speeds = net.edges.speeds_max[ids_edge]
3427
3428        # limit allowed speeds with max speeds of mode
3429        speeds = np.clip(speeds, 0.0, net.modes.speeds_max[id_mode])
3430
3431        times[ids_edge] = net.edges.lengths[ids_edge]/speeds
3432
3433        return times
3434
3435    def config_results(self, results):
3436        # print 'config_results',results, id(results)
3437        # keep a link to results here because needed to
3438        # log data during simulation
3439        # this link should not be followed during save process
3440        #self._results = results
3441
3442        tripresults = res.Tripresults('prttripresults', results,
3443                                      self.prtvehicles,
3444                                      self.get_scenario().net.edges,
3445                                      name='PRT trip results',
3446                                      info='Table with simulation results for each PRT vehicle. The results refer to the vehicle journey over the entire simulation period.',
3447                                      )
3448        results.add_resultobj(tripresults, groupnames=['Trip results'])
3449
3450        prtstopresults = Stopresults('prtstopresults', results, self.prtstops)
3451        results.add_resultobj(prtstopresults, groupnames=['PRT stop results'])
3452
3453    # def get_results(self):
3454    #    return self._results
3455
3456    def process_results(self, results, process=None):
3457        pass
3458
3459
3460class Stopresults(am.ArrayObjman):
3461    def __init__(self, ident, results, prtstops,
3462                 name='Stop results',
3463                 info='Table with simulation results of stops generated from vehicle management.',
3464                 **kwargs):
3465
3466        self._init_objman(ident=ident,
3467                          parent=results,  # main results object
3468                          info=info,
3469                          name=name,
3470                          **kwargs)
3471
3472        self.add(cm.AttrConf('time_step', 5.0,
3473                             groupnames=['parameters'],
3474                             name='Step time',
3475                             info="Time of one recording step.",
3476                             unit='s',
3477                             ))
3478
3479        self.add(cm.ObjConf(prtstops, is_child=False,  # groupnames = ['_private']
3480                            ))
3481
3482        self.add_col(am.IdsArrayConf('ids_stop', prtstops,
3483                                     groupnames=['state'],
3484                                     is_index=True,
3485                                     name='PRT stop ID',
3486                                     info='ID of PRT stop.',
3487                                     ))
3488
3489        attrinfos = [
3490            ('inflows_veh', {'name': 'Vehicle in-flows', 'unit': '1/s',
3491                             'dtype': np.float32, 'info': 'Vehicle flow into the stop over time.'}),
3492            ('inflows_veh_sched', {'name': 'Sched. vehicle in-flows', 'unit': '1/s',
3493                                   'dtype': np.float32, 'info': 'Scheduled vehicle flow into the stop over time.'}),
3494            ('inflows_person', {'name': 'Person in-flows', 'unit': '1/s',
3495                                'dtype': np.float32, 'info': 'Person flow into the stop over time.'}),
3496            ('numbers_person_wait', {'name': 'waiting person',         'dtype': np.int32,
3497                                     'info': 'Number of waiting persons at stop over time.'}),
3498            ('waittimes_tot', {'name': 'total wait time',         'dtype': np.float32,
3499                               'info': 'Wait times of all waiting persons at a stop over time.'}),
3500        ]
3501
3502        for attrname, kwargs in attrinfos:
3503            self.add_resultattr(attrname, **kwargs)
3504
3505    def get_dimensions(self):
3506        return len(self), len(self.inflows_veh.get_default())
3507
3508    def get_prtstops(self):
3509        return self.prtstops.get_value()
3510        # return self.ids_stop.get_linktab()
3511
3512    def init_recording(self, n_timesteps, time_step):
3513        print 'init_recording n_timesteps, time_step', n_timesteps, time_step, len(
3514            self.ids_stop.get_linktab().get_ids())
3515        self.clear()
3516
3517        self.time_step.set_value(time_step)
3518
3519        for attrconfig in self.get_stopresultattrconfigs():
3520            # print '  reset attrconfig',attrconfig.attrname
3521            attrconfig.set_default(np.zeros(n_timesteps, dtype=attrconfig.get_dtype()))
3522            attrconfig.reset()
3523            # print '  default=',attrconfig.get_default(),attrconfig.get_default().dtype
3524        ids_stop = self.get_prtstops().get_ids()
3525        # print '  ids_stop',ids_stop
3526        self.add_rows(n=len(ids_stop), ids_stop=ids_stop)
3527
3528    def record(self, timestep, ids, **kwargs):
3529        inds = self.ids_stop.get_linktab().get_inds(ids)
3530        for attrname, values in kwargs.iteritems():
3531            # print 'record',attrname,values.dtype,values.shape, getattr(self,attrname).get_value().dtype, getattr(self,attrname).get_value().shape
3532            getattr(self, attrname).get_value()[inds, timestep] = values
3533
3534    def get_stopresultattrconfigs(self):
3535        return self.get_attrsman().get_group_attrs('PRT results').values()
3536
3537    def get_persons(self):
3538        return self.ids_person.get_linktab()
3539
3540    def add_resultattr(self, attrname, **kwargs):
3541        self.add_col(am.ArrayConf(attrname, 0, groupnames=['PRT results', 'results'], **kwargs))
3542
3543    def import_xml(self, sumo, datapaths):
3544        # no imports, data come from prtservice
3545        pass
3546