eflips-depot 1.0.1__tar.gz → 1.1.1__tar.gz
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-1.0.1 → eflips_depot-1.1.1}/PKG-INFO +2 -2
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/api/__init__.py +309 -47
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/api/defaults/default_settings.json +2 -2
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/api/private.py +1 -1
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/pyproject.toml +2 -2
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/LICENSE.md +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/README.md +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/__init__.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/configuration.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/depot.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/evaluation.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/filters.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/input_epex_power_price.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/__init__.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/doc/__init__.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/doc/direct_details.pdf +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/evaluation.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/opt_tools/__init__.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/opt_tools/crossover.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/opt_tools/fitness_c_urfd.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/opt_tools/fitness_util.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/opt_tools/init.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/opt_tools/mutation.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/optimize_c_urfd.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/packing.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/settings.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/template_creation.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/util.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/plots.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/processes.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/rating.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/resources.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/settings_config.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/simple_vehicle.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/simulation.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/smart_charging.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/standalone.py +0 -0
- {eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: eflips-depot
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Depot Simulation for eFLIPS
|
|
5
5
|
License: AGPLv3
|
|
6
6
|
Author: Enrico Lauth
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Requires-Dist: eflips (>=0.1.0,<0.2.0)
|
|
14
|
-
Requires-Dist: eflips-model (>=
|
|
14
|
+
Requires-Dist: eflips-model (>=2.1.2,<3.0.0)
|
|
15
15
|
Requires-Dist: pandas (>=2.1.4,<3.0.0)
|
|
16
16
|
Requires-Dist: simpy (>=4.0.1,<5.0.0)
|
|
17
17
|
Requires-Dist: xlrd (<=1.2.0)
|
|
@@ -8,12 +8,26 @@ the simulation, which can be added to the database using the :func:`eflips.depot
|
|
|
8
8
|
function.
|
|
9
9
|
"""
|
|
10
10
|
import os
|
|
11
|
+
from contextlib import contextmanager
|
|
11
12
|
from datetime import timedelta
|
|
12
13
|
from math import ceil
|
|
13
|
-
from typing import Any, Dict, Optional, Union
|
|
14
|
+
from typing import Any, Dict, Optional, Union, Tuple
|
|
14
15
|
|
|
15
16
|
import sqlalchemy.orm
|
|
16
|
-
from eflips.model import
|
|
17
|
+
from eflips.model import (
|
|
18
|
+
Event,
|
|
19
|
+
EventType,
|
|
20
|
+
Rotation,
|
|
21
|
+
Scenario,
|
|
22
|
+
Vehicle,
|
|
23
|
+
Depot,
|
|
24
|
+
Plan,
|
|
25
|
+
Process,
|
|
26
|
+
AssocPlanProcess,
|
|
27
|
+
AssocAreaProcess,
|
|
28
|
+
Area,
|
|
29
|
+
AreaType,
|
|
30
|
+
)
|
|
17
31
|
from sqlalchemy import create_engine, inspect
|
|
18
32
|
from sqlalchemy.orm import Session
|
|
19
33
|
|
|
@@ -28,6 +42,272 @@ from eflips.depot.api.private import (
|
|
|
28
42
|
)
|
|
29
43
|
|
|
30
44
|
|
|
45
|
+
@contextmanager
|
|
46
|
+
def create_session(
|
|
47
|
+
scenario: Union[Scenario, int, Any], database_url: Optional[str] = None
|
|
48
|
+
) -> Tuple[Session, Scenario]:
|
|
49
|
+
"""
|
|
50
|
+
This method takes a scenario, which can be either a :class:`eflips.model.Scenario` object, an integer specifying
|
|
51
|
+
the ID of a scenario in the database, or any other object that has an attribute `id` that is an integer. It then
|
|
52
|
+
creates a SQLAlchemy session and returns it. If the scenario is a :class:`eflips.model.Scenario` object, the
|
|
53
|
+
session is created and returned. If the scenario is an integer or an object with an `id` attribute, the session
|
|
54
|
+
is created, returned and closed after the context manager is exited.
|
|
55
|
+
:param scenario:
|
|
56
|
+
:return: Yield a Tuple of the session and the scenario.
|
|
57
|
+
"""
|
|
58
|
+
if isinstance(scenario, Scenario):
|
|
59
|
+
session = inspect(scenario).session
|
|
60
|
+
do_close_session = False
|
|
61
|
+
elif isinstance(scenario, int) or hasattr(scenario, "id"):
|
|
62
|
+
do_close_session = True
|
|
63
|
+
if isinstance(scenario, int):
|
|
64
|
+
scenario_id = scenario
|
|
65
|
+
else:
|
|
66
|
+
scenario_id = scenario.id
|
|
67
|
+
|
|
68
|
+
if database_url is None:
|
|
69
|
+
if "DATABASE_URL" in os.environ:
|
|
70
|
+
database_url = os.environ.get("DATABASE_URL")
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError("No database URL specified.")
|
|
73
|
+
|
|
74
|
+
engine = create_engine(database_url)
|
|
75
|
+
session = Session(engine)
|
|
76
|
+
scenario = session.query(Scenario).filter(Scenario.id == scenario_id).one()
|
|
77
|
+
else:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
"The scenario parameter must be either a Scenario object, an integer or an object with an 'id' attribute."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
yield session, scenario
|
|
84
|
+
finally:
|
|
85
|
+
if do_close_session:
|
|
86
|
+
session.commit()
|
|
87
|
+
session.close()
|
|
88
|
+
engine.dispose()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _delete_depot(scenario: Scenario, session: Session):
|
|
92
|
+
"""This function deletes all depot-related data from the database for a given scenario. Used before a new depot
|
|
93
|
+
in this scenario is created.
|
|
94
|
+
|
|
95
|
+
:param scenario: The scenario to be simulated
|
|
96
|
+
:param session: The database session
|
|
97
|
+
|
|
98
|
+
:return: None. The depot-related data will be deleted from the database.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
# Delete assocs
|
|
102
|
+
session.query(AssocPlanProcess).filter(
|
|
103
|
+
AssocPlanProcess.scenario_id == scenario.id
|
|
104
|
+
).delete()
|
|
105
|
+
list_of_area = session.query(Area).filter(Area.scenario_id == scenario.id).all()
|
|
106
|
+
|
|
107
|
+
for area in list_of_area:
|
|
108
|
+
session.query(AssocAreaProcess).filter(
|
|
109
|
+
AssocAreaProcess.area_id == area.id
|
|
110
|
+
).delete()
|
|
111
|
+
session.query(Event).filter(Event.area_id == area.id).delete()
|
|
112
|
+
|
|
113
|
+
# delete processes
|
|
114
|
+
session.query(Process).filter(Process.scenario_id == scenario.id).delete()
|
|
115
|
+
|
|
116
|
+
# delete areas
|
|
117
|
+
session.query(Area).filter(Area.scenario_id == scenario.id).delete()
|
|
118
|
+
# delete depot
|
|
119
|
+
session.query(Depot).filter(Depot.scenario_id == scenario.id).delete()
|
|
120
|
+
# delete plan
|
|
121
|
+
session.query(Plan).filter(Plan.scenario_id == scenario.id).delete()
|
|
122
|
+
# delete assoc_plan_process
|
|
123
|
+
|
|
124
|
+
session.commit()
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def generate_depot_layout(
|
|
128
|
+
scenario: Union[Scenario, int, Any],
|
|
129
|
+
charging_power: float,
|
|
130
|
+
database_url: Optional[str] = None,
|
|
131
|
+
delete_existing_depot: bool = False,
|
|
132
|
+
capacity: Optional[int] = None,
|
|
133
|
+
):
|
|
134
|
+
"""
|
|
135
|
+
This function generates a simple depot layout according to the vehicle types and rotations in the scenario.
|
|
136
|
+
|
|
137
|
+
For each vehicle type, it generates 3 areas: arrival_area, charging_area, and standby-departure_area, all with type
|
|
138
|
+
DIRECT_ONESIDE. The capacity of each area can be specified by user, or generated according to number of
|
|
139
|
+
rotations. Each area has a list of available processes. When specified by user, the capacity of all areas will be
|
|
140
|
+
the same.
|
|
141
|
+
|
|
142
|
+
A default plan will also be generated, which includes the following default processes: standby_arrival, cleaning,
|
|
143
|
+
charging and standby_departure. Each vehicle will be processed with this exact order (stancby_arrival is optional
|
|
144
|
+
because it only happens if a vehicle needs to wait for the next process).
|
|
145
|
+
|
|
146
|
+
Using this function causes deleting the original depot in this scenario.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
:param scenario: The scenario to be simulated
|
|
150
|
+
:param session: The database session
|
|
151
|
+
:param charging_power: the charging power of the charging area in kW
|
|
152
|
+
:param delete_existing_depot: if there is already a depot existing in this scenario, set True to delete this
|
|
153
|
+
existing depot. Set to False and a ValueError will be raised if there is a depot in this scenario.
|
|
154
|
+
:param capacity: capacity of each area. If not specified, the capacity will be generated according to the rotation.
|
|
155
|
+
|
|
156
|
+
:return: None. The depot layout will be added to the database.
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
with create_session(scenario, database_url) as (session, scenario):
|
|
160
|
+
# Handles existing depot
|
|
161
|
+
if session.query(Depot).filter(Depot.scenario_id == scenario.id).count() != 0:
|
|
162
|
+
if delete_existing_depot is False:
|
|
163
|
+
raise ValueError("Depot already exists.")
|
|
164
|
+
else:
|
|
165
|
+
_delete_depot(scenario, session)
|
|
166
|
+
|
|
167
|
+
# Create a simple depot
|
|
168
|
+
depot = Depot(scenario=scenario, name="Test Depot", name_short="TD")
|
|
169
|
+
session.add(depot)
|
|
170
|
+
|
|
171
|
+
# Create plan
|
|
172
|
+
plan = Plan(scenario=scenario, name="Test Plan")
|
|
173
|
+
session.add(plan)
|
|
174
|
+
|
|
175
|
+
depot.default_plan = plan
|
|
176
|
+
|
|
177
|
+
# Create processes
|
|
178
|
+
standby_arrival = Process(
|
|
179
|
+
name="Standby Arrival",
|
|
180
|
+
scenario=scenario,
|
|
181
|
+
dispatchable=False,
|
|
182
|
+
)
|
|
183
|
+
clean = Process(
|
|
184
|
+
name="Arrival Cleaning",
|
|
185
|
+
scenario=scenario,
|
|
186
|
+
dispatchable=False,
|
|
187
|
+
duration=timedelta(minutes=30),
|
|
188
|
+
)
|
|
189
|
+
charging = Process(
|
|
190
|
+
name="Charging",
|
|
191
|
+
scenario=scenario,
|
|
192
|
+
dispatchable=False,
|
|
193
|
+
electric_power=charging_power,
|
|
194
|
+
)
|
|
195
|
+
standby_departure = Process(
|
|
196
|
+
name="Standby Pre-departure",
|
|
197
|
+
scenario=scenario,
|
|
198
|
+
dispatchable=True,
|
|
199
|
+
)
|
|
200
|
+
session.add(standby_arrival)
|
|
201
|
+
session.add(clean)
|
|
202
|
+
session.add(charging)
|
|
203
|
+
session.add(standby_departure)
|
|
204
|
+
|
|
205
|
+
for vehicle_type in scenario.vehicle_types:
|
|
206
|
+
list_of_rotations = [
|
|
207
|
+
rotation
|
|
208
|
+
for rotation in scenario.rotations
|
|
209
|
+
if rotation.vehicle_type_id == vehicle_type.id
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
max_vehicle_num = (
|
|
213
|
+
session.query(Rotation)
|
|
214
|
+
.filter(Rotation.vehicle_type_id == vehicle_type.id)
|
|
215
|
+
.filter(Rotation.scenario_id == scenario.id)
|
|
216
|
+
.count()
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Create areas
|
|
220
|
+
if max_vehicle_num != 0:
|
|
221
|
+
if capacity is None:
|
|
222
|
+
# Assuming each vehicle only be assigned to one rotation
|
|
223
|
+
parking_capacity = max_vehicle_num
|
|
224
|
+
|
|
225
|
+
else:
|
|
226
|
+
parking_capacity = capacity
|
|
227
|
+
|
|
228
|
+
# Create stand by arrival area
|
|
229
|
+
arrival_area = Area(
|
|
230
|
+
scenario=scenario,
|
|
231
|
+
name=f"Arrival for {vehicle_type.name_short}",
|
|
232
|
+
depot=depot,
|
|
233
|
+
area_type=AreaType.DIRECT_ONESIDE,
|
|
234
|
+
capacity=parking_capacity,
|
|
235
|
+
)
|
|
236
|
+
session.add(arrival_area)
|
|
237
|
+
arrival_area.vehicle_type = vehicle_type
|
|
238
|
+
|
|
239
|
+
# Create charging area
|
|
240
|
+
charging_area = Area(
|
|
241
|
+
scenario=scenario,
|
|
242
|
+
name=f"Direct Charging Area for {vehicle_type.name_short}",
|
|
243
|
+
depot=depot,
|
|
244
|
+
area_type=AreaType.DIRECT_ONESIDE,
|
|
245
|
+
capacity=parking_capacity,
|
|
246
|
+
)
|
|
247
|
+
session.add(charging_area)
|
|
248
|
+
charging_area.vehicle_type = vehicle_type
|
|
249
|
+
|
|
250
|
+
# Create cleaning area
|
|
251
|
+
|
|
252
|
+
list_of_rotations.sort(key=lambda x: x.trips[-1].arrival_time)
|
|
253
|
+
|
|
254
|
+
if capacity is None:
|
|
255
|
+
clean_capacity = 1
|
|
256
|
+
|
|
257
|
+
# Maximum number of vehicles that can park in the cleaning area according to rotation
|
|
258
|
+
for rot_idx in range(0, len(list_of_rotations)):
|
|
259
|
+
cleaning_interval_start = (
|
|
260
|
+
list_of_rotations[rot_idx].trips[-1].arrival_time
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Potential improvement: the "edge" between copy and non-copy schedules might need higher cleaning
|
|
264
|
+
# capacity than real, causing standby-arrival events. Considering adding repetition_period here
|
|
265
|
+
# This could be solved by implementing a sliging window that rolls over from e.g. sunday (last
|
|
266
|
+
# day to monday (first day) again, to calculate the load from both the beginning and end of the
|
|
267
|
+
# data.
|
|
268
|
+
for next_rot_idx in range(rot_idx + 1, len(list_of_rotations)):
|
|
269
|
+
arrival_time = (
|
|
270
|
+
list_of_rotations[next_rot_idx].trips[-1].arrival_time
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if arrival_time > cleaning_interval_start + clean.duration:
|
|
274
|
+
clean_capacity = max(
|
|
275
|
+
clean_capacity, next_rot_idx - rot_idx
|
|
276
|
+
)
|
|
277
|
+
break
|
|
278
|
+
|
|
279
|
+
else:
|
|
280
|
+
clean_capacity = capacity
|
|
281
|
+
|
|
282
|
+
cleaning_area = Area(
|
|
283
|
+
scenario=scenario,
|
|
284
|
+
name=f"Cleaning Area for {vehicle_type.name_short}",
|
|
285
|
+
depot=depot,
|
|
286
|
+
area_type=AreaType.DIRECT_ONESIDE,
|
|
287
|
+
capacity=clean_capacity,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
session.add(cleaning_area)
|
|
291
|
+
cleaning_area.vehicle_type = vehicle_type
|
|
292
|
+
|
|
293
|
+
arrival_area.processes.append(standby_arrival)
|
|
294
|
+
cleaning_area.processes.append(clean)
|
|
295
|
+
charging_area.processes.append(charging)
|
|
296
|
+
charging_area.processes.append(standby_departure)
|
|
297
|
+
|
|
298
|
+
assocs = [
|
|
299
|
+
AssocPlanProcess(
|
|
300
|
+
scenario=scenario, process=standby_arrival, plan=plan, ordinal=0
|
|
301
|
+
),
|
|
302
|
+
AssocPlanProcess(scenario=scenario, process=clean, plan=plan, ordinal=1),
|
|
303
|
+
AssocPlanProcess(scenario=scenario, process=charging, plan=plan, ordinal=2),
|
|
304
|
+
AssocPlanProcess(
|
|
305
|
+
scenario=scenario, process=standby_departure, plan=plan, ordinal=3
|
|
306
|
+
),
|
|
307
|
+
]
|
|
308
|
+
session.add_all(assocs)
|
|
309
|
+
|
|
310
|
+
|
|
31
311
|
def simulate_scenario(
|
|
32
312
|
scenario: Union[Scenario, int, Any],
|
|
33
313
|
simple_consumption_simulation: bool = False,
|
|
@@ -69,52 +349,26 @@ def simulate_scenario(
|
|
|
69
349
|
"""
|
|
70
350
|
|
|
71
351
|
# Step 0: Load the scenario
|
|
72
|
-
|
|
73
|
-
session = inspect(scenario).session
|
|
74
|
-
do_close_session = False
|
|
75
|
-
elif isinstance(scenario, int) or hasattr(scenario, "id"):
|
|
76
|
-
do_close_session = True
|
|
77
|
-
if isinstance(scenario, int):
|
|
78
|
-
scenario_id = scenario
|
|
79
|
-
else:
|
|
80
|
-
scenario_id = scenario.id
|
|
81
|
-
|
|
82
|
-
if database_url is None:
|
|
83
|
-
if "DATABASE_URL" in os.environ:
|
|
84
|
-
database_url = os.environ.get("DATABASE_URL")
|
|
85
|
-
else:
|
|
86
|
-
raise ValueError("No database URL specified.")
|
|
87
|
-
|
|
88
|
-
engine = create_engine(database_url)
|
|
89
|
-
session = Session(engine)
|
|
90
|
-
scenario = session.query(Scenario).filter(Scenario.id == scenario_id).one()
|
|
91
|
-
else:
|
|
92
|
-
raise ValueError(
|
|
93
|
-
"The scenario parameter must be either a Scenario object, an integer or an object with an 'id' attribute."
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
simulation_host = _init_simulation(
|
|
97
|
-
scenario=scenario,
|
|
98
|
-
simple_consumption_simulation=simple_consumption_simulation,
|
|
99
|
-
repetition_period=repetition_period,
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
ev = _run_simulation(simulation_host)
|
|
103
|
-
|
|
104
|
-
if calculate_exact_vehicle_count:
|
|
105
|
-
vehicle_counts = ev.nvehicles_used_calculation()
|
|
352
|
+
with create_session(scenario, database_url) as (session, scenario):
|
|
106
353
|
simulation_host = _init_simulation(
|
|
107
354
|
scenario=scenario,
|
|
108
355
|
simple_consumption_simulation=simple_consumption_simulation,
|
|
109
356
|
repetition_period=repetition_period,
|
|
110
|
-
vehicle_count_dict=vehicle_counts,
|
|
111
357
|
)
|
|
358
|
+
|
|
112
359
|
ev = _run_simulation(simulation_host)
|
|
113
360
|
|
|
114
|
-
|
|
361
|
+
if calculate_exact_vehicle_count:
|
|
362
|
+
vehicle_counts = ev.nvehicles_used_calculation()
|
|
363
|
+
simulation_host = _init_simulation(
|
|
364
|
+
scenario=scenario,
|
|
365
|
+
simple_consumption_simulation=simple_consumption_simulation,
|
|
366
|
+
repetition_period=repetition_period,
|
|
367
|
+
vehicle_count_dict=vehicle_counts,
|
|
368
|
+
)
|
|
369
|
+
ev = _run_simulation(simulation_host)
|
|
115
370
|
|
|
116
|
-
|
|
117
|
-
session.close()
|
|
371
|
+
_add_evaluation_to_database(scenario.id, ev, session)
|
|
118
372
|
|
|
119
373
|
|
|
120
374
|
def _init_simulation(
|
|
@@ -324,6 +578,7 @@ def _add_evaluation_to_database(
|
|
|
324
578
|
session.query(Event)
|
|
325
579
|
.filter(Event.scenario_id == scenario_id)
|
|
326
580
|
.filter(Event.event_type != EventType.DRIVING)
|
|
581
|
+
.filter(Event.area_id != None)
|
|
327
582
|
)
|
|
328
583
|
if non_driving_event_q.count() > 0:
|
|
329
584
|
raise ValueError(
|
|
@@ -578,13 +833,20 @@ def _add_evaluation_to_database(
|
|
|
578
833
|
f"Could not find Rotation {schedule_id} in scenario {scenario_id}."
|
|
579
834
|
)
|
|
580
835
|
else:
|
|
581
|
-
rotation_q.
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
836
|
+
old_vehicle_id = rotation_q.one().vehicle_id
|
|
837
|
+
if old_vehicle_id is None:
|
|
838
|
+
rotation_q.update({"vehicle_id": vehicle_id})
|
|
839
|
+
for trip in rotation_q.one().trips:
|
|
840
|
+
for event in trip.events:
|
|
841
|
+
event.vehicle_id = vehicle_id
|
|
842
|
+
else:
|
|
843
|
+
# If there already is a vehicle assigned to this rotation, we need to change all teh events by this
|
|
844
|
+
# vehicle to the new vehicle
|
|
845
|
+
event_q = session.query(Event).filter(
|
|
846
|
+
Event.vehicle_id == old_vehicle_id
|
|
847
|
+
)
|
|
848
|
+
event_q.update({"vehicle_id": vehicle_id})
|
|
849
|
+
rotation_q.update({"vehicle_id": vehicle_id})
|
|
588
850
|
|
|
589
851
|
# Write Events
|
|
590
852
|
session.add_all(list_of_events)
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
],
|
|
13
13
|
"FLEXPRINT_SWITCHES": {
|
|
14
14
|
"operations": false,
|
|
15
|
-
"dispatch":
|
|
15
|
+
"dispatch": false,
|
|
16
16
|
"objID": false,
|
|
17
17
|
"timetable": false,
|
|
18
|
-
"dispatch2":
|
|
18
|
+
"dispatch2": false,
|
|
19
19
|
"processes": false,
|
|
20
20
|
"res_break": false,
|
|
21
21
|
"parking_full": false,
|
|
@@ -198,7 +198,7 @@ def depot_to_template(depot: Depot) -> Dict:
|
|
|
198
198
|
"capacity": service_capacity,
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
if process.availability is not None:
|
|
201
|
+
if process.availability is not None and len(process.availability) > 0:
|
|
202
202
|
template["resource_switches"]["service_switch"] = {
|
|
203
203
|
"resource": "workers_service",
|
|
204
204
|
"breaks": [],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "eflips-depot"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.1.1"
|
|
4
4
|
description = "Depot Simulation for eFLIPS"
|
|
5
5
|
authors = ["Enrico Lauth <enrico.lauth@tu-berlin.de>",
|
|
6
6
|
"Ludger Heide <ludger.heide@tu-berlin.de",
|
|
@@ -12,7 +12,7 @@ packages = [{ include = "eflips/depot" }]
|
|
|
12
12
|
[tool.poetry.dependencies]
|
|
13
13
|
python = "^3.11"
|
|
14
14
|
simpy = "^4.0.1"
|
|
15
|
-
eflips-model = "^
|
|
15
|
+
eflips-model = "^2.1.2"
|
|
16
16
|
# Legacy dependencies, which are still needed until we refactor the code
|
|
17
17
|
eflips = "^0.1.0"
|
|
18
18
|
xlsxwriter = "^3.1.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{eflips_depot-1.0.1 → eflips_depot-1.1.1}/eflips/depot/layout_opt/opt_tools/fitness_c_urfd.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|