Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cp27643 patch 4 #6

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Auxiliary-File-Format-18.pdf
Binary file not shown.
195 changes: 141 additions & 54 deletions pypowerworld/pypowerworld.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,20 @@
# be changed ad hoc by the opencase method. #
#####################################################################"""

import pandas as pd
import numpy as np
# You will need to replace the original pypowerworld.py that comes with installing the package initally with this file before running calc_gen_shift_factors.

import os

import numpy as np
import pandas as pd
import pythoncom
import win32com
from win32com.client import VARIANT
import pythoncom


class PyPowerWorld(object):
"""Class object designed for easy interface with PowerWorld."""

def __init__(self, pwb_file_path=None):
try:
self.__pwcom__ = win32com.client.Dispatch('pwrworld.SimulatorAuto')
Expand Down Expand Up @@ -43,6 +47,14 @@ def __pwerr__(self):
self.output = None
self.error = False
self.error_message = ''
elif type(self.COMout) == bool:
if self.COMout is False:
self.error = True
self.error_message = self.COMout[0]
else:
self.output = None
self.error = False
self.error_message = ''
elif self.COMout[0] == '':
self.output = None
self.error = False
Expand All @@ -55,39 +67,47 @@ def __pwerr__(self):
self.output = self.COMout[-1]
self.error = True
self.error_message = self.COMout[0]
return self.error
return self.error

def opencase(self, pwb_file_path=None):
"""Opens case defined by the full file path; if this is undefined, opens by previous file path"""
if pwb_file_path is None and self.pwb_file_path is None:
pwb_file_path = input('Enter full pwb file path > ')
if pwb_file_path:
self.pwb_file_path = os.path.splitext(pwb_file_path)[0] + '.pwb'
else:
self.COMout = self.__pwcom__.OpenCase(self.file_folder + '/' + self.file_name + '.pwb')
self.COMout = self.__pwcom__.OpenCase(os.path.normpath(self.pwb_file_path))
if self.__pwerr__():
print('Error opening case:\n\n%s\n\n', self.error_message)
print('Error opening case:\n\n{}\n\n'.format(self.error_message))
print('Please check the file name and path and try again (using the opencase method)\n')
return False
return True
def savecase(self):

def savecase(self, filetype = 'PWB'):
"""Saves case with changes to existing file name and path."""
self.COMout = self.__pwcom__.SaveCase(self.pwb_file_path, 'PWB', 1)
self.COMout = self.__pwcom__.SaveCase(self.pwb_file_path, filetype, 1)
if self.__pwerr__():
print('Error saving case:\n\n%s\n\n', self.error_message)
print('Error saving case:\n\n{}\n\n'.format(self.error_message))
print('******CASE NOT SAVED!******\n\n')
return False
return True
def savecaseas(self, pwb_file_path=None):

def savecaseas(self, pwb_file_path=None, filetype = 'PWB'):
"""If file name and path are specified, saves case as a new file.
Overwrites any existing file with the same name and path."""
if pwb_file_path is not None:
self.pwb_file_path = os.path.splitext(pwb_file_path)[1] + '.pwb'
self.__setfilenames__()
return self.savecase()

if str(pwb_file_path).endswith('.raw'):
if 'PTI' not in filetype:
filetype = 'PTI33'
self.pwb_file_path = os.path.normpath(os.path.join(os.path.dirname(pwb_file_path),os.path.basename(pwb_file_path)))
else:
filetype = 'PWB'
return self.savecase(filetype= filetype)
else:
print('Error saving case:\n\n{}\n\n'.format(pwb_file_path))
print('******CASE NOT SAVED!******\n\n')
return False

