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-12-berthoptok.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
25#from xml.sax import saxutils, parse, handler
26
27
28from coremodules.modules_common import *
29import agilepy.lib_base.classman as cm
30import agilepy.lib_base.arrayman as am
31import agilepy.lib_base.xmlman as xm
32#from agilepy.lib_base.misc import get_inversemap
33#from agilepy.lib_base.geometry import find_area
34#from agilepy.lib_base.processes import Process,CmlMixin,ff,call
35from coremodules.network.network import SumoIdsConf
36from coremodules.network.routing import edgedijkstra, get_mincostroute_edge2edge
37from coremodules.simulation import sumo
38from coremodules.simulation.sumo import traci
39#from coremodules.network import routing
40
41from coremodules.demand.virtualpop import StageTypeMixin
42
43BERTHSTATES = {'free': 0, 'waiting': 1, 'boarding': 2, 'alighting': 3}
44VEHICLESTATES = {'init': 0, 'waiting': 1, 'boarding': 2,
45                 'alighting': 3, 'emptytrip': 4, 'occupiedtrip': 5, 'forewarding': 6}
46STOPTYPES = {'person': 0, 'freight': 1, 'depot': 2, 'group': 3, 'mixed': 4}
47# def detect_entered_left(x,y):
48#    """
49#    returns the enter and left elemets of list x (before)
50#    and list y (after)
51#    """
52#    if len(x) == 0:
53#        if len(y) == 0:
54#            return
55
56
57class PrtBerths(am.ArrayObjman):
58
59    def __init__(self, ident, prtstops, **kwargs):
60        # print 'PrtVehicles vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
61        self._init_objman(ident=ident,
62                          parent=prtstops,
63                          name='PRT Berths',
64                          info='PRT Berths.',
65                          **kwargs)
66
67        self._init_attributes()
68
69    def _init_attributes(self):
70        #vtypes = self.get_scenario().demand.vtypes
71        net = self.get_scenario().net
72        self.add(cm.AttrConf('length_default', 4.0,
73                             groupnames=['parameters', 'options'],
74                             name='Default length',
75                             info='Default berth length.',
76                             unit='m',
77                             ))
78
79        self.add_col(am.IdsArrayConf('ids_prtstop', self.parent,
80                                     name='PRT stop ID',
81                                     info='PRT stop ID',
82                                     ))
83
84        # states now dynamic, see prepare_sim
85        if hasattr(self, 'states'):
86            self.delete('states')
87        # self.add_col(am.ArrayConf( 'states', default = BERTHSTATES['free'],
88        #                            dtype = np.int32,
89        #                            choices = BERTHSTATES,
90        #                            name = 'state',
91        #                            info = 'State of berth.',
92        #                            ))
93
94        self.add_col(am.ArrayConf('stoppositions', default=0.0,
95                                  dtype=np.float32,
96                                  name='Stop position',
97                                  info='Position on edge where vehicle nose stops.',
98                                  ))
99
100    def prepare_sim(self, process):
101        # print 'PrtBerths.prepare_sim'
102        ids = self.get_ids()
103        self.states = BERTHSTATES['free']*np.ones(np.max(ids)+1, dtype=np.int32)
104        self.ids_veh = -1*np.ones(np.max(ids)+1, dtype=np.int32)
105        return []  # berth has no update function
106
107    def get_scenario(self):
108        return self.parent.get_scenario()
109
110    def get_prtvehicles(self):
111        return self.parent.parent.prtvehicles
112
113    def make(self, id_stop, position_from=None, position_to=None,
114             n_berth=None,
115             offset_firstberth=0.0, offset_stoppos=-0.0):
116        stoplength = position_to-position_from
117        print 'Berths.make', id_stop, stoplength
118
119        # TODO: let define berth number either explicitely or through stoplength
120
121        length_berth = self.length_default.get_value()
122        positions = position_from + offset_firstberth\
123            + np.arange(length_berth-offset_firstberth, stoplength+length_berth, length_berth) + offset_stoppos
124        n_berth = len(positions)
125
126        # force number of berth to be pair
127        if n_berth % 2 == 1:
128            positions = positions[1:]
129            n_berth -= 1
130
131        ids_berth = self.add_rows(n=n_berth,
132                                  stoppositions=positions,
133                                  ids_prtstop=id_stop * np.ones(n_berth, dtype=np.int32),
134                                  )
135        return ids_berth
136
137    def set_prtvehicles(self, prtvehicles):
138        """
139        Defines attributes which are linked with prtvehicles
140        """
141        self.add_col(am.IdsArrayConf('ids_veh_allocated', prtvehicles,
142                                     name='Alloc. veh ID',
143                                     info='ID of  vehicle which have allocated this berth. -1 means no allocation.',
144                                     ))
145
146
147class PrtStops(am.ArrayObjman):
148    def __init__(self, ident, prtservices, **kwargs):
149        self._init_objman(ident=ident,
150                          parent=prtservices,
151                          name='Public transport stops',
152                          info='Contains information on PRT stops.',
153                          #xmltag = ('additional','busStop','stopnames'),
154                          version=0.1,
155                          **kwargs)
156        self._init_attributes()
157
158    def _init_attributes(self):
159        self.add(cm.ObjConf(PrtBerths('berths', self)))
160
161        berths = self.get_berths()
162        net = self.get_scenario().net
163
164        self.add(cm.AttrConf('time_update', 0.5,
165                             groupnames=['parameters'],
166                             name='Update time',
167                             info="Update time for station.",
168                             unit='s',
169                             ))
170
171        if hasattr(self, 'time_update_man'):
172            self.delete('time_update_man')
173            self.delete('timehorizon')
174
175        self.add_col(am.IdsArrayConf('ids_ptstop', net.ptstops,
176                                     name='ID PT stop',
177                                     info='ID of public transport stop. ',
178                                     ))
179
180        if hasattr(self, 'are_depot'):
181            self.delete('are_depot')
182
183        self.add_col(am.ArrayConf('types', default=STOPTYPES['person'],
184                                  dtype=np.int32,
185                                  perm='rw',
186                                  choices=STOPTYPES,
187                                  name='Type',
188                                  info='Type of stop.',
189                                  ))
190
191        self.add_col(am.IdlistsArrayConf('ids_berth_alight', berths,
192                                         #groupnames = ['_private'],
193                                         name='Alight berth IDs',
194                                         info="Alight berth IDs.",
195                                         ))
196
197        self.add_col(am.IdlistsArrayConf('ids_berth_board', berths,
198                                         #groupnames = ['_private'],
199                                         name='Board berth IDs',
200                                         info="Board berth IDs.",
201                                         ))
202
203        # self.add_col(am.ArrayConf( 'inds_berth_alight_allocated', default = 0,
204        #                            #groupnames = ['_private'],
205        #                            dtype = np.int32,
206        #                            perm = 'rw',
207        #                            name = 'Ind aberth lastalloc',
208        #                            info = 'Berth index of last allocated berth in alight zone.',
209        #                            ))
210
211        # self.add_col(am.ArrayConf( 'inds_berth_board_allocated', default = 0,
212        #                            #groupnames = ['_private'],
213        #                            dtype = np.int32,
214        #                            perm = 'rw',
215        #                            name = 'Ind bberth lastalloc',
216        #                            info = 'Berth index of last allocated berth in boarding zone.',
217        #                            ))
218
219    def get_edges(self, ids_prtstop):
220        net = self.get_scenario().net
221        return net.lanes.ids_edge[net.ptstops.ids_lane[self.ids_ptstop[ids_prtstop]]]
222
223    def get_berths(self):
224        return self.berths.get_value()
225
226    def get_scenario(self):
227        return self.parent.get_scenario()
228
229    def set_prtvehicles(self, prtvehicles):
230        self.get_berths().set_prtvehicles(prtvehicles)
231
232    def set_vehicleman(self, vehicleman):
233        self.add(cm.ObjConf(vehicleman, is_child=False, groups=['_private']))
234
235    def get_vehicleman(self):
236        return self.vehicleman.get_value()
237
238    def get_closest(self, coords):
239        """
240        Returns the closest prt stop for each coord in coords vector.
241        """
242        net = self.get_scenario().net
243        ptstops = net.ptstops
244        lanes = net.lanes
245        n = len(coords)
246        # print 'get_closest',n
247
248        #ids_stop = self.get_ids()
249
250        ids_prtstop = self.get_ids()
251        ids_ptstop = self.ids_ptstop[ids_prtstop]
252        coords_stop = ptstops.centroids[ids_ptstop]
253        ids_edge_stop = net.lanes.ids_edge[ptstops.ids_lane[ids_ptstop]]
254
255        inds_closest = np.zeros(n, dtype=np.int32)
256
257        i = 0
258        for coord in coords:
259            ind_closest = np.argmin(np.sum((coord-coords_stop)**2, 1))
260            inds_closest[i] = ind_closest
261            i += 1
262
263        ids_prtstop_closest = ids_prtstop[inds_closest]
264        ids_edge_closest = ids_edge_stop[inds_closest]
265
266        return ids_prtstop_closest, ids_edge_closest
267
268    def get_waitpositions(self, ids, is_alight=False, offset=-0.0):
269        """
270        Assign a wait-position for each stop in ids
271
272        offset is wait position relative to the vehicle nose.
273        """
274        # print 'get_waitpositions min(ids),max(ids)',min(ids),is_alight,max(ids),offset
275        positions = np.zeros(len(ids), dtype=np.float32)
276        randint = random.randint
277        if is_alight:
278            ids_berths = self.ids_berth_alight[ids]
279        else:
280            ids_berths = self.ids_berth_board[ids]
281
282        stoppositions = self.get_berths().stoppositions
283        # print '  ids_berths',ids_berths
284        i = 0
285        for id_stop, ids_berth in zip(ids, ids_berths):
286            #ids_berth = ids_berths[id_stop]
287            ind_berth = randint(0, len(ids_berth)-1)
288
289            positions[i] = stoppositions[ids_berth[ind_berth]]
290            # print '  id_stop,ids_berth,posiions',id_stop,ids_berth,stoppositions[ids_berth[ind_berth]]
291            i += 1
292            #positions[i] = stoppositions[ids_berth[randint(0,len(ids_berth))]]
293        # for id_stop , pos in zip(ids, positions):
294        #    print '  id_stop %d, is_alight = %s, pos %.2fm'%(id_stop, is_alight ,pos)
295
296        return positions+offset
297
298    def prepare_sim(self, process):
299        print 'PrtStops.prepare_sim'
300        net = self.get_scenario().net
301        ptstops = net.ptstops
302        ids_edge_sumo = net.edges.ids_sumo
303
304        berths = self.get_berths()
305        lanes = net.lanes
306        ids_edge_sumo = net.edges.ids_sumo
307        ids = self.get_ids()
308
309        # station management
310        self.ids_stop_to_ids_edge_sumo = np.zeros(np.max(ids)+1, dtype=np.object)
311        self.ids_stop_to_ids_edge_sumo[ids] = ids_edge_sumo[lanes.ids_edge[ptstops.ids_lane[self.ids_ptstop[ids]]]]
312
313        self.id_edge_sumo_to_id_stop = {}
314        for id_stop, id_edge_sumo in zip(ids, self.ids_stop_to_ids_edge_sumo[ids]):
315            self.id_edge_sumo_to_id_stop[id_edge_sumo] = id_stop
316
317        self.inds_berth_alight_allocated = -1*np.ones(np.max(ids)+1, dtype=np.int32)
318        self.inds_berth_board_allocated = -1*np.ones(np.max(ids)+1, dtype=np.int32)
319        self.ids_vehs_alight_allocated = np.zeros(np.max(ids)+1, dtype=np.object)
320        self.ids_vehs_board_allocated = np.zeros(np.max(ids)+1, dtype=np.object)
321
322        self.ids_vehs_sumo_prev = np.zeros(np.max(ids)+1, dtype=np.object)
323        self.ids_vehs = np.zeros(np.max(ids)+1, dtype=np.object)
324        self.ids_vehs_toallocate = np.zeros(np.max(ids)+1, dtype=np.object)
325        self.times_lastboard = 10**4*np.ones(np.max(ids)+1, dtype=np.int32)
326
327        # for vehicle management
328        self.numbers_veh = np.zeros(np.max(ids)+1, dtype=np.int32)
329        self.numbers_person_wait = np.zeros(np.max(ids)+1, dtype=np.int32)
330
331        # person management
332        self.ids_persons_sumo_prev = np.zeros(np.max(ids)+1, dtype=np.object)
333        self.ids_persons_sumo_boarded = np.zeros(np.max(ids)+1, dtype=np.object)
334        self.ids_persons_sumo_wait = np.zeros(np.max(ids)+1, dtype=np.object)
335        virtualpop = self.get_scenario().demand.virtualpop
336        stagelists = virtualpop.get_plans().stagelists
337        ids_persons = virtualpop.get_ids()
338        prttransits = self.parent.prttransits
339        id_person_to_origs_dests = {}
340
341        # if len(ids_persons)>0:
342        # TODO: move to prtservices
343        for id_person, stagelist in zip(ids_persons, stagelists[virtualpop.ids_plan[ids_persons]]):
344            for stages, id_stage in stagelist:
345                if stages.ident == 'prttransits':
346                    id_fromedge_sumo = ids_edge_sumo[stages.ids_fromedge[id_stage]]
347                    id_toedge_sumo = ids_edge_sumo[stages.ids_toedge[id_stage]]
348                    data_orig_dest = (self.id_edge_sumo_to_id_stop[id_fromedge_sumo],
349                                      self.id_edge_sumo_to_id_stop[id_toedge_sumo],
350                                      id_fromedge_sumo,
351                                      id_toedge_sumo)
352
353                    id_person_sumo = virtualpop.get_id_sumo_from_id(id_person)
354                    if id_person_to_origs_dests.has_key(id_person_sumo):
355                        id_person_to_origs_dests[id_person_sumo].append(data_orig_dest)
356                    else:
357                        id_person_to_origs_dests[id_person_sumo] = [data_orig_dest]
358
359        print '   id_person_to_origs_dests=\n', id_person_to_origs_dests
360        self.id_person_to_origs_dests = id_person_to_origs_dests
361
362        # this is only used for crazy person stage detection
363        # angles_stop =
364
365        # various initianilizations
366        for id_stop, id_edge_sumo in zip(ids, self.ids_stop_to_ids_edge_sumo[ids]):
367            # set allocation index to last possible berth
368            self.inds_berth_alight_allocated[id_stop] = len(self.ids_berth_alight[id_stop])
369            self.inds_berth_board_allocated[id_stop] = len(self.ids_berth_board[id_stop])
370
371            self.ids_vehs_alight_allocated[id_stop] = []
372            self.ids_vehs_board_allocated[id_stop] = []
373
374            self.ids_vehs_sumo_prev[id_stop] = set([])
375            self.ids_persons_sumo_prev[id_stop] = set([])
376            self.ids_persons_sumo_boarded[id_stop] = []
377            self.ids_persons_sumo_wait[id_stop] = []
378            self.ids_vehs[id_stop] = []
379            self.ids_vehs_toallocate[id_stop] = []
380
381        #    traci.edge.subscribe(id_edge_sumo, [traci.constants.VAR_ARRIVED_VEHICLES_IDS])
382        updatedata_berth = berths.prepare_sim(process)
383
384        return [(self.time_update.get_value(), self.process_step),
385                ]+updatedata_berth
386
387    def process_step(self, process):
388        print 79*'_'
389        print 'PrtStops.process_step'
390        net = self.get_scenario().net
391        ptstops = net.ptstops
392        berths = self.get_berths()
393        lanes = net.lanes
394        ids_edge_sumo = net.edges.ids_sumo
395        vehicles = self.parent.prtvehicles
396        virtualpop = self.get_scenario().demand.virtualpop
397        ids = self.get_ids()
398
399        for id_stop, id_edge_sumo, ids_veh_sumo_prev, ids_person_sumo_prev in\
400            zip(ids, self.ids_stop_to_ids_edge_sumo[ids],
401                self.ids_vehs_sumo_prev[ids],
402                self.ids_persons_sumo_prev[ids]):
403            print '  '+60*'.'
404            print '  process id_stop,id_edge_sumo', id_stop, id_edge_sumo
405            if 1:
406
407                # print '    ids_berth_alight',self.ids_berth_alight[id_stop]
408                # print '    ids_berth_board',self.ids_berth_board[id_stop]
409                print '    ids_vehs_toallocate', self.ids_vehs_toallocate[id_stop]
410                print '    ids_vehs_alight_allocated', self.ids_vehs_alight_allocated[id_stop]
411                print '    ids_vehs_board_allocated', self.ids_vehs_board_allocated[id_stop]
412                print '    inds_berth_alight_allocated', self.inds_berth_alight_allocated[id_stop]
413                print '    inds_berth_board_allocated', self.inds_berth_board_allocated[id_stop]
414                print '    numbers_person_wait', self.numbers_person_wait[id_stop]
415                print '    ids_persons_sumo_wait', self.ids_persons_sumo_wait[id_stop]
416                print '    ids_persons_sumo_boarded', self.ids_persons_sumo_boarded[id_stop]
417                print '    times_lastboard', self.times_lastboard[id_stop]
418
419            if 0:
420                for id_veh_sumo in self.ids_vehs_sumo_prev[id_stop]:
421                    print '    stopstate ', id_veh_sumo, bin(traci.vehicle.getStopState(id_veh_sumo))[2:]
422
423            if 0:
424                self.get_berthqueues(id_stop)
425
426            # check for new vehicle arrivals/departures
427            ids_veh_sumo = set(traci.edge.getLastStepVehicleIDs(id_edge_sumo))
428            # print '    ids_veh_sumo_prev=',ids_veh_sumo_prev
429            # print '    ids_veh_sumo=',ids_veh_sumo
430
431            if ids_veh_sumo_prev != ids_veh_sumo:
432                ids_veh_entered = vehicles.get_ids_from_ids_sumo(list(ids_veh_sumo.difference(ids_veh_sumo_prev)))
433                ids_veh_left = vehicles.get_ids_from_ids_sumo(list(ids_veh_sumo_prev.difference(ids_veh_sumo)))
434                for id_veh in ids_veh_entered:
435                    self.enter(id_stop, id_veh)
436
437                for id_veh in ids_veh_left:
438                    self.exit(id_stop, id_veh)
439                self.ids_vehs_sumo_prev[id_stop] = ids_veh_sumo
440                # print '    ids_veh_sumo_entered',ids_veh_sumo_entered
441                # print '    ids_veh_sumo_left',ids_veh_sumo_left
442
443            # check whether allocated vehicles arrived at alighting berths
444            ids_veh_remove = []
445            for id_veh in self.ids_vehs_alight_allocated[id_stop]:
446                # TODO: here we could also check vehicle position
447                if traci.vehicle.isStopped(vehicles.get_id_sumo(id_veh)):
448                    ids_veh_remove.append(id_veh)
449                    id_berth_alight = vehicles.ids_berth[id_veh]
450                    berths.ids_veh[id_berth_alight] = id_veh
451                    berths.states[id_berth_alight] = BERTHSTATES['alighting']
452                    vehicles.alight(id_veh)
453
454            for id_veh in ids_veh_remove:
455                self.ids_vehs_alight_allocated[id_stop].remove(id_veh)
456
457            # check whether we can move vehicles from alighting to
458            # boarding berths
459
460            # TODO: here we must check if berth in boarding zone are free
461            # AND if they are occupied with empty vehicles, those
462            # vehicles need to be kicked out...but only in case
463            # new vehicles are waiting to be allocated
464
465            ids_berth_alight = self.ids_berth_alight[id_stop][::-1]
466            ids_berth_board = self.ids_berth_board[id_stop][::-1]
467            for id_berth_alight, id_veh in zip(
468                ids_berth_alight,
469                berths.ids_veh[ids_berth_alight],
470            ):
471
472                print '    check alight->board  veh', id_veh
473                # TODO: this could go into one vehicle method?
474
475                if id_veh >= 0:  # is there a waiting vehicle
476                    # print '    is_completed_alighting',vehicles.is_completed_alighting(id_veh)
477                    if vehicles.is_completed_alighting(id_veh):
478
479                        id_berth_board = self.allocate_board(id_stop)
480                        print '    try allocate id_veh=prt.%d for berth id_berth_board=%d' % (id_veh, id_berth_board)
481                        if id_berth_board >= 0:
482                            # ,berths.stoppositions[id_berth_board]
483                            print '     send vehicle id_veh %d to id_berth_board %d' % (id_veh, id_berth_board)
484                            berths.ids_veh[id_berth_alight] = -1
485                            berths.states[id_berth_alight] = BERTHSTATES['free']
486
487                            vehicles.control_stop_board(id_veh, id_stop, id_berth_board,
488                                                        id_edge_sumo=self.ids_stop_to_ids_edge_sumo[id_stop],
489                                                        position=berths.stoppositions[id_berth_board],
490                                                        )
491                            self.ids_vehs_board_allocated[id_stop].append(id_veh)
492
493            # if all allocated vehicles found their berth and all berths are free, then
494            # reset  alight allocation index
495            if len(self.ids_vehs_alight_allocated[id_stop]) == 0:
496                if np.all(berths.states[ids_berth_alight] == BERTHSTATES['free']):
497                    # print '    reset inds_berth_alight_allocated',self.inds_berth_alight_allocated[id_stop],'->',len(self.ids_berth_alight[id_stop])
498                    self.inds_berth_alight_allocated[id_stop] = len(self.ids_berth_alight[id_stop])
499
500                    # try to allocate unallocated vehicles
501                    ids_veh_remove = []
502                    for id_veh in self.ids_vehs_toallocate[id_stop]:
503                        id_berth = self.allocate_alight(id_stop)
504                        if id_berth < 0:
505                            # allocation failed
506                            # do nothing, vehicle continues to wait for allocation
507                            pass
508                        else:
509                            # command vehicle to go to berth for alighting
510                            # print '     send waiting vehicle id_veh %d to id_berth_alight %d'%(id_veh,id_berth)#,berths.stoppositions[id_berth]
511                            self.parent.prtvehicles.control_stop_alight(id_veh, id_stop, id_berth,
512                                                                        id_edge_sumo=self.ids_stop_to_ids_edge_sumo[id_stop],
513                                                                        position=self.get_berths(
514                                                                        ).stoppositions[id_berth],
515                                                                        )
516                            self.ids_vehs_alight_allocated[id_stop].append(id_veh)
517                            ids_veh_remove.append(id_veh)
518
519                    for id_veh in ids_veh_remove:
520                        self.ids_vehs_toallocate[id_stop].remove(id_veh)
521
522            # check whether allocated vehicles arrived at boarding berths
523            ids_veh_remove = []
524            for id_veh in self.ids_vehs_board_allocated[id_stop]:
525                # TODO: here we could also check vehicle position
526                if traci.vehicle.isStopped(vehicles.get_id_sumo(id_veh)):
527                    ids_veh_remove.append(id_veh)
528                    id_berth_board = vehicles.ids_berth[id_veh]
529                    berths.ids_veh[id_berth_board] = id_veh
530                    berths.states[id_berth_board] = BERTHSTATES['boarding']
531                    vehicles.board(id_veh,
532                                   id_edge_sumo=self.ids_stop_to_ids_edge_sumo[id_stop])
533
534            for id_veh in ids_veh_remove:
535                self.ids_vehs_board_allocated[id_stop].remove(id_veh)
536
537            # if all allocated vehicles for board area
538            # found their berth and all berths are free, then
539            # reset  allocation index
540            # 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]
541            if (self.inds_berth_board_allocated[id_stop] == 0) & (len(self.ids_vehs_board_allocated[id_stop]) == 0):
542
543                if np.all(berths.states[ids_berth_board] == BERTHSTATES['free']):
544                    # print '    reset inds_berth_board_allocated to',len(self.ids_berth_board[id_stop])
545                    self.inds_berth_board_allocated[id_stop] = len(self.ids_berth_board[id_stop])
546
547            # check for new person entering/left the station edge
548            ids_person_sumo = set(traci.edge.getLastStepPersonIDs(id_edge_sumo))
549            # for id_person_sumo in ids_person_sumo:
550            #    print '  id_person_sumo',id_person_sumo,traci.person.getRoadID(id_person_sumo)
551
552            if ids_person_sumo_prev != ids_person_sumo:
553
554                # deal with persons who left the edge
555                ids_person_sumo_left = ids_person_sumo_prev.difference(ids_person_sumo)
556                for id_person_sumo in ids_person_sumo_left:
557                    print '  id_person_sumo_left pers', id_person_sumo, traci.person.getRoadID(id_person_sumo)
558                    # print '  ids_person_sumo',ids_person_sumo
559                    # tricky: if the person who left the edge id_edge_sumo
560                    # shows still id_edge_sumo then this person is in a vehicle
561                    if traci.person.getRoadID(id_person_sumo) == id_edge_sumo:
562                        print '  person boarded: pers', id_person_sumo, traci.person.getLanePosition(id_person_sumo)
563                        self.ids_persons_sumo_boarded[id_stop].append(id_person_sumo)
564                        self.ids_persons_sumo_wait[id_stop].remove(id_person_sumo)
565                        self.numbers_person_wait[id_stop] -= 1
566
567                # deal with persons who entered the edge
568                ids_person_sumo_entered = ids_person_sumo.difference(ids_person_sumo_prev)
569                for id_person_sumo in ids_person_sumo_entered:
570                    print '  id_person_sumo_entered pers', id_person_sumo, traci.person.getRoadID(id_person_sumo)
571                    if self.id_person_to_origs_dests.has_key(id_person_sumo):
572                        id_edge_sumo_dests = self.id_person_to_origs_dests[id_person_sumo]
573                        # check if person still has a PRT trip
574                        if len(id_edge_sumo_dests) > 0:
575                            # check if next trip has origin edge equal to edge of this stop
576                            if id_edge_sumo_dests[0][2] == id_edge_sumo:
577                                self.ids_persons_sumo_wait[id_stop].append(id_person_sumo)
578                                self.numbers_person_wait[id_stop] += 1
579                            # else:
580                            #    print 'WARNING: person %s starts with % insted of %s.'%(id_person_sumo,id_edge_sumo_dests[0][2],id_edge_sumo)
581
582                self.ids_persons_sumo_prev[id_stop] = ids_person_sumo
583
584            if 0:
585                for id_person_sumo in ids_person_sumo_prev:
586                    print '    ids_person_sumo=%s pos = %.2f ' % (
587                        id_person_sumo, traci.person.getLanePosition(id_person_sumo))
588                print '    ids_persons_sumo_boarded', self.ids_persons_sumo_boarded[id_stop]
589
590            # check if boarding is completed in load area and program
591            ids_berth_board = self.ids_berth_board[id_stop][::-1]
592            for id_berth_board, id_veh in zip(
593                ids_berth_board,
594                berths.ids_veh[ids_berth_board],
595            ):
596                if id_veh >= 0:  # is there a waiting vehicle
597                    if vehicles.is_completed_boarding(id_veh):
598                        self.schedule_trip_occupied(id_stop,
599                                                    id_berth_board,
600                                                    id_veh,
601                                                    process.simtime)
602
603            # check if there are passengers in the vehicles which wait for
604            # alight allocate
605            # TODO: can be replaced by a single instruction
606            n_pax = 0
607            for id_veh in self.ids_vehs_alight_allocated[id_stop]+self.ids_vehs_toallocate[id_stop]:
608                if vehicles.states[id_veh] == VEHICLESTATES['occupiedtrip']:
609                    n_pax += 1
610            # print '    n_pax' ,n_pax
611            # check whether to foreward vehicles in boarding berth
612
613            # no foreward if all berth are free occupied vehicles
614            if np.all(berths.states[ids_berth_board] == BERTHSTATES['free']):
615                # print '    foreward all occupied id_stop,ids_berth_board',id_stop,ids_berth_board
616                #self.foreward_boardzone(id_stop, ids_berth_board)
617                self.times_lastboard[id_stop] = 10**4  # reset clock if all are free
618
619            # foreward if there are passengers in unallocated vehicles
620            elif n_pax > 0:
621                # print '  call foreward_boardzone unblock',n_pax
622                self.foreward_boardzone(id_stop, ids_berth_board, process.simtime)
623
624            elif process.simtime - self.times_lastboard[id_stop] > 40:
625                # print '  call foreward_boardzone timeout',process.simtime,self.times_lastboard[id_stop],process.simtime - self.times_lastboard[id_stop]
626                self.foreward_boardzone(id_stop, ids_berth_board, process.simtime)
627
628    def schedule_trip_occupied(self, id_stop, id_berth, id_veh, simtime):
629        # TODO: actually a berth method??
630        berths = self.get_berths()
631
632        print 'schedule_trip_occupied', id_stop, id_berth, id_veh
633        # identify whic of the boarding persons is in the
634        # vehicle which completed boarding
635        dist_min = np.inf
636        id_person_sumo_inveh = None
637        stoppos = berths.stoppositions[id_berth]
638
639        for id_person_sumo in self.ids_persons_sumo_boarded[id_stop]:
640            d = abs(stoppos - traci.person.getLanePosition(id_person_sumo))
641            if d < dist_min:
642                dist_min = d
643                id_person_sumo_inveh = id_person_sumo
644
645        if id_person_sumo_inveh is not None:
646            # program vehicle to person's destination
647            id_stop_orig, id_stop_dest, id_edge_sumo_from, id_edge_sumo_to = \
648                self.id_person_to_origs_dests[id_person_sumo_inveh].pop(0)
649            print '    found person', id_person_sumo_inveh, 'from', id_stop_orig, id_edge_sumo_from, 'to', id_edge_sumo_to, id_stop_dest
650            self.parent.vehicleman.push_occupiedtrip(id_veh, id_stop, id_stop_dest)
651
652            self.parent.prtvehicles.schedule_trip_occupied(id_veh, id_edge_sumo_to)
653            self.ids_persons_sumo_boarded[id_stop].remove(id_person_sumo_inveh)
654            self.times_lastboard[id_stop] = simtime
655            berths.states[id_berth] = BERTHSTATES['free']
656            berths.ids_veh[id_berth] = -1
657
658        else:
659            print 'WARNING: on stop %d edge %s, berth %d no person found inside vehicle %d'(
660                id_stop, self.ids_stop_to_ids_edge_sumo[id_stop], id_berth, id_veh)
661
662    def schedule_trip_empty(self, id_stop, id_berth, id_veh, simtime):
663
664        # TODO: actually a berth method??
665        berths = self.get_berths()
666
667        id_stop_target = self.parent.vehicleman.push_emptytrip(id_veh, id_stop)
668
669        # print 'schedule_trip_empty for',id_veh,' from',id_stop,'to',id_stop_target,id_edge_sumo_target
670        self.parent.prtvehicles.schedule_trip_empty(id_veh, self.ids_stop_to_ids_edge_sumo[id_stop_target])
671
672        berths.states[id_berth] = BERTHSTATES['free']
673        berths.ids_veh[id_berth] = -1
674
675    def foreward_boardzone(self, id_stop,  ids_berth_board, simtime):
676        print 'foreward_boardzone', id_stop, ids_berth_board
677        berths = self.get_berths()
678        #ids_berth_board = self.ids_berth_board[id_stop][::-1]
679        # inds_o berths.states[ids_berth_board] != BERTHSTATES['free']
680        for id_berth, state in zip(ids_berth_board, berths.states[ids_berth_board]):
681            print '    id_berth,boarding?,id_veh', id_berth, state == BERTHSTATES['boarding'], berths.ids_veh[id_berth]
682            if state == BERTHSTATES['boarding']:
683                self.schedule_trip_empty(id_stop, id_berth, berths.ids_veh[id_berth], simtime)
684
685        self.times_lastboard[id_stop] = 10**4  # reset last board counter
686
687    def enter(self, id_stop, id_veh):
688        # print 'enter id_stop, id_veh',id_stop, id_veh
689        self.ids_vehs[id_stop].append(id_veh)
690
691        # tell vehman that veh arrived
692        #self.numbers_veh_arr[id_stop] -= 1
693        self.parent.vehicleman.conclude_trip(id_veh, id_stop)
694
695        self.numbers_veh[id_stop] += 1
696        id_berth = self.allocate_alight(id_stop)
697        if id_berth < 0:
698            # allocation failed
699            # command vehicle to slow down and wait for allocation
700            self.ids_vehs_toallocate[id_stop].append(id_veh)
701            self.parent.prtvehicles.control_slow_down(id_veh)
702        else:
703            # command vehicle to go to berth for alighting
704            # print '     send entering vehicle id_veh %d to id_berth_alight %d'%(id_veh,id_berth)#,self.get_berths().stoppositions[id_berth]
705            self.parent.prtvehicles.control_stop_alight(id_veh, id_stop, id_berth,
706                                                        id_edge_sumo=self.ids_stop_to_ids_edge_sumo[id_stop],
707                                                        position=self.get_berths().stoppositions[id_berth],
708                                                        )
709            self.ids_vehs_alight_allocated[id_stop].append(id_veh)
710
711    def exit(self, id_stop, id_veh):
712        self.ids_vehs[id_stop].remove(id_veh)
713        self.numbers_veh[id_stop] -= 1
714
715    def allocate_alight(self, id_stop):
716        # print 'allocate_alight',id_stop, id_veh_sumo
717        #self.inds_berth_alight_allocated [id_stop] = len(self.ids_berth_alight[id_stop])
718        ind_berth = self.inds_berth_alight_allocated[id_stop]
719
720        if ind_berth == 0:
721            # no free berth :(
722            return -1
723        else:
724            ind_berth -= 1
725            self.inds_berth_alight_allocated[id_stop] = ind_berth
726            return self.ids_berth_alight[id_stop][ind_berth]
727
728    def allocate_board(self, id_stop):
729        # print 'allocate_alight',id_stop, id_veh_sumo
730        #self.inds_berth_alight_allocated [id_stop] = len(self.ids_berth_alight[id_stop])
731        ind_berth = self.inds_berth_board_allocated[id_stop]
732
733        if ind_berth == 0:
734            # no free berth :(
735            return -1
736        else:
737            ind_berth -= 1
738            self.inds_berth_board_allocated[id_stop] = ind_berth
739            return self.ids_berth_board[id_stop][ind_berth]
740
741    # def process_man(self, process):
742    #    self.timehorizon.get_value()
743
744    def get_berthqueues(self, id_stop):
745        # currently not used
746        print 'get_berthqueues', id_stop
747        # TODO: use stop angle and person angle to detect waiting persons
748        ids_berth_board = self.ids_berth_board[id_stop]
749        ids_person_sumo = self.ids_persons_sumo_prev[id_stop]
750        positions = np.zeros(len(ids_person_sumo), dtype=np.float32)
751        #stages  = np.zeros(len(ids_person_sumo),dtype = np.object)
752        angles = np.zeros(len(ids_person_sumo), dtype=np.float32)
753        stoppositions = self.get_berths().stoppositions[ids_berth_board]
754        bins = [stoppositions[0]-1.0]+list(stoppositions)
755
756        i = 0
757        for id_person_sumo in ids_person_sumo:
758            positions[i] = traci.person.getLanePosition(id_person_sumo)
759            #stages[i]  =  traci.person.getParameter(id_person_sumo,'stage')
760            angles[i] = traci.person.getAngle(id_person_sumo)
761            i += 1
762
763        queues, bins_after = np.histogram(positions, bins)
764        print '  ids_person_sumo', ids_person_sumo
765        print '  angles', angles
766        print '  stages', stages
767        print '  positions', positions
768        print '  bins', bins
769        print '  ids_berth_board=\n', ids_berth_board
770        print '  queues=\n', queues
771        return ids_berth_board, queues
772        #ids_persons_berth = np.zeros()
773        #queues_berth = np.zeros(len(ids_berth_board), dtype = np.int32)
774        # for id_berth, stopposition in stoppositions:
775        #    inds_person = np.abs(positions-stopposition)<1.0
776        #
777        #    #ids_persons_berth[id_berth] = ids_person_sumo[inds_person]
778
779    def make_from_net(self, mode='custom1'):
780        """
781        Make prt stop database from PT stops in network.
782        """
783        self.clear()
784        net = self.get_scenario().net
785        ptstops = net.ptstops
786        lanes = net.lanes
787        ids_ptstop = ptstops.get_ids()
788        id_mode_prt = net.modes.get_id_mode(mode)
789        ids_lane = ptstops.ids_lane[ids_ptstop]
790        #edgelengths = net.edges.lengths
791
792        for id_stop, modes_allow, position_from, position_to in zip(
793            ids_ptstop,
794            lanes.modes_allow[ids_lane],
795            ptstops.positions_from[ids_ptstop],
796            ptstops.positions_to[ids_ptstop],
797        ):
798            if id_mode_prt in modes_allow:
799                self.make(id_stop,
800                          position_from,
801                          position_to)
802
803    def make(self, id_ptstop, position_from, position_to):
804        """
805        Initialize a new prt stop and generate berth.
806        """
807        id_stop = self.add_row(ids_ptstop=id_ptstop)
808        ids_berth = self.get_berths().make(id_stop, position_from=position_from,
809                                           position_to=position_to)
810        n_berth = len(ids_berth)
811        n_berth_alight = int(0.5*n_berth)
812        n_berth_board = n_berth-n_berth_alight
813        self.ids_berth_alight[id_stop] = ids_berth[0:n_berth_alight]
814        self.ids_berth_board[id_stop] = ids_berth[n_berth_alight:n_berth]
815        return id_stop
816
817
818class PrtVehicles(am.ArrayObjman):
819
820    def __init__(self, ident, prtservices, **kwargs):
821        # print 'PrtVehicles vtype id_default',vtypes.ids_sumo.get_id_from_index('passenger1')
822        self._init_objman(ident=ident,
823                          parent=prtservices,
824                          name='PRT Veh.',
825                          info='PRT vehicle database. These are shared vehicles.',
826                          **kwargs)
827
828        self._init_attributes()
829
830    def _init_attributes(self):
831        vtypes = self.get_scenario().demand.vtypes
832        net = self.get_scenario().net
833
834        # TODO: add/update vtypes here
835        self.add_col(SumoIdsConf('Veh', xmltag='id'))
836
837        id_vtype = self.make_vtype()
838
839        self.add_col(am.IdsArrayConf('ids_vtype', vtypes,
840                                     id_default=id_vtype,
841                                     groupnames=['state'],
842                                     name='Veh. type',
843                                     info='PRT vehicle type.',
844                                     #xmltag = 'type',
845                                     ))
846
847        self.add_col(am.ArrayConf('states', default=VEHICLESTATES['init'],
848                                  dtype=np.int32,
849                                  choices=VEHICLESTATES,
850                                  name='state',
851                                  info='State of vehicle.',
852                                  ))
853
854        self.add_col(am.IdsArrayConf('ids_targetprtstop', self.parent.prtstops,
855                                     groupnames=['parameters'],
856                                     name='Target stop ID',
857                                     info='ID of current target PRT stop.',
858                                     ))
859
860        self.add_col(am.IdsArrayConf('ids_currentedge', net.edges,
861                                     groupnames=['state'],
862                                     name='Current edge ID',
863                                     info='Edge ID of most recent reported position.',
864                                     ))
865
866        self.add_col(am.IdsArrayConf('ids_targetedge', net.edges,
867                                     groupnames=['state'],
868                                     name='Target edge ID',
869                                     info='Target edge ID to be reached. This can be either intermediate target edges (), such as a compressor station.',
870                                     ))
871
872    def make_vtype(self):
873        id_vtype = self.get_scenario().demand.vtypes.add_vtype('PRT',
874                                                               accel=2.5,
875                                                               decel=2.5,
876                                                               sigma=1.0,
877                                                               length=3.5,
878                                                               width=1.6,
879                                                               height=1.7,
880                                                               number_persons=1,
881                                                               capacity_persons=1,
882                                                               dist_min=0.5,
883                                                               speed_max=10.0,
884                                                               emissionclass='HBEFA3/zero',
885                                                               mode='custom1',  # specifies mode for demand
886                                                               color=np.array((255, 240, 0, 255), np.float32)/255.0,
887                                                               shape_gui='evehicle',
888                                                               times_boarding=1.5,
889                                                               times_loading=20.0,
890                                                               sublane_alignment_lat='center',
891                                                               sublane_speed_max_lat=0.5,
892                                                               sublane_gap_min_lat=0.24,
893                                                               sublane_alignment_eager=1000000.0,
894                                                               )
895        return id_vtype
896
897    def prepare_sim(self, process):
898        print 'PrtVehicles.prepare_sim'
899        net = self.get_scenario().net
900        ptstops = net.ptstops
901        lanes = net.lanes
902        ids_edge_sumo = net.edges.ids_sumo
903        ids = self.get_ids()
904
905        self.ids_berth = -1*np.ones(np.max(ids)+1, dtype=np.int32)
906
907        # for id_veh, id_veh_sumo in zip(ids,self.ids_sumo[ids]):
908        #    traci.vehicle.subscribe(id_veh_sumo,
909        #                            [   traci.constants.VAR_ROAD_ID,
910        #                                traci.constants.VAR_POSITION,
911        #                                traci.constants.VAR_STOPSTATE,
912        #                                ])
913        return []  # currentlu vehicles are not updated
914
915    def process_step(self, process):
916        # print 'process_step',traci.vehicle.getSubscriptionResults()
917        pass
918        # VEHICLESTATES = {'init':0,'waiting':1,'boarding':2,'alighting':3,'emptytrip':4,'occupiedtrip':5,'forewarding':6}
919
920    def control_slow_down(self, id_veh, speed=1.0, time_slowdown=5):
921        traci.vehicle.slowDown(self.get_id_sumo(id_veh), speed, time_slowdown)
922        # pass
923
924    def control_stop_alight(self, id_veh, id_stop, id_berth,
925                            id_edge_sumo=None,
926                            position=None,
927                            ):
928        id_veh_sumo = self.get_id_sumo(id_veh)
929        p = traci.vehicle.getLanePosition(id_veh_sumo)
930        print 'control_stop_alight', id_veh_sumo, p, '->', position, 'id_berth', id_berth
931        #d = position - p
932        #v = traci.vehicle.getSpeed(id_veh_sumo)
933        #d_save = 1.0/(2*2.5)*(v**2)
934        # print '  v=',v
935        # print '  d,d_save',d,d_save
936        self.states[id_veh] = VEHICLESTATES['forewarding']
937        self.ids_berth[id_veh] = id_berth
938        traci.vehicle.setStop(self.get_id_sumo(id_veh),
939                              id_edge_sumo,
940                              pos=position,
941                              flags=0,
942                              laneIndex=1,
943                              )
944
945    def control_stop_board(self, id_veh, id_stop, id_berth,
946                           id_edge_sumo=None,
947                           position=None,
948                           ):
949
950        print 'control_stop_board', self.get_id_sumo(id_veh), id_stop, id_berth, id_edge_sumo, position
951
952        id_veh_sumo = self.get_id_sumo(id_veh)
953        # print 'control_stop_board',id_veh_sumo,traci.vehicle.getLanePosition(id_veh_sumo),'->',position,id_berth
954        self.ids_berth[id_veh] = id_berth
955        self.states[id_veh] = VEHICLESTATES['forewarding']
956        traci.vehicle.resume(id_veh_sumo)
957
958        traci.vehicle.setStop(id_veh_sumo,
959                              id_edge_sumo,
960                              startPos=position-4.0,
961                              pos=position,
962                              flags=2,  # park and trigger 1+2,#
963                              laneIndex=1,
964                              )
965
966    def alight(self, id_veh):
967        print 'alight', self.get_id_sumo(id_veh)
968        # TODO: necessary to keep copy of state?
969        self.states[id_veh] = VEHICLESTATES['alighting']
970        # traci.vehicle.getStopState(self.get_id_sumo(id_veh))
971        # VEHICLESTATES = {'init':0,'waiting':1,'boarding':2,'alighting'
972
973    def board(self, id_veh, id_edge_sumo=None, position=None):
974        print 'board', self.get_id_sumo(id_veh)
975        # TODO: necessary to keep copy of state?
976        self.states[id_veh] = VEHICLESTATES['boarding']
977        #id_veh_sumo = self.get_id_sumo(id_veh)
978        # print 'board',id_veh_sumo,'stopstate',bin(traci.vehicle.getStopState(id_veh_sumo ))[2:]
979        # print '  ',dir(traci.vehicle)
980        # traci.vehicle.getLastStepPersonIDs()
981        # traci.vehicle.getStopState(self.get_id_sumo(id_veh))
982        # VEHICLESTATES = {'init':0,'waiting':1,'boarding':2,'alighting'
983        #traci.vehicle.setRoute(id_veh_sumo, [id_edge_sumo])
984        # traci.vehicle.resume(id_veh_sumo)
985
986        # traci.vehicle.setStop(  self.get_id_sumo(id_veh),
987        #                        traci.vehicle.getRoadID(id_veh_sumo),
988        #                        pos = traci.vehicle.getLanePosition(id_veh_sumo),
989        #                        flags= 2,#
990        #                        laneIndex= 1,
991        #                        )
992        # print 'board ',id_veh_sumo, traci.vehicle.getStopState(id_veh_sumo )# bin(traci.vehicle.getStopState(id_veh_sumo ))[2:]
993
994    def is_completed_alighting(self, id_veh):
995        print 'is_completed_alighting', self.get_id_sumo(id_veh), self.states[id_veh], self.states[id_veh] == VEHICLESTATES['alighting'], traci.vehicle.getPersonNumber(
996            self.get_id_sumo(id_veh)), type(traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)))
997        if self.states[id_veh] == VEHICLESTATES['alighting']:
998            if traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)) == 0:
999                print '  is_completed_alighting', self.get_id_sumo(id_veh), 'completed alighting'
1000                self.states[id_veh] = VEHICLESTATES['waiting']
1001                return True
1002            else:
1003                return False
1004
1005        else:
1006            return True
1007
1008    def is_completed_boarding(self, id_veh):
1009        # 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)))
1010        if self.states[id_veh] == VEHICLESTATES['boarding']:
1011            if traci.vehicle.getPersonNumber(self.get_id_sumo(id_veh)) == 1:
1012                print 'is_completed_boarding', self.get_id_sumo(id_veh), 'completed boarding'
1013                self.states[id_veh] = VEHICLESTATES['waiting']
1014                return True
1015            else:
1016                False
1017
1018        else:
1019            return True
1020
1021    def schedule_trip_occupied(self, id_veh, id_edge_sumo_dest):
1022        print 'schedule_trip_occupied', self.get_id_sumo(id_veh), id_edge_sumo_dest
1023        self.states[id_veh] = VEHICLESTATES['occupiedtrip']
1024        traci.vehicle.changeTarget(self.get_id_sumo(id_veh), id_edge_sumo_dest)
1025
1026    def schedule_trip_empty(self, id_veh, id_edge_sumo_to):
1027        print 'schedule_trip_empty', self.get_id_sumo(id_veh), id_edge_sumo_to
1028        self.states[id_veh] = VEHICLESTATES['emptytrip']
1029        id_veh_sumo = self.get_id_sumo(id_veh)
1030        traci.vehicle.resume(id_veh_sumo)
1031        traci.vehicle.changeTarget(id_veh_sumo, id_edge_sumo_to)
1032
1033    def update_states(self, ids_veh):
1034        print 'update_states', ids_veh
1035        for id_veh in ids_veh:
1036            # getStopState
1037            #    returns information in regard to stopping:
1038            #    The returned integer is defined as 1 * stopped + 2 * parking
1039            #    + 4 * personTriggered + 8 * containerTriggered + 16 * isBusStop
1040            #    + 32 * isContainerStop
1041
1042            #isStopped, isStoppedTriggered
1043
1044            #
1045            binstate = traci.vehicle.getStopState(self.get_id_sumo(id_veh))
1046            print '  id_veh,binstate=', id_veh, bin(binstate)[2:]
1047
1048    def make(self, n=-1, length_veh_av=4.0):
1049        """
1050        Make n PRT vehicles
1051        If n = -1 then fill up stops with vehicles.
1052        """
1053        print 'PrtVehicles.make', n, length_veh_av
1054        self.clear()
1055        net = self.get_scenario().net
1056        ptstops = net.ptstops
1057        prtstops = self.parent.prtstops
1058        lanes = net.lanes
1059        ids_prtstop = prtstops.get_ids()
1060        ids_ptstop = prtstops.ids_ptstop[ids_prtstop]
1061        ids_veh = []
1062        for id_prt, id_edge, pos_from, pos_to in zip(
1063            ids_prtstop,
1064            lanes.ids_edge[ptstops.ids_lane[ids_ptstop]],
1065            ptstops.positions_from[ids_ptstop],
1066            ptstops.positions_to[ids_ptstop],
1067        ):
1068            # TODO: here we can select depos or distribute a
1069            # fixed number of vehicles or put them into berth
1070            # print '  ',pos_to,pos_from,int((pos_to-pos_from)/length_veh_av)
1071
1072            for i in range(int((pos_to-pos_from)/length_veh_av)):
1073                id_veh = self.add_row(ids_targetstop=id_prt,
1074                                      ids_currentedge=id_edge,
1075                                      )
1076
1077                self.ids_sumo[id_veh] = self.get_id_sumo(id_veh)
1078                ids_veh.append(id_veh)
1079
1080        return ids_veh
1081
1082    # def write_veh
1083    #
1084
1085    def get_scenario(self):
1086        return self.parent.get_scenario()
1087
1088    def get_vtypes(self):
1089        """
1090        Returns a set with all used PRT vehicle types.
1091        """
1092        # print 'Vehicles_individual.get_vtypes',self.cols.vtype
1093        return set(self.ids_vtype.get_value())
1094
1095    def get_id_sumo(self, id_veh):
1096        return 'prt.%s' % (id_veh)
1097
1098    def get_id_from_id_sumo(self, id_veh_sumo):
1099        prefix, idstr = id_veh_sumo.split('.')
1100        return int(idstr)
1101
1102    def get_ids_from_ids_sumo(self, ids_veh_sumo):
1103        n = len(ids_veh_sumo)
1104        ids = np.zeros(n, np.int32)
1105        for i in xrange(n):
1106            ids[i] = self.get_id_from_id_sumo(ids_veh_sumo[i])
1107        return ids
1108
1109    def get_id_line_xml(self):
1110        return 'prt'
1111
1112
1113class PrtTransits(StageTypeMixin):
1114    def __init__(self, ident, population, name='Ride on PRT', info='Ride on Personal Rapid Transit network.'):
1115        self.init_stagetype(ident,
1116                            population, name=name,
1117                            info=info,
1118                            )
1119        self._init_attributes()
1120
1121    def _init_attributes(self):
1122        edges = self.parent.get_net().edges
1123
1124        self.add_col(am.IdsArrayConf('ids_fromedge', edges,
1125                                     groupnames=['parameters'],
1126                                     name='Edge ID from',
1127                                     info='Edge ID of departure PRT station.',
1128                                     ))
1129
1130        self.add_col(am.IdsArrayConf('ids_toedge', edges,
1131                                     groupnames=['parameters'],
1132                                     name='Edge ID to',
1133                                     info='Edge ID of destination PRT station.',
1134                                     ))
1135
1136    def set_prtservice(self, prtservice):
1137        self.add(cm.ObjConf(prtservice, is_child=False, groups=['_private']))
1138
1139    def get_prtservice(self):
1140        return self.prtservice.get_value()
1141
1142    def prepare_planning(self):
1143        pass
1144
1145    def append_stage(self, id_plan, time_start=-1.0,
1146                     duration=0.0,
1147                     id_fromedge=-1, id_toedge=-1, **kwargs):
1148        """
1149        Appends a PRT transit stage to plan id_plan.
1150
1151        """
1152        # print 'PrtTransits.append_stage',id_stage
1153
1154        id_stage, time_end = StageTypeMixin.append_stage(self,
1155                                                         id_plan,
1156                                                         time_start,
1157                                                         durations=duration,
1158                                                         ids_fromedge=id_fromedge,
1159                                                         ids_toedge=id_toedge,
1160                                                         )
1161
1162        # add this stage to the vehicle database
1163        # ind_ride gives the index of this ride (within the same plan??)
1164        #ind_ride = self.parent.get_individualvehicles().append_ride(id_veh, id_stage)
1165        return id_stage, time_end
1166
1167    def to_xml(self, id_stage, fd, indent=0):
1168        # <ride from="1/3to0/3" to="0/4to1/4" lines="train0"/>
1169        net = self.parent.get_net()
1170        #ids_stoplane = net.ptstops.ids_lane
1171        #ids_laneedge = net.lanes.ids_edge
1172        ids_sumoedge = net.edges.ids_sumo
1173
1174        #ind = self.get_ind(id_stage)
1175        fd.write(xm.start('ride', indent=indent))
1176        fd.write(xm.num('from', ids_sumoedge[self.ids_fromedge[id_stage]]))
1177        fd.write(xm.num('to', ids_sumoedge[self.ids_toedge[id_stage]]))
1178        fd.write(xm.num('lines', 'prt'))
1179        # if self.cols.pos_edge_from[ind]>0:
1180        #    fd.write(xm.num('departPos', self.cols.pos_edge_from[ind]))
1181        # if self.cols.pos_edge_to[ind]>0:
1182        #    fd.write(xm.num('arrivalPos', self.cols.pos_edge_to[ind]))
1183
1184        fd.write(xm.stopit())  # ends stage
1185
1186
1187class VehicleMan(am.ArrayObjman):
1188    def __init__(self, ident, prtservices, **kwargs):
1189        self._init_objman(ident=ident,
1190                          parent=prtservices,
1191                          name='PRT vehicle management',
1192                          info='PRT vehicle management.',
1193                          #xmltag = ('additional','busStop','stopnames'),
1194                          version=0.0,
1195                          **kwargs)
1196
1197        self._init_attributes()
1198        self._init_constants()
1199
1200    def _init_attributes(self):
1201        self.add(cm.AttrConf('time_update', 30.0,
1202                             groupnames=['parameters'],
1203                             name='Man. update time',
1204                             info="Update time for vehicle management.",
1205                             unit='s',
1206                             ))
1207
1208        self.add(cm.AttrConf('time_horizon', 1200,
1209                             groupnames=['parameters'],
1210                             name='Time horizon',
1211                             info="Prediction time horizon of vehicle management.",
1212                             unit='s',
1213                             ))
1214
1215    # def set_stops(self,vehicleman):
1216    #    self.add( cm.ObjConf( stops, is_child = False,groups = ['_private']))
1217
1218    def get_stops(self):
1219        return self.parent.prtstops
1220
1221    def get_scenario(self):
1222        return self.parent.parent.get_scenario()
1223
1224    def prepare_sim(self, process):
1225        print 'VehicleMan.prepare_sim'
1226        net = self.get_scenario().net
1227        # station management
1228        # self.ids_stop_to_ids_edge_sumo = np.zeros(np.max(ids)+1,dtype = np.object)
1229        # vehicle management
1230        self.ids_stop = self.get_stops().get_ids()
1231        n_stoparray = np.max(self.ids_stop)+1
1232        self.numbers_veh_arr = np.zeros(n_stoparray, dtype=np.int32)
1233        return [(self.time_update.get_value(), self.process_step)]
1234
1235    def process_step(self, process):
1236        # roll time horizon
1237        pass
1238
1239    def push_occupiedtrip(self, id_veh, id_stop_from, id_stop_to):
1240        # print 'push_occupiedtrip from',id_veh, id_stop_from, id_stop_to
1241        # search closest stop
1242        stops = self.get_stops()
1243        self.numbers_veh_arr[id_stop_to] += 1
1244        # print '  to stop',id_stop
1245        return id_stop_to
1246
1247    def push_emptytrip(self, id_veh, id_stop):
1248        # print 'push_emptytrip id_veh,id_stop',id_veh,id_stop
1249        # search closest stop
1250        stops = self.get_stops()
1251        ids_stop = list(self.ids_stop)
1252        ids_stop.remove(id_stop)
1253        costs = (stops.numbers_person_wait[ids_stop]-stops.numbers_veh[ids_stop] -
1254                 self.numbers_veh_arr[ids_stop])/self.parent.times_stop_to_stop[id_stop, ids_stop]
1255
1256        id_stop_target = ids_stop[np.argmax(costs)]
1257        self.numbers_veh_arr[id_stop_target] += 1
1258        #id_stop_target = ids_stop[random.randint(0,len(ids_stop)-1)]
1259        # print '  to stop',id_stop
1260        return id_stop_target
1261
1262    def conclude_trip(self, id_veh, id_stop):
1263        self.numbers_veh_arr[id_stop] -= 1
1264
1265
1266class PrtService(cm.BaseObjman):
1267    def __init__(self, ident, demand=None,
1268                 name='PRT service', info='PRT service',
1269                 **kwargs):
1270            # print 'PrtService.__init__',name
1271
1272        self._init_objman(ident=ident, parent=demand,
1273                          name=name, info=info, **kwargs)
1274
1275        attrsman = self.set_attrsman(cm.Attrsman(self))
1276
1277        self._init_attributes()
1278        self._init_constants()
1279
1280    def _init_attributes(self):
1281        print 'PrtService._init_attributes', hasattr(self, 'prttransit')
1282        attrsman = self.get_attrsman()
1283        #self.virtualpop = attrsman.add( cm.ObjConf( virtualpop, is_child = False,groups = ['_private']))
1284        # if hasattr(self, 'prtvehicles'):
1285        #    self.delete('prtvehicles')
1286
1287        self.prtstops = attrsman.add(cm.ObjConf(PrtStops('prtstops', self)))
1288        self.prtvehicles = attrsman.add(cm.ObjConf(PrtVehicles('prtvehicles', self)))
1289        self.vehicleman = attrsman.add(cm.ObjConf(VehicleMan('vehicleman', self)))
1290
1291        # --------------------------------------------------------------------
1292        # prt rides table
1293        #self.add(cm.ObjConf(Transits('transits',self))   )
1294
1295        # if not hasattr(self,'prttransit'):
1296        virtualpop = self.parent.virtualpop
1297        prttransits = virtualpop.add_stagetype('prttransits', PrtTransits)
1298        # print '  prttransits =',prttransits,
1299        self.prttransits = attrsman.add(
1300            cm.ObjConf(prttransits, is_child=False),
1301            is_overwrite=False,
1302        )
1303        self.prttransits.set_prtservice(self)
1304
1305        # temporary attrfix
1306        #prtserviceconfig = self.parent.get_attrsman().prtservice
1307        #prtserviceconfig.groupnames = []
1308        #prtserviceconfig.add_groupnames(['demand objects'])
1309
1310    def _init_constants(self):
1311        # print 'PrtService._init_constants',self,self.parent
1312        # this will ensure that PRT vtypes will be exported to routes
1313        # self.parent.add_demandobject(self)
1314        self.times_stop_to_stop = None
1315
1316    def get_vtypes(self):
1317
1318        ids_vtypes = set(self.prtvehicles.ids_vtype.get_value())
1319        return ids_vtypes
1320
1321    def get_writexmlinfo(self, is_route=False):
1322        """
1323        Returns three array where the first array is the
1324        begin time of the first vehicle and the second array is the
1325        write function to be called for the respectice vehicle and
1326        the third array contains the vehicle ids
1327
1328        Method used to sort trips when exporting to route or trip xml file
1329        """
1330        print 'PRT.get_writexmlinfo'
1331
1332        # time of first PRT vehicle(s) to be inserted
1333        t_start = 0.0
1334
1335        # time betwenn insertion of consecutive vehicles at same stop
1336        t_delta = 10  # s
1337
1338        n_veh = len(self.prtvehicles)
1339        times_depart = np.zeros(n_veh, dtype=np.int32)
1340        writefuncs = np.zeros(n_veh, dtype=np.object)
1341        writefuncs[:] = self.write_prtvehicle_xml
1342        ids_veh = self.prtvehicles.get_ids()
1343
1344        id_edge_prev = -1
1345        i = 0
1346        t0 = t_start
1347        for id_edge in self.prtvehicles.ids_currentedge[ids_veh]:
1348            print '  id_edge, t_start, id_edge_prev', id_edge, t0, id_edge_prev
1349            times_depart[i] = t0
1350            t0 += t_delta
1351            if id_edge != id_edge_prev:
1352                t0 = t_start
1353                id_edge_prev = 1*id_edge
1354            i += 1
1355
1356        return times_depart, writefuncs, ids_veh
1357
1358    def write_prtvehicle_xml(self,  fd, id_veh, time_begin, indent=2):
1359        print 'write_prtvehicle_xml', id_veh, time_begin
1360        # TODO: actually this should go in prtvehicles
1361        #time_veh_wait_after_stop = 3600
1362        net = self.get_scenario().net
1363        #lanes = net.lanes
1364        edges = net.edges
1365        #ind_ride = rides.get_inds(id_stage)
1366        #id_veh = rides.ids_veh[id_stage]
1367        prtvehicles = self.prtvehicles
1368        #ptstops = net.ptstops
1369        #prtstops = self.parent.prtstops
1370        #ids_prtstop = prtstops.get_ids()
1371        #ids_ptstop = prtstops.ids_ptstop[id_stop]
1372        # lanes.ids_edge[ptstops.ids_lane[ids_ptstop]],
1373        #id_lane_from = parking.ids_lane[id_parking_from]
1374        #laneindex_from =  lanes.indexes[id_lane_from]
1375        #pos_from = parking.positions[id_parking_from]
1376
1377        #id_parking_to = rides.ids_parking_to[id_stage]
1378        #id_lane_to = parking.ids_lane[id_parking_to]
1379        #laneindex_to =  lanes.indexes[id_lane_to]
1380        #pos_to = parking.positions[id_parking_to]
1381
1382        # write unique veh ID to prevent confusion with other veh declarations
1383        fd.write(xm.start('vehicle id="%s"' % prtvehicles.get_id_sumo(id_veh), indent+2))
1384
1385        fd.write(xm.num('depart', '%d' % time_begin))
1386        fd.write(xm.num('type', self.parent.vtypes.ids_sumo[prtvehicles.ids_vtype[id_veh]]))
1387        fd.write(xm.num('line', prtvehicles.get_id_line_xml()))
1388        fd.write(xm.stop())
1389
1390        # write route
1391        fd.write(xm.start('route', indent+4))
1392        # print '  edgeindex[ids_edge]',edgeindex[ids_edge]
1393        fd.write(xm.arr('edges', [edges.ids_sumo[prtvehicles.ids_currentedge[id_veh]]]))
1394
1395        # does not seem to have an effect, always starts at base????
1396        fd.write(xm.num('departPos', 'base'))
1397        #fd.write(xm.num('departLane', laneindex_from ))
1398        fd.write(xm.stopit())
1399
1400        # write depart stop
1401        # fd.write(xm.start('stop',indent+4))
1402        #fd.write(xm.num('lane', edges.ids_sumo[lanes.ids_edge[id_lane_from]]+'_%d'%laneindex_from ))
1403        #fd.write(xm.num('duration', time_veh_wait_after_stop))
1404        #fd.write(xm.num('startPos', pos_from ))
1405        #fd.write(xm.num('endPos', pos_from + parking.lengths[id_parking_from]))
1406        #fd.write(xm.num('triggered', "True"))
1407        # fd.write(xm.stopit())
1408
1409        fd.write(xm.end('vehicle', indent+2))
1410
1411    def make_stops_and_vehicles(self, n_veh=-1):
1412        self.prtstops.make_from_net()
1413        self.prtvehicles.make(n_veh)
1414        self.make_times_stop_to_stop()
1415
1416    def prepare_sim(self, process):
1417        if self.times_stop_to_stop is None:
1418            self.make_times_stop_to_stop()
1419        updatedata = self.prtvehicles.prepare_sim(process)
1420
1421        updatedata += self.prtstops.prepare_sim(process)
1422        updatedata += self.vehicleman.prepare_sim(process)
1423        print 'PrtService.prepare_sim updatedata', updatedata
1424        return updatedata
1425
1426    # def process_step(self, process):
1427    #    self.prtvehicles.process_step(process)
1428    #    self.prtstops.process_step(process)
1429
1430    def update(self):
1431        pass
1432        # person
1433        # http://www.sumo.dlr.de/daily/pydoc/traci._person.html
1434        # interesting
1435        #getPosition3D(self, personID)
1436
1437        # get edge id
1438        # getRoadID(self, personID)
1439
1440        # next edge or empty string at end of stage
1441        #getNextEdge(self, personID)
1442
1443        # getLanePosition(self, personID)
1444
1445        # getWaitingTime(self, personID)
1446
1447        # vehicle
1448        # http://www.sumo.dlr.de/daily/pydoc/traci._vehicle.html
1449
1450        # interesting:
1451
1452        # The vehicle's destination edge is set to the given edge id. The route is rebuilt.
1453        #changeTarget(id_sumo_veh, id_sumo_edge)
1454
1455        # getRoute
1456        # getRoute(string) -> list(string)
1457        # returns the ids of the edges the vehicle's route is made of.
1458
1459        #rerouteTraveltime(self, vehID, currentTravelTimes=True)
1460
1461        # getStopState
1462        #        getStopState(string) -> integer
1463
1464        # or
1465        #'isAtBusStop', 'isAtContainerStop', 'isRouteValid', 'isStopped', 'isStoppedParking', 'isStoppedTriggered',
1466
1467        # setBusStop(self, vehID, stopID, duration=2147483647, until=-1, flags=0)
1468
1469        #setStop(self, vehID, edgeID, pos=1.0, laneIndex=0, duration=2147483647, flags=0, startPos=-1001.0, until=-1)
1470
1471        # to make it a follower or leader
1472        #setType(self, vehID, typeID)
1473
1474        # to reroute over compressors.....reroute after setting via!!
1475        # setVia(self, vehID, edgeList)
1476
1477        # slow down in stops
1478        # slowDown(self, vehID, speed, duration)
1479
1480        #traci.vehicle.setSpeed(id_veh_last, v_parking)
1481        #traci.vehicle.setRoute(id_veh_last, route_return)
1482        #traci.vehicle.setStop(id_veh_last, edgeid_return, pos= edgelength_return-20.0, laneIndex=0, duration = 15000)
1483
1484        # edge
1485        #getLastStepPersonIDs(self, edgeID)
1486        # getLastStepPersonIDs(string) -> list(string)
1487
1488        # Returns the ids of the persons on the given edge during the last time step.
1489
1490        #getLastStepVehicleIDs(self, edgeID)
1491        # getLastStepVehicleIDs(string) -> list(string)
1492
1493    def make_plans_prt(self, ids_person=None, mode='custom1'):
1494        # routing necessary?
1495        landuse = self.get_landuse()
1496        facilities = landuse.facilities
1497
1498        scenario = self.parent.get_scenario()
1499        vp = self.parent.get_scenario().demand.virtualpop
1500
1501        edges = scenario.net.edges
1502        lanes = scenario.net.lanes
1503        modes = scenario.net.modes
1504
1505        walks = vp.get_walks()
1506        prttransits = self.prttransits
1507        #transits = vp.get_transits()
1508        #ptstops = vp.get_ptstops()
1509        #ptlines = vp.get_ptlines()
1510
1511        #ptfstar = ptlinks.get_fstar()
1512        #pttimes = ptlinks.get_times()
1513        #stops_to_enter, stops_to_exit = ptlinks.get_stops_to_enter_exit()
1514
1515        #ids_stoplane = ptstops.ids_lane
1516        #ids_laneedge = scenario.net.lanes.ids_edge
1517
1518        times_est_plan = vp.plans.get_value().times_est
1519        # here we can determine edge weights for different modes
1520
1521        # centralize:
1522        walks.prepare_planning()
1523        prttransits.prepare_planning()
1524
1525        if ids_person is None:
1526            # print '  ids_mode_preferred',self.ids_mode_preferred.value
1527            # print '  private',MODES['private']
1528            # print '  ',self.ids_mode_preferred == MODES['private']
1529
1530            ids_person = self.select_ids(
1531                self.ids_mode_preferred.get_value() == modes.get_id_mode(mode),
1532            )
1533
1534        ids_plan = vp.add_plans(ids_person)
1535
1536        n_plans = len(ids_plan)
1537
1538        print 'make_plans_prt n_plans=', n_plans
1539
1540        #ids_veh = self.get_individualvehicles().assign_to_persons(ids_person)
1541        inds_pers = self.get_inds(ids_person)
1542        # self.persons.cols.mode_preferred[inds_pers]='private'
1543
1544        times_start = self.times_start.value[inds_pers]
1545        inds_fac_home = facilities.get_inds(self.ids_fac_home.value[inds_pers])
1546        inds_fac_activity = facilities.get_inds(self.ids_fac_activity.value[inds_pers])
1547
1548        centroids_home = facilities.centroids.value[inds_fac_home]
1549        centroids_activity = facilities.centroids.value[inds_fac_activity]
1550
1551        ids_edge_home = facilities.ids_roadedge_closest.value[inds_fac_home]
1552        poss_edge_home = facilities.positions_roadedge_closest.value[inds_fac_home]
1553
1554        ids_edge_activity = facilities.ids_roadedge_closest.value[inds_fac_activity]
1555        poss_edge_activity = facilities.positions_roadedge_closest.value[inds_fac_activity]
1556
1557        # find closest prt stop!!
1558        ids_stop_home = ptstops.get_closest(centroids_home)
1559        ids_stop_activity = ptstops.get_closest(centroids_activity)
1560
1561        ids_stopedge_home = ids_laneedge[ids_stoplane[ids_stop_home]]
1562        ids_stopedge_activity = ids_laneedge[ids_stoplane[ids_stop_activity]]
1563
1564        poss_stop_home = 0.5*(ptstops.positions_from[ids_stop_home]
1565                              + ptstops.positions_to[ids_stop_home])
1566        poss_stop_activity = 0.5*(ptstops.positions_from[ids_stop_activity]
1567                                  + ptstops.positions_to[ids_stop_activity])
1568
1569        i = 0
1570        for id_person, id_plan, time_start, id_edge_home, pos_edge_home, id_edge_activity, pos_edge_activity, id_stop_home, id_stopedge_home, pos_stop_home, id_stop_activity, id_stopedge_activity, pos_stop_activity\
1571                in zip(ids_person, ids_plan, times_start,  ids_edge_home, poss_edge_home, ids_edge_activity, poss_edge_activity, ids_stop_home, ids_stopedge_home, poss_stop_home, ids_stop_activity, ids_stopedge_activity, poss_stop_activity):
1572            self.plans.value.set_row(id_plan, ids_person=id_person)
1573
1574            print 79*'_'
1575            print '  id_plan=%d, id_person=%d, ' % (id_plan, id_person)
1576
1577            id_stage_walk1, time = walks.append_stage(id_plan, time_start,
1578                                                      id_edge_from=id_edge_home,
1579                                                      position_edge_from=pos_edge_home,
1580                                                      id_edge_to=id_stopedge_home,
1581                                                      position_edge_to=pos_stop_home,  # -7.0,
1582                                                      )
1583
1584            # print '    id_stopedge_home',id_stopedge_home
1585            # print '    pos_stop_home',pos_stop_home
1586
1587            # print
1588            # print '    id_stopedge_activity',id_stopedge_activity
1589            # print '    pos_stop_activity',pos_stop_activity
1590            # print
1591            # print '    id_stop_home',id_stop_home
1592            # print '    id_stop_activity',id_stop_activity
1593
1594            durations, linktypes, ids_line, ids_fromstop, ids_tostop =\
1595                ptlinks.route(id_stop_home, id_stop_activity,
1596                              fstar=ptfstar, times=pttimes,
1597                              stops_to_enter=stops_to_enter,
1598                              stops_to_exit=stops_to_exit)
1599
1600            # print '  routing done. make plan..'
1601
1602            if len(linktypes) > 0:
1603                if linktypes[-1] == type_walk:  # is last stage a walk?
1604                        # remove it, because will go directly to destination
1605                    linktypes = linktypes[:-1]
1606                    ids_line = ids_line[:-1]
1607                    durations = durations[:-1]
1608                    ids_fromstop = ids_fromstop[:-1]
1609                    ids_tostop = ids_tostop[:-1]
1610
1611            # print '  ids_line    ',ids_line
1612            # print '  ids_fromstop',ids_fromstop
1613            # print '  ids_tostop  ',ids_tostop
1614
1615            if len(linktypes) > 0:  # is there any public transport line to take?
1616
1617                # go though PT links and generate transits and walks to trasfer
1618                ids_stopedge_from = ids_laneedge[ids_stoplane[ids_fromstop]]
1619                ids_stopedge_to = ids_laneedge[ids_stoplane[ids_tostop]]
1620                poss_stop_from = 0.5*(ptstops.positions_from[ids_fromstop]
1621                                      + ptstops.positions_to[ids_fromstop])
1622                poss_stop_to = 0.5*(ptstops.positions_from[ids_tostop]
1623                                    + ptstops.positions_to[ids_tostop])
1624
1625                # this is wait time buffer to be added to the successive stage
1626                # as waiting is currently not modelled as an extra stage
1627                duration_wait = 0.0
1628
1629                # create stages for PT
1630                for linktype, id_line, duration,\
1631                    id_stopedge_from, pos_fromstop,\
1632                    id_stopedge_to, pos_tostop in\
1633                        zip(linktypes,
1634                            ids_line,
1635                            durations,
1636                            ids_stopedge_from, poss_stop_from,
1637                            ids_stopedge_to, poss_stop_to,
1638                            ):
1639                    # print '    stage for linktype %2d fromedge %s toedge %s'%(linktype, edges.ids_sumo[id_stopedge_from],edges.ids_sumo[id_stopedge_to] )
1640
1641                    if linktype == type_transit:  # transit!
1642                        id_stage_transit, time = transits.append_stage(
1643                            id_plan, time,
1644                            id_line=id_line,
1645                            duration=duration+duration_wait,
1646                            id_fromedge=id_stopedge_from,
1647                            id_toedge=id_stopedge_to,
1648                        )
1649                        duration_wait = 0.0
1650
1651                    elif linktype == type_walk:  # walk to transfer
1652
1653                        id_stage_transfer, time = walks.append_stage(
1654                            id_plan, time,
1655                            id_edge_from=id_stopedge_from,
1656                            position_edge_from=pos_fromstop,
1657                            id_edge_to=id_stopedge_to,
1658                            position_edge_to=pos_tostop,
1659                            duration=duration+duration_wait,
1660                        )
1661                        duration_wait = 0.0
1662
1663                    else:  # all other link time are no modelld
1664                        # do not do anything , just add wait time to next stage
1665                        duration_wait += duration
1666
1667                # walk from final stop to activity
1668                # print '    Stage for linktype %2d fromedge %s toedge %s'%(linktype, edges.ids_sumo[id_stopedge_to],edges.ids_sumo[id_edge_activity] )
1669                id_stage_walk2, time = walks.append_stage(id_plan, time,
1670                                                          id_edge_from=id_stopedge_to,
1671                                                          position_edge_from=pos_tostop,
1672                                                          id_edge_to=id_edge_activity,
1673                                                          position_edge_to=pos_edge_activity,
1674                                                          )
1675
1676            else:
1677                # there is no public transport line linking these nodes.
1678                # Modify walk directly from home to activity
1679                time = walks.modify_stage(id_stage_walk1, time_start,
1680                                          id_edge_from=id_edge_home,
1681                                          position_edge_from=pos_edge_home,
1682                                          id_edge_to=id_edge_activity,
1683                                          position_edge_to=pos_edge_activity,
1684                                          )
1685
1686            # store time estimation for this plan
1687            times_est_plan[id_plan] = time-time_start
1688
1689    def make_times_stop_to_stop(self, fstar=None, times=None):
1690        # print 'make_times_stop_to_stop'
1691        if fstar is None:
1692            fstar = self.get_fstar()
1693            times = self.get_times(fstar)
1694
1695        if len(fstar) == 0:
1696            self.times_stop_to_stop = [[]]
1697            return
1698
1699        ids_prtstop = self.prtstops.get_ids()
1700
1701        ids_edge = self.prtstops.get_edges(ids_prtstop)
1702        # print '  ids_prtstop,ids_edge',ids_prtstop,ids_edge
1703        n_elem = np.max(ids_prtstop)+1
1704        stop_to_stop = np.zeros((n_elem, n_elem), dtype=np.int32)
1705
1706        ids_edge_to_ids_prtstop = np.zeros(np.max(ids_edge)+1, dtype=np.int32)
1707        ids_edge_to_ids_prtstop[ids_edge] = ids_prtstop
1708
1709        ids_edge_target = set(ids_edge)
1710
1711        for id_stop, id_edge in zip(ids_prtstop, ids_edge):
1712            # print '    id_stop, id_edge',id_stop, id_edge
1713
1714            # remove origin from target
1715            ids_edge_target.discard(id_edge)
1716
1717            costs, routes = edgedijkstra(id_edge,
1718                                         ids_edge_target=ids_edge_target,
1719                                         weights=times, fstar=fstar
1720                                         )
1721
1722            # print '    ids_edge_target',ids_edge_target
1723            # print '    costs\n',   costs
1724            # print '    routes\n',   routes
1725
1726            # TODO: could be vectorialized, but not so easy
1727            for id_edge_target in ids_edge_target:
1728                #stop_to_stop[id_edge,id_edge_target] = costs[id_edge_target]
1729                # 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]
1730                # stop_to_stop[ids_edge_to_ids_prtstop[[id_edge,id_edge_target]]]=costs[id_edge_target]
1731                stop_to_stop[ids_edge_to_ids_prtstop[id_edge],
1732                             ids_edge_to_ids_prtstop[id_edge_target]] = costs[id_edge_target]
1733
1734            # put back origin to targets (probably not the best way)
1735            ids_edge_target.add(id_edge)
1736            # print '    ids_edge_target (all)',ids_edge_target
1737
1738            # print '    stop_to_stop',stop_to_stop
1739            # TODO: here we could also store the routes
1740
1741        self.times_stop_to_stop = stop_to_stop
1742        self.ids_edge_to_ids_prtstop = ids_edge_to_ids_prtstop
1743        # print '  times_stop_to_stop=\n',self.times_stop_to_stop
1744
1745    def get_fstar(self):
1746        """
1747        Returns the forward star graph of the network as dictionary:
1748            fstar[id_fromedge] = set([id_toedge1, id_toedge2,...])
1749        """
1750        # print 'get_fstar'
1751        net = self.get_scenario().net
1752        # prt mode
1753        id_mode = net.modes.get_id_mode('custom1')
1754
1755        #ids_edge = self.get_ids()
1756        #fstar = np.array(np.zeros(np.max(ids_edge)+1, np.obj))
1757        fstar = {}
1758        connections = net.connections
1759        lanes = net.lanes
1760        inds_con = connections.get_inds()
1761        ids_fromlane = connections.ids_fromlane.get_value()[inds_con]
1762        ids_modes_allow = lanes.modes_allow[ids_fromlane]
1763        ids_fromedge = lanes.ids_edge[ids_fromlane]
1764        ids_toedge = lanes.ids_edge[connections.ids_tolane.get_value()[inds_con]]
1765        # print '  ids_fromedge',ids_fromedge
1766        # print '  ids_modes_allow',ids_modes_allow
1767        for id_fromedge, id_toedge, ids_mode_allow in\
1768                zip(ids_fromedge, ids_toedge, ids_modes_allow):
1769            if len(ids_mode_allow) > 0:
1770                if ids_mode_allow[-1] == id_mode:
1771                    if fstar.has_key(id_fromedge):
1772                        fstar[id_fromedge].add(id_toedge)
1773                    else:
1774                        fstar[id_fromedge] = set([id_toedge])
1775
1776        return fstar
1777
1778    def get_times(self, fstar):
1779        """
1780        Returns freeflow travel times for all edges.
1781        The returned array represents the speed and the index corresponds to
1782        edge IDs.
1783
1784        """
1785        if len(fstar) == 0:
1786            return []
1787
1788        net = self.get_scenario().net
1789        #id_mode = net.modes.get_id_mode(mode)
1790        id_mode = net.modes.get_id_mode('custom1')
1791        # print 'get_times id_mode,is_check_lanes,speed_max',id_mode,is_check_lanes,speed_max
1792        ids_edge = np.array(fstar.keys(), dtype=np.int32)
1793
1794        times = np.array(np.zeros(np.max(ids_edge)+1, np.float32))
1795        speeds = net.edges.speeds_max[ids_edge]
1796
1797        # limit allowed speeds with max speeds of mode
1798        speeds = np.clip(speeds, 0.0, net.modes.speeds_max[id_mode])
1799
1800        times[ids_edge] = net.edges.lengths[ids_edge]/speeds
1801
1802        return times
1803
1804    def get_scenario(self):
1805        return self.parent.parent
1806
1807    def get_landuse(self):
1808        return self.get_scenario().landuse
1809
1810    def make_plans_prt(self, ids_person=None, mode='custom1'):
1811        # routing necessary?
1812
1813        scenario = self.get_scenario()
1814        edges = scenario.net.edges
1815        lanes = scenario.net.lanes
1816        modes = scenario.net.modes
1817
1818        landuse = scenario.landuse
1819        facilities = landuse.facilities
1820
1821        virtualpop = self.parent.virtualpop
1822        walks = virtualpop.get_walks()
1823        #transits = virtualpop.get_transits()
1824
1825        #ptstops = virtualpop.get_ptstops()
1826        #ptlines = virtualpop.get_ptlines()
1827
1828        #ptlinks = ptlines.ptlinks.get_value()
1829        #ptlinktypes = ptlinks.types.choices
1830        #type_enter = ptlinktypes['enter']
1831        #type_transit = ptlinktypes['transit']
1832        #type_board = ptlinktypes['board']
1833        #type_alight = ptlinktypes['alight']
1834        #type_transfer = ptlinktypes['transfer']
1835        #type_walk = ptlinktypes['walk']
1836        #type_exit = ptlinktypes['exit']
1837
1838        #ptfstar = ptlinks.get_fstar()
1839        #pttimes = ptlinks.get_times()
1840        #stops_to_enter, stops_to_exit = ptlinks.get_stops_to_enter_exit()
1841
1842        #ids_prtstoplane = ptstops.ids_lane
1843        #ids_laneedge = scenario.net.lanes.ids_edge
1844
1845        if self.times_stop_to_stop is None:
1846            self.make_times_stop_to_stop()
1847
1848        times_est_plan = virtualpop.plans.get_value().times_est
1849        # here we can determine edge weights for different modes
1850        walks.prepare_planning()
1851        # transits.prepare_planning()
1852
1853        if ids_person is None:
1854            # print '  ids_mode_preferred',self.ids_mode_preferred.value
1855            # print '  private',MODES['private']
1856            # print '  ',self.ids_mode_preferred == MODES['private']
1857
1858            ids_person = virtualpop.select_ids(
1859                virtualpop.ids_mode_preferred.get_value() == modes.get_id_mode(mode),
1860            )
1861
1862        ids_plan = virtualpop.add_plans(ids_person)
1863
1864        n_plans = len(ids_plan)
1865
1866        print 'make_plans_prt n_plans=', n_plans
1867
1868        #ids_veh = self.get_individualvehicles().assign_to_persons(ids_person)
1869        inds_pers = virtualpop.get_inds(ids_person)
1870        # self.persons.cols.mode_preferred[inds_pers]='private'
1871
1872        times_start = virtualpop.times_start.value[inds_pers]
1873        inds_fac_home = facilities.get_inds(virtualpop.ids_fac_home.value[inds_pers])
1874        inds_fac_activity = facilities.get_inds(virtualpop.ids_fac_activity.value[inds_pers])
1875
1876        centroids_home = facilities.centroids.value[inds_fac_home]
1877        centroids_activity = facilities.centroids.value[inds_fac_activity]
1878
1879        ids_edge_home = facilities.ids_roadedge_closest.value[inds_fac_home]
1880        poss_edge_home = facilities.positions_roadedge_closest.value[inds_fac_home]
1881
1882        ids_edge_activity = facilities.ids_roadedge_closest.value[inds_fac_activity]
1883        poss_edge_activity = facilities.positions_roadedge_closest.value[inds_fac_activity]
1884
1885        ids_stop_home, ids_stopedge_home = self.prtstops.get_closest(centroids_home)
1886        ids_stop_activity, ids_stopedge_activity = self.prtstops.get_closest(centroids_activity)
1887
1888        poss_stop_home = self.prtstops.get_waitpositions(ids_stop_home, is_alight=False, offset=-0.5)
1889        poss_stop_activity = self.prtstops.get_waitpositions(ids_stop_activity, is_alight=True, offset=-0.5)
1890
1891        #ids_stopedge_home = ids_laneedge[ids_stoplane[ids_stop_home]]
1892        #ids_stopedge_activity = ids_laneedge[ids_stoplane[ids_stop_activity]]
1893
1894        # poss_stop_home = 0.5*(  ptstops.positions_from[ids_stop_home]\
1895        #                        +ptstops.positions_to[ids_stop_home])
1896        # poss_stop_activity = 0.5*(  ptstops.positions_from[ids_stop_activity]\
1897        #                            +ptstops.positions_to[ids_stop_activity])
1898
1899        i = 0
1900        for id_person, id_plan, time_start, id_edge_home, pos_edge_home, id_edge_activity, pos_edge_activity, id_stop_home, id_stopedge_home, pos_stop_home, id_stop_activity, id_stopedge_activity, pos_stop_activity\
1901                in zip(ids_person, ids_plan, times_start,  ids_edge_home, poss_edge_home, ids_edge_activity, poss_edge_activity, ids_stop_home, ids_stopedge_home, poss_stop_home, ids_stop_activity, ids_stopedge_activity, poss_stop_activity):
1902
1903            # can be done before??
1904            virtualpop.plans.value.set_row(id_plan, ids_person=id_person)
1905            #virtualpop.plans.value.ids_person[id_plan] = id_person
1906            print 79*'_'
1907            print '  id_plan=%d, id_person=%d, ' % (id_plan, id_person)
1908
1909            # are nearest stops different?
1910            if id_stop_home != id_stop_activity:
1911                # so walk to prt stop
1912                id_stage_walk1, time = walks.append_stage(id_plan, time_start,
1913                                                          id_edge_from=id_edge_home,
1914                                                          position_edge_from=pos_edge_home,
1915                                                          id_edge_to=id_stopedge_home,
1916                                                          position_edge_to=pos_stop_home,
1917                                                          )
1918                # take PRT
1919                # self.ids_edge_to_ids_prtstop
1920                id_stopedge_home, pos_stop_home
1921                id_stage_transit, time = self.prttransits.append_stage(
1922                    id_plan, time,
1923                    duration=self.times_stop_to_stop[id_stop_home, id_stop_activity],
1924                    id_fromedge=id_stopedge_home,
1925                    id_toedge=id_stopedge_activity,
1926                )
1927
1928                # walk from PRT stop to activity
1929                id_stage_transfer, time = walks.append_stage(
1930                    id_plan, time,
1931                    id_edge_from=id_stopedge_activity,
1932                    position_edge_from=pos_stop_activity,  # should be -1 to indicate whatever position. Check in toxml
1933                    id_edge_to=id_edge_activity,
1934                    position_edge_to=pos_edge_activity,
1935                )
1936
1937            else:
1938                # origin and destination stop are identical.
1939                # walk directly from home to activity
1940                time = walks.append_stage(id_plan, time_start,
1941                                          id_edge_from=id_edge_home,
1942                                          position_edge_from=pos_edge_home,
1943                                          id_edge_to=id_edge_activity,
1944                                          position_edge_to=pos_edge_activity,
1945                                          )
1946
1947            # store time estimation for this plan
1948            times_est_plan[id_plan] = time-time_start
1949
1950
1951class SumoPrt(sumo.SumoTraci):
1952    """
1953    SUMO simulation process with interactive control of PRT vehicles.
1954    """
1955
1956    def _init_special(self, **kwargs):
1957        """
1958        Special initializations. To be overridden.
1959        """
1960        # prtservices = None
1961        self._prtservice = kwargs['prtservice']
1962
1963    def run_cml(self, cml):
1964        cmllist = cml.split(' ')
1965        print 'PRT.run_cml', cmllist
1966        traci.start(cmllist)
1967        self.simtime = self.simtime_start
1968        self.duration = 1.0+self.simtime_end-self.simtime_start
1969        self.get_attrsman().status.set('running')
1970        print '  traci started', self.get_attrsman().status.get()
1971
1972        simobjects = self._prtservice.prepare_sim(self)
1973        self.simobjects = []
1974        for time_sample, simfunc in simobjects:
1975            self.simobjects.append([0, time_sample, simfunc])
1976        # print '  simobjects=',self.simobjects
1977        return True
1978
1979    def process_step(self):
1980        # print 'process_step time=',self.simtime
1981        # self._prtservice.process_step(self)
1982
1983        i = 0
1984        for time_last, time_sample, simfunc in self.simobjects:
1985            if self.simtime-time_last > time_sample:
1986                self.simobjects[i][0] += time_sample
1987                simfunc(self)
1988            i += 1
1989