Source code for GC_Baran1989

import numpy as np
import networkx as nx
import random
import sys
import os
import logging
import warnings
import pandas as pd
import GC_utils

import GridCalEngine.api as gce  # For interfacing with the GridCal API
from GridCalEngine.IO.file_handler import FileOpen, FileSave

# Add the directory containing utility_script.py to the Python path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'src')))

import GC_utils


warnings.filterwarnings('ignore')  # Ignore warnings during execution

## based on baran 1989
[docs] class Baran1989: """ Class for solving the Baran 1989 test feeder network problem. Attributes: net (DistributionNetwork): The distribution network object. initial_config (list, optional): The initial tie line configuration. Defaults to None. logger (logging.Logger): Logger object for debug messages. """
[docs] def __init__(self, grid=None, TieLines=None, fitness_ratio=1, loss_factor = 0.08, verbose_logging=logging.WARNING): """ Initializes the Baran1989 class. Args: net (DistributionNetwork): The distribution network object. TieLines (list, optional): The initial tie line configuration. Defaults to None. verbose_logging (int, optional): The logging level for debug messages. Defaults to logging.WARNING. """ self.grid = grid # Store the distribution network object self.logger = logging.getLogger('baran1989.py') # Create a logger object for debug messages self.logger.setLevel(verbose_logging) # Set the logging level for debug messages self.initial_config = TieLines # Store the initial tie line configuration self.NumPF=0 self.totalLoad = 1 #abs(sum([self.grid.loads[i].P for i in range(len(self.grid.loads))])) self.loss_factor = loss_factor self.fitness_ratio = fitness_ratio
def __FitnessCalculation(self, config=None): self.NumPF+=1 if config: GC_utils.NetworkReconfiguration(self.grid, all=True, value_all=True, selected_configuration= config, value_configuration=False) #fitness = GC_utils.GC_FitnessCalculation(self.grid, self.totalLoad, loss_factor=self.loss_factor, split_factor=self.fitness_ratio) fitness = GC_utils.GC_FitnessCalculation(self.grid, 1, loss_factor=1, split_factor=1) return fitness
[docs] def Solve(self, order=None): """ Solves the Baran 1989 problem by finding the best tie line configuration that minimizes power losses. This function iterates through all loops in the initial network and tries opening/closing tie lines within each loop. It then checks for radial connectivity and performs power flow analysis to determine the power losses. The configuration with the minimum losses is returned. Args: order (list, optional): An order for iterating through loops (not used in this implementation). Defaults to None. Returns: list: The best tie line configuration that minimizes power losses. """ self.logger.info("Start solving Baran 1989") # Perform network reconfiguration (consider using a more descriptive method name) #for line in self.grid.lines: # line.active = True # Find initial loops GC_utils.NetworkReconfiguration(self.grid, all=True, value_all=True) init_loops = GC_utils.SearchLoopsLines(self.grid,flagUsed=True) self.logger.debug(f"Initial loops =") for loop in init_loops: self.logger.debug(f"\t\t{loop}") #set initial configuration if self.initial_config is None: self.initial_config = [sublist[0] for sublist in init_loops if sublist] #GC_utils.NetworkReconfiguration(self.grid,all=True,value_all=True,selected_configuration=self.initial_config,value_configuration=False) #self.logger.debug(f"initial config: {self.initial_config}") #self.logger.debug(f"radiality: {GC_utils.CheckRadialConnectedNetwork(self.grid)}") #_, losses = GC_utils.GC_PowerFlow(grid=self.grid) #_, losses = self.__powerflow() #self.logger.debug(f"losses: {losses}") GC_utils.NetworkReconfiguration(self.grid, all=True, selected_configuration=self.initial_config, value_all=True, value_configuration=False) # Check for radial connectivity radial, _, _ = GC_utils.CheckRadialConnectedNetwork(self.grid) #_,init_losses = GC_utils.GC_PowerFlow(self.grid) #_, init_losses = self.__powerflow() bestTieLines = self.initial_config if radial: #bestlosses = init_losses best_fitness = self.__FitnessCalculation() else: #bestlosses = np.inf best_fitness = np.inf #self.logger.debug(f"initial configuration {bestTieLines}, losses:{bestlosses},radiality:{radial} ") self.logger.debug(f"initial configuration {bestTieLines}, fitness:{best_fitness},radiality:{radial} ") for idx, loop in enumerate(init_loops): self.logger.debug(f"new loop {loop}") for tie in loop: self.logger.debug(f" new tie: '{tie}' while loop(idx)={idx}") orig_tie = bestTieLines[idx] newTieLines = [tie if x == orig_tie else x for x in bestTieLines] # Perform network reconfiguration with the new tie line configuration GC_utils.NetworkReconfiguration(self.grid, all=True, selected_configuration=newTieLines, value_all=True, value_configuration=False) # Check for radial connectivity radial, _, _ = GC_utils.CheckRadialConnectedNetwork(self.grid) # Log debug information self.logger.debug(f" temporary newTieLines {newTieLines} : radial:{radial}") # If the network is radial, perform power flow analysis and check for improved losses if radial: #_,losses = GC_utils.GC_PowerFlow(self.grid) #_, losses = self.__powerflow() fitness = self.__FitnessCalculation() #self.logger.debug(f" TieLine {newTieLines} : radial = {radial} losses={self.net.losses.real:.4f} outofservice={OutofServiceLines_end}") # If the new losses are better than the best found so far, update the best tie lines and losses #if (losses < bestlosses): if (fitness < best_fitness): bestTieLines = newTieLines # bestlosses = losses best_fitness = fitness #self.logger.debug(f" losses are smaller {bestlosses} than previous tieline, so new one is :{bestTieLines}") self.logger.debug(f" fitness is smaller {best_fitness} than previous tieline, so new one is :{bestTieLines}") else: #self.logger.debug(f" losses are bigger {losses} than the previous besttielines {bestlosses}") self.logger.debug(f" fitness is bigger {fitness} than the previous besttielines {best_fitness}") # Log the best tie lines and losses #self.logger.debug(f"Best TieLines ={bestTieLines} with losses={bestlosses}") self.logger.debug(f"Best TieLines ={bestTieLines} with fitness={best_fitness}") # Return the best tie lines return list(bestTieLines)
if __name__ == '__main__': print('Algorithms to find the optimal distribution network configuration') logging.basicConfig( level=logging.ERROR, # Set the log level to DEBUG format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', # Set the log format datefmt='%Y-%m-%d %H:%M:%S' # Set the date format ) case=1 # Create a network object if case==1: gridGC = FileOpen("D:\\15_Thesis-code\\DistributionNetwork_libraries\\NetworkExamples\\gridcal\\case33.gridcal").open() TieLinesName=['line 32','line 33','line 34','line 35','line 36'] if case==2: gridGC = FileOpen("D:\\15_Thesis-code\\DistributionNetwork_libraries\\NetworkExamples\\gridcal\\case69.gridcal").open() TieLinesName = ['line 57','line 10','line 69','line 14','line 19'] if case==3: gridGC = FileOpen("D:\\15_Thesis-code\\DistributionNetwork_libraries\\NetworkExamples\\gridcal\\case118.m").open() TieLinesName = ['75_77_1', '69_75_1', '77_80_1', '80_97_1', '94_95_1', '92_94_1', '105_106_1', '100_103_1', '100_104_1', '103_110_1', '103_104_1', '92_102_1', '80_99_1', '80_98_1', '92_93_1', '89_90_1', '85_88_1', '82_83_1', '83_84_1', '68_81_1', '62_66_1', '60_61_1', '64_65_1', '59_60_1', '63_64_1', '54_55_1', '55_56_1', '54_56_1', '49_51_1', '51_52_1', '49_50_1', '49_54_1', '49_69_1', '45_46_1', '46_47_1', '34_37_1', '37_39_1', '40_42_1', '40_41_1', '15_19_1', '15_17_1', '27_32_1', '23_25_1', '17_31_1', '17_113_1', '27_28_1', '23_24_1', '24_72_1', '19_20_1', '4_5_1', '8_30_1', '3_5_1', '12_14_1', '12_16_1', '5_6_1', '1_2_1', '17_18_1', '34_36_1', '47_69_1', '77_78_1', '70_74_1', '69_70_1'] if case==4: import pandapower as pp import simbench as sb import GC_PandaPowerImporter sb_code1 = "1-HVMV-urban-2.203-0-no_sw" gridPP = sb.get_simbench_net(sb_code1) gridPP.switch.drop([232,234,236,238,240, 242,244,246], inplace=True) gridPP.trafo.drop([1,3,4], inplace=True) gridPP.line.drop(set([123,226,139,140,151,161,166,170,173,178,180,186,187,188,208,223,225,123,226,227,232,228,229,230,231,227,232,233]), inplace=True) gridPP.ext_grid.at[0,'name']="grid_ext" gridPP.line['in_service'] = True pp.runpp(gridPP) gridGC = GC_PandaPowerImporter.PP2GC(gridPP) TieLinesName=['1_2_1', '1_24_1', '1_36_1', '1_47_1', '51_52_1', '1_60_1', '1_74_1', '1_85_1', '117_181_1', '171_117_1', '117_125_1', '127_164_1', '121_188_1', '146_147_1', '171_181_1', '116_196_1', '116_154_1'] TieLinesID=GC_utils.GC_Line_Name2idtag_array(gridGC, TieLinesName) _, loss = GC_utils.GC_PowerFlow(gridGC, config=TieLinesID) radiality = GC_utils.CheckRadialConnectedNetwork(gridGC) print("Original network: ",GC_utils.GC_Line_idtag2name_array(gridGC,TieLinesID), loss, radiality ) fitness_ratios = [1] for fitness_ratio in fitness_ratios: # Create a Baran object baran = Baran1989(gridGC, TieLines=TieLinesID, fitness_ratio=fitness_ratio, loss_factor = 0.08, verbose_logging=logging.ERROR) disabled_lines = baran.Solve() # Print the list of disabled line indices res,loss = GC_utils.GC_PowerFlow(gridGC, config=disabled_lines) Vmin = res.results.get_bus_df().Vm.min() radiality = GC_utils.CheckRadialConnectedNetwork(gridGC) print(f"The new optimal configuration losses:{loss}, Vmin:{Vmin}, radiality:{radiality}, numPF:{baran.NumPF} ")#is {GC_utils.GC_Line_idtag2name_array(gridGC, disabled_lines)}" )