def savecaseasaux(self, file_name=None, FilterName='', ObjectType=None, ToAppend=True, FieldList='all'):
"""If file name and path are specified, saves case as a new aux file.
Overwrites any existing file with the same name and path."""
Expand All @@ -96,107 +116,174 @@ def savecaseasaux(self, file_name=None, FilterName='', ObjectType=None, ToAppend
self.file_folder = os.path.split(file_name)[0]
self.save_file_path = os.path.splitext(os.path.split(file_name)[1])[0]
self.aux_file_path = self.file_folder + '/' + self.save_file_path + '.aux'
self.COMout = self.__pwcom__.WriteAuxFile(self.aux_file_path,FilterName,ObjectType,ToAppend,FieldList)
self.COMout = self.__pwcom__.WriteAuxFile(self.aux_file_path, FilterName, ObjectType, ToAppend, FieldList)
if self.__pwerr__():
print('Error saving case:\n\n%s\n\n', self.error_message)
print('Error saving case:\n\n{}\n\n'.format(self.error_message))
print('******CASE NOT SAVED!******\n\n')
return False
return True


def sendtoexcel(self, obj_type='', filtername='', fieldlist="all"):
print("trying to send to excel")
self.COMout = self.__pwcom__.SendToExcel(obj_type, filtername, fieldlist)
if self.__pwerr__():
print('Error sending to excel:\n\n{}\n\n'.format(self.error_message))
print('******DATA NOT SENT TO EXCEL******\n\n')
return False
return True

def getfieldlist(self, obj_type=''):
print("getting field list for {}".format(obj_type))
self.COMout = self.__pwcom__.GetFieldList(obj_type)
if self.__pwerr__():
print('Error getting field list:\n\n{}'.format(self.error_message))
elif self.error_message != '':
print(self.error_message)
elif self.COMout is not None:
return pd.DataFrame(data=[list(x) for x in self.COMout[1]])
return None

def calculatetlr(self, flowelement='', direction='', transactor='', setoutofservicebuses=False, filter='all'):
'''
Use this action to calculate the TLR values a particular flow element (transmission line or interface). You also
specify one end of the potential transfer direction. You may optionally specify the linear calculation method. If no
Linear Method is specified, Lossless DC will be used.
[flow element]: This is the flow element we are interested in. Choices are:
[INTERFACE "name"]
[BRANCH nearbusnum farbusnum ckt]
direction: the type of the transactor. Either BUYER or SELLER. [transactor buyer]: the transactor of power. There are six possible settings.
[AREA num]
[ZONE num]
[SUPERAREA "name"]
[INJECTIONGROUP "name"]
[BUS num]
[SLACK]
LinearMethod: The linear method to be used for the calculation. The options are: AC: for calculation including losses. DC:
for lossless DC. DCPC: for lossless DC that takes into account phase shifter operation
EXAMPLE: CalculateTLR([BRANCH 3391 44645 1], BUYER,[SLACK],AC);
:param flowelement:
:param direction:
:param transactor:
:param setoutofservicebuses:
:param filter:
:return:
'''
self.runscriptcommand(
script_command="CalculateTLR({}, {}, {}, {})".format(flowelement, direction, transactor, "AC")
)

def closecase(self):
"""Closes case without saving changes."""
self.COMout = self.__pwcom__.CloseCase()
if self.__pwerr__():
print('Error closing case:\n\n%s\n\n', self.error_message)
print('Error closing case:\n\n{}\n\n'.format(self.error_message))
return False
return True
def runscriptcommand(self,script_command):

def runscriptcommand(self, script_command):
"""Input a script command as in an Auxiliary file SCRIPT{} statement or the PowerWorld Script command prompt."""
self.COMout = self.__pwcom__.RunScriptCommand(script_command)
if self.__pwerr__():
print('Error encountered with script:\n\n%s\n\n', self.error_message)
print('Script command which was attempted:\n\n%s\n\n', script_command)
print('Error encountered with script:\n\n{}\n\n'.format(self.error_message))
print('Script command which was attempted:\n\n{}\n\n'.format(script_command))
return False
return True
def loadauxfiletext(self,auxtext):

def loadauxfiletext(self, auxtext):
"""Creates and loads an Auxiliary file with the text specified in auxtext parameter."""
f = open(self.aux_file_path, 'w')
f.writelines(auxtext)
f.close()
self.COMout = self.__pwcom__.ProcessAuxFile(self.aux_file_path)
if self.__pwerr__():
print('Error running auxiliary text:\n\n%s\n', self.error_message)
print('Error running auxiliary text:\n\n{}\n'.format(self.error_message))
return False
return True
def getparameterssingleelement(self, element_type = 'BUS', field_list = ['BusName', 'BusNum'], value_list = [0, 1]):

def getparameterssingleelement(self, element_type='BUS', field_list=['BusName', 'BusNum'], value_list=[0, 1]):
"""Retrieves parameter data according to the fields specified in field_list.
value_list consists of identifying parameter values and zeroes and should be
the same length as field_list"""
assert len(field_list) == len(value_list)
field_array = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, field_list)
value_array = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, value_list)
value_array = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, value_list)
self.COMout = self.__pwcom__.GetParametersSingleElement(element_type, field_array, value_array)
if self.__pwerr__():
print('Error retrieving single element parameters:\n\n%s', self.error_message)
print('Error retrieving single element parameters:\n\n{}'.format(self.error_message))
elif self.error_message != '':
print(self.error_message)
elif self.__pwcom__.output is not None:
df = pd.DataFrame(np.array(self.__pwcom__.output[1]).transpose(),columns=field_list)
df = df.replace('',np.nan,regex=True)
elif self.COMout is not None:
df = pd.DataFrame(data=np.array(self.COMout[1][:])).transpose()
df.columns = field_list
return df
return None

