def run(modelDir, inputDict, fs): ''' Run the model in its directory. ''' logger.info("Running voltageDrop model... modelDir: %s; inputDict: %s", modelDir, inputDict) startTime = dt.datetime.now() allOutput = {} # Check whether model exist or not if not os.path.isdir(modelDir): os.makedirs(modelDir) inputDict["created"] = str(dt.datetime.now()) with open(pJoin(modelDir, "allInputData.json"), "w") as inputFile: json.dump(inputDict, inputFile, indent=4) # Copy feeder data into the model directory. feederDir, feederName = inputDict["feederName"].split("___") fs.export_from_fs_to_local(pJoin("data", "Feeder", feederDir, feederName + ".json"), pJoin(modelDir, "feeder.json")) # Create voltage drop plot. tree = json.load(open(pJoin(modelDir, "feeder.json"))).get("tree", {}) if inputDict.get("layoutAlgorithm", "geospatial") == "geospatial": neato = False else: neato = True chart = voltPlot(tree, workDir=modelDir, neatoLayout=neato) Plot.save_fig(plt, pJoin(modelDir, "output.png")) with open(pJoin(modelDir, "output.png"), "rb") as inFile: allOutput["voltageDrop"] = inFile.read().encode("base64") fs.save(pJoin(modelDir, "allOutputData.json"), json.dumps(allOutput, indent=4)) # Update the runTime in the input file. endTime = dt.datetime.now() inputDict["runTime"] = str( dt.timedelta(seconds=int((endTime - startTime).total_seconds()))) fs.save(pJoin(modelDir, "allInputData.json"), json.dumps(inputDict, indent=4))
def runForeground(modelDir, inputDict, fs): ''' Run the model in the foreground. WARNING: can take about a minute. ''' # Global vars, and load data from the model directory. print "STARTING TO RUN", modelDir try: startTime = datetime.datetime.now() feederJson = json.load(open(pJoin(modelDir, "feeder.json"))) tree = feederJson.get("tree", {}) attachments = feederJson.get("attachments", {}) allOutput = {} ''' Run CVR analysis. ''' # Reformate monthData and rates. rates = {k: float(inputDict[k]) for k in ["capitalCost", "omCost", "wholesaleEnergyCostPerKwh", "retailEnergyCostPerKwh", "peakDemandCostSpringPerKw", "peakDemandCostSummerPerKw", "peakDemandCostFallPerKw", "peakDemandCostWinterPerKw"]} # print "RATES", rates monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] monthToSeason = {'January': 'Winter', 'February': 'Winter', 'March': 'Spring', 'April': 'Spring', 'May': 'Spring', 'June': 'Summer', 'July': 'Summer', 'August': 'Summer', 'September': 'Fall', 'October': 'Fall', 'November': 'Fall', 'December': 'Winter'} monthData = [] for i, x in enumerate(monthNames): monShort = x[0:3].lower() season = monthToSeason[x] histAvg = float(inputDict.get(monShort + "Avg", 0)) histPeak = float(inputDict.get(monShort + "Peak", 0)) monthData.append({"monthId": i, "monthName": x, "histAverage": histAvg, "histPeak": histPeak, "season": season}) # for row in monthData: # print row # Graph the SCADA data. fig = plt.figure(figsize=(10, 6)) indices = [r['monthName'] for r in monthData] d1 = [r['histPeak'] / (10**3) for r in monthData] d2 = [r['histAverage'] / (10**3) for r in monthData] ticks = range(len(d1)) bar_peak = plt.bar(ticks, d1, color='gray') bar_avg = plt.bar(ticks, d2, color='dimgray') plt.legend([bar_peak[0], bar_avg[0]], ['histPeak', 'histAverage'], bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.xticks([t + 0.5 for t in ticks], indices) plt.ylabel('Mean and peak historical power consumptions (kW)') fig.autofmt_xdate() Plot.save_fig(plt, pJoin(modelDir, "scadaChart.png")) allOutput["histPeak"] = d1 allOutput["histAverage"] = d2 allOutput["monthName"] = [name[0:3] for name in monthNames] # Graph feeder. fig = plt.figure(figsize=(10, 10)) myGraph = omf.feeder.treeToNxGraph(tree) omf.feeder.latLonNxGraph(myGraph, neatoLayout=False) Plot.save_fig(plt, pJoin(modelDir, "feederChart.png")) with open(pJoin(modelDir, "feederChart.png"), "rb") as inFile: allOutput["feederChart"] = inFile.read().encode("base64") # Get the load levels we need to test. allLoadLevels = [x.get( 'histPeak', 0) for x in monthData] + [y.get('histAverage', 0) for y in monthData] maxLev = _roundOne(max(allLoadLevels), 'up') minLev = _roundOne(min(allLoadLevels), 'down') tenLoadLevels = range( int(minLev), int(maxLev), int((maxLev - minLev) / 10)) # Gather variables from the feeder. for key in tree.keys(): # Set clock to single timestep. if tree[key].get('clock', '') == 'clock': tree[key] = {"timezone": "PST+8PDT", "stoptime": "'2013-01-01 00:00:00'", "starttime": "'2013-01-01 00:00:00'", "clock": "clock"} # Save swing node index. if tree[key].get('bustype', '').lower() == 'swing': swingIndex = key swingName = tree[key].get('name') # Remove all includes. if tree[key].get('omftype', '') == '#include': del key # Find the substation regulator and config. for key in tree: if tree[key].get('object', '') == 'regulator' and tree[key].get('from', '') == swingName: regIndex = key regConfName = tree[key]['configuration'] for key in tree: if tree[key].get('name', '') == regConfName: regConfIndex = key # Set substation regulator to manual operation. # GLOBAL VARIABLE FOR DEFAULT TAP POSITION baselineTap = int(inputDict.get("baselineTap")) tree[regConfIndex] = { 'name': tree[regConfIndex]['name'], 'object': 'regulator_configuration', 'connect_type': '1', 'raise_taps': '10', 'lower_taps': '10', 'CT_phase': 'ABC', 'PT_phase': 'ABC', # Yo, 0.10 means at tap_pos 10 we're 10% above 120V. 'regulation': '0.10', 'Control': 'MANUAL', 'control_level': 'INDIVIDUAL', 'Type': 'A', 'tap_pos_A': str(baselineTap), 'tap_pos_B': str(baselineTap), 'tap_pos_C': str(baselineTap)} # Attach recorders relevant to CVR. recorders = [ {'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': tree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag'}, {'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)'}, {'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': tree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C'}, {'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': tree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C'}] biggest = 1 + max([int(k) for k in tree.keys()]) for index, rec in enumerate(recorders): tree[biggest + index] = rec # Change constant PF loads to ZIP loads. (See evernote for rationale # about 50/50 power/impedance mix.) blankZipModel = {'object': 'triplex_load', 'name': 'NAMEVARIABLE', 'base_power_12': 'POWERVARIABLE', 'power_fraction_12': str(inputDict.get("p_percent")), 'impedance_fraction_12': str(inputDict.get("z_percent")), 'current_fraction_12': str(inputDict.get("i_percent")), # MAYBEFIX: we can probably get this PF data from the # Milsoft loads. 'power_pf_12': str(inputDict.get("power_factor")), 'impedance_pf_12': str(inputDict.get("power_factor")), 'current_pf_12': str(inputDict.get("power_factor")), 'nominal_voltage': '120', 'phases': 'PHASESVARIABLE', 'parent': 'PARENTVARIABLE'} def powerClean(powerStr): ''' take 3339.39+1052.29j to 3339.39 ''' return powerStr[0:powerStr.find('+')] for key in tree: if tree[key].get('object', '') == 'triplex_node': # Get existing variables. name = tree[key].get('name', '') power = tree[key].get('power_12', '') parent = tree[key].get('parent', '') phases = tree[key].get('phases', '') # Replace object and reintroduce variables. tree[key] = copy(blankZipModel) tree[key]['name'] = name tree[key]['base_power_12'] = powerClean(power) tree[key]['parent'] = parent tree[key]['phases'] = phases # Function to determine how low we can tap down in the CVR case: def loweringPotential(baseLine): ''' Given a baseline end of line voltage, how many more percent can we shave off the substation voltage? ''' ''' testsWePass = [122.0,118.0,200.0,110.0] ''' lower = int(math.floor((baseLine / 114.0 - 1) * 100)) - 1 # If lower is negative, we can't return it because we'd be # undervolting beyond what baseline already was! if lower < 0: return baselineTap else: return baselineTap - lower # Run all the powerflows. powerflows = [] for doingCvr in [False, True]: # For each load level in the tenLoadLevels, run a powerflow with # the load objects scaled to the level. for desiredLoad in tenLoadLevels: # Find the total load that was defined in Milsoft: loadList = [] for key in tree: if tree[key].get('object', '') == 'triplex_load': loadList.append(tree[key].get('base_power_12', '')) totalLoad = sum([float(x) for x in loadList]) # Rescale each triplex load: for key in tree: if tree[key].get('object', '') == 'triplex_load': currentPow = float(tree[key]['base_power_12']) ratio = desiredLoad / totalLoad tree[key]['base_power_12'] = str(currentPow * ratio) # If we're doing CVR then lower the voltage. if doingCvr: # Find the minimum voltage we can tap down to: newTapPos = baselineTap for row in powerflows: if row.get('loadLevel', '') == desiredLoad: newTapPos = loweringPotential( row.get('lowVoltage', 114)) # Tap it down to there. # MAYBEFIX: do each phase separately because that's how # it's done in the field... Oof. tree[regConfIndex]['tap_pos_A'] = str(newTapPos) tree[regConfIndex]['tap_pos_B'] = str(newTapPos) tree[regConfIndex]['tap_pos_C'] = str(newTapPos) # Run the model through gridlab and put outputs in the table. output = gridlabd.runInFilesystem(tree, attachments=attachments, keepFiles=True, workDir=modelDir) os.remove(pJoin(modelDir, "PID.txt")) p = output['Zregulator.csv']['power_in.real'][0] q = output['Zregulator.csv']['power_in.imag'][0] s = math.sqrt(p**2 + q**2) lossTotal = 0.0 for device in ['ZlossesOverhead.csv', 'ZlossesTransformer.csv', 'ZlossesUnderground.csv']: for letter in ['A', 'B', 'C']: r = output[device][ 'sum(power_losses_' + letter + '.real)'][0] i = output[device][ 'sum(power_losses_' + letter + '.imag)'][0] lossTotal += math.sqrt(r**2 + i**2) # Entire output: powerflows.append({ 'doingCvr': doingCvr, 'loadLevel': desiredLoad, 'realPower': p, 'powerFactor': p / s, 'losses': lossTotal, 'subVoltage': ( output['ZsubstationBottom.csv']['voltage_A'][0] + output['ZsubstationBottom.csv']['voltage_B'][0] + output['ZsubstationBottom.csv']['voltage_C'][0]) / 3 / 60, 'lowVoltage': output['ZvoltageJiggle.csv']['min(voltage_12.mag)'][0] / 2, 'highVoltage': output['ZvoltageJiggle.csv']['max(voltage_12.mag)'][0] / 2}) # For a given load level, find two points to interpolate on. def getInterpPoints(t): ''' Find the two points we can interpolate from. ''' ''' tests pass on [tenLoadLevels[0],tenLoadLevels[5]+499,tenLoadLevels[-1]-988] ''' loc = sorted(tenLoadLevels + [t]).index(t) if loc == 0: return (tenLoadLevels[0], tenLoadLevels[1]) elif loc > len(tenLoadLevels) - 2: return (tenLoadLevels[-2], tenLoadLevels[-1]) else: return (tenLoadLevels[loc - 1], tenLoadLevels[loc + 1]) # Calculate peak reduction. for row in monthData: peak = row['histPeak'] peakPoints = getInterpPoints(peak) peakTopBase = [x for x in powerflows if x.get( 'loadLevel', '') == peakPoints[-1] and x.get('doingCvr', '') == False][0] peakTopCvr = [x for x in powerflows if x.get( 'loadLevel', '') == peakPoints[-1] and x.get('doingCvr', '') == True][0] peakBottomBase = [x for x in powerflows if x.get( 'loadLevel', '') == peakPoints[0] and x.get('doingCvr', '') == False][0] peakBottomCvr = [x for x in powerflows if x.get( 'loadLevel', '') == peakPoints[0] and x.get('doingCvr', '') == True][0] # Linear interpolation so we aren't running umpteen million # loadflows. x = (peakPoints[0], peakPoints[1]) y = (peakTopBase['realPower'] - peakTopCvr['realPower'], peakBottomBase['realPower'] - peakBottomCvr['realPower']) peakRed = y[0] + (y[1] - y[0]) * (peak - x[0]) / (x[1] - x[0]) row['peakReduction'] = peakRed # Calculate energy reduction and loss reduction based on average load. for row in monthData: avgEnergy = row['histAverage'] energyPoints = getInterpPoints(avgEnergy) avgTopBase = [x for x in powerflows if x.get( 'loadLevel', '') == energyPoints[-1] and x.get('doingCvr', '') == False][0] avgTopCvr = [x for x in powerflows if x.get( 'loadLevel', '') == energyPoints[-1] and x.get('doingCvr', '') == True][0] avgBottomBase = [x for x in powerflows if x.get( 'loadLevel', '') == energyPoints[0] and x.get('doingCvr', '') == False][0] avgBottomCvr = [x for x in powerflows if x.get( 'loadLevel', '') == energyPoints[0] and x.get('doingCvr', '') == True][0] # Linear interpolation so we aren't running umpteen million # loadflows. x = (energyPoints[0], energyPoints[1]) y = (avgTopBase['realPower'] - avgTopCvr['realPower'], avgBottomBase['realPower'] - avgBottomCvr['realPower']) energyRed = y[0] + \ (y[1] - y[0]) * (avgEnergy - x[0]) / (x[1] - x[0]) row['energyReduction'] = energyRed lossY = (avgTopBase['losses'] - avgTopCvr['losses'], avgBottomBase['losses'] - avgBottomCvr['losses']) lossRed = lossY[0] + (lossY[1] - lossY[0]) * \ (avgEnergy - x[0]) / (x[1] - x[0]) row['lossReduction'] = lossRed # Multiply by dollars. for row in monthData: row['energyReductionDollars'] = row['energyReduction'] / 1000 * \ (rates['wholesaleEnergyCostPerKwh'] - rates['retailEnergyCostPerKwh']) row['peakReductionDollars'] = row['peakReduction'] / \ 1000 * rates['peakDemandCost' + row['season'] + 'PerKw'] row['lossReductionDollars'] = row['lossReduction'] / \ 1000 * rates['wholesaleEnergyCostPerKwh'] # Pretty output def plotTable(inData): fig = plt.figure(figsize=(10, 5)) plt.axis('off') plt.tight_layout() plt.table(cellText=[row for row in inData[1:]], loc='center', rowLabels=range(len(inData) - 1), colLabels=inData[0]) def dictalToMatrix(dictList): ''' Take our dictal format to a matrix. ''' matrix = [dictList[0].keys()] for row in dictList: matrix.append(row.values()) return matrix # Powerflow results. plotTable(dictalToMatrix(powerflows)) Plot.save_fig(plt, pJoin(modelDir, "powerflowTable.png")) # Monetary results. # To print partial money table monthDataMat = dictalToMatrix(monthData) dimX = len(monthDataMat) dimY = len(monthDataMat[0]) monthDataPart = [] for k in range(0, dimX): monthDatatemp = [] for m in range(4, dimY): monthDatatemp.append(monthDataMat[k][m]) monthDataPart.append(monthDatatemp) plotTable(monthDataPart) Plot.save_fig(plt, pJoin(modelDir, "moneyTable.png")) allOutput["monthDataMat"] = dictalToMatrix(monthData) allOutput["monthDataPart"] = monthDataPart # Graph the money data. fig = plt.figure(figsize=(10, 8)) indices = [r['monthName'] for r in monthData] d1 = [r['energyReductionDollars'] for r in monthData] d2 = [r['lossReductionDollars'] for r in monthData] d3 = [r['peakReductionDollars'] for r in monthData] ticks = range(len(d1)) bar_erd = plt.bar(ticks, d1, color='red') bar_lrd = plt.bar(ticks, d2, color='green') bar_prd = plt.bar(ticks, d3, color='blue', yerr=d2) plt.legend([bar_prd[0], bar_lrd[0], bar_erd[0]], ['peakReductionDollars', 'lossReductionDollars', 'energyReductionDollars'], bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.xticks([t + 0.5 for t in ticks], indices) plt.ylabel('Utility Savings ($)') plt.tight_layout(5.5, 1.3, 1.2) fig.autofmt_xdate() Plot.save_fig(plt, pJoin(modelDir, "spendChart.png")) allOutput["energyReductionDollars"] = d1 allOutput["lossReductionDollars"] = d2 allOutput["peakReductionDollars"] = d3 # Graph the cumulative savings. fig = plt.figure(figsize=(10, 5)) annualSavings = sum(d1) + sum(d2) + sum(d3) annualSave = lambda x: ( annualSavings - rates['omCost']) * x - rates['capitalCost'] simplePayback = rates['capitalCost'] / \ (annualSavings - rates['omCost']) plt.xlabel('Year After Installation') plt.xlim(0, 30) plt.ylabel('Cumulative Savings ($)') plt.plot([0 for x in range(31)], c='gray') plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--') plt.plot([annualSave(x) for x in range(31)], c='green') Plot.save_fig(plt, pJoin(modelDir, "savingsChart.png")) allOutput["annualSave"] = [annualSave(x) for x in range(31)] # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str( datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) fs.save(pJoin(modelDir, "allInputData.json"), json.dumps(inputDict, indent=4)) # Write output file. fs.save(pJoin(modelDir, "allOutputData.json"), json.dumps(allOutput, indent=4)) # For autotest, there won't be such file. try: os.remove(pJoin(modelDir, "PPID.txt")) except: pass print "DONE RUNNING", modelDir except Exception as e: print "Oops, Model Crashed!!!" print e cancel(modelDir)
def runForeground(modelDir, inData, fs): '''This reads a glm file, changes the method of powerflow and reruns''' try: startTime = datetime.now() # calibrate and run cvrdynamic feederPath = pJoin("data", "Feeder", inData[ "feederName"].split("___")[0], inData["feederName"].split("___")[1] + '.json') fs.export_from_fs_to_local(feederPath, feederPath) scadaPath = pJoin("uploads", (inData["scadaFile"] + '.tsv')) fs.export_from_fs_to_local(scadaPath, scadaPath) omf.calibrate.omfCalibrate(modelDir, feederPath, scadaPath) allOutput = {} print "here" with open(pJoin(modelDir, "calibratedFeeder.json"), "r") as jsonIn: feederJson = json.load(jsonIn) localTree = feederJson.get("tree", {}) for key in localTree: if "solver_method" in localTree[key].keys(): print "current solver method", localTree[key]["solver_method"] localTree[key]["solver_method"] = 'FBS' # find the swing bus and recorder attached to substation for key in localTree: if localTree[key].get('bustype', '').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') if localTree[key].get('object', '') == 'regulator' and localTree[key].get('from', '') == swingName: regIndex = key regConfName = localTree[key]['configuration'] # find the regulator and capacitor names and combine to form a string # for volt-var control object regKeys = [] accum_reg = "" for key in localTree: if localTree[key].get("object", "") == "regulator": accum_reg += localTree[key].get("name", "ERROR") + "," regKeys.append(key) regstr = accum_reg[:-1] print regKeys capKeys = [] accum_cap = "" for key in localTree: if localTree[key].get("object", "") == "capacitor": accum_cap += localTree[key].get("name", "ERROR") + "," capKeys.append(key) if localTree[key].get("control", "").lower() == "manual": localTree[key]['control'] = "VOLT" print "changing capacitor control from manual to volt" capstr = accum_cap[:-1] print capKeys # Attach recorders relevant to CVR. recorders = [ {'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'}, {'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': localTree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag'}, {'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)'}, {'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': localTree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C'}, {'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': localTree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C'}] # recorder object for capacitor switching - if capacitors exist if capKeys != []: for key in capKeys: recorders.append({'object': 'recorder', 'file': 'ZcapSwitch' + str(key) + '.csv', 'limit': '0', 'parent': localTree[key]['name'], 'property': 'switchA,switchB,switchC'}) # attach recorder process biggest = 1 + max([int(k) for k in localTree.keys()]) for index, rec in enumerate(recorders): localTree[biggest + index] = rec # run a reference load flow HOURS = float(inData['simLengthHours']) simStartDate = inData['simStart'] feeder.adjustTime(localTree, HOURS, "hours", simStartDate) output = gridlabd.runInFilesystem( localTree, keepFiles=False, workDir=modelDir) os.remove(pJoin(modelDir, "PID.txt")) p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] # calculating length of simulation because it migth be different from # the simulation input HOURS simRealLength = int(len(p)) # time delays from configuration files time_delay_reg = '30.0' time_delay_cap = '300.0' for key in localTree: if localTree[key].get('object', '') == "regulator_configuration": time_delay_reg = localTree[key]['time_delay'] print "time_delay_reg", time_delay_reg # if localTree[key].get('object','') == "capacitor": # time_delay_cap = localTree[key]['time_delay'] # print "time_delay_cap",time_delay_cap # change the recorder names for key in localTree: if localTree[key].get('object', '') == "collector" or localTree[key].get('object', '') == "recorder": if localTree[key].get('file', '').startswith('Z'): localTree[key]['file'] = localTree[key].get( 'file', '').replace('Z', 'NewZ') # create volt-var control object max_key = max([int(key) for key in localTree.keys()]) print max_key localTree[max_key + 1] = {'object': 'volt_var_control', 'name': 'IVVC1', 'control_method': 'ACTIVE', 'capacitor_delay': str(time_delay_cap), 'regulator_delay': str(time_delay_reg), 'desired_pf': '0.99', 'd_max': '0.6', 'd_min': '0.1', 'substation_link': str(localTree[regIndex]['name']), 'regulator_list': regstr, 'capacitor_list': capstr} # running powerflow analysis via gridalab after attaching a regulator feeder.adjustTime(localTree, HOURS, "hours", simStartDate) output1 = gridlabd.runInFilesystem( localTree, keepFiles=True, workDir=modelDir) os.remove(pJoin(modelDir, "PID.txt")) pnew = output1['NewZregulator.csv']['power_in.real'] qnew = output1['NewZregulator.csv']['power_in.imag'] # total real and imaginary losses as a function of time def vecSum(u, v): ''' Add vectors u and v element-wise. Return has len <= len(u) and <=len(v). ''' return map(sum, zip(u, v)) def zeroVec(length): ''' Give a zero vector of input length. ''' return [0 for x in xrange(length)] (realLoss, imagLoss, realLossnew, imagLossnew) = (zeroVec(int(HOURS)) for x in range(4)) for device in ['ZlossesOverhead.csv', 'ZlossesTransformer.csv', 'ZlossesUnderground.csv']: for letter in ['A', 'B', 'C']: realLoss = vecSum( realLoss, output[device]['sum(power_losses_' + letter + '.real)']) imagLoss = vecSum( imagLoss, output[device]['sum(power_losses_' + letter + '.imag)']) realLossnew = vecSum( realLossnew, output1['New' + device]['sum(power_losses_' + letter + '.real)']) imagLossnew = vecSum( imagLossnew, output1['New' + device]['sum(power_losses_' + letter + '.imag)']) # voltage calculations and tap calculations def divby2(u): '''divides by 2''' return u / 2 lowVoltage = [] meanVoltage = [] highVoltage = [] lowVoltagenew = [] meanVoltagenew = [] highVoltagenew = [] tap = {'A': [], 'B': [], 'C': []} tapnew = {'A': [], 'B': [], 'C': []} volt = {'A': [], 'B': [], 'C': []} voltnew = {'A': [], 'B': [], 'C': []} switch = {'A': [], 'B': [], 'C': []} switchnew = {'A': [], 'B': [], 'C': []} for letter in ['A', 'B', 'C']: tap[letter] = output['Zregulator.csv']['tap_' + letter] tapnew[letter] = output1['NewZregulator.csv']['tap_' + letter] if capKeys != []: switch[letter] = output[ 'ZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch' + letter] switchnew[letter] = output1[ 'NewZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch' + letter] volt[letter] = map( returnMag, output['ZsubstationBottom.csv']['voltage_' + letter]) voltnew[letter] = map( returnMag, output1['NewZsubstationBottom.csv']['voltage_' + letter]) lowVoltage = map( divby2, output['ZvoltageJiggle.csv']['min(voltage_12.mag)']) lowVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['min(voltage_12.mag)']) meanVoltage = map( divby2, output['ZvoltageJiggle.csv']['mean(voltage_12.mag)']) meanVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['mean(voltage_12.mag)']) highVoltage = map( divby2, output['ZvoltageJiggle.csv']['max(voltage_12.mag)']) highVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['max(voltage_12.mag)']) # energy calculations whEnergy = [] whLosses = [] whLoads = [] whEnergy.append(sum(p) / 10**6) whLosses.append(sum(realLoss) / 10**6) whLoads.append((sum(p) - sum(realLoss)) / 10**6) whEnergy.append(sum(pnew) / 10**6) whLosses.append(sum(realLossnew) / 10**6) whLoads.append((sum(pnew) - sum(realLossnew)) / 10**6) indices = ['No IVVC', 'With IVVC'] # energySalesRed = (whLoads[1]-whLoads[0])*(inData['wholesaleEnergyCostPerKwh'])*1000 # lossSav = (whLosses[0]-whLosses[1])*inData['wholesaleEnergyCostPerKwh']*1000 # print energySalesRed, lossSav # plots ticks = [] plt.clf() plt.title("total energy") plt.ylabel("total load and losses (MWh)") for element in range(2): ticks.append(element) bar_loss = plt.bar(element, whLosses[element], 0.15, color='red') bar_load = plt.bar( element + 0.15, whLoads[element], 0.15, color='orange') plt.legend([bar_load[0], bar_loss[0]], ['total load', 'total losses'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.xticks([t + 0.15 for t in ticks], indices) Plot.save_fig(plt, pJoin(modelDir, "totalEnergy.png")) # real and imaginary power plt.figure("real power") plt.title("Real Power at substation") plt.ylabel("substation real power (MW)") pMW = [element / 10**6 for element in p] pMWn = [element / 10**6 for element in pnew] pw = plt.plot(pMW) npw = plt.plot(pMWn) plt.legend([pw[0], npw[0]], ['NO IVVC', 'WITH IVVC'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) Plot.save_fig(plt, pJoin(modelDir, "realPower.png")) plt.figure("Reactive power") plt.title("Reactive Power at substation") plt.ylabel("substation reactive power (MVAR)") qMVAR = [element / 10**6 for element in q] qMVARn = [element / 10**6 for element in qnew] iw = plt.plot(qMVAR) niw = plt.plot(qMVARn) plt.legend([iw[0], niw[0]], ['NO IVVC', 'WITH IVVC'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) Plot.save_fig(plt, pJoin(modelDir, "imaginaryPower.png")) # voltage plots plt.figure("voltages as a function of time") f, ax = plt.subplots(2, sharex=True) f.suptitle("Min and Max voltages on the feeder") lv = ax[0].plot(lowVoltage, color='cadetblue') mv = ax[0].plot(meanVoltage, color='blue') hv = ax[0].plot(highVoltage, color='cadetblue') ax[0].legend([lv[0], mv[0], hv[0]], ['low voltage', 'mean voltage', 'high voltage'], bbox_to_anchor=(0., 0.915, 1., .1), loc=3, ncol=3, mode="expand", borderaxespad=0.1) ax[0].set_ylabel('NO IVVC') nlv = ax[1].plot(lowVoltagenew, color='cadetblue') nmv = ax[1].plot(meanVoltagenew, color='blue') nhv = ax[1].plot(highVoltagenew, color='cadetblue') ax[1].set_ylabel('WITH IVVC') Plot.save_fig(plt, pJoin(modelDir, "Voltages.png")) # tap positions plt.figure("TAP positions NO IVVC") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("Regulator Tap positions") ax[0].plot(tap['A']) ax[0].set_title("Regulator Tap positions NO IVVC") ax[0].set_ylabel("TAP A") ax[1].plot(tap['B']) ax[1].set_ylabel("TAP B") ax[2].plot(tap['C']) ax[2].set_ylabel("TAP C") ax[3].plot(tapnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("TAP A") ax[4].plot(tapnew['B']) ax[4].set_ylabel("TAP B") ax[5].plot(tapnew['C']) ax[5].set_ylabel("TAP C") for subplot in range(6): ax[subplot].set_ylim(-20, 20) f.tight_layout() Plot.save_fig(plt, pJoin(modelDir, "RegulatorTAPpositions.png")) # substation voltages plt.figure("substation voltage as a function of time") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("voltages at substation NO IVVC") ax[0].plot(volt['A']) ax[0].set_title('Substation voltages NO IVVC') ax[0].set_ylabel('voltage A') ax[1].plot(volt['B']) ax[1].set_ylabel('voltage B') ax[2].plot(volt['C']) ax[2].set_ylabel('voltage C') ax[3].plot(voltnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel('voltage A') ax[4].plot(voltnew['B']) ax[4].set_ylabel('voltage B') ax[5].plot(voltnew['C']) ax[5].set_ylabel('voltage C') f.tight_layout() Plot.save_fig(plt, pJoin(modelDir, "substationVoltages.png")) # cap switches plt.figure("capacitor switch state as a function of time") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("Capacitor switch state NO IVVC") ax[0].plot(switch['A']) ax[0].set_title("Capacitor switch state NO IVVC") ax[0].set_ylabel("switch A") ax[1].plot(switch['B']) ax[1].set_ylabel("switch B") ax[2].plot(switch['C']) ax[2].set_ylabel("switch C") ax[3].plot(switchnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("switch A") ax[4].plot(switchnew['B']) ax[4].set_ylabel("switch B") ax[5].plot(switchnew['C']) ax[5].set_ylabel("switch C") for subplot in range(6): ax[subplot].set_ylim(-2, 2) f.tight_layout() Plot.save_fig(plt, pJoin(modelDir, "capacitorSwitch.png")) # plt.show() # monetization monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] monthToSeason = {'January': 'Winter', 'February': 'Winter', 'March': 'Spring', 'April': 'Spring', 'May': 'Spring', 'June': 'Summer', 'July': 'Summer', 'August': 'Summer', 'September': 'Fall', 'October': 'Fall', 'November': 'Fall', 'December': 'Winter'} # calculate the month and hour of simulation start and month and hour # of simulation end simStartTimestamp = simStartDate + " 00:00:00" simFormattedDate = datetime.strptime( simStartTimestamp, "%Y-%m-%d %H:%M:%S") simStartMonthNum = int(simFormattedDate.strftime('%m')) simstartMonth = monthNames[simStartMonthNum - 1] simStartDay = int(simFormattedDate.strftime('%d')) if calendar.isleap(int(simFormattedDate.strftime('%Y'))): febDays = 29 else: febDays = 28 monthHours = [int(31 * 24), int(febDays * 24), int(31 * 24), int(30 * 24), int(31 * 24), int( 30 * 24), int(31 * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(30 * 24), int(31 * 24)] simStartIndex = int( sum(monthHours[:(simStartMonthNum - 1)]) + (simStartDay - 1) * 24) temp = 0 cumulHours = [0] for x in range(12): temp += monthHours[x] cumulHours.append(temp) for i in range((simStartMonthNum), 13): if int(simStartIndex + simRealLength) <= cumulHours[i] and int(simStartIndex + simRealLength) > cumulHours[i - 1]: simEndMonthNum = i - 1 simEndMonth = monthNames[simEndMonthNum] print simstartMonth, simEndMonth # calculate peaks for the number of months in simulation previndex = 0 monthPeak = {} monthPeakNew = {} peakSaveDollars = {} energyLostDollars = {} lossRedDollars = {} simMonthList = monthNames[ monthNames.index(simstartMonth):(monthNames.index(simEndMonth) + 1)] print simMonthList for monthElement in simMonthList: print monthElement month = monthNames.index(monthElement) index1 = int(previndex) index2 = int(min((index1 + int(monthHours[month])), simRealLength)) monthPeak[monthElement] = max(p[index1:index2]) / 1000.0 monthPeakNew[monthElement] = max(pnew[index1:index2]) / 1000.0 peakSaveDollars[monthElement] = (monthPeak[monthElement] - monthPeakNew[monthElement]) * float( inData['peakDemandCost' + str(monthToSeason[monthElement]) + 'PerKw']) lossRedDollars[monthElement] = (sum(realLoss[index1:index2]) / 1000.0 - sum( realLossnew[index1:index2]) / 1000.0) * (float(inData['wholesaleEnergyCostPerKwh'])) energyLostDollars[monthElement] = (sum(p[index1:index2]) / 1000.0 - sum(pnew[index1:index2]) / 1000.0 - sum(realLoss[index1:index2]) / 1000.0 + sum(realLossnew[index1:index2]) / 1000.0) * (float(inData['wholesaleEnergyCostPerKwh']) - float(inData['retailEnergyCostPerKwh'])) previndex = index2 # money charts fig = plt.figure("cost benefit barchart", figsize=(10, 8)) ticks = range(len(simMonthList)) ticks1 = [element + 0.15 for element in ticks] ticks2 = [element + 0.30 for element in ticks] print ticks eld = [energyLostDollars[month] for month in simMonthList] lrd = [lossRedDollars[month] for month in simMonthList] psd = [peakSaveDollars[month] for month in simMonthList] bar_eld = plt.bar(ticks, eld, 0.15, color='red') bar_psd = plt.bar(ticks1, psd, 0.15, color='blue') bar_lrd = plt.bar(ticks2, lrd, 0.15, color='green') plt.legend([bar_eld[0], bar_psd[0], bar_lrd[0]], ['energyLostDollars', 'peakReductionDollars', 'lossReductionDollars'], bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) monShort = [element[0:3] for element in simMonthList] plt.xticks([t + 0.15 for t in ticks], monShort) plt.ylabel('Utility Savings ($)') Plot.save_fig(plt, pJoin(modelDir, "spendChart.png")) # cumulative savings graphs fig = plt.figure("cost benefit barchart", figsize=(10, 5)) annualSavings = sum(eld) + sum(lrd) + sum(psd) annualSave = lambda x: ( annualSavings - float(inData['omCost'])) * x - float(inData['capitalCost']) simplePayback = float( inData['capitalCost']) / (annualSavings - float(inData['omCost'])) plt.xlabel('Year After Installation') plt.xlim(0, 30) plt.ylabel('Cumulative Savings ($)') plt.plot([0 for x in range(31)], c='gray') plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--') plt.plot([annualSave(x) for x in range(31)], c='green') Plot.save_fig(plt, pJoin(modelDir, "savingsChart.png")) # get exact time stamps from the CSV files generated by Gridlab-D timeWithZone = output['Zregulator.csv']['# timestamp'] timestamps = [element[:19] for element in timeWithZone] # data for highcharts allOutput["timeStamps"] = timestamps allOutput["noCVRPower"] = p allOutput["withCVRPower"] = pnew allOutput["noCVRLoad"] = whLoads[0] allOutput["withCVRLoad"] = whLoads[1] allOutput["noCVRLosses"] = whLosses[0] allOutput["withCVRLosses"] = whLosses[1] allOutput["noCVRTaps"] = tap allOutput["withCVRTaps"] = tapnew allOutput["noCVRSubVolts"] = volt allOutput["withCVRSubVolts"] = voltnew allOutput["noCVRCapSwitch"] = switch allOutput["withCVRCapSwitch"] = switchnew allOutput["noCVRHighVolt"] = highVoltage allOutput["withCVRHighVolt"] = highVoltagenew allOutput["noCVRLowVolt"] = lowVoltage allOutput["withCVRLowVolt"] = lowVoltagenew allOutput["noCVRMeanVolt"] = meanVoltage allOutput["withCVRMeanVolt"] = meanVoltagenew # monetization allOutput["simMonthList"] = monShort allOutput["energyLostDollars"] = energyLostDollars allOutput["lossRedDollars"] = lossRedDollars allOutput["peakSaveDollars"] = peakSaveDollars allOutput["annualSave"] = [annualSave(x) for x in range(31)] # Update the runTime in the input file. endTime = datetime.now() inData["runTime"] = str( timedelta(seconds=int((endTime - startTime).total_seconds()))) fs.save(pJoin(modelDir, "allInputData.json"), json.dumps(inData, indent=4)) fs.save(pJoin(modelDir, "allOutputData.json"), json.dumps(allOutput, indent=4)) # For autotest, there won't be such file. try: os.remove(pJoin(modelDir, "PPID.txt")) except: pass print "DONE RUNNING", modelDir except Exception as e: print "Oops, Model Crashed!!!" cancel(modelDir) print e
outData['demand'] = [t['power'] * 1000.0 for t in dc] outData['demandAfterBattery'] = [t['netpower'] * 1000.0 for t in dc] # outData['batterySoc'] = [t['battSoC']/battCapacity*100.0 for t in dc] outData['batterySoc'] = [t['battSoC'] / battCapacity * 100.0 * dodFactor + (100 - 100 * dodFactor) for t in dc] # Estimate number of cyles the battery went through. SoC = outData['batterySoc'] outData['cycleEquivalents'] = sum( [SoC[i] - SoC[i + 1] for i, x in enumerate(SoC[0:-1]) if SoC[i + 1] < SoC[i]]) / 100.0 # Output some matplotlib results as well. plt.plot([t['power'] for t in dc]) plt.plot([t['netpower'] for t in dc]) plt.plot([t['battSoC'] for t in dc]) for month in range(12): plt.axvline(hoursThroughTheMonth[month]) Plot.save_fig(plt, pJoin(modelDir, "plot.png")) # DRDAN: Summary of results outData['months'] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] totMonNum = [] monthlyDemand = [] for x in range(0, len(dcGroupByMonth)): totMonNum.append(sum(dcGroupByMonth[x]) / 1000) monthlyDemand.append([outData['months'][x], totMonNum[x]]) outData['monthlyDemand'] = totMonNum outData['ps'] = ps # TODO: [Battery Capacity Left] outData['monthlyDemandRed'] = [ totMonNum - ps for totMonNum, ps in zip(totMonNum, ps)] outData['benefitMonthly'] = [x * demandCharge for x in outData['ps']] outData['kWhtoRecharge'] = [battCapacity - x for x in outData['ps']] outData['costtoRecharge'] = [
def omfCalibrate(workDir, feederPath, scadaPath): '''calibrates a feeder and saves the calibrated tree at a location''' logger.info('Calibrating feeder... work dir: %s; feeder path: %s; scada path: %s', workDir, feederPath, scadaPath) with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) scadaSubPower, firstDateTime = _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 = omf.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', '') in ['regulator', 'overhead_line', 'underground_line', 'transformer', 'fuse'] and tree[key].get('from', '') == swingName: 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 omf.feeder.adjustTime(tree, HOURS, "hours", firstDateTime.strftime("%Y-%m-%d")) # 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.figure() 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) Plot.save_fig(plt, 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