Skip to content

Commit

Permalink
Add plots for blocks and vehicle services
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulScheerRLI committed Sep 18, 2024
1 parent c567fd0 commit bfcaa77
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 1 deletion.
148 changes: 147 additions & 1 deletion simba/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
import re
from typing import Iterable

import matplotlib.colors
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from spice_ev.report import aggregate_global_results, plot, generate_reports, aggregate_timeseries
from spice_ev.util import sanitize

from simba import util


def open_for_csv(file_path):
""" Create a file handle to write to.
Expand Down Expand Up @@ -191,6 +195,9 @@ def generate_plots(schedule, scenario, args):
extended_plots_path = args.results_directory.joinpath("extended_plots")
extended_plots_path.mkdir(parents=True, exist_ok=True)

plot_blocks_dense(schedule, extended_plots_path)
plot_vehicle_services(schedule, extended_plots_path)

plot_consumption_per_rotation_distribution(extended_plots_path, schedule)
plot_distance_per_rotation_distribution(extended_plots_path, schedule)
plot_charge_type_distribution(extended_plots_path, scenario, schedule)
Expand Down Expand Up @@ -596,12 +603,151 @@ def plot_gc_power_timeseries(extended_plots_path, scenario):

# xaxis are datetime strings
ax.set_xlim(time_values[0], time_values[-1])
ax.tick_params(axis='x', rotation=30)
# ax.tick_params(axis='x', rotation=30)

plt.savefig(extended_plots_path / f"{sanitize(gc)}_power_time_overview.png")
plt.close(fig)


def plot_vehicle_services(schedule, output_path):
"""Plots the rotations serviced by the same vehicle
:param schedule: Provides the schedule data.
:type schedule; simba.schedule.Schedule
:param output_path: Path to the output folder
:type output_path: pathlib.Path
"""

# find depots for every rotation
all_rotations = schedule.rotations
rotations_per_depot = {r.departure_name: [] for r in all_rotations.values()}
for rotation in all_rotations.values():
rotations_per_depot[rotation.departure_name].append(rotation)

def color_generator():
# generates color according to vehicle_type and charging type of rotation
colors = util.cycling_generator(plt.rcParams['axes.prop_cycle'].by_key()["color"])
color_per_vt = {vt: next(colors) for vt in schedule.vehicle_types}
color = None
while True:
rotation = yield color
color = color_per_vt[rotation.vehicle_type]
rgb = matplotlib.colors.to_rgb(color)
hsv = matplotlib.colors.rgb_to_hsv(rgb)
if rotation.charging_type == "depb":
pass
elif rotation.charging_type == "oppb":
hsv[-1] /= 2
else:
raise NotImplementedError
color = matplotlib.colors.hsv_to_rgb(hsv)

def vehicle_id_row_generator():
# generate row ids by using the vehicle_id of the rotation
vehicle_id = None
while True:
rotation = yield vehicle_id
vehicle_id = rotation.vehicle_id

# create instances of the generators
dense_row_generator = vehicle_id_row_generator()
color_gen = color_generator()
# initialize row_generator by yielding None
next(dense_row_generator)
next(color_gen)

rotations_per_depot["All_Depots"] = all_rotations.values()

output_path_folder = output_path / "vehicle_services"
output_path_folder.mkdir(parents=True, exist_ok=True)
for depot, rotations in rotations_per_depot.items():
sorted_rotations = list(
sorted(rotations,
key=lambda x: (x.vehicle_type, x.charging_type, x.departure_time)))
fig, ax = create_plot_blocks(sorted_rotations, color_gen, dense_row_generator)
ax.set_xlabel("Vehicle ID")
ax.set_xlabel("Block")
fig.savefig(output_path_folder / f"{sanitize(depot)}_vehicle_services.png")
plt.close(fig)


def plot_blocks_dense(schedule, output_path):
"""Plots the different loads (total, feedin, external) of all grid connectors.
:param schedule: Provides the schedule data.
:type schedule; simba.schedule.Schedule
:param output_path: Path to the output folder
:type output_path: pathlib.Path
"""
# find depots for every rotation
all_rotations = schedule.rotations
rotations_per_depot = {r.departure_name: [] for r in all_rotations.values()}
for rotation in all_rotations.values():
rotations_per_depot[rotation.departure_name].append(rotation)

def color_generator():
# Generates color according to rotation vehicle_type

colors = util.cycling_generator(plt.rcParams['axes.prop_cycle'].by_key()["color"])
color_per_vt = {vt: next(colors) for vt in schedule.vehicle_types}
color = None
while True:
rotation = yield color
color = color_per_vt[rotation.vehicle_type]

def dense_row_generator():
# Generates row id by trying to find lowes row with a rotation which arrived
arrival_times = []
row_nr = None
while True:
rotation = yield row_nr
for i in range(len(arrival_times)):
at = arrival_times[i]
if at <= rotation.departure_time:
arrival_times[i] = rotation.arrival_time
row_nr = i
break
else:
arrival_times.append(rotation.arrival_time)
row_nr = len(arrival_times) - 1

rotations_per_depot["All_Depots"] = all_rotations.values()
# create instances of the generators
dense_row_generator = dense_row_generator()
color_gen = color_generator()
# initialize row_generator by yielding None
next(dense_row_generator)
next(color_gen)
output_path_folder = output_path / "block_distribution"
output_path_folder.mkdir(parents=True, exist_ok=True)
for depot, rotations in rotations_per_depot.items():
fig, ax = create_plot_blocks(rotations, color_gen, dense_row_generator)
ax.set_xlabel("Block")
fig.savefig(output_path_folder / f"{sanitize(depot)}_block_distribution.png")
plt.close(fig)


def create_plot_blocks(rotations, color_generator, row_generator):
fig, ax = plt.subplots()
rotations = list(sorted(rotations, key=lambda r: r.departure_time))
for rotation in rotations:
row_nr = row_generator.send(rotation)
width = rotation.arrival_time - rotation.departure_time
artist = ax.barh([row_nr], width=[width], height=0.8, left=rotation.departure_time,
label=rotation.id, color=color_generator.send(rotation))
ax.bar_label(artist, labels=[rotation.id], label_type='center')

ax.xaxis.set_major_locator(mdates.AutoDateLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter('%D %H:%M'))
ax.yaxis.get_major_locator().set_params(integer=True)
ax.set_xlim(min(r.departure_time for r in rotations) - datetime.timedelta(minutes=10),
max(r.arrival_time for r in rotations) + datetime.timedelta(minutes=10))
ax.set_xticklabels(ax.get_xticklabels(), rotation=30, ha='right')
ax.set_xlabel('Time')
fig.tight_layout()
return fig, ax


def count_active_rotations(scenario, schedule):
num_active_rotations = [0] * scenario.n_intervals
for rotation in schedule.rotations.values():
Expand Down
13 changes: 13 additions & 0 deletions simba/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,19 @@ def cast_float_or_none(val: any) -> any:
return None


def cycling_generator(cycle: []):
"""Generator to loop over lists
:param cycle: list to cycle through
:type cycle: list()
:yields: iterated value
:rtype: Iterator[any]
"""
i = 0
while True:
yield cycle[i % len(cycle)]
i += 1


def setup_logging(args, time_str):
""" Setup logging.
Expand Down

0 comments on commit bfcaa77

Please sign in to comment.