#Company: DIgSILENT GmbH #Author: DIgSILENT GmbH #Version: 1.0 #Description: #Select transformer elements or types in the diagram or Data Manager and #execute the script. # #The voltage/current pairs from the Saturation tab of the RMS simulation page are #then converted into peak values. #The results will be written into matrix objects (IntMat) within the script. Further #result handling depends on the values of the input parameters: #- writeResults2Types: if 1, the converted values are written into the transformer # type on the EMT Simulation page. The transformer type has to be located within # the project. #- outputResults: if 1, the RMS and peak values will be printed to the Output Window # for all treated transformer types #For troubleshooting take a look at the description page of the ComPython-object ############################################################################### # Module import ############################################################### ############################################################################### import powerfactory import math as m import datetime ############################################################################### # Function definitions ######################################################## ############################################################################### def RMS2peak(I, U): nMeas = len(I) #number of measurement points psi = U #initialisation of vectors i = [0] #first measurement point is necessarily [0,0] k = [] #process second measurement point -> linear part of the saturation curve i.append(I[1]*m.sqrt(2)) #due to linear behaviour, the current peak is sqrt(2)*RMS-value of the measured current for iMeas in range(2,nMeas): #for all other measurements phi = [] #initialisation of angle-vector offset = [] #initialisation of the offset-vector for the calculation of the known RMS-parts alpha = [] #initialisation of the RMS-values of the known parts k = [] #initialisation of the sinus-scale-vector for the calculation of the known sinus parts for itmp in range(iMeas): #calculation of the angles for the integration of the known sinus-parts phi.append(m.asin(psi[itmp]/psi[iMeas])) #angles for the calculation of measurement pair iMeas phi.append(m.pi/2) for itmp in range(1,iMeas): k.append((i[itmp]-i[itmp-1])/(psi[itmp]-psi[itmp-1])*psi[iMeas]) alphasum = 0 for ialpha in range(0,iMeas-1): #for all known parts of the sinus offset.append(-1*k[ialpha]*m.sin(phi[ialpha]) + i[ialpha]) #offset of each sinus part of the current alpha.append(m.pow(k[ialpha],2)/2*(phi[ialpha+1]-phi[ialpha]) - 0.25*m.pow(k[ialpha],2)*(m.sin(2*phi[ialpha+1]) - m.sin(2*phi[ialpha])) - 2*offset[ialpha]*k[ialpha]*(m.cos(phi[ialpha+1]) - m.cos(phi[ialpha])) + m.pow(offset[ialpha],2)*(phi[ialpha+1]-phi[ialpha])) alphasum += alpha[ialpha] #determine the unknown f = ((0.5 + m.pow(m.sin(phi[iMeas-1]), 2))*(phi[iMeas] - phi[iMeas-1]) + 0.25*m.sin(2*phi[iMeas-1]) - 2*m.sin(phi[iMeas-1])*m.cos(phi[iMeas-1])) g = 2*i[iMeas-1]*(m.cos(phi[iMeas-1]) - m.sin(phi[iMeas-1])*(phi[iMeas] - phi[iMeas-1])) h = m.pow(i[iMeas-1], 2)*(phi[iMeas] - phi[iMeas-1]) a = f*m.pow(psi[iMeas],2)/m.pow(psi[iMeas] - psi[iMeas-1], 2) b = g*psi[iMeas]/(psi[iMeas] - psi[iMeas-1]) - 2*i[iMeas-1]*f*m.pow(psi[iMeas],2)/m.pow(psi[iMeas] - psi[iMeas-1], 2) c = m.pow(i[iMeas-1], 2)*f*m.pow(psi[iMeas],2)/(m.pow(psi[iMeas] - psi[iMeas-1], 2)) - i[iMeas-1]*g*psi[iMeas]/(psi[iMeas] - psi[iMeas-1]) + h + alphasum - 0.5*m.pi*m.pow(I[iMeas], 2) x1 = (-1*b - m.sqrt(m.pow(b, 2) - 4*a*c))/(2*a) x2 = (-1*b + m.sqrt(m.pow(b, 2) - 4*a*c))/(2*a) if x1 < 0 and x2 > 0: i.append(x2) elif x1 > 0 and x2 < 0: i.append(x1) else: i.append(x1) k.append((i[iMeas] - i[iMeas-1])/(psi[iMeas] - psi[iMeas-1])*psi[iMeas]) return i #end of RMS2peak ############################################################################### # Output Window messages ###################################################### ############################################################################### #message language depends on PowerFactory language setting #currently available languages: English and German, for all other languages, the English messages will be printed #additional languages can easily be added by filling the corresponding dictionary with same keys and the text in the corresponding language mess_en = {'exec':'Execution of {}.ComPython in version {}. {:>34}', 'nosel':'No elements selected.', 'notrf':'No transformer element(s) or type(s) selected in the diagram or the data manager.', 'ignore':'Selection contains object(s), which are not a transformer type or element. Those objects will be skipped', 'count':'{} transformer types will be treated:', 'nosatur':'{} has no saturation defined on the RMS simulation page. Type will be skipped', 'interpolRMS':'{}: The RMS saturation characteristic interpolation is not set to "piecewise linear", but the conversion process from RMS to peak values bases on a piecewise linear approach.', 'overwrite':'{}: Saturation characteristic for EMT-simulations will be overwritten.\nOriginal values are saved in matrix {}, located within the script.', 'interpolEMT':'{}: The interpolation of the EMT saturation characteristic will be changed from originally "spline" to "piecewise linear" due to the piecewise linear conversion approach.', 'notinprj':'{} is not located within the project. Converted values will not be written to the transformer type.', 'convert':'{}: {} RMS value pairs converted to peak values (more accurate values available in {}).', 'primary':'Ir = {:.2f} A (primary side)', 'rms':'RMS Values', 'peak':'Peak Values'} mess_de = {'exec':'Ausführung von {}.ComPython in Version {}. {:>32}', 'nosel':'Keine Elemente ausgewählt.', 'notrf':'Kein Transformator oder Transformatortyp im Netzdiagramm oder dem Datenmanager ausgewählt.', 'ignore':'Auswahl enthält Objekte, die kein Transformator or Transformatortyp sind. Diese Objekte werden ignoriert.', 'count':'{} Transformatortypen werden im Skript verarbeitet:', 'nosatur':'{} hat keine Werte in der Sättigungstabelle für RMS-Simulationen hinterlegt. Transformatortyp wird übersprungen.', 'interpolRMS':'{}: Die Interpolation der Sättigungscharakteristik für RMS-Simulationen ist nicht auf "stückweise linear" gesetzt, der Konvertierungsansatz von Effektiv- zu Scheitelwerten basiert aber auf einem stückweise linearen Ansatz.', 'overwrite':'{}: Sättigungscharakteristik für EMT-Simulationen wird überschrieben:\nUrsprüngliche Werte sind im Matrixobjekt {}, innerhalb des Skripts abgespeichert.', 'interpolEMT':'{}: Die Interpolation der EMT Sättigungscharakteristik wird aufgrund der stückweise linearen Konvertierung von ursprünglich "spline" auf "stückweise linear" umgestellt.', 'notinprj':'{} ist nicht innerhalb des Projekts gespeichert. Umgerechnete Werte werden nicht in den Transformatortyp geschrieben.', 'convert':'{}: {} Effektivwertpaare in Scheitelwerte umgerechnet (genauere Werte sind in {} verfügbar).', 'primary':'Ir = {:.2f} A (Primärseite)', 'rms':'Effektivwerte', 'peak':'Scheitelwerte'} mess_es = mess_en mess_fr = mess_en mess_cn = mess_en mess_ru = mess_en mess_tr = mess_en mess = {'en':mess_en, 'de':mess_de, 'es':mess_es, 'fr':mess_fr, 'cn':mess_cn, 'ru':mess_ru, 'tr':mess_tr} ############################################################################### # main ######################################################################## ############################################################################### app = powerfactory.GetApplication() prj = app.GetActiveProject() prjName = prj.GetFullName() script = app.GetCurrentScript() sel = app.GetCurrentSelection() lang = app.GetLanguage() if script.script_id is not None and script.script_id.typemetadata_version != []: version = script.script_id.typemetadata_version[-1] elif script.script_id is None and script.typemetadata_version != []: version = script.typemetadata_version[-1] else: version = '"NoVersionNumber"' app.PrintInfo(mess[lang]['exec'].format(script.loc_name, version, datetime.datetime.now().strftime('%d %b %Y %X'))) if sel == []: #if script was not started via right click on selected elements sel = app.GetDiagramSelection() #get selection of diagram if sel == []: #if also no elements selected in the diagram exit(mess[lang]['nosel']) flag = 0 trftypes = [] #all transformer types, which should be treated for el in sel: className = el.GetClassName() if className not in ['ElmTr2', 'ElmTr3', 'ElmTr4', 'TypTr2', 'TypTr3', 'TypTr4']: flag = 1 else: if className[0:3] == 'Elm': typ = el.typ_id else: typ = el if typ not in trftypes: trftypes.append(typ) if trftypes == []: #no transformer types found in the selection exit(mess[lang]['notrf']) if flag: #message of ignored elements from selection, which are not referring to a transformer or type app.PrintInfo(mess[lang]['ignore']) app.PrintInfo(mess[lang]['count'].format(len(trftypes))) app.SetWriteCacheEnabled(1) #has to be set in order to write settings of the transformer without getting intermediate input errors #for each of the transformer types, perform the conversion of the RMS-values for typ in trftypes: zeroMeas = 0 #flag, which checks, if first RMS measurement pair is [0, 0] if typ.itrldf == 0: #no RMS saturation configured in the type, skip type app.PrintWarn(mess[lang]['nosatur'].format(typ)) continue if typ.iIntPola != 1: #if interpolation is not piecewise linear, warn user app.PrintWarn(mess[lang]['interpolRMS'].format(typ)) I = typ.satcue U = typ.satvol if I[0] != 0 or U[0] != 0: #if first measurement pair is not [0, 0] I = [0] + I #measurements are enhanced by the pair [0, 0] U = [0] + U zeroMeas = 1 Ir = typ.GetInom()*1000 #rated current of transformer in A I = [tmp*Ir/100 for tmp in I] #rescales the RMS values from % to A i = RMS2peak(I, U) #conversion from RMS to peak values i = [tmp/Ir/m.sqrt(2) for tmp in i] #rescale to p.u. values if typ.itrmt == 3: #already values available for EMT saturation, values are saved mat = script.CreateObject('IntMat','backup_'+typ.loc_name) mat.M = [[0], [0]] mat.ColLabels = ['i (p.u.)', 'psi (p.u.)'] mat.M = list(map(list, zip(*[typ.satcur, typ.satflux]))) #transposes the vector-arrays app.PrintWarn(mess[lang]['overwrite'].format(typ, mat)) if typ.iInterPol != 1: #if interpolation was not yet piecewise linear, set it and warn user about the change typ.iInterPol = 1 app.PrintWarn(mess[lang]['interpolEMT'].format(typ)) if zeroMeas == 1: #cut out the [0, 0] pair for the types, which do not have a RMS [0, 0]-pair i = i[1:] U = U[1:] I = I[1:] if script.writeResults2Types == 1: #if peak values should be written into the type object typName = typ.GetFullName() if prjName not in typName: #if type not located within the project, the results will not be written app.PrintWarn(mess[lang]['notinprj'].format(typ)) else: typ.itrmt = 3 typ.satcur = i typ.satflux = U app.WriteChangesToDb() Irel = [tmp/Ir for tmp in I] #p.u. values of RMS currents iabs = [tmp*Ir*m.sqrt(2) for tmp in i] #absolute Ampere values of peak p.u. currents mat = script.CreateObject('IntMat',typ.loc_name) mat.M = [[0], [0]] mat.ColLabels = ['U (p.u.)', 'I (p.u.)', 'I (A)', 'psi (p.u.)', 'i (p.u.)', 'i (A)'] mat.M = list(map(list, zip(*[U, Irel, I, U, i, iabs]))) #transposes the vector-arrays #report the converted values app.PrintInfo(mess[lang]['convert'].format(typ, len(i), mat)) if script.outputResults == 1: app.PrintPlain(mess[lang]['primary'].format(Ir)) app.PrintPlain('-'*65) app.PrintPlain('|{:^31}|{:^31}|'.format(mess[lang]['rms'], mess[lang]['peak'])) app.PrintPlain('|{:>10}{:>10}{:>10} | {:>10}{:>10}{:>10}|'.format('U (p.u.)', 'I (p.u.)', 'I (A)', 'psi (p.u.)', 'i (p.u.)', 'i (A)')) app.PrintPlain('-'*65) for itmp in range(len(I)): app.PrintPlain('|{:10.4f}{:10.4f}{:10.4f} | {:10.4f}{:10.4f}{:10.4f}|'.format(U[itmp], I[itmp]/Ir, I[itmp], U[itmp], i[itmp], i[itmp]*Ir*m.sqrt(2))) app.PrintPlain('-'*65) app.SetWriteCacheEnabled(0)