eflips-depot 4.6.5__py3-none-any.whl → 4.6.7__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/__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
@@ -266,8 +266,18 @@ def simple_consumption_simulation(
266
266
  vehicles = (
267
267
  session.query(Vehicle).filter(Vehicle.scenario_id == scenario.id).all()
268
268
  )
269
+
270
+ # Get the event count for each vbehicle in a single query using a groub_py clause
271
+ vehicle_event_count_q = (
272
+ session.query(Event.vehicle_id, sqlalchemy.func.count(Event.id))
273
+ .join(Vehicle)
274
+ .filter(Vehicle.scenario_id == scenario.id)
275
+ .group_by(Event.vehicle_id)
276
+ )
277
+ vehicle_event_count = dict(vehicle_event_count_q.all())
278
+
269
279
  for vehicle in vehicles:
270
- if session.query(Event).filter(Event.vehicle_id == vehicle.id).count() == 0:
280
+ if vehicle.id not in vehicle_event_count.keys():
271
281
  add_initial_standby_event(vehicle, session)
272
282
 
273
283
  # Since we are doing no_autoflush blocks later, we need to flush the session once here so that unflushed stuff
@@ -594,6 +604,7 @@ def simulate_scenario(
594
604
  database_url: Optional[str] = None,
595
605
  smart_charging_strategy: SmartChargingStrategy = SmartChargingStrategy.EVEN,
596
606
  ignore_unstable_simulation: bool = False,
607
+ ignore_delayed_trips: bool = False,
597
608
  ) -> None:
598
609
  """
599
610
  This method simulates a scenario and adds the results to the database.
@@ -627,11 +638,13 @@ def simulate_scenario(
627
638
  - SmartChargingStrategy.MIN_PRICE: Not implemented yet.
628
639
 
629
640
  :param ignore_unstable_simulation: If True, the simulation will not raise an exception if it becomes unstable.
641
+ :param ignore_delayed_trips: If True, the simulation will not raise an exception if there are delayed trips.
630
642
 
631
643
  :return: Nothing. The results are added to the database.
632
644
 
633
645
  :raises UnstableSimulationException: If the simulation becomes numerically unstable or if
634
646
  the parameters cause the solver to diverge.
647
+ :raises DelayedTripException: If there are delayed trips in the simulation.
635
648
  """
636
649
  logger = logging.getLogger(__name__)
637
650
 
@@ -649,6 +662,11 @@ def simulate_scenario(
649
662
  logger.warning("Simulation is unstable. Continuing.")
650
663
  else:
651
664
  raise e
665
+ except eflips.depot.DelayedTripException as e:
666
+ if ignore_delayed_trips:
667
+ logger.warning("Simulation has delayed trips. Continuing.")
668
+ else:
669
+ raise e
652
670
 
653
671
  match smart_charging_strategy:
654
672
  case SmartChargingStrategy.NONE:
@@ -997,6 +1015,8 @@ def add_evaluation_to_database(
997
1015
 
998
1016
  :raises UnstableSimulationException: If the simulation becomes numerically unstable or if
999
1017
  the parameters cause the solver to diverge.
1018
+ :raises DelayedTripException: If there are delayed trips in the simulation.
1019
+
1000
1020
  """
1001
1021
 
1002
1022
  # Read simulation start time
@@ -78,13 +78,14 @@ def add_initial_standby_event(
78
78
  but changes are not yet committed.
79
79
  """
80
80
 
81
- earliest_trip = (
81
+ earliest_trip_q = (
82
82
  session.query(Trip)
83
83
  .join(Rotation)
84
84
  .filter(Rotation.vehicle == vehicle)
85
85
  .order_by(Trip.departure_time)
86
- .first()
86
+ .limit(1)
87
87
  )
88
+ earliest_trip = earliest_trip_q.one_or_none()
88
89
  if earliest_trip is None:
89
90
  warnings.warn(
90
91
  f"No trips found for vehicle {vehicle.id}. Cannot add initial standby event.",
@@ -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.5
3
+ Version: 4.6.7
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=0ifYtEwSs2LbpPINxdDsGNxdywKRicCQBETB7BpBh9Y,48631
1
+ eflips/depot/__init__.py,sha256=06GUem0JIEunIyJ0_P_MLAGfibGEnNqcPPY0OBpO2NQ,1662
2
+ eflips/depot/api/__init__.py,sha256=2isjSF0bef8Nq_lHMC2MGgfAFBgj4OWeezouzeSisss,49527
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
- eflips/depot/api/private/consumption.py,sha256=vDPCogd3NPx2duCUhg_CcW9rMQWgrppfqsS4E7W9T1Y,16400
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.5.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
41
- eflips_depot-4.6.5.dist-info/METADATA,sha256=aPK2OCraATDQr-H1kBZGqyZkj1fC0ZUBecES6EFCyoU,5940
42
- eflips_depot-4.6.5.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
43
- eflips_depot-4.6.5.dist-info/RECORD,,
40
+ eflips_depot-4.6.7.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
41
+ eflips_depot-4.6.7.dist-info/METADATA,sha256=8ZhFsc9DzScN-igSjjhMmKbUnErElAu9FRWZn8rI1w8,5940
42
+ eflips_depot-4.6.7.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
43
+ eflips_depot-4.6.7.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