eflips-depot 4.12.2__py3-none-any.whl → 4.13.1__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 +43 -3
- eflips/depot/api/__init__.py +120 -62
- eflips/depot/api/private/depot.py +14 -3
- eflips/depot/api/private/results_to_database.py +54 -34
- eflips/depot/api/private/util.py +2 -1
- {eflips_depot-4.12.2.dist-info → eflips_depot-4.13.1.dist-info}/METADATA +3 -3
- {eflips_depot-4.12.2.dist-info → eflips_depot-4.13.1.dist-info}/RECORD +9 -9
- {eflips_depot-4.12.2.dist-info → eflips_depot-4.13.1.dist-info}/LICENSE.md +0 -0
- {eflips_depot-4.12.2.dist-info → eflips_depot-4.13.1.dist-info}/WHEEL +0 -0
eflips/depot/__init__.py
CHANGED
|
@@ -54,9 +54,49 @@ from eflips.depot.standalone import VehicleGenerator, SimpleTrip, Timetable
|
|
|
54
54
|
from eflips.depot.validation import Validator
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
class DelayedTripException(Exception):
|
|
58
|
+
def __init__(self):
|
|
59
|
+
self._delayed_trips = []
|
|
60
|
+
|
|
61
|
+
def raise_later(self, simple_trip):
|
|
62
|
+
self._delayed_trips.append(simple_trip)
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def has_errors(self):
|
|
66
|
+
return len(self._delayed_trips) > 0
|
|
67
|
+
|
|
68
|
+
def __str__(self):
|
|
69
|
+
trip_names = ", ".join(
|
|
70
|
+
f"{trip.ID} originally departure at {trip.std}"
|
|
71
|
+
for trip in self._delayed_trips
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
f"The following blocks/rotations are delayed. "
|
|
76
|
+
f"Ignoring this error will write related depot events into database. However, this may lead to errors due "
|
|
77
|
+
f"to conflicts with driving events: {trip_names}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
57
81
|
class UnstableSimulationException(Exception):
|
|
58
|
-
|
|
82
|
+
def __init__(self):
|
|
83
|
+
self._unstable_trips = []
|
|
59
84
|
|
|
85
|
+
def raise_later(self, simple_trip):
|
|
86
|
+
self._unstable_trips.append(simple_trip)
|
|
60
87
|
|
|
61
|
-
|
|
62
|
-
|
|
88
|
+
@property
|
|
89
|
+
def has_errors(self):
|
|
90
|
+
return len(self._unstable_trips) > 0
|
|
91
|
+
|
|
92
|
+
def __str__(self):
|
|
93
|
+
trip_names = ", ".join(trip.ID for trip in self._unstable_trips)
|
|
94
|
+
return (
|
|
95
|
+
f"The following blocks/rotations require a new vehicle. This suggests an unstable "
|
|
96
|
+
f" simulation result, where a repeated schedule might require more vehicles: {trip_names}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class MultipleErrors(Exception):
|
|
101
|
+
def __init__(self, errors):
|
|
102
|
+
self.errors = errors
|
eflips/depot/api/__init__.py
CHANGED
|
@@ -29,7 +29,6 @@ import logging
|
|
|
29
29
|
import os
|
|
30
30
|
import warnings
|
|
31
31
|
from collections import OrderedDict
|
|
32
|
-
from dataclasses import dataclass
|
|
33
32
|
from datetime import timedelta, datetime
|
|
34
33
|
from enum import Enum
|
|
35
34
|
from math import ceil
|
|
@@ -51,7 +50,6 @@ from eflips.model import (
|
|
|
51
50
|
Route,
|
|
52
51
|
ConsistencyWarning,
|
|
53
52
|
Station,
|
|
54
|
-
ConsumptionLut,
|
|
55
53
|
)
|
|
56
54
|
from sqlalchemy.orm import Session
|
|
57
55
|
|
|
@@ -59,6 +57,9 @@ import eflips.depot
|
|
|
59
57
|
from eflips.depot import (
|
|
60
58
|
DepotEvaluation,
|
|
61
59
|
SimulationHost,
|
|
60
|
+
UnstableSimulationException,
|
|
61
|
+
DelayedTripException,
|
|
62
|
+
MultipleErrors,
|
|
62
63
|
)
|
|
63
64
|
from eflips.depot.api.private.consumption import ConsumptionResult
|
|
64
65
|
from eflips.depot.api.private.consumption import (
|
|
@@ -637,16 +638,30 @@ def simulate_scenario(
|
|
|
637
638
|
ev = run_simulation(simulation_host)
|
|
638
639
|
try:
|
|
639
640
|
add_evaluation_to_database(scenario, ev, session)
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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)
|
|
650
665
|
|
|
651
666
|
match smart_charging_strategy:
|
|
652
667
|
case SmartChargingStrategy.NONE:
|
|
@@ -784,6 +799,8 @@ def init_simulation(
|
|
|
784
799
|
# Clear old vehicle counts, if they exist
|
|
785
800
|
eflips.globalConstants["depot"]["vehicle_count"] = {}
|
|
786
801
|
|
|
802
|
+
grouped_rotations = group_rotations_by_start_end_stop(scenario.id, session)
|
|
803
|
+
|
|
787
804
|
# We need to calculate roughly how many vehicles we need for each depot
|
|
788
805
|
for depot in session.query(Depot).filter(Depot.scenario_id == scenario.id).all():
|
|
789
806
|
depot_id = str(depot.id)
|
|
@@ -806,31 +823,32 @@ def init_simulation(
|
|
|
806
823
|
depot_id
|
|
807
824
|
] = vehicle_count_dict[depot_id]
|
|
808
825
|
else:
|
|
809
|
-
# Calculate it from the
|
|
826
|
+
# Calculate it from the amount of rotations with a 4x margin because 4 times of repetition
|
|
827
|
+
# in repeat_vehicle_schedules()
|
|
828
|
+
rotations = grouped_rotations[(depot.station, depot.station)]
|
|
810
829
|
|
|
811
830
|
for vehicle_type in vehicle_types_for_depot:
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
)
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
] = (vehicle_count * 2)
|
|
831
|
+
vehicle_type_object = (
|
|
832
|
+
session.query(VehicleType)
|
|
833
|
+
.filter(
|
|
834
|
+
VehicleType.id == vehicle_type,
|
|
835
|
+
VehicleType.scenario_id == scenario.id,
|
|
836
|
+
)
|
|
837
|
+
.one()
|
|
838
|
+
)
|
|
839
|
+
vehicle_count = len(rotations.get(vehicle_type_object, []))
|
|
840
|
+
|
|
841
|
+
if vehicle_count > 0:
|
|
842
|
+
eflips.globalConstants["depot"]["vehicle_count"][depot_id][
|
|
843
|
+
vehicle_type
|
|
844
|
+
] = (
|
|
845
|
+
vehicle_count
|
|
846
|
+
* 4 # We multiply by 4 because we repeat the vehicle schedules 4 times
|
|
847
|
+
)
|
|
848
|
+
else:
|
|
849
|
+
warnings.warn(
|
|
850
|
+
f"There are no rotations assigned to type {vehicle_type_object} in depot {depot_id}"
|
|
851
|
+
)
|
|
834
852
|
|
|
835
853
|
# We need to put the vehicle type objects into the GlobalConstants
|
|
836
854
|
for vehicle_type in (
|
|
@@ -1023,6 +1041,9 @@ def add_evaluation_to_database(
|
|
|
1023
1041
|
f"one waiting area."
|
|
1024
1042
|
)
|
|
1025
1043
|
|
|
1044
|
+
unstable_exp = UnstableSimulationException()
|
|
1045
|
+
delay_exp = DelayedTripException()
|
|
1046
|
+
|
|
1026
1047
|
for current_vehicle in depot_evaluation.vehicle_generator.items:
|
|
1027
1048
|
# Vehicle-layer operations
|
|
1028
1049
|
|
|
@@ -1049,23 +1070,16 @@ def add_evaluation_to_database(
|
|
|
1049
1070
|
# handled. It is usually the departure time of the last copy trip in the "early-shifted" copy
|
|
1050
1071
|
# schedules and the departure time of the first copy trip in the "late-shifted" copy schedules.
|
|
1051
1072
|
) = get_finished_schedules_per_vehicle(
|
|
1052
|
-
dict_of_events,
|
|
1073
|
+
dict_of_events,
|
|
1074
|
+
current_vehicle.finished_trips,
|
|
1075
|
+
current_vehicle_db.id,
|
|
1076
|
+
unstable_exp,
|
|
1077
|
+
delay_exp,
|
|
1053
1078
|
)
|
|
1054
1079
|
|
|
1055
|
-
|
|
1056
|
-
assert earliest_time is not None and latest_time is not None
|
|
1057
|
-
|
|
1058
|
-
except AssertionError as e:
|
|
1059
|
-
warnings.warn(
|
|
1060
|
-
f"Vehicle {current_vehicle_db.id} has only copied trips. The profiles of this vehicle "
|
|
1061
|
-
f"will not be written into database."
|
|
1062
|
-
)
|
|
1080
|
+
if schedule_current_vehicle is None:
|
|
1063
1081
|
continue
|
|
1064
1082
|
|
|
1065
|
-
assert (
|
|
1066
|
-
earliest_time < latest_time
|
|
1067
|
-
), f"Earliest time {earliest_time} is not less than latest time {latest_time}."
|
|
1068
|
-
|
|
1069
1083
|
list_of_assigned_schedules.extend(schedule_current_vehicle)
|
|
1070
1084
|
|
|
1071
1085
|
generate_vehicle_events(
|
|
@@ -1082,16 +1096,6 @@ def add_evaluation_to_database(
|
|
|
1082
1096
|
|
|
1083
1097
|
add_soc_to_events(dict_of_events, current_vehicle.battery_logs)
|
|
1084
1098
|
|
|
1085
|
-
try:
|
|
1086
|
-
assert (not dict_of_events) is False
|
|
1087
|
-
except AssertionError as e:
|
|
1088
|
-
warnings.warn(
|
|
1089
|
-
f"Vehicle {current_vehicle_db.id} has no valid events. The vehicle will not be written "
|
|
1090
|
-
f"into database."
|
|
1091
|
-
)
|
|
1092
|
-
|
|
1093
|
-
continue
|
|
1094
|
-
|
|
1095
1099
|
add_events_into_database(
|
|
1096
1100
|
current_vehicle_db,
|
|
1097
1101
|
dict_of_events,
|
|
@@ -1104,6 +1108,16 @@ def add_evaluation_to_database(
|
|
|
1104
1108
|
update_vehicle_in_rotation(session, scenario, list_of_assigned_schedules)
|
|
1105
1109
|
update_waiting_events(session, scenario, waiting_area_id)
|
|
1106
1110
|
|
|
1111
|
+
errors = []
|
|
1112
|
+
|
|
1113
|
+
if delay_exp.has_errors:
|
|
1114
|
+
errors.append(delay_exp)
|
|
1115
|
+
if unstable_exp.has_errors:
|
|
1116
|
+
errors.append(unstable_exp)
|
|
1117
|
+
|
|
1118
|
+
if len(errors) > 0:
|
|
1119
|
+
raise MultipleErrors(errors)
|
|
1120
|
+
|
|
1107
1121
|
|
|
1108
1122
|
def generate_depot_optimal_size(
|
|
1109
1123
|
scenario: Union[Scenario, int, Any],
|
|
@@ -1112,6 +1126,7 @@ def generate_depot_optimal_size(
|
|
|
1112
1126
|
database_url: Optional[str] = None,
|
|
1113
1127
|
delete_existing_depot: bool = False,
|
|
1114
1128
|
use_consumption_lut: bool = False,
|
|
1129
|
+
repetition_period: Optional[timedelta] = None,
|
|
1115
1130
|
) -> None:
|
|
1116
1131
|
"""
|
|
1117
1132
|
Generates an optimal depot layout with the smallest possible size for each depot in the scenario. Line charging areas
|
|
@@ -1125,8 +1140,12 @@ def generate_depot_optimal_size(
|
|
|
1125
1140
|
:param database_url: An optional database URL. Used if no database url is given by the environment variable.
|
|
1126
1141
|
:param delete_existing_depot: If there is already a depot existing in this scenario, set True to delete this
|
|
1127
1142
|
existing depot. Set to False and a ValueError will be raised if there is a depot in this scenario.
|
|
1128
|
-
:param
|
|
1143
|
+
:param use_consumption_lut: If True, the depot layout will be generated based on the consumption lookup table.
|
|
1129
1144
|
If False, constant consumption stored in VehicleType table will be used.
|
|
1145
|
+
:param repetition_period: An optional timedelta object specifying the period of the vehicle schedules. If not
|
|
1146
|
+
specified, a default repetition period will be generated in simulate_scenario(). If the depot layout generated
|
|
1147
|
+
in this function will be used for further simulations, make sure that the repetition period is set to the same
|
|
1148
|
+
value as in the simulation.
|
|
1130
1149
|
|
|
1131
1150
|
:return: None. The depot layout will be added to the database.
|
|
1132
1151
|
|
|
@@ -1183,10 +1202,12 @@ def generate_depot_optimal_size(
|
|
|
1183
1202
|
|
|
1184
1203
|
num_rotations_for_scenario: Dict[Station, int] = {}
|
|
1185
1204
|
|
|
1205
|
+
grouped_rotations = group_rotations_by_start_end_stop(scenario.id, session)
|
|
1206
|
+
|
|
1186
1207
|
for (
|
|
1187
1208
|
first_last_stop_tup,
|
|
1188
1209
|
vehicle_type_dict,
|
|
1189
|
-
) in
|
|
1210
|
+
) in grouped_rotations.items():
|
|
1190
1211
|
first_stop, last_stop = first_last_stop_tup
|
|
1191
1212
|
if first_stop != last_stop:
|
|
1192
1213
|
raise ValueError("First and last stop of a rotation are not the same.")
|
|
@@ -1230,6 +1251,7 @@ def generate_depot_optimal_size(
|
|
|
1230
1251
|
session,
|
|
1231
1252
|
standard_block_length,
|
|
1232
1253
|
charging_power,
|
|
1254
|
+
repetition_period,
|
|
1233
1255
|
)
|
|
1234
1256
|
|
|
1235
1257
|
depot_capacities_for_scenario[station] = vt_capacities_for_station
|
|
@@ -1239,8 +1261,44 @@ def generate_depot_optimal_size(
|
|
|
1239
1261
|
|
|
1240
1262
|
outer_savepoint.rollback()
|
|
1241
1263
|
|
|
1264
|
+
# Estimation of the number of shunting and cleaning slots
|
|
1265
|
+
|
|
1242
1266
|
# Create depot using the calculated capacities
|
|
1243
1267
|
for depot_station, capacities in depot_capacities_for_scenario.items():
|
|
1268
|
+
vehicle_type_rot_dict = grouped_rotations[depot_station, depot_station]
|
|
1269
|
+
|
|
1270
|
+
all_rotations_this_depot = []
|
|
1271
|
+
|
|
1272
|
+
for vehicle_type, rotations in vehicle_type_rot_dict.items():
|
|
1273
|
+
all_rotations_this_depot.extend(rotations)
|
|
1274
|
+
|
|
1275
|
+
# sort the rotations by their start time
|
|
1276
|
+
all_rotations_this_depot.sort(key=lambda r: r.trips[0].departure_time)
|
|
1277
|
+
|
|
1278
|
+
start_time = all_rotations_this_depot[0].trips[0].departure_time
|
|
1279
|
+
end_time = all_rotations_this_depot[-1].trips[-1].arrival_time
|
|
1280
|
+
|
|
1281
|
+
elapsed_time = (end_time - start_time).total_seconds()
|
|
1282
|
+
# make them into a numpy with 30 min resolution
|
|
1283
|
+
import numpy as np
|
|
1284
|
+
|
|
1285
|
+
TIME_RESOLUTION = 30 * 60 # 30 minutes in seconds
|
|
1286
|
+
|
|
1287
|
+
time_range = np.zeros(int(elapsed_time / TIME_RESOLUTION) + 1)
|
|
1288
|
+
# calculate the number of rotations per time slot
|
|
1289
|
+
for rot in all_rotations_this_depot:
|
|
1290
|
+
start_time_index = int(
|
|
1291
|
+
(rot.trips[0].departure_time - start_time).total_seconds()
|
|
1292
|
+
// TIME_RESOLUTION
|
|
1293
|
+
)
|
|
1294
|
+
end_time_index = int(
|
|
1295
|
+
(rot.trips[-1].arrival_time - start_time).total_seconds()
|
|
1296
|
+
// TIME_RESOLUTION
|
|
1297
|
+
)
|
|
1298
|
+
# interpolate the start and end time to the time range
|
|
1299
|
+
|
|
1300
|
+
time_range[start_time_index : end_time_index + 1] += 1
|
|
1301
|
+
|
|
1244
1302
|
generate_depot(
|
|
1245
1303
|
capacities,
|
|
1246
1304
|
depot_station,
|
|
@@ -1248,6 +1306,6 @@ def generate_depot_optimal_size(
|
|
|
1248
1306
|
session,
|
|
1249
1307
|
standard_block_length=standard_block_length,
|
|
1250
1308
|
charging_power=charging_power,
|
|
1251
|
-
num_shunting_slots=
|
|
1252
|
-
num_cleaning_slots=
|
|
1309
|
+
num_shunting_slots=int(max(time_range)),
|
|
1310
|
+
num_cleaning_slots=int(max(time_range)),
|
|
1253
1311
|
)
|
|
@@ -3,7 +3,7 @@ import logging
|
|
|
3
3
|
import math
|
|
4
4
|
from datetime import timedelta
|
|
5
5
|
from enum import Enum, auto
|
|
6
|
-
from typing import Dict, List, Tuple
|
|
6
|
+
from typing import Dict, List, Tuple, Optional
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import sqlalchemy.orm
|
|
@@ -583,12 +583,17 @@ def generate_depot(
|
|
|
583
583
|
session.add_all(assocs) # It's complete, so add all at once
|
|
584
584
|
|
|
585
585
|
# Create shared waiting area
|
|
586
|
+
rotation_count = len(
|
|
587
|
+
session.query(Rotation).filter(Rotation.scenario_id == scenario.id).all()
|
|
588
|
+
)
|
|
589
|
+
|
|
586
590
|
waiting_area = Area(
|
|
587
591
|
scenario=scenario,
|
|
588
592
|
name=f"Waiting Area for every type of vehicle",
|
|
589
593
|
depot=depot,
|
|
590
594
|
area_type=AreaType.DIRECT_ONESIDE,
|
|
591
|
-
capacity=
|
|
595
|
+
capacity=rotation_count
|
|
596
|
+
* 4, # Initialize with 4 times of rotation count because all rotations are copied three times. Assuming each rotation needs a vehicle.
|
|
592
597
|
)
|
|
593
598
|
session.add(waiting_area)
|
|
594
599
|
|
|
@@ -849,6 +854,7 @@ def depot_smallest_possible_size(
|
|
|
849
854
|
session: sqlalchemy.orm.session.Session,
|
|
850
855
|
standard_block_length: int = 6,
|
|
851
856
|
charging_power: float = 90,
|
|
857
|
+
repetition_period: Optional[timedelta] = None,
|
|
852
858
|
) -> Dict[VehicleType, Dict[AreaType, None | int]]:
|
|
853
859
|
"""
|
|
854
860
|
Identifies the smallest (in terms of area footprint) depot that can still fit the required vehicles.
|
|
@@ -919,7 +925,12 @@ def depot_smallest_possible_size(
|
|
|
919
925
|
from eflips.depot.api import SmartChargingStrategy
|
|
920
926
|
|
|
921
927
|
# Simulate the depot
|
|
922
|
-
|
|
928
|
+
|
|
929
|
+
simulate_scenario(
|
|
930
|
+
scenario,
|
|
931
|
+
smart_charging_strategy=SmartChargingStrategy.NONE,
|
|
932
|
+
repetition_period=repetition_period,
|
|
933
|
+
)
|
|
923
934
|
|
|
924
935
|
# Find the peak usage of the depot
|
|
925
936
|
peak_occupancies: Dict[VehicleType, Dict[AreaType, int]] = find_peak_usage(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import itertools
|
|
3
3
|
import logging
|
|
4
|
+
import warnings
|
|
4
5
|
from datetime import timedelta
|
|
5
6
|
from typing import List, Dict
|
|
6
7
|
|
|
@@ -13,7 +14,11 @@ from eflips.depot import UnstableSimulationException, DelayedTripException
|
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def get_finished_schedules_per_vehicle(
|
|
16
|
-
dict_of_events,
|
|
17
|
+
dict_of_events,
|
|
18
|
+
list_of_finished_trips: List,
|
|
19
|
+
db_vehicle_id: int,
|
|
20
|
+
unstable_exp: UnstableSimulationException,
|
|
21
|
+
delay_exp: DelayedTripException,
|
|
17
22
|
):
|
|
18
23
|
"""
|
|
19
24
|
This function completes the following tasks:
|
|
@@ -42,47 +47,62 @@ def get_finished_schedules_per_vehicle(
|
|
|
42
47
|
:return: A tuple of three elements. The first element is a list of finished schedules of the vehicle. The second and
|
|
43
48
|
third elements are the earliest and latest time of the vehicle's schedules.
|
|
44
49
|
"""
|
|
45
|
-
finished_schedules = []
|
|
46
50
|
|
|
47
51
|
list_of_finished_trips.sort(key=lambda x: x.atd)
|
|
48
|
-
earliest_time = None
|
|
49
|
-
latest_time = None
|
|
50
52
|
|
|
51
53
|
for i in range(len(list_of_finished_trips)):
|
|
52
|
-
|
|
53
|
-
|
|
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
|
+
if list_of_finished_trips[i].atd != list_of_finished_trips[i].std:
|
|
55
|
+
delay_exp.raise_later(list_of_finished_trips[i])
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
current_trip = list_of_finished_trips[i]
|
|
57
|
+
non_copy_trips = [trip for trip in list_of_finished_trips if trip.is_copy is False]
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
raise UnstableSimulationException(
|
|
70
|
-
f"New Vehicle required for the rotation/block {current_trip.ID}, which suggests the fleet or the "
|
|
71
|
-
f"infrastructure might not be enough for the full electrification. Please add charging "
|
|
72
|
-
f"interfaces or increase charging power ."
|
|
73
|
-
)
|
|
59
|
+
if len(non_copy_trips) == 0:
|
|
60
|
+
warnings.warn(
|
|
61
|
+
f"Vehicle {db_vehicle_id} has only copied trips and will not be stored in the database. "
|
|
62
|
+
f"This might suggest an unstable simulation result. "
|
|
63
|
+
f"Repetition of this schedule may require more vehicles."
|
|
64
|
+
)
|
|
74
65
|
|
|
75
|
-
|
|
76
|
-
# Vehicle's last trip is a non-copy trip
|
|
77
|
-
if earliest_time is None:
|
|
78
|
-
earliest_time = list_of_finished_trips[i - 1].ata
|
|
79
|
-
latest_time = list_of_finished_trips[i].ata
|
|
66
|
+
return None, None, None
|
|
80
67
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
68
|
+
idx_first_orig_trip = list_of_finished_trips.index(non_copy_trips[0])
|
|
69
|
+
idx_last_orig_trip = list_of_finished_trips.index(non_copy_trips[-1])
|
|
70
|
+
|
|
71
|
+
if idx_first_orig_trip == 0:
|
|
72
|
+
unstable_exp.raise_later(non_copy_trips[0])
|
|
73
|
+
earliest_time = non_copy_trips[0].ata
|
|
74
|
+
|
|
75
|
+
else:
|
|
76
|
+
earliest_time = list_of_finished_trips[idx_first_orig_trip - 1].ata
|
|
77
|
+
|
|
78
|
+
if idx_last_orig_trip == len(list_of_finished_trips) - 1:
|
|
79
|
+
# the last trip of this vehicle is a non-copy trip
|
|
80
|
+
all_processes_current_vehicle = non_copy_trips[-1].vehicle.logger.loggedData[
|
|
81
|
+
"dwd.active_processes_copy"
|
|
82
|
+
]
|
|
83
|
+
time_stamps = [k for k, v in all_processes_current_vehicle.items()]
|
|
84
|
+
last_time_stamp = max(time_stamps)
|
|
85
|
+
if len(all_processes_current_vehicle[last_time_stamp]) == 0:
|
|
86
|
+
latest_time = last_time_stamp
|
|
87
|
+
else:
|
|
88
|
+
current_processes = all_processes_current_vehicle[last_time_stamp]
|
|
89
|
+
|
|
90
|
+
latest_time = 0
|
|
91
|
+
for p in current_processes:
|
|
92
|
+
latest_time = max((p.starts[0] + p.dur), latest_time)
|
|
93
|
+
|
|
94
|
+
else:
|
|
95
|
+
latest_time = list_of_finished_trips[idx_last_orig_trip + 1].atd
|
|
96
|
+
|
|
97
|
+
assert earliest_time <= latest_time
|
|
98
|
+
|
|
99
|
+
finished_schedules = []
|
|
100
|
+
for non_copy_trip in non_copy_trips:
|
|
101
|
+
finished_schedules.append((int(non_copy_trip.ID), db_vehicle_id))
|
|
102
|
+
dict_of_events[non_copy_trip.atd] = {
|
|
103
|
+
"type": "Trip",
|
|
104
|
+
"id": int(non_copy_trip.ID),
|
|
105
|
+
}
|
|
86
106
|
|
|
87
107
|
return finished_schedules, earliest_time, latest_time
|
|
88
108
|
|
eflips/depot/api/private/util.py
CHANGED
|
@@ -18,7 +18,8 @@ from eflips.model import (
|
|
|
18
18
|
Depot,
|
|
19
19
|
Temperatures,
|
|
20
20
|
)
|
|
21
|
-
from
|
|
21
|
+
from eflips.model import create_engine
|
|
22
|
+
from sqlalchemy import inspect
|
|
22
23
|
from sqlalchemy.orm import Session
|
|
23
24
|
|
|
24
25
|
from eflips.depot import SimpleTrip, Timetable as EflipsTimeTable
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: eflips-depot
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.13.1
|
|
4
4
|
Summary: Depot Simulation for eFLIPS
|
|
5
5
|
License: AGPL-3.0-or-later
|
|
6
6
|
Author: Enrico Lauth
|
|
@@ -13,8 +13,8 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
15
|
Requires-Dist: eflips (>=0.1.3,<0.2.0)
|
|
16
|
-
Requires-Dist: eflips-model (
|
|
17
|
-
Requires-Dist: eflips-opt (>=0.3.
|
|
16
|
+
Requires-Dist: eflips-model (>=10.0.0,<11.0.0)
|
|
17
|
+
Requires-Dist: eflips-opt (>=0.3.0,<0.4.0)
|
|
18
18
|
Requires-Dist: pandas (>=2.2.0,<3.0.0)
|
|
19
19
|
Requires-Dist: scipy (>=1.14.0,<2.0.0)
|
|
20
20
|
Requires-Dist: simpy (>=4.0.1,<5.0.0)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
eflips/depot/__init__.py,sha256=
|
|
2
|
-
eflips/depot/api/__init__.py,sha256=
|
|
1
|
+
eflips/depot/__init__.py,sha256=p-oqm1LmTuIk5PRiWDyychBhwAsr-9Rw7dKmRCZ58wc,2968
|
|
2
|
+
eflips/depot/api/__init__.py,sha256=0iSCRtff8Ljb8g4fUmLxqWXI_u-qTE6wv_Jj4fgTXLM,57403
|
|
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=
|
|
7
|
-
eflips/depot/api/private/results_to_database.py,sha256=
|
|
8
|
-
eflips/depot/api/private/util.py,sha256=
|
|
6
|
+
eflips/depot/api/private/depot.py,sha256=uESFVBhHybDN-LzvceKHlFSfsVsBfh5gaOuTxspX6rY,44055
|
|
7
|
+
eflips/depot/api/private/results_to_database.py,sha256=FBlZRKqAIG80tFyuf5vCGHRycWKYkm53jFxJEk9t1NA,25984
|
|
8
|
+
eflips/depot/api/private/util.py,sha256=DasTkuGUhlBpY_BtTFWoxSNZU_CRyM3RqEDgO07Eks8,17990
|
|
9
9
|
eflips/depot/configuration.py,sha256=Op3hlir-dEN7yHr0kTqbYANoCBKFWK6uKOv3NJl8w_w,35678
|
|
10
10
|
eflips/depot/depot.py,sha256=pREutXtJlDxjgfwRobAy7UqTHh-tldbVWHN8DIyxs8s,106986
|
|
11
11
|
eflips/depot/evaluation.py,sha256=qqXyP4jA1zFcKuWhliQ6n25ZlGl9mJV-vtXf0yu8WN8,140842
|
|
@@ -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.
|
|
40
|
-
eflips_depot-4.
|
|
41
|
-
eflips_depot-4.
|
|
42
|
-
eflips_depot-4.
|
|
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,,
|
|
File without changes
|
|
File without changes
|