eflips-depot 4.13.1__py3-none-any.whl → 4.13.3__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
@@ -95,8 +95,3 @@ class UnstableSimulationException(Exception):
95
95
  f"The following blocks/rotations require a new vehicle. This suggests an unstable "
96
96
  f" simulation result, where a repeated schedule might require more vehicles: {trip_names}"
97
97
  )
98
-
99
-
100
- class MultipleErrors(Exception):
101
- def __init__(self, errors):
102
- self.errors = errors
@@ -32,7 +32,7 @@ from collections import OrderedDict
32
32
  from datetime import timedelta, datetime
33
33
  from enum import Enum
34
34
  from math import ceil
35
- from typing import Any, Dict, Optional, Union, List
35
+ from typing import Any, Dict, Optional, Union
36
36
 
37
37
  import sqlalchemy.orm
38
38
  from eflips.model import (
@@ -51,6 +51,7 @@ from eflips.model import (
51
51
  ConsistencyWarning,
52
52
  Station,
53
53
  )
54
+ from sqlalchemy import func
54
55
  from sqlalchemy.orm import Session
55
56
 
56
57
  import eflips.depot
@@ -59,7 +60,6 @@ from eflips.depot import (
59
60
  SimulationHost,
60
61
  UnstableSimulationException,
61
62
  DelayedTripException,
62
- MultipleErrors,
63
63
  )
64
64
  from eflips.depot.api.private.consumption import ConsumptionResult
65
65
  from eflips.depot.api.private.consumption import (
@@ -639,29 +639,20 @@ def simulate_scenario(
639
639
  try:
640
640
  add_evaluation_to_database(scenario, ev, session)
641
641
 
642
- except MultipleErrors as e:
643
- if e.errors:
644
- for error in e.errors:
645
- if (
646
- isinstance(error, DelayedTripException)
647
- and not ignore_delayed_trips
648
- ):
649
- logger.error(
650
- "There are delayed trips in the simulation. "
651
- "Please check the input data and try again."
652
- )
653
- raise error
654
- if (
655
- isinstance(error, UnstableSimulationException)
656
- and not ignore_unstable_simulation
657
- ):
658
- logger.error(
659
- "Simulation became unstable. "
660
- "Please check the input data and try again."
661
- )
662
- raise error
663
-
664
- logger.warning("An error occurred during the simulation: %s", error)
642
+ except* DelayedTripException as delay_exp:
643
+ if not ignore_delayed_trips:
644
+ logger.error(
645
+ "There are delayed trips in the simulation. "
646
+ "Please check the input data and try again."
647
+ )
648
+ raise delay_exp
649
+ except* UnstableSimulationException as unstable_exp:
650
+ if not ignore_unstable_simulation:
651
+ logger.error(
652
+ "The simulation became unstable. "
653
+ "Please check the input data and try again."
654
+ )
655
+ raise unstable_exp
665
656
 
666
657
  match smart_charging_strategy:
667
658
  case SmartChargingStrategy.NONE:
@@ -761,28 +752,15 @@ def init_simulation(
761
752
  .all()
762
753
  ]
763
754
 
764
- first_departure_time = min(
765
- [vehicle_schedule.departure for vehicle_schedule in vehicle_schedules]
766
- )
767
- last_arrival_time = max(
768
- [vehicle_schedule.arrival for vehicle_schedule in vehicle_schedules]
769
- )
770
-
771
- # We take first arrival time as simulation start
772
- total_duration = (last_arrival_time - first_departure_time).total_seconds()
773
- schedule_duration_days = ceil(total_duration / (24 * 60 * 60))
774
-
775
- if repetition_period is None and schedule_duration_days in [1, 2]:
776
- repetition_period = timedelta(days=1)
777
- elif repetition_period is None and schedule_duration_days in [7, 8]:
778
- repetition_period = timedelta(weeks=1)
779
- elif repetition_period is None:
780
- raise ValueError(
781
- "Could not automatically detect repetition period. Please specify manually."
782
- )
755
+ if repetition_period is None:
756
+ repetition_period = schedule_duration_days(scenario)
757
+ if repetition_period not in (timedelta(days=1), timedelta(days=7)):
758
+ warnings.warn(
759
+ f"Non-standard schedule duration of {repetition_period}. Please make sure this is intended.",
760
+ UserWarning,
761
+ )
783
762
 
784
763
  # Now, we need to repeat the vehicle schedules
785
-
786
764
  vehicle_schedules = repeat_vehicle_schedules(vehicle_schedules, repetition_period)
787
765
 
788
766
  sim_start_stime, total_duration_seconds = start_and_end_times(vehicle_schedules)
@@ -1116,7 +1094,9 @@ def add_evaluation_to_database(
1116
1094
  errors.append(unstable_exp)
1117
1095
 
1118
1096
  if len(errors) > 0:
1119
- raise MultipleErrors(errors)
1097
+ raise ExceptionGroup(
1098
+ "Simulation is either unstable or including delayed blocks", errors
1099
+ )
1120
1100
 
1121
1101
 
1122
1102
  def generate_depot_optimal_size(
@@ -1129,7 +1109,9 @@ def generate_depot_optimal_size(
1129
1109
  repetition_period: Optional[timedelta] = None,
1130
1110
  ) -> None:
1131
1111
  """
1132
- Generates an optimal depot layout with the smallest possible size for each depot in the scenario. Line charging areas
1112
+ Generates an optimal depot layout with the smallest possible size for each depot in the scenario.
1113
+
1114
+ Line charging areas
1133
1115
  with given block length area preferred. The existing depot will be deleted if `delete_existing_depot` is set to True.
1134
1116
 
1135
1117
  :param scenario: Either a :class:`eflips.model.Scenario` object containing the input data for the simulation. Or
@@ -1148,7 +1130,6 @@ def generate_depot_optimal_size(
1148
1130
  value as in the simulation.
1149
1131
 
1150
1132
  :return: None. The depot layout will be added to the database.
1151
-
1152
1133
  """
1153
1134
 
1154
1135
  logger = logging.getLogger(__name__)
@@ -1309,3 +1290,47 @@ def generate_depot_optimal_size(
1309
1290
  num_shunting_slots=int(max(time_range)),
1310
1291
  num_cleaning_slots=int(max(time_range)),
1311
1292
  )
1293
+
1294
+
1295
+ def schedule_duration_days(
1296
+ scenario: Union[Scenario, int, Any], database_url: Optional[str] = None
1297
+ ) -> timedelta:
1298
+ """
1299
+ This method calculates the duration of a given scenario in days.
1300
+
1301
+ This is the duration
1302
+ between the first departure of the first day, and the last departure on the last day, rounded up to full days.
1303
+
1304
+ Most of the time, this is the "natural" repetition period of the scenario. We are simulating one full period, and
1305
+ this period – continuously repeated – is what happens in reality.
1306
+
1307
+ This method can be used to show the user what the detected repetition period is and to auto-set the repetition
1308
+ period if none is provided.
1309
+
1310
+ :param scenario: Either a :class:`eflips.model.Scenario` object containing the input data for the simulation. Or
1311
+ an integer specifying the ID of a scenario in the database. Or any other object that has an attribute "id"
1312
+ containing an integer pointing to a unique scenario id.
1313
+ :param database_url: An optional database URL. Used if no database url is given by the environment variable.
1314
+ :return: a timedelta object representing the duration in days.
1315
+ """
1316
+ with create_session(scenario, database_url) as (session, scenario):
1317
+ first_departure = (
1318
+ session.query(func.min(Trip.departure_time))
1319
+ .filter(Trip.scenario_id == scenario.id)
1320
+ .scalar()
1321
+ )
1322
+ if first_departure is None:
1323
+ raise ValueError("No trips found in the scenario.")
1324
+
1325
+ last_departure = (
1326
+ session.query(func.max(Trip.departure_time))
1327
+ .filter(Trip.scenario_id == scenario.id)
1328
+ .scalar()
1329
+ )
1330
+ if last_departure is None:
1331
+ raise ValueError("No trips found in the scenario.")
1332
+
1333
+ duration = last_departure - first_departure
1334
+ duration_days = ceil(duration.total_seconds() / (24 * 60 * 60))
1335
+
1336
+ return timedelta(days=duration_days)
@@ -27,6 +27,8 @@ from eflips.model import (
27
27
  from sqlalchemy import or_
28
28
  from sqlalchemy.orm import Session
29
29
 
30
+ from eflips.depot import UnstableSimulationException, DelayedTripException
31
+
30
32
 
31
33
  class MissingVehicleDimensionError(ValueError):
32
34
  pass
@@ -1091,14 +1093,15 @@ def depot_smallest_possible_size(
1091
1093
 
1092
1094
  except MissingVehicleDimensionError as e:
1093
1095
  raise e
1094
- except Exception as e:
1096
+ except UnstableSimulationException as e:
1095
1097
  # This change is made after Unstable exception and delay exceptions are introduced
1096
- if (
1097
- "which suggests the fleet or the infrastructure might not be enough for the full electrification. Please add charging interfaces or increase charging power ."
1098
- in repr(e)
1099
- ):
1100
- logger.debug(f"Depot is too small.")
1101
- continue
1098
+ logger.debug(
1099
+ f"Results are unstable, suggesting depot is too small."
1100
+ )
1101
+ continue
1102
+ except DelayedTripException as e:
1103
+ logger.debug(f"Trips are delayed, suggesting depot is too small.")
1104
+ continue
1102
1105
  finally:
1103
1106
  inner_savepoint.rollback()
1104
1107
  savepoint.rollback()
@@ -1,14 +1,13 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: eflips-depot
3
- Version: 4.13.1
3
+ Version: 4.13.3
4
4
  Summary: Depot Simulation for eFLIPS
5
5
  License: AGPL-3.0-or-later
6
6
  Author: Enrico Lauth
7
7
  Author-email: enrico.lauth@tu-berlin.de
8
- Requires-Python: >=3.10,<3.14
8
+ Requires-Python: >=3.11,<3.14
9
9
  Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
11
  Classifier: Programming Language :: Python :: 3.11
13
12
  Classifier: Programming Language :: Python :: 3.12
14
13
  Classifier: Programming Language :: Python :: 3.13
@@ -1,9 +1,9 @@
1
- eflips/depot/__init__.py,sha256=p-oqm1LmTuIk5PRiWDyychBhwAsr-9Rw7dKmRCZ58wc,2968
2
- eflips/depot/api/__init__.py,sha256=0iSCRtff8Ljb8g4fUmLxqWXI_u-qTE6wv_Jj4fgTXLM,57403
1
+ eflips/depot/__init__.py,sha256=I42xmkE7GDZfoZeSn_ey85sYGI7xyGqAt6Xd4UZpHLw,2872
2
+ eflips/depot/api/__init__.py,sha256=I0KQ_lDAWWPaH4HJfNLPxkv2eFKai9by2WqX0AKNsP4,58571
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=nImegyhKrZlEwKTNdmAmDxjTQCvwfPgxA588e0M_F9o,27282
6
- eflips/depot/api/private/depot.py,sha256=uESFVBhHybDN-LzvceKHlFSfsVsBfh5gaOuTxspX6rY,44055
6
+ eflips/depot/api/private/depot.py,sha256=NLrRBjb-wq7E3hnVAiX08IzUSVjfMV_NgRqzIx53CE0,44120
7
7
  eflips/depot/api/private/results_to_database.py,sha256=FBlZRKqAIG80tFyuf5vCGHRycWKYkm53jFxJEk9t1NA,25984
8
8
  eflips/depot/api/private/util.py,sha256=DasTkuGUhlBpY_BtTFWoxSNZU_CRyM3RqEDgO07Eks8,17990
9
9
  eflips/depot/configuration.py,sha256=Op3hlir-dEN7yHr0kTqbYANoCBKFWK6uKOv3NJl8w_w,35678
@@ -36,7 +36,7 @@ eflips/depot/simulation.py,sha256=ee0qTzOzG-8ybN36ie_NJallXfC7jUaS9JZvaYFziLs,10
36
36
  eflips/depot/smart_charging.py,sha256=C3BYqzn2-OYY4ipXm0ETtavbAM9QXZMYULBpVoChf0E,54311
37
37
  eflips/depot/standalone.py,sha256=8O01zEXghFG9zZBu0fUD0sXvbHQ-AXw6RB5M750a_sM,22419
38
38
  eflips/depot/validation.py,sha256=TIuY7cQtEJI4H2VVMSuY5IIVkacEEZ67weeMuY3NSAM,7097
39
- eflips_depot-4.13.1.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
40
- eflips_depot-4.13.1.dist-info/METADATA,sha256=FW9SEPDE7MruN8IK-fAlC3o2Gj3EH_ZHYjmvUSIAxsA,5987
41
- eflips_depot-4.13.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
42
- eflips_depot-4.13.1.dist-info/RECORD,,
39
+ eflips_depot-4.13.3.dist-info/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
40
+ eflips_depot-4.13.3.dist-info/METADATA,sha256=FHoPKGqzbw6Vv25vPaFZm_4vDWXtmbvgiIHqaulg6VQ,5936
41
+ eflips_depot-4.13.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
42
+ eflips_depot-4.13.3.dist-info/RECORD,,