Friday, October 9, 2015

Contingency Analysis with MPjobs



Contingency Analysis (CA) is a basic study process to test the reliability of a network.

For a base case that solves (it means that it converges to a load flow run), the test consists in executing some network changes like opening a transmission line (a contingency), followed by a load flow solution and then a process to identify the network elements with thermal overload or voltage violations.

NERC TPL Standards (TPL-001-4) requires system planners to test the network with specific type of contingencies (P1 to P7 categories).


In PSSe, the activity ACCC allows the execution of contingency analysis as a process.  Check the PSSe manual for more information.  For a base case, additional data files are prepared prior to customize the study (minimum set of files):
  • *.con file – a text file with all the contingencies to be studied, like all P1 contingencies.
  • *.mon file – a text file with the definition of system data to be monitored, like bus voltages and lines overload limits.
  • *.sub file – a text file with definition of the group of network elements that will participate in the contingency definition file or the monitoring definition file.
In a single run, the ACCC process will create a *.dfx file, with a call to the DFAX activity and then it calls the ACCC activity (there are several flavors like …).  In summary, hundreds of contingency (or more!) can be tested with a single run of the ACCC process.  Of course, the larger the contingency number, the more time it will take to finalize the study.  [PSSe have a version of the ACCC activity that can run in parallel!]
Now, a touch of reality.  Contingency analysis are done for multiple base case (seasonal cases or future year cases) and for different loading conditions like peak and off-peak loading, as well as for expansion projects or new generation interconnection studies, or under outage conditions (n-1-1). In other word, there is a need to run multiple CA runs all the time.
MPjobs is a python-based tool, capable to start multiple PSSe instances. For a CA study, each PSSe instance would run the same python script to execute the required ACCC activities but with customized data for each individual run.  In this exercise, a new PSSe instance is activated per base case name (let’us assume that each base case represents a different loading) with the same CON, MON, SUB files. 
The code below (not the final code in the MPjobs release) will run a single ACCC process, running not in the PSSe GUI but in python, calling all needed PSSe activities with API calls.  A module JCtools, included with the MPjobs release, provides common python function like read a file, process strings, etc.  The CA run outcome will be a *.acc binary file from which monitoring data can be extracted later using other PSSe tools like pssearrays.py:

# run_accc1.py
#***************************************************
#***************************************************
#** Set paths, import modules, define run vars
#{-group0 – to be commented/deleted when using with MPjobs----------}
import os, sys
import time
sys.path.append('SCRIPTs\\')
import JCtools
My = JCtools.readIni('mpaccc.ini')
My['XVAR'] = 'savnw'
#{-end of group0------------------------------------------------------------------}
time1 = time.clock()
import JCtools
if My['PSSEVERSION']==34:
   import psse34
else:
   sys.path.append(My['PSSEPATH'])
for x in sys.path: print x

import psspy
psspy.psseinit(My['BUSDIM'])
#import redirect
#redirect.psse2py()
#__________________________________________________________________
def DFXmake(MySUB,MyMON,MyCON,MyDFX,dxoption):
    import psspy
    psspy.dfax(dxoption,MySUB,MyMON,MyCON,MyDFX)
    return
   
def ACCArun(tolerance,lfoption,MyDFX,MyACC):
    import psspy
    #psspy.accc(0.5,[1, 0, 1, 1, 1, 0, 0],MyDFX,MyACC,"")
    psspy.accc(tolerance,lfoption,MyDFX,MyACC,"")
    return
