Beispiel #1
0
def addSingleBattery(battDict):
   #Check to see if node where battery is going to be added exists 
    feederTree = battDict['feederDict']
    nodeOK = 0
    for key1 in feederTree:
        for key2 in feederTree[key1]:
            if feederTree[key1][key2] == battDict['battNode'] and key2=='name':
                if 'object' in feederTree[key1] and (feederTree[key1]['object'] == 'meter' or feederTree[key1]['object'] == 'node'): #Attaching to a normal meter
                    meterType = 'meter'
                    phases = str( feederTree[key1]['phases'])
                elif 'object' in feederTree[key1] and (feederTree[key1]['object'] == 'triplex_meter' or feederTree[key1]['object'] == 'triplex_node'):
                    meterType = 'triplex_meter'
                    phases = str(feederTree[key1]['phases'])
                targetKey = key1  #Need this to find the nominal voltage of the node where the meter is being added.
                nodeOK = 1
                #Find available key value
                maxKey = feeder.getMaxKey(feederTree)          
    if nodeOK == 0:            
        raise Exception('Battery not able to be added at node {}; node does not exist.'.format(battDict['battNode']))  
    else:      
        #Getting parameters needed to add components
        meterNominalV = feederTree[targetKey]['nominal_voltage']
        #Add new meter, inverter, and battery to feeder dictionary
        meterName = str(battDict['battNode'])+'_meter'
        feederTree[str(maxKey + 1)]={'phases': phases, 
                                     'name': meterName,
                                     'parent':str(battDict['battNode']),
                                     'object': meterType,
                                     'nominal_voltage':str(meterNominalV)}  
        invName = str(battDict['battNode'])+'_inv'
        feederTree[str(maxKey + 2)]={'name':invName,
                                'parent': meterName,
                                'object': 'inverter',
                                'inverter_type':'FOUR_QUADRANT',
                                'four_quadrant_control_mode': battDict['invControl'].upper(),
                                'generator_mode':'CONSTANT_PQ',
                                'generator_status':'ONLINE',
                                'sense_object': str(battDict['controlSenseNode']),
                                'charge_lockout_time': str(battDict['invChargeLockoutTime']),
                                'discharge_lockout_time':str(battDict['invDischargeLockoutTime']),
                                'rated_power':str(battDict['invPowerRatingkW']) + ' kW',
                                'inverter_efficiency':str(battDict['invEfficiency']),
                                'charge_on_threshold':str(battDict['invChargeOnThresholdkW']) + ' kW',
                                'charge_off_threshold': str(battDict['invChargeOffThresholdkW']) + ' kW',
                                'discharge_on_threshold':str(battDict['invDischargeOnThresholdkW']) + ' kW',
                                'discharge_off_threshold':str(battDict['invDischargeOffThresholdkW']) + ' kW',
                                'max_discharge_rate':str(battDict['invMaxChargeRatekW']) + ' kW',
                                'max_charge_rate':str(battDict['invMaxDischargeRatekW']) + ' kW'} 
        batteryName = str(battDict['battNode'])+'_battery'
        feederTree[str(maxKey + 3)]={'name': batteryName,
                                'parent': invName,
                                'object': 'battery',
                                'use_internal_battery_model':'true',
                                'battery_type': str(battDict['battType']),
                                'battery_capacity': str(battDict['battEnergyRatingkWh']) + ' kWh',
                                'base_efficiency': str(battDict['battEfficiency']),
                                'state_of_charge': str(battDict['battSOCpu']),
                                'generator_mode':'SUPPLY_DRIVEN'}
    return feederTree
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
from matplotlib import pyplot as plt
import sys, json
sys.path.append('../..')
import feeder, weather
from solvers.gridlabd import runInFilesystem

# Make a weather file.
weather.makeClimateCsv('2014-01-01', '2014-01-11', 'MSP', './mspWeather.csv')

# Add stuff to the feeder.
with open('./Orville Tree Pond Calibrated.json', 'r') as inFile:
    myFeed = json.load(inFile)
    myTree = myFeed['tree']