def getparametersmultipleelement(self, elementtype, fieldlist, filtername = ''):
fieldarray = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, fieldlist)
self.COMout = self.__pwcom__.GetParametersMultipleElement(elementtype, fieldarray, filtername)
def getparametersmultipleelement(self, element_type, field_list, filtername=''):
fieldarray = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, field_list)
self.COMout = self.__pwcom__.GetParametersMultipleElement(element_type, fieldarray, filtername)
if self.__pwerr__():
print('Error retrieving single element parameters:\n\n%s\n\n', self.error_message)
print('Error retrieving single element parameters:\n\n{}\n\n'.format(self.error_message))
elif self.error_message != '':
print(self.error_message)
elif self.__pwcom__.output is not None:
df = pd.DataFrame(np.array(self.__pwcom__.output[1]).transpose(), columns=fieldlist)
elif self.COMout is not None:
df = pd.DataFrame(data=np.array(self.COMout[1][:]).transpose())
df.columns = field_list
df = df.replace('', np.nan, regex=True)
return df
return None

def changeparameterssingleelement(self, element_type='BUS', field_list=['BusName', 'BusNum'], value_list=[0, 1]):
"""Changes parameter data according to the fields specified in field_list.
value_list consists of identifying parameter values and zeroes and should be
the same length as field_list
Refer to the user guide for required key fields for each element type eg. ['BusNum', 'ID'] for 'Gen' elements"""
assert len(field_list) == len(value_list)
field_array = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, field_list)
value_array = VARIANT(pythoncom.VT_VARIANT | pythoncom.VT_ARRAY, value_list)
self.COMout = self.__pwcom__.ChangeParametersSingleElement(element_type, field_array, value_array)
if self.__pwerr__():
print('Error changing single element parameters:\n\n{}'.format(self.error_message))
elif self.error_message != '':
print(self.error_message)
else:
return self.getparameterssingleelement(element_type=element_type,field_list=field_list,value_list=value_list)
return None

def get3PBfaultcurrent(self, busnum):
"""Calculates the three phase fault; this can be done even with cases which
only contain positive sequence impedances"""
scriptcmd = f'Fault([BUS {busnum}], 3PB);\n'
self.COMout = self.run_script(scriptcmd)
if self.__pwerr__():
print('Error running 3PB fault:\n\n%s\n\n', self.error_message)
print('Error running 3PB fault:\n\n{}\n\n'.format(self.error_message))
return None
fieldlist = ['BusNum', 'FaultCurMag']
return self.getparameterssingleelement('BUS', fieldlist, [busnum, 0])

def createfilter(self, condition, objecttype, filtername, filterlogic='AND', filterpre='NO', enabled='YES'):
"""Creates a filter in PowerWorld. The attempt is to reduce the clunkiness of
# creating a filter in the API, which entails creating an aux data file"""
auxtext = '''
DATA (FILTER, [ObjectType,FilterName,FilterLogic,FilterPre,Enabled])
{
"{objecttype}" "{filtername}" "{filterlogic}" "{filterpre}" "{enabled]"
{{
"{objecttype}" "{filtername}" "{filterlogic}" "{filterpre}" "{enabled}"
<SUBDATA Condition>
{condition}
</SUBDATA>
}'''.format(condition=condition, objecttype=objecttype, filtername=filtername, filterlogic=filterlogic,
filterpre=filterpre, enabled=enabled)
self.COMout = self.__pwcom__.load_aux(auxtext)
if self.__pwcom__.error:
print('Error creating filter %s:\n\n%s' % (filtername,self.__pwcom__.error_message))
}}'''.format(condition=condition, objecttype=objecttype, filtername=filtername, filterlogic=filterlogic,
filterpre=filterpre, enabled=enabled)
self.COMout = self.loadauxfiletext(auxtext)
if self.__pwerr__():
print('Error creating filter {}:\n\n{}'.format(filtername, self.COMout.error_message))
return False
return True
return filtername

def exit(self):
"""Clean up for the PowerWorld COM object"""
self.closecase()
del self.__pwcom__
self.__pwcom__ = None
return None

def __del__(self):
self.exit()
self.exit()