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): '''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","") == "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 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, 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 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