# HACK:doesn't matter if we import modules twice, so just stick everything on the end.
oldMax = feeder.getMaxKey(myTree)
myTree[oldMax + 1] = {'omftype': 'module', 'argument': 'tape'}
myTree[oldMax + 2] = {'omftype': 'module', 'argument': 'climate'}
myTree[oldMax + 3] = {
    'object': 'csv_reader',
    'name': 'weatherReader',
    'filename': 'mspWeather.csv'
}
myTree[oldMax + 4] = {
    'object': 'climate',
    'name': 'exampleClimate',
    'tmyfile': 'mspWeather.csv',
    'reader': 'weatherReader'
}
# Add a few panels too to test.
myTree[oldMax + 5] = {
Beispiel #7
0
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
Beispiel #8
0
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
Beispiel #9
0
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
from matplotlib import pyplot as plt
import sys, json
sys.path.append('../..')
import feeder, weather
from solvers.gridlabd import runInFilesystem

# Make a weather file.
weather.makeClimateCsv('2014-01-01', '2014-01-11', 'MSP', './mspWeather.csv')

# Add stuff to the feeder.
with open('./Orville Tree Pond Calibrated.json','r') as inFile:
	myFeed = json.load(inFile)
	myTree = myFeed['tree']
# HACK:doesn't matter if we import modules twice, so just stick everything on the end.
oldMax = feeder.getMaxKey(myTree)
myTree[oldMax + 1] = {'omftype':'module', 'argument':'tape'}
myTree[oldMax + 2] = {'omftype':'module', 'argument':'climate'}
myTree[oldMax + 3] = {'object':'csv_reader', 'name':'weatherReader', 'filename':'mspWeather.csv'}
myTree[oldMax + 4] = {'object':'climate', 'name':'exampleClimate', 'tmyfile':'mspWeather.csv', 'reader':'weatherReader'}
# Add a few panels too to test.
myTree[oldMax + 5] = {'name':'solEngInverter', 
	'parent':'node19 23CC 01001447022_A', 
	'generator_status':'ONLINE', 
	'inverter_type':'PWM',
	'object':'inverter',
	'generator_mode':'CONSTANT_PF' }
myTree[oldMax + 6] = {'generator_mode':'SUPPLY_DRIVEN', 
	'name':'solar172879', 
	'parent':'solEngInverter', 
	'area':'30000 sf', 
Beispiel #11
0
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
Beispiel #13
0
def addBattery(battDict):
    #Validating input dictionary keys
    validKeys= ['feederDict',                   #REQUIRED
                'battNode',                     #REQUIRED     
                'battEnergyRatingkWh',          #REQUIRED
                'invControl',                   #REQUIRED
                'battEfficiency',               #optional
                'battType',                     #optional
                'battSOCpu',                    #optional   
                'invPowerRatingkW',             #REQUIRED
                'invChargeLockoutTime',         #optional
                'invDischargeLockoutTime',      #optional
                'invEfficiency',                #optional
                'invChargeOnThresholdkW',       #REQUIRED for LOAD_FOLLOWING control mode if and only if controlSenseAveragePowerkW not specified
                'invChargeOffThresholdkW',      #REQUIRED for LOAD_FOLLOWING control mode if and only if controlSenseAveragePowerkW not specified
                'invDischargeOffThresholdkW',   #REQUIRED for LOAD_FOLLOWING control mode if and only if controlSenseAveragePowerkW not specified
                'invDischargeOnThresholdkW',    #REQUIRED for LOAD_FOLLOWING control mode if and only if controlSenseAveragePowerkW not specified
                'invMaxDischargeRatekW',        #optional
                'invMaxChargeRatekW',           #optional
                'controlSenseNode',             #REQUIRED for LOAD_FOLLOWING
                'controlSenseAveragePowerkW',   #REQUIRED for LOAD_FOLLOWING control mode if and only if invCharge/DischargeOn/OffThresholdKW not specified
                'constantPOut',                 #REQUIRED for CONSTANT_PQ control mode      
                'constantQout']                 #REQUIRED for CONSTANT_PQ control mode 
    for key1 in battDict:
        if (key1 not in validKeys) and (key1 != 'feederDict'):
            raise KeyError ('{} found in input parameter dictionary and is not a valid parameter.'.format(key1))
    #Validating string parameter values
    if 'invControl' in battDict:
        isinstance(battDict['invControl'], basestring)
    else:
        raise Exception('Required parameter inverter control mode (\'invControl\') is undefined. ')
    if 'battType' in battDict:
        isinstance(battDict['battType'], basestring)
    else:
        battDict['battType'] = 'LI_ION'
        warnings.warn ('Battery type (\'battType\') is undefined, setting to \'LI_ION\'.')
    #Validating numeric parameter values
    if 'battEnergyRatingkWh' in battDict:
        if not (isinstance(battDict['battEnergyRatingkWh'], int) or isinstance(battDict['battEnergyRatingkWh'], float)) :
            raise TypeError('Parameter \'battEnergyRatingkWh\' is \'{}\', must be a numeric value.'.format(battDict['battEnergyRatingkWh']))
    else:
        raise Exception('Required parameter battery power rating (\'battEnergyRatingkWh\') undefined.')
    if 'battEfficiency' in battDict:
        if not (isinstance(battDict['battEfficiency'], int) or isinstance(battDict['battEfficiency'], float)) :
            raise TypeError('Parameter \'battEfficiency\' is \'{}\', must be a numeric value.'.format(battDict['battEfficiency']))
    else:
        battDict['battEfficiency'] = 0.85
        warnings.warn('Battery efficiency (\'battEfficiency\') undefined, setting to 0.85 (85%).')
    if 'battSOCpu' in battDict:
        if not (isinstance(battDict['battSOCpu'], int) or isinstance(battDict['battSOCpu'], float)) :
            raise TypeError('Parameter \'battSOCpu\' is \'{}\', must be a numeric value.'.format(battDict['battSOCpu']))
    else:
        battDict['battSOCpu'] = 1
        warnings.warn('Battery initial state-of-charge (\'battSOCpu\') undefined, setting to 1pu (full).')    
    if 'invPowerRatingkW' in battDict:
        if not (isinstance(battDict['invPowerRatingkW'], int) or isinstance(battDict['invPowerRatingkW'], float)) :
            raise TypeError('Parameter \'invPowerRatingkW\' is \'{}\', must be a numeric value.'.format(battDict['invPowerRatingkW']))
    else:
        raise Exception('Required parameter inverter power rating (\'invPowerRatingkW\') undefined.')
    if 'invChargeLockoutTime' in battDict:
        if not (isinstance(battDict['invChargeLockoutTime'], int) or isinstance(battDict['invChargeLockoutTime'], float)) :
            raise TypeError('Parameter \'invChargeLockoutTime\' is \'{}\', must be a numeric value.'.format(battDict['invChargeLockoutTime']))
    else:
        battDict['invChargeLockoutTime'] = 5
        warnings.warn('Inverter charge lockout time (\'invChargeLockoutTime\') undefined, setting to 5 seconds.')
    if 'invDischargeLockoutTime' in battDict:
        if not (isinstance(battDict['invDischargeLockoutTime'], int) or isinstance(battDict['invDischargeLockoutTime'], float)) :
            raise TypeError('Parameter \'invDischargeLockoutTime\' is \'{}\', must be a numeric type.'.format(battDict['invDischargeLockoutTime']))
    else:
        battDict['invDischargeLockoutTime'] = 5
        warnings.warn('Inverter discharge lockout time (\'invDischargeLockoutTime\') undefined, setting to 5 seconds.')
    if 'invEfficiency' in battDict:
        if not (isinstance(battDict['invEfficiency'], int) or isinstance(battDict['invEfficiency'], float)) :
            raise TypeError('Parameter \'invEfficiency\' is \'{}\', must be a numeric value.'.format(battDict['invEfficiency']))
    else:
        battDict['invEfficiency'] = 0.95
        warnings.warn('Inverter efficiency (\'invEfficiency\') undefined, setting to 0.95 (95%).' ) 
    if 'invMaxDischargeRatekW' in battDict:
        if not (isinstance(battDict['invMaxDischargeRatekW'], int) or isinstance(battDict['invMaxDischargeRatekW'], float)) :
            raise TypeError('Parameter \'invMaxDischargeRatekW\' is \'{}\', must be a numeric value.'.format(battDict['invMaxDischargeRatekW']))
    else:
        battDict['invMaxDischargeRatekW'] = battDict['invPowerRatingkW']
        warnings.warn('Inverter maximum discharge rate (\'invMaxDischargeRatekW\') undefined, setting to inverter power rating of {}kW.'.format(battDict['invPowerRatingkW']))   
    if 'invMaxChargeRatekW' in battDict:
        if not (isinstance(battDict['invMaxChargeRatekW'], int) or isinstance(battDict['invMaxChargeRatekW'], float)) :
            raise TypeError('Parameter \'invMaxChargeRatekW\' is \'{}\', must be a numeric value.'.format(battDict['invMaxChargeRatekW']))
    else:
        battDict['invMaxChargeRatekW'] = battDict['invPowerRatingkW']
        warnings.warn('Inverter maximum charge rate (\'invMaxChargeRatekW\') undefined, setting to inverter power rating of {}kW.'.format(battDict['invPowerRatingkW']))  
    if battDict['invControl'].upper() == 'CONSTANT_PQ':
        if 'constantPOut' in battDict:
            if not (isinstance(battDict['constantPOut'], int) or isinstance(battDict['constantPOut'], float)) :
                raise TypeError('Parameter \'constantPOut\' is \'{}\', must be a numeric value.'.format(battDict['constantPOut']))
        else:
            raise Exception('Required parameter constant real power out (\'constantPOut\') is undefined. ')
        if 'constantQOUT' in battDict:
            if not (isinstance(battDict['constantQOut'], int) or isinstance(battDict['constantQOut'], float)) :
                raise TypeError('Parameter \'constantQOut\' is \'{}\', must be a numeric value.'.format(battDict['constantQOut']))
        else:
            raise Exception('Required parameter constant reactive power out (\'constantQOut\') is undefined. ')
    elif battDict['invControl'].upper() == 'LOAD_FOLLOWING':
        if 'controlSenseAveragePowerkW' in battDict:
            if('invChargeOffThresholdkW' in battDict or 'invDischargeOffThresholdkW' in battDict or 
            'invChargeOnThresholdkW' in battDict or 'invDischargeOnThresholdkW' in battDict):
                raise Exception('Conflicting inverter settings detected. Only either \'controlSenseAveragePowerkW\' or the set of \'invChargeOffThresholdkW\', \'invChargeOnThresholdkW\', \'invDischargeOffThresholdkW\', \'invDischargeOnThresholdkW\' can be defined.')
            if not (isinstance(battDict['controlSenseAveragePowerkW'], int) or isinstance(battDict['controlSenseAveragePowerkW'], float)) :
                raise TypeError('Parameter \'controlSenseAveragePowerkW\' is \'{}\', must be a numeric type.'.format(battDict['controlSenseAveragePowerkW']))
            print 'Setting inverter hysteris to default levels based on value of \'invChargeOnThresholdkW\'.'
            battDict['invChargeOffThresholdkW'] = battDict['controlSenseAveragePowerkW'] * 0.95
            battDict['invDischargeOffThresholdkW'] = battDict['controlSenseAveragePowerkW'] * 1.05
            battDict['invChargeOnThresholdkW'] = battDict['controlSenseAveragePowerkW'] * 0.7
            battDict['invDischargeOnThresholdkW'] = battDict['controlSenseAveragePowerkW'] * 1.3
        elif('invChargeOffThresholdkW' in battDict and 'invDischargeOffThresholdkW' in battDict and 
            'invChargeOnThresholdkW' in battDict and 'invDischargeOnThresholdkW' in battDict):
            if 'controlSenseAveragePowerkW' in battDict:
                raise Exception('Conflicting inverter settings detected. Only either \'controlSenseAveragePowerkW\' or the set of \'invChargeOffThresholdkW\', \'invChargeOnThresholdkW\', \'invDischargeOffThresholdkW\', \'invDischargeOnThresholdkW\' can be defined.')
        else:
            raise Exception('Either \'controlSenseAveragePowerkW\' or the set of \'invChargeOffThresholdkW\', \'invChargeOnThresholdkW\', \'invDischargeOffThresholdkW\', \'invDischargeOnThresholdkW\' can and must be defined.') 
    else:
        raise ValueError('{} is an unsupported value for \'invControl\'. Valid values are \'CONSTAN_PQ\' or \'LOAD_FOLLOWING\'.'.format(battDict['invControl']))
    #Adding in generators module declaration
    maxKey = feeder.getMaxKey(battDict['feederDict'])
    battDict['feederDict'][str(maxKey + 1)]={'omftype': 'module', 
                                             'argument': 'generators'}
    battNodeList = battDict['battNode']
    for index in range(len(battNodeList)):
        battDict['battNode'] = battNodeList[index]
        battDict['feederDict'] = addSingleBattery(battDict)
    return battDict['feederDict']
Beispiel #14
0
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