eflips-depot 4.6.6__py3-none-any.whl → 4.6.8__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.
eflips/depot/__init__.py CHANGED
@@ -1,7 +1,6 @@
1
1
  # 1st: Namespaced Package Compat., see https://packaging.python.org/en/latest/guides/packaging-namespace-packages/
2
2
  __path__ = __import__("pkgutil").extend_path(__path__, __name__)
3
3
 
4
-
5
4
  # 2nd: Importing of the eflips modules -> TODO Cleanups here to avoid all those imports
6
5
 
7
6
 
@@ -57,3 +56,7 @@ from eflips.depot.validation import Validator
57
56
 
58
57
  class UnstableSimulationException(Exception):
59
58
  pass
59
+
60
+
61
+ class DelayedTripException(Exception):
62
+ pass
@@ -500,14 +500,21 @@ def generate_depot_layout(
500
500
  AreaType.DIRECT_ONESIDE: rotation_count,
501
501
  AreaType.DIRECT_TWOSIDE: None,
502
502
  }
503
+
504
+ total_rotation_count = (
505
+ session.query(Rotation)
506
+ .filter(Rotation.scenario_id == scenario.id)
507
+ .count()
508
+ )
509
+
503
510
  generate_depot(
504
511
  vt_capacity_dict,
505
512
  first_stop,
506
513
  scenario,
507
514
  session,
508
515
  charging_power=charging_power,
509
- num_shunting_slots=max(rotation_count // 10, 1),
510
- num_cleaning_slots=max(rotation_count // 10, 1),
516
+ num_shunting_slots=max(total_rotation_count // 10, 1),
517
+ num_cleaning_slots=max(total_rotation_count // 10, 1),
511
518
  )
512
519
 
513
520
 
@@ -604,6 +611,7 @@ def simulate_scenario(
604
611
  database_url: Optional[str] = None,
605
612
  smart_charging_strategy: SmartChargingStrategy = SmartChargingStrategy.EVEN,
606
613
  ignore_unstable_simulation: bool = False,
614
+ ignore_delayed_trips: bool = False,
607
615
  ) -> None:
608
616
  """
609
617
  This method simulates a scenario and adds the results to the database.
@@ -637,11 +645,13 @@ def simulate_scenario(
637
645
  - SmartChargingStrategy.MIN_PRICE: Not implemented yet.
638
646
 
639
647
  :param ignore_unstable_simulation: If True, the simulation will not raise an exception if it becomes unstable.
648
+ :param ignore_delayed_trips: If True, the simulation will not raise an exception if there are delayed trips.
640
649
 
641
650
  :return: Nothing. The results are added to the database.
642
651
 
643
652
  :raises UnstableSimulationException: If the simulation becomes numerically unstable or if
644
653
  the parameters cause the solver to diverge.
654
+ :raises DelayedTripException: If there are delayed trips in the simulation.
645
655
  """
646
656
  logger = logging.getLogger(__name__)
647
657
 
@@ -659,6 +669,11 @@ def simulate_scenario(
659
669
  logger.warning("Simulation is unstable. Continuing.")
660
670
  else:
661
671
  raise e
672
+ except eflips.depot.DelayedTripException as e:
673
+ if ignore_delayed_trips:
674
+ logger.warning("Simulation has delayed trips. Continuing.")
675
+ else:
676
+ raise e
662
677
 
663
678
  match smart_charging_strategy:
664
679
  case SmartChargingStrategy.NONE:
@@ -1007,6 +1022,8 @@ def add_evaluation_to_database(
1007
1022
 
1008
1023
  :raises UnstableSimulationException: If the simulation becomes numerically unstable or if
1009
1024
  the parameters cause the solver to diverge.
1025
+ :raises DelayedTripException: If there are delayed trips in the simulation.
1026
+
1010
1027
  """
1011
1028
 
1012
1029
  # Read simulation start time
@@ -4,10 +4,12 @@ import logging
4
4
  from datetime import timedelta
5
5
  from typing import List, Dict
6
6
 
7
- from eflips.model import Event, EventType, Rotation, Vehicle, Area
7
+ import numpy as np
8
+ from eflips.model import Event, EventType, Rotation, Vehicle, Area, AreaType
8
9
  from sqlalchemy import select
9
10
 
10
11
  from eflips.depot import SimpleVehicle, ProcessStatus
12
+ from eflips.depot import UnstableSimulationException, DelayedTripException
11
13
 
12
14
 
13
15
  def get_finished_schedules_per_vehicle(
@@ -47,10 +49,13 @@ def get_finished_schedules_per_vehicle(
47
49
  latest_time = None
48
50
 
49
51
  for i in range(len(list_of_finished_trips)):
50
- assert list_of_finished_trips[i].atd == list_of_finished_trips[i].std, (
51
- "The trip {current_trip.ID} is delayed. The simulation doesn't "
52
- "support delayed trips for now."
53
- )
52
+ try:
53
+ assert list_of_finished_trips[i].atd == list_of_finished_trips[i].std
54
+ except AssertionError:
55
+ raise DelayedTripException(
56
+ f"The trip {list_of_finished_trips[i].ID} is delayed. The simulation doesn't "
57
+ "support delayed trips for now."
58
+ )
54
59
 
55
60
  if list_of_finished_trips[i].is_copy is False:
56
61
  current_trip = list_of_finished_trips[i]
@@ -61,7 +66,7 @@ def get_finished_schedules_per_vehicle(
61
66
  "id": int(current_trip.ID),
62
67
  }
63
68
  if i == 0:
64
- raise ValueError(
69
+ raise UnstableSimulationException(
65
70
  f"New Vehicle required for the trip {current_trip.ID}, which suggests the fleet or the "
66
71
  f"infrastructure might not be enough for the full electrification. Please add charging "
67
72
  f"interfaces or increase charging power ."
@@ -164,8 +169,7 @@ def generate_vehicle_events(
164
169
  "dwd.active_processes_copy"
165
170
  ].items():
166
171
  if earliest_time <= time_stamp <= latest_time:
167
- num_process = len(process_log)
168
- if num_process == 0:
172
+ if len(process_log) == 0:
169
173
  # A departure happens and this trip should already be stored in the dictionary
170
174
  pass
171
175
  else:
@@ -202,35 +206,50 @@ def generate_vehicle_events(
202
206
  f"A process with no duration could only "
203
207
  f"happen in the last area before dispatched"
204
208
  )
205
- if (
206
- time_stamp in dict_of_events.keys()
207
- and "end" in dict_of_events[time_stamp].keys()
208
- ):
209
+ start_this_event = None
210
+ if time_stamp in dict_of_events.keys():
211
+ assert "end" in dict_of_events[time_stamp].keys(), (
212
+ f"The former event of {process} "
213
+ f"should have an end time."
214
+ )
209
215
  start_this_event = dict_of_events[time_stamp]["end"]
210
- if start_this_event in dict_of_events.keys():
216
+ else:
217
+ for other_process in process_log:
211
218
  if (
212
- dict_of_events[start_this_event]["type"]
213
- == "Trip"
219
+ other_process.ID != process.ID
220
+ and other_process.dur > 0
214
221
  ):
215
- logger.info(
216
- f"Vehicle {current_vehicle.ID} must depart immediately after charged. "
217
- f"Thus there will be no STANDBY_DEPARTURE event."
218
- )
219
-
220
- else:
221
- raise ValueError(
222
- f"There is already an event "
223
- f"{dict_of_events[start_this_event]} at {start_this_event}."
224
- )
225
-
226
- continue
227
-
228
- dict_of_events[start_this_event] = {
229
- "type": type(process).__name__,
230
- "area": current_area.ID,
231
- "slot": current_slot,
232
- "id": process.ID,
233
- }
222
+ start_this_event = other_process.ends[0]
223
+ break
224
+
225
+ assert (
226
+ start_this_event is not None
227
+ ), f"Current process {process} should have a start time by now"
228
+
229
+ if start_this_event in dict_of_events.keys():
230
+ if (
231
+ dict_of_events[start_this_event]["type"]
232
+ == "Trip"
233
+ ):
234
+ logger.info(
235
+ f"Vehicle {current_vehicle.ID} must depart immediately after charged. "
236
+ f"Thus there will be no STANDBY_DEPARTURE event."
237
+ )
238
+
239
+ else:
240
+ raise ValueError(
241
+ f"There is already an event "
242
+ f"{dict_of_events[start_this_event]} at {start_this_event}."
243
+ )
244
+
245
+ continue
246
+
247
+ dict_of_events[start_this_event] = {
248
+ "type": type(process).__name__,
249
+ "area": current_area.ID,
250
+ "slot": current_slot,
251
+ "id": process.ID,
252
+ }
234
253
 
235
254
  case ProcessStatus.IN_PROGRESS:
236
255
  assert (
@@ -317,35 +336,25 @@ def add_soc_to_events(dict_of_events, battery_log) -> None:
317
336
  battery_log_list.append((log.t, log.energy / log.energy_real))
318
337
 
319
338
  time_keys = sorted(dict_of_events.keys())
339
+
340
+ battery_log_times = [log[0] for log in battery_log_list]
341
+ battery_log_socs = [log[1] for log in battery_log_list]
342
+
320
343
  for i in range(len(time_keys)):
321
344
  # Get soc
322
- soc_start = None
323
- soc_end = None
345
+
324
346
  start_time = time_keys[i]
325
347
  process_dict = dict_of_events[time_keys[i]]
326
- for j in range(len(battery_log_list)):
327
- # Access the correct battery log according to time since there is only one battery log for each time
328
- log = battery_log_list[j]
329
-
330
- if process_dict["type"] != "Trip":
331
- if log[0] == start_time:
332
- soc_start = log[1]
333
- if log[0] == process_dict["end"]:
334
- soc_end = log[1]
335
- if log[0] < start_time < battery_log_list[j + 1][0]:
336
- soc_start = log[1]
337
- if log[0] < process_dict["end"] < battery_log_list[j + 1][0]:
338
- soc_end = log[1]
339
-
340
- if soc_start is not None:
341
- soc_start = min(soc_start, 1) # so
342
- process_dict["soc_start"] = soc_start
343
- if soc_end is not None:
344
- soc_end = min(soc_end, 1) # soc should not exceed 1
345
- process_dict["soc_end"] = soc_end
346
348
 
347
- else:
348
- continue
349
+ if process_dict["type"] != "Trip":
350
+ soc_start = np.interp(start_time, battery_log_times, battery_log_socs)
351
+ process_dict["soc_start"] = min(float(soc_start), 1.0)
352
+ soc_end = np.interp(
353
+ process_dict["end"], battery_log_times, battery_log_socs
354
+ )
355
+ process_dict["soc_end"] = min(float(soc_end), 1.0)
356
+ else:
357
+ continue
349
358
 
350
359
 
351
360
  def add_events_into_database(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: eflips-depot
3
- Version: 4.6.6
3
+ Version: 4.6.8
4
4
  Summary: Depot Simulation for eFLIPS
5
5
  License: AGPL-3.0-or-later
6
6
  Author: Enrico Lauth
@@ -1,10 +1,10 @@
1
- eflips/depot/__init__.py,sha256=RQ_UKNrGWA6q17TZFu86ai8pC7qCpcbmAgVKh7aImwo,1613
2
- eflips/depot/api/__init__.py,sha256=4oNT-J3CB34m7cenCbx2Fi1IWFk9rfDG2-TT6EqCU64,49003
1
+ eflips/depot/__init__.py,sha256=06GUem0JIEunIyJ0_P_MLAGfibGEnNqcPPY0OBpO2NQ,1662
2
+ eflips/depot/api/__init__.py,sha256=GgFmHnpawNDdhc8ki4DwXzpMDrRjl1r0TYNXKdRUehk,49718
3
3
  eflips/depot/api/defaults/default_settings.json,sha256=0eUDTw_rtLQFvthP8oJL93iRXlmAOravAg-4qqGMQAY,5375
4
4
  eflips/depot/api/private/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  eflips/depot/api/private/consumption.py,sha256=FTF_E-DsbvJNWZJKQy_CPoHQpLz--pWY-inJYg8lsjM,16453
6
6
  eflips/depot/api/private/depot.py,sha256=WcCcib3NSbvoL3G06I2ajT8U5L7AkDK5qe969PHq-fg,41014
7
- eflips/depot/api/private/results_to_database.py,sha256=Sh2VJ3k60QJ5RGkc8sw-7XbljiMe65EHeoagKIpYlHM,24585
7
+ eflips/depot/api/private/results_to_database.py,sha256=R3wwGuaEsof2sW_fFv5Y9K0cl2K8W0JKe5VoN2elXTQ,25037
8
8
  eflips/depot/api/private/smart_charging.py,sha256=MQ9fXdKByHAz6RSKXYcpJXDBccdJKZ2qGReCHagVCyo,13033
9
9
  eflips/depot/api/private/util.py,sha256=gk5TJZrcvtkyrqnjR3Cq-hPRGRCovhzKz3LrBDIvN0E,16799
10
10
  eflips/depot/configuration.py,sha256=Op3hlir-dEN7yHr0kTqbYANoCBKFWK6uKOv3NJl8w_w,35678
@@ -37,7 +37,7 @@ eflips/depot/simulation.py,sha256=ee0qTzOzG-8ybN36ie_NJallXfC7jUaS9JZvaYFziLs,10
37
37
  eflips/depot/smart_charging.py,sha256=C3BYqzn2-OYY4ipXm0ETtavbAM9QXZMYULBpVoChf0E,54311
38
38
  eflips/depot/standalone.py,sha256=VxcTzBaB67fNJUMmjPRwKXjhqTy6oQ41Coote2LvAmk,22338
39
39
  eflips/depot/validation.py,sha256=TIuY7cQtEJI4H2VVMSuY5IIVkacEEZ67weeMuY3NSAM,7097
40
- eflips_depot-4.6.6.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
41
- eflips_depot-4.6.6.dist-info/METADATA,sha256=j67zcAG9Xk9dSXsmwe0HPZbTHRTlGYnUTc__Dl-ncLs,5940
42
- eflips_depot-4.6.6.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
43
- eflips_depot-4.6.6.dist-info/RECORD,,
40
+ eflips_depot-4.6.8.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
41
+ eflips_depot-4.6.8.dist-info/METADATA,sha256=YeJBI-UcgIp0m_fqql6ef1a8eMmNZRG-kMm4R9pkTqo,5940
42
+ eflips_depot-4.6.8.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
43
+ eflips_depot-4.6.8.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.0.1
2
+ Generator: poetry-core 2.1.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any