eflips-depot 4.14.3__tar.gz → 4.15.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.
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/PKG-INFO +2 -2
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/api/__init__.py +16 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/api/private/consumption.py +46 -23
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/api/private/depot.py +62 -4
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/api/private/results_to_database.py +65 -21
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/api/private/util.py +18 -6
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/depot.py +2 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/processes.py +21 -1
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/pyproject.toml +2 -2
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/LICENSE.md +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/README.md +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/__init__.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/api/defaults/default_settings.json +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/api/private/__init__.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/configuration.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/evaluation.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/filters.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/input_epex_power_price.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/__init__.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/doc/__init__.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/doc/direct_details.pdf +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/evaluation.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/opt_tools/__init__.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/opt_tools/crossover.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/opt_tools/fitness_c_urfd.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/opt_tools/fitness_util.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/opt_tools/init.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/opt_tools/mutation.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/optimize_c_urfd.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/packing.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/settings.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/template_creation.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/util.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/plots.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/rating.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/resources.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/settings_config.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/simple_vehicle.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/simulation.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/smart_charging.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/standalone.py +0 -0
- {eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: eflips-depot
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.15.1
|
|
4
4
|
Summary: Depot Simulation for eFLIPS
|
|
5
5
|
License: AGPL-3.0-or-later
|
|
6
6
|
License-File: LICENSE.md
|
|
@@ -14,7 +14,7 @@ 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
16
|
Requires-Dist: eflips-model (>=10.0.0,<11.0.0)
|
|
17
|
-
Requires-Dist: eflips-opt (>=0.3.
|
|
17
|
+
Requires-Dist: eflips-opt (>=0.3.6,<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)
|
|
@@ -532,6 +532,7 @@ def apply_even_smart_charging(
|
|
|
532
532
|
scenario: Union[Scenario, int, Any],
|
|
533
533
|
database_url: Optional[str] = None,
|
|
534
534
|
standby_departure_duration: timedelta = timedelta(minutes=5),
|
|
535
|
+
delete_existing_charging_timeseries: bool = False,
|
|
535
536
|
) -> None:
|
|
536
537
|
"""
|
|
537
538
|
Takes a scenario where depot simulation has been run and applies smart charging to the depot.
|
|
@@ -548,6 +549,8 @@ def apply_even_smart_charging(
|
|
|
548
549
|
:param standby_departure_duration: The duration of the STANDBY_DEPARTURE event. This is the time the vehicle is
|
|
549
550
|
allowed to wait at the depot before it has to leave. The default is 5 minutes.
|
|
550
551
|
|
|
552
|
+
:param delete_existing_timeseries: If True, the existing timeseries in the charging events will be deleted.
|
|
553
|
+
|
|
551
554
|
:return: None. The results are added to the database.
|
|
552
555
|
"""
|
|
553
556
|
logger = logging.getLogger(__name__)
|
|
@@ -564,6 +567,19 @@ def apply_even_smart_charging(
|
|
|
564
567
|
raise
|
|
565
568
|
|
|
566
569
|
with create_session(scenario, database_url) as (session, scenario):
|
|
570
|
+
if delete_existing_charging_timeseries is False:
|
|
571
|
+
raise ValueError(
|
|
572
|
+
"The existing timeseries of charging events needed to be deleted. Set "
|
|
573
|
+
"delete_existing_charging_timeseries=True to delete them."
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
# Delete existing timeseries in charging events
|
|
577
|
+
session.query(Event).filter(
|
|
578
|
+
Event.event_type == EventType.CHARGING_DEPOT,
|
|
579
|
+
Event.scenario_id == scenario.id,
|
|
580
|
+
).update({"timeseries": None}, synchronize_session=False)
|
|
581
|
+
session.expire_all()
|
|
582
|
+
|
|
567
583
|
depots = session.query(Depot).filter(Depot.scenario_id == scenario.id).all()
|
|
568
584
|
for depot in depots:
|
|
569
585
|
add_slack_time_to_events_of_depot(
|
|
@@ -286,20 +286,6 @@ def extract_trip_information(
|
|
|
286
286
|
)
|
|
287
287
|
.one()
|
|
288
288
|
)
|
|
289
|
-
# Check exactly one of the vehicle classes has a consumption LUT
|
|
290
|
-
all_consumption_luts = [
|
|
291
|
-
vehicle_class.consumption_lut
|
|
292
|
-
for vehicle_class in trip.rotation.vehicle_type.vehicle_classes
|
|
293
|
-
]
|
|
294
|
-
all_consumption_luts = [x for x in all_consumption_luts if x is not None]
|
|
295
|
-
if len(all_consumption_luts) != 1:
|
|
296
|
-
raise ValueError(
|
|
297
|
-
f"Expected exactly one consumption LUT, got {len(all_consumption_luts)}"
|
|
298
|
-
)
|
|
299
|
-
consumption_lut = all_consumption_luts[0]
|
|
300
|
-
# Disconnect the consumption LUT from the session to avoid loading the whole table
|
|
301
|
-
|
|
302
|
-
del all_consumption_luts
|
|
303
289
|
|
|
304
290
|
total_distance = trip.route.distance / 1000 # km
|
|
305
291
|
total_duration = (
|
|
@@ -307,6 +293,13 @@ def extract_trip_information(
|
|
|
307
293
|
).total_seconds() / 3600
|
|
308
294
|
average_speed = total_distance / total_duration # km/h
|
|
309
295
|
|
|
296
|
+
# Check exactly one of the vehicle classes has a consumption LUT
|
|
297
|
+
all_consumption_luts = [
|
|
298
|
+
vehicle_class.consumption_lut
|
|
299
|
+
for vehicle_class in trip.rotation.vehicle_type.vehicle_classes
|
|
300
|
+
]
|
|
301
|
+
all_consumption_luts = [x for x in all_consumption_luts if x is not None]
|
|
302
|
+
|
|
310
303
|
temperature = temperature_for_trip(trip_id, session)
|
|
311
304
|
|
|
312
305
|
payload_mass = passenger_mass * passenger_count
|
|
@@ -324,16 +317,46 @@ def extract_trip_information(
|
|
|
324
317
|
)
|
|
325
318
|
level_of_loading = payload_mass / full_payload
|
|
326
319
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
320
|
+
if len(all_consumption_luts) == 1:
|
|
321
|
+
consumption_lut = all_consumption_luts[0]
|
|
322
|
+
# Disconnect the consumption LUT from the session to avoid loading the whole table
|
|
323
|
+
|
|
324
|
+
del all_consumption_luts
|
|
325
|
+
|
|
326
|
+
info = ConsumptionInformation(
|
|
327
|
+
trip_id=trip.id,
|
|
328
|
+
consumption_lut=consumption_lut,
|
|
329
|
+
average_speed=average_speed,
|
|
330
|
+
distance=total_distance,
|
|
331
|
+
temperature=temperature,
|
|
332
|
+
level_of_loading=level_of_loading,
|
|
333
|
+
)
|
|
334
|
+
info.calculate()
|
|
335
|
+
elif len(all_consumption_luts) == 0:
|
|
336
|
+
warnings.warn(
|
|
337
|
+
f"No consumption LUT found for vehicle type {trip.rotation.vehicle_type}.",
|
|
338
|
+
ConsistencyWarning,
|
|
339
|
+
)
|
|
340
|
+
# Here, we fill out the condumption information without the LUT and LUT data, but with `consumption_per_km`
|
|
341
|
+
# set to the vehicle's `consumption` value.
|
|
342
|
+
assert (
|
|
343
|
+
VehicleType.consumption is not None
|
|
344
|
+
), f"Vehicle type {trip.rotation.vehicle_type} must have a consumption value set if no consumption LUT is available."
|
|
345
|
+
info = ConsumptionInformation(
|
|
346
|
+
trip_id=trip.id,
|
|
347
|
+
average_speed=average_speed,
|
|
348
|
+
distance=total_distance,
|
|
349
|
+
consumption_per_km=trip.rotation.vehicle_type.consumption,
|
|
350
|
+
consumption=trip.rotation.vehicle_type.consumption * total_distance,
|
|
351
|
+
consumption_lut=None,
|
|
352
|
+
temperature=temperature,
|
|
353
|
+
level_of_loading=level_of_loading,
|
|
354
|
+
)
|
|
355
|
+
else:
|
|
356
|
+
raise ValueError(
|
|
357
|
+
f"Expected exactly one consumption LUT, got {len(all_consumption_luts)}"
|
|
358
|
+
)
|
|
335
359
|
|
|
336
|
-
info.calculate()
|
|
337
360
|
return info
|
|
338
361
|
|
|
339
362
|
|
|
@@ -204,7 +204,7 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
|
|
|
204
204
|
list_of_processes = []
|
|
205
205
|
|
|
206
206
|
# Get dictionary of each area
|
|
207
|
-
# For line areas, generate
|
|
207
|
+
# For line areas, generate a dictionary item for total areas, later it will be split into individual lines
|
|
208
208
|
for area in depot.areas:
|
|
209
209
|
area_name = str(area.id)
|
|
210
210
|
template["areas"][area_name] = {
|
|
@@ -226,6 +226,17 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
|
|
|
226
226
|
"filter_names": ["vehicle_type"],
|
|
227
227
|
"vehicle_types": [str(area.vehicle_type_id)],
|
|
228
228
|
}
|
|
229
|
+
for processes_in_area in area.processes:
|
|
230
|
+
if process_type(processes_in_area) == ProcessType.CHARGING:
|
|
231
|
+
# Add the charging process for this vehicle type
|
|
232
|
+
template["areas"][area_name]["available_processes"].append(
|
|
233
|
+
str(processes_in_area.id) + str(area.vehicle_type_id)
|
|
234
|
+
)
|
|
235
|
+
# Delete the original charging process
|
|
236
|
+
template["areas"][area_name]["available_processes"].remove(
|
|
237
|
+
str(processes_in_area.id)
|
|
238
|
+
)
|
|
239
|
+
|
|
229
240
|
else:
|
|
230
241
|
# If the vehicle type id is not set, the area is for all vehicle types
|
|
231
242
|
scenario = depot.scenario
|
|
@@ -236,6 +247,19 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
|
|
|
236
247
|
"vehicle_types": all_vehicle_type_ids,
|
|
237
248
|
}
|
|
238
249
|
|
|
250
|
+
# if there are any charging areas, all a unique charging process for each vehicle type
|
|
251
|
+
for processes_in_area in area.processes:
|
|
252
|
+
if process_type(processes_in_area) == ProcessType.CHARGING:
|
|
253
|
+
# Add the charging process for this vehicle type
|
|
254
|
+
for vt in scenario.vehicle_types:
|
|
255
|
+
template["areas"][area_name]["available_processes"].append(
|
|
256
|
+
str(processes_in_area.id) + str(vt.id)
|
|
257
|
+
)
|
|
258
|
+
# Delete the original charging process
|
|
259
|
+
template["areas"][area_name]["available_processes"].remove(
|
|
260
|
+
str(processes_in_area.id)
|
|
261
|
+
)
|
|
262
|
+
|
|
239
263
|
for process in area.processes:
|
|
240
264
|
# Add process into process list
|
|
241
265
|
list_of_processes.append(
|
|
@@ -285,7 +309,7 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
|
|
|
285
309
|
for name in line_areas_to_delete:
|
|
286
310
|
del area_template[name]
|
|
287
311
|
|
|
288
|
-
# Fill in the dictionary of
|
|
312
|
+
# Fill in the dictionary of processes
|
|
289
313
|
for process in list_of_processes:
|
|
290
314
|
process_name = str(process.id)
|
|
291
315
|
# Shared template for all processes
|
|
@@ -358,8 +382,42 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
|
|
|
358
382
|
] = list_of_breaks_in_seconds
|
|
359
383
|
|
|
360
384
|
case ProcessType.CHARGING:
|
|
361
|
-
|
|
362
|
-
|
|
385
|
+
all_vehicle_types = depot.scenario.vehicle_types
|
|
386
|
+
|
|
387
|
+
for vt in all_vehicle_types:
|
|
388
|
+
charging_curve = vt.charging_curve
|
|
389
|
+
template["processes"][process_name + str(vt.id)] = template[
|
|
390
|
+
"processes"
|
|
391
|
+
][process_name].copy()
|
|
392
|
+
template["processes"][process_name + str(vt.id)][
|
|
393
|
+
"typename"
|
|
394
|
+
] = "ChargeEquationSteps"
|
|
395
|
+
template["processes"][process_name + str(vt.id)][
|
|
396
|
+
"vehicle_filter"
|
|
397
|
+
] = {
|
|
398
|
+
"filter_names": ["vehicle_type"],
|
|
399
|
+
"vehicle_types": [str(vt.id)],
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
template["processes"][process_name + str(vt.id)][
|
|
403
|
+
"peq_name"
|
|
404
|
+
] = "charging_curve_power"
|
|
405
|
+
template["processes"][process_name + str(vt.id)]["peq_params"] = {
|
|
406
|
+
"soc": [soc_power_pair[0] for soc_power_pair in charging_curve],
|
|
407
|
+
"power": [
|
|
408
|
+
soc_power_pair[1] for soc_power_pair in charging_curve
|
|
409
|
+
],
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
del template["processes"][process_name + str(vt.id)]["dur"]
|
|
413
|
+
|
|
414
|
+
# delete the original process
|
|
415
|
+
del template["processes"][process_name]
|
|
416
|
+
|
|
417
|
+
# The original one
|
|
418
|
+
# template["processes"][process_name]["typename"] = "Charge"
|
|
419
|
+
|
|
420
|
+
# del template["processes"][process_name]["dur"]
|
|
363
421
|
|
|
364
422
|
case ProcessType.STANDBY | ProcessType.STANDBY_DEPARTURE:
|
|
365
423
|
template["processes"][process_name]["typename"] = "Standby"
|
|
@@ -225,8 +225,6 @@ def generate_vehicle_events(
|
|
|
225
225
|
"is_waiting": True,
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
# Create a list of battery log in order of time asc. Convenient for looking up corresponding soc
|
|
229
|
-
|
|
230
228
|
for time_stamp, process_log in current_vehicle.logger.loggedData[
|
|
231
229
|
"dwd.active_processes_copy"
|
|
232
230
|
].items():
|
|
@@ -276,13 +274,18 @@ def generate_vehicle_events(
|
|
|
276
274
|
)
|
|
277
275
|
start_this_event = dict_of_events[time_stamp]["end"]
|
|
278
276
|
else:
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
277
|
+
if len(process_log) > 1:
|
|
278
|
+
# This is for the case where the charging and standby_departure happen in the same area, and the standby_departure is the last process.
|
|
279
|
+
for other_process in process_log:
|
|
280
|
+
if (
|
|
281
|
+
other_process.ID != process.ID
|
|
282
|
+
and other_process.dur > 0
|
|
283
|
+
):
|
|
284
|
+
start_this_event = other_process.ends[0]
|
|
285
|
+
break
|
|
286
|
+
else:
|
|
287
|
+
# This is for the case where only standby_departure happens in the last area.
|
|
288
|
+
start_this_event = time_stamp
|
|
286
289
|
|
|
287
290
|
assert (
|
|
288
291
|
start_this_event is not None
|
|
@@ -395,7 +398,7 @@ def add_soc_to_events(dict_of_events, battery_log) -> None:
|
|
|
395
398
|
"""
|
|
396
399
|
battery_log_list = []
|
|
397
400
|
for log in battery_log:
|
|
398
|
-
battery_log_list.append((log.t, log.energy / log.energy_real))
|
|
401
|
+
battery_log_list.append((log.t, round(log.energy / log.energy_real, 4)))
|
|
399
402
|
|
|
400
403
|
time_keys = sorted(dict_of_events.keys())
|
|
401
404
|
|
|
@@ -408,15 +411,42 @@ def add_soc_to_events(dict_of_events, battery_log) -> None:
|
|
|
408
411
|
start_time = time_keys[i]
|
|
409
412
|
process_dict = dict_of_events[time_keys[i]]
|
|
410
413
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
414
|
+
match process_dict["type"]:
|
|
415
|
+
case "Charge" | "ChargeSteps" | "ChargeEquationSteps":
|
|
416
|
+
event_start = start_time
|
|
417
|
+
event_end = process_dict["end"]
|
|
418
|
+
start_time_index = battery_log_times.index(event_start)
|
|
419
|
+
end_time_index = battery_log_times.index(event_end)
|
|
420
|
+
time_series = {
|
|
421
|
+
"time": battery_log_times[start_time_index:end_time_index],
|
|
422
|
+
"soc": battery_log_socs[start_time_index:end_time_index],
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
# if there are repeated timestamps, we need to remove them
|
|
426
|
+
unique_indices = np.unique(
|
|
427
|
+
time_series["time"], return_index=True, return_inverse=False
|
|
428
|
+
)[1]
|
|
429
|
+
time_series["time"] = [
|
|
430
|
+
time_series["time"][index] for index in unique_indices
|
|
431
|
+
]
|
|
432
|
+
time_series["soc"] = [
|
|
433
|
+
time_series["soc"][index] for index in unique_indices
|
|
434
|
+
]
|
|
435
|
+
process_dict["timeseries"] = time_series
|
|
436
|
+
process_dict["soc_start"] = battery_log_socs[start_time_index]
|
|
437
|
+
process_dict["soc_end"] = battery_log_socs[end_time_index]
|
|
438
|
+
|
|
439
|
+
case "Serve" | "Standby":
|
|
440
|
+
soc_start = np.interp(start_time, battery_log_times, battery_log_socs)
|
|
441
|
+
process_dict["soc_start"] = min(float(soc_start), 1.0)
|
|
442
|
+
soc_end = np.interp(
|
|
443
|
+
process_dict["end"], battery_log_times, battery_log_socs
|
|
444
|
+
)
|
|
445
|
+
process_dict["soc_end"] = min(float(soc_end), 1.0)
|
|
446
|
+
case "Trip":
|
|
447
|
+
continue
|
|
448
|
+
case _:
|
|
449
|
+
raise NotImplementedError
|
|
420
450
|
|
|
421
451
|
|
|
422
452
|
def add_events_into_database(
|
|
@@ -446,7 +476,7 @@ def add_events_into_database(
|
|
|
446
476
|
match process_dict["type"]:
|
|
447
477
|
case "Serve":
|
|
448
478
|
event_type = EventType.SERVICE
|
|
449
|
-
case "Charge":
|
|
479
|
+
case "Charge" | "ChargeSteps" | "ChargeEquationSteps": # TODO that might be problematic
|
|
450
480
|
event_type = EventType.CHARGING_DEPOT
|
|
451
481
|
case "Standby":
|
|
452
482
|
if (
|
|
@@ -488,6 +518,20 @@ def add_events_into_database(
|
|
|
488
518
|
capacity_per_line = int(current_area.capacity / current_area.row_count)
|
|
489
519
|
process_dict["slot"] = capacity_per_line * row + process_dict["slot"] - 1
|
|
490
520
|
|
|
521
|
+
timeseries = {}
|
|
522
|
+
if "timeseries" in process_dict.keys():
|
|
523
|
+
# Convert "time" in timeseries to timedelta
|
|
524
|
+
timeseries["time"] = [
|
|
525
|
+
(timedelta(seconds=t) + simulation_start_time).isoformat()
|
|
526
|
+
for t in process_dict["timeseries"]["time"]
|
|
527
|
+
]
|
|
528
|
+
timeseries["soc"] = process_dict["timeseries"]["soc"]
|
|
529
|
+
|
|
530
|
+
assert all(
|
|
531
|
+
timeseries["soc"][i] <= timeseries["soc"][i + 1]
|
|
532
|
+
for i in range(len(timeseries["soc"]) - 1)
|
|
533
|
+
), "SOC values in the timeseries should be non-decreasing."
|
|
534
|
+
|
|
491
535
|
current_event = Event(
|
|
492
536
|
scenario=scenario,
|
|
493
537
|
vehicle_type_id=db_vehicle.vehicle_type_id,
|
|
@@ -507,7 +551,7 @@ def add_events_into_database(
|
|
|
507
551
|
# then this is not an event with soc change
|
|
508
552
|
event_type=event_type,
|
|
509
553
|
description=process_dict["id"] if "id" in process_dict.keys() else None,
|
|
510
|
-
timeseries=None,
|
|
554
|
+
timeseries=timeseries if "timeseries" in process_dict.keys() else None,
|
|
511
555
|
)
|
|
512
556
|
|
|
513
557
|
session.add(current_event)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""This module contains miscellaneous utility functions for the eflips-depot API."""
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
import warnings
|
|
4
5
|
from contextlib import contextmanager
|
|
5
6
|
from dataclasses import dataclass
|
|
6
7
|
from datetime import timedelta, datetime
|
|
@@ -17,9 +18,11 @@ from eflips.model import (
|
|
|
17
18
|
Trip,
|
|
18
19
|
Depot,
|
|
19
20
|
Temperatures,
|
|
21
|
+
ConsistencyWarning,
|
|
20
22
|
)
|
|
21
23
|
from eflips.model import create_engine
|
|
22
24
|
from sqlalchemy import inspect
|
|
25
|
+
from sqlalchemy.exc import NoResultFound
|
|
23
26
|
from sqlalchemy.orm import Session
|
|
24
27
|
|
|
25
28
|
from eflips.depot import SimpleTrip, Timetable as EflipsTimeTable
|
|
@@ -204,7 +207,9 @@ def check_depot_validity(depot: Depot) -> None:
|
|
|
204
207
|
|
|
205
208
|
def temperature_for_trip(trip_id: int, session: Session) -> float:
|
|
206
209
|
"""
|
|
207
|
-
Returns the temperature for a trip.
|
|
210
|
+
Returns the temperature for a trip.
|
|
211
|
+
|
|
212
|
+
Finds the temperature for the mid-point of the trip.
|
|
208
213
|
|
|
209
214
|
:param trip_id: The ID of the trip
|
|
210
215
|
:param session: The SQLAlchemy session
|
|
@@ -212,11 +217,18 @@ def temperature_for_trip(trip_id: int, session: Session) -> float:
|
|
|
212
217
|
"""
|
|
213
218
|
|
|
214
219
|
trip = session.query(Trip).filter(Trip.id == trip_id).one()
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
+
try:
|
|
221
|
+
temperatures = (
|
|
222
|
+
session.query(Temperatures)
|
|
223
|
+
.filter(Temperatures.scenario_id == trip.scenario_id)
|
|
224
|
+
.one()
|
|
225
|
+
)
|
|
226
|
+
except NoResultFound:
|
|
227
|
+
warnings.warn(
|
|
228
|
+
f"No temperatures found for scenario {trip.scenario_id}.",
|
|
229
|
+
ConsistencyWarning,
|
|
230
|
+
)
|
|
231
|
+
return None
|
|
220
232
|
|
|
221
233
|
# Find the mid-point of the trip
|
|
222
234
|
mid_time = trip.departure_time + (trip.arrival_time - trip.departure_time) / 2
|
|
@@ -1360,7 +1360,9 @@ class DepotControl:
|
|
|
1360
1360
|
for procID in area.available_processes
|
|
1361
1361
|
if self.depot.processes[procID]["type"].request_immediately
|
|
1362
1362
|
and (
|
|
1363
|
+
# TODO: Simplify this condition
|
|
1363
1364
|
self.depot.processes[procID]["kwargs"]["ismandatory"]
|
|
1365
|
+
and self.depot.processes[procID]["kwargs"]["vehicle_filter"](vehicle)
|
|
1364
1366
|
or not self.depot.processes[procID]["kwargs"]["ismandatory"]
|
|
1365
1367
|
and self.depot.processes[procID]["kwargs"]["vehicle_filter"](vehicle)
|
|
1366
1368
|
)
|
|
@@ -6,6 +6,7 @@ from abc import ABC, abstractmethod
|
|
|
6
6
|
from enum import auto, Enum
|
|
7
7
|
from warnings import warn
|
|
8
8
|
|
|
9
|
+
import numpy as np
|
|
9
10
|
import simpy
|
|
10
11
|
from eflips.helperFunctions import flexprint
|
|
11
12
|
from eflips.settings import globalConstants
|
|
@@ -672,7 +673,7 @@ class ChargeAbstract(VehicleProcess, ABC):
|
|
|
672
673
|
cancellable_for_dispatch=False,
|
|
673
674
|
efficiency=1,
|
|
674
675
|
*args,
|
|
675
|
-
**kwargs
|
|
676
|
+
**kwargs,
|
|
676
677
|
):
|
|
677
678
|
if required_resources is not None:
|
|
678
679
|
raise ValueError(
|
|
@@ -1428,6 +1429,25 @@ def exponential_power(vehicle, charging_interface, peq_params, *args, **kwargs):
|
|
|
1428
1429
|
)
|
|
1429
1430
|
|
|
1430
1431
|
|
|
1432
|
+
def charging_curve_power(vehicle, charging_interface, peq_params):
|
|
1433
|
+
"""
|
|
1434
|
+
Return power in kW for given peq_params in the format of dict[flaot, float]
|
|
1435
|
+
:param vehicle: SimpleVehicle
|
|
1436
|
+
:param charging_interface: DepotChargingInterface storing maximum power from charger
|
|
1437
|
+
:param peq_params: A dictionary with "soc" storing soc turning points and "power" for corresponding charging power
|
|
1438
|
+
:return: charging power in float for given SimpleVehicle
|
|
1439
|
+
"""
|
|
1440
|
+
|
|
1441
|
+
current_soc = vehicle.battery.soc
|
|
1442
|
+
p_max = charging_interface.max_power
|
|
1443
|
+
|
|
1444
|
+
current_power = min(
|
|
1445
|
+
p_max, np.interp(current_soc, peq_params["soc"], peq_params["power"])
|
|
1446
|
+
)
|
|
1447
|
+
|
|
1448
|
+
return float(current_power)
|
|
1449
|
+
|
|
1450
|
+
|
|
1431
1451
|
class Standby(VehicleProcess):
|
|
1432
1452
|
"""Process of mandatory waiting such as standby times."""
|
|
1433
1453
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "eflips-depot"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.15.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",
|
|
@@ -23,7 +23,7 @@ pandas = "^2.2.0"
|
|
|
23
23
|
xlrd = "<=1.2.0"
|
|
24
24
|
scipy = "^1.14.0"
|
|
25
25
|
eflips-model = "^10.0.0"
|
|
26
|
-
eflips-opt = "^0.3.
|
|
26
|
+
eflips-opt = "^0.3.6"
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
[tool.pytest.ini_options]
|
|
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
|
{eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/opt_tools/fitness_c_urfd.py
RENAMED
|
File without changes
|
{eflips_depot-4.14.3 → eflips_depot-4.15.1}/eflips/depot/layout_opt/opt_tools/fitness_util.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
|