def add_HVRT(self,nodenumber,HVRTkey,V_threshold,t_threshold,mode): """Each inputs of the HVRT except nodenumber should be a list such that the entries in the list index should match. for ex:HVRTkey=["1","2"],V_threshold=[0.6,0.7],t_threshold=[1.0,1.0], mode=['mandatory_operation','mandatory_operation'] implies HVRT 1 and 2 are attached to transmission bus [nodenumber] and that HVRTs will operate as mandatory operation with V and t threshholds""" try: assert 'openDSSConfig' in self.data and \ 'manualFeederConfig' in self.data['openDSSConfig'] and \ 'nodes' in self.data['openDSSConfig']['manualFeederConfig'],""" Please use add_manualfeederconfig method to define nodes at which solar is present before running this method.""" nodes=self.data['openDSSConfig']['manualFeederConfig']['nodes'] targetnode={} for n in range(len(self.data['openDSSConfig']['manualFeederConfig']['nodes'])): if nodes[n]['nodenumber'] == nodenumber: targetnode = nodes[n] break; assert 'DERParameters' in targetnode and \ 'default' in targetnode['DERParameters'], """ Can't find the DERParameters accoding to the given nodenumber""" default=targetnode['DERParameters']['default'] HVRT = default['HVRT'] = {} #overwrite even if previous data exists for i in range(len(HVRTkey)): HVRT[HVRTkey[i]] = {} HVRT[HVRTkey[i]]['V_threshold'] = V_threshold[i] HVRT[HVRTkey[i]]['t_threshold'] = t_threshold[i] HVRT[HVRTkey[i]]['mode'] = mode[i] except: GlobalData.log()
def initialize(self): try: targetS, Vpcc = self._tnet_model.staticInitialize() GlobalData.log(20,'Attaching substaion to following buses: {}'.format(Vpcc.keys())) power = self._dnet_model.initialize(targetS, Vpcc) except: GlobalData.log()
def add_fault(self,faultBus,faultImpedance,faultOnTime,faultOffTime): try: if 'simulationConfig' not in self.data: self.data['simulationConfig']={} if 'dynamicConfig' not in self.data['simulationConfig']: self.data['simulationConfig']['dynamicConfig']={} self.data['simulationConfig']['dynamicConfig']['events']={} events=self.data['simulationConfig']['dynamicConfig']['events'] if events.keys(): prevEvents=[] for entry in events.keys(): prevEvents.append(int(entry)) else: prevEvents=[0] nextEvent=max(prevEvents)+1 events[str(nextEvent)]={} events[str(nextEvent)]['time'],events[str(nextEvent)]['faultBus'],\ events[str(nextEvent)]['faultImpedance']=faultOnTime,faultBus,faultImpedance events[str(nextEvent)]['type']='faultOn' events[str(nextEvent+1)]={} events[str(nextEvent+1)]['time']=faultOffTime events[str(nextEvent+1)]['faultBus']=faultBus events[str(nextEvent+1)]['type']='faultOff' except: GlobalData.log()
def add_PVPlacement(self,nodenumber,PVPlacementkey,derId,powerRating,pvderScale): """Each inputs of the PVPlacement except nodenumber should be a list such that the entries in the list index should match. for ex:PVPlacementkey=["25","13"],derId=[50,50],powerRating=[50,50], pvderScale=[1,1] implies DER will attached to distribution node 25 and 13 in transmission bus [nodenumber] and that the both DER will operate as DER setting as DERid 50, powerRating 50, and pvderScale 1""" try: assert 'openDSSConfig' in self.data and \ 'manualFeederConfig' in self.data['openDSSConfig'] and \ 'nodes' in self.data['openDSSConfig']['manualFeederConfig'],""" Please use add_manualfeederconfig method to define nodes at which solar is present before running this method.""" nodes=self.data['openDSSConfig']['manualFeederConfig']['nodes'] targetnode={} for n in range(len(self.data['openDSSConfig']['manualFeederConfig']['nodes'])): if nodes[n]['nodenumber'] == nodenumber: targetnode = nodes[n] break; assert 'DERParameters' in targetnode, """ Can't find the DERParameters accoding to the given nodenumber""" DERParameters=targetnode['DERParameters'] PVPlacement = DERParameters['PVPlacement'] = {} #overwrite even if previous data exists for i in range(len(PVPlacementkey)): PVPlacement[PVPlacementkey[i]] = {} PVPlacement[PVPlacementkey[i]]['derId'] = derId[i] PVPlacement[PVPlacementkey[i]]['powerRating'] = powerRating[i] PVPlacement[PVPlacementkey[i]]['pvderScale'] = pvderScale[i] except: GlobalData.log()
def add_manualfeederconfig(self,nodenumber,filePath,solarFlag,DERFilePath,initializeWithActual, DERSetting,DERModelType,PVPlacement, defaultDERParameters={"solarPenetration":0.02,"derId":"50","powerRating":50, "VrmsRating":177.0,"steadyStateInitialization":True,"pvderScale": 1}): """Each input should be a list such that the entries in the list index should match. for ex:nodenumber=[1,2],filePath=['case13.dss','case123.dss'],solarFlag=[0,1], solarPenetration=[0,50] implies case13.dss is attached to transmission bus 1 and that there is no solar generation in the distribution system.""" try: if 'openDSSConfig' not in self.data: self.data['openDSSConfig']={} if 'manualFeederConfig' not in self.data['openDSSConfig']: self.data['openDSSConfig']['manualFeederConfig']={} self.data['openDSSConfig']['manualFeederConfig']['nodes']=[] data=self.data['openDSSConfig']['manualFeederConfig']['nodes'] for i in range(len(nodenumber)): thisNodeData={'DERParameters':{}} thisNodeData['nodenumber']=nodenumber[i] thisNodeData['filePath']=[filePath[i]] thisNodeData['solarFlag']=solarFlag[i] thisNodeData['DERFilePath']=DERFilePath[i] thisNodeData['initializeWithActual']=initializeWithActual[i] thisNodeData['DERSetting']=DERSetting[i] thisNodeData['DERModelType']=DERModelType[i] thisNodeData['DERParameters']['PVPlacement']=PVPlacement[i] thisNodeData['DERParameters']['default']=defaultDERParameters data.append(thisNodeData) except: GlobalData.log()
def shunt(self, targetS, Vpcc, power): try: mismatchTolerance = 0.1 for node in power: if abs(power[node]['P'] - targetS[node][0]) > mismatchTolerance or abs( power[node]['Q'] - targetS[node][1] ) > mismatchTolerance: # add shunt if needed Pshunt = targetS[node][0] * 1e-3 - power[node]['P'] Qshunt = targetS[node][1] * 1e-3 - power[node]['Q'] # The remaining power is incorporated as compensating shunt # The compensating shunt power # Pshunt + j*Qshunt = Vpcc^2*(YPshunt - YQshunt) # which gives the below two equations for shunt. # Note the negative sign for YQshunt is because we are # considering admittances YPshunt = Pshunt / (Vpcc[node] * Vpcc[node]) YQshunt = -Qshunt / (Vpcc[node] * Vpcc[node]) # Add the remaining as fixed compensating shunt ierr = self._psspy.shunt_data(node, '1 ', 1, [YPshunt, YQshunt]) assert ierr == 0, "Adding shunt failed with error {}".format( ierr) except Exception as e: GlobalData.log('Failed to shunt from PSSEModel')
def add_derparameters(self,nodenumber,solarPenetration,derId,powerRating=50.0,VrmsRating=177.0, steadyStateInitialization=True,pvderScale=1): """Add DER parameters to a given nodenumber (nodeID/busID/busNumber)""" try: assert 'openDSSConfig' in self.data and \ 'manualFeederConfig' in self.data['openDSSConfig'] and \ 'nodes' in self.data['openDSSConfig']['manualFeederConfig'],""" Please use add_manualfeederconfig method to define nodes at which solar is present before running this method.""" nodes=self.data['openDSSConfig']['manualFeederConfig']['nodes'] targetnode={} for n in range(len(self.data['openDSSConfig']['manualFeederConfig']['nodes'])): if nodes[n]['nodenumber'] == nodenumber: targetnode = nodes[n] break; derprop=targetnode['DERParameters']={}#overwrite even if previous data exists default=derprop['default']={} default['solarPenetration'] = solarPenetration default['derId'] = derId default['powerRating'] = powerRating default['VrmsRating'] = VrmsRating default['steadyStateInitialization'] = steadyStateInitialization default['pvderScale'] = pvderScale except: GlobalData.log()
def read(self,fpath): """Will load the config data from an existing config file. Use this method to make modifications to an existing file. P.S. This will overwrite self.data.""" try: self.data=json.load(open(fpath)) except: GlobalData.log()
def initialize(self): try: GlobalData.data['dynamic'] = {} targetS, Vpcc = self._tnet_model.dynamicInitialize() power = self._dnet_model.initialize(targetS, Vpcc) self._tnet_model.shunt(targetS, Vpcc, power) except: GlobalData.log()
def write(self,fpath): """Will write the configuration data in self.data to the given filename.""" try: if not os.path.exists(os.path.dirname(fpath)): os.system('mkdir {}'.format(os.path.dirname(fpath))) json.dump(self.data,open(fpath,'w'),indent=3) except: GlobalData.log()
def add_loadshape(self,loadShape): try: if 'simulationConfig' not in self.data: self.data['simulationConfig']={} self.data['simulationConfig']['staticConfig']={} self.data['simulationConfig']['staticConfig']['loadShape']=loadShape except: GlobalData.log()
def add_psseconfig(self,rawFilePath,dyrFilePath, installLocation): try: psseConfig=self.data['psseConfig']={} psseConfig['rawFilePath']=rawFilePath psseConfig['dyrFilePath']=dyrFilePath psseConfig['installLocation']=installLocation except: GlobalData.log()
def add_simulationconfig(self,simType,protocol='loose_coupling',memoryThreshold=100.0): try: if 'simulationConfig' not in self.data: self.data['simulationConfig']={} simConf=self.data['simulationConfig'] simConf['simType']=simType simConf['protocol']=protocol simConf['memoryThreshold']=memoryThreshold except: GlobalData.log()
def remove_simend(self): try: assert 'events' in self.data['simulationConfig']['dynamicConfig'],"add events first" events=self.data['simulationConfig']['dynamicConfig']['events'] for entry in events: if events[entry]['type']=='simEnd': events.pop(entry) except: GlobalData.log()
def add_defaultfeederconfig(self,filePath,solarFlag,solarPenetration): try: if 'openDSSConfig' not in self.data: self.data['openDSSConfig']={} defConf=self.data['openDSSConfig']['defaultFeederConfig']={} defConf['filePath']=filePath defConf['solarFlag']=solarFlag defConf['solarPenetration']=solarPenetration except: GlobalData.log()
def simulate(self): try: self._procedure.setup() self._procedure.initialize() self._procedure.run() if GlobalData.config['simulationConfig']['simType'] == 'dynamic': generate_output(GlobalData, excel=False) elif GlobalData.config['simulationConfig']['simType'] == 'static': generate_output(GlobalData, excel=False, dataframe=True) except: GlobalData.log()
def main(): ''' Main function to run the T&D Cosimulation ''' GlobalData.set_config('config_td.json') GlobalData.set_TDdata() simulate() generateReport(GlobalData, fname='report.xlsx', sim=GlobalData.config['simulationConfig']['simType']) return 0
def main(): ''' Main function to run the T&D Cosimulation ''' GlobalData.set_config('config_qsts.json') GlobalData.set_TDdata() startTime=time.time() simulate() print ('Simulation time: ' + str(time.time()-startTime) + "sec") generateReport(GlobalData,fname='report.xlsx',sim=GlobalData.config['simulationConfig']['simType']) return 0
def __init__(self,templatePath=None): try: super(Dera,self).__init__() if not templatePath: baseDir=os.path.dirname(inspect.getfile(tdcosim)) templatePath=os.path.join(baseDir,'config','guideline.json') templates=self.load_config_template(templatePath) self.template=templates['table3.1'] self.ind=templates['ind'] except: GlobalData.log()
def add_outputconfig(self,outputDir,simID=None,outputFileName='report.xlsx',outputFileType='xlsx'): try: if not simID: simID=uuid.uuid4().hex if 'outputConfig' not in self.data: self.data['outputConfig']={} self.data['outputConfig']['outputDir']=outputDir self.data['outputConfig']['simID']=simID self.data['outputConfig']['outputfilename']=outputFileName self.data['outputConfig']['type']=outputFileType except: GlobalData.log()
def main(configfile): ''' Main function to run the T&D Cosimulation ''' GlobalData.set_config(configfile) GlobalData.set_TDdata() setOutLocation() startTime = time.time() simulate() # GlobalData.print_data() print('Simulation time: ' + str(time.time() - startTime) + "sec") generateReport(GlobalData, fname=GlobalData.config["outputPath"] + "\\reportfin.xlsx", sim=GlobalData.config['simulationConfig']['simType']) return 0
def __init__(self): try: # empty opendss log file from previous run baseDir = os.path.dirname(inspect.getfile(tdcosim)) f = open(os.path.join(baseDir, 'logs', 'opendssdata.log'), 'w') f.close() if GlobalData.config['simulationConfig']['simType'].lower( ) == 'static': self._procedure = DefaultStaticProcedure() elif GlobalData.config['simulationConfig']['simType'].lower( ) == 'dynamic': self._procedure = DefaultDynamicProcedure() else: print("Unsupported Simulation Type") except: GlobalData.log()
def validate(self): """Validates if the provided settings are valid. P.S. Validity in this context simply means that the provided options satisfy the minimum requirements. When the config options are validated by this method it does not mean that the cosimulation will run without errors. For instance, this method does not verify, if a given filepath exists. P.S. This method will not find the issues when used in optimized mode i.e. python -O foo.py or python -OO foo.py Sample call: self.validate() will return without error when config is correct.""" try: #join is used for better formatting while using GlobalData.log() assert 'cosimHome' in self.data and self.data['cosimHome'],\ ''.join(['cosimHome missing.\n','Please use add_cosimhome']) assert 'psseConfig' in self.data,\ ''.join(['psseConfig key is missing.\n','Please add pssConfig']) assert 'installLocation' in self.data['psseConfig'] and \ 'rawFilePath' in self.data['psseConfig'] and \ 'dyrFilePath' in self.data['psseConfig'],\ ''.join(['psse properties are missing.\n','Please add pssConfig properties']) assert ('defaultFeederConfig' in self.data['openDSSConfig'] and \ len(self.data['openDSSConfig']['defaultFeederConfig'])>0) or \ len(self.data['openDSSConfig']['manualFeederConfig'])>0,\ ''.join(['Either default feeder config or manual feeder config should be set.\n',\ 'Use add_defaultfeederconfig or add_manualfeederconfig.']) assert 'simulationConfig' in self.data,\ ''.join(['simulation config missing.\n',\ 'Use add_simulationconfig method to add simulation config.']) assert 'simType' in self.data['simulationConfig'],\ ''.join(['Simulation type missing.\n',\ 'Use add_simulationconfig method to define simulation type.']) assert 'outputConfig' in self.data,\ ''.join(['output config not set.\n',\ 'Use add_outputconfig method to set it.']) return True except: GlobalData.log() return False
def dera2dyr(self,conf,dyrFilePath,deraID=None,fileMode='w'): """Write dera data to dyrFilePath that can then be added using psspy.dyre_add method.""" try: dyrStr='' if not deraID: deraID=['1']*len(conf) for busID,thisDeraID in zip(conf,deraID): dyrStr+="{},'DERA1',{},".format(busID,thisDeraID) for thisIdata in conf[busID]['flags']: dyrStr+='{},'.format(thisIdata) for thisRdata in conf[busID]['params']: dyrStr+='{},'.format(thisRdata) dyrStr+=' /\n' f=open(dyrFilePath,fileMode); f.write(dyrStr); f.close() except: GlobalData.log()
def remove_fault(self,faultBus,faultOnTime,faultOffTime): try: events=self.data['simulationConfig']['dynamicConfig']['events'] popID=[] for entry in events: if events[entry]['faultBus']==faultBus and events[entry]['type']=='faultOn' and \ events[entry]['time']==faultOnTime: popID.append(entry) if events[entry]['faultBus']==faultBus and events[entry]['type']=='faultOff' and \ events[entry]['time']==faultOffTime: popID.append(entry) for entry in popID: events.pop(entry) except: GlobalData.log()
def run(self): try: GlobalData.data['static'] = {} maxIter = 20 tol=10**-4 count = 0 loadShape = GlobalData.config['simulationConfig']['staticConfig']['loadShape'] GlobalData.data['monitorData']={} updateBins=20 for scale in loadShape: GlobalData.log(20,'Running dispatch with loadshape {}'.format(scale)) GlobalData.data['static'][count] = {} f=lambda x:[x[entry] for entry in x] iteration=0 scaleData={} Vpcc = self._tnet_model.getVoltage() Vcheck=np.random.random((len(Vpcc),2)) for nodeID in Vpcc: scaleData[nodeID]=scale self._dnet_model.scaleLoad(scaleData)# now scale distribution feeder load while np.any(np.abs(Vcheck[:,0]-Vcheck[:,1])>tol) and iteration<maxIter: self._tnet_model.runPFLOW()# run power flow Vpcc = self._tnet_model.getVoltage() self._dnet_model.setVoltage(Vpcc)#set voltage based on previous guess S = self._dnet_model.getLoad()# get complex power injection self._tnet_model.setLoad(S)# set complex power injection as seen from T side Vcheck[:,0]=Vcheck[:,1]#iterate for tight coupling Vcheck[:,1]=np.array(f(Vpcc)) iteration+=1 print('Simulation Progress : ='+'='*int((updateBins-1)*(count/len(loadShape)))+'>'+\ ' {}%({} dispatches/{} dispatches)'.format(((count+1)/len(loadShape))*100,count+1,len(loadShape)),end='\r') GlobalData.log(20,'Loadshape {} Converged in {} iterations with mismatch {}'.format(scale,iteration,max(abs(np.abs(Vcheck[:,0]-Vcheck[:,1]))))) # collect data and store msg={} msg['varName']={} for node in Vpcc: msg['varName'][node]=['voltage'] GlobalData.data['monitorData'][count]=self._dnet_model.monitor(msg) GlobalData.data['static'][count]['V'] = Vpcc GlobalData.data['static'][count]['S'] = S GlobalData.data['static'][count]['Scale'] = scale count+=1 # close print('')# for newline ack=self._dnet_model.close() GlobalData.log(level=20,msg=json.dumps(ack)) ierr=self._tnet_model._psspy.pssehalt_2(); assert ierr==0 except: GlobalData.log()
def run(args): try: startTime = time.time() assert args.config, "config is not provided. You can specify this using -c --config" if not os.path.exists(os.path.abspath(args.config)) and os.path.exists(os.path.join(baseDir,args.config)): args.config=os.path.join(baseDir,args.config) assert os.path.exists(args.config),'{} does not exist'.format(args.config) # check if the config is valid check_config(args.config) GlobalData.set_config(args.config) GlobalData.set_TDdata() proc = Procedure() proc.simulate() print('Solution time:',time.time()-startTime) except: raise
def generate_config(self,busID,configType,userInput=None): try: conf={} if configType in self.template: for thisBusID in busID: thisTemplate=copy.deepcopy(self.template[configType]) if userInput: if 'all' in userInput: if 'flags' in userInput['all']: for thisFlag in userInput['all']['flags']: thisTemplate['flags'][thisFlag]['value']=\ userInput['all']['flags'][thisFlag] if 'params' in userInput['all']: for thisParam in userInput['all']['params']: thisTemplate['included_parameters'][thisParam]['value']=\ userInput['all']['params'][thisParam] elif str(thisBusID) in userInput: if 'flags' in userInput[str(thisBusID)]: for thisFlag in userInput[str(thisBusID)]['flags']: thisTemplate['flags'][thisFlag]['value']=\ userInput[str(thisBusID)]['flags'][thisFlag] if 'params' in userInput[str(thisBusID)]: for thisParam in userInput[str(thisBusID)]['params']: thisTemplate['included_parameters'][thisParam]['value']=\ userInput[str(thisBusID)]['params'][thisParam] flagArray=np.zeros(len(thisTemplate['flags']),dtype=int) paramArray=np.zeros(len(thisTemplate['included_parameters'])) for flag in thisTemplate['flags']: flagArray[self.ind['flag_properties'][flag]['index']]=\ thisTemplate['flags'][flag]['value'] for param in self.ind['parameter_properties']: paramArray[self.ind['parameter_properties'][param]['index']]=\ thisTemplate['included_parameters'][param]['value'] conf[thisBusID]={'flags':flagArray,'params':paramArray} return conf except: GlobalData.log()
def getVoltage(self): try: """Get PCC voltage from psse.""" Vpcc = {} if GlobalData.data['TNet']['LoadBusCount'] == len( GlobalData.data['TNet'] ['LoadBusNumber']): # dist syst interfaced at all load buses loadBusVPU = self._psspy.alodbusreal(string='PU') loadBusVPU = loadBusVPU[1][0] for entry, val in zip(GlobalData.data['TNet']['LoadBusNumber'], loadBusVPU): # efficient if entry in GlobalData.data['DNet']['Nodes']: Vpcc[entry] = val else: # subset of loadbuses interfaced as dist syst for entry in GlobalData.data['TNet'][ 'LoadBusNumber']: # not as efficient for large cases Vpcc[entry] = self._psspy.busdat(entry, 'PU')[1] return Vpcc except Exception as e: GlobalData.log('Failed to getVoltage from PSSEModel')
def staticInitialize(self): try: Vpcc = self.getVoltage() # scale feeder targetS = {} ierr, S = self._psspy.alodbuscplx(string='MVAACT') assert ierr == 0, "Reading load bus complex power failed with error {}".format( ierr) for entry, val in zip(GlobalData.data['TNet']['LoadBusNumber'], S[0]): if entry in GlobalData.data['DNet']['Nodes']: targetS[entry] = [ val.real * 10**3, val.imag * 10**3 ] # convert to kw and kvar from mw and mvar return targetS, Vpcc except Exception as e: GlobalData.log('Failed to initialize from PSSEModel')