eflips-depot 3.0.0__py3-none-any.whl → 3.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of eflips-depot might be problematic. Click here for more details.

eflips/depot/.DS_Store ADDED
Binary file
@@ -724,11 +724,7 @@ def add_evaluation_to_database(
724
724
  else:
725
725
  for process in process_log:
726
726
  match process.status:
727
- case ProcessStatus.CANCELLED:
728
- raise NotImplementedError(
729
- f"Cancelled processes {process.ID} are not implemented."
730
- )
731
- case ProcessStatus.COMPLETED:
727
+ case ProcessStatus.COMPLETED | ProcessStatus.CANCELLED:
732
728
  assert (
733
729
  len(process.starts) == 1 and len(process.ends) == 1
734
730
  ), (
@@ -754,13 +750,38 @@ def add_evaluation_to_database(
754
750
  }
755
751
  else:
756
752
  # Duration is 0
757
-
758
- if start_time in dict_of_events:
759
- assert current_area.issink is True
753
+ if current_area.issink is True:
760
754
  # Standby departure
761
- actual_start_time = dict_of_events[start_time][
762
- "end"
763
- ]
755
+ if start_time in dict_of_events:
756
+ # Actual start time should be the end time of the other positive duration
757
+ # process starting at the same time
758
+ actual_start_time = dict_of_events[
759
+ start_time
760
+ ]["end"]
761
+ else:
762
+ for other_process in process_log:
763
+ if (
764
+ other_process.dur > 0
765
+ and len(other_process.ends) != 0
766
+ ):
767
+ actual_start_time = (
768
+ other_process.ends[0]
769
+ )
770
+ else:
771
+ # Invalid standby before a process in progress will be ignored
772
+ continue
773
+
774
+ last_standby_departure_start = actual_start_time
775
+
776
+ # If this standby event lasts actually 0 seconds, it is not a real event
777
+ if (
778
+ actual_start_time in dict_of_events.keys()
779
+ and dict_of_events[actual_start_time][
780
+ "type"
781
+ ]
782
+ == "trip"
783
+ ):
784
+ continue
764
785
  dict_of_events[actual_start_time] = {
765
786
  "type": type(process).__name__,
766
787
  "area": current_area.ID,
@@ -769,11 +790,12 @@ def add_evaluation_to_database(
769
790
  "id": process.ID,
770
791
  }
771
792
 
772
- last_standby_departure_start = actual_start_time
773
-
774
793
  else:
775
794
  # Standby arrival
776
- assert current_area.issink is False
795
+ assert current_area.issink is False, (
796
+ f"A bus cannot go from Area {current_area.ID} to other areas. A Parking Area"
797
+ f" for standby arrival should be added."
798
+ )
777
799
  dict_of_events[start_time] = {
778
800
  "type": type(process).__name__,
779
801
  "area": current_area.ID,
@@ -782,9 +804,30 @@ def add_evaluation_to_database(
782
804
  "id": process.ID,
783
805
  }
784
806
  case ProcessStatus.IN_PROGRESS:
785
- raise NotImplementedError(
786
- f"Current process {process.ID} is in progress. Not implemented yet."
787
- )
807
+ assert (
808
+ len(process.starts) == 1 and len(process.ends) == 0
809
+ ), f"Current process {process.ID} is marked IN_PROGRESS, but has an end."
810
+ current_area = area_log[start_time]
811
+ current_slot = slot_log[start_time]
812
+
813
+ if current_area is None or current_slot is None:
814
+ raise ValueError(
815
+ f"For process {process.ID} Area and slot should not be None."
816
+ )
817
+
818
+ if process.dur > 0:
819
+ # Valid duration
820
+ dict_of_events[start_time] = {
821
+ "type": type(process).__name__,
822
+ "end": process.etc,
823
+ "area": current_area.ID,
824
+ "slot": current_slot,
825
+ "id": process.ID,
826
+ }
827
+ else:
828
+ raise NotImplementedError(
829
+ "We believe this should never happen. If it happens, handle it here."
830
+ )
788
831
  case ProcessStatus.WAITING:
789
832
  raise NotImplementedError(
790
833
  f"Current process {process.ID} is waiting. Not implemented yet."
@@ -137,7 +137,7 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
137
137
  "ismandatory": True,
138
138
  "vehicle_filter": {},
139
139
  # True if this process can be interrupted by a dispatch. False if it cannot be interrupted
140
- "cancellable_for_dispatch": False,
140
+ "cancellable_for_dispatch": process.dispatchable,
141
141
  }
142
142
 
143
143
  match process_type(process):
eflips/depot/depot.py CHANGED
@@ -1,11 +1,10 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """
3
- Created on Fri Oct 13 11:12:00 2017
3
+ Created on Fri Oct 13 11:12:00 2017.
4
4
 
5
5
  @author: P.Mundt, E.Lauth
6
6
 
7
7
  Core components of the depot simulation model.
8
-
9
8
  """
10
9
 
11
10
  from collections import Counter
@@ -43,6 +42,7 @@ from warnings import warn
43
42
 
44
43
  class DepotWorkingData:
45
44
  """Data container for communication between vehicle and depot.
45
+
46
46
  Attribute of a SimpleVehicle object.
47
47
 
48
48
  Parameters:
@@ -87,7 +87,9 @@ class DepotWorkingData:
87
87
 
88
88
  @property
89
89
  def current_slot(self):
90
- """Return slot number [int] if on an area, else None. The lowest slot
90
+ """Return slot number [int] if on an area, else None.
91
+
92
+ The lowest slot
91
93
  number is 0. Note that slot_no is not the list index.
92
94
  """
93
95
  if self.current_area is not None:
@@ -97,7 +99,8 @@ class DepotWorkingData:
97
99
 
98
100
  @property
99
101
  def current_charging_interface(self):
100
- """Return [ChargingInterface] object of current slot on current area,
102
+ """Return [ChargingInterface] object of current slot on current area,.
103
+
101
104
  if any, else None.
102
105
  """
103
106
  if self.current_area is not None and self.current_area.charging_interfaces:
@@ -108,7 +111,8 @@ class DepotWorkingData:
108
111
 
109
112
  @property
110
113
  def etc_processes(self):
111
- """Return simulation time estimate [int or EstimateValue] of when all
114
+ """Return simulation time estimate [int or EstimateValue] of when all.
115
+
112
116
  currently active processes will be completed.
113
117
  """
114
118
  etcs = [process.etc for process in self.active_processes]
@@ -134,7 +138,8 @@ class DepotWorkingData:
134
138
 
135
139
  @property
136
140
  def etc_processes_uncertain(self):
137
- """Return estimate [int or EstimateValue] of when all currently active
141
+ """Return estimate [int or EstimateValue] of when all currently active.
142
+
138
143
  processes will be completed. Unlike self.etc_processes, get a rough
139
144
  estimate if an etc value is EstimateValue.UNKNOWN.
140
145
  """
@@ -203,7 +208,8 @@ class BackgroundStoreGet(FilterStoreExtGet):
203
208
 
204
209
 
205
210
  class BackgroundStore(FilterStoreExt):
206
- """Virtual area for background tasks that require functionalities such as
211
+ """Virtual area for background tasks that require functionalities such as.
212
+
207
213
  logging.
208
214
  """
209
215
 
@@ -216,7 +222,9 @@ class BackgroundStore(FilterStoreExt):
216
222
  get = BoundClass(BackgroundStoreGet)
217
223
 
218
224
  def select(self, ID, print_missing=True):
219
- """Return the item with *ID* in self.items. Return None if the ID is
225
+ """Return the item with *ID* in self.items.
226
+
227
+ Return None if the ID is
220
228
  not found.
221
229
  Relies on *ID* to be unique.
222
230
 
@@ -252,7 +260,9 @@ class UnassignedTrips(SortedList):
252
260
 
253
261
 
254
262
  class Depot:
255
- """Representation of a depot. Owns all related objects such as areas
263
+ """Representation of a depot.
264
+
265
+ Owns all related objects such as areas
256
266
  and the DepotControl as attributes.
257
267
  A depot must be created and configured using a DepotConfigurator and may
258
268
  be empty upon instantation. Before simulation start, the validity of the
@@ -377,14 +387,16 @@ class Depot:
377
387
 
378
388
  @property
379
389
  def vacant_accessible(self):
380
- """Return the sum of slots that are unoccupied and accessible
390
+ """Return the sum of slots that are unoccupied and accessible.
391
+
381
392
  (not blocked) from the default entrance side on all areas.
382
393
  """
383
394
  return sum(area.vacant_accessible for area in self.list_areas)
384
395
 
385
396
  @property
386
397
  def count(self):
387
- """Return the amount of vehicles in the depot, i.e. the amount of
398
+ """Return the amount of vehicles in the depot, i.e. the amount of.
399
+
388
400
  occupied slots.
389
401
  """
390
402
  return self._count
@@ -397,14 +409,16 @@ class Depot:
397
409
 
398
410
  @property
399
411
  def max_count(self):
400
- """Return the maximum number of vehicles that were in the depot at the
412
+ """Return the maximum number of vehicles that were in the depot at the.
413
+
401
414
  same time.
402
415
  """
403
416
  return self._max_count
404
417
 
405
418
  @property
406
419
  def maxOccupiedSlots(self):
407
- """Return the total number of slots that have been occupied up to this
420
+ """Return the total number of slots that have been occupied up to this.
421
+
408
422
  point of time. Only functional if the simulation is run with the GUI.
409
423
  """
410
424
  totalUsedSlots = 0
@@ -414,7 +428,8 @@ class Depot:
414
428
 
415
429
  @property
416
430
  def urgent_trips(self):
417
- """Return a list of trips that are urgent: due or delayed and no
431
+ """Return a list of trips that are urgent: due or delayed and no.
432
+
418
433
  vehicle assigned to.
419
434
  Relies on self.unassigned_trips to be sorted by std.
420
435
  """
@@ -430,7 +445,8 @@ class Depot:
430
445
 
431
446
  @property
432
447
  def overdue_trips(self):
433
- """Return a list of trips that are overdue, i.e. have not started yet
448
+ """Return a list of trips that are overdue, i.e. have not started yet.
449
+
434
450
  although the target departure time has passed. Includes trips that have
435
451
  a scheduled vehicle.
436
452
  """
@@ -442,12 +458,14 @@ class Depot:
442
458
 
443
459
  def checkin(self, vehicle):
444
460
  """Redirect to checkin() of the depot's depot_control.
461
+
445
462
  Don't add code here.
446
463
  """
447
464
  self.depot_control.checkin(vehicle)
448
465
 
449
466
  def checkout(self, vehicle):
450
467
  """Redirect to checkout() of the depot's depot_control.
468
+
451
469
  Don't add code here.
452
470
  """
453
471
  self.depot_control.checkout(vehicle)
@@ -477,7 +495,9 @@ class BaseDispatchStrategy(ABC):
477
495
 
478
496
  @staticmethod
479
497
  def next_trips(depot):
480
- """Return depot.urgent_trips, if not empty. Else return a list
498
+ """Return depot.urgent_trips, if not empty.
499
+
500
+ Else return a list
481
501
  containing only the unassigned trip with the lowest scheduled time of
482
502
  departure (std), if any. Otherwise return an empty list.
483
503
  Relies on depot.unassigned_trips to be sorted by std.
@@ -636,7 +656,8 @@ class BaseDispatchStrategy(ABC):
636
656
 
637
657
  @staticmethod
638
658
  def trigger_until_found(depot, trip, interval=60):
639
- """Periodically trigger the *depot*'s dispatch until a vehicle is found
659
+ """Periodically trigger the *depot*'s dispatch until a vehicle is found.
660
+
640
661
  for *trip*. This triggering should be used for special cases only and
641
662
  *interval* [s] should be set with care due to a potentially high
642
663
  impact on sim time.
@@ -666,6 +687,7 @@ class DSFirst(BaseDispatchStrategy):
666
687
  @staticmethod
667
688
  def get_pending_vehicles(depot, trip):
668
689
  """Return a dict of pending vehicles.
690
+
669
691
  A vehicle is pending if it's at an area in a parking area group and has
670
692
  no trip assigned to, or its assigned trip's std is later than
671
693
  *trip*.std.
@@ -708,6 +730,7 @@ class DSFirst(BaseDispatchStrategy):
708
730
  @staticmethod
709
731
  def find_match(depot):
710
732
  """Find a matching vehicle for the next trip.
733
+
711
734
  Is called recursively while DSFirst.try_assign is successful.
712
735
  """
713
736
 
@@ -762,7 +785,9 @@ class DSFirst(BaseDispatchStrategy):
762
785
 
763
786
  @staticmethod
764
787
  def try_assign(vehicle, trip, depot):
765
- """Try to match *vehicle* and *trip*. Return True if successful, else
788
+ """Try to match *vehicle* and *trip*.
789
+
790
+ Return True if successful, else
766
791
  False.
767
792
  """
768
793
  urgent = trip.vehicle is None and trip.std <= trip.env.now
@@ -833,6 +858,7 @@ class DSSmart(BaseDispatchStrategy):
833
858
  @staticmethod
834
859
  def get_suitable_vehicles(depot, trip, vf):
835
860
  """Return a list of vehicles that are suitable for *trip*.
861
+
836
862
  A vehicle is suitable if it's at an area in a parking area group and
837
863
  has no trip assigned to, or its assigned trip's std is later than
838
864
  *trip*.std. Furthermore, a vehicle must pass all criteria of *vf*. For
@@ -882,6 +908,7 @@ class DSSmart(BaseDispatchStrategy):
882
908
  @staticmethod
883
909
  def find_match(depot):
884
910
  """Find a matching vehicle for the next trip.
911
+
885
912
  Is called recursively while successful.
886
913
  """
887
914
  next_trips = DSSmart.next_trips(depot)
@@ -963,7 +990,8 @@ class DSSmart(BaseDispatchStrategy):
963
990
 
964
991
  @staticmethod
965
992
  def scheduling_delay(env, trip):
966
- """Return the interval [int] from now until *lead_time_match* before
993
+ """Return the interval [int] from now until *lead_time_match* before.
994
+
967
995
  departure time of *trip*. Return 0 if the departure time is in less
968
996
  than *lead_time_match*.
969
997
  """
@@ -977,6 +1005,7 @@ class DSSmart(BaseDispatchStrategy):
977
1005
 
978
1006
  class DepotControl:
979
1007
  """Control of vehicle movement and actions in the depot.
1008
+
980
1009
  Instantiated as attribute of the corresponding depot.
981
1010
 
982
1011
  Attributes:
@@ -990,8 +1019,10 @@ class DepotControl:
990
1019
  dispatch_strategies = {DSFirst.name: DSFirst, DSSmart.name: DSSmart}
991
1020
 
992
1021
  parking_congestion_event_cls = None
993
- """Event that succeeds if parking congestion occurs because no slot can be
994
- found at a parking area upon request."""
1022
+ """Event that succeeds if parking congestion occurs because no slot can be.
1023
+
1024
+ found at a parking area upon request.
1025
+ """
995
1026
 
996
1027
  def __init__(self, env, depot, dispatch_strategy_name="FIRST"):
997
1028
  self.depot = depot
@@ -1004,7 +1035,8 @@ class DepotControl:
1004
1035
  self.departure_areas = AreaGroup(self.env, [], "departure_areas")
1005
1036
 
1006
1037
  def _complete(self):
1007
- """Actions that must take place before simulation start, but may not be
1038
+ """Actions that must take place before simulation start, but may not be.
1039
+
1008
1040
  possible during the depot configuration phase.
1009
1041
  """
1010
1042
  # Set self.process_request based on prioritize_init_store option. Not
@@ -1017,7 +1049,7 @@ class DepotControl:
1017
1049
 
1018
1050
  @property
1019
1051
  def dispatch_strategy_name(self):
1020
- """'name' attribute of set dispatch strategy"""
1052
+ """'name' attribute of set dispatch strategy."""
1021
1053
  return self._dispatch_strategy_name
1022
1054
 
1023
1055
  @dispatch_strategy_name.setter
@@ -1030,7 +1062,7 @@ class DepotControl:
1030
1062
 
1031
1063
  @property
1032
1064
  def dispatch_strategy(self):
1033
- """[BaseDispatchStrategy] subclass"""
1065
+ """[BaseDispatchStrategy] subclass."""
1034
1066
  return self._dispatch_strategy
1035
1067
 
1036
1068
  @dispatch_strategy.setter
@@ -1042,7 +1074,8 @@ class DepotControl:
1042
1074
  )
1043
1075
 
1044
1076
  def checkin(self, vehicle):
1045
- """Reset and set several variables upon arrival of a vehicle and
1077
+ """Reset and set several variables upon arrival of a vehicle and.
1078
+
1046
1079
  trigger follow-up actions.
1047
1080
  """
1048
1081
  # if vehicle.trip_at_departure is not vehicle.trip:
@@ -1088,7 +1121,9 @@ class DepotControl:
1088
1121
  vehicle.dwd.plan = self.depot.default_plan.copy()
1089
1122
 
1090
1123
  def proceed(self, vehicle):
1091
- """Main function to move a vehicle to the next area. Splits into
1124
+ """Main function to move a vehicle to the next area.
1125
+
1126
+ Splits into
1092
1127
  proceed_area and proceed_group for further actions.
1093
1128
  """
1094
1129
  # an = vehicle.dwd.current_area.ID if vehicle.dwd.current_area is not None else None
@@ -1124,8 +1159,9 @@ class DepotControl:
1124
1159
  current_area.trigger_get(None)
1125
1160
  self.trigger_dispatch()
1126
1161
 
1127
- if globalConstants["general"]["LOG_ATTRIBUTES"]:
1128
- current_area.logger.steplog()
1162
+ # This was removed cause it triggered an Exception after fixing #90
1163
+ # if globalConstants["general"]["LOG_ATTRIBUTES"]:
1164
+ # current_area.logger.steplog()
1129
1165
 
1130
1166
  def proceed_area(self, vehicle, current_area, next_area):
1131
1167
  """Proceed with *next_area* as a BaseArea subtype."""
@@ -1295,6 +1331,7 @@ class DepotControl:
1295
1331
 
1296
1332
  def get_process_need(self, vehicle, area):
1297
1333
  """Check what processes a vehicle will request at an area.
1334
+
1298
1335
  Return a list of processes by ID to request, which can be empty.
1299
1336
  """
1300
1337
  flexprint(
@@ -1320,7 +1357,8 @@ class DepotControl:
1320
1357
  return process_IDs
1321
1358
 
1322
1359
  def run_processes(self, vehicle, process_IDs):
1323
- """Initialize and run processes marked in process_IDs for a vehicle at
1360
+ """Initialize and run processes marked in process_IDs for a vehicle at.
1361
+
1324
1362
  an area.
1325
1363
  If more than one process is requested, then all processes will be
1326
1364
  started simultaneously. Call proceed() when all processes have
@@ -1437,7 +1475,8 @@ class DepotControl:
1437
1475
  self.trigger_dispatch()
1438
1476
 
1439
1477
  def trigger_dispatch(self):
1440
- """Trigger the matching of trips and vehicles of
1478
+ """Trigger the matching of trips and vehicles of.
1479
+
1441
1480
  self.dispatch_strategy.
1442
1481
  """
1443
1482
  self.dispatch_strategy.trigger(self.depot)
@@ -1452,8 +1491,10 @@ class DepotControl:
1452
1491
  self.env.process(self.process_request(trip, filter=filter))
1453
1492
 
1454
1493
  def register_for_dispatch(self, trip):
1455
- """Prepare processing the vehicle request for *trip* by the regular
1456
- dispatch."""
1494
+ """Prepare processing the vehicle request for *trip* by the regular.
1495
+
1496
+ dispatch.
1497
+ """
1457
1498
  self.depot.pending_departures.append(trip)
1458
1499
  self.env.process(self.schedule_for_matching(trip))
1459
1500
 
@@ -1473,6 +1514,7 @@ class DepotControl:
1473
1514
 
1474
1515
  def process_request_prio_parking(self, trip, filter=lambda item: True):
1475
1516
  """Return a vehicle from the depot for which *filter* returns True.
1517
+
1476
1518
  If there is no matching vehicle available immediately at
1477
1519
  self.departure_areas, try to get a vehicle from depot.init_store.
1478
1520
  If there is no matching vehicle either, issue requests at
@@ -1546,6 +1588,7 @@ class DepotControl:
1546
1588
 
1547
1589
  def process_request_prio_init(self, trip, filter=lambda item: True):
1548
1590
  """Return a vehicle from the depot for which *filter* returns True.
1591
+
1549
1592
  Try to get a vehicle from depot.init_store first. If there is no
1550
1593
  match available, issue requests at self.departure_areas.
1551
1594
  """
@@ -1597,6 +1640,7 @@ def assert_after_checkout(env, vehicle):
1597
1640
 
1598
1641
  def update_relations(put_event):
1599
1642
  """Updater to be called as callback of a successful put event for an area.
1643
+
1600
1644
  Relies on a corresponding get event to succeed in the same simulation
1601
1645
  time step.
1602
1646
  """
@@ -1612,7 +1656,9 @@ def update_relations(put_event):
1612
1656
 
1613
1657
 
1614
1658
  class BaseArea(ABC):
1615
- """Abstract base class for an area in a depot. Subclasses must inherit from
1659
+ """Abstract base class for an area in a depot.
1660
+
1661
+ Subclasses must inherit from
1616
1662
  a SimPy Store subclass that has attributes 'capacity', 'items' and
1617
1663
  'vacant'.
1618
1664
 
@@ -1647,7 +1693,6 @@ class BaseArea(ABC):
1647
1693
  slot_orientation: [str] orientation of slots. Used for display purposes
1648
1694
  in the GUI only, has no functional purpose. See
1649
1695
  gui.area_view.DepotAreaView for documentation.
1650
-
1651
1696
  """
1652
1697
 
1653
1698
  @abstractmethod
@@ -1700,7 +1745,8 @@ class BaseArea(ABC):
1700
1745
 
1701
1746
  @property
1702
1747
  def vacant_accessible(self):
1703
- """Helper function to distinguish between direct and line areas for the
1748
+ """Helper function to distinguish between direct and line areas for the.
1749
+
1704
1750
  determination of vacant accessible slots.
1705
1751
  """
1706
1752
  if isinstance(self, LineArea):
@@ -1714,7 +1760,8 @@ class BaseArea(ABC):
1714
1760
 
1715
1761
  @property
1716
1762
  def charge_proc(self):
1717
- """Return the type of the Charge or subclass process available at this
1763
+ """Return the type of the Charge or subclass process available at this.
1764
+
1718
1765
  area. Return None if there is None. Relies on the assumption that there
1719
1766
  is only one charging process at an area at max.
1720
1767
  """
@@ -1737,14 +1784,13 @@ class BaseArea(ABC):
1737
1784
 
1738
1785
  @property
1739
1786
  def scheduledVehicles(self):
1740
- """
1741
- Returns list of scheduled vehicles (trips assigned).
1742
- """
1787
+ """Returns list of scheduled vehicles (trips assigned)."""
1743
1788
  return [vehicle for vehicle in self.vehicle if vehicle.trip is not None]
1744
1789
 
1745
1790
  @property
1746
1791
  def count_rfd(self):
1747
- """Return the number of vehicles that are ready for departure at this
1792
+ """Return the number of vehicles that are ready for departure at this.
1793
+
1748
1794
  area.
1749
1795
  """
1750
1796
  return sum(item.dwd.isrfd for item in self.vehicles)
@@ -1752,12 +1798,14 @@ class BaseArea(ABC):
1752
1798
  @property
1753
1799
  @abstractmethod
1754
1800
  def count_rfd_unblocked(self):
1755
- """Return the number of vehicles that are ready for departure and not
1801
+ """Return the number of vehicles that are ready for departure and not.
1802
+
1756
1803
  blocked from departure at this area.
1757
1804
  """
1758
1805
 
1759
1806
  def istypestack(self, substitution=True):
1760
- """Return a tuple (istypestack [bool or None], vehicle_type
1807
+ """Return a tuple (istypestack [bool or None], vehicle_type.
1808
+
1761
1809
  [VehicleType or None]).
1762
1810
 
1763
1811
  istypestack is True if all vehicles are of the same type. vehicle_type
@@ -1779,7 +1827,8 @@ class BaseArea(ABC):
1779
1827
  return None, None
1780
1828
 
1781
1829
  def istypestack_with(self, vehicle, substitution=True):
1782
- """Return True if current vehicles would be a typestack together with
1830
+ """Return True if current vehicles would be a typestack together with.
1831
+
1783
1832
  *vehicle*. Extenuate the comparison with subsitutable types if
1784
1833
  *substitution* is True. Return None if the area is empty. Otherwise
1785
1834
  return False.
@@ -1795,7 +1844,9 @@ class BaseArea(ABC):
1795
1844
  return all(vi.vehicle_type is vehicle.vehicle_type for vi in self.vehicles)
1796
1845
 
1797
1846
  def select(self, ID, print_missing=True):
1798
- """Return the item with *ID* in self.items. Return None if the ID is
1847
+ """Return the item with *ID* in self.items.
1848
+
1849
+ Return None if the ID is
1799
1850
  not found.
1800
1851
  Relies on *ID* to be unique.
1801
1852
 
@@ -1840,7 +1891,8 @@ class BaseAreaPut(ABC):
1840
1891
  if globalConstants["general"]["LOG_ATTRIBUTES"]:
1841
1892
 
1842
1893
  def calc_waiting_time(request):
1843
- """Log the waiting time from init until success of *request*
1894
+ """Log the waiting time from init until success of *request*.
1895
+
1844
1896
  in the vehicle's data logger.
1845
1897
  """
1846
1898
  waiting_time = request.item.env.now - request.init_time
@@ -1893,11 +1945,12 @@ class BaseAreaGet(ABC):
1893
1945
 
1894
1946
 
1895
1947
  class DirectAreaPut(BaseAreaPut, ExclusiveRequest, StorePutExt):
1896
- """Request to put *item* into the *store*. The request is triggered once
1948
+ """Request to put *item* into the *store*.
1949
+
1950
+ The request is triggered once
1897
1951
  there is space for the item in the store.
1898
1952
 
1899
1953
  Callbacks by BaseAreaPut are added last.
1900
-
1901
1954
  """
1902
1955
 
1903
1956
  def __init__(self, store, item, other_requests=None):
@@ -1907,7 +1960,9 @@ class DirectAreaPut(BaseAreaPut, ExclusiveRequest, StorePutExt):
1907
1960
 
1908
1961
 
1909
1962
  class DirectAreaGet(BaseAreaGet, ExclusiveRequest, FilterStoreExtGet):
1910
- """Request to get an *item* from the *store* matching the *filter*. The
1963
+ """Request to get an *item* from the *store* matching the *filter*.
1964
+
1965
+ The
1911
1966
  request is triggered once there is such an item available in the store.
1912
1967
 
1913
1968
  *filter* is a function receiving one item. It should return ``True`` for
@@ -1916,7 +1971,6 @@ class DirectAreaGet(BaseAreaGet, ExclusiveRequest, FilterStoreExtGet):
1916
1971
  :class:`StoreGet`.
1917
1972
 
1918
1973
  Callbacks by BaseAreaGet are added last.
1919
-
1920
1974
  """
1921
1975
 
1922
1976
  def __init__(self, store, filter=lambda item: True, other_requests=None, **kwargs):
@@ -1926,11 +1980,11 @@ class DirectAreaGet(BaseAreaGet, ExclusiveRequest, FilterStoreExtGet):
1926
1980
 
1927
1981
 
1928
1982
  class DirectArea(BaseArea, PositionalFilterStore):
1929
- """Depot area where vehicles don't block each other and therefore direct
1983
+ """Depot area where vehicles don't block each other and therefore direct.
1984
+
1930
1985
  access on all stored vehicles is possible.
1931
1986
 
1932
1987
  See parent class descriptions for more details.
1933
-
1934
1988
  """
1935
1989
 
1936
1990
  def __init__(
@@ -1963,20 +2017,21 @@ class DirectArea(BaseArea, PositionalFilterStore):
1963
2017
 
1964
2018
  @property
1965
2019
  def pendingVehicles(self):
1966
- """
1967
- Returns list of pending vehicles (potentially blocking vehicles).
1968
- """
2020
+ """Returns list of pending vehicles (potentially blocking vehicles)."""
1969
2021
  return []
1970
2022
 
1971
2023
  @property
1972
2024
  def count_rfd_unblocked(self):
1973
- """Return the number of vehicles that are ready for departure and not
2025
+ """Return the number of vehicles that are ready for departure and not.
2026
+
1974
2027
  blocked from departure at this area.
1975
2028
  """
1976
2029
  return sum(vehicle.dwd.isrfd for vehicle in self.vehicles)
1977
2030
 
1978
2031
  def slot_no(self, item):
1979
- """Return the slot number of *item*. Return None if item is not in
2032
+ """Return the slot number of *item*.
2033
+
2034
+ Return None if item is not in
1980
2035
  self.items.
1981
2036
  Unlike list indexing, slot number counting starts at 1. Therefore only
1982
2037
  meant for display purposes, otherwise use items.index(vehicle).
@@ -1988,20 +2043,22 @@ class DirectArea(BaseArea, PositionalFilterStore):
1988
2043
 
1989
2044
  @staticmethod
1990
2045
  def index2slot_no(index):
1991
- """Convert *index* (correct value for indexing list self.items) to slot
1992
- number (for display purposes)."""
2046
+ """Convert *index* (correct value for indexing list self.items) to slot.
2047
+
2048
+ number (for display purposes).
2049
+ """
1993
2050
  return index + 1
1994
2051
 
1995
2052
 
1996
2053
  class LineAreaPut(BaseAreaPut, ExclusiveRequest, LineStorePut):
1997
- """Request to put *item* onto deepest accessible slot from *side* in
2054
+ """Request to put *item* onto deepest accessible slot from *side* in.
2055
+
1998
2056
  *store*. The request is triggered once there is accessible space for the
1999
2057
  item in the store.
2000
2058
 
2001
2059
  side: [str] Same as in LineStorePut.
2002
2060
 
2003
2061
  Callbacks by BaseAreaPut are added last.
2004
-
2005
2062
  """
2006
2063
 
2007
2064
  def __init__(self, store, item, side="default", other_requests=None):
@@ -2011,7 +2068,8 @@ class LineAreaPut(BaseAreaPut, ExclusiveRequest, LineStorePut):
2011
2068
 
2012
2069
 
2013
2070
  class LineAreaGet(BaseAreaGet, ExclusiveRequest, LineFilterStoreGet):
2014
- """Request to get the first accessible *item* from *side* in *store*
2071
+ """Request to get the first accessible *item* from *side* in *store*.
2072
+
2015
2073
  matching *filter*(item). The request is triggered once there is an
2016
2074
  accessible item available in the store.
2017
2075
 
@@ -2024,7 +2082,6 @@ class LineAreaGet(BaseAreaGet, ExclusiveRequest, LineFilterStoreGet):
2024
2082
  interface for customization.
2025
2083
 
2026
2084
  Callbacks by BaseAreaGet are added last.
2027
-
2028
2085
  """
2029
2086
 
2030
2087
  def __init__(
@@ -2036,14 +2093,14 @@ class LineAreaGet(BaseAreaGet, ExclusiveRequest, LineFilterStoreGet):
2036
2093
 
2037
2094
 
2038
2095
  class LineArea(BaseArea, LineFilterStore):
2039
- """Depot area where vehicles are arranged in a line and can block each
2096
+ """Depot area where vehicles are arranged in a line and can block each.
2097
+
2040
2098
  other (feature of LineFilterStore). Therefore not all stored vehicles may
2041
2099
  be directly accessible.
2042
2100
  See parent class descriptions for more details.
2043
2101
 
2044
2102
  At the moment, only areas where issink is True (e.g. typically False for a
2045
2103
  service area, True for parking areas) can reliably be of this area type.
2046
-
2047
2104
  """
2048
2105
 
2049
2106
  def __init__(
@@ -2086,14 +2143,13 @@ class LineArea(BaseArea, LineFilterStore):
2086
2143
 
2087
2144
  @property
2088
2145
  def pendingVehicles(self):
2089
- """
2090
- Returns list of pending vehicles (potentially blocking vehicles).
2091
- """
2146
+ """Returns list of pending vehicles (potentially blocking vehicles)."""
2092
2147
  return [item for item in self.items if item is not None and item.trip is None]
2093
2148
 
2094
2149
  @property
2095
2150
  def count_rfd_unblocked(self):
2096
- """Return the number of vehicles that are ready for departure and not
2151
+ """Return the number of vehicles that are ready for departure and not.
2152
+
2097
2153
  blocked from departure at this area.
2098
2154
  """
2099
2155
  return sum(
@@ -2101,7 +2157,9 @@ class LineArea(BaseArea, LineFilterStore):
2101
2157
  )
2102
2158
 
2103
2159
  def slot_no(self, item):
2104
- """Return the slot number of *item*. Return None if item is not in
2160
+ """Return the slot number of *item*.
2161
+
2162
+ Return None if item is not in
2105
2163
  self.items.
2106
2164
  The slot closest to the front has the lowest number, which is the
2107
2165
  reverse of items.index. Unlike list indexing, slot number counting
@@ -2114,8 +2172,10 @@ class LineArea(BaseArea, LineFilterStore):
2114
2172
  return None
2115
2173
 
2116
2174
  def index2slot_no(self, index):
2117
- """Convert *index* (correct value for indexing self.items) to slot
2118
- number (for display purposes)."""
2175
+ """Convert *index* (correct value for indexing self.items) to slot.
2176
+
2177
+ number (for display purposes).
2178
+ """
2119
2179
  return self.capacity - index
2120
2180
 
2121
2181
 
@@ -2135,7 +2195,9 @@ class BaseParkingStrategy(ABC):
2135
2195
  @staticmethod
2136
2196
  @abstractmethod
2137
2197
  def determine_store(*args, **kwargs):
2138
- """Return the most suitable area for parking a vehicle. A put request
2198
+ """Return the most suitable area for parking a vehicle.
2199
+
2200
+ A put request
2139
2201
  to this area must be immediately successful.
2140
2202
  """
2141
2203
 
@@ -2215,7 +2277,6 @@ class PSSmart(BaseParkingStrategy):
2215
2277
 
2216
2278
  Note: This strategy only makes sense with stores that don't restrict by
2217
2279
  vehicle type, since it wont have any effect on typed stores.
2218
-
2219
2280
  """
2220
2281
 
2221
2282
  name = "SMART"
@@ -2251,7 +2312,8 @@ class PSSmart(BaseParkingStrategy):
2251
2312
  @staticmethod
2252
2313
  def rate_stores(preselected_stores, vehicle):
2253
2314
  """
2254
- Rate [preselected_stores] by accessibility for [vehicle] and
2315
+ Rate [preselected_stores] by accessibility for [vehicle] and.
2316
+
2255
2317
  return a dict (key: result, value: list of stores having this result).
2256
2318
 
2257
2319
  Called by DSFirst.find_match.
@@ -2271,7 +2333,8 @@ class PSSmart(BaseParkingStrategy):
2271
2333
  @staticmethod
2272
2334
  def rate_store(store, vehicle):
2273
2335
  """
2274
- Rate [store] by accessibility for [vehicle]: 0 means directly
2336
+ Rate [store] by accessibility for [vehicle]: 0 means directly.
2337
+
2275
2338
  accessible, higher values mean lower accessibility (lower => better).
2276
2339
 
2277
2340
  Called by DSFirst.rate_stores.
@@ -2382,6 +2445,7 @@ class PSSmart2(BaseParkingStrategy):
2382
2445
  @staticmethod
2383
2446
  def log_best(parking_area_group, rating):
2384
2447
  """Add the best value of a rating to parking_area_group.pssmart2_logs.
2448
+
2385
2449
  rating: [ParkRating]
2386
2450
  """
2387
2451
  if globalConstants["general"]["LOG_ATTRIBUTES"]:
@@ -2408,7 +2472,6 @@ class AreaGroup(StoreConnector):
2408
2472
  max_capacity_line: [int] maximum capacity among Line areas in this group
2409
2473
  capacity_direct: [int] total cacpacity of Direct areas in this group
2410
2474
  capacity_line: [int] total cacpacity of Line areas in this group
2411
-
2412
2475
  """
2413
2476
 
2414
2477
  def __init__(self, env, stores, ID):
@@ -2423,13 +2486,16 @@ class AreaGroup(StoreConnector):
2423
2486
  self.ID = ID
2424
2487
 
2425
2488
  def clear(self):
2426
- """Remove all entries from self.stores and update. self.depot is
2489
+ """Remove all entries from self.stores and update.
2490
+
2491
+ self.depot is
2427
2492
  unaffected.
2428
2493
  """
2429
2494
  super().clear()
2430
2495
 
2431
2496
  def check_entry_filters(self, vehicle):
2432
- """Return a list of booleans that are the results of entry permission
2497
+ """Return a list of booleans that are the results of entry permission.
2498
+
2433
2499
  checks at areas in self.stores. Has the same length as
2434
2500
  self.stores.
2435
2501
  """
@@ -2437,7 +2503,8 @@ class AreaGroup(StoreConnector):
2437
2503
  return permissions
2438
2504
 
2439
2505
  def update_defaults(self):
2440
- """Update attributes after relevant changes such as the amount of areas
2506
+ """Update attributes after relevant changes such as the amount of areas.
2507
+
2441
2508
  in the group.
2442
2509
  """
2443
2510
  self.default_selection_put = [True] * len(self.stores)
@@ -2478,7 +2545,9 @@ class AreaGroup(StoreConnector):
2478
2545
 
2479
2546
 
2480
2547
  class ParkingAreaGroup(AreaGroup):
2481
- """Group specifically for parking areas. Provides various algorithms to
2548
+ """Group specifically for parking areas.
2549
+
2550
+ Provides various algorithms to
2482
2551
  decide in which area to put a vehicle.
2483
2552
 
2484
2553
  Parameters:
@@ -2491,7 +2560,6 @@ class ParkingAreaGroup(AreaGroup):
2491
2560
  simulation start. None if there are no charging interfaces.
2492
2561
  put_queue: [dict] Container for keeping count and time of pending put
2493
2562
  requests to the group. Provisional, may be changed.
2494
-
2495
2563
  """
2496
2564
 
2497
2565
  parking_strategies = {
@@ -2533,7 +2601,7 @@ class ParkingAreaGroup(AreaGroup):
2533
2601
 
2534
2602
  @property
2535
2603
  def parking_strategy_name(self):
2536
- """'name' attribute of set parking strategy"""
2604
+ """'name' attribute of set parking strategy."""
2537
2605
  return self._parking_strategy_name
2538
2606
 
2539
2607
  @parking_strategy_name.setter
@@ -2545,7 +2613,7 @@ class ParkingAreaGroup(AreaGroup):
2545
2613
 
2546
2614
  @property
2547
2615
  def parking_strategy(self):
2548
- """[BaseParkingStrategy] subclass"""
2616
+ """[BaseParkingStrategy] subclass."""
2549
2617
  return self._parking_strategy
2550
2618
 
2551
2619
  @parking_strategy.setter
@@ -2576,7 +2644,9 @@ class ParkingAreaGroup(AreaGroup):
2576
2644
  store.parking_area_group = None
2577
2645
 
2578
2646
  def clear(self):
2579
- """Remove all entries from self.stores and update. self.depot,
2647
+ """Remove all entries from self.stores and update.
2648
+
2649
+ self.depot,
2580
2650
  self.depot.parking_area_groups and self.parking_strategy are
2581
2651
  unaffected.
2582
2652
  """
@@ -2616,7 +2686,9 @@ class ParkingAreaGroup(AreaGroup):
2616
2686
  max_power = None
2617
2687
 
2618
2688
  def put(self, item, selection=None):
2619
- """Summarize put_imm and put_wait. A parking strategy is applied here.
2689
+ """Summarize put_imm and put_wait.
2690
+
2691
+ A parking strategy is applied here.
2620
2692
 
2621
2693
  In this method strategies can be determined before calling the actual
2622
2694
  put() methods, e.g. by modifying the *selection* parameter.
@@ -2679,11 +2751,11 @@ class ParkingAreaGroup(AreaGroup):
2679
2751
 
2680
2752
  class BaseActivityPlan(ABC):
2681
2753
  """Base class for the guidance of a vehicle inside a depot.
2754
+
2682
2755
  A copied instance is assigned to a vehicle at check-in.
2683
2756
 
2684
2757
  Parameters:
2685
2758
  locations: [list] of DepotArea or AreaGroup objects
2686
-
2687
2759
  """
2688
2760
 
2689
2761
  def __init__(self, ID, locations=None, vehicle_filter=None):
@@ -2735,9 +2807,10 @@ class BaseActivityPlan(ABC):
2735
2807
 
2736
2808
 
2737
2809
  class DefaultActivityPlan(BaseActivityPlan):
2738
- """Default plan applicable for every vehicle. One plan is mandatory for a
2739
- depot configuration.
2810
+ """Default plan applicable for every vehicle.
2740
2811
 
2812
+ One plan is mandatory for a
2813
+ depot configuration.
2741
2814
  """
2742
2815
 
2743
2816
  def __init__(self, locations=None):
@@ -2749,9 +2822,10 @@ class DefaultActivityPlan(BaseActivityPlan):
2749
2822
 
2750
2823
 
2751
2824
  class SpecificActivityPlan(BaseActivityPlan):
2752
- """Plan that is applicable to vehicles passing vehicle_filter only. A depot
2753
- configuration may include zero or more specific plans.
2825
+ """Plan that is applicable to vehicles passing vehicle_filter only.
2754
2826
 
2827
+ A depot
2828
+ configuration may include zero or more specific plans.
2755
2829
  """
2756
2830
 
2757
2831
  def __init__(self, ID, locations=None, vehicle_filter=None):
eflips/depot/processes.py CHANGED
@@ -1,14 +1,16 @@
1
1
  # -*- coding: utf-8 -*-
2
- """
3
- Components for processes in a depot.
4
-
5
- """
6
- from enum import auto, Enum
7
- import simpy
2
+ """Components for processes in a depot."""
3
+ import math
4
+ import warnings
8
5
  from abc import ABC, abstractmethod
6
+ from enum import auto, Enum
9
7
  from warnings import warn
10
- from eflips.settings import globalConstants
8
+
9
+ import simpy
11
10
  from eflips.helperFunctions import flexprint
11
+ from eflips.settings import globalConstants
12
+
13
+ import eflips
12
14
  from eflips.depot.evaluation import (
13
15
  BatteryLog,
14
16
  ChargeStart,
@@ -16,8 +18,6 @@ from eflips.depot.evaluation import (
16
18
  ProcessFinished,
17
19
  )
18
20
  from eflips.depot.filters import VehicleFilter
19
- import eflips
20
- import math
21
21
 
22
22
 
23
23
  class ProcessStatus(Enum):
@@ -31,9 +31,9 @@ class ProcessStatus(Enum):
31
31
 
32
32
 
33
33
  class EstimateValue(Enum):
34
- """Values for process completion time estimates in addition to an int
35
- value.
34
+ """Values for process completion time estimates in addition to an int.
36
35
 
36
+ value.
37
37
  """
38
38
 
39
39
  UNKNOWN = auto() # process not completed but an estimate is not possible
@@ -41,7 +41,9 @@ class EstimateValue(Enum):
41
41
 
42
42
 
43
43
  class BaseDepotProcess(ABC):
44
- """Base class for processes in a depot. Core functionalities are:
44
+ """Base class for processes in a depot.
45
+
46
+ Core functionalities are:
45
47
  - wrapping the execution of a simpy process and handling of required
46
48
  resources,
47
49
  - providing the option to resume the process after interruption.
@@ -87,7 +89,6 @@ class BaseDepotProcess(ABC):
87
89
  and won't be resumed, resulting in self.__call__ to proceed.
88
90
  etc: [int or EstimateValue] estimated simulation time of completion. See
89
91
  method self._estimate_time_of_completion for details.
90
-
91
92
  """
92
93
 
93
94
  @abstractmethod
@@ -141,7 +142,7 @@ class BaseDepotProcess(ABC):
141
142
 
142
143
  @property
143
144
  def resIDs(self):
144
- """Return a list of IDs of self.required_resources"""
145
+ """Return a list of IDs of self.required_resources."""
145
146
  return [res.ID for res in self.required_resources]
146
147
 
147
148
  @property
@@ -178,7 +179,9 @@ class BaseDepotProcess(ABC):
178
179
 
179
180
  @abstractmethod
180
181
  def _action(self, *args, **kwargs):
181
- """Generator function that characterizes the process. Subclasses must
182
+ """Generator function that characterizes the process.
183
+
184
+ Subclasses must
182
185
  implement this method. It must catch the exception simpy.Interrupt and
183
186
  let it pass or implement custom follow-ups. If this method yields a
184
187
  timeout, the duration must be set as self.dur before the yield
@@ -188,7 +191,8 @@ class BaseDepotProcess(ABC):
188
191
  @staticmethod
189
192
  @abstractmethod
190
193
  def estimate_duration(*args, **kwargs):
191
- """Return an estimate of the process duration in seconds based on given
194
+ """Return an estimate of the process duration in seconds based on given.
195
+
192
196
  parameters as as if it could start immediately. Must be int, not float.
193
197
  Possible waiting time for resources are not considered. Therefore the
194
198
  estimate is reliable only if the process is started immediately. Static
@@ -196,8 +200,10 @@ class BaseDepotProcess(ABC):
196
200
  """
197
201
 
198
202
  def _pre(self, *args, **kwargs):
199
- """Space for actions before calling self._pem that may not be possible
200
- upon init."""
203
+ """Space for actions before calling self._pem that may not be possible.
204
+
205
+ upon init.
206
+ """
201
207
  pass
202
208
 
203
209
  def _post(self, *args, **kwargs):
@@ -206,6 +212,7 @@ class BaseDepotProcess(ABC):
206
212
 
207
213
  def __call__(self, *args, **kwargs):
208
214
  """Generator that starts the execution of this process.
215
+
209
216
  Note that all parameters required by self._action() have to be passed
210
217
  as keyword arguments.
211
218
  """
@@ -225,7 +232,8 @@ class BaseDepotProcess(ABC):
225
232
  self._post(*args, **kwargs)
226
233
 
227
234
  def _pem(self, recall=False, *args, **kwargs):
228
- """Process execution method (pem) that wraps waiting for required
235
+ """Process execution method (pem) that wraps waiting for required.
236
+
229
237
  resources (if any) and self._action in a single generator. Is called
230
238
  again for the remaining duration after interruption if self.resume is
231
239
  True.
@@ -417,7 +425,9 @@ class BaseDepotProcess(ABC):
417
425
  )
418
426
 
419
427
  def cancel(self):
420
- """Immediately stop the current process execution. No restart is
428
+ """Immediately stop the current process execution.
429
+
430
+ No restart is
421
431
  scheduled, even if self.resume was set to True before.
422
432
  """
423
433
  if hasattr(
@@ -457,7 +467,9 @@ class BaseDepotProcess(ABC):
457
467
  self.finished.succeed()
458
468
 
459
469
  def interrupt(self):
460
- """Immediately stop the current process execution. If self.resume is
470
+ """Immediately stop the current process execution.
471
+
472
+ If self.resume is
461
473
  True, a restart is scheduled and may be successful immediately if
462
474
  required resources are available. If self.resume is False, same effect
463
475
  as self.cancel.
@@ -482,7 +494,6 @@ class VehicleProcess(BaseDepotProcess, ABC):
482
494
  request_immediately: [bool] if True, a vehicle will request this process to
483
495
  start immediately after entering the area this process is available at.
484
496
  There might still be waiting time due to resource requirements.
485
-
486
497
  """
487
498
 
488
499
  request_immediately = True
@@ -644,7 +655,6 @@ class ChargeAbstract(VehicleProcess, ABC):
644
655
 
645
656
  Attributes:
646
657
  last_update: [int] time of last call to self.update_battery.
647
-
648
658
  """
649
659
 
650
660
  @abstractmethod
@@ -723,7 +733,9 @@ class ChargeAbstract(VehicleProcess, ABC):
723
733
 
724
734
  @classmethod
725
735
  def get_chargedata(cls, vehicle):
726
- """Get data for charging process of class *cls*. Not robust if there
736
+ """Get data for charging process of class *cls*.
737
+
738
+ Not robust if there
727
739
  are multiple data sets using *cls* with different parameters and
728
740
  vehicle filters that can return True for the same vehicle.
729
741
  """
@@ -738,7 +750,9 @@ class ChargeAbstract(VehicleProcess, ABC):
738
750
  )
739
751
 
740
752
  def update_battery(self, event_name, amount=None):
741
- """Update the energy level of self.vehicle.battery. Can be called
753
+ """Update the energy level of self.vehicle.battery.
754
+
755
+ Can be called
742
756
  during the execution of the process to provide an interim update.
743
757
 
744
758
  Parameters:
@@ -780,7 +794,9 @@ class ChargeAbstract(VehicleProcess, ABC):
780
794
 
781
795
 
782
796
  class Charge(ChargeAbstract):
783
- """Process of charging a vehicle's battery. Charging is constant during the
797
+ """Process of charging a vehicle's battery.
798
+
799
+ Charging is constant during the
784
800
  whole charging process with maximum power provided by the charging
785
801
  interface.
786
802
 
@@ -788,7 +804,6 @@ class Charge(ChargeAbstract):
788
804
  soc_target: [int or float or str] the charging process will stop at this
789
805
  soc. Must be 0 < soc_target <= 1 or 'soc_max'. For soc_max,
790
806
  vehicle.battery.soc_max will be used as soc_target.
791
-
792
807
  """
793
808
 
794
809
  def __init__(
@@ -891,7 +906,9 @@ class Charge(ChargeAbstract):
891
906
 
892
907
  except simpy.Interrupt:
893
908
  flexprint("charge interrupted", env=self.env, switch="processes")
894
- self.update_battery("charge_interrupt")
909
+ actual_charging_duration = self.env.now - self.starts[0]
910
+ actual_charged_energy = (effective_power * actual_charging_duration) / 3600
911
+ self.update_battery("charge_interrupt", amount=actual_charged_energy)
895
912
 
896
913
  self.charging_interface.current_power = 0
897
914
  self.vehicle.power_logs[self.env.now] = 0
@@ -900,7 +917,8 @@ class Charge(ChargeAbstract):
900
917
 
901
918
  @staticmethod
902
919
  def estimate_duration(vehicle, charging_interface, *args, **kwargs):
903
- """Return a duration estimate [int] assuming constant charging at
920
+ """Return a duration estimate [int] assuming constant charging at.
921
+
904
922
  maximum power until soc_target is reached.
905
923
  """
906
924
  chargedata = Charge.get_chargedata(vehicle)
@@ -952,7 +970,6 @@ class ChargeSteps(ChargeAbstract):
952
970
  from the class ChargeAbstract/Charge.
953
971
  Note that the power values must not be higher than the maximum power of
954
972
  the charging interface.
955
-
956
973
  """
957
974
 
958
975
  def __init__(
@@ -1169,7 +1186,8 @@ class ChargeSteps(ChargeAbstract):
1169
1186
 
1170
1187
 
1171
1188
  class ChargeEquationSteps(ChargeAbstract):
1172
- """Process of charging a vehicle's battery based on a linearized function
1189
+ """Process of charging a vehicle's battery based on a linearized function.
1190
+
1173
1191
  for power.
1174
1192
 
1175
1193
  Parameters:
@@ -1183,7 +1201,6 @@ class ChargeEquationSteps(ChargeAbstract):
1183
1201
  significantly increase simulation runtime. Precision is capped to
1184
1202
  intervals resulting in step durations >= 1 second.
1185
1203
  soc_target: same as for class Charge
1186
-
1187
1204
  """
1188
1205
 
1189
1206
  def __init__(
@@ -1343,6 +1360,10 @@ class ChargeEquationSteps(ChargeAbstract):
1343
1360
  except simpy.Interrupt:
1344
1361
  flexprint("charge interrupted", env=self.env, switch="processes")
1345
1362
  self.update_battery("charge_interrupt")
1363
+ warnings.warn(
1364
+ "It is unclear whether interrupting a charging process of this type returns the desired "
1365
+ "result. Double-check the battery state after the simulation."
1366
+ )
1346
1367
 
1347
1368
  self.charging_interface.current_power = 0
1348
1369
  self.vehicle.power_logs[self.env.now] = 0
@@ -1390,7 +1411,6 @@ def exponential_power(vehicle, charging_interface, peq_params, *args, **kwargs):
1390
1411
  charging_interface: [DepotChargingInterface]
1391
1412
  peq_params: [dict] with parameters for this function in addition to what is
1392
1413
  accessible via vehicle and charging_interface
1393
-
1394
1414
  """
1395
1415
  SoC = vehicle.battery.soc
1396
1416
  P_max = charging_interface.max_power
@@ -1584,7 +1604,6 @@ class Precondition(VehicleProcess):
1584
1604
  Attributes:
1585
1605
  request_immediately: is False for this class because preconditioning is
1586
1606
  scheduled depending on departure time instead of entry time at an area.
1587
-
1588
1607
  """
1589
1608
 
1590
1609
  request_immediately = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: eflips-depot
3
- Version: 3.0.0
3
+ Version: 3.0.2
4
4
  Summary: Depot Simulation for eFLIPS
5
5
  License: AGPLv3
6
6
  Author: Enrico Lauth
@@ -1,11 +1,12 @@
1
+ eflips/depot/.DS_Store,sha256=KzHp46okh461fSINWUgzRhJ8nGo6jsHoENU0EAwl_pQ,6148
1
2
  eflips/depot/__init__.py,sha256=n7jte8R6j_Ad4Mp4hkklKwil5r8u8Q_SbXrCC-nf5jM,1556
2
- eflips/depot/api/__init__.py,sha256=NPHgcqnX6r0ZvK5Yi3S92dZKVH9e-Bs6uJCWMChLeI0,45403
3
+ eflips/depot/api/__init__.py,sha256=WenYoLrAk94Jc-nD7NOD0AZ_2IR7pjsfkz_dA-Jv644,48272
3
4
  eflips/depot/api/defaults/default_settings.json,sha256=0eUDTw_rtLQFvthP8oJL93iRXlmAOravAg-4qqGMQAY,5375
4
5
  eflips/depot/api/private/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- eflips/depot/api/private/depot.py,sha256=KIQ_K-UMSwipNYQ_Vl1jsyL6kn2Cmy1dTApZG4Ciluw,16579
6
+ eflips/depot/api/private/depot.py,sha256=3VvSAFnLZtsraD8m685zqqi-pwfOci66pc7FRVSjxP8,16594
6
7
  eflips/depot/api/private/util.py,sha256=2qVTGeAtAhoq-h5V7Xlli2aDniuoIZYtaCAHD0n1sl8,15261
7
8
  eflips/depot/configuration.py,sha256=Op3hlir-dEN7yHr0kTqbYANoCBKFWK6uKOv3NJl8w_w,35678
8
- eflips/depot/depot.py,sha256=K--jDwMUHs0dJMBIAKKLqxJjxLef4-zT_iB6_MdPY8M,105284
9
+ eflips/depot/depot.py,sha256=afIlaiX-J-M5-K_oAGMr_soL3_QjIAwrQKDaZzTwle0,105566
9
10
  eflips/depot/evaluation.py,sha256=qqXyP4jA1zFcKuWhliQ6n25ZlGl9mJV-vtXf0yu8WN8,140842
10
11
  eflips/depot/filters.py,sha256=ZBTiu0UJUp2NYWtvZewfb4Zd4mdmFLkrjIS-ujCgax8,15522
11
12
  eflips/depot/input_epex_power_price.py,sha256=VPDC1zy-klQpveGIZ8941hL1P_jeNq3IHoLgFTsANig,5569
@@ -25,7 +26,7 @@ eflips/depot/layout_opt/settings.py,sha256=EUGCp4dAX22j2uF8sKqbi9a5iP8hb6QpP7t2N
25
26
  eflips/depot/layout_opt/template_creation.py,sha256=H4LoFjQfbPjTt9rGvapH2tEUWcQ56kPwDucq7t6YahU,9736
26
27
  eflips/depot/layout_opt/util.py,sha256=EYh7IN58ZjysmCFdSieQqIQ9goe1a_ZwARRHxOgjEQo,3780
27
28
  eflips/depot/plots.py,sha256=85xInZWfJIOVm03onvppgP5yLTgQeMn-1t5aoNdavyY,2509
28
- eflips/depot/processes.py,sha256=pDc5lYvHsGBIB2O0Q5jnPuUCfdw2lsWaY2B6GtQTLtU,58260
29
+ eflips/depot/processes.py,sha256=vqwtaK5UfG5aJwsQL2Eo9ZOFg1z5khuNPyEC9-3M83Y,58757
29
30
  eflips/depot/rating.py,sha256=vBUr3y31DBcA-LjZW8t6wLfpAsuB8DPkrMThtCtN908,16648
30
31
  eflips/depot/resources.py,sha256=0SuzN8qgMmCqa7oUEXVC_XE6pCUtxTsqOfCsaM9Oh3o,13568
31
32
  eflips/depot/settings_config.py,sha256=z7CqPdjd8QRlgZj0Zis-H13cL7LOiheRT4ctYCYGguc,2527
@@ -34,7 +35,7 @@ eflips/depot/simulation.py,sha256=ee0qTzOzG-8ybN36ie_NJallXfC7jUaS9JZvaYFziLs,10
34
35
  eflips/depot/smart_charging.py,sha256=C3BYqzn2-OYY4ipXm0ETtavbAM9QXZMYULBpVoChf0E,54311
35
36
  eflips/depot/standalone.py,sha256=VxcTzBaB67fNJUMmjPRwKXjhqTy6oQ41Coote2LvAmk,22338
36
37
  eflips/depot/validation.py,sha256=TIuY7cQtEJI4H2VVMSuY5IIVkacEEZ67weeMuY3NSAM,7097
37
- eflips_depot-3.0.0.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
38
- eflips_depot-3.0.0.dist-info/METADATA,sha256=vFl6gYJzQFa7wxTpT2KNR_AC_dnZrE0RVazRrZrQhRI,5648
39
- eflips_depot-3.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
40
- eflips_depot-3.0.0.dist-info/RECORD,,
38
+ eflips_depot-3.0.2.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
39
+ eflips_depot-3.0.2.dist-info/METADATA,sha256=nbFn4zt1i10t3WkwSc6da-U5krwSbDFGWIOxhd9yKcM,5648
40
+ eflips_depot-3.0.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
41
+ eflips_depot-3.0.2.dist-info/RECORD,,