diff --git a/src/GridCal/Gui/Main/GridCalMain.py b/src/GridCal/Gui/Main/GridCalMain.py index a88a1182a..a135899d6 100644 --- a/src/GridCal/Gui/Main/GridCalMain.py +++ b/src/GridCal/Gui/Main/GridCalMain.py @@ -40,7 +40,7 @@ class MainGUI(ScriptingMain): MainGUI """ - def __init__(self): + def __init__(self) -> None: """ Main constructor """ @@ -76,8 +76,6 @@ def __init__(self): # this is the contingency planner tab, invisible until done self.ui.tabWidget_3.setTabVisible(4, True) - # self.view_cascade_menu() - self.clear_results() self.load_gui_config() diff --git a/src/GridCal/Gui/Main/SubClasses/Model/diagrams.py b/src/GridCal/Gui/Main/SubClasses/Model/diagrams.py index 00784e155..58484f737 100644 --- a/src/GridCal/Gui/Main/SubClasses/Model/diagrams.py +++ b/src/GridCal/Gui/Main/SubClasses/Model/diagrams.py @@ -19,8 +19,7 @@ import networkx as nx import numpy as np -import qdarktheme -from PySide6 import QtGui, QtWidgets, QtCore +from PySide6 import QtGui, QtWidgets from matplotlib import pyplot as plt from pandas.plotting import register_matplotlib_converters @@ -30,7 +29,7 @@ import GridCal.Gui.GuiFunctions as gf import GridCal.Gui.Visualization.palettes as palettes from GridCalEngine.IO.file_system import get_create_gridcal_folder -from GridCal.Gui.GeneralDialogues import LogsDialogue, CheckListDialogue, StartEndSelectionDialogue +from GridCal.Gui.GeneralDialogues import CheckListDialogue, StartEndSelectionDialogue from GridCal.Gui.BusViewer.bus_viewer_dialogue import BusViewerWidget from GridCal.Gui.GridEditorWidget import BusBranchEditorWidget, generate_bus_branch_diagram from GridCal.Gui.MapWidget.grid_map_widget import GridMapWidget @@ -155,9 +154,6 @@ def __init__(self, parent=None): self.ui.explosion_factor_doubleSpinBox.valueChanged.connect(self.explosion_factor_change) self.ui.defaultBusVoltageSpinBox.valueChanged.connect(self.default_voltage_change) - # check boxes - self.ui.dark_mode_checkBox.clicked.connect(self.change_theme_mode) - def auto_layout(self): """ Automatic layout of the nodes @@ -1049,38 +1045,6 @@ def set_diagram_widget(self, diagram: Union[BusBranchEditorWidget, GridMapWidget index = self.ui.diagramsListView.model().index(row, 0) self.ui.diagramsListView.setCurrentIndex(index) - def change_theme_mode(self) -> None: - """ - Change the GUI theme - :return: - """ - custom_colors = {"primary": "#00aa88ff", - "primary>list.selectionBackground": "#00aa88be"} - - if self.ui.dark_mode_checkBox.isChecked(): - qdarktheme.setup_theme(theme='dark', custom_colors=custom_colors) - - diagram = self.get_selected_diagram_widget() - if diagram is not None: - if isinstance(diagram, BusBranchEditorWidget): - diagram.set_dark_mode() - - self.colour_diagrams() - - if self.console is not None: - self.console.set_dark_theme() - else: - qdarktheme.setup_theme(theme='light', custom_colors=custom_colors) - - diagram = self.get_selected_diagram_widget() - if diagram is not None: - if isinstance(diagram, BusBranchEditorWidget): - diagram.set_light_mode() - - self.colour_diagrams() - - if self.console is not None: - self.console.set_light_theme() def plot_style_change(self): """ @@ -1317,28 +1281,11 @@ def update_available_steps_to_color(self): self.ui.simulation_results_step_slider.setSliderPosition(0) self.ui.schematic_step_label.setText("No steps") - def get_selected_contingency_devices(self) -> List[dev.EditableDevice]: - """ - Get the selected buses - :return: - """ - lst: List[dev.EditableDevice] = list() - for k, elm in enumerate(self.circuit.get_contingency_devices()): - if elm.graphic_obj is not None: - if elm.graphic_obj.isSelected(): - lst.append(elm) - return lst - - def get_selected_investment_devices(self) -> List[dev.EditableDevice]: + def get_selected_devices(self) -> List[dev.EditableDevice]: """ Get the selected investment devices - :return: + :return: list of selected devices """ - # lst: List[dev.EditableDevice] = list() - # for k, elm in enumerate(self.circuit.get_investment_devices()): - # if elm.graphic_obj is not None: - # if elm.graphic_obj.isSelected(): - # lst.append(elm) diagram = self.get_selected_diagram_widget() @@ -1358,7 +1305,7 @@ def add_selected_to_contingency(self): if len(self.circuit.buses) > 0: # get the selected buses - selected = self.get_selected_contingency_devices() + selected = self.get_selected_devices() if len(selected) > 0: names = [elm.type_name + ": " + elm.name for elm in selected] @@ -1392,7 +1339,7 @@ def add_selected_to_investment(self) -> None: if len(self.circuit.buses) > 0: # get the selected investment devices - selected = self.get_selected_investment_devices() + selected = self.get_selected_devices() if len(selected) > 0: diff --git a/src/GridCal/Gui/Main/SubClasses/Scripting/scripting.py b/src/GridCal/Gui/Main/SubClasses/Scripting/scripting.py index 30bd3ee50..06ba755c9 100644 --- a/src/GridCal/Gui/Main/SubClasses/Scripting/scripting.py +++ b/src/GridCal/Gui/Main/SubClasses/Scripting/scripting.py @@ -15,26 +15,14 @@ # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import os -import numpy as np -import pandas as pd -from typing import Union -from matplotlib import pyplot as plt import datetime as dtelib from PySide6.QtGui import QFont, QFontMetrics from GridCal.Gui.Main.SubClasses.io import IoMain from GridCal.Gui.Main.SubClasses.Scripting.python_highlighter import PythonHighlighter -from GridCal.Gui.GeneralDialogues import clear_qt_layout + from GridCal.Gui.GuiFunctions import CustomFileSystemModel from GridCal.Gui.messages import error_msg, yes_no_question -try: - from GridCal.Gui.ConsoleWidget import ConsoleWidget - - qt_console_available = True -except ModuleNotFoundError: - print('No qtconsole available') - qt_console_available = False - class ScriptingMain(IoMain): """ @@ -50,12 +38,7 @@ def __init__(self, parent=None): # create main window IoMain.__init__(self, parent) - # Console - self.console: Union[ConsoleWidget, None] = None - try: - self.create_console() - except TypeError: - error_msg('The console has failed because the QtConsole guys have a bug in their package :(') + # Source code text --------------------------------------------------------------------------------------------- # Set the font for your widget @@ -86,121 +69,6 @@ def __init__(self, parent=None): # double clicked ----------------------------------------------------------------------------------------------- self.ui.sourceCodeTreeView.doubleClicked.connect(self.source_code_tree_clicked) - def create_console(self) -> None: - """ - Create console - """ - if qt_console_available: - if self.console is not None: - clear_qt_layout(self.ui.pythonConsoleTab.layout()) - - self.console = ConsoleWidget(customBanner="GridCal console.\n\n" - "type hlp() to see the available specific commands.\n\n" - "the following libraries are already loaded:\n" - "np: numpy\n" - "pd: pandas\n" - "plt: matplotlib\n" - "app: This instance of GridCal\n" - "circuit: The current grid\n\n") - - self.console.buffer_size = 10000 - - # add the console widget to the user interface - self.ui.pythonConsoleTab.layout().addWidget(self.console) - - # push some variables to the console - self.console.push_vars({"hlp": self.print_console_help, - "np": np, - "pd": pd, - "plt": plt, - "clc": self.clc, - 'app': self, - 'circuit': self.circuit}) - - @staticmethod - def print_console_help(): - """ - Print the console help in the console - @return: - """ - print('GridCal internal commands.\n') - print('If a command is unavailable is because the study has not been executed yet.') - - print('\n\nclc():\tclear the console.') - - print('\n\nApp functions:') - print('\tapp.new_project(): Clear all.') - print('\tapp.open_file(): Prompt to load GridCal compatible file') - print('\tapp.save_file(): Prompt to save GridCal file') - print('\tapp.export_diagram(): Prompt to export the diagram in png.') - print('\tapp.create_schematic_from_api(): Create the schematic from the circuit information.') - print('\tapp.adjust_all_node_width(): Adjust the width of all the nodes according to their name.') - print('\tapp.numerical_circuit: get compilation of the assets.') - print('\tapp.islands: get compilation of the assets split into the topological islands.') - - print('\n\nCircuit functions:') - print('\tapp.circuit.plot_graph(): Plot a graph in a Matplotlib window. Call plt.show() after.') - - print('\n\nPower flow results:') - print('\tapp.session.power_flow.voltage:\t the nodal voltages in per unit') - print('\tapp.session.power_flow.current:\t the branch currents in per unit') - print('\tapp.session.power_flow.loading:\t the branch loading in %') - print('\tapp.session.power_flow.losses:\t the branch losses in per unit') - print('\tapp.session.power_flow.power:\t the nodal power Injections in per unit') - print('\tapp.session.power_flow.Sf:\t the branch power Injections in per unit at the "from" side') - print('\tapp.session.power_flow.St:\t the branch power Injections in per unit at the "to" side') - - print('\n\nShort circuit results:') - print('\tapp.session.short_circuit.voltage:\t the nodal voltages in per unit') - print('\tapp.session.short_circuit.current:\t the branch currents in per unit') - print('\tapp.session.short_circuit.loading:\t the branch loading in %') - print('\tapp.session.short_circuit.losses:\t the branch losses in per unit') - print('\tapp.session.short_circuit.power:\t the nodal power Injections in per unit') - print('\tapp.session.short_circuit.power_from:\t the branch power Injections in per unit at the "from" side') - print('\tapp.session.short_circuit.power_to:\t the branch power Injections in per unit at the "to" side') - print('\tapp.session.short_circuit.short_circuit_power:\t Short circuit power in MVA of the grid nodes') - - print('\n\nOptimal power flow results:') - print('\tapp.session.optimal_power_flow.voltage:\t the nodal voltages angles in rad') - print('\tapp.session.optimal_power_flow.load_shedding:\t the branch loading in %') - print('\tapp.session.optimal_power_flow.losses:\t the branch losses in per unit') - print('\tapp.session.optimal_power_flow.Sbus:\t the nodal power Injections in MW') - print('\tapp.session.optimal_power_flow.Sf:\t the branch power Sf') - - print('\n\nTime series power flow results:') - print('\tapp.session.power_flow_ts.time:\t Profiles time index (pandas DateTimeIndex object)') - print('\tapp.session.power_flow_ts.load_profiles:\t Load profiles matrix (row: time, col: node)') - print('\tapp.session.power_flow_ts.gen_profiles:\t Generation profiles matrix (row: time, col: node)') - print('\tapp.session.power_flow_ts.voltages:\t nodal voltages results matrix (row: time, col: node)') - print('\tapp.session.power_flow_ts.currents:\t Branches currents results matrix (row: time, col: branch)') - print('\tapp.session.power_flow_ts.loadings:\t Branches loadings results matrix (row: time, col: branch)') - print('\tapp.session.power_flow_ts.losses:\t Branches losses results matrix (row: time, col: branch)') - - print('\n\nVoltage stability power flow results:') - print('\tapp.session.continuation_power_flow.voltage:\t Voltage values for every power multiplication factor.') - print('\tapp.session.continuation_power_flow.lambda:\t Value of power multiplication factor applied') - print('\tapp.session.continuation_power_flow.Sf:\t Power values for every power multiplication factor.') - - print('\n\nMonte Carlo power flow results:') - print('\tapp.session.stochastic_power_flow.V_avg:\t nodal voltage average result.') - print('\tapp.session.stochastic_power_flow.I_avg:\t branch current average result.') - print('\tapp.session.stochastic_power_flow.Loading_avg:\t branch loading average result.') - print('\tapp.session.stochastic_power_flow.Losses_avg:\t branch losses average result.') - print('\tapp.session.stochastic_power_flow.V_std:\t nodal voltage standard deviation result.') - print('\tapp.session.stochastic_power_flow.I_std:\t branch current standard deviation result.') - print('\tapp.session.stochastic_power_flow.Loading_std:\t branch loading standard deviation result.') - print('\tapp.session.stochastic_power_flow.Losses_std:\t branch losses standard deviation result.') - print('\tapp.session.stochastic_power_flow.V_avg_series:\t nodal voltage average series.') - print('\tapp.session.stochastic_power_flow.V_std_series:\t branch current standard deviation series.') - print('\tapp.session.stochastic_power_flow.error_series:\t Monte Carlo error series (the convergence value).') - print('The same for app.latin_hypercube_sampling') - - def clc(self): - """ - Clear the console - """ - self.console.clear() - def console_msg(self, *msg_): """ Print some message in the console. diff --git a/src/GridCal/Gui/Main/SubClasses/Settings/configuration.py b/src/GridCal/Gui/Main/SubClasses/Settings/configuration.py index 1c3564bc0..9aed8cba0 100644 --- a/src/GridCal/Gui/Main/SubClasses/Settings/configuration.py +++ b/src/GridCal/Gui/Main/SubClasses/Settings/configuration.py @@ -16,12 +16,13 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import json import os +import qdarktheme from typing import Dict, Union - from PySide6 import QtWidgets from GridCalEngine.IO.file_system import get_create_gridcal_folder from GridCal.Gui.Main.SubClasses.Results.results import ResultsMain +from GridCal.Gui.GridEditorWidget import BusBranchEditorWidget class ConfigurationMain(ResultsMain): @@ -38,6 +39,42 @@ def __init__(self, parent=None): # create main window ResultsMain.__init__(self, parent) + # check boxes + self.ui.dark_mode_checkBox.clicked.connect(self.change_theme_mode) + + def change_theme_mode(self) -> None: + """ + Change the GUI theme + :return: + """ + custom_colors = {"primary": "#00aa88ff", + "primary>list.selectionBackground": "#00aa88be"} + + if self.ui.dark_mode_checkBox.isChecked(): + qdarktheme.setup_theme(theme='dark', custom_colors=custom_colors) + + diagram = self.get_selected_diagram_widget() + if diagram is not None: + if isinstance(diagram, BusBranchEditorWidget): + diagram.set_dark_mode() + + self.colour_diagrams() + + if self.console is not None: + self.console.set_dark_theme() + else: + qdarktheme.setup_theme(theme='light', custom_colors=custom_colors) + + diagram = self.get_selected_diagram_widget() + if diagram is not None: + if isinstance(diagram, BusBranchEditorWidget): + diagram.set_light_mode() + + self.colour_diagrams() + + if self.console is not None: + self.console.set_light_theme() + @staticmethod def config_file_path() -> str: """ diff --git a/src/GridCal/Gui/Main/SubClasses/base_gui.py b/src/GridCal/Gui/Main/SubClasses/base_gui.py index 63bf1be7c..c8eb804c0 100644 --- a/src/GridCal/Gui/Main/SubClasses/base_gui.py +++ b/src/GridCal/Gui/Main/SubClasses/base_gui.py @@ -26,6 +26,8 @@ import darkdetect import numpy as np import pandas as pd +from matplotlib import pyplot as plt + # GUI importswa from PySide6 import QtGui, QtWidgets, QtCore @@ -44,7 +46,7 @@ from GridCal.Gui.ContingencyPlanner.contingency_planner_dialogue import ContingencyPlannerGUI from GridCal.Gui.CoordinatesInput.coordinates_dialogue import CoordinatesInputGUI from GridCal.Gui.GeneralDialogues import CheckListDialogue, StartEndSelectionDialogue -from GridCal.Gui.messages import yes_no_question, warning_msg, info_msg +from GridCal.Gui.messages import yes_no_question, warning_msg, info_msg, error_msg from GridCal.Gui.GridGenerator.grid_generator_dialogue import GridGeneratorGUI from GridCal.Gui.Main.MainWindow import Ui_mainWindow, QMainWindow from GridCal.Gui.Main.object_select_window import ObjectSelectWindow @@ -54,6 +56,8 @@ from GridCal.Gui.SigmaAnalysis.sigma_analysis_dialogue import SigmaAnalysisGUI from GridCal.Gui.SyncDialogue.sync_dialogue import SyncDialogueWindow from GridCal.Gui.TowerBuilder.LineBuilderDialogue import TowerBuilderGUI +from GridCal.Gui.GeneralDialogues import clear_qt_layout +from GridCal.Gui.ConsoleWidget import ConsoleWidget from GridCal.templates import (get_cables_catalogue, get_transformer_catalogue, get_wires_catalogue, get_sequence_lines_catalogue) @@ -188,6 +192,13 @@ def __init__(self, parent=None): is_dark = darkdetect.theme() == "Dark" self.ui.dark_mode_checkBox.setChecked(is_dark) + # Console + self.console: Union[ConsoleWidget, None] = None + try: + self.create_console() + except TypeError: + error_msg('The console has failed because the QtConsole guys have a bug in their package :(') + self.calculation_inputs_to_display = None # ---------------------------------------------------------------------------------------------------------- @@ -229,6 +240,125 @@ def UNLOCK(self) -> None: if not self.any_thread_running(): self.LOCK(False) + def create_console(self) -> None: + """ + Create console + """ + if self.console is not None: + clear_qt_layout(self.ui.pythonConsoleTab.layout()) + + self.console = ConsoleWidget(customBanner="GridCal console.\n\n" + "type hlp() to see the available specific commands.\n\n" + "the following libraries are already loaded:\n" + "np: numpy\n" + "pd: pandas\n" + "plt: matplotlib\n" + "app: This instance of GridCal\n" + "circuit: The current grid\n\n") + + self.console.buffer_size = 10000 + + # add the console widget to the user interface + self.ui.pythonConsoleTab.layout().addWidget(self.console) + + # push some variables to the console + self.console.push_vars({"hlp": self.print_console_help, + "np": np, + "pd": pd, + "plt": plt, + "clc": self.clc, + 'app': self, + 'circuit': self.circuit}) + + if self.ui.dark_mode_checkBox.isChecked(): + self.console.set_dark_theme() + else: + self.console.set_light_theme() + + @staticmethod + def print_console_help(): + """ + Print the console help in the console + @return: + """ + print('GridCal internal commands.\n') + print('If a command is unavailable is because the study has not been executed yet.') + + print('\n\nclc():\tclear the console.') + + print('\n\nApp functions:') + print('\tapp.new_project(): Clear all.') + print('\tapp.open_file(): Prompt to load GridCal compatible file') + print('\tapp.save_file(): Prompt to save GridCal file') + print('\tapp.export_diagram(): Prompt to export the diagram in png.') + print('\tapp.create_schematic_from_api(): Create the schematic from the circuit information.') + print('\tapp.adjust_all_node_width(): Adjust the width of all the nodes according to their name.') + print('\tapp.numerical_circuit: get compilation of the assets.') + print('\tapp.islands: get compilation of the assets split into the topological islands.') + + print('\n\nCircuit functions:') + print('\tapp.circuit.plot_graph(): Plot a graph in a Matplotlib window. Call plt.show() after.') + + print('\n\nPower flow results:') + print('\tapp.session.power_flow.voltage:\t the nodal voltages in per unit') + print('\tapp.session.power_flow.current:\t the branch currents in per unit') + print('\tapp.session.power_flow.loading:\t the branch loading in %') + print('\tapp.session.power_flow.losses:\t the branch losses in per unit') + print('\tapp.session.power_flow.power:\t the nodal power Injections in per unit') + print('\tapp.session.power_flow.Sf:\t the branch power Injections in per unit at the "from" side') + print('\tapp.session.power_flow.St:\t the branch power Injections in per unit at the "to" side') + + print('\n\nShort circuit results:') + print('\tapp.session.short_circuit.voltage:\t the nodal voltages in per unit') + print('\tapp.session.short_circuit.current:\t the branch currents in per unit') + print('\tapp.session.short_circuit.loading:\t the branch loading in %') + print('\tapp.session.short_circuit.losses:\t the branch losses in per unit') + print('\tapp.session.short_circuit.power:\t the nodal power Injections in per unit') + print('\tapp.session.short_circuit.power_from:\t the branch power Injections in per unit at the "from" side') + print('\tapp.session.short_circuit.power_to:\t the branch power Injections in per unit at the "to" side') + print('\tapp.session.short_circuit.short_circuit_power:\t Short circuit power in MVA of the grid nodes') + + print('\n\nOptimal power flow results:') + print('\tapp.session.optimal_power_flow.voltage:\t the nodal voltages angles in rad') + print('\tapp.session.optimal_power_flow.load_shedding:\t the branch loading in %') + print('\tapp.session.optimal_power_flow.losses:\t the branch losses in per unit') + print('\tapp.session.optimal_power_flow.Sbus:\t the nodal power Injections in MW') + print('\tapp.session.optimal_power_flow.Sf:\t the branch power Sf') + + print('\n\nTime series power flow results:') + print('\tapp.session.power_flow_ts.time:\t Profiles time index (pandas DateTimeIndex object)') + print('\tapp.session.power_flow_ts.load_profiles:\t Load profiles matrix (row: time, col: node)') + print('\tapp.session.power_flow_ts.gen_profiles:\t Generation profiles matrix (row: time, col: node)') + print('\tapp.session.power_flow_ts.voltages:\t nodal voltages results matrix (row: time, col: node)') + print('\tapp.session.power_flow_ts.currents:\t Branches currents results matrix (row: time, col: branch)') + print('\tapp.session.power_flow_ts.loadings:\t Branches loadings results matrix (row: time, col: branch)') + print('\tapp.session.power_flow_ts.losses:\t Branches losses results matrix (row: time, col: branch)') + + print('\n\nVoltage stability power flow results:') + print('\tapp.session.continuation_power_flow.voltage:\t Voltage values for every power multiplication factor.') + print('\tapp.session.continuation_power_flow.lambda:\t Value of power multiplication factor applied') + print('\tapp.session.continuation_power_flow.Sf:\t Power values for every power multiplication factor.') + + print('\n\nMonte Carlo power flow results:') + print('\tapp.session.stochastic_power_flow.V_avg:\t nodal voltage average result.') + print('\tapp.session.stochastic_power_flow.I_avg:\t branch current average result.') + print('\tapp.session.stochastic_power_flow.Loading_avg:\t branch loading average result.') + print('\tapp.session.stochastic_power_flow.Losses_avg:\t branch losses average result.') + print('\tapp.session.stochastic_power_flow.V_std:\t nodal voltage standard deviation result.') + print('\tapp.session.stochastic_power_flow.I_std:\t branch current standard deviation result.') + print('\tapp.session.stochastic_power_flow.Loading_std:\t branch loading standard deviation result.') + print('\tapp.session.stochastic_power_flow.Losses_std:\t branch losses standard deviation result.') + print('\tapp.session.stochastic_power_flow.V_avg_series:\t nodal voltage average series.') + print('\tapp.session.stochastic_power_flow.V_std_series:\t branch current standard deviation series.') + print('\tapp.session.stochastic_power_flow.error_series:\t Monte Carlo error series (the convergence value).') + print('The same for app.latin_hypercube_sampling') + + def clc(self): + """ + Clear the console + """ + self.console.clear() + @property def file_name(self) -> str: """ diff --git a/src/GridCalEngine/Core/DataStructures/numerical_circuit.py b/src/GridCalEngine/Core/DataStructures/numerical_circuit.py index 57d865e81..e107763b5 100644 --- a/src/GridCalEngine/Core/DataStructures/numerical_circuit.py +++ b/src/GridCalEngine/Core/DataStructures/numerical_circuit.py @@ -1135,7 +1135,8 @@ def Ybus(self): c=self.branch_data.c, Yshunt_bus=self.Yshunt_from_devices, conn=self.branch_data.conn, - seq=1 + seq=1, + add_windings_phase=False ) return self.admittances_.Ybus diff --git a/src/GridCalEngine/Core/admittance_matrices.py b/src/GridCalEngine/Core/admittance_matrices.py index 86a9c0f2b..1df8dbca1 100644 --- a/src/GridCalEngine/Core/admittance_matrices.py +++ b/src/GridCalEngine/Core/admittance_matrices.py @@ -142,7 +142,8 @@ def compute_admittances(R: Vec, c: Vec, Yshunt_bus: CxVec, conn: Union[List[WindingsConnection], ObjVec], - seq: int) -> Admittance: + seq: int, + add_windings_phase: bool = False) -> Admittance: """ Compute the complete admittance matrices for the general power flow methods (Newton-Raphson based) @@ -166,6 +167,7 @@ def compute_admittances(R: Vec, :param Yshunt_bus: array of shunts equivalent power per bus, from the shunt devices (p.u.) :param seq: Sequence [0, 1, 2] :param conn: array of windings connections (numpy array of WindingsConnection) + :param add_windings_phase: Add the phases of the transformer windings (for short circuits mainly) :return: Admittance instance """ r30_deg = np.exp(1.0j * np.pi / 6.0) @@ -212,14 +214,20 @@ def compute_admittances(R: Vec, elif seq == 1: # positive sequence - # only need to include the phase shift of +-30 degrees - factor_psh = np.array([r30_deg if con == WindingsConnection.GD or con == WindingsConnection.SD else 1.0 - for con in conn]) + if add_windings_phase: + # only need to include the phase shift of +-30 degrees + factor_psh = np.array([r30_deg if con == WindingsConnection.GD or con == WindingsConnection.SD else 1.0 + for con in conn]) - Yff = Gsw + (ys + bc2 + 1.0j * Beq) / (mp * mp * vtap_f * vtap_f) - Yft = -ys / (mp * np.exp(-1.0j * tap_angle) * vtap_f * vtap_t) * factor_psh - Ytf = -ys / (mp * np.exp(1.0j * tap_angle) * vtap_t * vtap_f) * np.conj(factor_psh) - Ytt = (ys + bc2) / (vtap_t * vtap_t) + Yff = Gsw + (ys + bc2 + 1.0j * Beq) / (mp * mp * vtap_f * vtap_f) + Yft = -ys / (mp * np.exp(-1.0j * tap_angle) * vtap_f * vtap_t) * factor_psh + Ytf = -ys / (mp * np.exp(1.0j * tap_angle) * vtap_t * vtap_f) * np.conj(factor_psh) + Ytt = (ys + bc2) / (vtap_t * vtap_t) + else: + Yff = Gsw + (ys + bc2 + 1.0j * Beq) / (mp * mp * vtap_f * vtap_f) + Yft = -ys / (mp * np.exp(-1.0j * tap_angle) * vtap_f * vtap_t) + Ytf = -ys / (mp * np.exp(1.0j * tap_angle) * vtap_t * vtap_f) + Ytt = (ys + bc2) / (vtap_t * vtap_t) else: # original # Yff = Gsw + (ys + bc2 + 1.0j * Beq) / (mp * mp * vtap_f * vtap_f) diff --git a/src/GridCalEngine/Simulations/ContingencyAnalysis/helm_contingencies.py b/src/GridCalEngine/Simulations/ContingencyAnalysis/helm_contingencies.py index 3c77f74a6..9c803d055 100644 --- a/src/GridCalEngine/Simulations/ContingencyAnalysis/helm_contingencies.py +++ b/src/GridCalEngine/Simulations/ContingencyAnalysis/helm_contingencies.py @@ -95,7 +95,8 @@ def calc_V_outage(nc: NumericalCircuit, c=nc.branch_data.c[contingency_br_indices], Yshunt_bus=np.zeros(nc.nbus), conn=nc.branch_data.conn[contingency_br_indices], - seq=1) + seq=1, + add_windings_phase=False) # solve the modified HELM _, V, _, norm_f = helm_coefficients_dY(dY=adm.Ybus, diff --git a/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py b/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py index ac575ee23..f74afde4b 100644 --- a/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py +++ b/src/GridCalEngine/Simulations/OPF/linear_opf_ts.py @@ -264,7 +264,6 @@ def get_values(self, Sbase: float) -> "BranchVars": """ nt, n_elm = self.flows.shape data = BranchVars(nt=nt, n_elm=n_elm) - data.rates = self.rates for t in range(nt): @@ -994,8 +993,7 @@ def add_linear_node_balance(t_idx: int, gen_vars: GenerationVars, batt_vars: BatteryVars, load_vars: LoadVars, - prob: LpModel, - logger: Logger): + prob: LpModel): """ Add the kirchoff nodal equality :param t_idx: time step @@ -1027,13 +1025,10 @@ def add_linear_node_balance(t_idx: int, # add the equality restrictions for k in range(bus_data.nbus): - name = join("kirchoff_", [t_idx, k], "_") - try: - bus_vars.kirchhoff[t_idx, k] = prob.add_cst( - cst=bus_vars.Pcalc[t_idx, k] == P_esp[k], - name=name) - except AttributeError: - logger.add_warning("Kirchoff 0=0", name, comment='Cannot enforce Pcalc zero=Pset zero') + bus_vars.kirchhoff[t_idx, k] = prob.add_cst( + cst=bus_vars.Pcalc[t_idx, k] == P_esp[k], + name=join("kirchoff_", [t_idx, k], "_") + ) for i in vd: set_var_bounds(var=bus_vars.theta[t_idx, i], lb=0.0, ub=0.0) @@ -1195,8 +1190,7 @@ def run_linear_opf_ts(grid: MultiCircuit, gen_vars=mip_vars.gen_vars, batt_vars=mip_vars.batt_vars, load_vars=mip_vars.load_vars, - prob=lp_model, - logger=logger) + prob=lp_model) # add branch contingencies --------------------------------------------------------------------------------- if consider_contingencies: @@ -1259,16 +1253,18 @@ def run_linear_opf_ts(grid: MultiCircuit, # gather the results if status == LpModel.OPTIMAL: - print('Solution:') - print('Objective value =', lp_model.fobj_value()) + logger.add_info("Objective function", value=lp_model.fobj_value()) mip_vars.acceptable_solution = True else: - print('The problem does not have an optimal solution.') + logger.add_error("The problem does not have an optimal solution.") mip_vars.acceptable_solution = False lp_file_name = grid.name + "_debug.lp" lp_model.save_model(file_name=lp_file_name) - print("Debug LP model saved as:", lp_file_name) + logger.add_info("Debug LP model saved", value=lp_file_name) vars_v = mip_vars.get_values(grid.Sbase) + # add the model logger to the main logger + logger += lp_model.logger + return vars_v diff --git a/src/GridCalEngine/Simulations/OPF/opf_results.py b/src/GridCalEngine/Simulations/OPF/opf_results.py index 3fc068f73..d804f8e3e 100644 --- a/src/GridCalEngine/Simulations/OPF/opf_results.py +++ b/src/GridCalEngine/Simulations/OPF/opf_results.py @@ -382,7 +382,7 @@ def mdl(self, result_type) -> "ResultsTable": elif result_type == ResultTypes.HvdcLoading: labels = self.hvdc_names - y = self.hvdc_loading + y = self.hvdc_loading * 100.0 y_label = '(%)' title = 'HVDC loading' diff --git a/src/GridCalEngine/Simulations/OPF/opf_ts_driver.py b/src/GridCalEngine/Simulations/OPF/opf_ts_driver.py index 96f11086e..100ff5105 100644 --- a/src/GridCalEngine/Simulations/OPF/opf_ts_driver.py +++ b/src/GridCalEngine/Simulations/OPF/opf_ts_driver.py @@ -18,7 +18,6 @@ import numpy as np import pandas as pd -import time from typing import Union from GridCalEngine.basic_structures import TimeGrouping, get_time_groups from GridCalEngine.Core.Devices.multi_circuit import MultiCircuit @@ -213,8 +212,7 @@ def opf_by_groups(self): self.results.generator_power[time_indices, :] = opf_vars.gen_vars.p self.results.Sf[time_indices, :] = opf_vars.branch_vars.flows self.results.St[time_indices, :] = -opf_vars.branch_vars.flows - self.results.overloads[time_indices, - :] = opf_vars.branch_vars.flow_slacks_pos - opf_vars.branch_vars.flow_slacks_neg + self.results.overloads[time_indices, :] = opf_vars.branch_vars.flow_slacks_pos - opf_vars.branch_vars.flow_slacks_neg self.results.loading[time_indices, :] = opf_vars.branch_vars.loading self.results.phase_shift[time_indices, :] = opf_vars.branch_vars.tap_angles # self.results.Sbus[time_indices, :] = problem.get_power_injections() diff --git a/src/GridCalEngine/Simulations/PowerFlow/power_flow_driver.py b/src/GridCalEngine/Simulations/PowerFlow/power_flow_driver.py index 886e0ae03..0d02665ba 100644 --- a/src/GridCalEngine/Simulations/PowerFlow/power_flow_driver.py +++ b/src/GridCalEngine/Simulations/PowerFlow/power_flow_driver.py @@ -46,8 +46,9 @@ def __init__(self, grid: MultiCircuit, """ PowerFlowDriver class constructor :param grid: MultiCircuit instance - :param options: PowerFlowOptions instance - :param opf_results: OptimalPowerFlowResults instance + :param options: PowerFlowOptions instance (optional) + :param opf_results: OptimalPowerFlowResults instance (optional) + :param engine: EngineType (i.e. EngineType.GridCal) (optional) """ DriverTemplate.__init__(self, grid=grid, engine=engine) diff --git a/src/GridCalEngine/Simulations/ShortCircuitStudies/short_circuit_worker.py b/src/GridCalEngine/Simulations/ShortCircuitStudies/short_circuit_worker.py index e974e7af1..f05ef1bdb 100644 --- a/src/GridCalEngine/Simulations/ShortCircuitStudies/short_circuit_worker.py +++ b/src/GridCalEngine/Simulations/ShortCircuitStudies/short_circuit_worker.py @@ -153,7 +153,8 @@ def short_circuit_unbalanced(calculation_inputs: NumericalCircuit, Vpf, Zf, bus_ c=np.zeros(nbr), Yshunt_bus=Yshunt_bus0, conn=calculation_inputs.branch_data.conn, - seq=0) + seq=0, + add_windings_phase=True) Y_gen1 = calculation_inputs.generator_data.get_Yshunt(seq=1) Y_batt1 = calculation_inputs.battery_data.get_Yshunt(seq=1) @@ -178,7 +179,8 @@ def short_circuit_unbalanced(calculation_inputs: NumericalCircuit, Vpf, Zf, bus_ c=calculation_inputs.branch_data.c, Yshunt_bus=Yshunt_bus1, conn=calculation_inputs.branch_data.conn, - seq=1) + seq=1, + add_windings_phase=True) Y_gen2 = calculation_inputs.generator_data.get_Yshunt(seq=2) Y_batt2 = calculation_inputs.battery_data.get_Yshunt(seq=2) @@ -203,7 +205,8 @@ def short_circuit_unbalanced(calculation_inputs: NumericalCircuit, Vpf, Zf, bus_ c=np.zeros(nbr), Yshunt_bus=Yshunt_bus2, conn=calculation_inputs.branch_data.conn, - seq=2) + seq=2, + add_windings_phase=True) """ Initialize Vpf introducing phase shifts @@ -246,7 +249,8 @@ def short_circuit_unbalanced(calculation_inputs: NumericalCircuit, Vpf, Zf, bus_ c=calculation_inputs.branch_data.c, Yshunt_bus=np.zeros(nbus, dtype=complex), conn=calculation_inputs.branch_data.conn, - seq=1) + seq=1, + add_windings_phase=True) vd = calculation_inputs.vd pqpv = calculation_inputs.pqpv diff --git a/src/GridCalEngine/Utils/MIP/ortools_interface.py b/src/GridCalEngine/Utils/MIP/ortools_interface.py index 0a7bffce0..922525361 100644 --- a/src/GridCalEngine/Utils/MIP/ortools_interface.py +++ b/src/GridCalEngine/Utils/MIP/ortools_interface.py @@ -25,7 +25,7 @@ import ortools.linear_solver.pywraplp as ort from ortools.linear_solver.pywraplp import LinearExpr as LpExp from ortools.linear_solver.pywraplp import Variable as LpVar -from GridCalEngine.basic_structures import MIPSolvers +from GridCalEngine.basic_structures import MIPSolvers, Logger def get_lp_var_value(x: Union[float, LpVar, LpExp]) -> float: @@ -46,7 +46,6 @@ def get_lp_var_value(x: Union[float, LpVar, LpExp]) -> float: return x - def get_available_mip_solvers() -> List[str]: """ Get a list of candidate solvers @@ -88,6 +87,8 @@ def __init__(self, solver_type: MIPSolvers): self.model.SuppressOutput() + self.logger = Logger() + def save_model(self, file_name="ntc_opf_problem.lp"): """ Save problem in LP format @@ -124,15 +125,19 @@ def add_var(self, lb: float, ub: float, name: str = "") -> LpVar: """ return self.model.NumVar(lb=lb, ub=ub, name=name) - def add_cst(self, cst, name: str = "") -> LpExp: + def add_cst(self, cst, name: str = "") -> Union[LpExp, float]: """ Add constraint to the model :param cst: constraint object (or general expression) :param name: name of the constraint (optional) :return: Constraint object """ - return self.model.Add(constraint=cst, name=name) + try: + return self.model.Add(constraint=cst, name=name) + except AttributeError: + self.logger.add_warning("Kirchoff 0=0", name, comment='Cannot enforce Pcalc zero=Pset zero') + return 0 def sum(self, cst) -> LpExp: """ Add sum of the constraints to the model