eflips-depot 4.15.0__py3-none-any.whl → 4.15.2__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.

@@ -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
- info = ConsumptionInformation(
328
- trip_id=trip.id,
329
- consumption_lut=consumption_lut,
330
- average_speed=average_speed,
331
- distance=total_distance,
332
- temperature=temperature,
333
- level_of_loading=level_of_loading,
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
 
@@ -230,7 +230,7 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
230
230
  if process_type(processes_in_area) == ProcessType.CHARGING:
231
231
  # Add the charging process for this vehicle type
232
232
  template["areas"][area_name]["available_processes"].append(
233
- str(processes_in_area.id) + str(area.vehicle_type_id)
233
+ str(processes_in_area.id) + "vt" + str(area.vehicle_type_id)
234
234
  )
235
235
  # Delete the original charging process
236
236
  template["areas"][area_name]["available_processes"].remove(
@@ -253,7 +253,7 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
253
253
  # Add the charging process for this vehicle type
254
254
  for vt in scenario.vehicle_types:
255
255
  template["areas"][area_name]["available_processes"].append(
256
- str(processes_in_area.id) + str(vt.id)
256
+ str(processes_in_area.id) + "vt" + str(vt.id)
257
257
  )
258
258
  # Delete the original charging process
259
259
  template["areas"][area_name]["available_processes"].remove(
@@ -386,30 +386,31 @@ def depot_to_template(depot: Depot) -> Dict[str, str | Dict[str, str | int]]:
386
386
 
387
387
  for vt in all_vehicle_types:
388
388
  charging_curve = vt.charging_curve
389
- template["processes"][process_name + str(vt.id)] = template[
389
+
390
+ charging_process_name = process_name + "vt" + str(vt.id)
391
+ template["processes"][charging_process_name] = template[
390
392
  "processes"
391
393
  ][process_name].copy()
392
- template["processes"][process_name + str(vt.id)][
394
+ template["processes"][charging_process_name][
393
395
  "typename"
394
396
  ] = "ChargeEquationSteps"
395
- template["processes"][process_name + str(vt.id)][
396
- "vehicle_filter"
397
- ] = {
397
+ template["processes"][charging_process_name]["vehicle_filter"] = {
398
398
  "filter_names": ["vehicle_type"],
399
399
  "vehicle_types": [str(vt.id)],
400
400
  }
401
401
 
402
- template["processes"][process_name + str(vt.id)][
402
+ template["processes"][charging_process_name][
403
403
  "peq_name"
404
404
  ] = "charging_curve_power"
405
- template["processes"][process_name + str(vt.id)]["peq_params"] = {
405
+ template["processes"][charging_process_name]["peq_params"] = {
406
406
  "soc": [soc_power_pair[0] for soc_power_pair in charging_curve],
407
407
  "power": [
408
408
  soc_power_pair[1] for soc_power_pair in charging_curve
409
409
  ],
410
+ "precision": 0.01,
410
411
  }
411
412
 
412
- del template["processes"][process_name + str(vt.id)]["dur"]
413
+ del template["processes"][charging_process_name]["dur"]
413
414
 
414
415
  # delete the original process
415
416
  del template["processes"][process_name]
@@ -1,6 +1,7 @@
1
1
  import datetime
2
2
  import itertools
3
3
  import logging
4
+ import math
4
5
  import warnings
5
6
  from datetime import timedelta
6
7
  from typing import List, Dict
@@ -398,6 +399,10 @@ def add_soc_to_events(dict_of_events, battery_log) -> None:
398
399
  """
399
400
  battery_log_list = []
400
401
  for log in battery_log:
402
+ # TODO this is a bypass of update events having lower energy_real than the event before. It happens in processes L 1304
403
+ if log.event_name == "update":
404
+ continue
405
+
401
406
  battery_log_list.append((log.t, round(log.energy / log.energy_real, 4)))
402
407
 
403
408
  time_keys = sorted(dict_of_events.keys())
@@ -522,7 +527,7 @@ def add_events_into_database(
522
527
  if "timeseries" in process_dict.keys():
523
528
  # Convert "time" in timeseries to timedelta
524
529
  timeseries["time"] = [
525
- (timedelta(seconds=t) + simulation_start_time).isoformat()
530
+ (timedelta(seconds=math.ceil(t)) + simulation_start_time).isoformat()
526
531
  for t in process_dict["timeseries"]["time"]
527
532
  ]
528
533
  timeseries["soc"] = process_dict["timeseries"]["soc"]
@@ -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
@@ -99,6 +102,7 @@ def vehicle_type_to_global_constants_dict(vt: VehicleType) -> Dict[str, float]:
99
102
  "soc_max": 1.0,
100
103
  "soc_init": 1.0,
101
104
  "soh": 1.0,
105
+ "charging_efficiency": vt.charging_efficiency,
102
106
  }
103
107
  return the_dict
104
108
 
@@ -204,7 +208,9 @@ def check_depot_validity(depot: Depot) -> None:
204
208
 
205
209
  def temperature_for_trip(trip_id: int, session: Session) -> float:
206
210
  """
207
- Returns the temperature for a trip. Finds the temperature for the mid-point of the trip.
211
+ Returns the temperature for a trip.
212
+
213
+ Finds the temperature for the mid-point of the trip.
208
214
 
209
215
  :param trip_id: The ID of the trip
210
216
  :param session: The SQLAlchemy session
@@ -212,11 +218,18 @@ def temperature_for_trip(trip_id: int, session: Session) -> float:
212
218
  """
213
219
 
214
220
  trip = session.query(Trip).filter(Trip.id == trip_id).one()
215
- temperatures = (
216
- session.query(Temperatures)
217
- .filter(Temperatures.scenario_id == trip.scenario_id)
218
- .one()
219
- )
221
+ try:
222
+ temperatures = (
223
+ session.query(Temperatures)
224
+ .filter(Temperatures.scenario_id == trip.scenario_id)
225
+ .one()
226
+ )
227
+ except NoResultFound:
228
+ warnings.warn(
229
+ f"No temperatures found for scenario {trip.scenario_id}.",
230
+ ConsistencyWarning,
231
+ )
232
+ return None
220
233
 
221
234
  # Find the mid-point of the trip
222
235
  mid_time = trip.departure_time + (trip.arrival_time - trip.departure_time) / 2
eflips/depot/processes.py CHANGED
@@ -840,6 +840,7 @@ class Charge(ChargeAbstract):
840
840
 
841
841
  @property
842
842
  def power(self):
843
+ # TODO this doesn't consider vehicle charging power
843
844
  return self.charging_interface.max_power
844
845
 
845
846
  def _action(self, *args, **kwargs):
@@ -1240,6 +1241,8 @@ class ChargeEquationSteps(ChargeAbstract):
1240
1241
  self.precision = precision
1241
1242
  Charge.check_soc_target(soc_target, vehicle_filter)
1242
1243
  self.soc_target = soc_target
1244
+ if "precision" in self.peq_params:
1245
+ self.precision = self.peq_params["precision"]
1243
1246
 
1244
1247
  @property
1245
1248
  def power(self):
@@ -1271,8 +1274,12 @@ class ChargeEquationSteps(ChargeAbstract):
1271
1274
  )
1272
1275
  soc_target_step = self.vehicle.battery.soc + soc_interval
1273
1276
  amount = self.vehicle.battery.energy_real * soc_interval
1274
- effective_power = self.power * self.efficiency
1275
- self.dur = int(amount / effective_power * 3600)
1277
+ effective_power = (
1278
+ self.power
1279
+ * self.efficiency
1280
+ * self.vehicle.vehicle_type.charging_efficiency
1281
+ )
1282
+ self.dur = round(amount / effective_power * 3600, 12)
1276
1283
 
1277
1284
  if self.dur == 0:
1278
1285
  # amount is so small that dur is <1s. Reduce the precision
@@ -1295,6 +1302,7 @@ class ChargeEquationSteps(ChargeAbstract):
1295
1302
  yield self.env.timeout(self.dur)
1296
1303
 
1297
1304
  if soc_target_step < self.soc_target:
1305
+ # TODO why here we have energy higher than the step_soc_target?
1298
1306
  # recalculate amount because update_battery may have been
1299
1307
  # called in the meantime
1300
1308
  amount_step = (
@@ -1438,13 +1446,21 @@ def charging_curve_power(vehicle, charging_interface, peq_params):
1438
1446
  :return: charging power in float for given SimpleVehicle
1439
1447
  """
1440
1448
 
1449
+ precision = peq_params.get("precision", 0.01)
1441
1450
  current_soc = vehicle.battery.soc
1442
1451
  p_max = charging_interface.max_power
1452
+ target_step_soc = min(vehicle.battery.soc + precision, vehicle.battery.soc_max)
1443
1453
 
1444
- current_power = min(
1454
+ power_current_soc = min(
1445
1455
  p_max, np.interp(current_soc, peq_params["soc"], peq_params["power"])
1446
1456
  )
1447
1457
 
1458
+ power_target_soc = min(
1459
+ p_max, np.interp(target_step_soc, peq_params["soc"], peq_params["power"])
1460
+ )
1461
+
1462
+ current_power = min(power_current_soc, power_target_soc)
1463
+
1448
1464
  return float(current_power)
1449
1465
 
1450
1466
 
eflips/depot/rating.py CHANGED
@@ -422,7 +422,7 @@ class RfdDiffDispatch:
422
422
  diffs = []
423
423
  for vehicle in area.vehicles:
424
424
  etc = vehicle.dwd.etc_processes
425
- if isinstance(etc, int):
425
+ if isinstance(etc, int) or isinstance(etc, float):
426
426
  diff = etc - slot[0].env.now
427
427
  diffs.append(diff)
428
428
  # Convert EstimateValue to numerical rfddiff
@@ -36,7 +36,17 @@ class VehicleType:
36
36
 
37
37
  """
38
38
 
39
- def __init__(self, ID, battery_capacity, soc_min, soc_max, soc_init, soh, CR=None):
39
+ def __init__(
40
+ self,
41
+ ID,
42
+ battery_capacity,
43
+ soc_min,
44
+ soc_max,
45
+ soc_init,
46
+ soh,
47
+ charging_efficiency=1.0,
48
+ CR=None,
49
+ ):
40
50
  self.ID = ID
41
51
  self.battery_capacity = battery_capacity
42
52
  self.soc_min = soc_min
@@ -45,6 +55,7 @@ class VehicleType:
45
55
  self.soh = soh
46
56
  self.CR = CR
47
57
  self.group = None
58
+ self.charging_efficiency = charging_efficiency
48
59
 
49
60
  self.count = {}
50
61
  self.share = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eflips-depot
3
- Version: 4.15.0
3
+ Version: 4.15.2
4
4
  Summary: Depot Simulation for eFLIPS
5
5
  License: AGPL-3.0-or-later
6
6
  License-File: LICENSE.md
@@ -2,10 +2,10 @@ eflips/depot/__init__.py,sha256=FCVnYU7aSfR4BNCo722I1V3uU5336qr1tTt-zT6X4UI,1555
2
2
  eflips/depot/api/__init__.py,sha256=nq5aHGTOIpDmvDfcGMlCH_jd97E0VZ28KVcT3XTwpbE,67539
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
- eflips/depot/api/private/consumption.py,sha256=0wfI3ryfZS9qUqPbJP0okvwY2s6ogylw1PZFyAnCRf4,28915
6
- eflips/depot/api/private/depot.py,sha256=Vx6mC5YynyQ5hEw9QeXAi84VSaYc3qVJAqE2dSfHGJI,59655
7
- eflips/depot/api/private/results_to_database.py,sha256=-8qXk8lRsMElm9pke6MWzCNUQKtrd59n4Ye2X8Ojw-M,29814
8
- eflips/depot/api/private/util.py,sha256=DasTkuGUhlBpY_BtTFWoxSNZU_CRyM3RqEDgO07Eks8,17990
5
+ eflips/depot/api/private/consumption.py,sha256=494u6FelymWfI-Yt3ybJpLHUL-6zEqBOT2qSrPhRmMo,30038
6
+ eflips/depot/api/private/depot.py,sha256=G08E9yZ18c2oSQVYGRI8nAHtGZ8EHWsRJUOR23a2bao,59720
7
+ eflips/depot/api/private/results_to_database.py,sha256=OewbDwbk54GPORMVavLbJ7DSZpRKBLHM7q9aWKTngs8,30026
8
+ eflips/depot/api/private/util.py,sha256=e7T_-1AUXoCV9c11zaED2-za7p0TDhYG4Dg0lBZnrZI,18342
9
9
  eflips/depot/configuration.py,sha256=Op3hlir-dEN7yHr0kTqbYANoCBKFWK6uKOv3NJl8w_w,35678
10
10
  eflips/depot/depot.py,sha256=5NBw5bvOP2cplkRGZbKbG3SOA23F2Kw-UsabHubocPU,107555
11
11
  eflips/depot/evaluation.py,sha256=qqXyP4jA1zFcKuWhliQ6n25ZlGl9mJV-vtXf0yu8WN8,140842
@@ -27,16 +27,16 @@ eflips/depot/layout_opt/settings.py,sha256=EUGCp4dAX22j2uF8sKqbi9a5iP8hb6QpP7t2N
27
27
  eflips/depot/layout_opt/template_creation.py,sha256=H4LoFjQfbPjTt9rGvapH2tEUWcQ56kPwDucq7t6YahU,9736
28
28
  eflips/depot/layout_opt/util.py,sha256=EYh7IN58ZjysmCFdSieQqIQ9goe1a_ZwARRHxOgjEQo,3780
29
29
  eflips/depot/plots.py,sha256=85xInZWfJIOVm03onvppgP5yLTgQeMn-1t5aoNdavyY,2509
30
- eflips/depot/processes.py,sha256=n4p_TDvtCDdkagLyX8eJbFFxext3fpVTTfbrGsCO52o,59321
31
- eflips/depot/rating.py,sha256=audUASHExeWg5ugytPLlcy5QGiTiFucnZ6-v3REoO2g,16427
30
+ eflips/depot/processes.py,sha256=2ovkJuyBCHT3L4JudePVGVqdk_KKnGQXorndqdEFqsQ,60017
31
+ eflips/depot/rating.py,sha256=WjySy_cT3CtKLFaCCCVZB1f7qCYRyNFxAnM6IgJW7Ro,16453
32
32
  eflips/depot/resources.py,sha256=0SuzN8qgMmCqa7oUEXVC_XE6pCUtxTsqOfCsaM9Oh3o,13568
33
33
  eflips/depot/settings_config.py,sha256=z7CqPdjd8QRlgZj0Zis-H13cL7LOiheRT4ctYCYGguc,2527
34
- eflips/depot/simple_vehicle.py,sha256=Wl3IqYxMrs6J0U0iCrG3Qq8ONuDGoZ00r7h7svVyEik,9375
34
+ eflips/depot/simple_vehicle.py,sha256=-fqHhuyC1KXhl53zCZh8x8NUQnuz4EpZ6oLtaTaRqFw,9534
35
35
  eflips/depot/simulation.py,sha256=ee0qTzOzG-8ybN36ie_NJallXfC7jUaS9JZvaYFziLs,10676
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.15.0.dist-info/METADATA,sha256=QsUnXsbK3OWypgsK6H6gCsfv6eHkTV4upbF1X9ZAUhI,5961
40
- eflips_depot-4.15.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
41
- eflips_depot-4.15.0.dist-info/licenses/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
42
- eflips_depot-4.15.0.dist-info/RECORD,,
39
+ eflips_depot-4.15.2.dist-info/METADATA,sha256=vxdX4GRYggcgaLeIG84k6D6teA4g0i_PSYFY-U1IkGg,5961
40
+ eflips_depot-4.15.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
41
+ eflips_depot-4.15.2.dist-info/licenses/LICENSE.md,sha256=KB4XTk1fPHjtZCYDyPyreu6h1LVJVZXYg-5vePcWZAc,34143
42
+ eflips_depot-4.15.2.dist-info/RECORD,,