from matplotlib.collections import PatchCollection
from matplotlib.patches import Circle, Rectangle
__author__ = 'marvinler'
# Copyright (C) 2017-2018 RTE and INRIA (France)
# Authors: Marvin Lerousseau <marvin.lerousseau@gmail.com>
# This file is under the LGPL-v3 license and is part of PyPowNet.
import pygame
import math
from pygame import gfxdraw
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.backends.backend_agg as agg
import matplotlib.pyplot as plt
import matplotlib.lines as lines
import pylab
from copy import deepcopy
case_layouts = {
14: [(280, -81), (100, -270), (-366, -270), (-366, -54), (64, -54), (64, 54), (-366, 0), (-438, 0), (-326, 54),
(-222, 108), (-79, 162), (152, 270), (64, 270), (-222, 216)],
30: [(-320, -217), (-188, -306), (-191, -221), (-64, -220), (156, -307), (223, -232), (217, -274), (401, -236),
(200, -145), (238, -125), (87, -143), (-60, -113), (-185, -114), (-159, -60), (-62, 12), (-13, -73),
(102, -54), (89, 26), (281, 37), (240, 9), (278, -27), (322, -44), (99, 74), (328, 74), (219, 144), (97, 147),
(101, 215), (400, 195), (-179, 211), (-181, 136)]
,
96: [(49.0, -243.0), (95.5, -242.5), (24.5, -195.5), (41.5, -216.0), (87.5, -220.5), (154.0, -205.0),
(132.0, -243.0), (154.5, -228.0), (80.0, -196.5), (121.5, -197.0), (77.0, -163.5), (121.0, -163.0),
(164.0, -120.0), (65.5, -125.0), (29.5, -143.0), (25.5, -116.0), (13.5, -84.0), (44.0, -64.5),
(83.0, -106.5), (111.0, -105.5), (80.0, -65.5), (117.5, -65.5), (149.0, -93.0), (24.5, -163.5),
(252.0, -242.0), (295.0, -241.5), (221.5, -195.5), (245.0, -216.0), (291.0, -219.5), (357.5, -203.5),
(335.0, -240.5), (357.5, -227.0), (283.0, -196.5), (326.0, -195.5), (281.5, -163.0), (325.5, -162.5),
(367.5, -120.5), (268.0, -125.0), (230.0, -142.5), (225.0, -115.5), (216.5, -81.5), (248.0, -64.0),
(285.5, -105.0), (318.5, -105.0), (282.5, -64.0), (322.0, -64.0), (352.0, -93.0), (226.0, -164.0),
(449.0, -243.0), (493.0, -243.0), (427.0, -196.5), (444.0, -216.0), (490.5, -219.5), (555.5, -205.0),
(532.0, -242.5), (557.5, -224.5), (481.0, -197.0), (524.5, -196.0), (480.5, -163.5), (540.5, -162.5),
(566.5, -121.0), (467.5, -126.0), (431.0, -143.5), (426.0, -114.5), (415.5, -83.5), (447.5, -64.0),
(484.5, -106.0), (517.5, -106.0), (483.0, -64.0), (520.0, -64.5), (553.0, -94.0), (426.0, -164.0),
(379.0, -28.0)],
118:
[(-403, -311), (-355, -311), (-380, -275), (-355, -245), (-369, -191), (-330, -193), (-299, -190), (-366, -88),
(-364, -44), (-366, -7), (-320, -247), (-266, -266), (-241, -198), (-203, -231), (-188, -201), (-282, -153),
(-221, -123), (-161, -123), (-131, -156), (-139, -142), (-131, -27), (-123, -3), (-131, 29), (-18, -46),
(-162, 67), (-203, 39), (-324, 21), (-332, -15), (-331, -52), (-212, -88), (-292, -52), (-259, -29),
(-4, -254), (32, -203), (-34, -148), (51, -155), (74, -221), (88, -127), (59, -265), (86, -296), (129, -296),
(161, -296), (124, -198), (140, -226), (147, -163), (133, -138), (162, -134), (187, -173), (221, -125),
(268, -215), (287, -225), (199, -258), (202, -296), (237, -295), (329, -296), (283, -297), (268, -248),
(287, -248), (372, -277), (372, -197), (372, -153), (340, -74), (348, -254), (342, -168), (298, -29),
(283, -74), (297, -92), (213, -62), (184, -50), (61, -45), (40, -73), (25, -52), (61, -84), (43, 53), (61, 73),
(151, 73), (176, 99), (195, 53), (221, 33), (227, 73), (230, 56), (149, 131), (57, 154), (46, 171), (43, 205),
(43, 229), (57, 245), (78, 205), (119, 207), (122, 241), (191, 243), (196, 207), (219, 186), (245, 154),
(212, 154), (221, 132), (220, 110), (262, 94), (294, 74), (288, 154), (273, 230), (226, 229), (326, 230),
(330, 152), (365, 154), (364, 91), (404, 154), (370, 191), (373, 212), (361, 253), (330, 260), (403, 253),
(-256, -102), (-270, 0), (-236, 1), (229, -32), (-211, -266), (99, 74)]
}
# noinspection PyArgumentList
[docs]class Renderer(object):
def __init__(self, grid_case, or_ids, ex_ids, are_prods, are_loads, timestep_duration_seconds):
self.grid_case = grid_case
self.grid_layout = np.asarray(case_layouts[grid_case])
self.video_width, self.video_height = 1300, 700
self.timestep_duration_seconds = timestep_duration_seconds
self.screen = pygame.display.set_mode((self.video_width, self.video_height), pygame.RESIZABLE)
pygame.display.set_caption('pypownet - render mode') # Window title
# Set default background color
self.background_color = [70, 70, 73]
self.screen.fill(self.background_color)
self.topology_layout_shape = [1000, 800]
self.topology_layout = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
# Substations layer
self.nodes_surface = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
self.nodes_outer_radius = 8
self.nodes_inner_radius = 5
# node_img = pygame.image.load(os.path.join(media_path, 'substation.png')).convert_alpha()
# self.node_img = pygame.transform.scale(node_img, (20, 20))
self.injections_surface = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
self.are_prods = are_prods
self.are_loads = are_loads
# Lines layer
self.lines_surface = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
self.lines_ids_or = or_ids
self.lines_ids_ex = ex_ids
# Lines labels (e.g. mW) layer
self.lines_labels_surface = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA,
32).convert_alpha()
self.left_menu_shape = [300, 800]
self.left_menu = pygame.Surface(self.left_menu_shape, pygame.SRCALPHA, 32).convert_alpha()
self.left_menu_tile_color = [e + 10 for e in self.background_color]
# Helpers for printing or plotting
pygame.font.init()
font = 'Arial'
self.default_font = pygame.font.SysFont(font, 15)
text_color = (180, 180, 180)
value_color = (220, 220, 220)
self.text_render = lambda s: self.default_font.render(s, False, text_color)
self.value_render = lambda s: self.default_font.render(s, False, value_color)
big_value_font = pygame.font.SysFont('Arial', 18)
self.big_value_render = lambda s: big_value_font.render(s, False, value_color)
self.bold_white_font = pygame.font.SysFont(font, 15)
bold_white = (220, 220, 220)
self.bold_white_font.set_bold(True)
self.bold_white_render = lambda s: self.bold_white_font.render(s, False, bold_white)
# Containers for plotting prods and loads curves
self.loads = []
self.relative_thermal_limits = []
self.black_bold_font = pygame.font.SysFont(font, 15)
blackish = (70, 70, 70)
self.black_bold_font.set_bold(True)
self.black_bold_font_render = lambda s: self.black_bold_font.render(s, False, blackish)
self.last_rewards_surface = None
self.game_over_surface = self.draw_plot_game_over()
self.boolean_dynamic_arrows = True
# Keep data to track changes timestepwise
self.data = None
[docs] def draw_surface_grid(self, relative_thermal_limits, lines_por, lines_service_status, prods, loads,
are_substations_changed, number_nodes_per_substation):
layout = self.grid_layout
my_dpi = 200
fig = plt.figure(figsize=(1000 / my_dpi, 700 / my_dpi), dpi=my_dpi,
facecolor=[c / 255. for c in self.background_color], clear=True)
l = []
layout = np.asarray(deepcopy(layout))
min_x = np.min(layout[:, 0])
min_y = np.min(layout[:, 1])
layout[:, 0] -= (min_x + 890)
layout[:, 0] *= -1
layout[:, 1] -= min_y
if self.grid_case == 14:
layout[:, 0] -= 120
layout[:, 1] += 30
color_low = np.asarray((51, 204, 51))
color_middle = np.asarray((255, 93, 0))
color_high = np.asarray((255, 50, 30))
for or_id, ex_id, rtl, line_por, is_on in zip(self.lines_ids_or, self.lines_ids_ex, relative_thermal_limits,
lines_por, lines_service_status):
# Compute line thickness + color based on its thermal usage
thickness = .6 + .25 * (min(1., rtl) // .1)
if rtl < .5:
color = color_low + 2. * rtl * (color_middle - color_low)
elif rtl < 1.:
# color = (51, 204, 51) if rtl < .7 else (255, 165, 0) if rtl < 1. else (214, 0, 0)
color = color_low + min(1., rtl) * (color_high - color_low)
else:
color = (255, 20, 20)
# Compute the true origin of the flow (lines always fixed or -> dest in IEEE files)
if line_por >= 0:
ori = layout[or_id]
ext = layout[ex_id]
else:
ori = layout[ex_id]
ext = layout[or_id]
if not is_on:
l.append(lines.Line2D([ori[0], ext[0]], [50 + ori[1], 50 + ext[1]], linewidth=.8,
color=[.8, .8, .8], figure=fig, linestyle='dashed'))
else:
l.append(lines.Line2D([ori[0], ext[0]], [50 + ori[1], 50 + ext[1]], linewidth=thickness,
color=[c / 255. for c in color], figure=fig,
linestyle='--' if rtl > 1. else '-',
dashes=(2., .8) if rtl > 1. else (None, None)))
fig.lines.extend(l)
######## Draw nodes
ax = fig.gca(frame_on=False, autoscale_on=False, zorder=10)
ax.set_xlim(0, 1000)
ax.set_ylim(-50, 650)
fig.subplots_adjust(0, 0, 1, 1, 0, 0)
ax.set_xticks([])
ax.set_yticks([])
# Loop to compute prods minus loads
prods_iter, loads_iter = iter(prods), iter(loads)
prods_minus_loads = []
for is_prod, is_load in zip(self.are_prods, self.are_loads):
prod = next(prods_iter) if is_prod else 0.
load = next(loads_iter) if is_load else 0.
prods_minus_loads.append(prod - load)
max_diff = max(abs(np.max(prods_minus_loads)), abs(np.min(prods_minus_loads)))
activated_node_color = (255, 255, 0)
prods_iter, loads_iter = iter(prods), iter(loads)
for i, ((x, y), is_prod, is_load, is_changed, n_used_nodes) in enumerate(
zip(layout, self.are_prods, self.are_loads, are_substations_changed, number_nodes_per_substation)):
prod = next(prods_iter) if is_prod else 0.
load = next(loads_iter) if is_load else 0.
prod_minus_load = prod - load
# Determine color of filled circle based on the amount of production - consumption
linewidth_min = 1.
if prod_minus_load > 0: # Draw production
color = [c / 255. for c in (0, 153, 255)]
inner_circle_color = activated_node_color if is_changed else self.background_color
inner_circle_color = [c / 255. for c in inner_circle_color]
linewidth = linewidth_min + 2. * prod_minus_load / max_diff
outer_radius = self.nodes_outer_radius + 3. * prod_minus_load / max_diff
if n_used_nodes > 1:
c = Circle((x, y), outer_radius + linewidth + 4., linewidth=0., fill=True,
color=[c / 255. for c in self.background_color], zorder=10)
ax.add_artist(c)
c = Circle((x, y), outer_radius + linewidth + 4., linewidth=.75, fill=False, color=color,
zorder=10)
ax.add_artist(c)
c = Circle((x, y), outer_radius, linewidth=0, fill=True, color=inner_circle_color, zorder=9)
ax.add_artist(c)
c = Circle((x, y), outer_radius, linewidth=linewidth, fill=False, color=color, zorder=10)
ax.add_artist(c)
elif prod_minus_load < 0: # Draw consumption
color = [c / 255. for c in (210, 77, 255)]
inner_circle_color = activated_node_color if is_changed else self.background_color
inner_circle_color = [c / 255. for c in inner_circle_color]
linewidth = linewidth_min - 2. * prod_minus_load / max_diff
outer_radius = self.nodes_outer_radius - 3. * prod_minus_load / max_diff
if n_used_nodes > 1:
c = Rectangle((x - outer_radius - linewidth - 4., y - outer_radius - linewidth - 4.),
2. * (outer_radius + linewidth + 4.), 2. * (outer_radius + linewidth + 4.),
linewidth=0., fill=True, color=[c / 255. for c in self.background_color], zorder=10)
ax.add_artist(c)
c = Rectangle((x - outer_radius - linewidth - 4., y - outer_radius - linewidth - 4.),
2. * (outer_radius + linewidth + 4.), 2. * (outer_radius + linewidth + 4.),
linewidth=.6, fill=False, color=color, zorder=10)
ax.add_artist(c)
c = Rectangle((x - outer_radius, y - outer_radius), 2. * outer_radius, 2. * outer_radius,
linewidth=0, fill=True, color=inner_circle_color, zorder=9)
ax.add_artist(c)
c = Rectangle((x - outer_radius, y - outer_radius), 2. * outer_radius, 2. * outer_radius,
linewidth=linewidth, fill=False, color=color, zorder=10)
ax.add_artist(c)
else:
color = [c / 255. for c in (255, 255, 255)]
inner_circle_color = activated_node_color if is_changed else self.background_color
inner_circle_color = [c / 255. for c in inner_circle_color]
linewidth = linewidth_min
outer_radius = self.nodes_outer_radius
if n_used_nodes > 1:
c = Rectangle((x, y - math.sqrt(2.) * (outer_radius + 4.)),
2. * (outer_radius + 4.), 2. * (outer_radius + 4.),
linewidth=0., fill=True, color=[c / 255. for c in self.background_color],
zorder=10, angle=45.)
ax.add_artist(c)
c = Rectangle((x, y - math.sqrt(2.) * (outer_radius + 4.)),
2. * (outer_radius + 4.), 2. * (outer_radius + 4.),
linewidth=.6, fill=False, color=color, zorder=10, angle=45.)
ax.add_artist(c)
c = Rectangle((x, y - math.sqrt(2.) * outer_radius), 2. * outer_radius, 2. * outer_radius,
linewidth=0, fill=True, color=inner_circle_color, zorder=9, angle=45.)
ax.add_artist(c)
c = Rectangle((x, y - math.sqrt(2.) * outer_radius), 2. * outer_radius, 2. * outer_radius,
linewidth=linewidth, fill=False, color=color, zorder=10, angle=45.)
ax.add_artist(c)
# Circle((x, y), self.nodes_inner_radius, fill=True, color=inner_circle_color)
l = []
for or_id, ex_id, rtl, line_por, is_on in zip(self.lines_ids_or, self.lines_ids_ex, relative_thermal_limits,
lines_por, lines_service_status):
if not is_on:
continue
# Compute line thickness + color based on its thermal usage
thickness = .6 + .04 * (min(1., rtl) // .1)
if rtl < .5:
color = color_low + 2. * rtl * (color_middle - color_low)
elif rtl < 1.:
# color = (51, 204, 51) if rtl < .7 else (255, 165, 0) if rtl < 1. else (214, 0, 0)
color = color_low + min(1., rtl) * (color_high - color_low)
else:
color = (255, 20, 20)
# Compute the true origin of the flow (lines always fixed or -> dest in IEEE files)
if line_por >= 0:
ori = layout[or_id]
ext = layout[ex_id]
else:
ori = layout[ex_id]
ext = layout[or_id]
# Compute the line characteristics: draxing is done by plotting two lines starting from the center
# with a specific angle and semi-length
length = math.sqrt((ori[0] - ext[0]) ** 2. + (ori[1] - ext[1]) ** 2.) - 2. * self.nodes_outer_radius
center = ((ori[0] + ext[0]) / 2., (ori[1] + ext[1]) / 2.)
angle = math.atan2(ori[1] - ext[1], ori[0] - ext[0])
# First, draw the arrow heads; lines will be drawn on top
distance_arrow_heads = 25
n_arrow_heads = int(max(1, length // distance_arrow_heads))
for a in range(n_arrow_heads):
if n_arrow_heads != 1:
offset = a + .25 if self.boolean_dynamic_arrows else a + .75
x = center[0] + (offset * distance_arrow_heads - length / 2.) * math.cos(angle)
y = center[1] + (offset * distance_arrow_heads - length / 2.) * math.sin(angle)
else:
x = center[0]
y = center[1]
# draw_arrow_head(x, y, angle, color, thickness)
head_angle = math.pi / 6.
width = 8 + 20 * (thickness - .6)
x -= width / 2. * math.cos(angle)
y -= width / 2. * math.sin(angle)
x1 = x + width * math.cos(angle + head_angle)
y1 = y + width * math.sin(angle + head_angle)
x2 = x + width * math.cos(angle - head_angle)
y2 = y + width * math.sin(angle - head_angle)
l.append(lines.Line2D([x, x2], [50 + y, 50 + y2], linewidth=thickness,
color=[c / 255. for c in color], figure=fig, linestyle='-'))
l.append(lines.Line2D([x, x1], [50 + y, 50 + y1], linewidth=thickness,
color=[c / 255. for c in color], figure=fig, linestyle='-'))
fig.lines.extend(l)
# p.set_array(np.array(color*len(patches)))
# Export plot into something readable by pygame
canvas = agg.FigureCanvasAgg(fig)
canvas.draw()
renderer = canvas.get_renderer()
raw_data = renderer.tostring_rgb()
size = canvas.get_width_height()
img_loads_curve_week = pygame.image.fromstring(raw_data, size, "RGB")
loads_curve_surface = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
loads_curve_surface.fill(self.background_color)
loads_curve_surface.blit(img_loads_curve_week, (0, 30) if self.grid_case != 30 else (-100, 0))
return loads_curve_surface
[docs] def create_plot_loads_curve(self, n_timesteps, left_xlabel):
facecolor_asfloat = np.asarray(self.left_menu_tile_color) / 255.
layout_config = {'pad': 0.2}
fig = pylab.figure(figsize=[3, 1.5], dpi=100, facecolor=facecolor_asfloat, tight_layout=layout_config)
ax = fig.gca()
# Retrieve data for the specified time
data = np.sum(self.loads, axis=-1)
data = data[-min(len(data), n_timesteps):]
n_data = len(data)
ax.plot(np.linspace(n_data, 0, num=n_data), data, '#d24dff')
# Ticks and labels
ax.set_xlim([n_timesteps, 1])
ax.set_xticks([1, n_timesteps])
ax.set_xticklabels(['now', left_xlabel])
ax.set_ylim([0, np.max(data) * 1.05])
ax.set_yticks([0, np.max(data)])
ax.set_yticklabels(['', '%.0f MW' % (np.max(data))])
label_color_hexa = '#D2D2D2'
ax.tick_params(axis='y', labelsize=6, pad=-30, labelcolor=label_color_hexa, direction='in')
ax.tick_params(axis='x', labelsize=6, labelcolor=label_color_hexa)
# Top and right axis
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.set_facecolor(np.asarray(self.background_color) / 255.)
fig.tight_layout()
canvas = agg.FigureCanvasAgg(fig)
canvas.draw()
renderer = canvas.get_renderer()
raw_data = renderer.tostring_rgb()
size = canvas.get_width_height()
return pygame.image.fromstring(raw_data, size, "RGB")
[docs] def draw_surface_diagnosis(self, number_loads_cut, number_prods_cut, number_nodes_splitting, number_lines_switches,
distance_initial_grid, line_capacity_usage, n_offlines_lines, number_unavailable_lines,
number_unavailable_nodes, max_number_isolated_loads, max_number_isolated_prods):
my_dpi = 100
height = 245
fig = plt.figure(figsize=(self.left_menu_shape[0] / my_dpi, height / my_dpi), dpi=my_dpi,
facecolor=[c / 255. for c in self.left_menu_tile_color], clear=True, tight_layout={'pad': -.3})
fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0)
plt.axis('off')
plt.ylim(0, height)
plt.xlim(0, self.left_menu_shape[0])
string_color = (180 / 255., 180 / 255., 180 / 255.)
header_color = (220 / 255., 220 / 255., 220 / 255.)
value_color = (1., 1., 1.)
plt.text(90, height - 25, 'Live diagnosis', fontdict={'size': 12}, color=header_color)
string_offset = 65
value_offset = 10
up = '^'
up_offset = -11
if self.data is not None:
old_number_loads_cut, old_number_prods_cut, old_number_nodes_splitting, old_number_lines_switches, \
old_distance_initial_grid, old_usage, old_n_offlines_lines, old_number_unavailable_lines, \
old_number_unavailable_nodes, old_max_number_isolated_loads, old_max_number_isolated_prods = \
self.data['number_loads_cut'], self.data['number_prods_cut'], \
self.data['number_nodes_splitting'], self.data['number_lines_switches'], \
self.data['distance_initial_grid'], self.data['usage'], \
self.data['n_offlines_lines'], self.data['number_unavailable_lines'], \
self.data['number_unavailable_nodes'], self.data['max_number_isolated_loads'], \
self.data['max_number_isolated_prods']
else:
old_number_loads_cut, old_number_prods_cut, old_number_nodes_splitting, old_number_lines_switches, \
old_distance_initial_grid, old_usage, old_n_offlines_lines, \
old_number_unavailable_lines, old_number_unavailable_nodes, old_max_number_isolated_loads, \
old_max_number_isolated_prods = [0] * 11
def print_variation(old_val, new_val, h):
if new_val > old_val:
plt.text(up_offset + 2, height - h - 7, up, fontdict={'size': 12}, color=(1., .5, .5))
elif new_val < old_val:
plt.text(up_offset, height - h + 7, up, fontdict={'size': 12}, color=(.5, 1., .5), rotation=180.)
plt.text(string_offset, height - 60, '# of isolated loads', fontdict={'size': 8.5}, color=string_color)
plt.text(value_offset, height - 61, '%d' % number_loads_cut,
fontdict={'size': 8.5},
color=(1., 0.3, 0.3) if number_loads_cut > max_number_isolated_loads else value_color)
print_variation(old_number_loads_cut, number_loads_cut, 61)
plt.text(value_offset, height - 60, ' / %d' % max_number_isolated_loads,
fontdict={'size': 8.5}, color=value_color)
plt.text(string_offset, height - 80, '# of isolated productions', fontdict={'size': 8.5}, color=string_color)
plt.text(value_offset, height - 81, '%d' % number_prods_cut,
fontdict={'size': 8.5},
color=(1., 0.3, 0.3) if number_prods_cut > max_number_isolated_prods else value_color)
plt.text(value_offset, height - 80, ' / %d' % max_number_isolated_prods,
fontdict={'size': 8.5}, color=value_color)
print_variation(old_number_prods_cut, number_prods_cut, 81)
plt.text(string_offset, height - 110, '# of node switches of last action', fontdict={'size': 8.5},
color=string_color)
plt.text(value_offset, height - 110, '%d' % number_nodes_splitting, fontdict={'size': 8.5}, color=value_color)
print_variation(old_number_nodes_splitting, number_nodes_splitting, 110)
plt.text(string_offset, height - 130, '# of line switches of last action', fontdict={'size': 8.5},
color=string_color)
plt.text(value_offset, height - 130, '%d' % number_lines_switches, fontdict={'size': 8.5}, color=value_color)
print_variation(old_number_lines_switches, number_lines_switches, 130)
plt.text(string_offset, height - 160, 'average line capacity usage', fontdict={'size': 8.5}, color=string_color)
usage = 100. * np.mean(line_capacity_usage)
plt.text(value_offset, height - 160, '%.1f%%' % usage if usage < 5000 else '∞', fontdict={'size': 8.5},
color=value_color)
print_variation(old_usage, usage, 160)
plt.text(string_offset, height - 180, '# of OFF lines', fontdict={'size': 8.5}, color=string_color)
plt.text(value_offset, height - 180, '%d' % n_offlines_lines, fontdict={'size': 8.5}, color=value_color)
print_variation(old_n_offlines_lines, n_offlines_lines, 180)
plt.text(string_offset, height - 200, '# of unavailable lines', fontdict={'size': 8.5}, color=string_color)
plt.text(value_offset, height - 200, '%d' % number_unavailable_lines, fontdict={'size': 8.5}, color=value_color)
print_variation(old_number_unavailable_lines, number_unavailable_lines, 200)
plt.text(string_offset, height - 220, '# of unactionable nodes', fontdict={'size': 8.5}, color=string_color)
plt.text(value_offset, height - 220, '%d' % number_unavailable_nodes, fontdict={'size': 8.5}, color=value_color)
print_variation(number_unavailable_nodes, number_unavailable_nodes, 220)
plt.text(string_offset, height - 250, 'distance to reference grid', fontdict={'size': 8.5}, color=string_color)
plt.text(value_offset, height - 250, '%d' % distance_initial_grid, fontdict={'size': 8.5}, color=value_color)
print_variation(old_distance_initial_grid, distance_initial_grid, 250)
fig.tight_layout()
canvas = agg.FigureCanvasAgg(fig)
canvas.draw()
renderer = canvas.get_renderer()
raw_data = renderer.tostring_rgb()
size = canvas.get_width_height()
img = pygame.image.fromstring(raw_data, size, "RGB")
last_rewards_surface_shape = (self.left_menu_shape[0], height)
last_rewards_surface = pygame.Surface(last_rewards_surface_shape, pygame.SRCALPHA, 32).convert_alpha()
last_rewards_surface.fill(self.left_menu_tile_color)
# last_rewards_surface.blit(img, (0, 30) if self.grid_case != 30 else (-100, 0))
last_rewards_surface.blit(img, (-20, 0))
gfxdraw.hline(last_rewards_surface, 0, last_rewards_surface_shape[0], 0, (64, 64, 64))
gfxdraw.hline(last_rewards_surface, 0, last_rewards_surface_shape[0], last_rewards_surface_shape[1] - 1,
(64, 64, 64))
gfxdraw.vline(last_rewards_surface, 0, last_rewards_surface_shape[1] - 1, 0, (64, 64, 64))
gfxdraw.vline(last_rewards_surface, last_rewards_surface_shape[0], 0, last_rewards_surface_shape[1] - 1,
(64, 64, 64))
# Keep current data for next data differences
self.data = {'number_loads_cut': number_loads_cut, 'number_prods_cut': number_prods_cut,
'number_nodes_splitting': number_nodes_splitting, 'number_lines_switches': number_lines_switches,
'distance_initial_grid': distance_initial_grid, 'usage': usage,
'n_offlines_lines': n_offlines_lines, 'number_unavailable_lines': number_unavailable_lines,
'number_unavailable_nodes': number_unavailable_nodes,
'max_number_isolated_loads': max_number_isolated_loads,
'max_number_isolated_prods': max_number_isolated_prods}
return last_rewards_surface
[docs] def draw_surface_loads_curves(self, n_hours_to_display_top_loadplot, n_hours_to_display_bottom_loadplot):
# Loads curve surface: retrieve images surfaces, stack them into a common surface, plot horizontal lines
# at top and bottom of latter surface
# compute the string number of days
n_days_horizon = n_hours_to_display_top_loadplot // 24
img_loads_curve_week = self.create_plot_loads_curve(
n_timesteps=int(n_hours_to_display_top_loadplot * 3600 // self.timestep_duration_seconds),
left_xlabel=' {} day{} ago '.format(n_days_horizon, 's' if n_days_horizon > 1 else ''))
n_hours_horizon = n_hours_to_display_bottom_loadplot
img_loads_curve_day = self.create_plot_loads_curve(
n_timesteps=int(n_hours_to_display_bottom_loadplot * 3600 // self.timestep_duration_seconds),
left_xlabel='{} hours ago'.format(n_hours_horizon))
loads_curve_surface = pygame.Surface(
(img_loads_curve_week.get_width(), 2 * img_loads_curve_week.get_height() + 30),
pygame.SRCALPHA, 32).convert_alpha()
loads_curve_surface.fill(self.left_menu_tile_color)
loads_curve_surface.blit(self.bold_white_render('Historical total consumption'), (30, 10))
loads_curve_surface.blit(img_loads_curve_week, (0, 30))
loads_curve_surface.blit(img_loads_curve_day, (0, 30 + img_loads_curve_week.get_height()))
gfxdraw.hline(loads_curve_surface, 0, loads_curve_surface.get_width(), 0, (64, 64, 64))
gfxdraw.hline(loads_curve_surface, 0, loads_curve_surface.get_width(), loads_curve_surface.get_height() - 1,
(64, 64, 64))
return loads_curve_surface
[docs] def draw_surface_relative_thermal_limits(self, n_timesteps, left_xlabel='24 hours ago'):
facecolor_asfloat = np.asarray(self.left_menu_tile_color) / 255.
layout_config = {'pad': 0.2}
fig = pylab.figure(figsize=[3, 1.5], dpi=100, facecolor=facecolor_asfloat, tight_layout=layout_config)
ax = fig.gca()
# Retrieve data for the specified time
data = self.relative_thermal_limits
data = data[-min(len(data), n_timesteps):]
n_data = len(data)
medians = np.median(data, axis=-1)
p25 = np.percentile(data, 25, axis=-1)
p75 = np.percentile(data, 75, axis=-1)
p90 = np.percentile(data, 90, axis=-1)
p10 = np.percentile(data, 10, axis=-1)
maxes = np.max(data, axis=-1)
mines = np.min(data, axis=-1)
ax.fill_between(np.linspace(n_data, 0, num=n_data), p10, p90, color='#16AA16') # p10 p90 percentiles
ax.fill_between(np.linspace(n_data, 0, num=n_data), p25, p75, color='#16DC16') # p25 p75 percentiles
ax.plot(np.linspace(n_data, 0, num=n_data), medians, '#AAFFAA') # median
ax.plot(np.linspace(n_data, 0, num=n_data), maxes, '#339966', '.', linewidth=.75) # max
ax.plot(np.linspace(n_data, 0, num=n_data), mines, '#339966', '.', linewidth=.75) # min
# ax.plot(np.linspace(n_data, 0, num=n_data), percentiles_10, '#33cc33')
# ax.plot(np.linspace(n_data, 0, num=n_data), percentiles_90, '#33cc33')
# Ticks and labels
ax.set_xlim([n_timesteps, 1])
ax.set_xticks([1, n_timesteps])
ax.set_xticklabels(['now', left_xlabel])
ax.set_ylim([0, max(1.05, min(2., np.max([medians, p90, p10]) * 1.05))])
ax.set_yticks([0, .2, .4, .6, .8, 1])
ax.set_yticklabels(['', '20% ', '', '60% ', '', '100%'])
label_color_hexa = '#D2D2D2'
ax.tick_params(axis='y', labelsize=6, pad=-23, labelcolor=label_color_hexa, direction='in')
ax.tick_params(axis='x', labelsize=6, labelcolor=label_color_hexa)
# Top and right axis
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.set_facecolor(np.asarray(self.background_color) / 255.)
fig.tight_layout()
canvas = agg.FigureCanvasAgg(fig)
canvas.draw()
renderer = canvas.get_renderer()
raw_data = renderer.tostring_rgb()
size = canvas.get_width_height()
img_rtl = pygame.image.fromstring(raw_data, size, "RGB")
rtl_curves_surface = pygame.Surface((img_rtl.get_width(), 2 * img_rtl.get_height() + 30),
pygame.SRCALPHA, 32).convert_alpha()
rtl_curves_surface.fill(self.left_menu_tile_color)
rtl_curves_surface.blit(self.bold_white_render('Last 24h lines capacity usage'), (30, 10))
rtl_curves_surface.blit(img_rtl, (0, 30))
gfxdraw.hline(rtl_curves_surface, 0, rtl_curves_surface.get_width(), 0, (64, 64, 64))
gfxdraw.hline(rtl_curves_surface, 0, rtl_curves_surface.get_width(), rtl_curves_surface.get_height() - 1,
(64, 64, 64))
return rtl_curves_surface
[docs] def draw_surface_n_overflows(self, n_timesteps, left_xlabel=' 7 days ago '):
facecolor_asfloat = np.asarray(self.left_menu_tile_color) / 255.
layout_config = {'pad': 0.2}
fig = pylab.figure(figsize=[3, 1], dpi=100, facecolor=facecolor_asfloat, tight_layout=layout_config)
ax = fig.gca()
# Retrieve data for the specified time
data = np.sum(np.asarray(self.relative_thermal_limits) >= 1., axis=-1)
data = data[-min(len(data), n_timesteps):]
n_data = len(data)
ax.plot(np.linspace(n_data, 0, num=n_data), data, '#ff3333')
# Ticks and labels
ax.set_xlim([n_timesteps, 1])
ax.set_xticks([1, n_timesteps])
ax.set_xticklabels(['now', left_xlabel])
ax.set_ylim([0, max(1, np.max(data) * 1.05)])
ax.set_yticks([0, max(1, np.max(data))])
ax.set_yticklabels(['', '%d' % max(1, np.max(data))])
label_color_hexa = '#D2D2D2'
ax.tick_params(axis='y', labelsize=6, pad=-12, labelcolor=label_color_hexa, direction='in')
ax.tick_params(axis='x', labelsize=6, labelcolor=label_color_hexa)
# Top and right axis
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.set_facecolor(np.asarray(self.background_color) / 255.)
fig.tight_layout()
canvas = agg.FigureCanvasAgg(fig)
canvas.draw()
renderer = canvas.get_renderer()
raw_data = renderer.tostring_rgb()
size = canvas.get_width_height()
img_rtl = pygame.image.fromstring(raw_data, size, "RGB")
n_overflows_surface = pygame.Surface((img_rtl.get_width(), 2 * img_rtl.get_height() + 30),
pygame.SRCALPHA, 32).convert_alpha()
n_overflows_surface.fill(self.left_menu_tile_color)
n_overflows_surface.blit(self.bold_white_render('Number of overflows'), (30, 10))
n_overflows_surface.blit(img_rtl, (0, 30))
gfxdraw.hline(n_overflows_surface, 0, n_overflows_surface.get_width(), 0, (64, 64, 64))
gfxdraw.hline(n_overflows_surface, 0, n_overflows_surface.get_width(), n_overflows_surface.get_height() - 1,
(64, 64, 64))
return n_overflows_surface
[docs] def draw_surface_legend(self):
surface_shape = (175, 355)
surface = pygame.Surface(surface_shape, pygame.SRCALPHA, 32).convert_alpha()
surface.fill(self.left_menu_tile_color)
my_dpi = 100
fig = plt.figure(figsize=(surface_shape[0] / my_dpi, surface_shape[1] / my_dpi), dpi=my_dpi,
facecolor=[c / 255. for c in self.left_menu_tile_color], clear=True, tight_layout={'pad': -.3})
fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0)
plt.axis('off')
plt.ylim(0, surface_shape[1])
plt.xlim(0, surface_shape[0])
ax = fig.gca()
ax.set_xlim(0, surface_shape[0])
ax.set_ylim(0, surface_shape[1])
# fig.subplots_adjust(0, 0, 1, 1, 0, 0)
ax.set_xticks([])
ax.set_yticks([])
offset_text = 40
string_color = (180 / 255., 180 / 255., 180 / 255.)
header2_color = (200 / 255., 200 / 255., 200 / 255.)
header_color = (220 / 255., 220 / 255., 220 / 255.)
value_color = (1., 1., 1.)
plt.text(45, surface_shape[1] - 20, 'Legend', fontdict={'size': 12}, color=header_color)
plt.text(5, surface_shape[1] - 50, 'Substations', fontdict={'size': 8.5}, color=header2_color)
plt.text(offset_text, surface_shape[1] - 70, 'power output > 0', fontdict={'size': 8.5}, color=string_color)
c = Circle((21, surface_shape[1] - 66), self.nodes_outer_radius, linewidth=1.,
fill=False, color=[c / 255. for c in (0, 153, 255)])
ax.add_artist(c)
plt.text(offset_text, surface_shape[1] - 95, 'power output < 0', fontdict={'size': 8.5}, color=string_color)
c = Rectangle((13, surface_shape[1] - 99), 2. * self.nodes_outer_radius, 2. * self.nodes_outer_radius,
linewidth=1., fill=False, color=[c / 255. for c in (210, 77, 255)])
ax.add_artist(c)
plt.text(offset_text, surface_shape[1] - 120, 'power output = 0', fontdict={'size': 8.5}, color=string_color)
c = Rectangle((22, surface_shape[1] - 128), 2. * self.nodes_outer_radius, 2. * self.nodes_outer_radius,
linewidth=1., fill=False, color=[c / 255. for c in (255, 255, 255)], angle=45.)
ax.add_artist(c)
plt.text(5, surface_shape[1] - 150, 'Power lines', fontdict={'size': 8.5}, color=header2_color)
fig.lines.append(lines.Line2D([10, 33], [surface_shape[1] - 170, surface_shape[1] - 170], linewidth=1.,
color=[c / 255. for c in (51, 204, 51)]))
fig.lines.append(lines.Line2D([20, 23], [surface_shape[1] - 167, surface_shape[1] - 170], linewidth=1.,
color=[c / 255. for c in (51, 204, 51)]))
fig.lines.append(lines.Line2D([20, 23], [surface_shape[1] - 174, surface_shape[1] - 171], linewidth=1.,
color=[c / 255. for c in (51, 204, 51)]))
plt.text(offset_text, surface_shape[1] - 174, 'direction of current', fontdict={'size': 8.5},
color=string_color)
color_low = np.asarray((51, 204, 51))
color_middle = np.asarray((255, 93, 0))
color_high = np.asarray((255, 50, 30))
l = []
n = 50
for i in range(n):
if i < n // 2:
color = [c1 + (c2 - c1) * (i / (n // 2)) for c1, c2 in zip(color_low, color_middle)]
l.append(lines.Line2D([15, 28], [surface_shape[1] - (190 + i), surface_shape[1] - (190 + i)],
linewidth=1., color=[c / 255. for c in color]))
else:
color = [c1 + (c2 - c1) * ((i - n // 2) / (n // 2)) for c1, c2 in zip(color_middle, color_high)]
l.append(lines.Line2D([15, 28], [surface_shape[1] - (190 + i), surface_shape[1] - (190 + i)],
linewidth=1., color=[c / 255. for c in color]))
fig.lines.extend(l)
# Print lines charge indicators
fig.lines.append(lines.Line2D([29, offset_text - 5], [surface_shape[1] - 190, surface_shape[1] - 190],
linewidth=1., color=[c / 255. for c in [234, 234, 160]]))
plt.text(offset_text, surface_shape[1] - 196, '0% capacity usage', fontdict={'size': 8.5},
color=string_color)
fig.lines.append(lines.Line2D([29, offset_text - 5], [surface_shape[1] - 215, surface_shape[1] - 215],
linewidth=1., color=[c / 255. for c in [234, 234, 160]]))
plt.text(offset_text, surface_shape[1] - 219, '50% cap. usage', fontdict={'size': 8.5},
color=string_color)
fig.lines.append(lines.Line2D([29, offset_text - 5], [surface_shape[1] - 239, surface_shape[1] - 239],
linewidth=1., color=[c / 255. for c in [234, 234, 160]]))
plt.text(offset_text, surface_shape[1] - 242, '100% cap. usage', fontdict={'size': 8.5},
color=string_color)
# Overflowed lines
fig.lines.append(lines.Line2D([10, 33], [surface_shape[1] - 259, surface_shape[1] - 259], linewidth=2.,
color=[c / 255. for c in (255, 20, 20)], figure=fig, linestyle='--',
dashes=(2., .8)))
fig.lines.append(lines.Line2D([19, 23], [surface_shape[1] - 255, surface_shape[1] - 259], linewidth=2.,
color=[c / 255. for c in (255, 20, 20)]))
fig.lines.append(lines.Line2D([19, 23], [surface_shape[1] - 263, surface_shape[1] - 259], linewidth=2.,
color=[c / 255. for c in (255, 20, 20)]))
plt.text(offset_text, surface_shape[1] - 263, 'overflowed', fontdict={'size': 8.5}, color=string_color)
# OFF lines
fig.lines.append(lines.Line2D([10, 33], [surface_shape[1] - 279, surface_shape[1] - 279], linewidth=1.,
color=[.8, .8, .8], figure=fig, linestyle='dashed'))
plt.text(offset_text, surface_shape[1] - 283, 'switched OFF', fontdict={'size': 8.5}, color=string_color)
plt.text(5, surface_shape[1] - 315, 'Last action changes', fontdict={'size': 8.5}, color=header2_color)
c = Rectangle((12, surface_shape[1] - 335), 2.5 * self.nodes_outer_radius, self.nodes_outer_radius,
linewidth=1., fill=True, color=[c / 255. for c in (255, 255, 0)])
ax.add_artist(c)
plt.text(offset_text, surface_shape[1] - 335, 'node splitting', fontdict={'size': 8.5}, color=string_color)
# color=[.8, .8, .8], figure=fig, linestyle='dashed'))
# l.append(lines.Line2D([ori[0], ext[0]], [50 + ori[1], 50 + ext[1]], linewidth=.8,
# color=[.8, .8, .8], figure=fig, linestyle='dashed'))
# else:
# l.append(lines.Line2D([ori[0], ext[0]], [50 + ori[1], 50 + ext[1]], linewidth=thickness,
# color=[c / 255. for c in color], figure=fig,
# linestyle='--' if rtl > 1. else '-',
# dashes=(2., .8) if rtl > 1. else (None, None)))
# fig.lines.extend(l)
fig.tight_layout()
canvas = agg.FigureCanvasAgg(fig)
canvas.draw()
renderer = canvas.get_renderer()
raw_data = renderer.tostring_rgb()
size = canvas.get_width_height()
img_legend = pygame.image.fromstring(raw_data, size, "RGB")
surface.blit(img_legend, (10, 5))
gfxdraw.hline(surface, 0, surface_shape[0], 0, (64, 64, 64))
gfxdraw.hline(surface, 0, surface_shape[0], surface_shape[1] - 1,
(64, 64, 64))
gfxdraw.vline(surface, 0, surface_shape[1] - 1, 0, (64, 64, 64))
gfxdraw.vline(surface, surface_shape[0], 0, surface_shape[1] - 1,
(64, 64, 64))
return surface
[docs] @staticmethod
def draw_plot_pause():
pause_font = pygame.font.SysFont("Arial", 25)
yellow = (255, 255, 179)
txt_surf = pause_font.render('pause', False, (80., 80., 80.))
alpha_img = pygame.Surface(txt_surf.get_size(), pygame.SRCALPHA)
alpha_img.fill(yellow + (72,))
# txt_surf.blit(alpha_img, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
pause_surface = pygame.Surface((200, 70), pygame.SRCALPHA, 32).convert_alpha()
pause_surface.fill(yellow + (128,))
pause_surface.blit(txt_surf, (64, 18))
return pause_surface
[docs] @staticmethod
def draw_plot_game_over():
game_over_font = pygame.font.SysFont("Arial", 25)
red = (255, 26, 26)
txt_surf = game_over_font.render('game over', False, (255, 255, 255))
alpha_img = pygame.Surface(txt_surf.get_size(), pygame.SRCALPHA)
alpha_img.fill(red + (128,))
# txt_surf.blit(alpha_img, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
game_over_surface = pygame.Surface((200, 70), pygame.SRCALPHA, 32).convert_alpha()
game_over_surface.fill(red + (128,))
game_over_surface.blit(txt_surf, (38, 18))
return game_over_surface
def _update_left_menu(self, epoch, timestep):
self.left_menu = pygame.Surface(self.left_menu_shape, pygame.SRCALPHA, 32).convert_alpha()
# Top info about epoch and timestep
self.left_menu.blit(self.text_render('Epoch'), (30, 10))
self.left_menu.blit(self.text_render('Timestep'), (150, 10))
self.left_menu.blit(self.value_render(str(epoch)), (100, 10))
self.left_menu.blit(self.value_render(str(timestep)), (250, 10))
# Last reward surface
# last_rewards_surface = self.draw_surface_rewards(rewards)
# Loads curve surface
if self.timestep_duration_seconds > 30 * 60: # 30 minutes
n_hours_to_display_top_loadplot = 7 * 24 # 1 week
else:
n_hours_to_display_top_loadplot = 3 * 24 # 3 days
n_hours_to_display_bottom_loadplot = 1 * 24 # 1 day
loads_curve_surface = self.draw_surface_loads_curves(
n_hours_to_display_top_loadplot=n_hours_to_display_top_loadplot,
n_hours_to_display_bottom_loadplot=n_hours_to_display_bottom_loadplot)
# Relative thermal limits curves
# compute the horizon of x abscissa to display on monitoring curves
n_hours_to_display = 24 # 1 day
rtl_curves_surface = self.draw_surface_relative_thermal_limits(
n_timesteps=int(n_hours_to_display * 3600 // self.timestep_duration_seconds))
# Number of overflowed lines curves
n_hours_to_display = 24 # 1 day
n_days_horizon = n_hours_to_display // 24
horizon_scale = 'day'
if n_days_horizon <= 1:
n_days_horizon = n_hours_to_display
horizon_scale = 'hour'
n_overflows_surface = self.draw_surface_n_overflows(
n_timesteps=int(n_hours_to_display * 3600 // self.timestep_duration_seconds),
left_xlabel='{} {}{} ago'.format(n_days_horizon, horizon_scale, 's' if n_days_horizon > 1 else ''))
gfxdraw.vline(self.left_menu, self.left_menu_shape[0] - 1, 0, self.left_menu_shape[1], (128, 128, 128))
# self.left_menu.blit(last_rewards_surface, (0, 50))
self.left_menu.blit(loads_curve_surface, (0, 50))
self.left_menu.blit(rtl_curves_surface, (0, 380))
self.left_menu.blit(n_overflows_surface, (0, 560))
# noinspection PyArgumentList
def _update_topology(self, scenario_id, date, relative_thermal_limits, lines_por, lines_service_status, prods,
loads, are_substations_changed, game_over, cascading_frame_id, number_loads_cut,
number_prods_cut, number_nodes_splitting, number_lines_switches, distance_initial_grid,
line_capacity_usage, number_off_lines, number_unavailable_lines, number_unavailable_nodes,
max_number_isolated_loads, max_number_isolated_prods, number_nodes_per_substation):
self.topology_layout = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
self.nodes_surface = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
self.injections_surface = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
self.lines_surface = pygame.Surface(self.topology_layout_shape, pygame.SRCALPHA, 32).convert_alpha()
gfxdraw.vline(self.topology_layout, 0, 0, self.left_menu_shape[1], (20, 20, 20))
# Lines
if self.relative_thermal_limits:
if cascading_frame_id is None:
self.relative_thermal_limits.append(relative_thermal_limits)
else:
self.relative_thermal_limits.append(relative_thermal_limits)
lines_surf = self.draw_surface_grid(relative_thermal_limits, lines_por, lines_service_status, prods, loads,
are_substations_changed, number_nodes_per_substation)
offset = -68 if self.grid_case == 118 else -20 if self.grid_case == 30 else 0
self.topology_layout.blit(lines_surf, (0 + offset, 0))
# arrow_surf = self.draw_surface_arrows(relative_thermal_limits, lines_por, lines_service_status)
# self.topology_layout.blit(arrow_surf, (0, 0))
diagnosis_reward = self.draw_surface_diagnosis(number_loads_cut, number_prods_cut, number_nodes_splitting,
number_lines_switches, distance_initial_grid,
line_capacity_usage, number_off_lines, number_unavailable_lines,
number_unavailable_nodes, max_number_isolated_loads,
max_number_isolated_prods)
self.last_rewards_surface = diagnosis_reward
# Legend
legend_surface = self.draw_surface_legend()
# Dirty
if self.loads:
if cascading_frame_id is None:
self.loads.append(loads)
else:
self.loads.append(loads)
# Nodes
self.draw_surface_nodes_headers(scenario_id, date, cascading_result_frame=cascading_frame_id)
# self.topology_layout.blit(self.lines_surface, (0, 0))
self.topology_layout.blit(self.last_rewards_surface, (690, 11))
self.topology_layout.blit(legend_surface, (
815, self.last_rewards_surface.get_height() + (90 if self.grid_case != 118 else 30)))
self.topology_layout.blit(self.nodes_surface, (0, 0))
# Print a game over message if game has been lost
if game_over:
self.topology_layout.blit(self.game_over_surface, (320, 320))
[docs] def render(self, lines_capacity_usage, lines_por, lines_service_status, epoch, timestep, scenario_id, prods, loads,
date, are_substations_changed, number_nodes_per_substation, number_loads_cut, number_prods_cut,
number_nodes_splitting, number_lines_switches, distance_initial_grid, number_off_lines,
number_unavailable_lines, number_unactionable_nodes, max_number_isolated_loads,
max_number_isolated_prods, game_over=False, cascading_frame_id=None):
plt.close('all')
def event_looper(force=False):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
exit()
if event.key == pygame.K_SPACE:
pause_surface = self.draw_plot_pause()
self.screen.blit(pause_surface, (320 + self.left_menu_shape[0], 320))
pygame.display.flip()
return not force
return force
force = event_looper(force=False)
while event_looper(force=force):
pass
# The game is not paused anymore (or never has been), I can render the next surface
self.screen.fill(self.background_color)
# Execute full plotting mechanism: order is important
self._update_topology(scenario_id, date, lines_capacity_usage, lines_por, lines_service_status, prods, loads,
are_substations_changed, game_over, cascading_frame_id, number_loads_cut,
number_prods_cut, number_nodes_splitting, number_lines_switches, distance_initial_grid,
lines_capacity_usage, number_off_lines, number_unavailable_lines,
number_unactionable_nodes, max_number_isolated_loads, max_number_isolated_prods,
number_nodes_per_substation)
if cascading_frame_id is None:
self._update_left_menu(epoch, timestep)
# Blit all macro surfaces on screen
self.screen.blit(self.topology_layout, (self.left_menu_shape[0], 0))
self.screen.blit(self.left_menu, (0, 0))
pygame.display.flip()
# Bugfix for mac
# pygame.event.get()
self.boolean_dynamic_arrows = not self.boolean_dynamic_arrows
[docs]def scale(u, z, t):
for k, v in case_layouts.items():
print(k)
print([(int(a * u + -40), int(b * z + -0)) for a, b in v])
[docs]def recenter():
for k, v in case_layouts.items():
print(k)
arr = np.asarray(np.absolute(v))
minix = np.min(arr[:, 0])
miniy = np.min(arr[:, 1])
maxix = np.max(arr[:, 0])
maxiy = np.max(arr[:, 1])
x = (maxix - minix) / 2.
y = (maxiy - miniy) / 2.
print([(int(a - x), int(-b - y)) for a, b in v])
if __name__ == '__main__':
a = np.asarray(case_layouts[30])
print(np.min(a[:, 0]))
print(np.max(a[:, 0]))
print(np.min(a[:, 1]))
print(np.max(a[:, 1]))
a = np.asarray(case_layouts[14])
print()
print(np.min(a[:, 0]))
print(np.max(a[:, 0]))
print(np.min(a[:, 1]))
print(np.max(a[:, 1]))
scale(1, 1., 0)