##################################################################
# main:
#MySAV = My['MYSAV']
MySUB = My['MYSUB']
MyMON = My['MYMON']
MyCON = My['MYCON']
#MyDFX = My['MYDFX']
#MyACC = My['MYACC']
dxoption  = My['DXOPTION'] 
tolerance = My['TOLERANCE']
lfoption  = My['LFOPTION'] 
iterations= My['ITERATIONS']
#__________________________________________________________________
_i = psspy.getdefaultint()
_f = psspy.getdefaultreal()
_s = psspy.getdefaultchar()
zyxvars  = JCtools.ZYXvars(My)    #XYZ vars= [studyi,[xvarpath,xvarkey],[yvarpath,yvarkey],[zvarpath,zvarkey],zyxmsg]
studyi= zyxvars[0][0]
xvarpath = zyxvars[1][0]
xvarkey  = zyxvars[1][1]
zyxmsg   = zyxvars[-1]
#***************************************************
#** Set file names:
My['MYSAV']  = '%s%s'%(xvarpath,My['XVAR'])
My['MYDFX']  = '%s%s.dfx'%(My['ACCCPATH'],studyi)
My['MYACC']  = '%s%s.acc'%(My['ACCCPATH'],studyi)
MySAV = My['MYSAV']
MyDFX = My['MYDFX']
MyACC = My['MYACC']
My['LOGFILE']= '%s%s.log'%(My['LOGSPATH'],studyi)
#***************************************************
psspy.progress_output(2,My['LOGFILE'],[0,0])
psspy.prompt_output(2,My['LOGFILE'],[0,0])
#***************************************************
# run a simulation:
print zyxmsg
psspy.time(0)
psspy.case(MySAV)       # basecase name
psspy.solv()
#prepare a DFX file
if not os.path.isfile(MyDFX):
   DFXmake(MySUB,MyMON,MyCON,MyDFX,dxoption)
#update solution parameters
psspy.solution_parameters_2([_i,iterations,_i],
                            [_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f])
#execute a ACCA run
ACCArun(tolerance,lfoption,MyDFX,MyACC)
psspy.time(0)
My['RETURN'] = '%12.3f'%(time.clock() - time1)

To run it, open a DOS window and at the prompt (>), enter:
        python SCRIPTs\runACCC1.py
        [or if the python path is not defined in the system PATH
              c:\python27\python SCRIPTs\runACCC1.py
        ]

Some code modifications will be done to make it usable with MPjobs:
  • All study variables are ‘inherited’ when MPjobs calls this script, therefore the code block group0 that reads data in will be commented.
  • A base case name variable value is assigned to the X-variable, My['XVAR'] , the changing variable within the multiple MPjobs runs.

Because this code will run outside the PSSe GUI, the PSSPY module is loaded explicitly.
The modified code, ready for use with MPjobs is saved as SCRIPTs\run_ACCC1D.py

# run_accc1D.py
#***************************************************
#***************************************************
#** Set paths, import modules, define run vars
#{----------------------------------------------------------------------------}
#import os, sys
#import time
#sys.path.append('SCRIPTs\\')
#import JCtools
#My = JCtools.readIni('mpaccc.ini')
#My['XVAR'] = 'savnw'
#{----------------------------------------------------------------------------}
time1 = time.clock()
import JCtools
if    My['PSSEVERSION']==33:
       import psse33
elif My['PSSEVERSION']==34:
       import psse34
else:
   sys.path.append(My['PSSEPATH'])

#for x in sys.path: print x

import psspy
psspy.psseinit(My['BUSDIM'])
#import redirect
#redirect.psse2py()
#______________________________________________________________
def DFXmake(MySUB,MyMON,MyCON,MyDFX,dxoption):
    import psspy
    psspy.dfax(dxoption,MySUB,MyMON,MyCON,MyDFX)
    return
   
def ACCArun(tolerance,lfoption,MyDFX,MyACC):
    import psspy
    #psspy.accc(0.5,[1, 0, 1, 1, 1, 0, 0],MyDFX,MyACC,"")
    psspy.accc(tolerance,lfoption,MyDFX,MyACC,"")
    return
###############################################################
# main:
#MySAV = My['MYSAV']
MySUB = My['MYSUB']
MyMON = My['MYMON']
MyCON = My['MYCON']
#MyDFX = My['MYDFX']
#MyACC = My['MYACC']
dxoption  = My['DXOPTION'] 
tolerance = My['TOLERANCE']
lfoption  = My['LFOPTION'] 
iterations= My['ITERATIONS']
#______________________________________________________________
_i = psspy.getdefaultint()
_f = psspy.getdefaultreal()
_s = psspy.getdefaultchar()
zyxvars  = JCtools.ZYXvars(My)    #XYZ vars= [studyi,[xvarpath,xvarkey],[yvarpath,yvarkey],[zvarpath,zvarkey],zyxmsg]
studyi= zyxvars[0][0]
xvarpath = zyxvars[1][0]
xvarkey  = zyxvars[1][1]
zyxmsg   = zyxvars[-1]
#***************************************************
#** Set file names:
My['MYSAV']  = '%s%s'%(xvarpath,My['XVAR'])
My['MYDFX']  = '%s%s.dfx'%(My['ACCCPATH'],studyi)
My['MYACC']  = '%s%s.acc'%(My['ACCCPATH'],studyi)
MySAV = My['MYSAV']
MyDFX = My['MYDFX']
MyACC = My['MYACC']
My['LOGFILE']= '%s%s.log'%(My['LOGSPATH'],studyi)
#***************************************************
psspy.progress_output(2,My['LOGFILE'],[0,0])
psspy.prompt_output(2,My['LOGFILE'],[0,0])
#***************************************************
# run a simulation:
print zyxmsg
psspy.time(0)
psspy.case(MySAV)       # basecase name
psspy.solv()
#prepare a DFX file
if not os.path.isfile(MyDFX):
   DFXmake(MySUB,MyMON,MyCON,MyDFX,dxoption)
