eflips-depot 4.3.18__tar.gz → 4.4.0__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-4.3.18 → eflips_depot-4.4.0}/PKG-INFO +2 -1
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/api/__init__.py +31 -306
- eflips_depot-4.4.0/eflips/depot/api/private/depot.py +1036 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/api/private/results_to_database.py +2 -1
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/api/private/smart_charging.py +7 -6
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/api/private/util.py +3 -20
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/pyproject.toml +2 -1
- eflips_depot-4.3.18/eflips/depot/api/private/depot.py +0 -944
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/LICENSE.md +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/README.md +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/__init__.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/api/defaults/default_settings.json +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/api/private/__init__.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/configuration.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/depot.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/evaluation.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/filters.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/input_epex_power_price.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/__init__.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/doc/__init__.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/doc/direct_details.pdf +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/evaluation.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/opt_tools/__init__.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/opt_tools/crossover.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/opt_tools/fitness_c_urfd.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/opt_tools/fitness_util.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/opt_tools/init.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/opt_tools/mutation.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/optimize_c_urfd.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/packing.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/settings.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/template_creation.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/layout_opt/util.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/plots.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/processes.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/rating.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/resources.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/settings_config.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/simple_vehicle.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/simulation.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/smart_charging.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/standalone.py +0 -0
- {eflips_depot-4.3.18 → eflips_depot-4.4.0}/eflips/depot/validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: eflips-depot
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.4.0
|
|
4
4
|
Summary: Depot Simulation for eFLIPS
|
|
5
5
|
Home-page: https://github.com/mpm-tu-berlin/eflips-depot
|
|
6
6
|
License: AGPL-3.0-or-later
|
|
@@ -18,6 +18,7 @@ Requires-Dist: eflips-model (>=5.0.0,<6.0.0)
|
|
|
18
18
|
Requires-Dist: pandas (>=2.1.4,<3.0.0)
|
|
19
19
|
Requires-Dist: scipy (>=1.13.1,<2.0.0)
|
|
20
20
|
Requires-Dist: simpy (>=4.0.1,<5.0.0)
|
|
21
|
+
Requires-Dist: tqdm (>=4.67.0,<5.0.0)
|
|
21
22
|
Requires-Dist: xlrd (<=1.2.0)
|
|
22
23
|
Requires-Dist: xlsxwriter (>=3.1.9,<4.0.0)
|
|
23
24
|
Project-URL: Repository, https://github.com/mpm-tu-berlin/eflips-depot
|
|
@@ -30,25 +30,23 @@ import logging
|
|
|
30
30
|
import os
|
|
31
31
|
import warnings
|
|
32
32
|
from collections import OrderedDict
|
|
33
|
-
from dataclasses import dataclass
|
|
34
33
|
from datetime import timedelta
|
|
35
34
|
from enum import Enum
|
|
36
35
|
from math import ceil
|
|
37
|
-
from typing import Any, Dict, Optional, Union
|
|
36
|
+
from typing import Any, Dict, Optional, Union
|
|
38
37
|
|
|
39
38
|
import sqlalchemy.orm
|
|
40
39
|
from eflips.model import (
|
|
41
40
|
Area,
|
|
42
|
-
AreaType,
|
|
43
41
|
Depot,
|
|
44
42
|
Event,
|
|
45
43
|
EventType,
|
|
46
44
|
Rotation,
|
|
47
45
|
Scenario,
|
|
48
|
-
Station,
|
|
49
46
|
Trip,
|
|
50
47
|
Vehicle,
|
|
51
48
|
VehicleType,
|
|
49
|
+
AreaType,
|
|
52
50
|
)
|
|
53
51
|
from sqlalchemy.orm import Session
|
|
54
52
|
|
|
@@ -58,14 +56,10 @@ from eflips.depot import (
|
|
|
58
56
|
SimulationHost,
|
|
59
57
|
)
|
|
60
58
|
from eflips.depot.api.private.depot import (
|
|
61
|
-
_generate_all_direct_depot,
|
|
62
|
-
create_simple_depot,
|
|
63
59
|
delete_depots,
|
|
64
60
|
depot_to_template,
|
|
65
61
|
group_rotations_by_start_end_stop,
|
|
66
|
-
|
|
67
|
-
real_peak_area_utilization,
|
|
68
|
-
real_peak_vehicle_count,
|
|
62
|
+
generate_depot,
|
|
69
63
|
)
|
|
70
64
|
from eflips.depot.api.private.results_to_database import (
|
|
71
65
|
get_finished_schedules_per_vehicle,
|
|
@@ -245,8 +239,18 @@ def simple_consumption_simulation(
|
|
|
245
239
|
for rotation in rotations:
|
|
246
240
|
rotation: Rotation
|
|
247
241
|
with session.no_autoflush:
|
|
248
|
-
vehicle_type =
|
|
249
|
-
|
|
242
|
+
vehicle_type = (
|
|
243
|
+
session.query(VehicleType)
|
|
244
|
+
.join(Rotation)
|
|
245
|
+
.filter(Rotation.id == rotation.id)
|
|
246
|
+
.one()
|
|
247
|
+
)
|
|
248
|
+
vehicle = (
|
|
249
|
+
session.query(Vehicle)
|
|
250
|
+
.join(Rotation)
|
|
251
|
+
.filter(Rotation.id == rotation.id)
|
|
252
|
+
.one()
|
|
253
|
+
)
|
|
250
254
|
if vehicle_type.consumption is None:
|
|
251
255
|
raise ValueError(
|
|
252
256
|
"The vehicle type does not have a consumption value set."
|
|
@@ -387,297 +391,9 @@ def simple_consumption_simulation(
|
|
|
387
391
|
session.add(current_event)
|
|
388
392
|
|
|
389
393
|
|
|
390
|
-
@dataclass
|
|
391
|
-
class DepotConfiguration:
|
|
392
|
-
charging_power: float
|
|
393
|
-
line_counts: Dict[VehicleType, int]
|
|
394
|
-
direct_counts: Dict[VehicleType, int]
|
|
395
|
-
clean_duration: int
|
|
396
|
-
num_clean_areas: int
|
|
397
|
-
num_shunting_areas: int
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
def generate_realistic_depot_layout(
|
|
401
|
-
scenario: Union[Scenario, int, Any],
|
|
402
|
-
charging_power: float,
|
|
403
|
-
database_url: Optional[str] = None,
|
|
404
|
-
delete_existing_depot: bool = False,
|
|
405
|
-
line_length: int = 8,
|
|
406
|
-
CLEAN_DURATION: int = 10 * 60, # 10 minutes in seconds
|
|
407
|
-
shunting_duration: timedelta = timedelta(minutes=5),
|
|
408
|
-
) -> DepotConfiguration:
|
|
409
|
-
"""
|
|
410
|
-
Creates a realistic depot layout for the scenario.
|
|
411
|
-
|
|
412
|
-
This is done by starting with an all direct depot layout,
|
|
413
|
-
looking at the vehicle count, creating an "all line" layout and then turning some of these lines into direct
|
|
414
|
-
areas until the vehicle count of the all direct depot layout (+ an allowance) is reached.
|
|
415
|
-
|
|
416
|
-
:param scenario: The scenario for which the depot layout should be generated.
|
|
417
|
-
:param charging_power: The charging power for the line areas in kW.
|
|
418
|
-
:param database_url: An optional database URL. If no database URL is passed and the `scenario` parameter is not a
|
|
419
|
-
:class:`eflips.model.Scenario` object, the environment variable `DATABASE_URL` must be set to a valid database
|
|
420
|
-
URL.
|
|
421
|
-
:param delete_existing_depot: Whether to delete an existing depot layout for this scenario. If set to False and a
|
|
422
|
-
depot layout already exists, a ValueError will be raised.
|
|
423
|
-
:param charging_power_direct: The charging power for the direct areas in kW. If not set, the charging power for the
|
|
424
|
-
line areas will be used.
|
|
425
|
-
|
|
426
|
-
:return: None. The depot layout will be added to the database.
|
|
427
|
-
"""
|
|
428
|
-
logging.basicConfig(level=logging.DEBUG) # TODO: Remove this line
|
|
429
|
-
logger = logging.getLogger(__name__)
|
|
430
|
-
|
|
431
|
-
with create_session(scenario, database_url) as (session, scenario):
|
|
432
|
-
# STEP 0: Delete existing depots if asked to, raise an Exception otherwise
|
|
433
|
-
if session.query(Depot).filter(Depot.scenario_id == scenario.id).count() != 0:
|
|
434
|
-
if delete_existing_depot is False:
|
|
435
|
-
raise ValueError("Depot already exists.")
|
|
436
|
-
delete_depots(scenario, session)
|
|
437
|
-
|
|
438
|
-
# Make sure that the consumption simulation has been run
|
|
439
|
-
if session.query(Event).filter(Event.scenario_id == scenario.id).count() == 0:
|
|
440
|
-
raise ValueError(
|
|
441
|
-
"No consumption simulation found. Please run the consumption simulation first."
|
|
442
|
-
)
|
|
443
|
-
|
|
444
|
-
# STEP 2: Identify the spots where we will put a depot
|
|
445
|
-
# Identify all the spots that serve as start *and* end of a rotation
|
|
446
|
-
depot_stations_and_vehicle_types = group_rotations_by_start_end_stop(
|
|
447
|
-
scenario.id, session
|
|
448
|
-
)
|
|
449
|
-
|
|
450
|
-
# STEP 3: Put "all direct" depots at these spots and find the vehicle counts
|
|
451
|
-
depot_stations = []
|
|
452
|
-
vehicle_type_rotation_dict_by_station: Dict[
|
|
453
|
-
Station, Dict[VehicleType, List[Rotation]]
|
|
454
|
-
] = dict()
|
|
455
|
-
for (
|
|
456
|
-
first_last_stop_tup,
|
|
457
|
-
vehicle_type_rotation_dict,
|
|
458
|
-
) in depot_stations_and_vehicle_types.items():
|
|
459
|
-
first_stop, last_stop = first_last_stop_tup
|
|
460
|
-
if first_stop != last_stop:
|
|
461
|
-
raise ValueError("First and last stop of a rotation are not the same.")
|
|
462
|
-
depot_stations.append(first_stop)
|
|
463
|
-
vehicle_type_rotation_dict_by_station[
|
|
464
|
-
first_stop
|
|
465
|
-
] = vehicle_type_rotation_dict
|
|
466
|
-
|
|
467
|
-
del first_last_stop_tup, vehicle_type_rotation_dict
|
|
468
|
-
|
|
469
|
-
all_direct_counts: Dict[
|
|
470
|
-
Station, Dict[VehicleType, int]
|
|
471
|
-
] = vehicle_counts_for_direct_layout(
|
|
472
|
-
CLEAN_DURATION=CLEAN_DURATION,
|
|
473
|
-
charging_power=charging_power,
|
|
474
|
-
stations=depot_stations,
|
|
475
|
-
scenario=scenario,
|
|
476
|
-
session=session,
|
|
477
|
-
vehicle_type_dict_by_station=vehicle_type_rotation_dict_by_station,
|
|
478
|
-
shunting_duration=shunting_duration,
|
|
479
|
-
)
|
|
480
|
-
|
|
481
|
-
# STEP 4: Run the simulation with depots that also have a lot of line areas
|
|
482
|
-
# I know I could probably skip step 3 and go directly to step 4, but that's how I got it working and
|
|
483
|
-
# I'm too lazy to change it now
|
|
484
|
-
|
|
485
|
-
for station, vehicle_type_and_counts in all_direct_counts.items():
|
|
486
|
-
line_counts: Dict[VehicleType, int] = dict()
|
|
487
|
-
direct_counts: Dict[VehicleType, int] = dict()
|
|
488
|
-
|
|
489
|
-
# Create a Depot that has a lot of line areas as well
|
|
490
|
-
for vehicle_type, count in vehicle_type_and_counts.items():
|
|
491
|
-
line_counts[vehicle_type] = ceil(count / line_length)
|
|
492
|
-
direct_counts[vehicle_type] = ceil(count) + 500
|
|
493
|
-
|
|
494
|
-
# Run the simulation with this depot
|
|
495
|
-
generate_line_depot_layout(
|
|
496
|
-
CLEAN_DURATION=CLEAN_DURATION,
|
|
497
|
-
charging_power=charging_power,
|
|
498
|
-
station=station,
|
|
499
|
-
scenario=scenario,
|
|
500
|
-
session=session,
|
|
501
|
-
direct_counts=direct_counts,
|
|
502
|
-
line_counts=line_counts,
|
|
503
|
-
line_length=line_length,
|
|
504
|
-
vehicle_type_rotation_dict=vehicle_type_rotation_dict_by_station[
|
|
505
|
-
station
|
|
506
|
-
],
|
|
507
|
-
shunting_duration=shunting_duration,
|
|
508
|
-
)
|
|
509
|
-
|
|
510
|
-
# Simulate the depot
|
|
511
|
-
# We will not be using add_evaluation_to_database instead taking the vehicle counts directly from the `ev` object
|
|
512
|
-
logger.info("Simulating the scenario")
|
|
513
|
-
logger.info("1/2: Initializing the simulation host")
|
|
514
|
-
simulation_host = init_simulation(
|
|
515
|
-
scenario=scenario,
|
|
516
|
-
session=session,
|
|
517
|
-
)
|
|
518
|
-
logger.info("2/2: Running the simulation")
|
|
519
|
-
depot_evaluations = run_simulation(simulation_host)
|
|
520
|
-
|
|
521
|
-
# We need to remember the depot-id-station mapping
|
|
522
|
-
depot_id_station_mapping: Dict[str, Station] = dict()
|
|
523
|
-
for depot_id_as_str, ev in depot_evaluations.items():
|
|
524
|
-
station = (
|
|
525
|
-
session.query(Station)
|
|
526
|
-
.join(Depot)
|
|
527
|
-
.filter(Depot.id == int(depot_id_as_str))
|
|
528
|
-
.one()
|
|
529
|
-
)
|
|
530
|
-
depot_id_station_mapping[depot_id_as_str] = station
|
|
531
|
-
|
|
532
|
-
# Delete the old depot
|
|
533
|
-
delete_depots(scenario, session)
|
|
534
|
-
|
|
535
|
-
for depot_id_as_str, ev in depot_evaluations.items():
|
|
536
|
-
assert isinstance(ev, DepotEvaluation)
|
|
537
|
-
|
|
538
|
-
if False:
|
|
539
|
-
ev.path_results = depot_id_as_str
|
|
540
|
-
os.makedirs(depot_id_as_str, exist_ok=True)
|
|
541
|
-
|
|
542
|
-
ev.vehicle_periods(
|
|
543
|
-
periods={
|
|
544
|
-
"depot general": "darkgray",
|
|
545
|
-
"park": "lightgray",
|
|
546
|
-
"Arrival Cleaning": "steelblue",
|
|
547
|
-
"Charging": "forestgreen",
|
|
548
|
-
"Standby Pre-departure": "darkblue",
|
|
549
|
-
"precondition": "black",
|
|
550
|
-
"trip": "wheat",
|
|
551
|
-
},
|
|
552
|
-
save=True,
|
|
553
|
-
show=False,
|
|
554
|
-
formats=(
|
|
555
|
-
"pdf",
|
|
556
|
-
"png",
|
|
557
|
-
),
|
|
558
|
-
show_total_power=True,
|
|
559
|
-
show_annotates=True,
|
|
560
|
-
)
|
|
561
|
-
|
|
562
|
-
# Find the actual utilization.
|
|
563
|
-
utilization: Dict[str, int] = real_peak_area_utilization(ev)
|
|
564
|
-
utilization = {
|
|
565
|
-
session.query(VehicleType).filter(VehicleType.id == int(k)).one(): v
|
|
566
|
-
for k, v in utilization.items()
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
# Turn utilization into a two dictionaries, one for line areas and one for direct areas
|
|
570
|
-
for vehicle_type, counts in utilization.items():
|
|
571
|
-
line_counts[vehicle_type] = counts[AreaType.LINE]
|
|
572
|
-
direct_counts[vehicle_type] = counts[AreaType.DIRECT_ONESIDE] + 100
|
|
573
|
-
|
|
574
|
-
station = depot_id_station_mapping[depot_id_as_str]
|
|
575
|
-
|
|
576
|
-
generate_line_depot_layout(
|
|
577
|
-
CLEAN_DURATION=CLEAN_DURATION,
|
|
578
|
-
charging_power=charging_power,
|
|
579
|
-
station=station,
|
|
580
|
-
scenario=scenario,
|
|
581
|
-
session=session,
|
|
582
|
-
direct_counts=direct_counts,
|
|
583
|
-
line_counts=line_counts,
|
|
584
|
-
line_length=line_length,
|
|
585
|
-
vehicle_type_rotation_dict=vehicle_type_rotation_dict_by_station[
|
|
586
|
-
station
|
|
587
|
-
],
|
|
588
|
-
shunting_duration=shunting_duration,
|
|
589
|
-
)
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
def vehicle_counts_for_direct_layout(
|
|
593
|
-
CLEAN_DURATION: int,
|
|
594
|
-
charging_power: float,
|
|
595
|
-
stations: List[Station],
|
|
596
|
-
scenario: Scenario,
|
|
597
|
-
session: sqlalchemy.orm.session.Session,
|
|
598
|
-
vehicle_type_dict_by_station: Dict[Station, Dict[VehicleType, List[Rotation]]],
|
|
599
|
-
shunting_duration: timedelta = timedelta(minutes=5),
|
|
600
|
-
) -> Dict[Station, Dict[VehicleType, int]]:
|
|
601
|
-
"""
|
|
602
|
-
Generate a simple depot, simulate it and return the number of vehicles for each vehicle type.
|
|
603
|
-
|
|
604
|
-
Do this for each depot station in the scenario.
|
|
605
|
-
:param CLEAN_DURATION: The duration of the cleaning process in seconds.
|
|
606
|
-
:param charging_power: The charging power of the charging area in kW.
|
|
607
|
-
:param station: The stop where the depot is located.
|
|
608
|
-
:param scenario: The scenario for which the depot layout should be generated.
|
|
609
|
-
:param session: The SQLAlchemy session object.
|
|
610
|
-
:param vehicle_type_dict: A dictionary with vehicle types as keys and rotations as values.
|
|
611
|
-
:return: A dictionary with vehicle types as keys and the number of vehicles as values.
|
|
612
|
-
"""
|
|
613
|
-
logger = logging.getLogger(__name__)
|
|
614
|
-
|
|
615
|
-
for station in stations:
|
|
616
|
-
logger.info(f"Generating all direct depot layout at {station.name}")
|
|
617
|
-
# Generate the depot
|
|
618
|
-
direct_counts = {}
|
|
619
|
-
line_counts = {}
|
|
620
|
-
for vehicle_type, rotations in vehicle_type_dict_by_station[station].items():
|
|
621
|
-
direct_counts[vehicle_type] = len(rotations)
|
|
622
|
-
line_counts[vehicle_type] = 0
|
|
623
|
-
|
|
624
|
-
generate_line_depot_layout(
|
|
625
|
-
CLEAN_DURATION=CLEAN_DURATION,
|
|
626
|
-
charging_power=charging_power,
|
|
627
|
-
station=station,
|
|
628
|
-
scenario=scenario,
|
|
629
|
-
session=session,
|
|
630
|
-
direct_counts=direct_counts,
|
|
631
|
-
line_counts=line_counts,
|
|
632
|
-
line_length=8, # We don't care about the line length here
|
|
633
|
-
vehicle_type_rotation_dict=vehicle_type_dict_by_station[station],
|
|
634
|
-
shunting_duration=shunting_duration,
|
|
635
|
-
)
|
|
636
|
-
|
|
637
|
-
# Simulate the scenario
|
|
638
|
-
# We will not be using add_evaluation_to_database instead taking the vehicle counts directly from the `ev` object
|
|
639
|
-
logger.info("Simulating the scenario")
|
|
640
|
-
logger.info("1/2: Initializing the simulation host")
|
|
641
|
-
simulation_host = init_simulation(
|
|
642
|
-
scenario=scenario,
|
|
643
|
-
session=session,
|
|
644
|
-
)
|
|
645
|
-
logger.info("2/2: Running the simulation")
|
|
646
|
-
depot_evaluations = run_simulation(simulation_host)
|
|
647
|
-
|
|
648
|
-
assert len(depot_evaluations) == len(stations)
|
|
649
|
-
depot_evaluations: Dict[str, DepotEvaluation]
|
|
650
|
-
|
|
651
|
-
ret_val: Dict[Station, Dict[VehicleType, int]] = dict()
|
|
652
|
-
|
|
653
|
-
for depot_id_as_str, ev in depot_evaluations.items():
|
|
654
|
-
assert isinstance(ev, DepotEvaluation)
|
|
655
|
-
counts: Dict[str, int] = real_peak_vehicle_count(ev)
|
|
656
|
-
# The key of the dictionary is the vehicle type ID as a string. We need to convert it to a vehicle type object
|
|
657
|
-
vehicle_type_dict = {
|
|
658
|
-
session.query(VehicleType).filter(VehicleType.id == int(k)).one(): v
|
|
659
|
-
for k, v in counts.items()
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
# Find the station object
|
|
663
|
-
station = (
|
|
664
|
-
session.query(Station)
|
|
665
|
-
.join(Depot)
|
|
666
|
-
.filter(Depot.id == int(depot_id_as_str))
|
|
667
|
-
.one()
|
|
668
|
-
)
|
|
669
|
-
|
|
670
|
-
ret_val[station] = vehicle_type_dict
|
|
671
|
-
|
|
672
|
-
# Delete the old depots
|
|
673
|
-
delete_depots(scenario, session)
|
|
674
|
-
|
|
675
|
-
return ret_val
|
|
676
|
-
|
|
677
|
-
|
|
678
394
|
def generate_depot_layout(
|
|
679
395
|
scenario: Union[Scenario, int, Any],
|
|
680
|
-
charging_power: float =
|
|
396
|
+
charging_power: float = 90,
|
|
681
397
|
database_url: Optional[str] = None,
|
|
682
398
|
delete_existing_depot: bool = False,
|
|
683
399
|
) -> None:
|
|
@@ -710,8 +426,6 @@ def generate_depot_layout(
|
|
|
710
426
|
|
|
711
427
|
:return: None. The depot layout will be added to the database.
|
|
712
428
|
"""
|
|
713
|
-
CLEAN_DURATION = 30 * 60 # 30 minutes in seconds
|
|
714
|
-
|
|
715
429
|
with create_session(scenario, database_url) as (session, scenario):
|
|
716
430
|
# Handles existing depot
|
|
717
431
|
if session.query(Depot).filter(Depot.scenario_id == scenario.id).count() != 0:
|
|
@@ -727,13 +441,24 @@ def generate_depot_layout(
|
|
|
727
441
|
first_stop, last_stop = first_last_stop_tup
|
|
728
442
|
if first_stop != last_stop:
|
|
729
443
|
raise ValueError("First and last stop of a rotation are not the same.")
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
444
|
+
|
|
445
|
+
# Create one direct slot for each rotation (it's way too much, but should work)
|
|
446
|
+
vt_capacity_dict: Dict[VehicleType, Dict[AreaType, None | int]] = {}
|
|
447
|
+
for vehicle_type, rotations in vehicle_type_dict.items():
|
|
448
|
+
rotation_count = len(rotations)
|
|
449
|
+
vt_capacity_dict[vehicle_type] = {
|
|
450
|
+
AreaType.LINE: None,
|
|
451
|
+
AreaType.DIRECT_ONESIDE: rotation_count,
|
|
452
|
+
AreaType.DIRECT_TWOSIDE: None,
|
|
453
|
+
}
|
|
454
|
+
generate_depot(
|
|
455
|
+
vt_capacity_dict,
|
|
733
456
|
first_stop,
|
|
734
457
|
scenario,
|
|
735
458
|
session,
|
|
736
|
-
|
|
459
|
+
charging_power=charging_power,
|
|
460
|
+
num_shunting_slots=max(rotation_count // 10, 1),
|
|
461
|
+
num_cleaning_slots=max(rotation_count // 10, 1),
|
|
737
462
|
)
|
|
738
463
|
|
|
739
464
|
|