def omfCalibrate(workDir, feederPath, scadaPath, simStartDate, simLength, simLengthUnits, solver="FBS", calibrateError=(0.05,5), trim=5): '''calibrates a feeder and saves the calibrated tree at a location. Note: feeders with cap banks should be calibrated with cap banks OPEN. We have seen cap banks throw off calibration.''' with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) simLength = simLength + trim # Process scada data. scadaSubPower = _processScadaData(pJoin(workDir,"gridlabD"),scadaPath, simStartDate, simLengthUnits) # Load specified solver. for key in tree: if tree[key].get("module","").lower() == "powerflow": tree[key] = {"module":"powerflow","solver_method":solver} # Attach player. classOb = {'omftype':'class player','argument':'{double value;}'} playerOb = {"object":"player", "property":"value", "name":"scadaLoads", "file":"subScada.player", "loop":"0"} maxKey = feeder.getMaxKey(tree) playerKey = maxKey + 2 tree[maxKey+1] = classOb tree[playerKey] = playerOb # Make loads reference player. loadTemplate = {"object": "triplex_load", "power_pf_12": "0.95", "impedance_pf_12": "0.98", "power_pf_12": "0.90", "impedance_fraction_12": "0.7", "power_fraction_12": "0.3"} loadTemplateR = {"object": "load", "impedance_pf_A": "0.98", "impedance_pf_B": "0.98", "impedance_pf_C": "0.98", "power_pf_A": "0.90", "power_pf_B": "0.90", "power_pf_C": "0.90", "impedance_fraction_A": "0.7", "impedance_fraction_B": "0.7", "impedance_fraction_C": "0.7", "power_fraction_A": "0.3", "power_fraction_B": "0.3", "power_fraction_C": "0.3"} for key in tree: ob = tree[key] if ob.get("object","") == "triplex_node" and ob.get("power_12","") != "": # Add to triplex_nodes. newOb = dict(loadTemplate) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") oldPow = ob.get("power_12","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower) tree[key] = newOb elif ob.get("object","") == "load": # Add to residential_loads too. newOb = dict(loadTemplateR) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["load_class"] = ob.get("load_class", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") try: oldPow = ob.get("constant_power_A","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_A"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_B","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_B"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_C","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_C"] = "scadaLoads.value*" + str(pythagPower) except: pass tree[key] = newOb # Convert swing bus to a meter. for key in tree: if tree[key].get('bustype','').lower() == 'swing' and tree[key].get('object','') != 'meter': swingName = tree[key].get('name') regIndex = key tree[key]['object'] = 'meter' # Search for the substation meter and attach a recorder there. for key in tree: if tree[key].get('bustype','').lower() == 'swing': swingName = tree[key].get('name') recOb = {"object": "recorder", "parent": swingName, "property": "measured_real_power,measured_reactive_power,measured_power", "file": "caliSub.csv", "interval": "900"} outputRecorderKey = maxKey + 3 tree[outputRecorderKey] = recOb feeder.adjustTime(tree, simLength, simLengthUnits, simStartDate['Date'].strftime("%Y-%m-%d %H:%M:%S")) # Run Gridlabd, calculate scaling constant. def runPowerflowIter(tree,scadaSubPower): '''Runs powerflow once, then iterates.''' # Run initial powerflow to get power. print "Starting calibration." print "Goal of calibration: Error: %s, Iterations: <%s, trim: %s"%(calibrateError[0], calibrateError[1], trim) output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir,"gridlabD")) outRealPow = output["caliSub.csv"]["measured_real_power"][trim:simLength] outImagPower = output["caliSub.csv"]["measured_reactive_power"][trim:simLength] outAppPowerKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPow, outImagPower)] lastFile = "subScada.player" nextFile = "subScadaCalibrated.player" nextPower = outAppPowerKw error = (sum(outRealPow)/1000-sum(scadaSubPower))/sum(scadaSubPower) iteration = 1 print "First error:", error while abs(error)>calibrateError[0] and iteration<calibrateError[1]: # Run calibration and iterate up to 5 times. SCAL_CONST = sum(scadaSubPower)/sum(nextPower) print "Calibrating & running again... Error: %s, Iteration: %s, SCAL_CONST: %s"%(str(round(abs(error*100),2)), str(iteration), round(SCAL_CONST,2)) newPlayData = [] with open(pJoin(pJoin(workDir,"gridlabD"), lastFile), "r") as playerFile: for line in playerFile: (key,val) = line.split(',') newPlayData.append(str(key) + ',' + str(float(val)*SCAL_CONST) + "\n") with open(pJoin(pJoin(workDir,"gridlabD"), nextFile), "w") as playerFile: for row in newPlayData: playerFile.write(row) tree[playerKey]["file"] = nextFile tree[outputRecorderKey]["file"] = "caliSubCheck.csv" nextOutput = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir,"gridlabD")) outRealPowIter = nextOutput["caliSubCheck.csv"]["measured_real_power"][trim:simLength] outImagPowerIter = nextOutput["caliSubCheck.csv"]["measured_reactive_power"][trim:simLength] nextAppKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPowIter, outImagPowerIter)] lastFile = nextFile nextFile = "subScadaCalibrated"+str(iteration)+".player" nextPower = nextAppKw # Compute error and iterate. error = (sum(outRealPowIter)/1000-sum(scadaSubPower))/sum(scadaSubPower) iteration+=1 else: if iteration==1: outRealPowIter = outRealPow print "Calibration done: Error: %s, Iteration: %s, SCAL_CONST: %s"%(str(round(abs(error*100),2)), str(iteration), round(SCAL_CONST,2)) return outRealPow, outRealPowIter, lastFile, iteration outRealPow, outRealPowIter, lastFile, iteration = runPowerflowIter(tree,scadaSubPower[trim:simLength]) caliPowVectors = [[float(element) for element in scadaSubPower[trim:simLength]], [float(element)/1000 for element in outRealPow], [float(element)/1000 for element in outRealPowIter]] labels = ["scadaSubPower","initialGuess","finalGuess"] colors = ['red','lightblue','blue'] chartData = {"Title":"Substation Calibration Check (Iterated "+str(iteration+1)+"X)", "fileName":"caliCheckPlot", "colors":colors,"labels":labels, "timeZone":simStartDate['timeZone']} print "Len:", len(caliPowVectors[0]), len(caliPowVectors[1]), len(caliPowVectors[2]) plotLine(workDir, caliPowVectors, chartData, simStartDate['Date']+dt.timedelta(hours=trim), simLengthUnits) # Write the final output. with open(pJoin(workDir,"calibratedFeeder.omd"),"w") as outJson: playerString = open(pJoin(pJoin(workDir,"gridlabD"),lastFile)).read() feederJson["attachments"][lastFile] = playerString feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return
def runModel(modelDir,localTree,inData): '''This reads a glm file, changes the method of powerflow and reruns''' try: os.remove(pJoin(modelDir,"allOutputData.json")) except: pass allOutput = {} if not os.path.isdir(modelDir): os.makedirs(modelDir) inData["created"] = str(datetime.now()) with open(pJoin(modelDir,"allInputData.json"),"w") as inputFile: json.dump(inData, inputFile, indent=4) binaryName = "gridlabd" for key in localTree: if "solver_method" in localTree[key].keys(): print "current solver method", localTree[key]["solver_method"] localTree[key]["solver_method"] = 'FBS' #find the swing bus and recorder attached to substation for key in localTree: if localTree[key].get('bustype','').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') if localTree[key].get('object','') == 'regulator' and localTree[key].get('from','') == swingName: regIndex = key regConfName = localTree[key]['configuration'] #find the regulator and capacitor names and combine to form a string for volt-var control object regKeys = [] accum_reg = "" for key in localTree: if localTree[key].get("object","") == "regulator": accum_reg += localTree[key].get("name","ERROR") + "," regKeys.append(key) regstr = accum_reg[:-1] print regKeys capKeys = [] accum_cap = "" for key in localTree: if localTree[key].get("object","") == "capacitor": accum_cap += localTree[key].get("name","ERROR") + "," capKeys.append(key) if localTree[key].get("control","").lower() == "manual": localTree[key]['control'] = "VOLT" print "changing capacitor control from manual to volt" capstr = accum_cap[:-1] print capKeys # Attach recorders relevant to CVR. recorders = [ {'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': localTree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag'}, {'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)'}, {'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': localTree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C'}, {'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': localTree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C'}] #recorder object for capacitor switching - if capacitors exist if capKeys != []: for key in capKeys: recorders.append({'object': 'recorder', 'file': 'ZcapSwitch' + str(key) + '.csv', 'limit': '0', 'parent': localTree[key]['name'], 'property': 'switchA,switchB,switchC'}) #attach recorder process biggest = 1 + max([int(k) for k in localTree.keys()]) for index, rec in enumerate(recorders): localTree[biggest + index] = rec #run a reference load flow HOURS = float(100) year_lp = False #leap year feeder.adjustTime(localTree,HOURS,"hours","2011-01-01") output = gridlabd.runInFilesystem(localTree,keepFiles=False,workDir=modelDir) os.remove(pJoin(modelDir,"PID.txt")) p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] #time delays from configuration files time_delay_reg = '30.0' time_delay_cap = '300.0' for key in localTree: if localTree[key].get('object','') == "regulator_configuration": time_delay_reg = localTree[key]['time_delay'] print "time_delay_reg",time_delay_reg # if localTree[key].get('object','') == "capacitor": # time_delay_cap = localTree[key]['time_delay'] # print "time_delay_cap",time_delay_cap #change the recorder names for key in localTree: if localTree[key].get('object','') == "collector" or localTree[key].get('object','') == "recorder": if localTree[key].get('file','').startswith('Z'): localTree[key]['file'] = localTree[key].get('file','').replace('Z','NewZ') #create volt-var control object max_key = max([int(key) for key in localTree.keys()]) print max_key localTree[max_key+1] = {'object' : 'volt_var_control', 'name' : 'IVVC1', 'control_method' : 'ACTIVE', 'capacitor_delay' : str(time_delay_cap), 'regulator_delay' : str(time_delay_reg), 'desired_pf' : '0.99', 'd_max' : '0.6', 'd_min' : '0.1', 'substation_link' : str(localTree[regIndex]['name']), 'regulator_list' : regstr, 'capacitor_list': capstr} #running powerflow analysis via gridalab after attaching a regulator feeder.adjustTime(localTree,HOURS,"hours","2011-01-01") output1 = gridlabd.runInFilesystem(localTree,keepFiles=True,workDir=modelDir) os.remove(pJoin(modelDir,"PID.txt")) pnew = output1['NewZregulator.csv']['power_in.real'] qnew = output1['NewZregulator.csv']['power_in.imag'] #total real and imaginary losses as a function of time realLoss = [] imagLoss = [] realLossnew = [] imagLossnew = [] for element in range(int(HOURS)): r = 0.0 i = 0.0 rnew = 0.0 inew = 0.0 for device in ['ZlossesOverhead.csv','ZlossesTransformer.csv','ZlossesUnderground.csv']: for letter in ['A','B','C']: r += output[device]['sum(power_losses_' + letter + '.real)'][element] i += output[device]['sum(power_losses_' + letter + '.imag)'][element] rnew += output1['New'+device]['sum(power_losses_' + letter + '.real)'][element] inew += output1['New'+device]['sum(power_losses_' + letter + '.imag)'][element] realLoss.append(r) imagLoss.append(i) realLossnew.append(rnew) imagLossnew.append(inew) #voltage calculations and tap calculations lowVoltage = [] meanVoltage = [] highVoltage = [] lowVoltagenew = [] meanVoltagenew = [] highVoltagenew = [] tap = {'A':[],'B':[],'C':[]} tapnew = {'A':[],'B':[],'C':[]} volt = {'A':[],'B':[],'C':[]} voltnew = {'A':[],'B':[],'C':[]} switch = {'A':[],'B':[],'C':[]} switchnew = {'A':[],'B':[],'C':[]} for element in range(int(HOURS)): for letter in ['A','B','C']: tap[letter].append(output['Zregulator.csv']['tap_' + letter][element]) tapnew[letter].append(output1['NewZregulator.csv']['tap_' + letter][element]) #voltage real, imag vr, vi = sepRealImag(output['ZsubstationBottom.csv']['voltage_'+letter][element]) volt[letter].append(math.sqrt(vr**2+vi**2)/60) vrnew, vinew = sepRealImag(output1['NewZsubstationBottom.csv']['voltage_'+letter][element]) voltnew[letter].append(math.sqrt(vrnew**2+vinew**2)/60) if capKeys != []: switch[letter].append(output['ZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch'+ letter][element]) switchnew[letter].append(output1['NewZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch'+ letter][element]) lowVoltage.append(float(output['ZvoltageJiggle.csv']['min(voltage_12.mag)'][element])/2.0) meanVoltage.append(float(output['ZvoltageJiggle.csv']['mean(voltage_12.mag)'][element])/2.0) highVoltage.append(float(output['ZvoltageJiggle.csv']['max(voltage_12.mag)'][element])/2.0) lowVoltagenew.append(float(output1['NewZvoltageJiggle.csv']['min(voltage_12.mag)'][element])/2.0) meanVoltagenew.append(float(output1['NewZvoltageJiggle.csv']['mean(voltage_12.mag)'][element])/2.0) highVoltagenew.append(float(output1['NewZvoltageJiggle.csv']['max(voltage_12.mag)'][element])/2.0) #energy calculations whEnergy = [] whLosses = [] whLoads = [] whEnergy.append(sum(p)/10**6) whLosses.append(sum(realLoss)/10**6) whLoads.append((sum(p)-sum(realLoss))/10**6) whEnergy.append(sum(pnew)/10**6) whLosses.append(sum(realLossnew)/10**6) whLoads.append((sum(pnew)-sum(realLossnew))/10**6) indices = ['No IVVC', 'With IVVC'] # energySalesRed = (whLoads[1]-whLoads[0])*(inData['wholesaleEnergyCostPerKwh'])*1000 # lossSav = (whLosses[0]-whLosses[1])*inData['wholesaleEnergyCostPerKwh']*1000 # print energySalesRed, lossSav #plots ticks = [] plt.clf() plt.title("total energy") plt.ylabel("total load and losses (MWh)") for element in range(2): ticks.append(element) bar_loss = plt.bar(element, whLosses[element], 0.15, color= 'red') bar_load = plt.bar(element+0.15, whLoads[element], 0.15, color= 'orange') plt.legend([bar_load[0],bar_loss[0]],['total load', 'total losses'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.xticks([t+0.15 for t in ticks],indices) plt.savefig(pJoin(modelDir,"total energy.png")) #real and imaginary power plt.figure("real power") plt.title("Real Power at substation") plt.ylabel("substation real power (MW)") pMW = [element/10**6 for element in p] pMWn = [element/10**6 for element in pnew] pw = plt.plot(pMW) npw = plt.plot(pMWn) plt.legend([pw[0], npw[0]], ['NO IVVC','WITH IVVC'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir,"real power.png")) plt.figure("Reactive power") plt.title("Reactive Power at substation") plt.ylabel("substation reactive power (MVAR)") qMVAR = [element/10**6 for element in q] qMVARn = [element/10**6 for element in qnew] iw = plt.plot(qMVAR) niw = plt.plot(qMVARn) plt.legend([iw[0], niw[0]], ['NO IVVC','WITH IVVC'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir,"imaginary power.png")) #voltage plots plt.figure("voltages as a function of time") f,ax = plt.subplots(2,sharex=True) f.suptitle("Voltages high and low") lv = ax[0].plot(lowVoltage,color = 'cadetblue') mv = ax[0].plot(meanVoltage,color = 'blue') hv = ax[0].plot(highVoltage, color = 'cadetblue') ax[0].legend([lv[0], mv[0], hv[0]], ['low voltage','mean voltage','high voltage'],bbox_to_anchor=(0., 0.915, 1., .1), loc=3, ncol=3, mode="expand", borderaxespad=0.1) ax[0].set_ylabel('NO IVVC') nlv = ax[1].plot(lowVoltagenew,color = 'cadetblue') nmv = ax[1].plot(meanVoltagenew,color = 'blue') nhv = ax[1].plot(highVoltagenew, color = 'cadetblue') ax[1].set_ylabel('WITH IVVC') plt.savefig(pJoin(modelDir,"Voltages.png")) #tap positions plt.figure("TAP positions NO IVVC") f,ax = plt.subplots(6,sharex=True) f.set_size_inches(18.5,12.0) #f.suptitle("Regulator Tap positions") ax[0].plot(tap['A']) ax[0].set_title("Regulator Tap positions NO IVVC") ax[0].set_ylabel("TAP A") ax[1].plot(tap['B']) ax[1].set_ylabel("TAP B") ax[2].plot(tap['C']) ax[2].set_ylabel("TAP C") ax[3].plot(tapnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("TAP A") ax[4].plot(tapnew['B']) ax[4].set_ylabel("TAP B") ax[5].plot(tapnew['C']) ax[5].set_ylabel("TAP C") for subplot in range(6): ax[subplot].set_ylim(-20,20) f.tight_layout() plt.savefig(pJoin(modelDir,"Regulator TAP positions.png")) #substation voltages plt.figure("substation voltage as a function of time") f,ax = plt.subplots(6,sharex=True) f.set_size_inches(18.5,12.0) #f.suptitle("voltages at substation NO IVVC") ax[0].plot(volt['A']) ax[0].set_title('Substation voltages NO IVVC') ax[0].set_ylabel('voltage A') ax[1].plot(volt['B']) ax[1].set_ylabel('voltage B') ax[2].plot(volt['C']) ax[2].set_ylabel('voltage C') ax[3].plot(voltnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel('voltage A') ax[4].plot(voltnew['B']) ax[4].set_ylabel('voltage B') ax[5].plot(voltnew['C']) ax[5].set_ylabel('voltage C') f.tight_layout() plt.savefig(pJoin(modelDir,"substation voltages.png")) #cap switches - plotted if capacitors are present if capKeys != []: plt.figure("capacitor switch state as a function of time") f,ax = plt.subplots(6,sharex=True) f.set_size_inches(18.5,12.0) #f.suptitle("Capacitor switch state NO IVVC") ax[0].plot(switch['A']) ax[0].set_title("Capacitor switch state NO IVVC") ax[0].set_ylabel("switch A") ax[1].plot(switch['B']) ax[1].set_ylabel("switch B") ax[2].plot(switch['C']) ax[2].set_ylabel("switch C") ax[3].plot(switchnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("switch A") ax[4].plot(switchnew['B']) ax[4].set_ylabel("switch B") ax[5].plot(switchnew['C']) ax[5].set_ylabel("switch C") for subplot in range(6): ax[subplot].set_ylim(-2,2) f.tight_layout() plt.savefig(pJoin(modelDir,"capacitor switch.png")) #plt.show() #monetization monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] monthToSeason = {'January':'Winter','February':'Winter','March':'Spring','April':'Spring', 'May':'Spring','June':'Summer','July':'Summer','August':'Summer', 'September':'Fall','October':'Fall','November':'Fall','December':'Winter'} if year_lp == True: febDays = 29 else: febDays = 28 monthHours = [int(31*24),int(febDays*24),int(31*24),int(30*24),int(31*24),int(30*24),int(31*24),int(31*24),int(30*24),int(31*24),int(30*24),int(31*24)] #find simulation months temp = 0 cumulHours = [] for x in range(12): temp += monthHours[x] cumulHours.append(temp) for i in range(12): if i == 0: lowval = 0 else: lowval = cumulHours[i-1] if HOURS<=cumulHours[i] and HOURS>=lowval: hourMonth = monthNames[i] hourIndex = i #calculate peaks for the number of months in simulation previndex = 0 monthPeak = {} monthPeakNew = {} peakSaveDollars = {} energyLostDollars = {} lossRedDollars = {} for month in range(hourIndex+1): index1 = int(previndex) index2 = int(min((index1 + int(monthHours[month])), HOURS)) monthPeak[monthNames[month]] = max(p[index1:index2])/1000.0 monthPeakNew[monthNames[month]] = max(pnew[index1:index2])/1000.0 peakSaveDollars[monthNames[month]] = (monthPeak[monthNames[month]]-monthPeakNew[monthNames[month]])*inData['peakDemandCost'+str(monthToSeason[monthNames[month]])+'PerKw'] lossRedDollars[monthNames[month]] = (sum(realLoss[index1:index2])/1000.0 - sum(realLossnew[index1:index2])/1000.0)*(inData['wholesaleEnergyCostPerKwh']) energyLostDollars[monthNames[month]] = (sum(p[index1:index2])/1000.0 - sum(pnew[index1:index2])/1000.0 - sum(realLoss[index1:index2])/1000.0 + sum(realLossnew[index1:index2])/1000.0 )*(inData['wholesaleEnergyCostPerKwh'] - inData['retailEnergyCostPerKwh']) previndex = index2 #money charts simMonths = monthNames[:hourIndex+1] fig = plt.figure("cost benefit barchart",figsize=(10,8)) ticks = range(len(simMonths)) ticks1 = [element+0.15 for element in ticks] ticks2 = [element+0.30 for element in ticks] print ticks eld = [energyLostDollars[month] for month in simMonths] lrd = [lossRedDollars[month] for month in simMonths] psd = [peakSaveDollars[month] for month in simMonths] bar_eld = plt.bar(ticks,eld,0.15,color='red') bar_psd = plt.bar(ticks1,psd,0.15,color='blue') bar_lrd = plt.bar(ticks2,lrd,0.15,color='green') plt.legend([bar_eld[0], bar_psd[0], bar_lrd[0]], ['energyLostDollars','peakReductionDollars','lossReductionDollars'],bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) monShort = [element[0:3] for element in simMonths] plt.xticks([t+0.15 for t in ticks],monShort) plt.ylabel('Utility Savings ($)') plt.savefig(pJoin(modelDir,"spendChart.png")) with open(pJoin(modelDir,"spendChart.png"),"rb") as inFile: allOutput["spendChart"] = inFile.read().encode("base64") #cumulative savings graphs fig = plt.figure("cost benefit barchart",figsize=(10,5)) annualSavings = sum(eld) + sum(lrd) + sum(psd) annualSave = lambda x:(annualSavings - inData['omCost']) * x - inData['capitalCost'] simplePayback = inData['capitalCost']/(annualSavings - inData['omCost']) plt.xlabel('Year After Installation') plt.xlim(0,30) plt.ylabel('Cumulative Savings ($)') plt.plot([0 for x in range(31)],c='gray') plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--') plt.plot([annualSave(x) for x in range(31)], c='green') plt.savefig(pJoin(modelDir,"savingsChart.png")) with open(pJoin(modelDir,"savingsChart.png"),"rb") as inFile: allOutput["savingsChart"] = inFile.read().encode("base64") # Update the runTime in the input file. # endTime = datetime.now() # inDat["runTime"] = str(timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir,"allInputData.json"),"w") as inFile: json.dump(inData, inFile, indent=4) with open(pJoin(modelDir,"allOutputData.json"),"w") as outFile: json.dump(allOutput, outFile, indent=4) print "DONE RUNNING", modelDir
'generator_mode': 'SUPPLY_DRIVEN', 'name': 'solar172879', 'parent': 'solEngInverter', 'area': '30000 sf', 'generator_status': 'ONLINE', 'object': 'solar', 'efficiency': '0.14', 'panel_type': 'SINGLE_CRYSTAL_SILICON' } # myTree[oldMax + 7] = { 'interval':'3600', # 'parent':'solEngInverter', # 'limit':'0', # 'file':'Inverter_solEngInverter.csv', # 'property':'power_A,power_B,power_C', # 'object': 'recorder'} feeder.adjustTime(myTree, 240, 'hours', '2014-01-01') # Run here to test. rawOut = runInFilesystem(myTree, attachments=myFeed['attachments'], keepFiles=True, workDir='.', glmName='Orville Tree Pond Calibrated.glm') # # Show some output. # print 'Output Keys:', rawOut.keys() # plt.plot([abs(complex(x)) for x in rawOut['Inverter_solEngInverter.csv']['power_A']]) # plt.show() # Write back the full feeder. outJson = dict(myFeed)
def attachVolts(workDir, feederPath, voltVectorA, voltVectorB, voltVectorC, simStartDate, simLength, simLengthUnits): '''read voltage vectors of 3 different phases, run gridlabd, and attach output to the feeder.''' try: timeStamp = [simStartDate['Date']] for x in range(1, 8760): timeStamp.append(timeStamp[x - 1] + dt.timedelta(hours=1)) firstDateTime = timeStamp[1] with open(pJoin(pJoin(workDir, "gridlabD"), "phaseAVoltage.player"), "w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f" % float(voltVectorA[x])) + "+0j" line = timestamp.strftime( "%Y-%m-%d %H:%M:%S" ) + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(pJoin(pJoin(workDir, "gridlabD"), "phaseBVoltage.player"), "w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f" % float(voltVectorB[x])) + "-" + str( "%0.4f" % float(random.uniform(6449, 6460))) + "j" line = timestamp.strftime( "%Y-%m-%d %H:%M:%S" ) + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(pJoin(pJoin(workDir, "gridlabD"), "phaseCVoltage.player"), "w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f" % float(voltVectorC[x])) + "+" + str( "%0.4f" % float(random.uniform(6449, 6460))) + "j" line = timestamp.strftime( "%Y-%m-%d %H:%M:%S" ) + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) # Find swingNode name. for key in tree: if tree[key].get('bustype', '').lower() == 'swing': swingName = tree[key].get('name') # Attach player. classOb = {'omftype': 'class player', 'argument': '{double value;}'} voltageObA = { "object": "player", "property": "voltage_A", "file": "phaseAVoltage.player", "loop": "0", "parent": swingName } voltageObB = { "object": "player", "property": "voltage_B", "file": "phaseBVoltage.player", "loop": "0", "parent": swingName } voltageObC = { "object": "player", "property": "voltage_C", "file": "phaseCVoltage.player", "loop": "0", "parent": swingName } maxKey = feeder.getMaxKey(tree) voltplayerKeyA = maxKey + 2 voltplayerKeyB = maxKey + 3 voltplayerKeyC = maxKey + 4 tree[maxKey + 1] = classOb tree[voltplayerKeyA] = voltageObA tree[voltplayerKeyB] = voltageObB tree[voltplayerKeyC] = voltageObC # Adjust time and run output. feeder.adjustTime(tree, simLength, simLengthUnits, firstDateTime.strftime("%Y-%m-%d %H:%M:%S")) output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir, "gridlabD")) # Write the output. with open(pJoin(workDir, "calibratedFeeder.omd"), "w") as outJson: playerStringA = open( pJoin(pJoin(workDir, "gridlabD"), "phaseAVoltage.player")).read() playerStringB = open( pJoin(pJoin(workDir, "gridlabD"), "phaseBVoltage.player")).read() playerStringC = open( pJoin(pJoin(workDir, "gridlabD"), "phaseCVoltage.player")).read() feederJson["attachments"]["phaseAVoltage.player"] = playerStringA feederJson["attachments"]["phaseBVoltage.player"] = playerStringB feederJson["attachments"]["phaseCVoltage.player"] = playerStringC feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return pJoin(workDir, "calibratedFeeder.omd"), True except: print "Failed to run gridlabD with voltage players." return "", False
def runForeground(modelDir, inputDict): ''' Run the model in its directory. WARNING: GRIDLAB CAN TAKE HOURS TO COMPLETE. ''' # Check whether model exist or not if not os.path.isdir(modelDir): os.makedirs(modelDir) inputDict["created"] = str(datetime.datetime.now()) print "STARTING TO RUN", modelDir beginTime = datetime.datetime.now() feederList = [] # Get prepare of data and clean workspace if re-run, If re-run remove all the data in the subfolders for dirs in os.listdir(modelDir): if os.path.isdir(pJoin(modelDir, dirs)): shutil.rmtree(pJoin(modelDir, dirs)) # Get each feeder, prepare data in separate folders, and run there. for key in sorted(inputDict, key=inputDict.get): if key.startswith("feederName"): feederDir, feederName = inputDict[key].split("___") feederList.append(feederName) try: os.remove(pJoin(modelDir, feederName, "allOutputData.json")) except Exception, e: pass if not os.path.isdir(pJoin(modelDir, feederName)): os.makedirs(pJoin(modelDir, feederName)) # create subfolders for feeders shutil.copy(pJoin(__metaModel__._omfDir, "data", "Feeder", feederDir, feederName + ".json"), pJoin(modelDir, feederName, "feeder.json")) inputDict["climateName"], latforpvwatts = zipCodeToClimateName(inputDict["zipCode"]) shutil.copy(pJoin(__metaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, feederName, "climate.tmy2")) try: startTime = datetime.datetime.now() feederJson = json.load(open(pJoin(modelDir, feederName, "feeder.json"))) tree = feederJson["tree"] # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) feeder.adjustTime(tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"]) # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem(tree, attachments=feederJson["attachments"], keepFiles=True, workDir=pJoin(modelDir, feederName)) cleanOut = {} # Std Err and Std Out cleanOut['stderr'] = rawOut['stderr'] cleanOut['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# property.. timestamp'] else: cleanOut['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get('timeStamps',[]) level = inputDict.get('simLengthUnits','hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): cleanOut['climate'] = {} cleanOut['climate']['Rain Fall (in/h)'] = hdmAgg(rawOut[key].get('rainfall'), sum, level) cleanOut['climate']['Wind Speed (m/s)'] = hdmAgg(rawOut[key].get('wind_speed'), avg, level) cleanOut['climate']['Temperature (F)'] = hdmAgg(rawOut[key].get('temperature'), max, level) cleanOut['climate']['Snow Depth (in)'] = hdmAgg(rawOut[key].get('snowdepth'), max, level) cleanOut['climate']['Direct Insolation (W/m^2)'] = hdmAgg(rawOut[key].get('solar_direct'), sum, level) # Voltage Band if 'VoltageJiggle.csv' in rawOut: cleanOut['allMeterVoltages'] = {} cleanOut['allMeterVoltages']['Min'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)']], min, level) cleanOut['allMeterVoltages']['Mean'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['StdDev'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['Max'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)']], max, level) cleanOut['allMeterVoltages']['stdDevPos'] = [(x+y/2) for x,y in zip(cleanOut['allMeterVoltages']['Mean'], cleanOut['allMeterVoltages']['StdDev'])] cleanOut['allMeterVoltages']['stdDevNeg'] = [(x-y/2) for x,y in zip(cleanOut['allMeterVoltages']['Mean'], cleanOut['allMeterVoltages']['StdDev'])] # Total # of meters count = 0 with open(pJoin(modelDir, feederName, "feeder.json")) as f: for line in f: if "\"objectType\": \"triplex_meter\"" in line: count+=1 print "count=", count cleanOut['allMeterVoltages']['triplexMeterCount'] = float(count) # Power Consumption cleanOut['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads cleanOut['Consumption']['Power'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['DG'] = [0] * int(inputDict["simLength"]) for key in rawOut: if key.startswith('SwingKids_') and key.endswith('.csv'): oneSwingPower = hdmAgg(vecPyth(rawOut[key]['sum(power_in.real)'],rawOut[key]['sum(power_in.imag)']), avg, level) if 'Power' not in cleanOut['Consumption']: cleanOut['Consumption']['Power'] = oneSwingPower else: cleanOut['Consumption']['Power'] = vecSum(oneSwingPower,cleanOut['Consumption']['Power']) elif key.startswith('Inverter_') and key.endswith('.csv'): realA = rawOut[key]['power_A.real'] realB = rawOut[key]['power_B.real'] realC = rawOut[key]['power_C.real'] imagA = rawOut[key]['power_A.imag'] imagB = rawOut[key]['power_B.imag'] imagC = rawOut[key]['power_C.imag'] oneDgPower = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key.startswith('Windmill_') and key.endswith('.csv'): vrA = rawOut[key]['voltage_A.real'] vrB = rawOut[key]['voltage_B.real'] vrC = rawOut[key]['voltage_C.real'] viA = rawOut[key]['voltage_A.imag'] viB = rawOut[key]['voltage_B.imag'] viC = rawOut[key]['voltage_C.imag'] crB = rawOut[key]['current_B.real'] crA = rawOut[key]['current_A.real'] crC = rawOut[key]['current_C.real'] ciA = rawOut[key]['current_A.imag'] ciB = rawOut[key]['current_B.imag'] ciC = rawOut[key]['current_C.imag'] powerA = vecProd(vecPyth(vrA,viA),vecPyth(crA,ciA)) powerB = vecProd(vecPyth(vrB,viB),vecPyth(crB,ciB)) powerC = vecProd(vecPyth(vrC,viC),vecPyth(crC,ciC)) # HACK: multiply by negative one because turbine power sign is opposite all other DG: oneDgPower = [-1.0 * x for x in hdmAgg(vecSum(powerA,powerB,powerC), avg, level)] if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key in ['OverheadLosses.csv', 'UndergroundLosses.csv', 'TriplexLosses.csv', 'TransformerLosses.csv']: realA = rawOut[key]['sum(power_losses_A.real)'] imagA = rawOut[key]['sum(power_losses_A.imag)'] realB = rawOut[key]['sum(power_losses_B.real)'] imagB = rawOut[key]['sum(power_losses_B.imag)'] realC = rawOut[key]['sum(power_losses_C.real)'] imagC = rawOut[key]['sum(power_losses_C.imag)'] oneLoss = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'Losses' not in cleanOut['Consumption']: cleanOut['Consumption']['Losses'] = oneLoss else: cleanOut['Consumption']['Losses'] = vecSum(oneLoss,cleanOut['Consumption']['Losses']) # Aggregate up the timestamps: if level=='days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, feederName, "allOutputData.json"),"w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, feederName, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, feederName,"PID.txt")) print "DONE RUNNING GRIDLABMULTI", modelDir, feederName except Exception as e: print "MODEL CRASHED GRIDLABMULTI", e, modelDir, feederName cancel(pJoin(modelDir, feederName)) with open(pJoin(modelDir, feederName, "stderr.txt"), "a+") as stderrFile: traceback.print_exc(file = stderrFile)
def runForeground(modelDir, inputDict): ''' Run the model in its directory. WARNING: GRIDLAB CAN TAKE HOURS TO COMPLETE. ''' # Check whether model exist or not if not os.path.isdir(modelDir): os.makedirs(modelDir) inputDict["created"] = str(datetime.datetime.now()) print "STARTING TO RUN", modelDir beginTime = datetime.datetime.now() feederList = [] # Get prepare of data and clean workspace if re-run, If re-run remove all the data in the subfolders for dirs in os.listdir(modelDir): if os.path.isdir(pJoin(modelDir, dirs)): shutil.rmtree(pJoin(modelDir, dirs)) # Get each feeder, prepare data in separate folders, and run there. for key in sorted(inputDict, key=inputDict.get): if key.startswith("feederName"): feederName = inputDict[key] feederList.append(feederName) try: os.remove(pJoin(modelDir, feederName, "allOutputData.json")) except Exception, e: pass if not os.path.isdir(pJoin(modelDir, feederName)): os.makedirs(pJoin(modelDir, feederName)) # create subfolders for feeders shutil.copy(pJoin(modelDir, feederName + ".omd"), pJoin(modelDir, feederName, "feeder.omd")) inputDict["climateName"], latforpvwatts = zipCodeToClimateName(inputDict["zipCode"]) shutil.copy(pJoin(__metaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, feederName, "climate.tmy2")) try: startTime = datetime.datetime.now() feederJson = json.load(open(pJoin(modelDir, feederName, "feeder.omd"))) tree = feederJson["tree"] # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) feeder.adjustTime(tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"]) # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem(tree, attachments=feederJson["attachments"], keepFiles=True, workDir=pJoin(modelDir, feederName)) cleanOut = {} # Std Err and Std Out cleanOut['stderr'] = rawOut['stderr'] cleanOut['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# property.. timestamp'] else: cleanOut['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get('timeStamps',[]) level = inputDict.get('simLengthUnits','hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): cleanOut['climate'] = {} cleanOut['climate']['Rain Fall (in/h)'] = hdmAgg(rawOut[key].get('rainfall'), sum, level) cleanOut['climate']['Wind Speed (m/s)'] = hdmAgg(rawOut[key].get('wind_speed'), avg, level) cleanOut['climate']['Temperature (F)'] = hdmAgg(rawOut[key].get('temperature'), max, level) cleanOut['climate']['Snow Depth (in)'] = hdmAgg(rawOut[key].get('snowdepth'), max, level) cleanOut['climate']['Direct Insolation (W/m^2)'] = hdmAgg(rawOut[key].get('solar_direct'), sum, level) # Voltage Band if 'VoltageJiggle.csv' in rawOut: cleanOut['allMeterVoltages'] = {} cleanOut['allMeterVoltages']['Min'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)']], min, level) cleanOut['allMeterVoltages']['Mean'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['StdDev'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['Max'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)']], max, level) cleanOut['allMeterVoltages']['stdDevPos'] = [(x+y/2) for x,y in zip(cleanOut['allMeterVoltages']['Mean'], cleanOut['allMeterVoltages']['StdDev'])] cleanOut['allMeterVoltages']['stdDevNeg'] = [(x-y/2) for x,y in zip(cleanOut['allMeterVoltages']['Mean'], cleanOut['allMeterVoltages']['StdDev'])] # Total # of meters count = 0 with open(pJoin(modelDir, feederName, "feeder.omd")) as f: for line in f: if "\"objectType\": \"triplex_meter\"" in line: count+=1 print "count=", count cleanOut['allMeterVoltages']['triplexMeterCount'] = float(count) # Power Consumption cleanOut['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads cleanOut['Consumption']['Power'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['DG'] = [0] * int(inputDict["simLength"]) for key in rawOut: if key.startswith('SwingKids_') and key.endswith('.csv'): oneSwingPower = hdmAgg(vecPyth(rawOut[key]['sum(power_in.real)'],rawOut[key]['sum(power_in.imag)']), avg, level) if 'Power' not in cleanOut['Consumption']: cleanOut['Consumption']['Power'] = oneSwingPower else: cleanOut['Consumption']['Power'] = vecSum(oneSwingPower,cleanOut['Consumption']['Power']) elif key.startswith('Inverter_') and key.endswith('.csv'): realA = rawOut[key]['power_A.real'] realB = rawOut[key]['power_B.real'] realC = rawOut[key]['power_C.real'] imagA = rawOut[key]['power_A.imag'] imagB = rawOut[key]['power_B.imag'] imagC = rawOut[key]['power_C.imag'] oneDgPower = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key.startswith('Windmill_') and key.endswith('.csv'): vrA = rawOut[key]['voltage_A.real'] vrB = rawOut[key]['voltage_B.real'] vrC = rawOut[key]['voltage_C.real'] viA = rawOut[key]['voltage_A.imag'] viB = rawOut[key]['voltage_B.imag'] viC = rawOut[key]['voltage_C.imag'] crB = rawOut[key]['current_B.real'] crA = rawOut[key]['current_A.real'] crC = rawOut[key]['current_C.real'] ciA = rawOut[key]['current_A.imag'] ciB = rawOut[key]['current_B.imag'] ciC = rawOut[key]['current_C.imag'] powerA = vecProd(vecPyth(vrA,viA),vecPyth(crA,ciA)) powerB = vecProd(vecPyth(vrB,viB),vecPyth(crB,ciB)) powerC = vecProd(vecPyth(vrC,viC),vecPyth(crC,ciC)) # HACK: multiply by negative one because turbine power sign is opposite all other DG: oneDgPower = [-1.0 * x for x in hdmAgg(vecSum(powerA,powerB,powerC), avg, level)] if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key in ['OverheadLosses.csv', 'UndergroundLosses.csv', 'TriplexLosses.csv', 'TransformerLosses.csv']: realA = rawOut[key]['sum(power_losses_A.real)'] imagA = rawOut[key]['sum(power_losses_A.imag)'] realB = rawOut[key]['sum(power_losses_B.real)'] imagB = rawOut[key]['sum(power_losses_B.imag)'] realC = rawOut[key]['sum(power_losses_C.real)'] imagC = rawOut[key]['sum(power_losses_C.imag)'] oneLoss = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'Losses' not in cleanOut['Consumption']: cleanOut['Consumption']['Losses'] = oneLoss else: cleanOut['Consumption']['Losses'] = vecSum(oneLoss,cleanOut['Consumption']['Losses']) # Aggregate up the timestamps: if level=='days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, feederName, "allOutputData.json"),"w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, feederName, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, feederName,"PID.txt")) print "DONE RUNNING GRIDLABMULTI", modelDir, feederName except Exception as e: print "MODEL CRASHED GRIDLABMULTI", e, modelDir, feederName cancel(pJoin(modelDir, feederName)) with open(pJoin(modelDir, feederName, "stderr.txt"), "a+") as stderrFile: traceback.print_exc(file = stderrFile)
def omfCalibrate(workDir, feederPath, scadaPath, simStartDate, simLength): '''calibrates a feeder and saves the calibrated tree at a location''' with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) # Process scada data. gridlabdDir = pJoin(workDir,"gridlabD") scadaSubPower = _processScadaData(gridlabdDir,scadaPath, simStartDate) # Force FBS powerflow, because NR fails a lot. for key in tree: if tree[key].get("module","").lower() == "powerflow": tree[key] = {"module":"powerflow","solver_method":"FBS"} # Attach player. classOb = {'omftype':'class player','argument':'{double value;}'} playerOb = {"object":"player", "property":"value", "name":"scadaLoads", "file":"subScada.player", "loop":"0"} maxKey = feeder.getMaxKey(tree) playerKey = maxKey + 2 tree[maxKey+1] = classOb tree[playerKey] = playerOb # Make loads reference player. loadTemplate = {"object": "triplex_load", "power_pf_12": "0.95", "impedance_pf_12": "0.98", "power_pf_12": "0.90", "impedance_fraction_12": "0.7", "power_fraction_12": "0.3"} loadTemplateR = {"object": "load", "impedance_pf_A": "0.98", "impedance_pf_B": "0.98", "impedance_pf_C": "0.98", "power_pf_A": "0.90", "power_pf_B": "0.90", "power_pf_C": "0.90", "impedance_fraction_A": "0.7", "impedance_fraction_B": "0.7", "impedance_fraction_C": "0.7", "power_fraction_A": "0.3", "power_fraction_B": "0.3", "power_fraction_C": "0.3"} for key in tree: ob = tree[key] if ob.get("object","") == "triplex_node" and ob.get("power_12","") != "": # Add to triplex_nodes. newOb = dict(loadTemplate) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") oldPow = ob.get("power_12","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower) tree[key] = newOb elif ob.get("object","") == "load": # Add to residential_loads too. newOb = dict(loadTemplateR) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["load_class"] = ob.get("load_class", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") try: oldPow = ob.get("constant_power_A","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_A"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_B","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_B"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_C","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_C"] = "scadaLoads.value*" + str(pythagPower) except: pass tree[key] = newOb # Convert swing bus to a meter. for key in tree: if tree[key].get('bustype','').lower() == 'swing' and tree[key].get('object','') != 'meter': swingName = tree[key].get('name') regIndex = key tree[key]['object'] = 'meter' # Search for the substation meter and attach a recorder there. for key in tree: if tree[key].get('bustype','').lower() == 'swing': swingName = tree[key].get('name') recOb = {"object": "recorder", "parent": swingName, "property": "measured_real_power,measured_reactive_power,measured_power", "file": "caliSub.csv", "interval": "900"} outputRecorderKey = maxKey + 3 tree[outputRecorderKey] = recOb feeder.adjustTime(tree, simLength, "hours", simStartDate['Date'].strftime("%Y-%m-%d %H:%M:%S")) # Run Gridlabd, calculate scaling constant. def runPowerflowIter(tree,scadaSubPower, iterationTimes): '''Runs powerflow once, then iterates.''' print "Running calibration powerflow #1." output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=gridlabdDir) outRealPow = output["caliSub.csv"]["measured_real_power"] outImagPower = output["caliSub.csv"]["measured_reactive_power"] outAppPowerKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPow, outImagPower)] lastFile = "subScada.player" nextFile = "subScadaCalibrated.player" nextPower = outAppPowerKw for i in range(1, iterationTimes+1): SCAL_CONST = sum(scadaSubPower[1:simLength])/sum(nextPower[1:simLength]) print "Running calibration powerflow (iteration", str(i+1), "of", iterationTimes+1,") (SCAL_CONST: ", SCAL_CONST,")" newPlayData = [] with open(pJoin(gridlabdDir, lastFile), "r") as playerFile: for line in playerFile: (key,val) = line.split(',') newPlayData.append(str(key) + ',' + str(float(val)*SCAL_CONST) + "\n") with open(pJoin(gridlabdDir, nextFile), "w") as playerFile: for row in newPlayData: playerFile.write(row) tree[playerKey]["file"] = nextFile tree[outputRecorderKey]["file"] = "caliSubCheck.csv" nextOutput = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=gridlabdDir) outRealPow2nd = nextOutput["caliSubCheck.csv"]["measured_real_power"] outImagPower2nd = nextOutput["caliSubCheck.csv"]["measured_reactive_power"] nextAppKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPow2nd, outImagPower2nd)] lastFile = nextFile nextFile = "subScadaCalibrated"+str(i)+".player" nextPower = outAppPowerKw return outRealPow, outRealPow2nd, lastFile iterationTimes = 1 outRealPow, outRealPow2nd, lastFile = runPowerflowIter(tree,scadaSubPower,iterationTimes) caliPowVectors = [[float(element) for element in scadaSubPower[1:simLength]], [float(element)/1000 for element in outRealPow[1:simLength]], [float(element)/1000 for element in outRealPow2nd[1:simLength]]] labels = ["scadaSubPower","initialGuess","finalGuess"] colors = ['red','lightblue','blue'] chartData = {"Title":"Substation Calibration Check (Iterated "+str(iterationTimes+1)+"X)", "fileName":"caliCheckPlot", "colors":colors,"labels":labels, "timeZone":simStartDate['timeZone']} plotLine(workDir, caliPowVectors, chartData, simStartDate['Date']+dt.timedelta(hours=1), 'hours') # Write the final output. with open(pJoin(workDir,"calibratedFeeder.json"),"w") as outJson: playerString = open(pJoin(gridlabdDir,lastFile)).read() feederJson["attachments"][lastFile] = playerString feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return
def heavyProcessing(modelDir, inputDict): ''' Run the model in its directory. WARNING: GRIDLAB CAN TAKE HOURS TO COMPLETE. ''' print "STARTING TO RUN", modelDir beginTime = datetime.datetime.now() # Get feeder name and data in. try: os.mkdir(pJoin(modelDir, 'gldContainer')) except: pass feederDir, feederName = inputDict["feederName"].split("___") shutil.copy( pJoin(__metaModel__._omfDir, "data", "Feeder", feederDir, feederName + ".json"), pJoin(modelDir, "feeder.json")) shutil.copy( pJoin(__metaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, "gldContainer", "climate.tmy2")) try: startTime = datetime.datetime.now() feederJson = json.load(open(pJoin(modelDir, "feeder.json"))) tree = feederJson["tree"] # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) # Attach recorders for system voltage map: stub = { 'object': 'group_recorder', 'group': '"class=node"', 'property': 'voltage_A', 'interval': 3600, 'file': 'aVoltDump.csv' } for phase in ['A', 'B', 'C']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'VoltDump.csv' tree[feeder.getMaxKey(tree) + 1] = copyStub feeder.adjustTime(tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"]) # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem( tree, attachments=feederJson["attachments"], keepFiles=True, workDir=pJoin(modelDir, 'gldContainer')) cleanOut = {} # Std Err and Std Out cleanOut['stderr'] = rawOut['stderr'] cleanOut['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# property.. timestamp'] else: cleanOut['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get('timeStamps', []) level = inputDict.get('simLengthUnits', 'hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): cleanOut['climate'] = {} cleanOut['climate']['Rain Fall (in/h)'] = hdmAgg( rawOut[key].get('rainfall'), sum, level) cleanOut['climate']['Wind Speed (m/s)'] = hdmAgg( rawOut[key].get('wind_speed'), avg, level) cleanOut['climate']['Temperature (F)'] = hdmAgg( rawOut[key].get('temperature'), max, level) cleanOut['climate']['Snow Depth (in)'] = hdmAgg( rawOut[key].get('snowdepth'), max, level) cleanOut['climate']['Direct Normal (W/sf)'] = hdmAgg( rawOut[key].get('solar_direct'), sum, level) #cleanOut['climate']['Global Horizontal (W/sf)'] = hdmAgg(rawOut[key].get('solar_global'), sum, level) climateWbySFList = hdmAgg(rawOut[key].get('solar_global'), sum, level) #converting W/sf to W/sm climateWbySMList = [x * 10.76392 for x in climateWbySFList] cleanOut['climate'][ 'Global Horizontal (W/sm)'] = climateWbySMList # Voltage Band if 'VoltageJiggle.csv' in rawOut: cleanOut['allMeterVoltages'] = {} cleanOut['allMeterVoltages']['Min'] = hdmAgg([ float(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)'] ], min, level) cleanOut['allMeterVoltages']['Mean'] = hdmAgg([ float(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)'] ], avg, level) cleanOut['allMeterVoltages']['StdDev'] = hdmAgg([ float(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)'] ], avg, level) cleanOut['allMeterVoltages']['Max'] = hdmAgg([ float(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)'] ], max, level) # Power Consumption cleanOut['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads cleanOut['Consumption']['Power'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['DG'] = [0] * int(inputDict["simLength"]) for key in rawOut: if key.startswith('SwingKids_') and key.endswith('.csv'): oneSwingPower = hdmAgg( vecPyth(rawOut[key]['sum(power_in.real)'], rawOut[key]['sum(power_in.imag)']), avg, level) if 'Power' not in cleanOut['Consumption']: cleanOut['Consumption']['Power'] = oneSwingPower else: cleanOut['Consumption']['Power'] = vecSum( oneSwingPower, cleanOut['Consumption']['Power']) elif key.startswith('Inverter_') and key.endswith('.csv'): realA = rawOut[key]['power_A.real'] realB = rawOut[key]['power_B.real'] realC = rawOut[key]['power_C.real'] imagA = rawOut[key]['power_A.imag'] imagB = rawOut[key]['power_B.imag'] imagC = rawOut[key]['power_C.imag'] oneDgPower = hdmAgg( vecSum(vecPyth(realA, imagA), vecPyth(realB, imagB), vecPyth(realC, imagC)), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum( oneDgPower, cleanOut['Consumption']['DG']) elif key.startswith('Windmill_') and key.endswith('.csv'): vrA = rawOut[key]['voltage_A.real'] vrB = rawOut[key]['voltage_B.real'] vrC = rawOut[key]['voltage_C.real'] viA = rawOut[key]['voltage_A.imag'] viB = rawOut[key]['voltage_B.imag'] viC = rawOut[key]['voltage_C.imag'] crB = rawOut[key]['current_B.real'] crA = rawOut[key]['current_A.real'] crC = rawOut[key]['current_C.real'] ciA = rawOut[key]['current_A.imag'] ciB = rawOut[key]['current_B.imag'] ciC = rawOut[key]['current_C.imag'] powerA = vecProd(vecPyth(vrA, viA), vecPyth(crA, ciA)) powerB = vecProd(vecPyth(vrB, viB), vecPyth(crB, ciB)) powerC = vecProd(vecPyth(vrC, viC), vecPyth(crC, ciC)) oneDgPower = hdmAgg(vecSum(powerA, powerB, powerC), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum( oneDgPower, cleanOut['Consumption']['DG']) elif key in [ 'OverheadLosses.csv', 'UndergroundLosses.csv', 'TriplexLosses.csv', 'TransformerLosses.csv' ]: realA = rawOut[key]['sum(power_losses_A.real)'] imagA = rawOut[key]['sum(power_losses_A.imag)'] realB = rawOut[key]['sum(power_losses_B.real)'] imagB = rawOut[key]['sum(power_losses_B.imag)'] realC = rawOut[key]['sum(power_losses_C.real)'] imagC = rawOut[key]['sum(power_losses_C.imag)'] oneLoss = hdmAgg( vecSum(vecPyth(realA, imagA), vecPyth(realB, imagB), vecPyth(realC, imagC)), avg, level) if 'Losses' not in cleanOut['Consumption']: cleanOut['Consumption']['Losses'] = oneLoss else: cleanOut['Consumption']['Losses'] = vecSum( oneLoss, cleanOut['Consumption']['Losses']) elif key.startswith('Regulator_') and key.endswith('.csv'): #split function to strip off .csv from filename and user rest of the file name as key. for example- Regulator_VR10.csv -> key would be Regulator_VR10 regName = "" regName = key newkey = regName.split(".")[0] cleanOut[newkey] = {} cleanOut[newkey]['RegTapA'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapB'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapC'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapA'] = rawOut[key]['tap_A'] cleanOut[newkey]['RegTapB'] = rawOut[key]['tap_B'] cleanOut[newkey]['RegTapC'] = rawOut[key]['tap_C'] cleanOut[newkey]['RegPhases'] = rawOut[key]['phases'][0] elif key.startswith('Capacitor_') and key.endswith('.csv'): capName = "" capName = key newkey = capName.split(".")[0] cleanOut[newkey] = {} cleanOut[newkey]['Cap1A'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1B'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1C'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1A'] = rawOut[key]['switchA'] cleanOut[newkey]['Cap1B'] = rawOut[key]['switchB'] cleanOut[newkey]['Cap1C'] = rawOut[key]['switchC'] cleanOut[newkey]['CapPhases'] = rawOut[key]['phases'][0] # What percentage of our keys have lat lon data? latKeys = [ tree[key]['latitude'] for key in tree if 'latitude' in tree[key] ] latPerc = 1.0 * len(latKeys) / len(tree) if latPerc < 0.25: doNeato = True else: doNeato = False # Generate the frames for the system voltage map time traveling chart. genTime = generateVoltChart(tree, rawOut, modelDir, neatoLayout=doNeato) cleanOut['genTime'] = genTime # Aggregate up the timestamps: if level == 'days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x: x[0][0:10], 'days') elif level == 'months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x: x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, "allOutputData.json"), "w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str( datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"), "w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, "gldContainer", "PID.txt")) print "DONE RUNNING", modelDir except Exception as e: print "MODEL CRASHED", e # Cancel to get rid of extra background processes. try: os.remove(pJoin(modelDir, 'PPID.txt')) except: pass thisErr = traceback.format_exc() inputDict['stderr'] = thisErr with open(os.path.join(modelDir, 'stderr.txt'), 'w') as errorFile: errorFile.write(thisErr) # Dump input with error included. with open(pJoin(modelDir, "allInputData.json"), "w") as inFile: json.dump(inputDict, inFile, indent=4) finishTime = datetime.datetime.now() inputDict["runTime"] = str( datetime.timedelta(seconds=int((finishTime - beginTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"), "w") as inFile: json.dump(inputDict, inFile, indent=4) try: os.remove(pJoin(modelDir, "PPID.txt")) except: pass
def runForeground(modelDir,inData): '''This reads a glm file, changes the method of powerflow and reruns''' try: startTime = datetime.now() #calibrate and run cvrdynamic feederPath = pJoin(__metaModel__._omfDir,"data", "Feeder", inData["feederName"].split("___")[0], inData["feederName"].split("___")[1]+'.json') scadaPath = pJoin(__metaModel__._omfDir,"uploads",(inData["scadaFile"]+'.tsv')) calibrate.omfCalibrate(modelDir,feederPath,scadaPath) allOutput = {} print "here" with open(pJoin(modelDir,"calibratedFeeder.json"), "r") as jsonIn: feederJson = json.load(jsonIn) localTree = feederJson.get("tree", {}) for key in localTree: if "solver_method" in localTree[key].keys(): print "current solver method", localTree[key]["solver_method"] localTree[key]["solver_method"] = 'FBS' #find the swing bus and recorder attached to substation for key in localTree: if localTree[key].get('bustype','').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') if localTree[key].get('object','') == 'regulator' and localTree[key].get('from','') == swingName: regIndex = key regConfName = localTree[key]['configuration'] #find the regulator and capacitor names and combine to form a string for volt-var control object regKeys = [] accum_reg = "" for key in localTree: if localTree[key].get("object","") == "regulator": accum_reg += localTree[key].get("name","ERROR") + "," regKeys.append(key) regstr = accum_reg[:-1] print regKeys capKeys = [] accum_cap = "" for key in localTree: if localTree[key].get("object","") == "capacitor": accum_cap += localTree[key].get("name","ERROR") + "," capKeys.append(key) if localTree[key].get("control","").lower() == "manual": localTree[key]['control'] = "VOLT" print "changing capacitor control from manual to volt" capstr = accum_cap[:-1] print capKeys # Attach recorders relevant to CVR. recorders = [ {'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': localTree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag'}, {'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)'}, {'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': localTree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C'}, {'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': localTree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C'}] #recorder object for capacitor switching - if capacitors exist if capKeys != []: for key in capKeys: recorders.append({'object': 'recorder', 'file': 'ZcapSwitch' + str(key) + '.csv', 'limit': '0', 'parent': localTree[key]['name'], 'property': 'switchA,switchB,switchC'}) #attach recorder process biggest = 1 + max([int(k) for k in localTree.keys()]) for index, rec in enumerate(recorders): localTree[biggest + index] = rec #run a reference load flow HOURS = float(inData['simLengthHours']) simStartDate = inData['simStart'] feeder.adjustTime(localTree,HOURS,"hours",simStartDate) output = gridlabd.runInFilesystem(localTree,keepFiles=False,workDir=modelDir) os.remove(pJoin(modelDir,"PID.txt")) p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] #calculating length of simulation because it migth be different from the simulation input HOURS simRealLength = int(len(p)) #time delays from configuration files time_delay_reg = '30.0' time_delay_cap = '300.0' for key in localTree: if localTree[key].get('object','') == "regulator_configuration": time_delay_reg = localTree[key]['time_delay'] print "time_delay_reg",time_delay_reg # if localTree[key].get('object','') == "capacitor": # time_delay_cap = localTree[key]['time_delay'] # print "time_delay_cap",time_delay_cap #change the recorder names for key in localTree: if localTree[key].get('object','') == "collector" or localTree[key].get('object','') == "recorder": if localTree[key].get('file','').startswith('Z'): localTree[key]['file'] = localTree[key].get('file','').replace('Z','NewZ') #create volt-var control object max_key = max([int(key) for key in localTree.keys()]) print max_key localTree[max_key+1] = {'object' : 'volt_var_control', 'name' : 'IVVC1', 'control_method' : 'ACTIVE', 'capacitor_delay' : str(time_delay_cap), 'regulator_delay' : str(time_delay_reg), 'desired_pf' : '0.99', 'd_max' : '0.6', 'd_min' : '0.1', 'substation_link' : str(localTree[regIndex]['name']), 'regulator_list' : regstr, 'capacitor_list': capstr} #running powerflow analysis via gridalab after attaching a regulator feeder.adjustTime(localTree,HOURS,"hours",simStartDate) output1 = gridlabd.runInFilesystem(localTree,keepFiles=True,workDir=modelDir) os.remove(pJoin(modelDir,"PID.txt")) pnew = output1['NewZregulator.csv']['power_in.real'] qnew = output1['NewZregulator.csv']['power_in.imag'] #total real and imaginary losses as a function of time def vecSum(u,v): ''' Add vectors u and v element-wise. Return has len <= len(u) and <=len(v). ''' return map(sum, zip(u,v)) def zeroVec(length): ''' Give a zero vector of input length. ''' return [0 for x in xrange(length)] (realLoss, imagLoss, realLossnew, imagLossnew) = (zeroVec(int(HOURS)) for x in range(4)) for device in ['ZlossesOverhead.csv','ZlossesTransformer.csv','ZlossesUnderground.csv']: for letter in ['A','B','C']: realLoss = vecSum(realLoss, output[device]['sum(power_losses_' + letter + '.real)']) imagLoss = vecSum(imagLoss, output[device]['sum(power_losses_' + letter + '.imag)']) realLossnew = vecSum(realLossnew, output1['New'+device]['sum(power_losses_' + letter + '.real)']) imagLossnew = vecSum(imagLossnew, output1['New'+device]['sum(power_losses_' + letter + '.imag)']) #voltage calculations and tap calculations def divby2(u): '''divides by 2''' return u/2 lowVoltage = [] meanVoltage = [] highVoltage = [] lowVoltagenew = [] meanVoltagenew = [] highVoltagenew = [] tap = {'A':[],'B':[],'C':[]} tapnew = {'A':[],'B':[],'C':[]} volt = {'A':[],'B':[],'C':[]} voltnew = {'A':[],'B':[],'C':[]} switch = {'A':[],'B':[],'C':[]} switchnew = {'A':[],'B':[],'C':[]} for letter in ['A','B','C']: tap[letter] = output['Zregulator.csv']['tap_' + letter] tapnew[letter] = output1['NewZregulator.csv']['tap_' + letter] if capKeys != []: switch[letter] = output['ZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch'+ letter] switchnew[letter] = output1['NewZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch'+ letter] volt[letter] = map(returnMag,output['ZsubstationBottom.csv']['voltage_'+letter]) voltnew[letter] = map(returnMag,output1['NewZsubstationBottom.csv']['voltage_'+letter]) lowVoltage = map(divby2,output['ZvoltageJiggle.csv']['min(voltage_12.mag)']) lowVoltagenew = map(divby2,output1['NewZvoltageJiggle.csv']['min(voltage_12.mag)']) meanVoltage = map(divby2,output['ZvoltageJiggle.csv']['mean(voltage_12.mag)']) meanVoltagenew = map(divby2,output1['NewZvoltageJiggle.csv']['mean(voltage_12.mag)']) highVoltage = map(divby2,output['ZvoltageJiggle.csv']['max(voltage_12.mag)']) highVoltagenew = map(divby2,output1['NewZvoltageJiggle.csv']['max(voltage_12.mag)']) #energy calculations whEnergy = [] whLosses = [] whLoads = [] whEnergy.append(sum(p)/10**6) whLosses.append(sum(realLoss)/10**6) whLoads.append((sum(p)-sum(realLoss))/10**6) whEnergy.append(sum(pnew)/10**6) whLosses.append(sum(realLossnew)/10**6) whLoads.append((sum(pnew)-sum(realLossnew))/10**6) indices = ['No IVVC', 'With IVVC'] # energySalesRed = (whLoads[1]-whLoads[0])*(inData['wholesaleEnergyCostPerKwh'])*1000 # lossSav = (whLosses[0]-whLosses[1])*inData['wholesaleEnergyCostPerKwh']*1000 # print energySalesRed, lossSav #plots ticks = [] plt.clf() plt.title("total energy") plt.ylabel("total load and losses (MWh)") for element in range(2): ticks.append(element) bar_loss = plt.bar(element, whLosses[element], 0.15, color= 'red') bar_load = plt.bar(element+0.15, whLoads[element], 0.15, color= 'orange') plt.legend([bar_load[0],bar_loss[0]],['total load', 'total losses'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.xticks([t+0.15 for t in ticks],indices) plt.savefig(pJoin(modelDir,"totalEnergy.png")) #real and imaginary power plt.figure("real power") plt.title("Real Power at substation") plt.ylabel("substation real power (MW)") pMW = [element/10**6 for element in p] pMWn = [element/10**6 for element in pnew] pw = plt.plot(pMW) npw = plt.plot(pMWn) plt.legend([pw[0], npw[0]], ['NO IVVC','WITH IVVC'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir,"realPower.png")) plt.figure("Reactive power") plt.title("Reactive Power at substation") plt.ylabel("substation reactive power (MVAR)") qMVAR = [element/10**6 for element in q] qMVARn = [element/10**6 for element in qnew] iw = plt.plot(qMVAR) niw = plt.plot(qMVARn) plt.legend([iw[0], niw[0]], ['NO IVVC','WITH IVVC'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir,"imaginaryPower.png")) #voltage plots plt.figure("voltages as a function of time") f,ax = plt.subplots(2,sharex=True) f.suptitle("Min and Max voltages on the feeder") lv = ax[0].plot(lowVoltage,color = 'cadetblue') mv = ax[0].plot(meanVoltage,color = 'blue') hv = ax[0].plot(highVoltage, color = 'cadetblue') ax[0].legend([lv[0], mv[0], hv[0]], ['low voltage','mean voltage','high voltage'],bbox_to_anchor=(0., 0.915, 1., .1), loc=3, ncol=3, mode="expand", borderaxespad=0.1) ax[0].set_ylabel('NO IVVC') nlv = ax[1].plot(lowVoltagenew,color = 'cadetblue') nmv = ax[1].plot(meanVoltagenew,color = 'blue') nhv = ax[1].plot(highVoltagenew, color = 'cadetblue') ax[1].set_ylabel('WITH IVVC') plt.savefig(pJoin(modelDir,"Voltages.png")) #tap positions plt.figure("TAP positions NO IVVC") f,ax = plt.subplots(6,sharex=True) f.set_size_inches(10,12.0) #f.suptitle("Regulator Tap positions") ax[0].plot(tap['A']) ax[0].set_title("Regulator Tap positions NO IVVC") ax[0].set_ylabel("TAP A") ax[1].plot(tap['B']) ax[1].set_ylabel("TAP B") ax[2].plot(tap['C']) ax[2].set_ylabel("TAP C") ax[3].plot(tapnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("TAP A") ax[4].plot(tapnew['B']) ax[4].set_ylabel("TAP B") ax[5].plot(tapnew['C']) ax[5].set_ylabel("TAP C") for subplot in range(6): ax[subplot].set_ylim(-20,20) f.tight_layout() plt.savefig(pJoin(modelDir,"RegulatorTAPpositions.png")) #substation voltages plt.figure("substation voltage as a function of time") f,ax = plt.subplots(6,sharex=True) f.set_size_inches(10,12.0) #f.suptitle("voltages at substation NO IVVC") ax[0].plot(volt['A']) ax[0].set_title('Substation voltages NO IVVC') ax[0].set_ylabel('voltage A') ax[1].plot(volt['B']) ax[1].set_ylabel('voltage B') ax[2].plot(volt['C']) ax[2].set_ylabel('voltage C') ax[3].plot(voltnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel('voltage A') ax[4].plot(voltnew['B']) ax[4].set_ylabel('voltage B') ax[5].plot(voltnew['C']) ax[5].set_ylabel('voltage C') f.tight_layout() plt.savefig(pJoin(modelDir,"substationVoltages.png")) #cap switches plt.figure("capacitor switch state as a function of time") f,ax = plt.subplots(6,sharex=True) f.set_size_inches(10,12.0) #f.suptitle("Capacitor switch state NO IVVC") ax[0].plot(switch['A']) ax[0].set_title("Capacitor switch state NO IVVC") ax[0].set_ylabel("switch A") ax[1].plot(switch['B']) ax[1].set_ylabel("switch B") ax[2].plot(switch['C']) ax[2].set_ylabel("switch C") ax[3].plot(switchnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("switch A") ax[4].plot(switchnew['B']) ax[4].set_ylabel("switch B") ax[5].plot(switchnew['C']) ax[5].set_ylabel("switch C") for subplot in range(6): ax[subplot].set_ylim(-2,2) f.tight_layout() plt.savefig(pJoin(modelDir,"capacitorSwitch.png")) #plt.show() #monetization monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] monthToSeason = {'January':'Winter','February':'Winter','March':'Spring','April':'Spring', 'May':'Spring','June':'Summer','July':'Summer','August':'Summer', 'September':'Fall','October':'Fall','November':'Fall','December':'Winter'} #calculate the month and hour of simulation start and month and hour of simulation end simStartTimestamp = simStartDate + " 00:00:00" simFormattedDate = datetime.strptime(simStartTimestamp,"%Y-%m-%d %H:%M:%S") simStartMonthNum = int(simFormattedDate.strftime('%m')) simstartMonth = monthNames[simStartMonthNum-1] simStartDay = int(simFormattedDate.strftime('%d')) if calendar.isleap(int(simFormattedDate.strftime('%Y'))): febDays = 29 else: febDays = 28 monthHours = [int(31*24),int(febDays*24),int(31*24),int(30*24),int(31*24),int(30*24),int(31*24),int(31*24),int(30*24),int(31*24),int(30*24),int(31*24)] simStartIndex = int(sum(monthHours[:(simStartMonthNum-1)])+(simStartDay-1)*24) temp = 0 cumulHours = [0] for x in range(12): temp += monthHours[x] cumulHours.append(temp) for i in range((simStartMonthNum),13): if int(simStartIndex+simRealLength)<=cumulHours[i] and int(simStartIndex+simRealLength)>cumulHours[i-1]: simEndMonthNum = i-1 simEndMonth = monthNames[simEndMonthNum] print simstartMonth,simEndMonth #calculate peaks for the number of months in simulation previndex = 0 monthPeak = {} monthPeakNew = {} peakSaveDollars = {} energyLostDollars = {} lossRedDollars = {} simMonthList = monthNames[monthNames.index(simstartMonth):(monthNames.index(simEndMonth)+1)] print simMonthList for monthElement in simMonthList: print monthElement month = monthNames.index(monthElement) index1 = int(previndex) index2 = int(min((index1 + int(monthHours[month])), simRealLength)) monthPeak[monthElement] = max(p[index1:index2])/1000.0 monthPeakNew[monthElement] = max(pnew[index1:index2])/1000.0 peakSaveDollars[monthElement] = (monthPeak[monthElement]-monthPeakNew[monthElement])*float(inData['peakDemandCost'+str(monthToSeason[monthElement])+'PerKw']) lossRedDollars[monthElement] = (sum(realLoss[index1:index2])/1000.0 - sum(realLossnew[index1:index2])/1000.0)*(float(inData['wholesaleEnergyCostPerKwh'])) energyLostDollars[monthElement] = (sum(p[index1:index2])/1000.0 - sum(pnew[index1:index2])/1000.0 - sum(realLoss[index1:index2])/1000.0 + sum(realLossnew[index1:index2])/1000.0 )*(float(inData['wholesaleEnergyCostPerKwh']) - float(inData['retailEnergyCostPerKwh'])) previndex = index2 #money charts fig = plt.figure("cost benefit barchart",figsize=(10,8)) ticks = range(len(simMonthList)) ticks1 = [element+0.15 for element in ticks] ticks2 = [element+0.30 for element in ticks] print ticks eld = [energyLostDollars[month] for month in simMonthList] lrd = [lossRedDollars[month] for month in simMonthList] psd = [peakSaveDollars[month] for month in simMonthList] bar_eld = plt.bar(ticks,eld,0.15,color='red') bar_psd = plt.bar(ticks1,psd,0.15,color='blue') bar_lrd = plt.bar(ticks2,lrd,0.15,color='green') plt.legend([bar_eld[0], bar_psd[0], bar_lrd[0]], ['energyLostDollars','peakReductionDollars','lossReductionDollars'],bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) monShort = [element[0:3] for element in simMonthList] plt.xticks([t+0.15 for t in ticks],monShort) plt.ylabel('Utility Savings ($)') plt.savefig(pJoin(modelDir,"spendChart.png")) #cumulative savings graphs fig = plt.figure("cost benefit barchart",figsize=(10,5)) annualSavings = sum(eld) + sum(lrd) + sum(psd) annualSave = lambda x:(annualSavings - float(inData['omCost'])) * x - float(inData['capitalCost']) simplePayback = float(inData['capitalCost'])/(annualSavings - float(inData['omCost'])) plt.xlabel('Year After Installation') plt.xlim(0,30) plt.ylabel('Cumulative Savings ($)') plt.plot([0 for x in range(31)],c='gray') plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--') plt.plot([annualSave(x) for x in range(31)], c='green') plt.savefig(pJoin(modelDir,"savingsChart.png")) #get exact time stamps from the CSV files generated by Gridlab-D timeWithZone = output['Zregulator.csv']['# timestamp'] timestamps = [element[:19] for element in timeWithZone] #data for highcharts allOutput["timeStamps"] = timestamps allOutput["noCVRPower"] = p allOutput["withCVRPower"] = pnew allOutput["noCVRLoad"] = whLoads[0] allOutput["withCVRLoad"] = whLoads[1] allOutput["noCVRLosses"] = whLosses[0] allOutput["withCVRLosses"] = whLosses[1] allOutput["noCVRTaps"] = tap allOutput["withCVRTaps"] = tapnew allOutput["noCVRSubVolts"] = volt allOutput["withCVRSubVolts"] = voltnew allOutput["noCVRCapSwitch"] = switch allOutput["withCVRCapSwitch"] = switchnew allOutput["noCVRHighVolt"] = highVoltage allOutput["withCVRHighVolt"] = highVoltagenew allOutput["noCVRLowVolt"] = lowVoltage allOutput["withCVRLowVolt"] = lowVoltagenew allOutput["noCVRMeanVolt"] = meanVoltage allOutput["withCVRMeanVolt"] = meanVoltagenew #monetization allOutput["simMonthList"] = monShort allOutput["energyLostDollars"] = energyLostDollars allOutput["lossRedDollars"] = lossRedDollars allOutput["peakSaveDollars"] = peakSaveDollars allOutput["annualSave"] = [annualSave(x) for x in range(31)] # Update the runTime in the input file. endTime = datetime.now() inData["runTime"] = str(timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir,"allInputData.json"),"w") as inFile: json.dump(inData, inFile, indent=4) with open(pJoin(modelDir,"allOutputData.json"),"w") as outFile: json.dump(allOutput, outFile, indent=4) # For autotest, there won't be such file. try: os.remove(pJoin(modelDir, "PPID.txt")) except: pass print "DONE RUNNING", modelDir except Exception as e: print "Oops, Model Crashed!!!" cancel(modelDir) print e
def runForeground(modelDir, inData): '''This reads a glm file, changes the method of powerflow and reruns''' print "STARTING TO RUN", modelDir try: startTime = datetime.now() if not os.path.isdir(modelDir): os.makedirs(modelDir) inData["created"] = str(startTime) #read pre-calibrated feeder and run cvrdynamic feederName = inData.get('feederName1', 'feeder1') feederPath = pJoin(modelDir, feederName + '.omd') # Reads a pre-calibrated feeder. allOutput = {} with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) localTree = feederJson.get("tree", {}) attachments = feederJson.get("attachments", {}) for key in localTree: if "solver_method" in localTree[key].keys(): # print "current solver method", localTree[key]["solver_method"] localTree[key]["solver_method"] = 'FBS' #find the swing bus and recorder attached to substation try: for key in localTree: if localTree[key].get('bustype', '').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') if localTree[key].get( 'object', '') == 'regulator' and localTree[key].get( 'from', '') == swingName: regIndex = key regConfName = localTree[key]['configuration'] except: raise ValueError('Invalid feeder selected:', str(inData["feederName1"])) #find the regulator and capacitor names and combine to form a string for volt-var control object regKeys = [] accum_reg = "" for key in localTree: if localTree[key].get("object", "") == "regulator": accum_reg += localTree[key].get("name", "ERROR") + "," regKeys.append(key) regstr = accum_reg[:-1] # print regKeys capKeys = [] accum_cap = "" for key in localTree: if localTree[key].get("object", "") == "capacitor": accum_cap += localTree[key].get("name", "ERROR") + "," capKeys.append(key) if localTree[key].get("control", "").lower() == "manual": localTree[key]['control'] = "VOLT" # print "changing capacitor control from manual to volt" capstr = accum_cap[:-1] # print capKeys # Attach recorders relevant to CVR. recorders = [{ 'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': localTree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag' }, { 'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)' }, { 'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': localTree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C' }, { 'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': localTree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C' }] #recorder object for capacitor switching - if capacitors exist if capKeys != []: for key in capKeys: recorders.append({ 'object': 'recorder', 'file': 'ZcapSwitch' + str(key) + '.csv', 'limit': '0', 'parent': localTree[key]['name'], 'property': 'switchA,switchB,switchC' }) #attach recorder process biggest = 1 + max([int(k) for k in localTree.keys()]) for index, rec in enumerate(recorders): localTree[biggest + index] = rec #run a reference load flow HOURS = float(inData['simLengthHours']) simStartDate = inData['simStart'] feeder.adjustTime(localTree, HOURS, "hours", simStartDate) output = gridlabd.runInFilesystem(localTree, attachments, keepFiles=False, workDir=modelDir) try: os.remove(pJoin(modelDir, "PID.txt")) except: pass p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] #calculating length of simulation because it migth be different from the simulation input HOURS simRealLength = int(len(p)) #time delays from configuration files time_delay_reg = '30.0' time_delay_cap = '300.0' for key in localTree: if localTree[key].get('object', '') == "regulator_configuration": time_delay_reg = localTree[key]['time_delay'] # print "time_delay_reg",time_delay_reg # if localTree[key].get('object','') == "capacitor": # time_delay_cap = localTree[key]['time_delay'] # print "time_delay_cap",time_delay_cap #change the recorder names for key in localTree: if localTree[key].get('object', '') == "collector" or localTree[key].get( 'object', '') == "recorder": if localTree[key].get('file', '').startswith('Z'): localTree[key]['file'] = localTree[key].get( 'file', '').replace('Z', 'NewZ') #create volt-var control object max_key = max([int(key) for key in localTree.keys()]) # print max_key localTree[max_key + 1] = { 'object': 'volt_var_control', 'name': 'IVVC1', 'control_method': 'ACTIVE', 'capacitor_delay': str(time_delay_cap), 'regulator_delay': str(time_delay_reg), 'desired_pf': '0.99', 'd_max': '0.6', 'd_min': '0.1', 'substation_link': str(localTree[regIndex]['name']), 'regulator_list': regstr, 'capacitor_list': capstr, 'voltage_measurements': str(inData.get("voltageNodes", 0)), } #running powerflow analysis via gridalab after attaching a regulator feeder.adjustTime(localTree, HOURS, "hours", simStartDate) output1 = gridlabd.runInFilesystem(localTree, attachments, keepFiles=True, workDir=modelDir) os.remove(pJoin(modelDir, "PID.txt")) pnew = output1['NewZregulator.csv']['power_in.real'] qnew = output1['NewZregulator.csv']['power_in.imag'] #total real and imaginary losses as a function of time def vecSum(u, v): ''' Add vectors u and v element-wise. Return has len <= len(u) and <=len(v). ''' return map(sum, zip(u, v)) def zeroVec(length): ''' Give a zero vector of input length. ''' return [0 for x in xrange(length)] (realLoss, imagLoss, realLossnew, imagLossnew) = (zeroVec(int(HOURS)) for x in range(4)) for device in [ 'ZlossesOverhead.csv', 'ZlossesTransformer.csv', 'ZlossesUnderground.csv' ]: for letter in ['A', 'B', 'C']: realLoss = vecSum( realLoss, output[device]['sum(power_losses_' + letter + '.real)']) imagLoss = vecSum( imagLoss, output[device]['sum(power_losses_' + letter + '.imag)']) realLossnew = vecSum( realLossnew, output1['New' + device]['sum(power_losses_' + letter + '.real)']) imagLossnew = vecSum( imagLossnew, output1['New' + device]['sum(power_losses_' + letter + '.imag)']) #voltage calculations and tap calculations def divby2(u): '''divides by 2''' return u / 2 lowVoltage = [] meanVoltage = [] highVoltage = [] lowVoltagenew = [] meanVoltagenew = [] highVoltagenew = [] tap = {'A': [], 'B': [], 'C': []} tapnew = {'A': [], 'B': [], 'C': []} volt = {'A': [], 'B': [], 'C': []} voltnew = {'A': [], 'B': [], 'C': []} switch = {'A': [], 'B': [], 'C': []} switchnew = {'A': [], 'B': [], 'C': []} for letter in ['A', 'B', 'C']: tap[letter] = output['Zregulator.csv']['tap_' + letter] tapnew[letter] = output1['NewZregulator.csv']['tap_' + letter] if capKeys != []: switch[letter] = output['ZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch' + letter] switchnew[letter] = output1['NewZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch' + letter] volt[letter] = map( returnMag, output['ZsubstationBottom.csv']['voltage_' + letter]) voltnew[letter] = map( returnMag, output1['NewZsubstationBottom.csv']['voltage_' + letter]) lowVoltage = map(divby2, output['ZvoltageJiggle.csv']['min(voltage_12.mag)']) lowVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['min(voltage_12.mag)']) meanVoltage = map(divby2, output['ZvoltageJiggle.csv']['mean(voltage_12.mag)']) meanVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['mean(voltage_12.mag)']) highVoltage = map(divby2, output['ZvoltageJiggle.csv']['max(voltage_12.mag)']) highVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['max(voltage_12.mag)']) #energy calculations whEnergy = [] whLosses = [] whLoads = [] whEnergy.append(sum(p) / 10**6) whLosses.append(sum(realLoss) / 10**6) whLoads.append((sum(p) - sum(realLoss)) / 10**6) whEnergy.append(sum(pnew) / 10**6) whLosses.append(sum(realLossnew) / 10**6) whLoads.append((sum(pnew) - sum(realLossnew)) / 10**6) indices = ['No IVVC', 'With IVVC'] # energySalesRed = (whLoads[1]-whLoads[0])*(inData['wholesaleEnergyCostPerKwh'])*1000 # lossSav = (whLosses[0]-whLosses[1])*inData['wholesaleEnergyCostPerKwh']*1000 # print energySalesRed, lossSav #plots ticks = [] plt.clf() plt.title("total energy") plt.ylabel("total load and losses (MWh)") for element in range(2): ticks.append(element) bar_loss = plt.bar(element, whLosses[element], 0.15, color='red') bar_load = plt.bar(element + 0.15, whLoads[element], 0.15, color='orange') plt.legend([bar_load[0], bar_loss[0]], ['total load', 'total losses'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.xticks([t + 0.15 for t in ticks], indices) plt.savefig(pJoin(modelDir, "totalEnergy.png")) #real and imaginary power plt.figure("real power") plt.title("Real Power at substation") plt.ylabel("substation real power (MW)") pMW = [element / 10**6 for element in p] pMWn = [element / 10**6 for element in pnew] pw = plt.plot(pMW) npw = plt.plot(pMWn) plt.legend([pw[0], npw[0]], ['NO IVVC', 'WITH IVVC'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir, "realPower.png")) plt.figure("Reactive power") plt.title("Reactive Power at substation") plt.ylabel("substation reactive power (MVAR)") qMVAR = [element / 10**6 for element in q] qMVARn = [element / 10**6 for element in qnew] iw = plt.plot(qMVAR) niw = plt.plot(qMVARn) plt.legend([iw[0], niw[0]], ['NO IVVC', 'WITH IVVC'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir, "imaginaryPower.png")) #voltage plots plt.figure("voltages as a function of time") f, ax = plt.subplots(2, sharex=True) f.suptitle("Min and Max voltages on the feeder") lv = ax[0].plot(lowVoltage, color='cadetblue') mv = ax[0].plot(meanVoltage, color='blue') hv = ax[0].plot(highVoltage, color='cadetblue') ax[0].legend([lv[0], mv[0], hv[0]], ['low voltage', 'mean voltage', 'high voltage'], bbox_to_anchor=(0., 0.915, 1., .1), loc=3, ncol=3, mode="expand", borderaxespad=0.1) ax[0].set_ylabel('NO IVVC') nlv = ax[1].plot(lowVoltagenew, color='cadetblue') nmv = ax[1].plot(meanVoltagenew, color='blue') nhv = ax[1].plot(highVoltagenew, color='cadetblue') ax[1].set_ylabel('WITH IVVC') plt.savefig(pJoin(modelDir, "Voltages.png")) #tap positions plt.figure("TAP positions NO IVVC") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("Regulator Tap positions") ax[0].plot(tap['A']) ax[0].set_title("Regulator Tap positions NO IVVC") ax[0].set_ylabel("TAP A") ax[1].plot(tap['B']) ax[1].set_ylabel("TAP B") ax[2].plot(tap['C']) ax[2].set_ylabel("TAP C") ax[3].plot(tapnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("TAP A") ax[4].plot(tapnew['B']) ax[4].set_ylabel("TAP B") ax[5].plot(tapnew['C']) ax[5].set_ylabel("TAP C") for subplot in range(6): ax[subplot].set_ylim(-20, 20) f.tight_layout() plt.savefig(pJoin(modelDir, "RegulatorTAPpositions.png")) #substation voltages plt.figure("substation voltage as a function of time") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("voltages at substation NO IVVC") ax[0].plot(volt['A']) ax[0].set_title('Substation voltages NO IVVC') ax[0].set_ylabel('voltage A') ax[1].plot(volt['B']) ax[1].set_ylabel('voltage B') ax[2].plot(volt['C']) ax[2].set_ylabel('voltage C') ax[3].plot(voltnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel('voltage A') ax[4].plot(voltnew['B']) ax[4].set_ylabel('voltage B') ax[5].plot(voltnew['C']) ax[5].set_ylabel('voltage C') f.tight_layout() plt.savefig(pJoin(modelDir, "substationVoltages.png")) #cap switches plt.figure("capacitor switch state as a function of time") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("Capacitor switch state NO IVVC") ax[0].plot(switch['A']) ax[0].set_title("Capacitor switch state NO IVVC") ax[0].set_ylabel("switch A") ax[1].plot(switch['B']) ax[1].set_ylabel("switch B") ax[2].plot(switch['C']) ax[2].set_ylabel("switch C") ax[3].plot(switchnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("switch A") ax[4].plot(switchnew['B']) ax[4].set_ylabel("switch B") ax[5].plot(switchnew['C']) ax[5].set_ylabel("switch C") for subplot in range(6): ax[subplot].set_ylim(-2, 2) f.tight_layout() plt.savefig(pJoin(modelDir, "capacitorSwitch.png")) #plt.show() #monetization monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] monthToSeason = { 'January': 'Winter', 'February': 'Winter', 'March': 'Spring', 'April': 'Spring', 'May': 'Spring', 'June': 'Summer', 'July': 'Summer', 'August': 'Summer', 'September': 'Fall', 'October': 'Fall', 'November': 'Fall', 'December': 'Winter' } #calculate the month and hour of simulation start and month and hour of simulation end simStartTimestamp = simStartDate + " 00:00:00" simFormattedDate = datetime.strptime(simStartTimestamp, "%Y-%m-%d %H:%M:%S") simStartMonthNum = int(simFormattedDate.strftime('%m')) simstartMonth = monthNames[simStartMonthNum - 1] simStartDay = int(simFormattedDate.strftime('%d')) if calendar.isleap(int(simFormattedDate.strftime('%Y'))): febDays = 29 else: febDays = 28 monthHours = [ int(31 * 24), int(febDays * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(30 * 24), int(31 * 24) ] simStartIndex = int( sum(monthHours[:(simStartMonthNum - 1)]) + (simStartDay - 1) * 24) temp = 0 cumulHours = [0] for x in range(12): temp += monthHours[x] cumulHours.append(temp) for i in range((simStartMonthNum), 13): if int(simStartIndex + simRealLength) <= cumulHours[i] and int( simStartIndex + simRealLength) > cumulHours[i - 1]: simEndMonthNum = i - 1 simEndMonth = monthNames[simEndMonthNum] # print simstartMonth,simEndMonth #calculate peaks for the number of months in simulation previndex = 0 monthPeak = {} monthPeakNew = {} peakSaveDollars = {} energyLostDollars = {} lossRedDollars = {} simMonthList = monthNames[monthNames.index(simstartMonth):( monthNames.index(simEndMonth) + 1)] # print simMonthList for monthElement in simMonthList: # print monthElement month = monthNames.index(monthElement) index1 = int(previndex) index2 = int(min((index1 + int(monthHours[month])), simRealLength)) monthPeak[monthElement] = max(p[index1:index2]) / 1000.0 monthPeakNew[monthElement] = max(pnew[index1:index2]) / 1000.0 peakSaveDollars[monthElement] = ( monthPeak[monthElement] - monthPeakNew[monthElement]) * float( inData['peakDemandCost' + str(monthToSeason[monthElement]) + 'PerKw']) lossRedDollars[monthElement] = ( sum(realLoss[index1:index2]) / 1000.0 - sum(realLossnew[index1:index2]) / 1000.0) * (float( inData['wholesaleEnergyCostPerKwh'])) energyLostDollars[monthElement] = ( sum(p[index1:index2]) / 1000.0 - sum(pnew[index1:index2]) / 1000.0 - sum(realLoss[index1:index2]) / 1000.0 + sum(realLossnew[index1:index2]) / 1000.0) * ( float(inData['wholesaleEnergyCostPerKwh']) - float(inData['retailEnergyCostPerKwh'])) previndex = index2 #money charts fig = plt.figure("cost benefit barchart", figsize=(10, 8)) ticks = range(len(simMonthList)) ticks1 = [element + 0.15 for element in ticks] ticks2 = [element + 0.30 for element in ticks] # print ticks eld = [energyLostDollars[month] for month in simMonthList] lrd = [lossRedDollars[month] for month in simMonthList] psd = [peakSaveDollars[month] for month in simMonthList] bar_eld = plt.bar(ticks, eld, 0.15, color='red') bar_psd = plt.bar(ticks1, psd, 0.15, color='blue') bar_lrd = plt.bar(ticks2, lrd, 0.15, color='green') plt.legend([bar_eld[0], bar_psd[0], bar_lrd[0]], [ 'energyLostDollars', 'peakReductionDollars', 'lossReductionDollars' ], bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) monShort = [element[0:3] for element in simMonthList] plt.xticks([t + 0.15 for t in ticks], monShort) plt.ylabel('Utility Savings ($)') plt.savefig(pJoin(modelDir, "spendChart.png")) #cumulative savings graphs fig = plt.figure("cost benefit barchart", figsize=(10, 5)) annualSavings = sum(eld) + sum(lrd) + sum(psd) annualSave = lambda x: (annualSavings - float(inData['omCost']) ) * x - float(inData['capitalCost']) simplePayback = float( inData['capitalCost']) / (annualSavings - float(inData['omCost'])) plt.xlabel('Year After Installation') plt.xlim(0, 30) plt.ylabel('Cumulative Savings ($)') plt.plot([0 for x in range(31)], c='gray') plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--') plt.plot([annualSave(x) for x in range(31)], c='green') plt.savefig(pJoin(modelDir, "savingsChart.png")) #get exact time stamps from the CSV files generated by Gridlab-D timeWithZone = output['Zregulator.csv']['# timestamp'] timestamps = [element[:19] for element in timeWithZone] #data for highcharts allOutput["timeStamps"] = timestamps allOutput["noCVRPower"] = p allOutput["withCVRPower"] = pnew allOutput["noCVRLoad"] = whLoads[0] allOutput["withCVRLoad"] = whLoads[1] allOutput["noCVRLosses"] = whLosses[0] allOutput["withCVRLosses"] = whLosses[1] allOutput["noCVRTaps"] = tap allOutput["withCVRTaps"] = tapnew allOutput["noCVRSubVolts"] = volt allOutput["withCVRSubVolts"] = voltnew allOutput["noCVRCapSwitch"] = switch allOutput["withCVRCapSwitch"] = switchnew allOutput["noCVRHighVolt"] = highVoltage allOutput["withCVRHighVolt"] = highVoltagenew allOutput["noCVRLowVolt"] = lowVoltage allOutput["withCVRLowVolt"] = lowVoltagenew allOutput["noCVRMeanVolt"] = meanVoltage allOutput["withCVRMeanVolt"] = meanVoltagenew #monetization allOutput["simMonthList"] = monShort allOutput["energyLostDollars"] = energyLostDollars allOutput["lossRedDollars"] = lossRedDollars allOutput["peakSaveDollars"] = peakSaveDollars allOutput["annualSave"] = [annualSave(x) for x in range(31)] # Generate warnings #TODO: Timezone adjustment try: # Check if times for simulation and scada match. scadaDates = [] with open(pJoin(modelDir, "subScadaCalibrated1.player"), "r") as scadaFile: for line in scadaFile: (date, val) = line.split(',') scadaDates.append(str(date)) simFormattedEndDate = simFormattedDate + timedelta(hours=HOURS) scadaStartDate = datetime.strptime(scadaDates[0].split(' PST')[0], "%Y-%m-%d %H:%M:%S") scadaEndDate = datetime.strptime( scadaDates[len(scadaDates) - 1].split(' PST')[0], "%Y-%m-%d %H:%M:%S") beginRange = (scadaStartDate - simFormattedDate).total_seconds() endRange = (scadaEndDate - simFormattedEndDate).total_seconds() # Check if houses exist. housesExist, voltageNodeExists = False, False for key in localTree: if localTree[key].get('object', '') == 'house': housesExist = True if localTree[key].get('name', '') == str(inData.get("voltageNodes", 0)): voltageNodeExists = True if (beginRange > 0.0 or endRange < 0.0) and not housesExist: allOutput[ "warnings"] = "<strong>WARNING:</strong> The simulation dates entered are not compatible with the scada curve in the feeder." # Check if voltage node exists. if not voltageNodeExists: if allOutput.get('warnings', '') != "": previousWarning = allOutput["warnings"] allOutput[ "warnings"] = previousWarning + " The voltage node: " + str( inData.get("voltageNodes", 0)) + " does not exist in the feeder." else: allOutput[ "warnings"] = "<strong>WARNING:</strong> The voltage node <i>" + str( inData.get( "voltageNodes", 0)) + "</i> does not exist in the feeder." except: pass # Update the runTime in the input file. endTime = datetime.now() inData["runTime"] = str( timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"), "w") as inFile: json.dump(inData, inFile, indent=4) with open(pJoin(modelDir, "allOutputData.json"), "w") as outFile: json.dump(allOutput, outFile, indent=4) # For autotest, there won't be such file. try: os.remove(pJoin(modelDir, "PPID.txt")) except Exception, e: pass print "DONE RUNNING", modelDir
'generator_mode':'CONSTANT_PF' } myTree[oldMax + 6] = {'generator_mode':'SUPPLY_DRIVEN', 'name':'solar172879', 'parent':'solEngInverter', 'area':'30000 sf', 'generator_status':'ONLINE', 'object':'solar', 'efficiency':'0.14', 'panel_type':'SINGLE_CRYSTAL_SILICON' } # myTree[oldMax + 7] = { 'interval':'3600', # 'parent':'solEngInverter', # 'limit':'0', # 'file':'Inverter_solEngInverter.csv', # 'property':'power_A,power_B,power_C', # 'object': 'recorder'} feeder.adjustTime(myTree, 240, 'hours', '2014-01-01') # Run here to test. rawOut = runInFilesystem(myTree, attachments=myFeed['attachments'], keepFiles=True, workDir='.', glmName='Orville Tree Pond Calibrated.glm') # # Show some output. # print 'Output Keys:', rawOut.keys() # plt.plot([abs(complex(x)) for x in rawOut['Inverter_solEngInverter.csv']['power_A']]) # plt.show() # Write back the full feeder. outJson = dict(myFeed) with open('mspWeather.csv','r') as weatherFile: weatherString = weatherFile.read() outJson['attachments']['mspWeather.csv'] = weatherString outJson['tree'] = myTree
def omfCalibrate(workDir, feederPath, scadaPath, simStartDate, simLength, calibrateError=0.05): '''calibrates a feeder and saves the calibrated tree at a location''' with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) # Process scada data. gridlabdDir = pJoin(workDir,"gridlabD") scadaSubPower = _processScadaData(gridlabdDir,scadaPath, simStartDate) # Force FBS powerflow, because NR fails a lot. for key in tree: if tree[key].get("module","").lower() == "powerflow": tree[key] = {"module":"powerflow","solver_method":"FBS"} # Attach player. classOb = {'omftype':'class player','argument':'{double value;}'} playerOb = {"object":"player", "property":"value", "name":"scadaLoads", "file":"subScada.player", "loop":"0"} maxKey = feeder.getMaxKey(tree) playerKey = maxKey + 2 tree[maxKey+1] = classOb tree[playerKey] = playerOb # Make loads reference player. loadTemplate = {"object": "triplex_load", "power_pf_12": "0.95", "impedance_pf_12": "0.98", "power_pf_12": "0.90", "impedance_fraction_12": "0.7", "power_fraction_12": "0.3"} loadTemplateR = {"object": "load", "impedance_pf_A": "0.98", "impedance_pf_B": "0.98", "impedance_pf_C": "0.98", "power_pf_A": "0.90", "power_pf_B": "0.90", "power_pf_C": "0.90", "impedance_fraction_A": "0.7", "impedance_fraction_B": "0.7", "impedance_fraction_C": "0.7", "power_fraction_A": "0.3", "power_fraction_B": "0.3", "power_fraction_C": "0.3"} for key in tree: ob = tree[key] if ob.get("object","") == "triplex_node" and ob.get("power_12","") != "": # Add to triplex_nodes. newOb = dict(loadTemplate) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") oldPow = ob.get("power_12","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower) tree[key] = newOb elif ob.get("object","") == "load": # Add to residential_loads too. newOb = dict(loadTemplateR) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["load_class"] = ob.get("load_class", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") try: oldPow = ob.get("constant_power_A","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_A"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_B","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_B"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_C","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_C"] = "scadaLoads.value*" + str(pythagPower) except: pass tree[key] = newOb # Convert swing bus to a meter. for key in tree: if tree[key].get('bustype','').lower() == 'swing' and tree[key].get('object','') != 'meter': swingName = tree[key].get('name') regIndex = key tree[key]['object'] = 'meter' # Search for the substation meter and attach a recorder there. for key in tree: if tree[key].get('bustype','').lower() == 'swing': swingName = tree[key].get('name') recOb = {"object": "recorder", "parent": swingName, "property": "measured_real_power,measured_reactive_power,measured_power", "file": "caliSub.csv", "interval": "900"} outputRecorderKey = maxKey + 3 tree[outputRecorderKey] = recOb feeder.adjustTime(tree, simLength, "hours", simStartDate['Date'].strftime("%Y-%m-%d %H:%M:%S")) # Run Gridlabd, calculate scaling constant. def runPowerflowIter(tree,scadaSubPower): '''Runs powerflow once, then iterates.''' # Run initial powerflow to get power. print "Running initial calibration powerflow." output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=gridlabdDir) outRealPow = output["caliSub.csv"]["measured_real_power"] outImagPower = output["caliSub.csv"]["measured_reactive_power"] outAppPowerKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPow, outImagPower)] lastFile = "subScada.player" nextFile = "subScadaCalibrated.player" nextPower = outAppPowerKw error = (sum(outRealPow[1:simLength])/1000-sum(scadaSubPower[1:simLength]))/sum(scadaSubPower[1:simLength]) iteration = 1 while abs(error)>calibrateError and iteration<5: # Run calibration and iterate up to 5 times. SCAL_CONST = sum(scadaSubPower[1:simLength])/sum(nextPower[1:simLength]) print "Calibrating loads, running powerflow again. Our SCAL_CONST is: ", SCAL_CONST newPlayData = [] with open(pJoin(gridlabdDir, lastFile), "r") as playerFile: for line in playerFile: (key,val) = line.split(',') newPlayData.append(str(key) + ',' + str(float(val)*SCAL_CONST) + "\n") with open(pJoin(gridlabdDir, nextFile), "w") as playerFile: for row in newPlayData: playerFile.write(row) tree[playerKey]["file"] = nextFile tree[outputRecorderKey]["file"] = "caliSubCheck.csv" nextOutput = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=gridlabdDir) outRealPowIter = nextOutput["caliSubCheck.csv"]["measured_real_power"] outImagPowerIter = nextOutput["caliSubCheck.csv"]["measured_reactive_power"] nextAppKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPowIter, outImagPowerIter)] lastFile = nextFile nextFile = "subScadaCalibrated"+str(iteration)+".player" nextPower = nextAppKw # Compute error and iterate. error = (sum(outRealPowIter[1:simLength])/1000-sum(scadaSubPower[1:simLength]))/sum(scadaSubPower[1:simLength]) iteration+=1 print "Error:", abs(error*100), "% Iteration:", iteration return outRealPow, outRealPowIter, lastFile, iteration outRealPow, outRealPowIter, lastFile, iteration = runPowerflowIter(tree,scadaSubPower) caliPowVectors = [[float(element) for element in scadaSubPower[1:simLength]], [float(element)/1000 for element in outRealPow[1:simLength]], [float(element)/1000 for element in outRealPowIter[1:simLength]]] labels = ["scadaSubPower","initialGuess","finalGuess"] colors = ['red','lightblue','blue'] chartData = {"Title":"Substation Calibration Check (Iterated "+str(iteration+1)+"X)", "fileName":"caliCheckPlot", "colors":colors,"labels":labels, "timeZone":simStartDate['timeZone']} plotLine(workDir, caliPowVectors, chartData, simStartDate['Date']+dt.timedelta(hours=1), 'hours') # Write the final output. with open(pJoin(workDir,"calibratedFeeder.json"),"w") as outJson: playerString = open(pJoin(gridlabdDir,lastFile)).read() feederJson["attachments"][lastFile] = playerString feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return
def heavyProcessing(modelDir, inputDict): ''' Run the model in its directory. WARNING: GRIDLAB CAN TAKE HOURS TO COMPLETE. ''' print "STARTING TO RUN", modelDir beginTime = datetime.datetime.now() # Get feeder name and data in. try: os.mkdir(pJoin(modelDir,'gldContainer')) except: pass try: feederName = inputDict["feederName1"] inputDict["climateName"], latforpvwatts = zipCodeToClimateName(inputDict["zipCode"]) shutil.copy(pJoin(__metaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, "gldContainer", "climate.tmy2")) startTime = datetime.datetime.now() feederJson = json.load(open(pJoin(modelDir, feederName+'.omd'))) tree = feederJson["tree"] # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) # Attach recorders for system voltage map: stub = {'object':'group_recorder', 'group':'"class=node"', 'property':'voltage_A', 'interval':3600, 'file':'aVoltDump.csv'} for phase in ['A','B','C']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'VoltDump.csv' tree[feeder.getMaxKey(tree) + 1] = copyStub feeder.adjustTime(tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"]) # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem(tree, attachments=feederJson["attachments"], keepFiles=True, workDir=pJoin(modelDir,'gldContainer')) cleanOut = {} # Std Err and Std Out cleanOut['stderr'] = rawOut['stderr'] cleanOut['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# property.. timestamp'] else: cleanOut['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get('timeStamps',[]) level = inputDict.get('simLengthUnits','hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): cleanOut['climate'] = {} cleanOut['climate']['Rain Fall (in/h)'] = hdmAgg(rawOut[key].get('rainfall'), sum, level) cleanOut['climate']['Wind Speed (m/s)'] = hdmAgg(rawOut[key].get('wind_speed'), avg, level) cleanOut['climate']['Temperature (F)'] = hdmAgg(rawOut[key].get('temperature'), max, level) cleanOut['climate']['Snow Depth (in)'] = hdmAgg(rawOut[key].get('snowdepth'), max, level) cleanOut['climate']['Direct Normal (W/sf)'] = hdmAgg(rawOut[key].get('solar_direct'), sum, level) #cleanOut['climate']['Global Horizontal (W/sf)'] = hdmAgg(rawOut[key].get('solar_global'), sum, level) climateWbySFList= hdmAgg(rawOut[key].get('solar_global'), sum, level) #converting W/sf to W/sm climateWbySMList= [x*10.76392 for x in climateWbySFList] cleanOut['climate']['Global Horizontal (W/sm)']=climateWbySMList # Voltage Band if 'VoltageJiggle.csv' in rawOut: cleanOut['allMeterVoltages'] = {} cleanOut['allMeterVoltages']['Min'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)']], min, level) cleanOut['allMeterVoltages']['Mean'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['StdDev'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['Max'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)']], max, level) # Power Consumption cleanOut['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads cleanOut['Consumption']['Power'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['DG'] = [0] * int(inputDict["simLength"]) for key in rawOut: if key.startswith('SwingKids_') and key.endswith('.csv'): oneSwingPower = hdmAgg(vecPyth(rawOut[key]['sum(power_in.real)'],rawOut[key]['sum(power_in.imag)']), avg, level) if 'Power' not in cleanOut['Consumption']: cleanOut['Consumption']['Power'] = oneSwingPower else: cleanOut['Consumption']['Power'] = vecSum(oneSwingPower,cleanOut['Consumption']['Power']) elif key.startswith('Inverter_') and key.endswith('.csv'): realA = rawOut[key]['power_A.real'] realB = rawOut[key]['power_B.real'] realC = rawOut[key]['power_C.real'] imagA = rawOut[key]['power_A.imag'] imagB = rawOut[key]['power_B.imag'] imagC = rawOut[key]['power_C.imag'] oneDgPower = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key.startswith('Windmill_') and key.endswith('.csv'): vrA = rawOut[key]['voltage_A.real'] vrB = rawOut[key]['voltage_B.real'] vrC = rawOut[key]['voltage_C.real'] viA = rawOut[key]['voltage_A.imag'] viB = rawOut[key]['voltage_B.imag'] viC = rawOut[key]['voltage_C.imag'] crB = rawOut[key]['current_B.real'] crA = rawOut[key]['current_A.real'] crC = rawOut[key]['current_C.real'] ciA = rawOut[key]['current_A.imag'] ciB = rawOut[key]['current_B.imag'] ciC = rawOut[key]['current_C.imag'] powerA = vecProd(vecPyth(vrA,viA),vecPyth(crA,ciA)) powerB = vecProd(vecPyth(vrB,viB),vecPyth(crB,ciB)) powerC = vecProd(vecPyth(vrC,viC),vecPyth(crC,ciC)) oneDgPower = hdmAgg(vecSum(powerA,powerB,powerC), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key in ['OverheadLosses.csv', 'UndergroundLosses.csv', 'TriplexLosses.csv', 'TransformerLosses.csv']: realA = rawOut[key]['sum(power_losses_A.real)'] imagA = rawOut[key]['sum(power_losses_A.imag)'] realB = rawOut[key]['sum(power_losses_B.real)'] imagB = rawOut[key]['sum(power_losses_B.imag)'] realC = rawOut[key]['sum(power_losses_C.real)'] imagC = rawOut[key]['sum(power_losses_C.imag)'] oneLoss = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'Losses' not in cleanOut['Consumption']: cleanOut['Consumption']['Losses'] = oneLoss else: cleanOut['Consumption']['Losses'] = vecSum(oneLoss,cleanOut['Consumption']['Losses']) elif key.startswith('Regulator_') and key.endswith('.csv'): #split function to strip off .csv from filename and user rest of the file name as key. for example- Regulator_VR10.csv -> key would be Regulator_VR10 regName="" regName = key newkey=regName.split(".")[0] cleanOut[newkey] ={} cleanOut[newkey]['RegTapA'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapB'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapC'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapA'] = rawOut[key]['tap_A'] cleanOut[newkey]['RegTapB'] = rawOut[key]['tap_B'] cleanOut[newkey]['RegTapC'] = rawOut[key]['tap_C'] cleanOut[newkey]['RegPhases'] = rawOut[key]['phases'][0] elif key.startswith('Capacitor_') and key.endswith('.csv'): capName="" capName = key newkey=capName.split(".")[0] cleanOut[newkey] ={} cleanOut[newkey]['Cap1A'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1B'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1C'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1A'] = rawOut[key]['switchA'] cleanOut[newkey]['Cap1B'] = rawOut[key]['switchB'] cleanOut[newkey]['Cap1C'] = rawOut[key]['switchC'] cleanOut[newkey]['CapPhases'] = rawOut[key]['phases'][0] # What percentage of our keys have lat lon data? latKeys = [tree[key]['latitude'] for key in tree if 'latitude' in tree[key]] latPerc = 1.0*len(latKeys)/len(tree) if latPerc < 0.25: doNeato = True else: doNeato = False # Generate the frames for the system voltage map time traveling chart. genTime = generateVoltChart(tree, rawOut, modelDir, neatoLayout=doNeato) cleanOut['genTime'] = genTime # Aggregate up the timestamps: if level=='days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, "allOutputData.json"),"w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, "gldContainer", "PID.txt")) print "DONE RUNNING", modelDir except Exception as e: # If input range wasn't valid delete output, write error to disk. cancel(modelDir) thisErr = traceback.format_exc() print 'ERROR IN MODEL', modelDir, thisErr inputDict['stderr'] = thisErr with open(os.path.join(modelDir,'stderr.txt'),'w') as errorFile: errorFile.write(thisErr) with open(pJoin(modelDir,"allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) finishTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds = int((finishTime - beginTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent = 4) try: os.remove(pJoin(modelDir,"PPID.txt")) except: pass
def runModel(modelDir, localTree, inData): '''This reads a glm file, changes the method of powerflow and reruns''' try: os.remove(pJoin(modelDir, "allOutputData.json")) except: pass allOutput = {} if not os.path.isdir(modelDir): os.makedirs(modelDir) inData["created"] = str(datetime.now()) with open(pJoin(modelDir, "allInputData.json"), "w") as inputFile: json.dump(inData, inputFile, indent=4) binaryName = "gridlabd" for key in localTree: if "solver_method" in localTree[key].keys(): print "current solver method", localTree[key]["solver_method"] localTree[key]["solver_method"] = 'FBS' #find the swing bus and recorder attached to substation for key in localTree: if localTree[key].get('bustype', '').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') if localTree[key].get('object', '') == 'regulator' and localTree[key].get( 'from', '') == swingName: regIndex = key regConfName = localTree[key]['configuration'] #find the regulator and capacitor names and combine to form a string for volt-var control object regKeys = [] accum_reg = "" for key in localTree: if localTree[key].get("object", "") == "regulator": accum_reg += localTree[key].get("name", "ERROR") + "," regKeys.append(key) regstr = accum_reg[:-1] print regKeys capKeys = [] accum_cap = "" for key in localTree: if localTree[key].get("object", "") == "capacitor": accum_cap += localTree[key].get("name", "ERROR") + "," capKeys.append(key) if localTree[key].get("control", "").lower() == "manual": localTree[key]['control'] = "VOLT" print "changing capacitor control from manual to volt" capstr = accum_cap[:-1] print capKeys # Attach recorders relevant to CVR. recorders = [{ 'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': localTree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag' }, { 'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)' }, { 'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': localTree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C' }, { 'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': localTree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C' }] #recorder object for capacitor switching - if capacitors exist if capKeys != []: for key in capKeys: recorders.append({ 'object': 'recorder', 'file': 'ZcapSwitch' + str(key) + '.csv', 'limit': '0', 'parent': localTree[key]['name'], 'property': 'switchA,switchB,switchC' }) #attach recorder process biggest = 1 + max([int(k) for k in localTree.keys()]) for index, rec in enumerate(recorders): localTree[biggest + index] = rec #run a reference load flow HOURS = float(100) year_lp = False #leap year feeder.adjustTime(localTree, HOURS, "hours", "2011-01-01") output = gridlabd.runInFilesystem(localTree, keepFiles=False, workDir=modelDir) os.remove(pJoin(modelDir, "PID.txt")) p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] #time delays from configuration files time_delay_reg = '30.0' time_delay_cap = '300.0' for key in localTree: if localTree[key].get('object', '') == "regulator_configuration": time_delay_reg = localTree[key]['time_delay'] print "time_delay_reg", time_delay_reg # if localTree[key].get('object','') == "capacitor": # time_delay_cap = localTree[key]['time_delay'] # print "time_delay_cap",time_delay_cap #change the recorder names for key in localTree: if localTree[key].get('object', '') == "collector" or localTree[key].get( 'object', '') == "recorder": if localTree[key].get('file', '').startswith('Z'): localTree[key]['file'] = localTree[key].get('file', '').replace( 'Z', 'NewZ') #create volt-var control object max_key = max([int(key) for key in localTree.keys()]) print max_key localTree[max_key + 1] = { 'object': 'volt_var_control', 'name': 'IVVC1', 'control_method': 'ACTIVE', 'capacitor_delay': str(time_delay_cap), 'regulator_delay': str(time_delay_reg), 'desired_pf': '0.99', 'd_max': '0.6', 'd_min': '0.1', 'substation_link': str(localTree[regIndex]['name']), 'regulator_list': regstr, 'capacitor_list': capstr } #running powerflow analysis via gridalab after attaching a regulator feeder.adjustTime(localTree, HOURS, "hours", "2011-01-01") output1 = gridlabd.runInFilesystem(localTree, keepFiles=True, workDir=modelDir) os.remove(pJoin(modelDir, "PID.txt")) pnew = output1['NewZregulator.csv']['power_in.real'] qnew = output1['NewZregulator.csv']['power_in.imag'] #total real and imaginary losses as a function of time realLoss = [] imagLoss = [] realLossnew = [] imagLossnew = [] for element in range(int(HOURS)): r = 0.0 i = 0.0 rnew = 0.0 inew = 0.0 for device in [ 'ZlossesOverhead.csv', 'ZlossesTransformer.csv', 'ZlossesUnderground.csv' ]: for letter in ['A', 'B', 'C']: r += output[device]['sum(power_losses_' + letter + '.real)'][element] i += output[device]['sum(power_losses_' + letter + '.imag)'][element] rnew += output1['New' + device]['sum(power_losses_' + letter + '.real)'][element] inew += output1['New' + device]['sum(power_losses_' + letter + '.imag)'][element] realLoss.append(r) imagLoss.append(i) realLossnew.append(rnew) imagLossnew.append(inew) #voltage calculations and tap calculations lowVoltage = [] meanVoltage = [] highVoltage = [] lowVoltagenew = [] meanVoltagenew = [] highVoltagenew = [] tap = {'A': [], 'B': [], 'C': []} tapnew = {'A': [], 'B': [], 'C': []} volt = {'A': [], 'B': [], 'C': []} voltnew = {'A': [], 'B': [], 'C': []} switch = {'A': [], 'B': [], 'C': []} switchnew = {'A': [], 'B': [], 'C': []} for element in range(int(HOURS)): for letter in ['A', 'B', 'C']: tap[letter].append(output['Zregulator.csv']['tap_' + letter][element]) tapnew[letter].append( output1['NewZregulator.csv']['tap_' + letter][element]) #voltage real, imag vr, vi = sepRealImag( output['ZsubstationBottom.csv']['voltage_' + letter][element]) volt[letter].append(math.sqrt(vr**2 + vi**2) / 60) vrnew, vinew = sepRealImag( output1['NewZsubstationBottom.csv']['voltage_' + letter][element]) voltnew[letter].append(math.sqrt(vrnew**2 + vinew**2) / 60) if capKeys != []: switch[letter].append( output['ZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch' + letter][element]) switchnew[letter].append( output1['NewZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch' + letter][element]) lowVoltage.append( float(output['ZvoltageJiggle.csv']['min(voltage_12.mag)'][element]) / 2.0) meanVoltage.append( float( output['ZvoltageJiggle.csv']['mean(voltage_12.mag)'][element]) / 2.0) highVoltage.append( float(output['ZvoltageJiggle.csv']['max(voltage_12.mag)'][element]) / 2.0) lowVoltagenew.append( float(output1['NewZvoltageJiggle.csv']['min(voltage_12.mag)'] [element]) / 2.0) meanVoltagenew.append( float(output1['NewZvoltageJiggle.csv']['mean(voltage_12.mag)'] [element]) / 2.0) highVoltagenew.append( float(output1['NewZvoltageJiggle.csv']['max(voltage_12.mag)'] [element]) / 2.0) #energy calculations whEnergy = [] whLosses = [] whLoads = [] whEnergy.append(sum(p) / 10**6) whLosses.append(sum(realLoss) / 10**6) whLoads.append((sum(p) - sum(realLoss)) / 10**6) whEnergy.append(sum(pnew) / 10**6) whLosses.append(sum(realLossnew) / 10**6) whLoads.append((sum(pnew) - sum(realLossnew)) / 10**6) indices = ['No IVVC', 'With IVVC'] # energySalesRed = (whLoads[1]-whLoads[0])*(inData['wholesaleEnergyCostPerKwh'])*1000 # lossSav = (whLosses[0]-whLosses[1])*inData['wholesaleEnergyCostPerKwh']*1000 # print energySalesRed, lossSav #plots ticks = [] plt.clf() plt.title("total energy") plt.ylabel("total load and losses (MWh)") for element in range(2): ticks.append(element) bar_loss = plt.bar(element, whLosses[element], 0.15, color='red') bar_load = plt.bar(element + 0.15, whLoads[element], 0.15, color='orange') plt.legend([bar_load[0], bar_loss[0]], ['total load', 'total losses'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.xticks([t + 0.15 for t in ticks], indices) plt.savefig(pJoin(modelDir, "total energy.png")) #real and imaginary power plt.figure("real power") plt.title("Real Power at substation") plt.ylabel("substation real power (MW)") pMW = [element / 10**6 for element in p] pMWn = [element / 10**6 for element in pnew] pw = plt.plot(pMW) npw = plt.plot(pMWn) plt.legend([pw[0], npw[0]], ['NO IVVC', 'WITH IVVC'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir, "real power.png")) plt.figure("Reactive power") plt.title("Reactive Power at substation") plt.ylabel("substation reactive power (MVAR)") qMVAR = [element / 10**6 for element in q] qMVARn = [element / 10**6 for element in qnew] iw = plt.plot(qMVAR) niw = plt.plot(qMVARn) plt.legend([iw[0], niw[0]], ['NO IVVC', 'WITH IVVC'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir, "imaginary power.png")) #voltage plots plt.figure("voltages as a function of time") f, ax = plt.subplots(2, sharex=True) f.suptitle("Voltages high and low") lv = ax[0].plot(lowVoltage, color='cadetblue') mv = ax[0].plot(meanVoltage, color='blue') hv = ax[0].plot(highVoltage, color='cadetblue') ax[0].legend([lv[0], mv[0], hv[0]], ['low voltage', 'mean voltage', 'high voltage'], bbox_to_anchor=(0., 0.915, 1., .1), loc=3, ncol=3, mode="expand", borderaxespad=0.1) ax[0].set_ylabel('NO IVVC') nlv = ax[1].plot(lowVoltagenew, color='cadetblue') nmv = ax[1].plot(meanVoltagenew, color='blue') nhv = ax[1].plot(highVoltagenew, color='cadetblue') ax[1].set_ylabel('WITH IVVC') plt.savefig(pJoin(modelDir, "Voltages.png")) #tap positions plt.figure("TAP positions NO IVVC") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(18.5, 12.0) #f.suptitle("Regulator Tap positions") ax[0].plot(tap['A']) ax[0].set_title("Regulator Tap positions NO IVVC") ax[0].set_ylabel("TAP A") ax[1].plot(tap['B']) ax[1].set_ylabel("TAP B") ax[2].plot(tap['C']) ax[2].set_ylabel("TAP C") ax[3].plot(tapnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("TAP A") ax[4].plot(tapnew['B']) ax[4].set_ylabel("TAP B") ax[5].plot(tapnew['C']) ax[5].set_ylabel("TAP C") for subplot in range(6): ax[subplot].set_ylim(-20, 20) f.tight_layout() plt.savefig(pJoin(modelDir, "Regulator TAP positions.png")) #substation voltages plt.figure("substation voltage as a function of time") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(18.5, 12.0) #f.suptitle("voltages at substation NO IVVC") ax[0].plot(volt['A']) ax[0].set_title('Substation voltages NO IVVC') ax[0].set_ylabel('voltage A') ax[1].plot(volt['B']) ax[1].set_ylabel('voltage B') ax[2].plot(volt['C']) ax[2].set_ylabel('voltage C') ax[3].plot(voltnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel('voltage A') ax[4].plot(voltnew['B']) ax[4].set_ylabel('voltage B') ax[5].plot(voltnew['C']) ax[5].set_ylabel('voltage C') f.tight_layout() plt.savefig(pJoin(modelDir, "substation voltages.png")) #cap switches - plotted if capacitors are present if capKeys != []: plt.figure("capacitor switch state as a function of time") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(18.5, 12.0) #f.suptitle("Capacitor switch state NO IVVC") ax[0].plot(switch['A']) ax[0].set_title("Capacitor switch state NO IVVC") ax[0].set_ylabel("switch A") ax[1].plot(switch['B']) ax[1].set_ylabel("switch B") ax[2].plot(switch['C']) ax[2].set_ylabel("switch C") ax[3].plot(switchnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("switch A") ax[4].plot(switchnew['B']) ax[4].set_ylabel("switch B") ax[5].plot(switchnew['C']) ax[5].set_ylabel("switch C") for subplot in range(6): ax[subplot].set_ylim(-2, 2) f.tight_layout() plt.savefig(pJoin(modelDir, "capacitor switch.png")) #plt.show() #monetization monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] monthToSeason = { 'January': 'Winter', 'February': 'Winter', 'March': 'Spring', 'April': 'Spring', 'May': 'Spring', 'June': 'Summer', 'July': 'Summer', 'August': 'Summer', 'September': 'Fall', 'October': 'Fall', 'November': 'Fall', 'December': 'Winter' } if year_lp == True: febDays = 29 else: febDays = 28 monthHours = [ int(31 * 24), int(febDays * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(30 * 24), int(31 * 24) ] #find simulation months temp = 0 cumulHours = [] for x in range(12): temp += monthHours[x] cumulHours.append(temp) for i in range(12): if i == 0: lowval = 0 else: lowval = cumulHours[i - 1] if HOURS <= cumulHours[i] and HOURS >= lowval: hourMonth = monthNames[i] hourIndex = i #calculate peaks for the number of months in simulation previndex = 0 monthPeak = {} monthPeakNew = {} peakSaveDollars = {} energyLostDollars = {} lossRedDollars = {} for month in range(hourIndex + 1): index1 = int(previndex) index2 = int(min((index1 + int(monthHours[month])), HOURS)) monthPeak[monthNames[month]] = max(p[index1:index2]) / 1000.0 monthPeakNew[monthNames[month]] = max(pnew[index1:index2]) / 1000.0 peakSaveDollars[monthNames[month]] = ( monthPeak[monthNames[month]] - monthPeakNew[monthNames[month]] ) * inData['peakDemandCost' + str(monthToSeason[monthNames[month]]) + 'PerKw'] lossRedDollars[ monthNames[month]] = (sum(realLoss[index1:index2]) / 1000.0 - sum(realLossnew[index1:index2]) / 1000.0) * ( inData['wholesaleEnergyCostPerKwh']) energyLostDollars[monthNames[month]] = ( sum(p[index1:index2]) / 1000.0 - sum(pnew[index1:index2]) / 1000.0 - sum(realLoss[index1:index2]) / 1000.0 + sum(realLossnew[index1:index2]) / 1000.0) * ( inData['wholesaleEnergyCostPerKwh'] - inData['retailEnergyCostPerKwh']) previndex = index2 #money charts simMonths = monthNames[:hourIndex + 1] fig = plt.figure("cost benefit barchart", figsize=(10, 8)) ticks = range(len(simMonths)) ticks1 = [element + 0.15 for element in ticks] ticks2 = [element + 0.30 for element in ticks] print ticks eld = [energyLostDollars[month] for month in simMonths] lrd = [lossRedDollars[month] for month in simMonths] psd = [peakSaveDollars[month] for month in simMonths] bar_eld = plt.bar(ticks, eld, 0.15, color='red') bar_psd = plt.bar(ticks1, psd, 0.15, color='blue') bar_lrd = plt.bar(ticks2, lrd, 0.15, color='green') plt.legend( [bar_eld[0], bar_psd[0], bar_lrd[0]], ['energyLostDollars', 'peakReductionDollars', 'lossReductionDollars'], bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) monShort = [element[0:3] for element in simMonths] plt.xticks([t + 0.15 for t in ticks], monShort) plt.ylabel('Utility Savings ($)') plt.savefig(pJoin(modelDir, "spendChart.png")) with open(pJoin(modelDir, "spendChart.png"), "rb") as inFile: allOutput["spendChart"] = inFile.read().encode("base64") #cumulative savings graphs fig = plt.figure("cost benefit barchart", figsize=(10, 5)) annualSavings = sum(eld) + sum(lrd) + sum(psd) annualSave = lambda x: (annualSavings - inData['omCost']) * x - inData[ 'capitalCost'] simplePayback = inData['capitalCost'] / (annualSavings - inData['omCost']) plt.xlabel('Year After Installation') plt.xlim(0, 30) plt.ylabel('Cumulative Savings ($)') plt.plot([0 for x in range(31)], c='gray') plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--') plt.plot([annualSave(x) for x in range(31)], c='green') plt.savefig(pJoin(modelDir, "savingsChart.png")) with open(pJoin(modelDir, "savingsChart.png"), "rb") as inFile: allOutput["savingsChart"] = inFile.read().encode("base64") # Update the runTime in the input file. # endTime = datetime.now() # inDat["runTime"] = str(timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"), "w") as inFile: json.dump(inData, inFile, indent=4) with open(pJoin(modelDir, "allOutputData.json"), "w") as outFile: json.dump(allOutput, outFile, indent=4) print "DONE RUNNING", modelDir
def attachVolts(workDir, feederPath, voltVectorA, voltVectorB, voltVectorC, simStartDate, simLength, simLengthUnits): '''read voltage vectors of 3 different phases, run gridlabd, and attach output to the feeder.''' try: timeStamp = [simStartDate['Date']] for x in range (1, 8760): timeStamp.append(timeStamp[x-1] + dt.timedelta(hours=1)) firstDateTime = timeStamp[1] with open(pJoin(pJoin(workDir,"gridlabD"),"phaseAVoltage.player"),"w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f"%float(voltVectorA[x]))+"+0j" line = timestamp.strftime("%Y-%m-%d %H:%M:%S") + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(pJoin(pJoin(workDir,"gridlabD"),"phaseBVoltage.player"),"w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f"%float(voltVectorB[x]))+"-"+str("%0.4f"%float(random.uniform(6449,6460)))+"j" line = timestamp.strftime("%Y-%m-%d %H:%M:%S") + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(pJoin(pJoin(workDir,"gridlabD"),"phaseCVoltage.player"),"w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f"%float(voltVectorC[x]))+"+"+str("%0.4f"%float(random.uniform(6449,6460)))+"j" line = timestamp.strftime("%Y-%m-%d %H:%M:%S") + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) # Find swingNode name. for key in tree: if tree[key].get('bustype','').lower() == 'swing': swingName = tree[key].get('name') # Attach player. classOb = {'omftype':'class player','argument':'{double value;}'} voltageObA = {"object":"player", "property":"voltage_A", "file":"phaseAVoltage.player", "loop":"0", "parent":swingName} voltageObB = {"object":"player", "property":"voltage_B", "file":"phaseBVoltage.player", "loop":"0", "parent":swingName} voltageObC = {"object":"player", "property":"voltage_C", "file":"phaseCVoltage.player", "loop":"0", "parent":swingName} maxKey = feeder.getMaxKey(tree) voltplayerKeyA = maxKey + 2 voltplayerKeyB = maxKey + 3 voltplayerKeyC = maxKey + 4 tree[maxKey+1] = classOb tree[voltplayerKeyA] = voltageObA tree[voltplayerKeyB] = voltageObB tree[voltplayerKeyC] = voltageObC # Adjust time and run output. feeder.adjustTime(tree, simLength, simLengthUnits, firstDateTime.strftime("%Y-%m-%d %H:%M:%S")) output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir,"gridlabD")) # Write the output. with open(pJoin(workDir,"calibratedFeeder.omd"),"w") as outJson: playerStringA = open(pJoin(pJoin(workDir,"gridlabD"),"phaseAVoltage.player")).read() playerStringB = open(pJoin(pJoin(workDir,"gridlabD"),"phaseBVoltage.player")).read() playerStringC = open(pJoin(pJoin(workDir,"gridlabD"),"phaseCVoltage.player")).read() feederJson["attachments"]["phaseAVoltage.player"] = playerStringA feederJson["attachments"]["phaseBVoltage.player"] = playerStringB feederJson["attachments"]["phaseCVoltage.player"] = playerStringC feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return pJoin(workDir,"calibratedFeeder.omd"), True except: print "Failed to run gridlabD with voltage players." return "", False
def comparesol(modelDir, localTree): '''This reads a glm file, changes the method of powerflow and reruns''' print "Testing GridlabD solver." startTime = datetime.now() binaryName = "gridlabd" for key in localTree: if "solver_method" in localTree[key].keys(): solvmeth = localTree[key]["solver_method"] print "success", solvmeth if solvmeth == 'NR': localTree[key]["solver_method"] = 'FBS' else: localTree[key]["solver_method"] = 'NR' # feeder.attachRecorders(localTree, "Regulator", "object", "regulator") # feeder.attachRecorders(localTree, "CollectorVoltage", None, None) # last_key = len(localTree) # print last_key for key in localTree: if localTree[key].get('bustype', '').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') print swingIndex, swingName for key in localTree: if localTree[key].get('object', '') == 'regulator' and localTree[key].get( 'from', '') == swingName: regIndex = key regConfName = localTree[key]['configuration'] print regIndex # Attach recorders relevant to CVR. recorders = [{ 'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': localTree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag' }, { 'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)' }, { 'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': localTree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C' }, { 'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': localTree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C' }] biggest = 1 + max([int(k) for k in localTree.keys()]) for index, rec in enumerate(recorders): localTree[biggest + index] = rec max_key = max([int(key) for key in localTree.keys()]) print max_key regKeys = [] accum = "" for key in localTree: if localTree[key].get("object", "") == "regulator": accum += localTree[key].get("name", "ERROR") + "," regKeys.append(key) regstr = accum[:-1] print regKeys print regstr, type(regstr) localTree[max_key + 1] = { 'object': 'volt_var_control', 'name': 'volt_var_control', 'control_method': 'ACTIVE', 'capacitor_delay': '30.0', 'regulator_delay': '30.0', 'desired_pf': '0.99', 'd_max': '0.6', 'd_min': '0.1', 'substation_link': 'substation_transformer', 'regulator_list': regstr } feeder.adjustTime(tree=localTree, simLength=float("8760"), simLengthUnits="hours", simStartDate="2012-01-01") output = gridlabd.runInFilesystem(localTree, keepFiles=True, workDir=modelDir) os.remove(pJoin(modelDir, "PID.txt")) p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] xtime = {} for key in output: if '# timestamp' in output[key]: xtime['timeStamps'] = output[key]['# timestamp'] #print type(xtime['timeStamps'][0]) #print len(p) #xaxtick = str(xtime['timeStamps']) # plt.plot(range(8760),p) # plt.show() # print "p=" , p # print "q=" , q print "DONE RUNNING", modelDir
def omfCalibrate(workDir, feederPath, scadaPath): '''calibrates a feeder and saves the calibrated tree at a location''' with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) scadaSubPower = _processScadaData(workDir, scadaPath) # Force FBS powerflow, because NR fails a lot. for key in tree: if tree[key].get("module", "").lower() == "powerflow": tree[key] = {"module": "powerflow", "solver_method": "FBS"} # Attach player. classOb = { "class": "player", "variable_names": ["value"], "variable_types": ["double"] } playerOb = { "object": "player", "property": "value", "name": "scadaLoads", "file": "subScada.player", "loop": "0" } maxKey = feeder.getMaxKey(tree) tree[maxKey + 1] = classOb tree[maxKey + 2] = playerOb # Make loads reference player. loadTemplate = { "object": "triplex_load", "power_pf_12": "0.95", "impedance_pf_12": "0.98", "power_pf_12": "0.90", "impedance_fraction_12": "0.7", "power_fraction_12": "0.3" } for key in tree: ob = tree[key] if ob.get("object", "") == "triplex_node" and ob.get("power_12", "") != "": newOb = dict(loadTemplate) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["nominal_voltage"] = ob.get("nominal_voltage", "") newOb["latitude"] = ob.get("latitude", "0") newOb["longitude"] = ob.get("longitude", "0") oldPow = ob.get("power_12", "").replace("j", "d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower) tree[key] = newOb # Search for the substation regulator and attach a recorder there. for key in tree: if tree[key].get('bustype', '').lower() == 'swing': swingName = tree[key].get('name') for key in tree: if tree[key].get('object', '') == 'regulator' and tree[key].get( 'from', '') == swingName: regIndex = key SUB_REG_NAME = tree[key]['name'] recOb = { "object": "recorder", "parent": SUB_REG_NAME, "property": "power_in.real,power_in.imag", "file": "caliSub.csv", "interval": "900" } tree[maxKey + 3] = recOb HOURS = 100 feeder.adjustTime(tree, HOURS, "hours", "2011-01-01") # Run Gridlabd. output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=workDir) # Calculate scaling constant. outRealPow = output["caliSub.csv"]["power_in.real"] outImagPower = output["caliSub.csv"]["power_in.imag"] outAppPowerKw = [(x[0]**2 + x[1]**2)**0.5 / 1000 for x in zip(outRealPow, outImagPower)] # HACK: ignore first time step in output and input because GLD sometimes breaks the first step. SCAL_CONST = sum(scadaSubPower[1:HOURS]) / sum(outAppPowerKw[1:HOURS]) # Rewrite the subScada.player file so all the power values are multiplied by the SCAL_CONSTANT. newPlayData = [] with open(pJoin(workDir, "subScada.player"), "r") as playerFile: for line in playerFile: (key, val) = line.split(',') newPlayData.append( str(key) + ',' + str(float(val) * SCAL_CONST) + "\n") with open(pJoin(workDir, "subScadaCalibrated.player"), "w") as playerFile: for row in newPlayData: playerFile.write(row) # Test by running a glm with subScadaCalibrated.player and caliSub.csv2. tree[maxKey + 2]["file"] = "subScadaCalibrated.player" tree[maxKey + 3]["file"] = "caliSubCheck.csv" secondOutput = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=workDir) plt.plot(outAppPowerKw[1:HOURS], label="initialGuess") plt.plot(scadaSubPower[1:HOURS], label="scadaSubPower") secondAppKw = [ (x[0]**2 + x[1]**2)**0.5 / 1000 for x in zip(secondOutput["caliSubCheck.csv"]["power_in.real"], secondOutput["caliSubCheck.csv"]["power_in.imag"]) ] plt.plot(secondAppKw[1:HOURS], label="finalGuess") plt.legend(loc=3) plt.savefig(pJoin(workDir, "caliCheckPlot.png")) # Write the final output. with open(pJoin(workDir, "calibratedFeeder.json"), "w") as outJson: playerString = open(pJoin(workDir, "subScadaCalibrated.player")).read() feederJson["attachments"]["subScadaCalibrated.player"] = playerString feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return
def omfCalibrate(workDir, feederPath, scadaPath, simStartDate, simLength, simLengthUnits, solver="FBS", calibrateError=(0.05, 5), trim=5): '''calibrates a feeder and saves the calibrated tree at a location. Note: feeders with cap banks should be calibrated with cap banks OPEN. We have seen cap banks throw off calibration.''' with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) simLength = simLength + trim # Process scada data. scadaSubPower = _processScadaData(pJoin(workDir, "gridlabD"), scadaPath, simStartDate, simLengthUnits) # Load specified solver. for key in tree: if tree[key].get("module", "").lower() == "powerflow": tree[key] = {"module": "powerflow", "solver_method": solver} # Attach player. classOb = {'omftype': 'class player', 'argument': '{double value;}'} playerOb = { "object": "player", "property": "value", "name": "scadaLoads", "file": "subScada.player", "loop": "0" } maxKey = feeder.getMaxKey(tree) playerKey = maxKey + 2 tree[maxKey + 1] = classOb tree[playerKey] = playerOb # Make loads reference player. loadTemplate = { "object": "triplex_load", "power_pf_12": "0.95", "impedance_pf_12": "0.98", "power_pf_12": "0.90", "impedance_fraction_12": "0.7", "power_fraction_12": "0.3" } loadTemplateR = { "object": "load", "impedance_pf_A": "0.98", "impedance_pf_B": "0.98", "impedance_pf_C": "0.98", "power_pf_A": "0.90", "power_pf_B": "0.90", "power_pf_C": "0.90", "impedance_fraction_A": "0.7", "impedance_fraction_B": "0.7", "impedance_fraction_C": "0.7", "power_fraction_A": "0.3", "power_fraction_B": "0.3", "power_fraction_C": "0.3" } for key in tree: ob = tree[key] if ob.get("object", "") in ("triplex_node", "triplex_load") and ( ob.get("power_12") or ob.get("base_power_12")): # Add to triplex_nodes. newOb = dict(loadTemplate) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["nominal_voltage"] = ob.get("nominal_voltage", "") newOb["latitude"] = ob.get("latitude", "0") newOb["longitude"] = ob.get("longitude", "0") oldPow = ob.get("power_12", "").replace("j", "d") if not oldPow: oldPow = ob.get("base_power_12") if "scadaloads.value*" in oldPow: oldPow = oldPow[17:] pythagPower = gridlabd._strClean(oldPow) newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower) tree[key] = newOb elif ob.get("object", "") == "load": # Add to residential_loads too. newOb = dict(loadTemplateR) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["load_class"] = ob.get("load_class", "") newOb["nominal_voltage"] = ob.get("nominal_voltage", "") newOb["latitude"] = ob.get("latitude", "0") newOb["longitude"] = ob.get("longitude", "0") try: oldPow = ob.get("constant_power_A", "").replace("j", "d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_A"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_B", "").replace("j", "d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_B"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_C", "").replace("j", "d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_C"] = "scadaLoads.value*" + str(pythagPower) except: pass tree[key] = newOb # Convert swing bus to a meter. for key in tree: if tree[key].get('bustype', '').lower() == 'swing' and tree[key].get( 'object', '') != 'meter': swingName = tree[key].get('name') regIndex = key tree[key]['object'] = 'meter' # Search for the substation meter and attach a recorder there. for key in tree: if tree[key].get('bustype', '').lower() == 'swing': swingName = tree[key].get('name') recOb = { "object": "recorder", "parent": swingName, "property": "measured_real_power,measured_reactive_power,measured_power", "file": "caliSub.csv", "interval": "3600" } outputRecorderKey = maxKey + 3 tree[outputRecorderKey] = recOb feeder.adjustTime(tree, simLength, simLengthUnits, simStartDate['Date'].strftime("%Y-%m-%d %H:%M:%S")) # Run Gridlabd, calculate scaling constant. def runPowerflowIter(tree, scadaSubPower): '''Runs powerflow once, then iterates.''' # Run initial powerflow to get power. print "Starting calibration." print "Goal of calibration: Error: %s, Iterations: <%s, trim: %s" % ( calibrateError[0], calibrateError[1], trim) output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir, "gridlabD")) outRealPow = output["caliSub.csv"]["measured_real_power"][ trim:simLength] outImagPower = output["caliSub.csv"]["measured_reactive_power"][ trim:simLength] outAppPowerKw = [(x[0]**2 + x[1]**2)**0.5 / 1000 for x in zip(outRealPow, outImagPower)] lastFile = "subScada.player" nextFile = "subScadaCalibrated.player" nextPower = outAppPowerKw error = (sum(outRealPow) / 1000 - sum(scadaSubPower)) / sum(scadaSubPower) iteration = 1 print "First error:", error while abs(error) > calibrateError[0] and iteration < calibrateError[1]: # Run calibration and iterate up to 5 times. SCAL_CONST = sum(scadaSubPower) / sum(nextPower) print "Calibrating & running again... Error: %s, Iteration: %s, SCAL_CONST: %s" % ( str(round(abs(error * 100), 6)), str(iteration), round(SCAL_CONST, 6)) newPlayData = [] with open(pJoin(pJoin(workDir, "gridlabD"), lastFile), "r") as playerFile: for line in playerFile: (key, val) = line.split(',') newPlayData.append( str(key) + ',' + str(float(val) * SCAL_CONST) + "\n") with open(pJoin(pJoin(workDir, "gridlabD"), nextFile), "w") as playerFile: for row in newPlayData: playerFile.write(row) tree[playerKey]["file"] = nextFile tree[outputRecorderKey]["file"] = "caliSubCheck.csv" nextOutput = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin( workDir, "gridlabD")) outRealPowIter = nextOutput["caliSubCheck.csv"][ "measured_real_power"][trim:simLength] outImagPowerIter = nextOutput["caliSubCheck.csv"][ "measured_reactive_power"][trim:simLength] nextAppKw = [(x[0]**2 + x[1]**2)**0.5 / 1000 for x in zip(outRealPowIter, outImagPowerIter)] lastFile = nextFile nextFile = "subScadaCalibrated" + str(iteration) + ".player" nextPower = nextAppKw # Compute error and iterate. error = (sum(outRealPowIter) / 1000 - sum(scadaSubPower)) / sum(scadaSubPower) iteration += 1 else: if iteration == 1: outRealPowIter = outRealPow SCAL_CONST = 1.0 print "Calibration done: Error: %s, Iteration: %s, SCAL_CONST: %s" % ( str(round(abs(error * 100), 2)), str(iteration), round(SCAL_CONST, 2)) return outRealPow, outRealPowIter, lastFile, iteration outRealPow, outRealPowIter, lastFile, iteration = runPowerflowIter( tree, scadaSubPower[trim:simLength]) caliPowVectors = [[ float(element) for element in scadaSubPower[trim:simLength] ], [float(element) / 1000 for element in outRealPow], [float(element) / 1000 for element in outRealPowIter]] labels = ["scadaSubPower", "initialGuess", "finalGuess"] colors = ['red', 'lightblue', 'blue'] chartData = { "Title": "Substation Calibration Check (Iterated " + str(iteration + 1) + "X)", "fileName": "caliCheckPlot", "colors": colors, "labels": labels, "timeZone": simStartDate['timeZone'] } # Trimming vectors to make them all the same length as the smallest vector minCaliPowVecLen = min(len(caliPowVectors[0]), len(caliPowVectors[1]), len(caliPowVectors[2])) caliPowVectors[0] = caliPowVectors[0][:minCaliPowVecLen] caliPowVectors[1] = caliPowVectors[1][:minCaliPowVecLen] caliPowVectors[2] = caliPowVectors[2][:minCaliPowVecLen] print "Len:", len(caliPowVectors[0]), len(caliPowVectors[1]), len( caliPowVectors[2]) plotLine(workDir, caliPowVectors, chartData, simStartDate['Date'] + dt.timedelta(hours=trim), simLengthUnits) # Write the final output. with open(pJoin(workDir, "calibratedFeeder.omd"), "w") as outJson: playerString = open(pJoin(pJoin(workDir, "gridlabD"), lastFile)).read() feederJson["attachments"][lastFile] = playerString feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return
def omfCalibrate(workDir, feederPath, scadaPath): '''calibrates a feeder and saves the calibrated tree at a location''' with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) scadaSubPower = _processScadaData(workDir,scadaPath) # Force FBS powerflow, because NR fails a lot. for key in tree: if tree[key].get("module","").lower() == "powerflow": tree[key] = {"module":"powerflow","solver_method":"FBS"} # Attach player. classOb = {"class":"player", "variable_names":["value"], "variable_types":["double"]} playerOb = {"object":"player", "property":"value", "name":"scadaLoads", "file":"subScada.player", "loop":"0"} maxKey = feeder.getMaxKey(tree) tree[maxKey+1] = classOb tree[maxKey+2] = playerOb # Make loads reference player. loadTemplate = {"object": "triplex_load", "power_pf_12": "0.95", "impedance_pf_12": "0.98", "power_pf_12": "0.90", "impedance_fraction_12": "0.7", "power_fraction_12": "0.3"} for key in tree: ob = tree[key] if ob.get("object","") == "triplex_node" and ob.get("power_12","") != "": newOb = dict(loadTemplate) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") oldPow = ob.get("power_12","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower) tree[key] = newOb # Search for the substation regulator and attach a recorder there. for key in tree: if tree[key].get('bustype','').lower() == 'swing': swingName = tree[key].get('name') for key in tree: if tree[key].get('object','') == 'regulator' and tree[key].get('from','') == swingName: regIndex = key SUB_REG_NAME = tree[key]['name'] recOb = {"object": "recorder", "parent": SUB_REG_NAME, "property": "power_in.real,power_in.imag", "file": "caliSub.csv", "interval": "900"} tree[maxKey + 3] = recOb HOURS = 100 feeder.adjustTime(tree, HOURS, "hours", "2011-01-01") # Run Gridlabd. output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=workDir) # Calculate scaling constant. outRealPow = output["caliSub.csv"]["power_in.real"] outImagPower = output["caliSub.csv"]["power_in.imag"] outAppPowerKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPow, outImagPower)] # HACK: ignore first time step in output and input because GLD sometimes breaks the first step. SCAL_CONST = sum(scadaSubPower[1:HOURS])/sum(outAppPowerKw[1:HOURS]) # Rewrite the subScada.player file so all the power values are multiplied by the SCAL_CONSTANT. newPlayData = [] with open(pJoin(workDir, "subScada.player"), "r") as playerFile: for line in playerFile: (key,val) = line.split(',') newPlayData.append(str(key) + ',' + str(float(val)*SCAL_CONST) + "\n") with open(pJoin(workDir, "subScadaCalibrated.player"), "w") as playerFile: for row in newPlayData: playerFile.write(row) # Test by running a glm with subScadaCalibrated.player and caliSub.csv2. tree[maxKey+2]["file"] = "subScadaCalibrated.player" tree[maxKey + 3]["file"] = "caliSubCheck.csv" secondOutput = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=workDir) plt.plot(outAppPowerKw[1:HOURS], label="initialGuess") plt.plot(scadaSubPower[1:HOURS], label="scadaSubPower") secondAppKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(secondOutput["caliSubCheck.csv"]["power_in.real"], secondOutput["caliSubCheck.csv"]["power_in.imag"])] plt.plot(secondAppKw[1:HOURS], label="finalGuess") plt.legend(loc=3) plt.savefig(pJoin(workDir,"caliCheckPlot.png")) # Write the final output. with open(pJoin(workDir,"calibratedFeeder.json"),"w") as outJson: playerString = open(pJoin(workDir,"subScadaCalibrated.player")).read() feederJson["attachments"]["subScadaCalibrated.player"] = playerString feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return
def comparesol(modelDir,localTree): '''This reads a glm file, changes the method of powerflow and reruns''' print "Testing GridlabD solver." startTime = datetime.now() binaryName = "gridlabd" for key in localTree: if "solver_method" in localTree[key].keys(): solvmeth = localTree[key]["solver_method"] print "success", solvmeth if solvmeth == 'NR': localTree[key]["solver_method"] = 'FBS' else: localTree[key]["solver_method"] = 'NR' # feeder.attachRecorders(localTree, "Regulator", "object", "regulator") # feeder.attachRecorders(localTree, "CollectorVoltage", None, None) # last_key = len(localTree) # print last_key for key in localTree: if localTree[key].get('bustype','').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') print swingIndex, swingName for key in localTree: if localTree[key].get('object','') == 'regulator' and localTree[key].get('from','') == swingName: regIndex = key regConfName = localTree[key]['configuration'] print regIndex # Attach recorders relevant to CVR. recorders = [ {'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': localTree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag'}, {'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)'}, {'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': localTree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C'}, {'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': localTree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C'} ] biggest = 1 + max([int(k) for k in localTree.keys()]) for index, rec in enumerate(recorders): localTree[biggest + index] = rec max_key = max([int(key) for key in localTree.keys()]) print max_key regKeys = [] accum = "" for key in localTree: if localTree[key].get("object","") == "regulator": accum += localTree[key].get("name","ERROR") + "," regKeys.append(key) regstr = accum[:-1] print regKeys print regstr, type(regstr) localTree[max_key+1] = {'object' : 'volt_var_control', 'name' : 'volt_var_control', 'control_method' : 'ACTIVE', 'capacitor_delay' : '30.0', 'regulator_delay' : '30.0', 'desired_pf' : '0.99', 'd_max' : '0.6', 'd_min' : '0.1', 'substation_link' : 'substation_transformer', 'regulator_list' : regstr } feeder.adjustTime(tree=localTree, simLength=float("8760"), simLengthUnits="hours", simStartDate="2012-01-01") output = gridlabd.runInFilesystem(localTree,keepFiles=True,workDir=modelDir) os.remove(pJoin(modelDir,"PID.txt")) p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] xtime = {} for key in output: if '# timestamp' in output[key]: xtime['timeStamps'] = output[key]['# timestamp'] #print type(xtime['timeStamps'][0]) #print len(p) #xaxtick = str(xtime['timeStamps']) # plt.plot(range(8760),p) # plt.show() # print "p=" , p # print "q=" , q print "DONE RUNNING", modelDir