diff --git a/requirements.txt b/requirements.txt
index 7043dbe93..eed2f984a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,7 +4,7 @@ networkx>=2.1
pandas>=1.1
xlwt>=1.3.0
xlrd>=1.1.0
-ortools>=9.8.0, <=9.9.3963
+ortools>=9.10.0,
matplotlib>=2.1.1
qtconsole>=4.3.1
openpyxl>=2.4.9
diff --git a/src/GridCal/Gui/Diagrams/MapWidget/Branches/map_line_container.py b/src/GridCal/Gui/Diagrams/MapWidget/Branches/map_line_container.py
index b20bfb09c..610b85cbb 100644
--- a/src/GridCal/Gui/Diagrams/MapWidget/Branches/map_line_container.py
+++ b/src/GridCal/Gui/Diagrams/MapWidget/Branches/map_line_container.py
@@ -122,7 +122,7 @@ def add_segment(self, segment: MapLineSegment):
"""
self.segments_list.append(segment)
- def set_colour(self, color: QColor, w, style: Qt.PenStyle, tool_tip: str = '') -> None:
+ def set_colour(self, color: QColor, style: Qt.PenStyle, tool_tip: str = '') -> None:
"""
Set color and style
:param color: QColor instance
@@ -133,7 +133,7 @@ def set_colour(self, color: QColor, w, style: Qt.PenStyle, tool_tip: str = '') -
"""
for segment in self.segments_list:
segment.setToolTip(tool_tip)
- segment.set_colour(color=color, w=w, style=style)
+ segment.set_colour(color=color, style=style)
def update_connectors(self) -> None:
"""
diff --git a/src/GridCal/Gui/Diagrams/MapWidget/Branches/map_line_segment.py b/src/GridCal/Gui/Diagrams/MapWidget/Branches/map_line_segment.py
index c5ed83920..cd32815ff 100644
--- a/src/GridCal/Gui/Diagrams/MapWidget/Branches/map_line_segment.py
+++ b/src/GridCal/Gui/Diagrams/MapWidget/Branches/map_line_segment.py
@@ -76,7 +76,7 @@ def __init__(self, first: NodeGraphicItem, second: NodeGraphicItem, container: M
self.first.add_position_change_callback(self.set_from_side_coordinates)
self.second.add_position_change_callback(self.set_to_side_coordinates)
- self.set_colour(self.color, self.width, self.style)
+ self.set_colour(self.color, self.style)
self.update_endings()
self.needsUpdate = True
self.setZValue(0)
@@ -115,7 +115,7 @@ def set_width(self, width: float):
self.arrow_p_to.label.setScale(width)
self.arrow_q_to.label.setScale(width)
- def set_colour(self, color: QColor, w: float, style: Qt.PenStyle):
+ def set_colour(self, color: QColor, style: Qt.PenStyle):
"""
Set color and style
:param color: QColor instance
@@ -124,14 +124,13 @@ def set_colour(self, color: QColor, w: float, style: Qt.PenStyle):
:return:
"""
- pen = QPen(color, w, style, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin)
- pen.setWidthF(w)
+ pen = QPen(color, self.width, style, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin)
self.setPen(pen)
- self.arrow_p_from.set_colour(color, w, style)
- self.arrow_q_from.set_colour(color, w, style)
- self.arrow_p_to.set_colour(color, w, style)
- self.arrow_q_to.set_colour(color, w, style)
+ self.arrow_p_from.set_colour(color, self.arrow_p_from.w, style)
+ self.arrow_q_from.set_colour(color, self.arrow_q_from.w, style)
+ self.arrow_p_to.set_colour(color, self.arrow_p_to.w, style)
+ self.arrow_q_to.set_colour(color, self.arrow_q_to.w, style)
def set_from_side_coordinates(self, x: float, y: float):
"""
@@ -286,7 +285,7 @@ def set_enable(self, val=True):
self.color = OTHER['color']
# Set pen for everyone
- self.set_colour(self.color, self.width, self.style)
+ self.set_colour(self.color, self.style)
def enable_disable_label_drawing(self):
"""
diff --git a/src/GridCal/Gui/Diagrams/MapWidget/Substation/substation_graphic_item.py b/src/GridCal/Gui/Diagrams/MapWidget/Substation/substation_graphic_item.py
index 499d2cdbc..6dfb0b454 100644
--- a/src/GridCal/Gui/Diagrams/MapWidget/Substation/substation_graphic_item.py
+++ b/src/GridCal/Gui/Diagrams/MapWidget/Substation/substation_graphic_item.py
@@ -84,9 +84,9 @@ def __init__(self,
self.setAcceptHoverEvents(True)
# Allow selecting the node
- self.setFlag(self.GraphicsItemFlag.ItemIsSelectable | QGraphicsRectItem.ItemIsMovable)
+ self.setFlag(self.GraphicsItemFlag.ItemIsSelectable | self.GraphicsItemFlag.ItemIsMovable)
- self.setCursor(QCursor(Qt.PointingHandCursor))
+ self.setCursor(QCursor(Qt.CursorShape.PointingHandCursor))
# Create a pen with reduced line width
self.change_pen_width(0.5)
@@ -113,6 +113,7 @@ def set_size(self, r: float):
rect = self.rect()
rect.setWidth(r)
rect.setHeight(r)
+ self.radius = r
# change the width and height while keeping the same center
r2 = r / 2
@@ -121,8 +122,8 @@ def set_size(self, r: float):
# Set the new rectangle with the updated dimensions
self.setRect(new_x, new_y, r, r)
- self.radius = r
+ # update the callbacks position for the lines to move accordingly
self.set_callabacks(new_x + r2, new_y + r2)
for vl_graphics in self.voltage_level_graphics:
@@ -130,7 +131,21 @@ def set_size(self, r: float):
self.update_diagram()
- def set_api_object_color(self):
+ def resize_voltage_levels(self):
+ """
+
+ :return:
+ """
+ max_vl = 1.0 # 1 KV
+ for vl_graphics in self.voltage_level_graphics:
+ max_vl = max(max_vl, vl_graphics.api_object.Vnom)
+
+ for vl_graphics in self.voltage_level_graphics:
+ # radius here is the width, therefore we need to send W/2
+ scale = vl_graphics.api_object.Vnom / max_vl * 0.5
+ vl_graphics.set_size(r=self.radius * scale)
+
+ def set_api_object_color(self) -> None:
"""
Gather the API object color and update this objects
"""
@@ -169,12 +184,20 @@ def sort_voltage_levels(self) -> None:
"""
Set the Zorder based on the voltage level voltage
"""
- # TODO: Check this
- sorted_objects = sorted(self.voltage_level_graphics, key=lambda x: x.api_object.Vnom)
+ max_vl = 1.0 # 1 KV
+ for vl_graphics in self.voltage_level_graphics:
+ max_vl = max(max_vl, vl_graphics.api_object.Vnom)
+
+ for vl_graphics in self.voltage_level_graphics:
+ scale = vl_graphics.api_object.Vnom / max_vl * 0.8
+ vl_graphics.set_size(r=self.radius * scale)
+ vl_graphics.center_on_substation()
+
+ sorted_objects = sorted(self.voltage_level_graphics, key=lambda x: -x.api_object.Vnom)
for i, vl_graphics in enumerate(sorted_objects):
vl_graphics.setZValue(i)
- def update_diagram(self):
+ def update_diagram(self) -> None:
"""
Updates the element position in the diagram (to save)
:return:
@@ -246,7 +269,7 @@ def hoverEnterEvent(self, event: QtWidgets.QGraphicsSceneHoverEvent) -> None:
# self.editor.map.view.in_item = True
self.set_color(self.hoover_color, self.color)
self.hovered = True
- QApplication.instance().setOverrideCursor(Qt.PointingHandCursor)
+ QApplication.instance().setOverrideCursor(Qt.CursorShape.PointingHandCursor)
def hoverLeaveEvent(self, event: QtWidgets.QGraphicsSceneHoverEvent) -> None:
"""
@@ -266,34 +289,39 @@ def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent):
add_menu_entry(menu=menu,
text="Add voltage level",
- icon_path="",
+ icon_path=":/Icons/icons/plus.svg",
function_ptr=self.add_voltage_level)
add_menu_entry(menu=menu,
text="Create line from here",
- icon_path="",
+ icon_path=":/Icons/icons/plus.svg",
function_ptr=self.create_new_line)
add_menu_entry(menu=menu,
- text="Move to API coordinates",
- icon_path="",
+ text="Set coordinates to DB",
+ icon_path=":/Icons/icons/down.svg",
function_ptr=self.move_to_api_coordinates)
add_menu_entry(menu=menu,
- text="Remove",
- icon_path="",
+ text="Remove from schematic",
+ icon_path=":/Icons/icons/delete_schematic.svg",
function_ptr=self.remove_function)
- add_menu_entry(menu=menu,
- text="ADD node",
- icon_path=":/Icons/icons/divide.svg",
- function_ptr=self.add_function)
+ # add_menu_entry(menu=menu,
+ # text="ADD node",
+ # icon_path=":/Icons/icons/plus.svg",
+ # function_ptr=self.add_function)
add_menu_entry(menu=menu,
text="Show diagram",
- icon_path="",
+ icon_path=":/Icons/icons/grid_icon.svg",
function_ptr=self.new_substation_diagram)
+ add_menu_entry(menu=menu,
+ text="Plot",
+ icon_path=":/Icons/icons/plot.svg",
+ function_ptr=self.plot)
+
menu.exec_(event.screenPos())
def create_new_line(self):
@@ -343,7 +371,7 @@ def remove_function(self) -> None:
"Remove substation graphics")
if ok:
- self.editor.removeSubstation(self)
+ self.editor.removeSubstation(substation=self)
def move_to_api_coordinates(self):
"""
@@ -364,6 +392,13 @@ def new_substation_diagram(self):
"""
self.editor.new_substation_diagram(substation=self.api_object)
+ def plot(self):
+ """
+ Plot the substations data
+ """
+ i = self.editor.circuit.get_substations().index(self.api_object)
+ self.editor.plot_substation(i, self.api_object)
+
def add_voltage_level(self) -> None:
"""
Add Voltage Level
@@ -392,9 +427,8 @@ def add_voltage_level(self) -> None:
self.editor.circuit.add_voltage_level(vl)
self.editor.circuit.add_bus(obj=bus)
-
- self.editor.add_api_voltage_level(substation_graphics=self,
- api_object=vl)
+ self.editor.add_api_voltage_level(substation_graphics=self, api_object=vl)
+ self.sort_voltage_levels()
def set_color(self, inner_color: QColor = None, border_color: QColor = None) -> None:
"""
@@ -433,14 +467,6 @@ def getPos(self) -> QPointF:
return center_point
- # def resize(self, new_radius: float) -> None:
- # """
- # Resize the node.
- # :param new_radius: New radius for the node.
- # """
- # self.radius = new_radius
- # self.setRect(self.x - new_radius, self.y - new_radius, new_radius * 2, new_radius * 2)
-
def change_pen_width(self, width: float) -> None:
"""
Change the pen width for the node.
diff --git a/src/GridCal/Gui/Diagrams/MapWidget/Substation/voltage_level_graphic_item.py b/src/GridCal/Gui/Diagrams/MapWidget/Substation/voltage_level_graphic_item.py
index f0b46d266..93ba2ed65 100644
--- a/src/GridCal/Gui/Diagrams/MapWidget/Substation/voltage_level_graphic_item.py
+++ b/src/GridCal/Gui/Diagrams/MapWidget/Substation/voltage_level_graphic_item.py
@@ -26,7 +26,6 @@
from GridCalEngine.Devices.Substation.voltage_level import VoltageLevel
from GridCalEngine.Devices.Substation.bus import Bus
-from GridCalEngine.enumerations import DeviceType
if TYPE_CHECKING: # Only imports the below statements during type checking
from GridCal.Gui.Diagrams.MapWidget.grid_map_widget import GridMapWidget
@@ -64,21 +63,25 @@ def __init__(self,
api_object=api_object,
editor=editor,
draw_labels=draw_labels)
- QGraphicsEllipseItem.__init__(self, parent_center.x(), parent_center.y(), r * api_object.Vnom * 0.01,
- r * api_object.Vnom * 0.01, parent)
+ QGraphicsEllipseItem.__init__(self,
+ parent_center.x(),
+ parent_center.y(),
+ r * api_object.Vnom * 0.01,
+ r * api_object.Vnom * 0.01,
+ parent)
parent.register_voltage_level(vl=self)
self.editor: GridMapWidget = editor # to reinforce the type
+
self.api_object: VoltageLevel = api_object # to reinforce the type
self.radius = r * api_object.Vnom * 0.01
- # print(f"VL created at x:{parent_center.x()}, y:{parent_center.y()}")
self.setAcceptHoverEvents(True) # Enable hover events for the item
- # self.setFlag(QtWidgets.QGraphicsItem.GraphicsItemFlag.ItemIsMovable) # Allow moving the node
- self.setFlag(
- self.GraphicsItemFlag.ItemIsSelectable | self.GraphicsItemFlag.ItemIsMovable) # Allow selecting the node
+
+ # Allow moving the node
+ self.setFlag(self.GraphicsItemFlag.ItemIsSelectable | self.GraphicsItemFlag.ItemIsMovable)
# Create a pen with reduced line width
self.change_pen_width(0.5)
@@ -114,6 +117,26 @@ def move_to_xy(self, x: float, y: float):
self.setRect(x, y, self.rect().width(), self.rect().height())
return x, y
+ def set_size(self, r: float):
+ """
+
+ :param r: radius in pixels
+ :return:
+ """
+ # if r != self.radius:
+ rect = self.rect()
+ rect.setWidth(r)
+ rect.setHeight(r)
+ self.radius = r
+
+ # change the width and height while keeping the same center
+ r2 = r / 2
+ new_x = rect.x() - r2
+ new_y = rect.y() - r2
+
+ # Set the new rectangle with the updated dimensions
+ self.setRect(new_x, new_y, r, r)
+
def updateDiagram(self) -> None:
"""
@@ -124,8 +147,6 @@ def updateDiagram(self) -> None:
lat, long = self.editor.to_lat_lon(x=center_point.x() + real_position.x(),
y=center_point.y() + real_position.y())
- print(f'Updating VL position id:{self.api_object.idtag}, lat:{lat}, lon:{long}')
-
self.editor.update_diagram_element(device=self.api_object,
latitude=lat,
longitude=long,
diff --git a/src/GridCal/Gui/Diagrams/MapWidget/grid_map_widget.py b/src/GridCal/Gui/Diagrams/MapWidget/grid_map_widget.py
index b4fc2acdc..7f292bfe5 100644
--- a/src/GridCal/Gui/Diagrams/MapWidget/grid_map_widget.py
+++ b/src/GridCal/Gui/Diagrams/MapWidget/grid_map_widget.py
@@ -20,6 +20,9 @@
import json
import numpy as np
import math
+import pandas as pd
+from matplotlib import pyplot as plt
+
from PySide6.QtWidgets import QGraphicsItem
from collections.abc import Callable
from PySide6.QtSvg import QSvgGenerator
@@ -40,9 +43,11 @@
from GridCalEngine.Devices.Substation.voltage_level import VoltageLevel
from GridCalEngine.Devices.Branches.line_locations import LineLocation
from GridCalEngine.Devices.multi_circuit import MultiCircuit
-from GridCalEngine.enumerations import DeviceType
+from GridCalEngine.enumerations import DeviceType, ResultTypes
from GridCalEngine.Devices.types import ALL_DEV_TYPES
from GridCalEngine.basic_structures import Logger
+from GridCalEngine.Simulations.OPF.opf_ts_results import OptimalPowerFlowTimeSeriesResults
+from GridCalEngine.Simulations.PowerFlow.power_flow_ts_results import PowerFlowTimeSeriesResults
from GridCal.Gui.Diagrams.MapWidget.Branches.map_ac_line import MapAcLine
from GridCal.Gui.Diagrams.MapWidget.Branches.map_dc_line import MapDcLine
@@ -57,7 +62,7 @@
import GridCalEngine.Devices.Diagrams.palettes as palettes
from GridCal.Gui.Diagrams.graphics_manager import ALL_MAP_GRAPHICS
from GridCal.Gui.Diagrams.MapWidget.Tiles.tiles import Tiles
-from GridCal.Gui.Diagrams.base_diagram_widget import BaseDiagramWidget, qimage_to_cv
+from GridCal.Gui.Diagrams.base_diagram_widget import BaseDiagramWidget
from GridCal.Gui.messages import error_msg
if TYPE_CHECKING:
@@ -870,10 +875,9 @@ def wheelEvent(self, event: QWheelEvent):
"""
# SANTIAGO: NO TOCAR ESTO ES EL COMPORTAMIENTO DESEADO
-
self.update_device_sizes()
- def update_device_sizes(self):
+ def get_branch_width(self):
"""
:return:
@@ -882,6 +886,13 @@ def update_device_sizes(self):
min_zoom = self.map.min_level
zoom = self.map.zoom_factor
scale = self.diagram.min_branch_width + (zoom - min_zoom) / (max_zoom - min_zoom)
+ return scale
+
+ def update_device_sizes(self):
+ """
+
+ :return:
+ """
# rescale lines
for dev_tpe in [DeviceType.LineDevice,
@@ -889,14 +900,14 @@ def update_device_sizes(self):
DeviceType.HVDCLineDevice,
DeviceType.FluidPathDevice]:
graphics_dict = self.graphics_manager.get_device_type_dict(device_type=dev_tpe)
- for key, lne in graphics_dict.items():
- lne.set_width_scale(scale)
+ for key, elm_graphics in graphics_dict.items():
+ elm_graphics.set_width_scale(self.get_branch_width())
# rescale substations
data: Dict[str, SubstationGraphicItem] = self.graphics_manager.get_device_type_dict(DeviceType.SubstationDevice)
- for se_key, se in data.items():
- se.set_api_object_color()
- se.set_size(r=self.diagram.min_bus_width)
+ for se_key, elm_graphics in data.items():
+ elm_graphics.set_api_object_color()
+ elm_graphics.set_size(r=self.diagram.min_bus_width)
def change_size_and_pen_width_all(self, new_radius, pen_width):
"""
@@ -1070,9 +1081,10 @@ def colour_results(self,
weight = int(
np.floor(min_branch_width + Sfnorm[i] * (max_branch_width - min_branch_width) * 0.1))
else:
- weight = 0.5
+ weight = self.get_branch_width()
- graphic_object.set_colour(color=color, w=weight, style=style, tool_tip=tooltip)
+ graphic_object.set_colour(color=color, style=style, tool_tip=tooltip)
+ graphic_object.set_width_scale(weight)
if hasattr(graphic_object, 'set_arrows_with_power'):
graphic_object.set_arrows_with_power(
@@ -1130,7 +1142,7 @@ def colour_results(self,
weight = int(
np.floor(min_branch_width + Sfnorm[i] * (max_branch_width - min_branch_width) * 0.1))
else:
- weight = 0.5
+ weight = self.get_branch_width()
tooltip = str(i) + ': ' + graphic_object.api_object.name
tooltip += '\n' + loading_label + ': ' + "{:10.4f}".format(
@@ -1145,7 +1157,8 @@ def colour_results(self,
else:
graphic_object.set_arrows_with_hvdc_power(Pf=hvdc_Pf[i], Pt=-hvdc_Pf[i])
- graphic_object.set_colour(color=color, w=weight, style=style, tool_tip=tooltip)
+ graphic_object.set_colour(color=color, style=style, tool_tip=tooltip)
+ graphic_object.set_width_scale(weight)
def get_image(self, transparent: bool = False) -> QImage:
"""
@@ -1213,6 +1226,7 @@ def copy(self) -> "GridMapWidget":
logger=self.logger)
return GridMapWidget(
+ gui=self.gui,
tile_src=self.map.tile_src,
start_level=self.diagram.start_level,
longitude=self.diagram.longitude,
@@ -1233,6 +1247,80 @@ def consolidate_coordinates(self):
gelm.api_object.latitude = gelm.lat
gelm.api_object.longitude = gelm.lon
+ def plot_substation(self, i: int, api_object: Substation):
+ """
+ Plot branch results
+ :param i: bus index
+ :param api_object: Substation API object
+ :return:
+ """
+
+ fig = plt.figure(figsize=(12, 8))
+ ax_1 = fig.add_subplot(211)
+ ax_1.set_title('Power', fontsize=14)
+ ax_1.set_ylabel('Injections [MW]', fontsize=11)
+
+ ax_2 = fig.add_subplot(212, sharex=ax_1)
+ ax_2.set_title('Time', fontsize=14)
+ ax_2.set_ylabel('Voltage [p.u]', fontsize=11)
+
+ # set time
+ x = self.circuit.get_time_array()
+
+ if x is not None:
+ if len(x) > 0:
+
+ # Get all devices grouped by bus
+ all_data = self.circuit.get_injection_devices_grouped_by_substation()
+
+ # search drivers for voltage data
+ for driver, results in self.gui.session.drivers_results_iter():
+ if results is not None:
+ if isinstance(results, PowerFlowTimeSeriesResults):
+ table = results.mdl(result_type=ResultTypes.BusVoltageModule)
+ table.plot_device(ax=ax_2, device_idx=i)
+ elif isinstance(results, OptimalPowerFlowTimeSeriesResults):
+ table = results.mdl(result_type=ResultTypes.BusVoltageModule)
+ table.plot_device(ax=ax_2, device_idx=i)
+
+ # Injections
+ # filter injections by bus
+ bus_devices = all_data.get(api_object, None)
+ if bus_devices:
+
+ power_data = dict()
+ for tpe_name, devices in bus_devices.items():
+ for device in devices:
+ if device.device_type == DeviceType.LoadDevice:
+ power_data[device.name] = -device.P_prof.toarray()
+ elif device.device_type == DeviceType.GeneratorDevice:
+ power_data[device.name] = device.P_prof.toarray()
+ elif device.device_type == DeviceType.ShuntDevice:
+ power_data[device.name] = -device.G_prof.toarray()
+ elif device.device_type == DeviceType.StaticGeneratorDevice:
+ power_data[device.name] = device.P_prof.toarray()
+ elif device.device_type == DeviceType.ExternalGridDevice:
+ power_data[device.name] = device.P_prof.toarray()
+ elif device.device_type == DeviceType.BatteryDevice:
+ power_data[device.name] = device.P_prof.toarray()
+ else:
+ raise Exception("Missing shunt device for plotting")
+
+ df = pd.DataFrame(data=power_data, index=x)
+
+ try:
+ # yt area plots
+ df.plot.area(ax=ax_1)
+ except ValueError:
+ # use regular plots
+ df.plot(ax=ax_1)
+
+ plt.legend()
+ fig.suptitle(api_object.name, fontsize=20)
+
+ # plot the profiles
+ plt.show()
+
def generate_map_diagram(substations: List[Substation],
voltage_levels: List[VoltageLevel],
diff --git a/src/GridCal/Gui/Diagrams/MapWidget/map_widget.py b/src/GridCal/Gui/Diagrams/MapWidget/map_widget.py
index eb295467f..a6e6a8115 100644
--- a/src/GridCal/Gui/Diagrams/MapWidget/map_widget.py
+++ b/src/GridCal/Gui/Diagrams/MapWidget/map_widget.py
@@ -124,7 +124,7 @@ def __init__(self,
# Create a QGraphicsProxyWidget for the QLabel
self.label_proxy_widget = QGraphicsProxyWidget()
self.label_proxy_widget.setWidget(self.attribution_label)
- self.label_proxy_widget.setFlag(QGraphicsItem.ItemIgnoresTransformations)
+ self.label_proxy_widget.setFlag(QGraphicsItem.GraphicsItemFlag.ItemIgnoresTransformations)
self.update_label_position()
# Add the proxy widget to the scene
@@ -150,6 +150,8 @@ def __init__(self,
self.scale(initial_zoom_factor, initial_zoom_factor)
+ self.setRenderHints(QPainter.RenderHint.Antialiasing | QPainter.RenderHint.SmoothPixmapTransform)
+
def set_notice(self, val: str):
"""
diff --git a/src/GridCal/Gui/Diagrams/SchematicWidget/Substation/bus_graphics.py b/src/GridCal/Gui/Diagrams/SchematicWidget/Substation/bus_graphics.py
index b0c6fe1aa..a5cbd0596 100644
--- a/src/GridCal/Gui/Diagrams/SchematicWidget/Substation/bus_graphics.py
+++ b/src/GridCal/Gui/Diagrams/SchematicWidget/Substation/bus_graphics.py
@@ -452,87 +452,6 @@ def contextMenuEvent(self, event: QtWidgets.QGraphicsSceneContextMenuEvent):
icon_path=":/Icons/icons/plot.svg",
function_ptr=self.plot_profiles)
-
- # arr = menu.addAction('Arrange')
- # arr_icon = QIcon()
- # arr_icon.addPixmap(QPixmap(":/Icons/icons/automatic_layout.svg"))
- # arr.setIcon(arr_icon)
- # arr.triggered.connect(self.arrange_children)
- #
- # ra5 = menu.addAction('Assign active state to profile')
- # ra5_icon = QIcon()
- # ra5_icon.addPixmap(QPixmap(":/Icons/icons/assign_to_profile.svg"))
- # ra5.setIcon(ra5_icon)
- # ra5.triggered.connect(self.assign_status_to_profile)
- #
- # ra3 = menu.addAction('Delete all the connections')
- # del2_icon = QIcon()
- # del2_icon.addPixmap(QPixmap(":/Icons/icons/delete_conn.svg"))
- # ra3.setIcon(del2_icon)
- # ra3.triggered.connect(self.delete_all_connections)
- #
- # da = menu.addAction('Delete')
- # del_icon = QIcon()
- # del_icon.addPixmap(QPixmap(":/Icons/icons/delete_db.svg"))
- # da.setIcon(del_icon)
- # da.triggered.connect(self.remove)
- #
- # re = menu.addAction('Expand schematic')
- # re_icon = QIcon()
- # re_icon.addPixmap(QPixmap(":/Icons/icons/grid_icon.svg"))
- # re.setIcon(re_icon)
- # re.triggered.connect(self.expand_diagram_from_bus)
- #
- # menu.addSection("Add")
- #
- # al = menu.addAction('Load')
- # al_icon = QIcon()
- # al_icon.addPixmap(QPixmap(":/Icons/icons/add_load.svg"))
- # al.setIcon(al_icon)
- # al.triggered.connect(self.add_load)
- #
- # ac_i = menu.addAction('Current injection')
- # ac_i_icon = QIcon()
- # ac_i_icon.addPixmap(QPixmap(":/Icons/icons/add_load.svg"))
- # ac_i.setIcon(ac_i_icon)
- # ac_i.triggered.connect(self.add_current_injection)
- #
- # ash = menu.addAction('Shunt')
- # ash_icon = QIcon()
- # ash_icon.addPixmap(QPixmap(":/Icons/icons/add_shunt.svg"))
- # ash.setIcon(ash_icon)
- # ash.triggered.connect(self.add_shunt)
- #
- # acsh = menu.addAction('Controllable shunt')
- # acsh_icon = QIcon()
- # acsh_icon.addPixmap(QPixmap(":/Icons/icons/add_shunt.svg"))
- # acsh.setIcon(acsh_icon)
- # acsh.triggered.connect(self.add_controllable_shunt)
- #
- # acg = menu.addAction('Generator')
- # acg_icon = QIcon()
- # acg_icon.addPixmap(QPixmap(":/Icons/icons/add_gen.svg"))
- # acg.setIcon(acg_icon)
- # acg.triggered.connect(self.add_generator)
- #
- # asg = menu.addAction('Static generator')
- # asg_icon = QIcon()
- # asg_icon.addPixmap(QPixmap(":/Icons/icons/add_stagen.svg"))
- # asg.setIcon(asg_icon)
- # asg.triggered.connect(self.add_static_generator)
- #
- # ab = menu.addAction('Battery')
- # ab_icon = QIcon()
- # ab_icon.addPixmap(QPixmap(":/Icons/icons/add_batt.svg"))
- # ab.setIcon(ab_icon)
- # ab.triggered.connect(self.add_battery)
- #
- # aeg = menu.addAction('External grid')
- # aeg_icon = QIcon()
- # aeg_icon.addPixmap(QPixmap(":/Icons/icons/add_external_grid.svg"))
- # aeg.setIcon(aeg_icon)
- # aeg.triggered.connect(self.add_external_grid)
-
add_menu_entry(menu,
text='Arrange',
icon_path=":/Icons/icons/automatic_layout.svg",
diff --git a/src/GridCal/Gui/Diagrams/SchematicWidget/schematic_widget.py b/src/GridCal/Gui/Diagrams/SchematicWidget/schematic_widget.py
index 9b191eddf..4308fb356 100644
--- a/src/GridCal/Gui/Diagrams/SchematicWidget/schematic_widget.py
+++ b/src/GridCal/Gui/Diagrams/SchematicWidget/schematic_widget.py
@@ -53,7 +53,8 @@
from GridCalEngine.Devices.Fluid import FluidNode, FluidPath
from GridCalEngine.Devices.Diagrams.schematic_diagram import SchematicDiagram
from GridCalEngine.Devices.Diagrams.graphic_location import GraphicLocation
-from GridCalEngine.Simulations import PowerFlowTimeSeriesResults
+from GridCalEngine.Simulations.OPF.opf_ts_results import OptimalPowerFlowTimeSeriesResults
+from GridCalEngine.Simulations.PowerFlow.power_flow_ts_results import PowerFlowTimeSeriesResults
from GridCalEngine.enumerations import DeviceType, ResultTypes
from GridCalEngine.basic_structures import Vec, CxVec, IntVec, Logger
@@ -3736,7 +3737,10 @@ def plot_bus(self, i: int, api_object: Bus):
if results is not None:
if isinstance(results, PowerFlowTimeSeriesResults):
table = results.mdl(result_type=ResultTypes.BusVoltageModule)
- table.plot(ax=ax_2, selected_col_idx=[i])
+ table.plot_device(ax=ax_2, device_idx=i)
+ elif isinstance(results, OptimalPowerFlowTimeSeriesResults):
+ table = results.mdl(result_type=ResultTypes.BusVoltageModule)
+ table.plot_device(ax=ax_2, device_idx=i)
# Injections
# filter injections by bus
diff --git a/src/GridCal/Gui/Diagrams/base_diagram_widget.py b/src/GridCal/Gui/Diagrams/base_diagram_widget.py
index 1e6f27f5e..acc0079d6 100644
--- a/src/GridCal/Gui/Diagrams/base_diagram_widget.py
+++ b/src/GridCal/Gui/Diagrams/base_diagram_widget.py
@@ -325,47 +325,47 @@ def plot_branch(self, i: int, api_object: Union[Line, DcLine, Transformer2W, VSC
if isinstance(results, PowerFlowTimeSeriesResults):
Sf_table = results.mdl(result_type=ResultTypes.BranchActivePowerFrom)
- Sf_table.plot(ax=ax_1, selected_col_idx=[i])
+ Sf_table.plot_device(ax=ax_1, device_idx=i)
loading_table = results.mdl(result_type=ResultTypes.BranchLoading)
loading_table.convert_to_cdf()
- loading_table.plot(ax=ax_2, selected_col_idx=[i])
+ loading_table.plot_device(ax=ax_2, device_idx=i)
any_plot = True
elif isinstance(results, LinearAnalysisTimeSeriesResults):
Sf_table = results.mdl(result_type=ResultTypes.BranchActivePowerFrom)
- Sf_table.plot(ax=ax_1, selected_col_idx=[i])
+ Sf_table.plot_device(ax=ax_1, device_idx=i)
loading_table = results.mdl(result_type=ResultTypes.BranchLoading)
loading_table.convert_to_cdf()
- loading_table.plot(ax=ax_2, selected_col_idx=[i])
+ loading_table.plot_device(ax=ax_2, device_idx=i)
any_plot = True
elif isinstance(results, ContingencyAnalysisTimeSeriesResults):
Sf_table = results.mdl(result_type=ResultTypes.MaxContingencyFlows)
- Sf_table.plot(ax=ax_1, selected_col_idx=[i])
+ Sf_table.plot_device(ax=ax_1, device_idx=i)
loading_table = results.mdl(result_type=ResultTypes.MaxContingencyLoading)
loading_table.convert_to_cdf()
- loading_table.plot(ax=ax_2, selected_col_idx=[i])
+ loading_table.plot_device(ax=ax_2, device_idx=i)
any_plot = True
elif isinstance(results, OptimalPowerFlowTimeSeriesResults):
Sf_table = results.mdl(result_type=ResultTypes.BranchActivePowerFrom)
- Sf_table.plot(ax=ax_1, selected_col_idx=[i])
+ Sf_table.plot_device(ax=ax_1, device_idx=i)
loading_table = results.mdl(result_type=ResultTypes.BranchLoading)
loading_table.convert_to_cdf()
- loading_table.plot(ax=ax_2, selected_col_idx=[i])
+ loading_table.plot_device(ax=ax_2, device_idx=i)
any_plot = True
elif isinstance(results, StochasticPowerFlowResults):
loading_table = results.mdl(result_type=ResultTypes.BranchLoadingAverage)
loading_table.convert_to_cdf()
- loading_table.plot(ax=ax_2, selected_col_idx=[i])
+ loading_table.plot_device(ax=ax_2, device_idx=i)
any_plot = True
if any_plot:
diff --git a/src/GridCal/Gui/Main/MainWindow.py b/src/GridCal/Gui/Main/MainWindow.py
index f45db8489..09e2e1fbf 100644
--- a/src/GridCal/Gui/Main/MainWindow.py
+++ b/src/GridCal/Gui/Main/MainWindow.py
@@ -965,8 +965,9 @@ def setupUi(self, mainWindow):
self.max_node_size_spinBox = QDoubleSpinBox(self.frame_58)
self.max_node_size_spinBox.setObjectName(u"max_node_size_spinBox")
self.max_node_size_spinBox.setFont(font2)
- self.max_node_size_spinBox.setDecimals(1)
- self.max_node_size_spinBox.setMinimum(0.100000000000000)
+ self.max_node_size_spinBox.setDecimals(3)
+ self.max_node_size_spinBox.setMinimum(0.010000000000000)
+ self.max_node_size_spinBox.setMaximum(9999.000000000000000)
self.max_node_size_spinBox.setSingleStep(0.100000000000000)
self.max_node_size_spinBox.setValue(40.000000000000000)
@@ -975,7 +976,9 @@ def setupUi(self, mainWindow):
self.max_branch_size_spinBox = QDoubleSpinBox(self.frame_58)
self.max_branch_size_spinBox.setObjectName(u"max_branch_size_spinBox")
self.max_branch_size_spinBox.setFont(font2)
- self.max_branch_size_spinBox.setDecimals(1)
+ self.max_branch_size_spinBox.setDecimals(3)
+ self.max_branch_size_spinBox.setMinimum(0.010000000000000)
+ self.max_branch_size_spinBox.setMaximum(9999.000000000000000)
self.max_branch_size_spinBox.setSingleStep(0.100000000000000)
self.max_branch_size_spinBox.setValue(20.000000000000000)
@@ -990,17 +993,20 @@ def setupUi(self, mainWindow):
self.min_node_size_spinBox = QDoubleSpinBox(self.frame_58)
self.min_node_size_spinBox.setObjectName(u"min_node_size_spinBox")
self.min_node_size_spinBox.setFont(font2)
- self.min_node_size_spinBox.setDecimals(1)
- self.min_node_size_spinBox.setMinimum(0.100000000000000)
+ self.min_node_size_spinBox.setDecimals(3)
+ self.min_node_size_spinBox.setMinimum(0.010000000000000)
+ self.min_node_size_spinBox.setMaximum(9999.000000000000000)
self.min_node_size_spinBox.setSingleStep(0.100000000000000)
- self.min_node_size_spinBox.setValue(20.000000000000000)
+ self.min_node_size_spinBox.setValue(5.000000000000000)
self.gridLayout.addWidget(self.min_node_size_spinBox, 10, 1, 1, 1)
self.min_branch_size_spinBox = QDoubleSpinBox(self.frame_58)
self.min_branch_size_spinBox.setObjectName(u"min_branch_size_spinBox")
self.min_branch_size_spinBox.setFont(font2)
- self.min_branch_size_spinBox.setDecimals(1)
+ self.min_branch_size_spinBox.setDecimals(3)
+ self.min_branch_size_spinBox.setMinimum(0.010000000000000)
+ self.min_branch_size_spinBox.setMaximum(9999.000000000000000)
self.min_branch_size_spinBox.setSingleStep(0.100000000000000)
self.min_branch_size_spinBox.setValue(0.500000000000000)
diff --git a/src/GridCal/Gui/Main/MainWindow.ui b/src/GridCal/Gui/Main/MainWindow.ui
index a3eb8b3a7..00c895b20 100644
--- a/src/GridCal/Gui/Main/MainWindow.ui
+++ b/src/GridCal/Gui/Main/MainWindow.ui
@@ -1076,10 +1076,13 @@ QProgressBar::chunk{
px
- 1
+ 3
- 0.100000000000000
+ 0.010000000000000
+
+
+ 9999.000000000000000
0.100000000000000
@@ -1100,7 +1103,13 @@ QProgressBar::chunk{
px
- 1
+ 3
+
+
+ 0.010000000000000
+
+
+ 9999.000000000000000
0.100000000000000
@@ -1133,16 +1142,19 @@ QProgressBar::chunk{
px
- 1
+ 3
- 0.100000000000000
+ 0.010000000000000
+
+
+ 9999.000000000000000
0.100000000000000
- 20.000000000000000
+ 5.000000000000000
@@ -1157,7 +1169,13 @@ QProgressBar::chunk{
px
- 1
+ 3
+
+
+ 0.010000000000000
+
+
+ 9999.000000000000000
0.100000000000000
diff --git a/src/GridCal/Gui/Main/SubClasses/Model/time_events.py b/src/GridCal/Gui/Main/SubClasses/Model/time_events.py
index 7b2c391bd..6090e85a2 100644
--- a/src/GridCal/Gui/Main/SubClasses/Model/time_events.py
+++ b/src/GridCal/Gui/Main/SubClasses/Model/time_events.py
@@ -21,9 +21,10 @@
from matplotlib import pyplot as plt
import GridCal.Gui.gui_functions as gf
+from GridCalEngine.basic_structures import Logger
from GridCalEngine.enumerations import DeviceType
from GridCalEngine.Devices.types import ALL_DEV_TYPES
-from GridCal.Gui.general_dialogues import NewProfilesStructureDialogue, TimeReIndexDialogue
+from GridCal.Gui.general_dialogues import NewProfilesStructureDialogue, TimeReIndexDialogue, LogsDialogue
from GridCal.Gui.messages import yes_no_question, warning_msg, info_msg
from GridCal.Gui.Main.SubClasses.Model.objects import ObjectsTableMain
from GridCal.Gui.ProfilesInput.models_dialogue import ModelsInputGUI
@@ -328,7 +329,7 @@ def set_profile_as_linear_combination(self):
Edit profiles with a linear combination
Returns: Nothing
"""
-
+ logger: Logger = Logger()
# value = self.ui.profile_factor_doubleSpinBox.value()
dev_type_text = self.get_db_object_selected_type()
@@ -356,13 +357,14 @@ def set_profile_as_linear_combination(self):
# Assign profiles
if len(objects) > 0:
- attr_from = objects[0].properties_with_profile[magnitude_from]
- attr_to = objects[0].properties_with_profile[magnitude_to]
for i, elm in enumerate(objects):
- profile_from = elm.get_profile(magnitude=attr_from)
- profile_to = elm.get_profile(magnitude=attr_to)
- profile_to.set(profile_from.toarray())
+ profile_from = elm.get_profile(magnitude=magnitude_from)
+ profile_to = elm.get_profile(magnitude=magnitude_to)
+ if profile_from is not None and profile_to is not None:
+ profile_to.set(profile_from.toarray())
+ else:
+ print(f"P or Q profile None in {elm.name}")
self.display_profiles()
@@ -374,6 +376,10 @@ def set_profile_as_linear_combination(self):
# no buses or no actual change
pass
+ if logger.has_logs():
+ dlg = LogsDialogue("Set profile", logger=logger)
+ dlg.exec_()
+
def re_index_time(self):
"""
Re-index time
diff --git a/src/GridCal/__version__.py b/src/GridCal/__version__.py
index 004781418..3990df210 100644
--- a/src/GridCal/__version__.py
+++ b/src/GridCal/__version__.py
@@ -16,7 +16,7 @@
_current_year_ = datetime.datetime.now().year
# do not forget to keep a three-number version!!!
-__GridCal_VERSION__ = "5.1.43"
+__GridCal_VERSION__ = "5.1.45"
url = 'https://github.com/SanPen/GridCal'
diff --git a/src/GridCalEngine/Devices/assets.py b/src/GridCalEngine/Devices/assets.py
index a57d39626..9f79599fb 100644
--- a/src/GridCalEngine/Devices/assets.py
+++ b/src/GridCalEngine/Devices/assets.py
@@ -504,11 +504,6 @@ def ensure_profiles_exist(self) -> None:
if self.time_profile is None:
raise Exception('Cannot ensure profiles existence without a time index. Try format_profiles instead')
- # for key, tpe in self.device_type_name_dict.items():
- # elements = self.get_elements_by_type(device_type=tpe)
- # for elm in elements:
- # elm.ensure_profiles_exist(self.time_profile)
-
for elm in self.items():
elm.ensure_profiles_exist(self.time_profile)
@@ -4321,7 +4316,6 @@ def injection_items(self) -> Generator[INJECTION_DEVICE_TYPES, None, None]:
for elm in lst:
yield elm
-
# ------------------------------------------------------------------------------------------------------------------
# Load-like devices
# ------------------------------------------------------------------------------------------------------------------
@@ -4508,28 +4502,28 @@ def get_elements_by_type(self, device_type: DeviceType) -> Union[pd.DatetimeInde
"""
if device_type == DeviceType.LoadDevice:
- return self.get_loads()
+ return self._loads
elif device_type == DeviceType.StaticGeneratorDevice:
- return self.get_static_generators()
+ return self._static_generators
elif device_type == DeviceType.GeneratorDevice:
- return self.get_generators()
+ return self._generators
elif device_type == DeviceType.BatteryDevice:
- return self.get_batteries()
+ return self._batteries
elif device_type == DeviceType.ShuntDevice:
- return self.get_shunts()
+ return self._shunts
elif device_type == DeviceType.ExternalGridDevice:
- return self.get_external_grids()
+ return self._external_grids
elif device_type == DeviceType.CurrentInjectionDevice:
- return self.get_current_injections()
+ return self._current_injections
elif device_type == DeviceType.ControllableShuntDevice:
- return self.get_controllable_shunts()
+ return self._controllable_shunts
elif device_type == DeviceType.LineDevice:
return self._lines
@@ -4641,15 +4635,6 @@ def get_elements_by_type(self, device_type: DeviceType) -> Union[pd.DatetimeInde
elif device_type == DeviceType.EmissionGasDevice:
return self._emission_gases
- # elif device_type == DeviceType.GeneratorTechnologyAssociation:
- # return self._generators_technologies
- #
- # elif device_type == DeviceType.GeneratorFuelAssociation:
- # return self._generators_fuels
- #
- # elif device_type == DeviceType.GeneratorEmissionAssociation:
- # return self._generators_emissions
-
elif device_type == DeviceType.ConnectivityNodeDevice:
return self._connectivity_nodes
@@ -5296,7 +5281,7 @@ def add_or_replace_object(self, api_obj: ALL_DEV_TYPES, logger: Logger) -> bool:
return found
- def get_all_elements_dict(self, logger = Logger()) -> Tuple[Dict[str, ALL_DEV_TYPES], bool]:
+ def get_all_elements_dict(self, logger=Logger()) -> Tuple[Dict[str, ALL_DEV_TYPES], bool]:
"""
Get a dictionary of all elements
:param: logger: Logger
diff --git a/src/GridCalEngine/Devices/multi_circuit.py b/src/GridCalEngine/Devices/multi_circuit.py
index 13023f964..25e1d9204 100644
--- a/src/GridCalEngine/Devices/multi_circuit.py
+++ b/src/GridCalEngine/Devices/multi_circuit.py
@@ -1309,6 +1309,32 @@ def change_base(self, Sbase_new: float):
# assign the new base
self.Sbase = Sbase_new
+ def get_injection_devices_grouped_by_substation(self) -> Dict[dev.Substation, Dict[DeviceType, List[INJECTION_DEVICE_TYPES]]]:
+ """
+ Get the injection devices grouped by bus and by device type
+ :return: Dict[bus, Dict[DeviceType, List[Injection devs]]
+ """
+ groups: Dict[dev.Substation, Dict[DeviceType, List[INJECTION_DEVICE_TYPES]]] = dict()
+
+ for lst in self.get_injection_devices_lists():
+
+ for elm in lst:
+
+ if elm.bus.substation is not None:
+
+ devices_by_type = groups.get(elm.bus.substation, None)
+
+ if devices_by_type is None:
+ groups[elm.bus.substation] = {elm.device_type: [elm]}
+ else:
+ lst = devices_by_type.get(elm.device_type, None)
+ if lst is None:
+ devices_by_type[elm.device_type] = [elm]
+ else:
+ devices_by_type[elm.device_type].append(elm)
+
+ return groups
+
def get_injection_devices_grouped_by_bus(self) -> Dict[dev.Bus, Dict[DeviceType, List[INJECTION_DEVICE_TYPES]]]:
"""
Get the injection devices grouped by bus and by device type
diff --git a/src/GridCalEngine/Devices/profile.py b/src/GridCalEngine/Devices/profile.py
index d9fd30a3c..fa003b577 100644
--- a/src/GridCalEngine/Devices/profile.py
+++ b/src/GridCalEngine/Devices/profile.py
@@ -19,6 +19,8 @@
from collections import Counter
import numpy as np
import numba as nb
+from numpy import dtype
+
from GridCalEngine.basic_structures import Numeric, NumericVec, IntVec
from GridCalEngine.enumerations import DeviceType
from GridCalEngine.Utils.Sparse.sparse_array import SparseArray, PROFILE_TYPES, check_type
@@ -336,7 +338,16 @@ def __getitem__(self, key: int):
if self._is_sparse:
return self._sparse_array[key]
else:
- return self._dense_array[key]
+
+ if self._dense_array is None:
+ # WTF, initialize sparse
+ self._is_sparse = True
+ self._sparse_array = SparseArray(data_type=self.dtype)
+ self._sparse_array.default_value = self.default_value
+ print("Initializing sparse when querying, this signals a mis initilaization")
+ return self.default_value
+ else:
+ return self._dense_array[key]
def __setitem__(self, key: int, value):
"""
@@ -392,7 +403,12 @@ def resize(self, n: int):
if self._is_sparse:
self._sparse_array.resize(n=n)
else:
- self._dense_array.resize(n)
+ try:
+ self._dense_array.resize(n)
+ except ValueError:
+ new_arr = np.zeros(n, dtype=self._dense_array.dtype)
+ new_arr[:len(self._dense_array)] = self._dense_array
+ self._dense_array = new_arr # this is to avoid ValueError when resizing a numpy array of Objects
else:
self._initialized = True
self.create_sparse(size=n, default_value=self.default_value)
diff --git a/src/GridCalEngine/IO/gridcal/pack_unpack.py b/src/GridCalEngine/IO/gridcal/pack_unpack.py
index 72b310f82..311ed1856 100644
--- a/src/GridCalEngine/IO/gridcal/pack_unpack.py
+++ b/src/GridCalEngine/IO/gridcal/pack_unpack.py
@@ -267,8 +267,7 @@ def profile_todict(profile: Profile) -> Dict[str, str]:
return {
'is_sparse': True,
'size': s,
- 'default': profile.default_value
- if profile.sparse_array is None else profile.sparse_array.default_value,
+ 'default': profile.default_value if profile.sparse_array is None else profile.sparse_array.default_value,
'sparse_data': {
'map': dict()
}
@@ -288,8 +287,9 @@ def profile_todict_idtag(profile: Profile) -> Dict[str, str]:
'size': profile.size(),
'default': default,
'sparse_data': {
- 'map': {key: val.idtag for key, val in profile.sparse_array.get_map().items()}
- if profile.sparse_array else dict()
+ 'map': {key: val.idtag if hasattr(val, 'idtag') else None
+ for key, val in profile.sparse_array.get_map().items()}
+ if profile.sparse_array is not None else dict()
}
}
else:
@@ -297,7 +297,8 @@ def profile_todict_idtag(profile: Profile) -> Dict[str, str]:
'is_sparse': profile.is_sparse,
'size': profile.size(),
'default': default,
- 'dense_data': [e.idtag for e in profile.dense_array] if profile.dense_array else list(),
+ 'dense_data': [e.idtag if hasattr(e, 'idtag') else None for e in profile.dense_array]
+ if profile.dense_array is not None else list(),
}
@@ -434,6 +435,10 @@ def gather_model_as_jsons(circuit: MultiCircuit) -> Dict[str, Dict[str, str]]:
:param circuit:
:return:
"""
+
+ if circuit.has_time_series:
+ circuit.ensure_profiles_exist()
+
data: Dict[str, Union[Dict[str, str], List[Dict[str, str]]]] = dict()
# declare objects to iterate name: [sample object, list of objects, headers]
@@ -1469,4 +1474,7 @@ def parse_gridcal_data(data: Dict[str, Union[str, float, pd.DataFrame, Dict[str,
if text_func is not None:
text_func("Done!")
+ if circuit.has_time_series:
+ circuit.ensure_profiles_exist()
+
return circuit
diff --git a/src/GridCalEngine/Simulations/results_table.py b/src/GridCalEngine/Simulations/results_table.py
index 768c9afdf..5cb65540f 100644
--- a/src/GridCalEngine/Simulations/results_table.py
+++ b/src/GridCalEngine/Simulations/results_table.py
@@ -374,3 +374,36 @@ def plot(self, ax=None, selected_col_idx=None, selected_rows=None, stacked=False
df.plot(ax=ax, legend=plot_legend, stacked=stacked)
except TypeError:
print('No numeric data to plot...')
+
+ def plot_device(self, ax=None, device_idx: int = 0, stacked=False):
+ """
+ Plot the data model
+ :param ax: Matplotlib axis
+ :param device_idx: list of selected column indices
+ :param stacked: Stack plot?
+ """
+ index, columns, data = self.get_data()
+
+ columns = [columns[device_idx]]
+ data = data[:, device_idx]
+
+ if ax is None:
+ fig = plt.figure(figsize=(12, 6))
+ ax = fig.add_subplot(111)
+
+ if 'voltage' in self.title.lower():
+ data[data == 0] = 'nan' # to avoid plotting the zeros
+
+ if len(columns) > 15:
+ plot_legend = False
+ else:
+ plot_legend = True
+
+ df = pd.DataFrame(data=data, index=index, columns=columns)
+ ax.set_title(self.title, fontsize=14)
+ ax.set_ylabel(self.y_label, fontsize=11)
+ ax.set_xlabel(self.x_label, fontsize=11)
+ try:
+ df.plot(ax=ax, legend=plot_legend, stacked=stacked)
+ except TypeError:
+ print('No numeric data to plot...')
diff --git a/src/GridCalEngine/__version__.py b/src/GridCalEngine/__version__.py
index dcc6a2e9f..7d2d538bf 100644
--- a/src/GridCalEngine/__version__.py
+++ b/src/GridCalEngine/__version__.py
@@ -16,7 +16,7 @@
_current_year_ = datetime.datetime.now().year
# do not forget to keep a three-number version!!!
-__GridCalEngine_VERSION__ = "5.1.43"
+__GridCalEngine_VERSION__ = "5.1.45"
url = 'https://github.com/SanPen/GridCal'
diff --git a/src/GridCalEngine/setup.py b/src/GridCalEngine/setup.py
index bd02d22e1..e03aba8c8 100644
--- a/src/GridCalEngine/setup.py
+++ b/src/GridCalEngine/setup.py
@@ -56,7 +56,7 @@
"scipy>=1.0.0",
"networkx>=2.1",
"pandas>=1.0",
- "ortools>=9.8.0,<=9.9.3963",
+ "ortools>=9.10.0",
"xlwt>=1.3.0",
"xlrd>=1.1.0",
"matplotlib>=2.1.1",
diff --git a/src/GridCalServer/__version__.py b/src/GridCalServer/__version__.py
index 7e67a6747..3afed76e2 100644
--- a/src/GridCalServer/__version__.py
+++ b/src/GridCalServer/__version__.py
@@ -16,7 +16,7 @@
_current_year_ = datetime.datetime.now().year
# do not forget to keep a three-number version!!!
-__GridCalServer_VERSION__ = "5.1.43"
+__GridCalServer_VERSION__ = "5.1.45"
url = 'https://github.com/SanPen/GridCal'