#update solution parameters
psspy.solution_parameters_2([_i,iterations,_i],
                            [_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f,_f])
#execute a ACCA run
ACCArun(tolerance,lfoption,MyDFX,MyACC)
psspy.time(0)
My['RETURN'] = '%12.3f'%(time.clock() - time1)


The ‘mpACCC.ini’  file has the input data needed for this run, including the name of the target python script that performs the CA study:

/mpACCC.ini for mpjobs + run_accc1D.py
/run accc
[scenario]
title1   = SAVNW
title2   = SB ctgs
studyname= SAVNW
StudyType= accc
CPU = 2
[myvars]   //path ending with '\' required
//Yfile    = CASEs\cases.lst
Xfile    = CASEs\sav.lst
Script   = SCRIPTs\run_accc1D.py
ACCCpath = ACCC\
LOGspath = LOGs\
//Xvecfc = 0.1
//MySAV = CASEs\SAVNW.sav
MySUB = ACCC\SAVNW.sub
MyMON = ACCC\SAVNW.mon
MyCON = ACCC\SAVNW.con
//MyDFX = ACCC\SAVNW.dfx
//MyACC = ACCC\SAVNW.acc
dxoption  = [1,1]
tolerance = 0.5
lfoption   = [1,0,0,0,0,0,0]
iterations= 30

BusDim     = 80000
PsseVersion= 33
PssePath   = C:\Program Files (x86)\PTI\PSSE33\PSSBIN
[notes]
/comments start with or from /
/path ending with '\' required
/in-line comments are stripped before reading keyword value
/string shall be empty, not set to be ='' which is read to has two chars of '

A text file ‘sav.lst ’ (declared as XFILE in the INI file) contains the names of the base cases to be used:
    Sav.lst:
              Savnw
              Savnw32


File names can have full paths or new vars can be defined to hold those path values.
MPjobs will read the INI file, and from the ‘Xfile’ variable, open the list of base cases, assigning each base case name entry to the My['XVAR'] variable and then call the target script.

Now the target python script (‘run_accc1D.py’) contains the code to run PSSe activities [execution of DFAX and ACCC activities], and uses the data made available by MPjobs throught the dictionary ‘My’.  This target python code is able to parse data and create new variables based on the received independents variable, like creating unique file names to output data:
  xvarpath= os.path.dirname(My['XFILE'])                                          #=CASEs\
  xvarkey,xext = os.path.splitext(My['XVAR'])                                     #= savnw & ‘’
  ..
  My['MYSAV']  = '%s\%s'%(xvarpath,My['XVAR'])                            # = CASEs\ savnw

With all this information, we are able to locate the base case to open it for a PSSe CA run.
To run this demo, open a DOS window and type command + inifile, without extension:
  C:..>mpjobs mpaccc
Once the parallel runs are completed, some output files, *.dfx, *.acc, will be created in the “ACCC” folder.

[You can download the code from: MPjobs at my Google Drive site Once you select “MPjobs_jconto_xxxx.zip”, an icon on the top-center screen will perform the download.  The demo data are set for PSSe v.33 but an update of the "psseversion" and "pssepath" in the INI file will make it usable for v. 34]


1 comment:

  1. Hi Jose, I use PSLF but I've always wanted to jump into PSSE! Appreciate it and I'm really enjoying it! Thank you!

    ReplyDelete