eflips-depot 4.3.19__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.

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