def calibrate_omd(start_date, omd_path, csv_path): """ Modify an .omd file so that it will run in GridLAB-D with the CSV of USCRN weather data. :param start_date: the starting date of the GridLAB-D simulation :type start_date: datetime :param omd_path: an absolute path to the .omd file to modify :type omd_path: str :param csv_path: an absolute path to the CSV file that contains USCRN weather data :type csv_path: str """ with open(omd_path, 'r') as f: omd = json.load(f) tree = omd["tree"] # Delete all climate objects from the feeder. Also delete any csv_reader objects that are also named "WeatherReader" weather_reader_name = "WeatherReader" for key in tree.keys(): object_type = tree[key].get("object") object_name = tree[key].get("name") if object_type == "climate" or (object_type == "csv_reader" and object_name == weather_reader_name): del tree[key] # Reinsert a new climate object and an associated csv_reader object oldMax = feeder.getMaxKey(tree) tree[oldMax + 1] = {'omftype': 'module', 'argument': 'tape'} tree[oldMax + 2] = {'omftype': 'module', 'argument': 'climate'} csv_name = os.path.basename(csv_path) tree[oldMax + 3] = { 'object': 'csv_reader', 'name': weather_reader_name, 'filename': csv_name } climate_name = "MyClimate" tree[oldMax + 4] = { 'object': 'climate', 'name': climate_name, 'reader': weather_reader_name, 'tmyfile': csv_name } # Set the time correctly. Modify certain objects in the feeder (e.g. recorder and clock) feeder.adjustTime( tree, 240, 'hours', '{}-{}-{}'.format(start_date.year, start_date.month, start_date.day)) omd["tree"] = tree # Add the weather attachment with open(csv_path, 'r') as f: weatherString = f.read() if omd.get("attachments") is None: omd["attachments"] = {} omd['attachments'][csv_name] = weatherString with open(omd_path, 'w') as f: json.dump(omd, f, indent=4)
def runForeground(modelDir, inData): '''This reads a glm file, changes the method of powerflow and reruns''' print "STARTING TO RUN", modelDir try: startTime = dt.now() if not os.path.isdir(modelDir): os.makedirs(modelDir) inData["created"] = str(startTime) #read pre-calibrated feeder and run cvrdynamic feederName = inData.get('feederName1', 'feeder1') feederPath = pJoin(modelDir, feederName + '.omd') # Reads a pre-calibrated feeder. allOutput = {} with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) localTree = feederJson.get("tree", {}) attachments = feederJson.get("attachments", {}) for key in localTree: if "solver_method" in localTree[key].keys(): # print "current solver method", localTree[key]["solver_method"] localTree[key]["solver_method"] = 'FBS' #find the swing bus and recorder attached to substation try: for key in localTree: if localTree[key].get('bustype', '').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') if localTree[key].get( 'object', '') == 'regulator' and localTree[key].get( 'from', '') == swingName: regIndex = key regConfName = localTree[key]['configuration'] except: raise ValueError('Invalid feeder selected:', str(inData["feederName1"])) #find the regulator and capacitor names and combine to form a string for volt-var control object regKeys = [] accum_reg = "" for key in localTree: if localTree[key].get("object", "") == "regulator": accum_reg += localTree[key].get("name", "ERROR") + "," regKeys.append(key) regstr = accum_reg[:-1] # print regKeys capKeys = [] accum_cap = "" for key in localTree: if localTree[key].get("object", "") == "capacitor": accum_cap += localTree[key].get("name", "ERROR") + "," capKeys.append(key) if localTree[key].get("control", "").lower() == "manual": localTree[key]['control'] = "VOLT" # print "changing capacitor control from manual to volt" capstr = accum_cap[:-1] # print capKeys # Attach recorders relevant to CVR. recorders = [{ 'object': 'collector', 'file': 'ZlossesTransformer.csv', 'group': 'class=transformer', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'collector', 'file': 'ZlossesUnderground.csv', 'group': 'class=underground_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'collector', 'file': 'ZlossesOverhead.csv', 'group': 'class=overhead_line', 'limit': '0', 'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)' }, { 'object': 'recorder', 'file': 'Zregulator.csv', 'limit': '0', 'parent': localTree[regIndex]['name'], 'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag' }, { 'object': 'collector', 'file': 'ZvoltageJiggle.csv', 'group': 'class=triplex_meter', 'limit': '0', 'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)' }, { 'object': 'recorder', 'file': 'ZsubstationTop.csv', 'limit': '0', 'parent': localTree[swingIndex]['name'], 'property': 'voltage_A,voltage_B,voltage_C' }, { 'object': 'recorder', 'file': 'ZsubstationBottom.csv', 'limit': '0', 'parent': localTree[regIndex]['to'], 'property': 'voltage_A,voltage_B,voltage_C' }] #recorder object for capacitor switching - if capacitors exist if capKeys != []: for key in capKeys: recorders.append({ 'object': 'recorder', 'file': 'ZcapSwitch' + str(key) + '.csv', 'limit': '0', 'parent': localTree[key]['name'], 'property': 'switchA,switchB,switchC' }) #attach recorder process biggest = 1 + max([int(k) for k in localTree.keys()]) for index, rec in enumerate(recorders): localTree[biggest + index] = rec #run a reference load flow HOURS = float(inData['simLengthHours']) simStartDate = inData['simStart'] feeder.adjustTime(localTree, HOURS, "hours", simStartDate) output = gridlabd.runInFilesystem(localTree, attachments, keepFiles=False, workDir=modelDir) try: os.remove(pJoin(modelDir, "PID.txt")) except: pass p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] #calculating length of simulation because it migth be different from the simulation input HOURS simRealLength = int(len(p)) #time delays from configuration files time_delay_reg = '30.0' time_delay_cap = '300.0' for key in localTree: if localTree[key].get('object', '') == "regulator_configuration": time_delay_reg = localTree[key]['time_delay'] # print "time_delay_reg",time_delay_reg # if localTree[key].get('object','') == "capacitor": # time_delay_cap = localTree[key]['time_delay'] # print "time_delay_cap",time_delay_cap #change the recorder names for key in localTree: if localTree[key].get('object', '') == "collector" or localTree[key].get( 'object', '') == "recorder": if localTree[key].get('file', '').startswith('Z'): localTree[key]['file'] = localTree[key].get( 'file', '').replace('Z', 'NewZ') #create volt-var control object max_key = max([int(key) for key in localTree.keys()]) # print max_key localTree[max_key + 1] = { 'object': 'volt_var_control', 'name': 'IVVC1', 'control_method': 'ACTIVE', 'capacitor_delay': str(time_delay_cap), 'regulator_delay': str(time_delay_reg), 'desired_pf': '0.99', 'd_max': '0.6', 'd_min': '0.1', 'substation_link': str(localTree[regIndex]['name']), 'regulator_list': regstr, 'capacitor_list': capstr, 'voltage_measurements': str(inData.get("voltageNodes", "IVVC1")), } #running powerflow analysis via gridalab after attaching a regulator feeder.adjustTime(localTree, HOURS, "hours", simStartDate) output1 = gridlabd.runInFilesystem(localTree, attachments, keepFiles=True, workDir=modelDir) os.remove(pJoin(modelDir, "PID.txt")) pnew = output1['NewZregulator.csv']['power_in.real'] qnew = output1['NewZregulator.csv']['power_in.imag'] #total real and imaginary losses as a function of time def vecSum(u, v): ''' Add vectors u and v element-wise. Return has len <= len(u) and <=len(v). ''' return map(sum, zip(u, v)) def zeroVec(length): ''' Give a zero vector of input length. ''' return [0 for x in xrange(length)] (realLoss, imagLoss, realLossnew, imagLossnew) = (zeroVec(int(HOURS)) for x in range(4)) for device in [ 'ZlossesOverhead.csv', 'ZlossesTransformer.csv', 'ZlossesUnderground.csv' ]: for letter in ['A', 'B', 'C']: realLoss = vecSum( realLoss, output[device]['sum(power_losses_' + letter + '.real)']) imagLoss = vecSum( imagLoss, output[device]['sum(power_losses_' + letter + '.imag)']) realLossnew = vecSum( realLossnew, output1['New' + device]['sum(power_losses_' + letter + '.real)']) imagLossnew = vecSum( imagLossnew, output1['New' + device]['sum(power_losses_' + letter + '.imag)']) #voltage calculations and tap calculations def divby2(u): '''divides by 2''' return u / 2 lowVoltage = [] meanVoltage = [] highVoltage = [] lowVoltagenew = [] meanVoltagenew = [] highVoltagenew = [] tap = {'A': [], 'B': [], 'C': []} tapnew = {'A': [], 'B': [], 'C': []} volt = {'A': [], 'B': [], 'C': []} voltnew = {'A': [], 'B': [], 'C': []} switch = {'A': [], 'B': [], 'C': []} switchnew = {'A': [], 'B': [], 'C': []} for letter in ['A', 'B', 'C']: tap[letter] = output['Zregulator.csv']['tap_' + letter] tapnew[letter] = output1['NewZregulator.csv']['tap_' + letter] if capKeys != []: switch[letter] = output['ZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch' + letter] switchnew[letter] = output1['NewZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch' + letter] volt[letter] = map( returnMag, output['ZsubstationBottom.csv']['voltage_' + letter]) voltnew[letter] = map( returnMag, output1['NewZsubstationBottom.csv']['voltage_' + letter]) lowVoltage = map(divby2, output['ZvoltageJiggle.csv']['min(voltage_12.mag)']) lowVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['min(voltage_12.mag)']) meanVoltage = map(divby2, output['ZvoltageJiggle.csv']['mean(voltage_12.mag)']) meanVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['mean(voltage_12.mag)']) highVoltage = map(divby2, output['ZvoltageJiggle.csv']['max(voltage_12.mag)']) highVoltagenew = map( divby2, output1['NewZvoltageJiggle.csv']['max(voltage_12.mag)']) #energy calculations whEnergy = [] whLosses = [] whLoads = [] whEnergy.append(sum(p) / 10**6) whLosses.append(sum(realLoss) / 10**6) whLoads.append((sum(p) - sum(realLoss)) / 10**6) whEnergy.append(sum(pnew) / 10**6) whLosses.append(sum(realLossnew) / 10**6) whLoads.append((sum(pnew) - sum(realLossnew)) / 10**6) indices = ['No IVVC', 'With IVVC'] # energySalesRed = (whLoads[1]-whLoads[0])*(inData['wholesaleEnergyCostPerKwh'])*1000 # lossSav = (whLosses[0]-whLosses[1])*inData['wholesaleEnergyCostPerKwh']*1000 # print energySalesRed, lossSav #plots ticks = [] plt.clf() plt.title("total energy") plt.ylabel("total load and losses (MWh)") for element in range(2): ticks.append(element) bar_loss = plt.bar(element, whLosses[element], 0.15, color='red') bar_load = plt.bar(element + 0.15, whLoads[element], 0.15, color='orange') plt.legend([bar_load[0], bar_loss[0]], ['total load', 'total losses'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.xticks([t + 0.15 for t in ticks], indices) plt.savefig(pJoin(modelDir, "totalEnergy.png")) #real and imaginary power plt.figure("real power") plt.title("Real Power at substation") plt.ylabel("substation real power (MW)") pMW = [element / 10**6 for element in p] pMWn = [element / 10**6 for element in pnew] pw = plt.plot(pMW) npw = plt.plot(pMWn) plt.legend([pw[0], npw[0]], ['NO IVVC', 'WITH IVVC'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir, "realPower.png")) plt.figure("Reactive power") plt.title("Reactive Power at substation") plt.ylabel("substation reactive power (MVAR)") qMVAR = [element / 10**6 for element in q] qMVARn = [element / 10**6 for element in qnew] iw = plt.plot(qMVAR) niw = plt.plot(qMVARn) plt.legend([iw[0], niw[0]], ['NO IVVC', 'WITH IVVC'], bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) plt.savefig(pJoin(modelDir, "imaginaryPower.png")) #voltage plots plt.figure("voltages as a function of time") f, ax = plt.subplots(2, sharex=True) f.suptitle("Min and Max voltages on the feeder") lv = ax[0].plot(lowVoltage, color='cadetblue') mv = ax[0].plot(meanVoltage, color='blue') hv = ax[0].plot(highVoltage, color='cadetblue') ax[0].legend([lv[0], mv[0], hv[0]], ['low voltage', 'mean voltage', 'high voltage'], bbox_to_anchor=(0., 0.915, 1., .1), loc=3, ncol=3, mode="expand", borderaxespad=0.1) ax[0].set_ylabel('NO IVVC') nlv = ax[1].plot(lowVoltagenew, color='cadetblue') nmv = ax[1].plot(meanVoltagenew, color='blue') nhv = ax[1].plot(highVoltagenew, color='cadetblue') ax[1].set_ylabel('WITH IVVC') plt.savefig(pJoin(modelDir, "Voltages.png")) #tap positions plt.figure("TAP positions NO IVVC") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("Regulator Tap positions") ax[0].plot(tap['A']) ax[0].set_title("Regulator Tap positions NO IVVC") ax[0].set_ylabel("TAP A") ax[1].plot(tap['B']) ax[1].set_ylabel("TAP B") ax[2].plot(tap['C']) ax[2].set_ylabel("TAP C") ax[3].plot(tapnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("TAP A") ax[4].plot(tapnew['B']) ax[4].set_ylabel("TAP B") ax[5].plot(tapnew['C']) ax[5].set_ylabel("TAP C") for subplot in range(6): ax[subplot].set_ylim(-20, 20) f.tight_layout() plt.savefig(pJoin(modelDir, "RegulatorTAPpositions.png")) #substation voltages plt.figure("substation voltage as a function of time") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("voltages at substation NO IVVC") ax[0].plot(volt['A']) ax[0].set_title('Substation voltages NO IVVC') ax[0].set_ylabel('voltage A') ax[1].plot(volt['B']) ax[1].set_ylabel('voltage B') ax[2].plot(volt['C']) ax[2].set_ylabel('voltage C') ax[3].plot(voltnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel('voltage A') ax[4].plot(voltnew['B']) ax[4].set_ylabel('voltage B') ax[5].plot(voltnew['C']) ax[5].set_ylabel('voltage C') f.tight_layout() plt.savefig(pJoin(modelDir, "substationVoltages.png")) #cap switches plt.figure("capacitor switch state as a function of time") f, ax = plt.subplots(6, sharex=True) f.set_size_inches(10, 12.0) #f.suptitle("Capacitor switch state NO IVVC") ax[0].plot(switch['A']) ax[0].set_title("Capacitor switch state NO IVVC") ax[0].set_ylabel("switch A") ax[1].plot(switch['B']) ax[1].set_ylabel("switch B") ax[2].plot(switch['C']) ax[2].set_ylabel("switch C") ax[3].plot(switchnew['A']) ax[3].set_title("WITH IVVC") ax[3].set_ylabel("switch A") ax[4].plot(switchnew['B']) ax[4].set_ylabel("switch B") ax[5].plot(switchnew['C']) ax[5].set_ylabel("switch C") for subplot in range(6): ax[subplot].set_ylim(-2, 2) f.tight_layout() plt.savefig(pJoin(modelDir, "capacitorSwitch.png")) #plt.show() #monetization monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] monthToSeason = { 'January': 'Winter', 'February': 'Winter', 'March': 'Spring', 'April': 'Spring', 'May': 'Spring', 'June': 'Summer', 'July': 'Summer', 'August': 'Summer', 'September': 'Fall', 'October': 'Fall', 'November': 'Fall', 'December': 'Winter' } #calculate the month and hour of simulation start and month and hour of simulation end simStartTimestamp = simStartDate + " 00:00:00" simFormattedDate = dt.strptime(simStartTimestamp, "%Y-%m-%d %H:%M:%S") simStartMonthNum = int(simFormattedDate.strftime('%m')) simstartMonth = monthNames[simStartMonthNum - 1] simStartDay = int(simFormattedDate.strftime('%d')) if calendar.isleap(int(simFormattedDate.strftime('%Y'))): febDays = 29 else: febDays = 28 monthHours = [ int(31 * 24), int(febDays * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(31 * 24), int(30 * 24), int(31 * 24), int(30 * 24), int(31 * 24) ] simStartIndex = int( sum(monthHours[:(simStartMonthNum - 1)]) + (simStartDay - 1) * 24) temp = 0 cumulHours = [0] for x in range(12): temp += monthHours[x] cumulHours.append(temp) for i in range((simStartMonthNum), 13): if int(simStartIndex + simRealLength) <= cumulHours[i] and int( simStartIndex + simRealLength) > cumulHours[i - 1]: simEndMonthNum = i - 1 simEndMonth = monthNames[simEndMonthNum] # print simstartMonth,simEndMonth #calculate peaks for the number of months in simulation previndex = 0 monthPeak = {} monthPeakNew = {} peakSaveDollars = {} energyLostDollars = {} lossRedDollars = {} simMonthList = monthNames[monthNames.index(simstartMonth):( monthNames.index(simEndMonth) + 1)] # print simMonthList for monthElement in simMonthList: # print monthElement month = monthNames.index(monthElement) index1 = int(previndex) index2 = int(min((index1 + int(monthHours[month])), simRealLength)) monthPeak[monthElement] = max(p[index1:index2]) / 1000.0 monthPeakNew[monthElement] = max(pnew[index1:index2]) / 1000.0 peakSaveDollars[monthElement] = ( monthPeak[monthElement] - monthPeakNew[monthElement]) * float( inData['peakDemandCost' + str(monthToSeason[monthElement]) + 'PerKw']) lossRedDollars[monthElement] = ( sum(realLoss[index1:index2]) / 1000.0 - sum(realLossnew[index1:index2]) / 1000.0) * (float( inData['wholesaleEnergyCostPerKwh'])) energyLostDollars[monthElement] = ( sum(p[index1:index2]) / 1000.0 - sum(pnew[index1:index2]) / 1000.0 - sum(realLoss[index1:index2]) / 1000.0 + sum(realLossnew[index1:index2]) / 1000.0) * ( float(inData['wholesaleEnergyCostPerKwh']) - float(inData['retailEnergyCostPerKwh'])) previndex = index2 #money charts fig = plt.figure("cost benefit barchart", figsize=(10, 8)) ticks = range(len(simMonthList)) ticks1 = [element + 0.15 for element in ticks] ticks2 = [element + 0.30 for element in ticks] # print ticks eld = [energyLostDollars[month] for month in simMonthList] lrd = [lossRedDollars[month] for month in simMonthList] psd = [peakSaveDollars[month] for month in simMonthList] bar_eld = plt.bar(ticks, eld, 0.15, color='red') bar_psd = plt.bar(ticks1, psd, 0.15, color='blue') bar_lrd = plt.bar(ticks2, lrd, 0.15, color='green') plt.legend([bar_eld[0], bar_psd[0], bar_lrd[0]], [ 'energyLostDollars', 'peakReductionDollars', 'lossReductionDollars' ], bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode="expand", borderaxespad=0.1) monShort = [element[0:3] for element in simMonthList] plt.xticks([t + 0.15 for t in ticks], monShort) plt.ylabel('Utility Savings ($)') plt.savefig(pJoin(modelDir, "spendChart.png")) #cumulative savings graphs fig = plt.figure("cost benefit barchart", figsize=(10, 5)) annualSavings = sum(eld) + sum(lrd) + sum(psd) annualSave = lambda x: (annualSavings - float(inData['omCost']) ) * x - float(inData['capitalCost']) simplePayback = float( inData['capitalCost']) / (annualSavings - float(inData['omCost'])) plt.xlabel('Year After Installation') plt.xlim(0, 30) plt.ylabel('Cumulative Savings ($)') plt.plot([0 for x in range(31)], c='gray') plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--') plt.plot([annualSave(x) for x in range(31)], c='green') plt.savefig(pJoin(modelDir, "savingsChart.png")) #get exact time stamps from the CSV files generated by Gridlab-D timeWithZone = output['Zregulator.csv']['# timestamp'] timestamps = [element[:19] for element in timeWithZone] #data for highcharts allOutput["timeStamps"] = timestamps allOutput["noCVRPower"] = p allOutput["withCVRPower"] = pnew allOutput["noCVRLoad"] = whLoads[0] allOutput["withCVRLoad"] = whLoads[1] allOutput["noCVRLosses"] = whLosses[0] allOutput["withCVRLosses"] = whLosses[1] allOutput["noCVRTaps"] = tap allOutput["withCVRTaps"] = tapnew allOutput["noCVRSubVolts"] = volt allOutput["withCVRSubVolts"] = voltnew allOutput["noCVRCapSwitch"] = switch allOutput["withCVRCapSwitch"] = switchnew allOutput["noCVRHighVolt"] = highVoltage allOutput["withCVRHighVolt"] = highVoltagenew allOutput["noCVRLowVolt"] = lowVoltage allOutput["withCVRLowVolt"] = lowVoltagenew allOutput["noCVRMeanVolt"] = meanVoltage allOutput["withCVRMeanVolt"] = meanVoltagenew #monetization allOutput["simMonthList"] = monShort allOutput["energyLostDollars"] = energyLostDollars allOutput["lossRedDollars"] = lossRedDollars allOutput["peakSaveDollars"] = peakSaveDollars allOutput["annualSave"] = [annualSave(x) for x in range(31)] # Generate warnings #TODO: Timezone adjustment try: # Check if times for simulation and scada match. scadaDates = [] with open(pJoin(modelDir, "subScadaCalibrated1.player"), "r") as scadaFile: for line in scadaFile: (date, val) = line.split(',') scadaDates.append(str(date)) simFormattedEndDate = simFormattedDate + timedelta(hours=HOURS) scadaStartDate = dt.strptime(scadaDates[0].split(' PST')[0], "%Y-%m-%d %H:%M:%S") scadaEndDate = dt.strptime( scadaDates[len(scadaDates) - 1].split(' PST')[0], "%Y-%m-%d %H:%M:%S") beginRange = (scadaStartDate - simFormattedDate).total_seconds() endRange = (scadaEndDate - simFormattedEndDate).total_seconds() # Check if houses exist. housesExist, voltageNodeExists = False, False for key in localTree: if localTree[key].get('object', '') == 'house': housesExist = True if localTree[key].get('name', '') == str(inData.get("voltageNodes", 0)): voltageNodeExists = True if (beginRange > 0.0 or endRange < 0.0) and not housesExist: allOutput[ "warnings"] = "<strong>WARNING:</strong> The simulation dates entered are not compatible with the scada curve in the feeder." # Check if voltage node exists. if not voltageNodeExists: if allOutput.get('warnings', '') != "": previousWarning = allOutput["warnings"] allOutput[ "warnings"] = previousWarning + " The voltage node: " + str( inData.get("voltageNodes", 0)) + " does not exist in the feeder." else: allOutput[ "warnings"] = "<strong>WARNING:</strong> The voltage node <i>" + str( inData.get( "voltageNodes", 0)) + "</i> does not exist in the feeder." except: pass # Update the runTime in the input file. endTime = dt.now() inData["runTime"] = str( timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"), "w") as inFile: json.dump(inData, inFile, indent=4) with open(pJoin(modelDir, "allOutputData.json"), "w") as outFile: json.dump(allOutput, outFile, indent=4) # For autotest, there won't be such file. try: os.remove(pJoin(modelDir, "PPID.txt")) except Exception, e: pass print "DONE RUNNING", modelDir
def attachVolts(workDir, feederPath, voltVectorA, voltVectorB, voltVectorC, simStartDate, simLength, simLengthUnits): '''read voltage vectors of 3 different phases, run gridlabd, and attach output to the feeder.''' try: timeStamp = [simStartDate['Date']] for x in range (1, 8760): timeStamp.append(timeStamp[x-1] + dt.timedelta(hours=1)) firstDateTime = timeStamp[1] with open(pJoin(pJoin(workDir,"gridlabD"),"phaseAVoltage.player"),"w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f"%float(voltVectorA[x]))+"+0j" line = timestamp.strftime("%Y-%m-%d %H:%M:%S") + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(pJoin(pJoin(workDir,"gridlabD"),"phaseBVoltage.player"),"w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f"%float(voltVectorB[x]))+"-"+str("%0.4f"%float(random.uniform(6449,6460)))+"j" line = timestamp.strftime("%Y-%m-%d %H:%M:%S") + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(pJoin(pJoin(workDir,"gridlabD"),"phaseCVoltage.player"),"w") as voltFile: for x in range(0, 8760): timestamp = timeStamp[x] voltage = str("%0.2f"%float(voltVectorC[x]))+"+"+str("%0.4f"%float(random.uniform(6449,6460)))+"j" line = timestamp.strftime("%Y-%m-%d %H:%M:%S") + " " + simStartDate['timeZone'] + "," + str(voltage) + "\n" voltFile.write(line) with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) # Find swingNode name. for key in tree: if tree[key].get('bustype','').lower() == 'swing': swingName = tree[key].get('name') # Attach player. classOb = {'omftype':'class player','argument':'{double value;}'} voltageObA = {"object":"player", "property":"voltage_A", "file":"phaseAVoltage.player", "loop":"0", "parent":swingName} voltageObB = {"object":"player", "property":"voltage_B", "file":"phaseBVoltage.player", "loop":"0", "parent":swingName} voltageObC = {"object":"player", "property":"voltage_C", "file":"phaseCVoltage.player", "loop":"0", "parent":swingName} maxKey = feeder.getMaxKey(tree) voltplayerKeyA = maxKey + 2 voltplayerKeyB = maxKey + 3 voltplayerKeyC = maxKey + 4 tree[maxKey+1] = classOb tree[voltplayerKeyA] = voltageObA tree[voltplayerKeyB] = voltageObB tree[voltplayerKeyC] = voltageObC # Adjust time and run output. feeder.adjustTime(tree, simLength, simLengthUnits, firstDateTime.strftime("%Y-%m-%d %H:%M:%S")) output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir,"gridlabD")) # Write the output. with open(pJoin(workDir,"calibratedFeeder.omd"),"w") as outJson: playerStringA = open(pJoin(pJoin(workDir,"gridlabD"),"phaseAVoltage.player")).read() playerStringB = open(pJoin(pJoin(workDir,"gridlabD"),"phaseBVoltage.player")).read() playerStringC = open(pJoin(pJoin(workDir,"gridlabD"),"phaseCVoltage.player")).read() feederJson["attachments"]["phaseAVoltage.player"] = playerStringA feederJson["attachments"]["phaseBVoltage.player"] = playerStringB feederJson["attachments"]["phaseCVoltage.player"] = playerStringC feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return pJoin(workDir,"calibratedFeeder.omd"), True except: print "Failed to run gridlabD with voltage players." return "", False
def omfCalibrate(workDir, feederPath, scadaPath, simStartDate, simLength, simLengthUnits, solver="FBS", calibrateError=(0.05,5), trim=5): '''calibrates a feeder and saves the calibrated tree at a location. Note: feeders with cap banks should be calibrated with cap banks OPEN. We have seen cap banks throw off calibration.''' with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) simLength = simLength + trim # Process scada data. scadaSubPower = _processScadaData(pJoin(workDir,"gridlabD"),scadaPath, simStartDate, simLengthUnits) # Load specified solver. for key in tree: if tree[key].get("module","").lower() == "powerflow": tree[key] = {"module":"powerflow","solver_method":solver} # Attach player. classOb = {'omftype':'class player','argument':'{double value;}'} playerOb = {"object":"player", "property":"value", "name":"scadaLoads", "file":"subScada.player", "loop":"0"} maxKey = feeder.getMaxKey(tree) playerKey = maxKey + 2 tree[maxKey+1] = classOb tree[playerKey] = playerOb # Make loads reference player. loadTemplate = {"object": "triplex_load", "power_pf_12": "0.95", "impedance_pf_12": "0.98", "power_pf_12": "0.90", "impedance_fraction_12": "0.7", "power_fraction_12": "0.3"} loadTemplateR = {"object": "load", "impedance_pf_A": "0.98", "impedance_pf_B": "0.98", "impedance_pf_C": "0.98", "power_pf_A": "0.90", "power_pf_B": "0.90", "power_pf_C": "0.90", "impedance_fraction_A": "0.7", "impedance_fraction_B": "0.7", "impedance_fraction_C": "0.7", "power_fraction_A": "0.3", "power_fraction_B": "0.3", "power_fraction_C": "0.3"} for key in tree: ob = tree[key] if ob.get("object","") in ("triplex_node", "triplex_load") and (ob.get("power_12") or ob.get("base_power_12")): # Add to triplex_nodes. newOb = dict(loadTemplate) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") oldPow = ob.get("power_12","").replace("j","d") if not oldPow: oldPow = ob.get("base_power_12") if "scadaloads.value*" in oldPow: oldPow = oldPow[17:] pythagPower = gridlabd._strClean(oldPow) newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower) tree[key] = newOb elif ob.get("object","") == "load": # Add to residential_loads too. newOb = dict(loadTemplateR) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["load_class"] = ob.get("load_class", "") newOb["nominal_voltage"] = ob.get("nominal_voltage","") newOb["latitude"] = ob.get("latitude","0") newOb["longitude"] = ob.get("longitude","0") try: oldPow = ob.get("constant_power_A","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_A"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_B","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_B"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_C","").replace("j","d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_C"] = "scadaLoads.value*" + str(pythagPower) except: pass tree[key] = newOb # Convert swing bus to a meter. for key in tree: if tree[key].get('bustype','').lower() == 'swing' and tree[key].get('object','') != 'meter': swingName = tree[key].get('name') regIndex = key tree[key]['object'] = 'meter' # Search for the substation meter and attach a recorder there. for key in tree: if tree[key].get('bustype','').lower() == 'swing': swingName = tree[key].get('name') recOb = {"object": "recorder", "parent": swingName, "property": "measured_real_power,measured_reactive_power,measured_power", "file": "caliSub.csv", "interval": "3600"} outputRecorderKey = maxKey + 3 tree[outputRecorderKey] = recOb feeder.adjustTime(tree, simLength, simLengthUnits, simStartDate['Date'].strftime("%Y-%m-%d %H:%M:%S")) # Run Gridlabd, calculate scaling constant. def runPowerflowIter(tree,scadaSubPower): '''Runs powerflow once, then iterates.''' # Run initial powerflow to get power. print "Starting calibration." print "Goal of calibration: Error: %s, Iterations: <%s, trim: %s"%(calibrateError[0], calibrateError[1], trim) output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir,"gridlabD")) outRealPow = output["caliSub.csv"]["measured_real_power"][trim:simLength] outImagPower = output["caliSub.csv"]["measured_reactive_power"][trim:simLength] outAppPowerKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPow, outImagPower)] lastFile = "subScada.player" nextFile = "subScadaCalibrated.player" nextPower = outAppPowerKw error = (sum(outRealPow)/1000-sum(scadaSubPower))/sum(scadaSubPower) iteration = 1 print "First error:", error while abs(error)>calibrateError[0] and iteration<calibrateError[1]: # Run calibration and iterate up to 5 times. SCAL_CONST = sum(scadaSubPower)/sum(nextPower) print "Calibrating & running again... Error: %s, Iteration: %s, SCAL_CONST: %s"%(str(round(abs(error*100),6)), str(iteration), round(SCAL_CONST,6)) newPlayData = [] with open(pJoin(pJoin(workDir,"gridlabD"), lastFile), "r") as playerFile: for line in playerFile: (key,val) = line.split(',') newPlayData.append(str(key) + ',' + str(float(val)*SCAL_CONST) + "\n") with open(pJoin(pJoin(workDir,"gridlabD"), nextFile), "w") as playerFile: for row in newPlayData: playerFile.write(row) tree[playerKey]["file"] = nextFile tree[outputRecorderKey]["file"] = "caliSubCheck.csv" nextOutput = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir,"gridlabD")) outRealPowIter = nextOutput["caliSubCheck.csv"]["measured_real_power"][trim:simLength] outImagPowerIter = nextOutput["caliSubCheck.csv"]["measured_reactive_power"][trim:simLength] nextAppKw = [(x[0]**2 + x[1]**2)**0.5/1000 for x in zip(outRealPowIter, outImagPowerIter)] lastFile = nextFile nextFile = "subScadaCalibrated"+str(iteration)+".player" nextPower = nextAppKw # Compute error and iterate. error = (sum(outRealPowIter)/1000-sum(scadaSubPower))/sum(scadaSubPower) iteration+=1 else: if iteration==1: outRealPowIter = outRealPow SCAL_CONST = 1.0 print "Calibration done: Error: %s, Iteration: %s, SCAL_CONST: %s"%(str(round(abs(error*100),2)), str(iteration), round(SCAL_CONST,2)) return outRealPow, outRealPowIter, lastFile, iteration outRealPow, outRealPowIter, lastFile, iteration = runPowerflowIter(tree,scadaSubPower[trim:simLength]) caliPowVectors = [[float(element) for element in scadaSubPower[trim:simLength]], [float(element)/1000 for element in outRealPow], [float(element)/1000 for element in outRealPowIter]] labels = ["scadaSubPower","initialGuess","finalGuess"] colors = ['red','lightblue','blue'] chartData = {"Title":"Substation Calibration Check (Iterated "+str(iteration+1)+"X)", "fileName":"caliCheckPlot", "colors":colors,"labels":labels, "timeZone":simStartDate['timeZone']} # Trimming vectors to make them all the same length as the smallest vector minCaliPowVecLen = min(len(caliPowVectors[0]), len(caliPowVectors[1]), len(caliPowVectors[2])) caliPowVectors[0] = caliPowVectors[0][:minCaliPowVecLen] caliPowVectors[1] = caliPowVectors[1][:minCaliPowVecLen] caliPowVectors[2] = caliPowVectors[2][:minCaliPowVecLen] print "Len:", len(caliPowVectors[0]), len(caliPowVectors[1]), len(caliPowVectors[2]) plotLine(workDir, caliPowVectors, chartData, simStartDate['Date']+dt.timedelta(hours=trim), simLengthUnits) # Write the final output. with open(pJoin(workDir,"calibratedFeeder.omd"),"w") as outJson: playerString = open(pJoin(pJoin(workDir,"gridlabD"),lastFile)).read() feederJson["attachments"][lastFile] = playerString feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return
def 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"] weather = inputDict["weather"] if weather == "typical": 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() else: #hack for testing makeClimateCsv('2010-07-01', '2010-08-01', 'DFW', 'Output/Automated dsoSimSuite Test/gldContainer/weather.csv') startTime = datetime.datetime.now() startTime = datetime.datetime.now() feederJson = json.load(open(pJoin(modelDir, feederName+'.omd'))) tree = feederJson["tree"] #add a check to see if there is already a climate object in the omd file #if there is delete the climate from attachments and the climate object attachKeys = feederJson["attachments"].keys() for key in attachKeys: if key.endswith('.tmy2'): del feederJson['attachments'][key] treeKeys = feederJson["tree"].keys() for key in treeKeys: if 'object' in feederJson['tree'][key]: if feederJson['tree'][key]['object'] == 'climate': del feederJson['tree'][key] #add weather objects and modules to .glm if there is no climate file in the omd file if weather == "historical": oldMax = feeder.getMaxKey(tree) tree[oldMax + 1] = {'omftype':'module', 'argument':'tape'} tree[oldMax + 2] = {'omftype':'module', 'argument':'climate'} tree[oldMax + 3] = {'object':'csv_reader', 'name':'weatherReader', 'filename':'weather.csv'} tree[oldMax + 4] = {'object':'climate', 'name':'exampleClimate', 'tmyfile':'weather.csv', 'reader':'weatherReader'} else: oldMax = feeder.getMaxKey(tree) tree[oldMax + 1] ={'object':'climate','name':'Climate','interpolate':'QUADRATIC', 'tmyfile':'climate.tmy2'} # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) # Attach recorders for system voltage map: stub = {'object':'group_recorder', 'group':'"class=node"', 'property':'voltage_A', 'interval':3600, 'file':'aVoltDump.csv'} for phase in ['A','B','C']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'VoltDump.csv' tree[feeder.getMaxKey(tree) + 1] = copyStub feeder.adjustTime(tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"]) # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem(tree, attachments=feederJson["attachments"], keepFiles=True, workDir=pJoin(modelDir,'gldContainer')) cleanOut = {} # Std Err and Std Out cleanOut['stderr'] = rawOut['stderr'] cleanOut['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# property.. timestamp'] else: cleanOut['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get('timeStamps',[]) level = inputDict.get('simLengthUnits','hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): cleanOut['climate'] = {} cleanOut['climate']['Rain Fall (in/h)'] = hdmAgg(rawOut[key].get('rainfall'), sum, level) cleanOut['climate']['Wind Speed (m/s)'] = hdmAgg(rawOut[key].get('wind_speed'), avg, level) cleanOut['climate']['Temperature (F)'] = hdmAgg(rawOut[key].get('temperature'), max, level) cleanOut['climate']['Snow Depth (in)'] = hdmAgg(rawOut[key].get('snowdepth'), max, level) cleanOut['climate']['Direct Normal (W/sf)'] = hdmAgg(rawOut[key].get('solar_direct'), sum, level) #cleanOut['climate']['Global Horizontal (W/sf)'] = hdmAgg(rawOut[key].get('solar_global'), sum, level) climateWbySFList= hdmAgg(rawOut[key].get('solar_global'), sum, level) #converting W/sf to W/sm climateWbySMList= [x*10.76392 for x in climateWbySFList] cleanOut['climate']['Global Horizontal (W/sm)']=climateWbySMList # Voltage Band if 'VoltageJiggle.csv' in rawOut: cleanOut['allMeterVoltages'] = {} cleanOut['allMeterVoltages']['Min'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)']], min, level) cleanOut['allMeterVoltages']['Mean'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['StdDev'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['Max'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)']], max, level) # Power Consumption cleanOut['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads cleanOut['Consumption']['Power'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['DG'] = [0] * int(inputDict["simLength"]) for key in rawOut: if key.startswith('SwingKids_') and key.endswith('.csv'): oneSwingPower = hdmAgg(vecPyth(rawOut[key]['sum(power_in.real)'],rawOut[key]['sum(power_in.imag)']), avg, level) if 'Power' not in cleanOut['Consumption']: cleanOut['Consumption']['Power'] = oneSwingPower else: cleanOut['Consumption']['Power'] = vecSum(oneSwingPower,cleanOut['Consumption']['Power']) elif key.startswith('Inverter_') and key.endswith('.csv'): realA = rawOut[key]['power_A.real'] realB = rawOut[key]['power_B.real'] realC = rawOut[key]['power_C.real'] imagA = rawOut[key]['power_A.imag'] imagB = rawOut[key]['power_B.imag'] imagC = rawOut[key]['power_C.imag'] oneDgPower = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key.startswith('Windmill_') and key.endswith('.csv'): vrA = rawOut[key]['voltage_A.real'] vrB = rawOut[key]['voltage_B.real'] vrC = rawOut[key]['voltage_C.real'] viA = rawOut[key]['voltage_A.imag'] viB = rawOut[key]['voltage_B.imag'] viC = rawOut[key]['voltage_C.imag'] crB = rawOut[key]['current_B.real'] crA = rawOut[key]['current_A.real'] crC = rawOut[key]['current_C.real'] ciA = rawOut[key]['current_A.imag'] ciB = rawOut[key]['current_B.imag'] ciC = rawOut[key]['current_C.imag'] powerA = vecProd(vecPyth(vrA,viA),vecPyth(crA,ciA)) powerB = vecProd(vecPyth(vrB,viB),vecPyth(crB,ciB)) powerC = vecProd(vecPyth(vrC,viC),vecPyth(crC,ciC)) oneDgPower = hdmAgg(vecSum(powerA,powerB,powerC), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key in ['OverheadLosses.csv', 'UndergroundLosses.csv', 'TriplexLosses.csv', 'TransformerLosses.csv']: realA = rawOut[key]['sum(power_losses_A.real)'] imagA = rawOut[key]['sum(power_losses_A.imag)'] realB = rawOut[key]['sum(power_losses_B.real)'] imagB = rawOut[key]['sum(power_losses_B.imag)'] realC = rawOut[key]['sum(power_losses_C.real)'] imagC = rawOut[key]['sum(power_losses_C.imag)'] oneLoss = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'Losses' not in cleanOut['Consumption']: cleanOut['Consumption']['Losses'] = oneLoss else: cleanOut['Consumption']['Losses'] = vecSum(oneLoss,cleanOut['Consumption']['Losses']) elif key.startswith('Regulator_') and key.endswith('.csv'): #split function to strip off .csv from filename and user rest of the file name as key. for example- Regulator_VR10.csv -> key would be Regulator_VR10 regName="" regName = key newkey=regName.split(".")[0] cleanOut[newkey] ={} cleanOut[newkey]['RegTapA'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapB'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapC'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapA'] = rawOut[key]['tap_A'] cleanOut[newkey]['RegTapB'] = rawOut[key]['tap_B'] cleanOut[newkey]['RegTapC'] = rawOut[key]['tap_C'] cleanOut[newkey]['RegPhases'] = rawOut[key]['phases'][0] elif key.startswith('Capacitor_') and key.endswith('.csv'): capName="" capName = key newkey=capName.split(".")[0] cleanOut[newkey] ={} cleanOut[newkey]['Cap1A'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1B'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1C'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1A'] = rawOut[key]['switchA'] cleanOut[newkey]['Cap1B'] = rawOut[key]['switchB'] cleanOut[newkey]['Cap1C'] = rawOut[key]['switchC'] cleanOut[newkey]['CapPhases'] = rawOut[key]['phases'][0] # What percentage of our keys have lat lon data? latKeys = [tree[key]['latitude'] for key in tree if 'latitude' in tree[key]] latPerc = 1.0*len(latKeys)/len(tree) if latPerc < 0.25: doNeato = True else: doNeato = False # Generate the frames for the system voltage map time traveling chart. genTime = generateVoltChart(tree, rawOut, modelDir, neatoLayout=doNeato) cleanOut['genTime'] = genTime # Aggregate up the timestamps: if level=='days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, "allOutputData.json"),"w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, "gldContainer", "PID.txt")) print "DONE RUNNING", modelDir except Exception as e: # If input range wasn't valid delete output, write error to disk. cancel(modelDir) thisErr = traceback.format_exc() print 'ERROR IN MODEL', modelDir, thisErr inputDict['stderr'] = thisErr with open(os.path.join(modelDir,'stderr.txt'),'w') as errorFile: errorFile.write(thisErr) with open(pJoin(modelDir,"allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) finishTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds = int((finishTime - beginTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent = 4) try: os.remove(pJoin(modelDir,"PPID.txt")) except: pass
def 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
# Delete all climate then reinsert. reader_name = 'weatherReader' climate_name = 'MyClimate' for key in myTree.keys(): obName = myTree[key].get('name','') obType = myTree[key].get('object','') if obName in [reader_name, climate_name] or obType is 'climate': del myTree[key] 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':reader_name, 'filename':CSV_NAME} myTree[oldMax + 4] = {'object':'climate', 'name':climate_name, 'reader': reader_name, 'tmyfile':CSV_NAME} # Set the time correctly. feeder.adjustTime(myTree, 240, 'hours', '{}-{}-{}'.format(INIT_TIME.year, INIT_TIME.month, INIT_TIME.day)) # Run here to test. rawOut = runInFilesystem(myTree, attachments=[], keepFiles=True, workDir='.', glmName='./outFile.glm') # Write back the full feeder. # outJson = dict(myFeed) # with open(CSV_NAME,'r') as weatherFile: # weatherString = weatherFile.read() # outJson['attachments']['weatheryearDCA.csv'] = weatherString # outJson['tree'] = myTree # try: os.remove('./Orville Tree Pond Calibrated With Weather.json') # except: pass # with open('./Orville Tree Pond Calibrated With Weather.json', 'w') as outFile: # json.dump(outJson, outFile, indent=4)
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"] #add a check to see if there is already a climate object in the omd file #if there is delete the climate from attachments and the climate object attachKeys = feederJson["attachments"].keys() for key in attachKeys: if key.endswith('.tmy2'): del feederJson['attachments'][key] treeKeys = feederJson["tree"].keys() for key in treeKeys: if 'object' in feederJson['tree'][key]: if feederJson['tree'][key]['object'] == 'climate': del feederJson['tree'][key] oldMax = feeder.getMaxKey(tree) tree[oldMax + 1] = {'omftype':'module', 'argument':'climate'} tree[oldMax + 2] ={'object':'climate','name':'Climate','interpolate':'QUADRATIC', 'tmyfile':'climate.tmy2'} # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) # Attach recorders for system voltage map: stub = {'object':'group_recorder', 'group':'"class=node"', 'property':'voltage_A', 'interval':3600, 'file':'aVoltDump.csv'} for phase in ['A','B','C']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'VoltDump.csv' tree[feeder.getMaxKey(tree) + 1] = copyStub feeder.adjustTime(tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"]) # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem(tree, attachments=feederJson["attachments"], keepFiles=True, workDir=pJoin(modelDir,'gldContainer')) cleanOut = {} # Std Err and Std Out cleanOut['stderr'] = rawOut['stderr'] cleanOut['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# property.. timestamp'] else: cleanOut['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get('timeStamps',[]) level = inputDict.get('simLengthUnits','hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): cleanOut['climate'] = {} cleanOut['climate']['Rain Fall (in/h)'] = hdmAgg(rawOut[key].get('rainfall'), sum, level) cleanOut['climate']['Wind Speed (m/s)'] = hdmAgg(rawOut[key].get('wind_speed'), avg, level) cleanOut['climate']['Temperature (F)'] = hdmAgg(rawOut[key].get('temperature'), max, level) cleanOut['climate']['Snow Depth (in)'] = hdmAgg(rawOut[key].get('snowdepth'), max, level) cleanOut['climate']['Direct Normal (W/sf)'] = hdmAgg(rawOut[key].get('solar_direct'), sum, level) #cleanOut['climate']['Global Horizontal (W/sf)'] = hdmAgg(rawOut[key].get('solar_global'), sum, level) climateWbySFList= hdmAgg(rawOut[key].get('solar_global'), sum, level) #converting W/sf to W/sm climateWbySMList= [x*10.76392 for x in climateWbySFList] cleanOut['climate']['Global Horizontal (W/sm)']=climateWbySMList # Voltage Band if 'VoltageJiggle.csv' in rawOut: cleanOut['allMeterVoltages'] = {} cleanOut['allMeterVoltages']['Min'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)']], min, level) cleanOut['allMeterVoltages']['Mean'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['StdDev'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['Max'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)']], max, level) # Power Consumption cleanOut['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads cleanOut['Consumption']['Power'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['DG'] = [0] * int(inputDict["simLength"]) for key in rawOut: if key.startswith('SwingKids_') and key.endswith('.csv'): oneSwingPower = hdmAgg(vecPyth(rawOut[key]['sum(power_in.real)'],rawOut[key]['sum(power_in.imag)']), avg, level) if 'Power' not in cleanOut['Consumption']: cleanOut['Consumption']['Power'] = oneSwingPower else: cleanOut['Consumption']['Power'] = vecSum(oneSwingPower,cleanOut['Consumption']['Power']) elif key.startswith('Inverter_') and key.endswith('.csv'): realA = rawOut[key]['power_A.real'] realB = rawOut[key]['power_B.real'] realC = rawOut[key]['power_C.real'] imagA = rawOut[key]['power_A.imag'] imagB = rawOut[key]['power_B.imag'] imagC = rawOut[key]['power_C.imag'] oneDgPower = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key.startswith('Windmill_') and key.endswith('.csv'): vrA = rawOut[key]['voltage_A.real'] vrB = rawOut[key]['voltage_B.real'] vrC = rawOut[key]['voltage_C.real'] viA = rawOut[key]['voltage_A.imag'] viB = rawOut[key]['voltage_B.imag'] viC = rawOut[key]['voltage_C.imag'] crB = rawOut[key]['current_B.real'] crA = rawOut[key]['current_A.real'] crC = rawOut[key]['current_C.real'] ciA = rawOut[key]['current_A.imag'] ciB = rawOut[key]['current_B.imag'] ciC = rawOut[key]['current_C.imag'] powerA = vecProd(vecPyth(vrA,viA),vecPyth(crA,ciA)) powerB = vecProd(vecPyth(vrB,viB),vecPyth(crB,ciB)) powerC = vecProd(vecPyth(vrC,viC),vecPyth(crC,ciC)) oneDgPower = hdmAgg(vecSum(powerA,powerB,powerC), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key in ['OverheadLosses.csv', 'UndergroundLosses.csv', 'TriplexLosses.csv', 'TransformerLosses.csv']: realA = rawOut[key]['sum(power_losses_A.real)'] imagA = rawOut[key]['sum(power_losses_A.imag)'] realB = rawOut[key]['sum(power_losses_B.real)'] imagB = rawOut[key]['sum(power_losses_B.imag)'] realC = rawOut[key]['sum(power_losses_C.real)'] imagC = rawOut[key]['sum(power_losses_C.imag)'] oneLoss = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'Losses' not in cleanOut['Consumption']: cleanOut['Consumption']['Losses'] = oneLoss else: cleanOut['Consumption']['Losses'] = vecSum(oneLoss,cleanOut['Consumption']['Losses']) elif key.startswith('Regulator_') and key.endswith('.csv'): #split function to strip off .csv from filename and user rest of the file name as key. for example- Regulator_VR10.csv -> key would be Regulator_VR10 regName="" regName = key newkey=regName.split(".")[0] cleanOut[newkey] ={} cleanOut[newkey]['RegTapA'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapB'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapC'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['RegTapA'] = rawOut[key]['tap_A'] cleanOut[newkey]['RegTapB'] = rawOut[key]['tap_B'] cleanOut[newkey]['RegTapC'] = rawOut[key]['tap_C'] cleanOut[newkey]['RegPhases'] = rawOut[key]['phases'][0] elif key.startswith('Capacitor_') and key.endswith('.csv'): capName="" capName = key newkey=capName.split(".")[0] cleanOut[newkey] ={} cleanOut[newkey]['Cap1A'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1B'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1C'] = [0] * int(inputDict["simLength"]) cleanOut[newkey]['Cap1A'] = rawOut[key]['switchA'] cleanOut[newkey]['Cap1B'] = rawOut[key]['switchB'] cleanOut[newkey]['Cap1C'] = rawOut[key]['switchC'] cleanOut[newkey]['CapPhases'] = rawOut[key]['phases'][0] # What percentage of our keys have lat lon data? latKeys = [tree[key]['latitude'] for key in tree if 'latitude' in tree[key]] latPerc = 1.0*len(latKeys)/len(tree) if latPerc < 0.25: doNeato = True else: doNeato = False # Generate the frames for the system voltage map time traveling chart. genTime = generateVoltChart(tree, rawOut, modelDir, neatoLayout=doNeato) cleanOut['genTime'] = genTime # Aggregate up the timestamps: if level=='days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, "allOutputData.json"),"w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, "gldContainer", "PID.txt")) print "DONE RUNNING", modelDir except Exception as e: # If input range wasn't valid delete output, write error to disk. cancel(modelDir) thisErr = traceback.format_exc() print 'ERROR IN MODEL', modelDir, thisErr inputDict['stderr'] = thisErr with open(os.path.join(modelDir,'stderr.txt'),'w') as errorFile: errorFile.write(thisErr) with open(pJoin(modelDir,"allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) finishTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds = int((finishTime - beginTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent = 4) try: os.remove(pJoin(modelDir,"PPID.txt")) except: pass
def 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"] #add a check to see if there is already a climate object in the omd file #if there is delete the climate from attachments and the climate object attachKeys = feederJson["attachments"].keys() for key in attachKeys: if key.endswith('.tmy2'): del feederJson['attachments'][key] treeKeys = feederJson["tree"].keys() for key in treeKeys: if 'object' in feederJson['tree'][key]: if feederJson['tree'][key]['object'] == 'climate': del feederJson['tree'][key] oldMax = feeder.getMaxKey(tree) tree[oldMax + 1] = {'omftype':'module','argument':'climate'} tree[oldMax + 2] = {'object':'climate','name':'Climate','interpolate':'QUADRATIC','tmyfile':'climate.tmy2'} # tree[oldMax + 3] = {'object':'capacitor','control':'VOLT','phases':'ABCN','name':'CAPTEST','parent':'tm_1','capacitor_A':'0.10 MVAr','capacitor_B':'0.10 MVAr','capacitor_C':'0.10 MVAr','time_delay':'300.0','nominal_voltage':'2401.7771','voltage_set_high':'2350.0','voltage_set_low':'2340.0','switchA':'CLOSED','switchB':'CLOSED','switchC':'CLOSED','control_level':'INDIVIDUAL','phases_connected':'ABCN','dwell_time':'0.0','pt_phases':'ABCN'} # 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 recorder for waterheaters on/off stub = {'object':'group_recorder', 'group':'"class=waterheater"', 'property':'is_waterheater_on', 'interval':3600, 'file':'allWaterheaterOn.csv'} copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # Attach recorder for waterheater tank temperatures stub = {'object':'group_recorder', 'group':'"class=waterheater"', 'property':'temperature', 'interval':3600, 'file':'allWaterheaterTemp.csv'} copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # Attach collector for total waterheater load stub = {'object':'collector', 'group':'"class=waterheater"', 'property':'sum(actual_load)', 'interval':3600, 'file':'allWaterheaterLoad.csv'} copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # Attach collector for total network load stub = {'object':'collector', 'group':'"class=triplex_meter"', 'property':'sum(measured_real_power)', 'interval':3600, 'file':'allMeterPower.csv'} copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # Attach collector for total overall ZIPload power/load stub = {'object':'collector', 'group':'"class=ZIPload"', 'property':'sum(base_power)', 'interval':3600, 'file':'allZIPloadPower.csv'} copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # Attach recorder for each ZIPload power/load stub = {'object':'group_recorder', 'group':'"class=ZIPload"', 'property':'base_power', 'interval':3600, 'file':'eachZIPloadPower.csv'} copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # Attach recorder for all ZIPloads demand_rate stub = {'object':'group_recorder', 'group':'"class=ZIPload"', 'property':'demand_rate', 'interval':3600, 'file':'allZIPloadDemand.csv'} copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # Attach recorder for all ZIPloads on stub = {'object':'group_recorder', 'group':'"class=ZIPload"', 'property':'number_of_devices_on', 'interval':3600, 'file':'allZIPloadOn.csv'} copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # Attach passive_controller tree[feeder.getMaxKey(tree)+1] = {'omftype':'module','argument':'market'} tree[feeder.getMaxKey(tree)+1] = {'omftype':'class auction','argument':'{\n\tdouble my_avg; double my_std;\n}'} tree[feeder.getMaxKey(tree)+1] = {'omftype':'class player','argument':'{\n\tdouble value;\n}'} stub = { 'object':'player', 'name':'cppDays', 'file':'superCpp.player' } copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub stub = { 'object':'player', 'name':'superClearing', 'file':'superClearingPrice.player', 'loop':10 } copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub stub = { 'object':'auction', 'name':'MARKET_1', 'my_std':0.037953, 'period':900, 'my_avg':0.110000, 'current_market.clearing_price':'superClearing.value', 'special_mode':'BUYERS_ONLY', 'unit': 'kW' } copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub stub = { 'object':'passive_controller', 'name':'waterheater_controller_waterheater171923', 'parent':'waterheater171923', 'control_mode':'RAMP', 'range_high':5, 'range_low':-5, 'ramp_high':1, 'ramp_low':-1, 'period':900, 'setpoint':'is_waterheater_on', 'base_setpoint':1, 'expectation_object':'MARKET_1', 'expectation_property':'my_avg', 'observation_object':'MARKET_1', 'observation_property':'past_market.clearing_price', 'stdev_observation_property':'my_std', 'state_property':'override' } copyStub = dict(stub) tree[feeder.getMaxKey(tree)+1] = copyStub # stub = { # 'object':'passive_controller', # 'name':'ZIPload_controller_ZIPload171922', # 'parent':'ZIPload171922', # 'control_mode':'RAMP', # 'range_high':5, # 'range_low':-5, # 'ramp_high':1, # 'ramp_low':-1, # 'period':900, # 'setpoint':'base_power' # 'base_setpoint':1, # 'expectation_object':'MARKET_1', # 'expectation_property':'my_avg', # 'observation_object':'MARKET_1', # 'observation_property':'past_market.clearing_price', # 'stdev_observation_property':'my_std' # 'state_property':'override' # } # copyStub = dict(stub) # tree[feeder.getMaxKey(tree)+1] = copyStub # Attach recorders for system voltage map: stub = {'object':'group_recorder', 'group':'"class=node"', 'interval':3600} 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 # Attach recorders for system voltage map, triplex: stub = {'object':'group_recorder', 'group':'"class=triplex_node"', 'interval':3600} for phase in ['1','2']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'nVoltDump.csv' tree[feeder.getMaxKey(tree) + 1] = copyStub # And get meters for system voltage map: stub = {'object':'group_recorder', 'group':'"class=triplex_meter"', 'interval':3600} for phase in ['1','2']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'mVoltDump.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: print key 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] # Print gridBallast Outputs to allOutputData.json cleanOut['gridBallast'] = {} if 'allWaterheaterOn.csv' in rawOut: cleanOut['gridBallast']['waterheaterOn'] = {} for key in rawOut['allWaterheaterOn.csv']: if key.startswith('waterheater'): cleanOut['gridBallast']['waterheaterOn'][key] = rawOut.get('allWaterheaterOn.csv')[key] if 'allWaterheaterTemp.csv' in rawOut: cleanOut['gridBallast']['waterheaterTemp'] = {} for key in rawOut['allWaterheaterTemp.csv']: if key.startswith('waterheater'): cleanOut['gridBallast']['waterheaterTemp'][key] = rawOut.get('allWaterheaterTemp.csv')[key] if 'allMeterPower.csv' in rawOut: cleanOut['gridBallast']['totalNetworkLoad'] = rawOut.get('allMeterPower.csv')['sum(measured_real_power)'] if ('allWaterheaterLoad.csv' in rawOut) and ('allZIPloadPower.csv' in rawOut): cleanOut['gridBallast']['availabilityMagnitude'] = [x + y for x, y in zip(rawOut.get('allWaterheaterLoad.csv')['sum(actual_load)'], rawOut.get('allZIPloadPower.csv')['sum(base_power)'])] if 'eachZIPloadPower.csv' in rawOut: cleanOut['gridBallast']['ZIPloadPower'] = {} for key in rawOut['eachZIPloadPower.csv']: if key.startswith('ZIPload'): cleanOut['gridBallast']['ZIPloadPower'][key] = rawOut.get('eachZIPloadPower.csv')[key] if 'allZIPloadDemand.csv' in rawOut: cleanOut['gridBallast']['ZIPloadDemand'] = {} for key in rawOut['allZIPloadDemand.csv']: if key.startswith('ZIPload'): cleanOut['gridBallast']['ZIPloadDemand'][key] = rawOut.get('allZIPloadDemand.csv')[key] if 'allZIPloadOn.csv' in rawOut: cleanOut['gridBallast']['ZIPloadOn'] = {} for key in rawOut['allZIPloadOn.csv']: if key.startswith('ZIPload'): cleanOut['gridBallast']['ZIPloadOn'][key] = rawOut.get('allZIPloadOn.csv')[key] # EventTime calculations eventTime = inputDict['eventTime'] eventLength = inputDict['eventLength'] eventLength = eventLength.split(':') eventDuration = datetime.timedelta(hours=int(eventLength[0]), minutes=int(eventLength[1])) eventStart = datetime.datetime.strptime(eventTime, '%Y-%m-%d %H:%M') eventEnd = eventStart + eventDuration cleanOut['gridBallast']['eventStart'] = str(eventStart) cleanOut['gridBallast']['eventEnd'] = str(eventEnd) # Drop timezone from timeStamp, Convert string to date timeStamps = [x[:19] for x in cleanOut['timeStamps']] dateTimeStamps = [datetime.datetime.strptime(x, '%Y-%m-%d %H:%M:%S') for x in timeStamps] eventEndIdx = dateTimeStamps.index(eventEnd) # Recovery Time whOn = cleanOut['gridBallast']['waterheaterOn'] whOnList = whOn.values() whOnZip = zip(*whOnList) whOnSum = [sum(x) for x in whOnZip] anyOn = [x > 0 for x in whOnSum] tRecIdx = anyOn.index(True, eventEndIdx) tRec = dateTimeStamps[tRecIdx] cleanOut['gridBallast']['recoveryTime'] = str(tRec) # Waterheaters Off-Duration offDuration = tRec - eventStart cleanOut['gridBallast']['offDuration'] = str(offDuration) # Reserve Magnitude Target (RMT) availMag = cleanOut['gridBallast']['availabilityMagnitude'] totNetLoad = cleanOut['gridBallast']['totalNetworkLoad'] # loadZip = zip(availMag,totNetLoad) # rmt = [x[0]/x[1] for x in loadZip] rmt = (1000*sum(availMag))/sum(totNetLoad) cleanOut['gridBallast']['rmt'] = rmt # Reserve Magnitude Variability Tolerance (RMVT) avgAvailMag = sum(availMag)/len(availMag) rmvtMax = max(availMag)/avgAvailMag rmvtMin = min(availMag)/avgAvailMag rmvt = rmvtMax - rmvtMin cleanOut['gridBallast']['rmvt'] = rmvt # Availability notAvail = availMag.count(0)/len(timeStamps) avail = (1-notAvail)*100 cleanOut['gridBallast']['availability'] = avail # Waterheater Temperature Drop calculations whTemp = cleanOut['gridBallast']['waterheaterTemp'] whTempList = whTemp.values() whTempZip = zip(*whTempList) whTempDrops = [] LOWER_LIMIT_TEMP = 125 # Used for calculating quality of service. for time in whTempZip: tempDrop = sum([t < LOWER_LIMIT_TEMP for t in time]) whTempDrops.append(tempDrop) cleanOut['gridBallast']['waterheaterTempDrops'] = whTempDrops # ZIPload calculations for Availability and QoS zPower = cleanOut['gridBallast']['ZIPloadPower'] zPowerList = zPower.values() zPowerZip = zip(*zPowerList) zPowerSum = [sum(x) for x in zPowerZip] zDemand = cleanOut['gridBallast']['ZIPloadDemand'] zDemandList = zDemand.values() zDemandZip = zip(*zDemandList) zDrops = [] for time in zDemandZip: for each in zPowerZip: zIdx = 0 if each[zIdx] == 0: zPowerIdx += 1 zDrop = sum([t > 0 for t in time]) zDrops.append(zDrop) else: zDrops.append(0) cleanOut['gridBallast']['qualityDrops'] = [x + y for x, y in zip(whTempDrops, zDrops)] # What percentage of our keys have lat lon data? latKeys = [tree[key]['latitude'] for key in tree if 'latitude' in tree[key]] latPerc = 1.0*len(latKeys)/len(tree) if latPerc < 0.25: doNeato = True else: doNeato = False # Generate the frames for the system voltage map time traveling chart. genTime = generateVoltChart(tree, rawOut, modelDir, neatoLayout=doNeato) cleanOut['genTime'] = genTime # Aggregate up the timestamps: if level=='days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, "allOutputData.json"),"w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, "gldContainer", "PID.txt")) print "DONE RUNNING", modelDir except Exception as e: # If input range wasn't valid delete output, write error to disk. cancel(modelDir) thisErr = traceback.format_exc() print 'ERROR IN MODEL', modelDir, thisErr inputDict['stderr'] = thisErr with open(os.path.join(modelDir,'stderr.txt'),'w') as errorFile: errorFile.write(thisErr) with open(pJoin(modelDir,"allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) finishTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds = int((finishTime - beginTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent = 4) try: os.remove(pJoin(modelDir,"PPID.txt")) except: pass
def work(modelDir, inputDict): feederName = inputDict["feederName1"] inputDict["climateName"] = zipCodeToClimateName(inputDict["zipCode"]) shutil.copy(pJoin(__neoMetaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, "climate.tmy2")) feederJson = json.load(open(pJoin(modelDir, feederName+'.omd'))) tree = feederJson["tree"] # tree[feeder.getMaxKey(tree)+1] = {'object':'capacitor','control':'VOLT','phases':'ABCN','name':'CAPTEST','parent':'tm_1','capacitor_A':'0.10 MVAr','capacitor_B':'0.10 MVAr','capacitor_C':'0.10 MVAr','time_delay':'300.0','nominal_voltage':'2401.7771','voltage_set_high':'2350.0','voltage_set_low':'2340.0','switchA':'CLOSED','switchB':'CLOSED','switchC':'CLOSED','control_level':'INDIVIDUAL','phases_connected':'ABCN','dwell_time':'0.0','pt_phases':'ABCN'} # 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) # System check - linux doesn't support newer GridLAB-D versions if sys.platform == 'linux2': pass else: # print feeder.getMaxKey(tree) # tree[14,20,27,28,47] empty for UCS Egan, add climate object to tree[14] # HACK: tree[10:19] is empty tree[11] = {'omftype':'#include', 'argument':'\"hot_water_demand.glm\"'} tree[12] = {'omftype':'#include', 'argument':'\"lock_mode_schedule.glm\"'} tree[13] = {'omftype':'#include', 'argument':'\"control_priority_schedule.glm\"'} # Attach frequency player tree[14] = {'omftype':'class player', 'argument':'{double value;}'} tree[feeder.getMaxKey(tree)+1] = {'object':'player', 'file':'frequency.PLAYER', 'property':'value', 'name':'frequency', 'loop':0} # Set up GridBallast Controls totalWH = 0 totalZIP = 0 gbWH = 0 gbZIP = 0 for key in tree.keys(): # Waterheater Controller properties if ('name' in tree[key]) and (tree[key].get('object') == 'waterheater'): totalWH += 1 gbWH += 1 # Frequency control parameters tree[key]['enable_freq_control'] = 'true' tree[key]['measured_frequency'] = 'frequency.value' tree[key]['freq_lowlimit'] = 59 tree[key]['freq_uplimit'] = 61 tree[key]['heat_mode'] = 'ELECTRIC' # tree[key]['average_delay_time'] = 60 # Voltage control parameters # tree[key]['enable_volt_control'] = 'true' # tree[key]['volt_lowlimit'] = 240.4 # tree[key]['volt_uplimit'] = 241.4 # Lock Mode parameters # tree[key]['enable_lock'] = 'temp_lock_enable' # tree[key]['lock_STATUS'] = 'temp_lock_status' # Controller Priority: a.lock, b.freq, c.volt, d.therm tree[key]['controller_priority'] = 3214 #default:therm>lock>freq>volt # tree[key]['controller_priority'] = 1423 #freq>therm>volt>lock # tree[key]['controller_priority'] = 'control_priority' # fix waterheater property demand to water_demand for newer GridLAB-D versions if 'demand' in tree[key]: # tree[key]['water_demand'] = tree[key]['demand'] tree[key]['water_demand'] = 'weekday_hotwater*1' del tree[key]['demand'] # ZIPload Controller properties if ('name' in tree[key]) and (tree[key].get('object') == 'ZIPload'): totalZIP += 1 if tree[key]['name'].startswith('responsive'): gbZIP += 1 # Frequency control parameters tree[key]['enable_freq_control'] = 'true' tree[key]['measured_frequency'] = 'frequency.value' tree[key]['freq_lowlimit'] = 59 tree[key]['freq_uplimit'] = 61 # tree[key]['average_delay_time'] = 60 # Voltage control parameters # tree[key]['enable_volt_control'] = 'true' # tree[key]['volt_lowlimit'] = 240.4 # tree[key]['volt_uplimit'] = 241.4 # Lock Mode parameters # tree[key]['enable_lock'] = 'temp_lock_enable' # tree[key]['lock_STATUS'] = 'temp_lock_status' tree[key]['controller_priority'] = 4321 #default:lock>freq>volt>therm # tree[key]['controller_priority'] = 2431 #freq>volt>lock>therm # tree[key]['groupid'] = 'fan' # Attach collector for total network load tree[feeder.getMaxKey(tree)+1] = {'object':'collector', 'group':'"class=triplex_meter"', 'property':'sum(measured_real_power)', 'interval':60, 'file':'allMeterPower.csv'} # Attach collector for total waterheater load tree[feeder.getMaxKey(tree)+1] = {'object':'collector', 'group':'"class=waterheater"', 'property':'sum(actual_load)', 'interval':60, 'file':'allWaterheaterLoad.csv'} # Attach collector for total ZIPload power/load tree[feeder.getMaxKey(tree)+1] = {'object':'collector', 'group':'"class=ZIPload"', 'property':'sum(base_power)', 'interval':60, 'file':'allZIPloadPower.csv'} # Attach recorder for each ZIPload power/load tree[feeder.getMaxKey(tree)+1] = {'object':'group_recorder', 'group':'"class=ZIPload"', 'property':'base_power', 'interval':60, 'file':'eachZIPloadPower.csv'} # Attach recorder for all ZIPloads demand_rate tree[feeder.getMaxKey(tree)+1] = {'object':'group_recorder', 'group':'"class=ZIPload"', 'property':'demand_rate', 'interval':60, 'file':'allZIPloadDemand.csv'} # Attach recorder for waterheaters on/off tree[feeder.getMaxKey(tree)+1] = {'object':'group_recorder', 'group':'"class=waterheater"', 'property':'is_waterheater_on', 'interval':60, 'file':'allWaterheaterOn.csv'} # Attach recorder for waterheater tank temperatures tree[feeder.getMaxKey(tree)+1] = {'object':'group_recorder', 'group':'"class=waterheater"', 'property':'temperature', 'interval':60, 'file':'allWaterheaterTemp.csv'} # Attach recorders for system voltage map: stub = {'object':'group_recorder', 'group':'"class=node"', 'interval':60} 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 # Attach recorders for system voltage map, triplex: stub = {'object':'group_recorder', 'group':'"class=triplex_node"', 'interval':60} for phase in ['1','2']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'nVoltDump.csv' tree[feeder.getMaxKey(tree) + 1] = copyStub # And get meters for system voltage map: stub = {'object':'group_recorder', 'group':'"class=triplex_meter"', 'interval':60} for phase in ['1','2']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'mVoltDump.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)) outData = {} # Std Err and Std Out outData['stderr'] = rawOut['stderr'] outData['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: outData['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: outData['timeStamps'] = rawOut[key]['# property.. timestamp'] else: outData['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = outData.get('timeStamps',[]) level = inputDict.get('simLengthUnits','hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): outData['climate'] = {} outData['climate']['Rain Fall (in/h)'] = hdmAgg(rawOut[key].get('rainfall'), sum, level) outData['climate']['Wind Speed (m/s)'] = hdmAgg(rawOut[key].get('wind_speed'), avg, level) outData['climate']['Temperature (F)'] = hdmAgg(rawOut[key].get('temperature'), max, level) outData['climate']['Snow Depth (in)'] = hdmAgg(rawOut[key].get('snowdepth'), max, level) outData['climate']['Direct Normal (W/sf)'] = hdmAgg(rawOut[key].get('solar_direct'), sum, level) #outData['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] outData['climate']['Global Horizontal (W/sm)']=climateWbySMList # Voltage Band if 'VoltageJiggle.csv' in rawOut: outData['allMeterVoltages'] = {} outData['allMeterVoltages']['Min'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)']], min, level) outData['allMeterVoltages']['Mean'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)']], avg, level) outData['allMeterVoltages']['StdDev'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)']], avg, level) outData['allMeterVoltages']['Max'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)']], max, level) # Power Consumption outData['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads outData['Consumption']['Power'] = [0] * int(inputDict["simLength"]) outData['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) outData['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 outData['Consumption']: outData['Consumption']['Power'] = oneSwingPower else: outData['Consumption']['Power'] = vecSum(oneSwingPower,outData['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 outData['Consumption']: outData['Consumption']['DG'] = oneDgPower else: outData['Consumption']['DG'] = vecSum(oneDgPower,outData['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 outData['Consumption']: outData['Consumption']['DG'] = oneDgPower else: outData['Consumption']['DG'] = vecSum(oneDgPower,outData['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 outData['Consumption']: outData['Consumption']['Losses'] = oneLoss else: outData['Consumption']['Losses'] = vecSum(oneLoss,outData['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] outData[newkey] ={} outData[newkey]['RegTapA'] = [0] * int(inputDict["simLength"]) outData[newkey]['RegTapB'] = [0] * int(inputDict["simLength"]) outData[newkey]['RegTapC'] = [0] * int(inputDict["simLength"]) outData[newkey]['RegTapA'] = rawOut[key]['tap_A'] outData[newkey]['RegTapB'] = rawOut[key]['tap_B'] outData[newkey]['RegTapC'] = rawOut[key]['tap_C'] outData[newkey]['RegPhases'] = rawOut[key]['phases'][0] elif key.startswith('Capacitor_') and key.endswith('.csv'): capName="" capName = key newkey=capName.split(".")[0] outData[newkey] ={} outData[newkey]['Cap1A'] = [0] * int(inputDict["simLength"]) outData[newkey]['Cap1B'] = [0] * int(inputDict["simLength"]) outData[newkey]['Cap1C'] = [0] * int(inputDict["simLength"]) outData[newkey]['Cap1A'] = rawOut[key]['switchA'] outData[newkey]['Cap1B'] = rawOut[key]['switchB'] outData[newkey]['Cap1C'] = rawOut[key]['switchC'] outData[newkey]['CapPhases'] = rawOut[key]['phases'][0] # Print gridBallast Outputs to allOutputData.json outData['gridBallast'] = {} if 'allMeterPower.csv' in rawOut: outData['gridBallast']['totalNetworkLoad'] = [x / 1000 for x in rawOut.get('allMeterPower.csv')['sum(measured_real_power)']] #Convert W to kW if ('allZIPloadPower.csv' in rawOut) and ('allWaterheaterLoad.csv' in rawOut): outData['gridBallast']['availabilityMagnitude'] = [x[0] + x[1] for x in zip(rawOut.get('allWaterheaterLoad.csv')['sum(actual_load)'], rawOut.get('allZIPloadPower.csv')['sum(base_power)'])] if 'allZIPloadDemand.csv' in rawOut: outData['gridBallast']['ZIPloadDemand'] = {} for key in rawOut['allZIPloadDemand.csv']: if (key.startswith('ZIPload')) or (key.startswith('responsive')) or (key.startswith('unresponsive')): outData['gridBallast']['ZIPloadDemand'][key] = rawOut.get('allZIPloadDemand.csv')[key] if 'eachZIPloadPower.csv' in rawOut: outData['gridBallast']['ZIPloadPower'] = {} for key in rawOut['eachZIPloadPower.csv']: if (key.startswith('ZIPload')) or (key.startswith('responsive')) or (key.startswith('unresponsive')): outData['gridBallast']['ZIPloadPower'][key] = rawOut.get('eachZIPloadPower.csv')[key] if 'allWaterheaterOn.csv' in rawOut: outData['gridBallast']['waterheaterOn'] = {} for key in rawOut['allWaterheaterOn.csv']: if (key.startswith('waterheater')) or (key.startswith('waterHeater')): outData['gridBallast']['waterheaterOn'][key] = rawOut.get('allWaterheaterOn.csv')[key] if 'allWaterheaterTemp.csv' in rawOut: outData['gridBallast']['waterheaterTemp'] = {} for key in rawOut['allWaterheaterTemp.csv']: if (key.startswith('waterheater')) or (key.startswith('waterHeater')): outData['gridBallast']['waterheaterTemp'][key] = rawOut.get('allWaterheaterTemp.csv')[key] # System check - linux doesn't support newer GridLAB-D versions if sys.platform == 'linux2': pass else: outData['gridBallast']['penetrationLevel'] = 100*(gbWH+gbZIP)/(totalWH+totalZIP) # Frequency Player inArray = feederJson['attachments']['frequency.PLAYER'].split('\n') tempArray = [] for each in inArray: x = each.split(',') y = float(x[1]) tempArray.append(y) outData['frequencyPlayer'] = tempArray # EventTime calculations eventTime = inputDict['eventTime'] eventLength = inputDict['eventLength'].split(':') eventDuration = datetime.timedelta(hours=int(eventLength[0]), minutes=int(eventLength[1])) eventStart = datetime.datetime.strptime(eventTime, '%Y-%m-%d %H:%M') eventEnd = eventStart + eventDuration outData['gridBallast']['eventStart'] = str(eventStart) outData['gridBallast']['eventEnd'] = str(eventEnd) outData['gridBallast']['xMin'] = str(eventStart - datetime.timedelta(minutes=30)) outData['gridBallast']['xMax'] = str(eventEnd + datetime.timedelta(minutes=30)) # Convert string to date # HACK: remove timezones, inconsistency in matching format timeStampsDebug = [x[:19] for x in outData['timeStamps']] dateTimeStamps = [datetime.datetime.strptime(x, '%Y-%m-%d %H:%M:%S') for x in timeStampsDebug] eventEndIdx = dateTimeStamps.index(eventEnd) # Recovery Time whOn = outData['gridBallast']['waterheaterOn'] whOnList = whOn.values() whOnZip = zip(*whOnList) whOnSum = [sum(x) for x in whOnZip] anyOn = [x > 0 for x in whOnSum] tRecIdx = anyOn.index(True, eventEndIdx) tRec = dateTimeStamps[tRecIdx] recoveryTime = tRec - eventEnd outData['gridBallast']['recoveryTime'] = str(recoveryTime) # Waterheaters Off-Duration offDuration = tRec - eventStart outData['gridBallast']['offDuration'] = str(offDuration) # Reserve Magnitude (RM) availMag = outData['gridBallast']['availabilityMagnitude'] totalNetLoad = outData['gridBallast']['totalNetworkLoad'] availPerc = [100 * x[0]/x[1] for x in zip(availMag,totalNetLoad)] outData['gridBallast']['availabilityPercent'] = availPerc outData['gridBallast']['rm'] = [100 - x for x in availPerc] # Average RM during event eventRM = [100 - x[1] for x in zip(dateTimeStamps, availPerc) if (x[0] == eventStart) or (x[0] == eventEnd)] outData['gridBallast']['rmAvg'] = np.mean(eventRM) # Reserve Magnitude Variability Tolerance (RMVT) outData['gridBallast']['rmvt'] = np.std(eventRM) # Availability rmt = 7 available = [x[1] > rmt for x in zip(dateTimeStamps, availPerc) if (x[0] < eventStart) or (x[0] > eventEnd)] outData['gridBallast']['availability'] = 100.0 * sum(available) / (int(inputDict['simLength']) - int(eventLength[1]) - 1) # Waterheater Temperature Drop calculations whTemp = outData['gridBallast']['waterheaterTemp'] whTempList = whTemp.values() whTempZip = zip(*whTempList) whTempDrops = [] LOWER_LIMIT_TEMP = 110 # Used for calculating quality of service. Typical hot shower temp = 105 F. for time in whTempZip: tempDrop = sum([t < LOWER_LIMIT_TEMP for t in time]) whTempDrops.append(tempDrop) outData['gridBallast']['waterheaterTempDrops'] = whTempDrops # ZIPload calculations for Availability and QoS zPower = outData['gridBallast']['ZIPloadPower'] zPowerList = zPower.values() zPowerZip = zip(*zPowerList) zDemand = outData['gridBallast']['ZIPloadDemand'] zDemandList = zDemand.values() zDemandZip = zip(*zDemandList) zDrops = [] for x, y in zip(zPowerZip,zDemandZip): zDrop = 0 for i in range(len(x)): if (x[i] == 0) and (y[i] > 0): zDrop += 1 zDrops.append(zDrop) outData['gridBallast']['qualityDrops'] = [x + y for x, y in zip(zDrops, whTempDrops)] # 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) outData['genTime'] = genTime # Aggregate up the timestamps: if level=='days': outData['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': outData['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') return outData
def runForeground(modelDir, test_mode=False): ''' Run the model in its directory. WARNING: GRIDLAB CAN TAKE HOURS TO COMPLETE. ''' with open(pJoin(modelDir, 'allInputData.json')) as f: inputDict = json.load(f) print("STARTING TO RUN", modelDir) beginTime = datetime.datetime.now() # Get prepare of data and clean workspace if re-run, If re-run remove all the data in the subfolders for dirs in os.listdir(modelDir): if os.path.isdir(pJoin(modelDir, dirs)): shutil.rmtree(pJoin(modelDir, dirs)) # Get the names of the feeders from the .omd files: feederNames = [x[0:-4] for x in os.listdir(modelDir) if x.endswith(".omd")] for i, key in enumerate(feederNames): inputDict['feederName' + str(i + 1)] = feederNames[i] # Run GridLAB-D once for each feeder: for feederName in feederNames: try: os.remove(pJoin(modelDir, feederName, "allOutputData.json")) except Exception as e: pass if not os.path.isdir(pJoin(modelDir, feederName)): os.makedirs(pJoin(modelDir, feederName)) # create subfolders for feeders shutil.copy(pJoin(modelDir, feederName + ".omd"), pJoin(modelDir, feederName, "feeder.omd")) inputDict["climateName"] = weather.zipCodeToClimateName( inputDict["zipCode"]) shutil.copy( pJoin(_omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, feederName, "climate.tmy2")) try: startTime = datetime.datetime.now() with open(pJoin(modelDir, feederName, "feeder.omd")) as f: feederJson = json.load(f) tree = feederJson["tree"] # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) feeder.adjustTime(tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"]) # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem( tree, attachments=feederJson["attachments"], keepFiles=True, workDir=pJoin(modelDir, feederName)) cleanOut = {} # Std Err and Std Out cleanOut['stderr'] = rawOut['stderr'] cleanOut['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key][ '# property.. timestamp'] else: cleanOut['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get('timeStamps', []) level = inputDict.get('simLengthUnits', 'hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): cleanOut['climate'] = {} cleanOut['climate']['Rain Fall (in/h)'] = hdmAgg( rawOut[key].get('rainfall'), sum, level) cleanOut['climate']['Wind Speed (m/s)'] = hdmAgg( rawOut[key].get('wind_speed'), avg, level) cleanOut['climate']['Temperature (F)'] = hdmAgg( rawOut[key].get('temperature'), max, level) cleanOut['climate']['Snow Depth (in)'] = hdmAgg( rawOut[key].get('snowdepth'), max, level) cleanOut['climate']['Direct Insolation (W/m^2)'] = hdmAgg( rawOut[key].get('solar_direct'), sum, level) # Voltage Band if 'VoltageJiggle.csv' in rawOut: cleanOut['allMeterVoltages'] = {} cleanOut['allMeterVoltages']['Min'] = hdmAgg([ (i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)'] ], min, level) cleanOut['allMeterVoltages']['Mean'] = hdmAgg( [(i / 2) for i in rawOut['VoltageJiggle.csv'] ['mean(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['StdDev'] = hdmAgg([ (i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)'] ], avg, level) cleanOut['allMeterVoltages']['Max'] = hdmAgg([ (i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)'] ], max, level) cleanOut['allMeterVoltages']['stdDevPos'] = [ (x + y / 2) for x, y in zip(cleanOut['allMeterVoltages']['Mean'], cleanOut['allMeterVoltages']['StdDev']) ] cleanOut['allMeterVoltages']['stdDevNeg'] = [ (x - y / 2) for x, y in zip(cleanOut['allMeterVoltages']['Mean'], cleanOut['allMeterVoltages']['StdDev']) ] # Total # of meters count = 0 with open(pJoin(modelDir, feederName, "feeder.omd")) as f: for line in f: if "\"objectType\": \"triplex_meter\"" in line: count += 1 # print "count=", count cleanOut['allMeterVoltages']['triplexMeterCount'] = float(count) # Power Consumption cleanOut['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads cleanOut['Consumption']['Power'] = [0] * int( inputDict["simLength"]) cleanOut['Consumption']['Losses'] = [0] * int( inputDict["simLength"]) cleanOut['Consumption']['DG'] = [0] * int(inputDict["simLength"]) for key in rawOut: if key.startswith('SwingKids_') and key.endswith('.csv'): oneSwingPower = hdmAgg( vecPyth(rawOut[key]['sum(power_in.real)'], rawOut[key]['sum(power_in.imag)']), avg, level) if 'Power' not in cleanOut['Consumption']: cleanOut['Consumption']['Power'] = oneSwingPower else: cleanOut['Consumption']['Power'] = vecSum( oneSwingPower, cleanOut['Consumption']['Power']) elif key.startswith('Inverter_') and key.endswith('.csv'): realA = rawOut[key]['power_A.real'] realB = rawOut[key]['power_B.real'] realC = rawOut[key]['power_C.real'] imagA = rawOut[key]['power_A.imag'] imagB = rawOut[key]['power_B.imag'] imagC = rawOut[key]['power_C.imag'] oneDgPower = hdmAgg( vecSum(vecPyth(realA, imagA), vecPyth(realB, imagB), vecPyth(realC, imagC)), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum( oneDgPower, cleanOut['Consumption']['DG']) elif key.startswith('Windmill_') and key.endswith('.csv'): vrA = rawOut[key]['voltage_A.real'] vrB = rawOut[key]['voltage_B.real'] vrC = rawOut[key]['voltage_C.real'] viA = rawOut[key]['voltage_A.imag'] viB = rawOut[key]['voltage_B.imag'] viC = rawOut[key]['voltage_C.imag'] crB = rawOut[key]['current_B.real'] crA = rawOut[key]['current_A.real'] crC = rawOut[key]['current_C.real'] ciA = rawOut[key]['current_A.imag'] ciB = rawOut[key]['current_B.imag'] ciC = rawOut[key]['current_C.imag'] powerA = vecProd(vecPyth(vrA, viA), vecPyth(crA, ciA)) powerB = vecProd(vecPyth(vrB, viB), vecPyth(crB, ciB)) powerC = vecProd(vecPyth(vrC, viC), vecPyth(crC, ciC)) # HACK: multiply by negative one because turbine power sign is opposite all other DG: oneDgPower = [ -1.0 * x for x in hdmAgg( vecSum(powerA, powerB, powerC), avg, level) ] if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum( oneDgPower, cleanOut['Consumption']['DG']) elif key in [ 'OverheadLosses.csv', 'UndergroundLosses.csv', 'TriplexLosses.csv', 'TransformerLosses.csv' ]: realA = rawOut[key]['sum(power_losses_A.real)'] imagA = rawOut[key]['sum(power_losses_A.imag)'] realB = rawOut[key]['sum(power_losses_B.real)'] imagB = rawOut[key]['sum(power_losses_B.imag)'] realC = rawOut[key]['sum(power_losses_C.real)'] imagC = rawOut[key]['sum(power_losses_C.imag)'] oneLoss = hdmAgg( vecSum(vecPyth(realA, imagA), vecPyth(realB, imagB), vecPyth(realC, imagC)), avg, level) if 'Losses' not in cleanOut['Consumption']: cleanOut['Consumption']['Losses'] = oneLoss else: cleanOut['Consumption']['Losses'] = vecSum( oneLoss, cleanOut['Consumption']['Losses']) # Aggregate up the timestamps: if level == 'days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x: x[0][0:10], 'days') elif level == 'months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x: x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, feederName, "allOutputData.json"), "w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str( datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, feederName, "allInputData.json"), "w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, feederName, "PID.txt")) print("DONE RUNNING GRIDLABMULTI", modelDir, feederName) except Exception as e: if test_mode == True: raise e print("MODEL CRASHED GRIDLABMULTI", e, modelDir, feederName) cancel(pJoin(modelDir, feederName)) with open(pJoin(modelDir, feederName, "stderr.txt"), "a+") as stderrFile: traceback.print_exc(file=stderrFile) 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) # Integrate data into allOutputData.json, if error happens, cancel it try: output = {} output["failures"] = {} numOfFeeders = 0 for root, dirs, files in os.walk(modelDir): # dump error info into dict if "stderr.txt" in files: with open(pJoin(root, "stderr.txt"), "r") as stderrFile: tempString = stderrFile.read() if "ERROR" in tempString or "FATAL" in tempString or "Traceback" in tempString: output["failures"]["feeder_" + str(os.path.split(root)[-1])] = { "stderr": tempString } continue # dump simulated data into dict if "allOutputData.json" in files: with open(pJoin(root, "allOutputData.json"), "r") as feederOutputData: numOfFeeders += 1 feederOutput = json.load(feederOutputData) # TODO: a better feeder name output["feeder_" + str(os.path.split(root)[-1])] = {} output["feeder_" + str(os.path.split(root)[-1] )]["Consumption"] = feederOutput["Consumption"] output["feeder_" + str(os.path.split(root)[-1])][ "allMeterVoltages"] = feederOutput["allMeterVoltages"] output["feeder_" + str(os.path.split( root)[-1])]["stderr"] = feederOutput["stderr"] output["feeder_" + str(os.path.split( root)[-1])]["stdout"] = feederOutput["stdout"] # output[root] = {feederOutput["Consumption"], feederOutput["allMeterVoltages"], feederOutput["stdout"], feederOutput["stderr"]} output["numOfFeeders"] = numOfFeeders output["timeStamps"] = feederOutput.get("timeStamps", []) output["climate"] = feederOutput.get("climate", []) # Add feederNames to output so allInputData feederName changes don't cause output rendering to disappear. for key, feederName in inputDict.items(): if 'feederName' in key: output[key] = feederName with open(pJoin(modelDir, "allOutputData.json"), "w") as outFile: json.dump(output, outFile, indent=4) try: os.remove(pJoin(modelDir, "PPID.txt")) except: pass # Send email to user on model success. emailStatus = inputDict.get('emailStatus', 0) if (emailStatus == "on"): print("\n EMAIL ALERT ON") email = session['user_id'] try: with open("data/User/" + email + ".json") as f: user = json.load(f) modelPath, modelName = pSplit(modelDir) message = "The model " + "<i>" + str( modelName ) + "</i>" + " has successfully completed running. It ran for a total of " + str( inputDict["runTime"]) + " seconds from " + str( beginTime) + ", to " + str(finishTime) + "." return web.send_link(email, message, user) except Exception as e: print("ERROR: Failed sending model status email to user: "******", with exception: \n", e) 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) # Send email to user on model failure. email = 'NoEmail' try: email = session['user_id'] with open("data/User/" + email + ".json") as f: user = json.load(f) modelPath, modelName = pSplit(modelDir) message = "The model " + "<i>" + str( modelName ) + "</i>" + " has failed to complete running. It ran for a total of " + str( inputDict["runTime"]) + " seconds from " + str( beginTime) + ", to " + str(finishTime) + "." return web.send_link(email, message, user) except Exception as e: print("Failed sending model status email to user: "******", with exception: \n", e)
def run(modelDir, inputDict): ''' Run the model in its directory. ''' try: # Set up GLM with correct time and recorders: omd = json.load(open(pJoin(modelDir, 'feeder.omd'))) tree = omd.get('tree', {}) feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) feeder.adjustTime(tree, 120, 'hours', '2011-01-01') # Run GridLAB-D startTime = dt.datetime.now() rawOut = gridlabd.runInFilesystem(tree, attachments=omd.get( 'attachments', {}), workDir=modelDir) # Clean the output. 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)'] = rawOut[key].get( 'rainfall') cleanOut['climate']['Wind Speed (m/s)'] = rawOut[key].get( 'wind_speed') cleanOut['climate']['Temperature (F)'] = rawOut[key].get( 'temperature') cleanOut['climate']['Snow Depth (in)'] = rawOut[key].get( 'snowdepth') cleanOut['climate']['Direct Normal (W/sf)'] = rawOut[key].get( 'solar_direct') climateWbySFList = rawOut[key].get('solar_global') #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'] = [ float(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)'] ] cleanOut['allMeterVoltages']['Mean'] = [ float(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)'] ] cleanOut['allMeterVoltages']['StdDev'] = [ float(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)'] ] cleanOut['allMeterVoltages']['Max'] = [ float(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)'] ] # Dump the results. endTime = dt.datetime.now() inputDict["runTime"] = str( dt.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, "allInputData.json"), "w") as inFile: json.dump(inputDict, inFile, indent=4) with open(pJoin(modelDir, "allOutputData.json"), "w") as outFile: json.dump(cleanOut, outFile, indent=4) except: # 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)
def work(modelDir,inputDict): '''This reads a glm file, changes the method of powerflow and reruns''' feederName = [x for x in os.listdir(modelDir) if x.endswith('.omd')][0][:-4] inputDict['feederName1'] = feederName feederPath = pJoin(modelDir,feederName+'.omd') # Reads a pre-calibrated feeder. outData = {} with open(feederPath, 'r') as jsonIn: feederJson = json.load(jsonIn) localTree = feederJson.get('tree', {}) attachments = feederJson.get('attachments', {}) for key in localTree: if 'solver_method' in localTree[key].keys(): localTree[key]['solver_method'] = 'FBS' #find the swing bus and recorder attached to substation try: for key in localTree: if localTree[key].get('bustype','').lower() == 'swing': swingIndex = key swingName = localTree[key].get('name') if localTree[key].get('object','') == 'regulator' and localTree[key].get('from','') == swingName: regIndex = key regConfName = localTree[key]['configuration'] except: raise ValueError('Invalid feeder selected:', str(inputDict['feederName1'])) #find the regulator and capacitor names and combine to form a string for volt-var control object regKeys = [] accum_reg = '' for key in localTree: if localTree[key].get('object','') == 'regulator': accum_reg += localTree[key].get('name','ERROR') + ',' regKeys.append(key) regstr = accum_reg[:-1] 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' capstr = accum_cap[:-1] # 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(inputDict['simLengthHours']) simStartDate = inputDict['simStart'] feeder.adjustTime(localTree,HOURS,'hours',simStartDate) output = gridlabd.runInFilesystem(localTree, attachments, keepFiles=False,workDir=modelDir) try: os.remove(pJoin(modelDir,'PID.txt')) except: pass p = output['Zregulator.csv']['power_in.real'] q = output['Zregulator.csv']['power_in.imag'] #calculating length of simulation because it migth be different from the simulation input HOURS simRealLength = int(len(p)) #time delays from configuration files time_delay_reg = '30.0' time_delay_cap = '300.0' for key in localTree: if localTree[key].get('object','') == 'regulator_configuration': time_delay_reg = localTree[key]['time_delay'] # if localTree[key].get('object','') == "capacitor": # time_delay_cap = localTree[key]['time_delay'] #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()]) localTree[max_key+1] = {'object' : 'volt_var_control', 'name' : 'IVVC1', 'control_method' : 'ACTIVE', 'capacitor_delay' : str(time_delay_cap), 'regulator_delay' : str(time_delay_reg), 'desired_pf' : '0.99', 'd_max' : '0.6', 'd_min' : '0.1', 'substation_link' : str(localTree[regIndex]['name']), 'regulator_list' : regstr, 'capacitor_list': capstr, 'voltage_measurements': str(inputDict.get('voltageNodes', 'IVVC1')), } #running powerflow analysis via gridalab after attaching a regulator feeder.adjustTime(localTree,HOURS,'hours',simStartDate) output1 = gridlabd.runInFilesystem(localTree,attachments,keepFiles=True,workDir=modelDir) os.remove(pJoin(modelDir,'PID.txt')) pnew = output1['NewZregulator.csv']['power_in.real'] qnew = output1['NewZregulator.csv']['power_in.imag'] #total real and imaginary losses as a function of time def vecSum(u,v): ''' Add vectors u and v element-wise. Return has len <= len(u) and <=len(v). ''' return map(sum, zip(u,v)) def zeroVec(length): ''' Give a zero vector of input length. ''' return [0 for x in xrange(length)] (realLoss, imagLoss, realLossnew, imagLossnew) = (zeroVec(int(HOURS)) for x in range(4)) for device in ['ZlossesOverhead.csv','ZlossesTransformer.csv','ZlossesUnderground.csv']: for letter in ['A','B','C']: realLoss = vecSum(realLoss, output[device]['sum(power_losses_' + letter + '.real)']) imagLoss = vecSum(imagLoss, output[device]['sum(power_losses_' + letter + '.imag)']) realLossnew = vecSum(realLossnew, output1['New'+device]['sum(power_losses_' + letter + '.real)']) imagLossnew = vecSum(imagLossnew, output1['New'+device]['sum(power_losses_' + letter + '.imag)']) #voltage calculations and tap calculations def divby2(u): '''divides by 2''' return u/2 lowVoltage = [] meanVoltage = [] highVoltage = [] lowVoltagenew = [] meanVoltagenew = [] highVoltagenew = [] tap = {'A':[],'B':[],'C':[]} tapnew = {'A':[],'B':[],'C':[]} volt = {'A':[],'B':[],'C':[]} voltnew = {'A':[],'B':[],'C':[]} switch = {'A':[],'B':[],'C':[]} switchnew = {'A':[],'B':[],'C':[]} for letter in ['A','B','C']: tap[letter] = output['Zregulator.csv']['tap_' + letter] tapnew[letter] = output1['NewZregulator.csv']['tap_' + letter] if capKeys != []: switch[letter] = output['ZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch'+ letter] switchnew[letter] = output1['NewZcapSwitch' + str(int(capKeys[0])) + '.csv']['switch'+ letter] volt[letter] = map(returnMag,output['ZsubstationBottom.csv']['voltage_'+letter]) voltnew[letter] = map(returnMag,output1['NewZsubstationBottom.csv']['voltage_'+letter]) lowVoltage = map(divby2,output['ZvoltageJiggle.csv']['min(voltage_12.mag)']) lowVoltagenew = map(divby2,output1['NewZvoltageJiggle.csv']['min(voltage_12.mag)']) meanVoltage = map(divby2,output['ZvoltageJiggle.csv']['mean(voltage_12.mag)']) meanVoltagenew = map(divby2,output1['NewZvoltageJiggle.csv']['mean(voltage_12.mag)']) highVoltage = map(divby2,output['ZvoltageJiggle.csv']['max(voltage_12.mag)']) highVoltagenew = map(divby2,output1['NewZvoltageJiggle.csv']['max(voltage_12.mag)']) #energy calculations whEnergy = [] whLosses = [] whLoads = [] whEnergy.append(sum(p)/10**6) whLosses.append(sum(realLoss)/10**6) whLoads.append((sum(p)-sum(realLoss))/10**6) whEnergy.append(sum(pnew)/10**6) whLosses.append(sum(realLossnew)/10**6) whLoads.append((sum(pnew)-sum(realLossnew))/10**6) indices = ['No IVVC', 'With IVVC'] # energySalesRed = (whLoads[1]-whLoads[0])*(inputDict['wholesaleEnergyCostPerKwh'])*1000 # lossSav = (whLosses[0]-whLosses[1])*inputDict['wholesaleEnergyCostPerKwh']*1000 #plots ticks = [] plt.clf() plt.title('total energy') plt.ylabel('total load and losses (MWh)') for element in range(2): ticks.append(element) bar_loss = plt.bar(element, whLosses[element], 0.15, color= 'red') bar_load = plt.bar(element+0.15, whLoads[element], 0.15, color= 'orange') plt.legend([bar_load[0],bar_loss[0]],['total load', 'total losses'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode='expand', borderaxespad=0.1) plt.xticks([t+0.15 for t in ticks],indices) plt.savefig(pJoin(modelDir,'totalEnergy.png')) #real and imaginary power plt.figure('real power') plt.title('Real Power at substation') plt.ylabel('substation real power (MW)') pMW = [element/10**6 for element in p] pMWn = [element/10**6 for element in pnew] pw = plt.plot(pMW) npw = plt.plot(pMWn) plt.legend([pw[0], npw[0]], ['NO IVVC','WITH IVVC'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode='expand', borderaxespad=0.1) plt.savefig(pJoin(modelDir,'realPower.png')) plt.figure('Reactive power') plt.title('Reactive Power at substation') plt.ylabel('substation reactive power (MVAR)') qMVAR = [element/10**6 for element in q] qMVARn = [element/10**6 for element in qnew] iw = plt.plot(qMVAR) niw = plt.plot(qMVARn) plt.legend([iw[0], niw[0]], ['NO IVVC','WITH IVVC'],bbox_to_anchor=(0., 0.915, 1., .102), loc=3, ncol=2, mode='expand', borderaxespad=0.1) plt.savefig(pJoin(modelDir,'imaginaryPower.png')) #voltage plots plt.figure('voltages as a function of time') f,ax = plt.subplots(2,sharex=True) f.suptitle('Min and Max voltages on the feeder') lv = ax[0].plot(lowVoltage,color = 'cadetblue') mv = ax[0].plot(meanVoltage,color = 'blue') hv = ax[0].plot(highVoltage, color = 'cadetblue') ax[0].legend([lv[0], mv[0], hv[0]], ['low voltage','mean voltage','high voltage'],bbox_to_anchor=(0., 0.915, 1., .1), loc=3, ncol=3, mode='expand', borderaxespad=0.1) ax[0].set_ylabel('NO IVVC') nlv = ax[1].plot(lowVoltagenew,color = 'cadetblue') nmv = ax[1].plot(meanVoltagenew,color = 'blue') nhv = ax[1].plot(highVoltagenew, color = 'cadetblue') ax[1].set_ylabel('WITH IVVC') plt.savefig(pJoin(modelDir,'Voltages.png')) #tap positions plt.figure('TAP positions NO IVVC') f,ax = plt.subplots(6,sharex=True) f.set_size_inches(10,12.0) #f.suptitle("Regulator Tap positions") ax[0].plot(tap['A']) ax[0].set_title('Regulator Tap positions NO IVVC') ax[0].set_ylabel('TAP A') ax[1].plot(tap['B']) ax[1].set_ylabel('TAP B') ax[2].plot(tap['C']) ax[2].set_ylabel('TAP C') ax[3].plot(tapnew['A']) ax[3].set_title('WITH IVVC') ax[3].set_ylabel('TAP A') ax[4].plot(tapnew['B']) ax[4].set_ylabel('TAP B') ax[5].plot(tapnew['C']) ax[5].set_ylabel('TAP C') for subplot in range(6): ax[subplot].set_ylim(-20,20) f.tight_layout() plt.savefig(pJoin(modelDir,'RegulatorTAPpositions.png')) #substation voltages plt.figure('substation voltage as a function of time') f,ax = plt.subplots(6,sharex=True) f.set_size_inches(10,12.0) #f.suptitle("voltages at substation NO IVVC") ax[0].plot(volt['A']) ax[0].set_title('Substation voltages NO IVVC') ax[0].set_ylabel('voltage A') ax[1].plot(volt['B']) ax[1].set_ylabel('voltage B') ax[2].plot(volt['C']) ax[2].set_ylabel('voltage C') ax[3].plot(voltnew['A']) ax[3].set_title('WITH IVVC') ax[3].set_ylabel('voltage A') ax[4].plot(voltnew['B']) ax[4].set_ylabel('voltage B') ax[5].plot(voltnew['C']) ax[5].set_ylabel('voltage C') f.tight_layout() plt.savefig(pJoin(modelDir,'substationVoltages.png')) #cap switches plt.figure('capacitor switch state as a function of time') f,ax = plt.subplots(6,sharex=True) f.set_size_inches(10,12.0) #f.suptitle("Capacitor switch state NO IVVC") ax[0].plot(switch['A']) ax[0].set_title('Capacitor switch state NO IVVC') ax[0].set_ylabel('switch A') ax[1].plot(switch['B']) ax[1].set_ylabel('switch B') ax[2].plot(switch['C']) ax[2].set_ylabel('switch C') ax[3].plot(switchnew['A']) ax[3].set_title('WITH IVVC') ax[3].set_ylabel('switch A') ax[4].plot(switchnew['B']) ax[4].set_ylabel('switch B') ax[5].plot(switchnew['C']) ax[5].set_ylabel('switch C') for subplot in range(6): ax[subplot].set_ylim(-2,2) f.tight_layout() plt.savefig(pJoin(modelDir,'capacitorSwitch.png')) #plt.show() #monetization monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] monthToSeason = {'January':'Winter','February':'Winter','March':'Spring','April':'Spring', 'May':'Spring','June':'Summer','July':'Summer','August':'Summer', 'September':'Fall','October':'Fall','November':'Fall','December':'Winter'} #calculate the month and hour of simulation start and month and hour of simulation end simStartTimestamp = simStartDate + ' 00:00:00' simFormattedDate = dt.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] #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)] for monthElement in simMonthList: 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(inputDict['peakDemandCost'+str(monthToSeason[monthElement])+'PerKw']) lossRedDollars[monthElement] = (sum(realLoss[index1:index2])/1000.0 - sum(realLossnew[index1:index2])/1000.0)*(float(inputDict['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(inputDict['wholesaleEnergyCostPerKwh']) - float(inputDict['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] eld = [energyLostDollars[month] for month in simMonthList] lrd = [lossRedDollars[month] for month in simMonthList] psd = [peakSaveDollars[month] for month in simMonthList] bar_eld = plt.bar(ticks,eld,0.15,color='red') bar_psd = plt.bar(ticks1,psd,0.15,color='blue') bar_lrd = plt.bar(ticks2,lrd,0.15,color='green') plt.legend([bar_eld[0], bar_psd[0], bar_lrd[0]], ['energyLostDollars','peakReductionDollars','lossReductionDollars'],bbox_to_anchor=(0., 1.015, 1., .102), loc=3, ncol=2, mode='expand', borderaxespad=0.1) monShort = [element[0:3] for element in simMonthList] plt.xticks([t+0.15 for t in ticks],monShort) plt.ylabel('Utility Savings ($)') plt.savefig(pJoin(modelDir,'spendChart.png')) #cumulative savings graphs fig = plt.figure('cost benefit barchart',figsize=(10,5)) annualSavings = sum(eld) + sum(lrd) + sum(psd) annualSave = lambda x:(annualSavings - float(inputDict['omCost'])) * x - float(inputDict['capitalCost']) simplePayback = float(inputDict['capitalCost'])/(annualSavings - float(inputDict['omCost'])) plt.xlabel('Year After Installation') plt.xlim(0,30) plt.ylabel('Cumulative Savings ($)') plt.plot([0 for x in range(31)],c='gray') plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--') plt.plot([annualSave(x) for x in range(31)], c='green') plt.savefig(pJoin(modelDir,'savingsChart.png')) #get exact time stamps from the CSV files generated by Gridlab-D timeWithZone = output['Zregulator.csv']['# timestamp'] timestamps = [element[:19] for element in timeWithZone] #data for highcharts outData['timeStamps'] = timestamps outData['noCVRPower'] = p outData['withCVRPower'] = pnew outData['noCVRLoad'] = whLoads[0] outData['withCVRLoad'] = whLoads[1] outData['noCVRLosses'] = whLosses[0] outData['withCVRLosses'] = whLosses[1] outData['noCVRTaps'] = tap outData['withCVRTaps'] = tapnew outData['noCVRSubVolts'] = volt outData['withCVRSubVolts'] = voltnew outData['noCVRCapSwitch'] = switch outData['withCVRCapSwitch'] = switchnew outData['noCVRHighVolt'] = highVoltage outData['withCVRHighVolt'] = highVoltagenew outData['noCVRLowVolt'] = lowVoltage outData['withCVRLowVolt'] = lowVoltagenew outData['noCVRMeanVolt'] = meanVoltage outData['withCVRMeanVolt'] = meanVoltagenew #monetization outData['simMonthList'] = monShort outData['energyLostDollars'] = energyLostDollars outData['lossRedDollars'] = lossRedDollars outData['peakSaveDollars'] = peakSaveDollars outData['annualSave'] = [annualSave(x) for x in range(31)] # Generate warnings #TODO: Timezone adjustment try: # Check if times for simulation and scada match. scadaDates = [] with open(pJoin(modelDir,'subScadaCalibrated1.player'),'r') as scadaFile: for line in scadaFile: (date,val) = line.split(',') scadaDates.append(str(date)) simFormattedEndDate = simFormattedDate + timedelta(hours=HOURS) scadaStartDate = dt.strptime(scadaDates[0].split(' PST')[0],"%Y-%m-%d %H:%M:%S") scadaEndDate = dt.strptime(scadaDates[len(scadaDates)-1].split(' PST')[0],"%Y-%m-%d %H:%M:%S") beginRange = (scadaStartDate - simFormattedDate).total_seconds() endRange = (scadaEndDate - simFormattedEndDate).total_seconds() # Check if houses exist. housesExist, voltageNodeExists = False, False for key in localTree: if localTree[key].get('object','') == 'house': housesExist = True if localTree[key].get('name','') == str(inputDict.get('voltageNodes', 0)): voltageNodeExists = True if (beginRange > 0.0 or endRange < 0.0) and not housesExist: outData['warnings'] = '<strong>WARNING:</strong> The simulation dates entered are not compatible with the scada curve in the feeder.' # Check if voltage node exists. if not voltageNodeExists: if outData.get('warnings','') != '': previousWarning = outData['warnings'] outData['warnings'] = previousWarning + ' The voltage node: ' + str(inputDict.get('voltageNodes', 0)) + ' does not exist in the feeder.' else: outData['warnings'] = '<strong>WARNING:</strong> The voltage node <i>' + str(inputDict.get('voltageNodes', 0)) + '</i> does not exist in the feeder.' except: pass # # Update the runTime in the input file. # endTime = dt.now() # with open(pJoin(modelDir,"allInputData.json"),"w") as inFile: # json.dump(inputDict, inFile, indent=4) # with open(pJoin(modelDir,"outDataData.json"),"w") as outFile: # json.dump(outData, outFile, indent=4) # # For autotest, there won't be such file. # try: # os.remove(pJoin(modelDir, "PPID.txt")) # except Exception, e: # pass return outData
def work(modelDir, inputDict): ''' Run the model in its directory. WARNING: GRIDLAB CAN TAKE HOURS TO COMPLETE. ''' # feederName = inputDict["feederName1"] feederName = [x for x in os.listdir(modelDir) if x.endswith('.omd')][0][:-4] inputDict["feederName1"] = feederName inputDict["climateName"] = zipCodeToClimateName(inputDict["zipCode"]) shutil.copy(pJoin(__neoMetaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, "climate.tmy2")) 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"', 'interval':3600} 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 # Attach recorders for system voltage map, triplex: stub = {'object':'group_recorder', 'group':'"class=triplex_node"', 'interval':3600} for phase in ['1','2']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'nVoltDump.csv' tree[feeder.getMaxKey(tree) + 1] = copyStub # Attach current recorder for overhead_lines currentStub = {'object':'group_recorder', 'group':'"class=overhead_line"', 'interval':3600} for phase in ['A','B','C']: copyCurrentStub = dict(currentStub) copyCurrentStub['property'] = 'current_out_' + phase copyCurrentStub['file'] = 'OH_line_current_phase' + phase + '.csv' tree[feeder.getMaxKey(tree) + 1] = copyCurrentStub rating_stub = {'object':'group_recorder', 'group':'"class=overhead_line"', 'interval':3600} copyRatingStub = dict(rating_stub) copyRatingStub['property'] = 'continuous_rating' copyRatingStub['file'] = 'OH_line_cont_rating.csv' tree[feeder.getMaxKey(tree) + 1] = copyRatingStub flow_stub = {'object':'group_recorder', 'group':'"class=overhead_line"', 'interval':3600} copyFlowStub = dict(flow_stub) copyFlowStub['property'] = 'flow_direction' copyFlowStub['file'] = 'OH_line_flow_direc.csv' tree[feeder.getMaxKey(tree) + 1] = copyFlowStub # Attach current recorder for underground_lines currentStubOH = {'object':'group_recorder', 'group':'"class=underground_line"', 'interval':3600} for phase in ['A','B','C']: copyCurrentStubOH = dict(currentStubOH) copyCurrentStubOH['property'] = 'current_out_' + phase copyCurrentStubOH['file'] = 'UG_line_current_phase' + phase + '.csv' tree[feeder.getMaxKey(tree) + 1] = copyCurrentStubOH ug_rating_stub = {'object':'group_recorder', 'group':'"class=underground_line"', 'interval':3600} copyUGRatingStub = dict(ug_rating_stub) copyUGRatingStub['property'] = 'continuous_rating' copyUGRatingStub['file'] = 'UG_line_cont_rating.csv' tree[feeder.getMaxKey(tree) + 1] = copyUGRatingStub ug_flow_stub = {'object':'group_recorder', 'group':'"class=underground_line"', 'interval':3600} ugCopyFlowStub = dict(ug_flow_stub) ugCopyFlowStub['property'] = 'flow_direction' ugCopyFlowStub['file'] = 'UG_line_flow_direc.csv' tree[feeder.getMaxKey(tree) + 1] = ugCopyFlowStub # And get meters for system voltage map: stub = {'object':'group_recorder', 'group':'"class=triplex_meter"', 'interval':3600} for phase in ['1','2']: copyStub = dict(stub) copyStub['property'] = 'voltage_' + phase copyStub['file'] = phase.lower() + 'mVoltDump.csv' tree[feeder.getMaxKey(tree) + 1] = copyStub for key in tree: if 'bustype' in tree[key].keys(): if tree[key]['bustype'] == 'SWING': tree[key]['object'] = 'meter' swingN = tree[key]['name'] swingRecord = {'object':'recorder', 'property':'voltage_A,measured_real_power,measured_power','file':'subVoltsA.csv','parent':swingN, 'interval':60} tree[feeder.getMaxKey(tree) + 1] = swingRecord for key in tree: if 'omftype' in tree[key].keys() and tree[key]['argument']=='minimum_timestep=3600': tree[key]['argument'] = 'minimum_timestep=60' # If there is a varvolt object in the tree, add recorder to swingbus and node from voltage_measurements property # Find var_volt object downLineNode = 'None' for key in tree: if 'object' in tree[key].keys() and tree[key]['object']=='volt_var_control': downLineNode = tree[key]['voltage_measurements'] if downLineNode != 'None': downNodeRecord = {'object':'recorder', 'property':'voltage_A','file':'firstDownlineVoltsA.csv','parent':downLineNode, 'interval':60} tree[feeder.getMaxKey(tree) + 1] = downNodeRecord # Violation recorder to display to users # violationRecorder = {'object':'violation_recorder','node_continuous_voltage_limit_lower':0.95,'file':'Violation_Log.csv', # 'secondary_dist_voltage_rise_lower_limit':-0.042,'substation_pf_lower_limit':0.85,'substation_breaker_C_limit':300, # 'secondary_dist_voltage_rise_upper_limit':0.025,'substation_breaker_B_limit':300,'violation_flag':'ALLVIOLATIONS', # 'node_instantaneous_voltage_limit_upper':1.1, 'inverter_v_chng_per_interval_lower_bound':-0.05, 'virtual_substation':swingN, # 'substation_breaker_A_limit':300, 'xfrmr_thermal_limit_lower':0,'node_continuous_voltage_interval':300,'strict':'false', # 'node_instantaneous_voltage_limit_lower':0,'line_thermal_limit_upper':1,'echo':'false','node_continuous_voltage_limit_upper':1.05, # 'interval':30,'line_thermal_limit_lower':0,'summary':'Violation_Summary.csv','inverter_v_chng_interval':60, # 'xfrmr_thermal_limit_upper':2,'inverter_v_chng_per_interval_upper_bound':0.050} # tree[feeder.getMaxKey(tree) + 1] = violationRecorder 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)) # voltDumps have no values when gridlabD fails or the files dont exist if not os.path.isfile(pJoin(modelDir,'aVoltDump.csv')): with open (pJoin(modelDir,'stderr.txt')) as inFile: stdErrText = inFile.read() message = 'GridLAB-D crashed. Error log:\n' + stdErrText raise Exception(message) elif len(rawOut['aVoltDump.csv']['# timestamp']) == 0: with open (pJoin(modelDir,'stderr.txt')) as inFile: stdErrText = inFile.read() message = 'GridLAB-D crashed. Error log:\n' + stdErrText raise Exception(message) outData = {} # Std Err and Std Out outData['stderr'] = rawOut['stderr'] outData['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: outData['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: outData['timeStamps'] = rawOut[key]['# property.. timestamp'] else: outData['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = outData.get('timeStamps',[]) level = inputDict.get('simLengthUnits','hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): outData['climate'] = {} outData['climate']['Rain Fall (in/h)'] = hdmAgg(rawOut[key].get('rainfall'), sum, level) outData['climate']['Wind Speed (m/s)'] = hdmAgg(rawOut[key].get('wind_speed'), avg, level) outData['climate']['Temperature (F)'] = hdmAgg(rawOut[key].get('temperature'), max, level) outData['climate']['Snow Depth (in)'] = hdmAgg(rawOut[key].get('snowdepth'), max, level) outData['climate']['Direct Normal (W/sf)'] = hdmAgg(rawOut[key].get('solar_direct'), sum, level) #outData['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] outData['climate']['Global Horizontal (W/sm)']=climateWbySMList # Voltage Band if 'VoltageJiggle.csv' in rawOut: outData['allMeterVoltages'] = {} outData['allMeterVoltages']['Min'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)']], min, level) outData['allMeterVoltages']['Mean'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)']], avg, level) outData['allMeterVoltages']['StdDev'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)']], avg, level) outData['allMeterVoltages']['Max'] = hdmAgg([float(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)']], max, level) # Power Consumption outData['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads outData['Consumption']['Power'] = [0] * int(inputDict["simLength"]) outData['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) outData['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 outData['Consumption']: outData['Consumption']['Power'] = oneSwingPower else: outData['Consumption']['Power'] = vecSum(oneSwingPower,outData['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 outData['Consumption']: outData['Consumption']['DG'] = oneDgPower else: outData['Consumption']['DG'] = vecSum(oneDgPower,outData['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 outData['Consumption']: outData['Consumption']['DG'] = oneDgPower else: outData['Consumption']['DG'] = vecSum(oneDgPower,outData['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 outData['Consumption']: outData['Consumption']['Losses'] = oneLoss else: outData['Consumption']['Losses'] = vecSum(oneLoss,outData['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] outData[newkey] ={} outData[newkey]['RegTapA'] = [0] * int(inputDict["simLength"]) outData[newkey]['RegTapB'] = [0] * int(inputDict["simLength"]) outData[newkey]['RegTapC'] = [0] * int(inputDict["simLength"]) outData[newkey]['RegTapA'] = rawOut[key]['tap_A'] outData[newkey]['RegTapB'] = rawOut[key]['tap_B'] outData[newkey]['RegTapC'] = rawOut[key]['tap_C'] outData[newkey]['RegPhases'] = rawOut[key]['phases'][0] elif key.startswith('Capacitor_') and key.endswith('.csv'): capName="" capName = key newkey=capName.split(".")[0] outData[newkey] ={} outData[newkey]['Cap1A'] = [0] * int(inputDict["simLength"]) outData[newkey]['Cap1B'] = [0] * int(inputDict["simLength"]) outData[newkey]['Cap1C'] = [0] * int(inputDict["simLength"]) outData[newkey]['Cap1A'] = rawOut[key]['switchA'] outData[newkey]['Cap1B'] = rawOut[key]['switchB'] outData[newkey]['Cap1C'] = rawOut[key]['switchC'] outData[newkey]['CapPhases'] = rawOut[key]['phases'][0] # Capture voltages at the swingbus # Loop through voltDump for swingbus voltages subData = [] downData = [] with open(pJoin(modelDir,"subVoltsA.csv")) as subFile: reader = csv.reader(subFile) subData = [x for x in reader] if downLineNode != 'None': with open(pJoin(modelDir,"firstDownlineVoltsA.csv")) as downFile: reader = csv.reader(downFile) downData = [x for x in reader] FIRST_DATA_ROW = 9 cleanDown = [stringToMag(x[1]) for x in downData[FIRST_DATA_ROW:-1]] swingTimestamps = [x[0] for x in subData[FIRST_DATA_ROW:-1]] cleanSub = [stringToMag(x[1]) for x in subData[FIRST_DATA_ROW:-1]] # real_power / power powerFactors = [] for row in subData[FIRST_DATA_ROW:-1]: powerFactors.append(abs(float(row[2])/stringToMag(row[3]))) outData['powerFactors'] = powerFactors outData['swingVoltage'] = cleanSub outData['downlineNodeVolts'] = cleanDown outData['swingTimestamps'] = swingTimestamps # If there is a var volt system, find the min and max voltage for a band minVoltBand = [] maxVoltBand = [] if downLineNode != 'None': for key in tree: objKeys = tree[key].keys() if 'object' in objKeys: if tree[key]['object']=='volt_var_control': minVoltBand.append(float(tree[key]['minimum_voltages'])) maxVoltBand.append(float(tree[key]['maximum_voltages'])) outData['minVoltBand'] = minVoltBand outData['maxVoltBand'] = maxVoltBand # Violation Summary and Log # violationData = '' # violationArray = [] # with open(pJoin(modelDir,"Violation_Summary.csv")) as vioSum: # reader = csv.reader(vioSum) # for row in reader: # violationArray.append(row) # for row in violationArray[4:]: # violationData += str(' '.join(row)) + "\n" # outData["violationSummary"] = violationData # violationLogArray = [] # violationLog = '' # with open(pJoin(modelDir,"Violation_Log.csv")) as vioLog: # logger = csv.reader(vioLog) # for row in logger: # violationLogArray.append(row) # for row in violationLogArray[6:]: # violationLog += str(' '.join(row)) + "\n" # outData['violationLog'] = violationLog # 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, mapTimestamp = generateVoltChart(tree, rawOut, modelDir, neatoLayout=doNeato) outData['genTime'] = genTime outData['mapTimestamp'] = mapTimestamp # Aggregate up the timestamps: if level=='days': outData['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': outData['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') return outData
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
def runForeground(modelDir, inputDict, fs): """ Run the model in its directory. WARNING: GRIDLAB CAN TAKE HOURS TO COMPLETE. """ print "STARTING TO RUN", modelDir beginTime = datetime.datetime.now() feederList = [] # Get prepare of data and clean workspace if re-run, If re-run remove all # the data in the subfolders for dirs in os.listdir(modelDir): if os.path.isdir(pJoin(modelDir, dirs)): shutil.rmtree(pJoin(modelDir, dirs)) # Get each feeder, prepare data in separate folders, and run there. for key in sorted(inputDict, key=inputDict.get): if key.startswith("feederName"): feederDir, feederName = inputDict[key].split("___") feederList.append(feederName) try: os.remove(pJoin(modelDir, feederName, "allOutputData.json")) fs.remove(pJoin(modelDir, feederName, "allOutputData.json")) except Exception, e: pass if not os.path.isdir(pJoin(modelDir, feederName)): # create subfolders for feeders os.makedirs(pJoin(modelDir, feederName)) fs.export_from_fs_to_local( pJoin("data", "Feeder", feederDir, feederName + ".json"), pJoin(modelDir, feederName, "feeder.json") ) inputDict["climateName"], latforpvwatts = zipCodeToClimateName(inputDict["zipCode"], fs) fs.export_from_fs_to_local( pJoin("data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, feederName, "climate.tmy2"), ) try: startTime = datetime.datetime.now() feederJson = json.load(open(pJoin(modelDir, feederName, "feeder.json"))) tree = feederJson["tree"] # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) feeder.adjustTime( tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"], ) if "attachments" in feederJson: attachments = feederJson["attachments"] else: attachments = [] # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem( tree, attachments=attachments, keepFiles=True, workDir=pJoin(modelDir, feederName) ) cleanOut = {} # Std Err and Std Out cleanOut["stderr"] = rawOut["stderr"] cleanOut["stdout"] = rawOut["stdout"] # Time Stamps for key in rawOut: if "# timestamp" in rawOut[key]: cleanOut["timeStamps"] = rawOut[key]["# timestamp"] break elif "# property.. timestamp" in rawOut[key]: cleanOut["timeStamps"] = rawOut[key]["# property.. timestamp"] else: cleanOut["timeStamps"] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get("timeStamps", []) level = inputDict.get("simLengthUnits", "hours") # Climate for key in rawOut: if key.startswith("Climate_") and key.endswith(".csv"): cleanOut["climate"] = {} cleanOut["climate"]["Rain Fall (in/h)"] = hdmAgg( rawOut[key].get("rainfall"), sum, level, stamps ) cleanOut["climate"]["Wind Speed (m/s)"] = hdmAgg( rawOut[key].get("wind_speed"), avg, level, stamps ) cleanOut["climate"]["Temperature (F)"] = hdmAgg( rawOut[key].get("temperature"), max, level, stamps ) cleanOut["climate"]["Snow Depth (in)"] = hdmAgg( rawOut[key].get("snowdepth"), max, level, stamps ) cleanOut["climate"]["Direct Insolation (W/m^2)"] = hdmAgg( rawOut[key].get("solar_direct"), sum, level, stamps ) # 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, stamps ) cleanOut["allMeterVoltages"]["Mean"] = hdmAgg( [float(i / 2) for i in rawOut["VoltageJiggle.csv"]["mean(voltage_12.mag)"]], avg, level, stamps ) cleanOut["allMeterVoltages"]["StdDev"] = hdmAgg( [float(i / 2) for i in rawOut["VoltageJiggle.csv"]["std(voltage_12.mag)"]], avg, level, stamps ) cleanOut["allMeterVoltages"]["Max"] = hdmAgg( [float(i / 2) for i in rawOut["VoltageJiggle.csv"]["max(voltage_12.mag)"]], max, level, stamps ) # 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, stamps, ) 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, stamps, ) if "DG" not in cleanOut["Consumption"]: cleanOut["Consumption"]["DG"] = oneDgPower else: cleanOut["Consumption"]["DG"] = vecSum(oneDgPower, cleanOut["Consumption"]["DG"]) elif key.startswith("Windmill_") and key.endswith(".csv"): vrA = rawOut[key]["voltage_A.real"] vrB = rawOut[key]["voltage_B.real"] vrC = rawOut[key]["voltage_C.real"] viA = rawOut[key]["voltage_A.imag"] viB = rawOut[key]["voltage_B.imag"] viC = rawOut[key]["voltage_C.imag"] crB = rawOut[key]["current_B.real"] crA = rawOut[key]["current_A.real"] crC = rawOut[key]["current_C.real"] ciA = rawOut[key]["current_A.imag"] ciB = rawOut[key]["current_B.imag"] ciC = rawOut[key]["current_C.imag"] powerA = vecProd(vecPyth(vrA, viA), vecPyth(crA, ciA)) powerB = vecProd(vecPyth(vrB, viB), vecPyth(crB, ciB)) powerC = vecProd(vecPyth(vrC, viC), vecPyth(crC, ciC)) # HACK: multiply by negative one because turbine power # sign is opposite all other DG: oneDgPower = [-1.0 * x for x in hdmAgg(vecSum(powerA, powerB, powerC), avg, level, stamps)] 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, stamps, ) if "Losses" not in cleanOut["Consumption"]: cleanOut["Consumption"]["Losses"] = oneLoss else: cleanOut["Consumption"]["Losses"] = vecSum(oneLoss, cleanOut["Consumption"]["Losses"]) # Aggregate up the timestamps: if level == "days": cleanOut["timeStamps"] = aggSeries(stamps, stamps, lambda x: x[0][0:10], "days") elif level == "months": cleanOut["timeStamps"] = aggSeries(stamps, stamps, lambda x: x[0][0:7], "months") # Write the output. with open(pJoin(modelDir, feederName, "allOutputData.json"), "w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, feederName, "allInputData.json"), "w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, feederName, "PID.txt")) print "DONE RUNNING GRIDLABMULTI", modelDir, feederName except Exception as e: print "MODEL CRASHED GRIDLABMULTI", e, modelDir, feederName cancel(pJoin(modelDir, feederName)) with open(pJoin(modelDir, feederName, "stderr.txt"), "a+") as stderrFile: traceback.print_exc(file=stderrFile)
def omfCalibrate(workDir, feederPath, scadaPath, simStartDate, simLength, simLengthUnits, solver="FBS", calibrateError=(0.05, 5), trim=5): '''calibrates a feeder and saves the calibrated tree at a location. Note: feeders with cap banks should be calibrated with cap banks OPEN. We have seen cap banks throw off calibration.''' with open(feederPath, "r") as jsonIn: feederJson = json.load(jsonIn) tree = feederJson.get("tree", {}) simLength = simLength + trim # Process scada data. scadaSubPower = _processScadaData(pJoin(workDir, "gridlabD"), scadaPath, simStartDate, simLengthUnits) # Load specified solver. for key in tree: if tree[key].get("module", "").lower() == "powerflow": tree[key] = {"module": "powerflow", "solver_method": solver} # Attach player. classOb = {'omftype': 'class player', 'argument': '{double value;}'} playerOb = { "object": "player", "property": "value", "name": "scadaLoads", "file": "subScada.player", "loop": "0" } maxKey = feeder.getMaxKey(tree) playerKey = maxKey + 2 tree[maxKey + 1] = classOb tree[playerKey] = playerOb # Make loads reference player. loadTemplate = { "object": "triplex_load", "power_pf_12": "0.95", "impedance_pf_12": "0.98", "power_pf_12": "0.90", "impedance_fraction_12": "0.7", "power_fraction_12": "0.3" } loadTemplateR = { "object": "load", "impedance_pf_A": "0.98", "impedance_pf_B": "0.98", "impedance_pf_C": "0.98", "power_pf_A": "0.90", "power_pf_B": "0.90", "power_pf_C": "0.90", "impedance_fraction_A": "0.7", "impedance_fraction_B": "0.7", "impedance_fraction_C": "0.7", "power_fraction_A": "0.3", "power_fraction_B": "0.3", "power_fraction_C": "0.3" } for key in tree: ob = tree[key] if ob.get("object", "") in ("triplex_node", "triplex_load") and ( ob.get("power_12") or ob.get("base_power_12")): # Add to triplex_nodes. newOb = dict(loadTemplate) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["nominal_voltage"] = ob.get("nominal_voltage", "") newOb["latitude"] = ob.get("latitude", "0") newOb["longitude"] = ob.get("longitude", "0") oldPow = ob.get("power_12", "").replace("j", "d") if not oldPow: oldPow = ob.get("base_power_12") if "scadaloads.value*" in oldPow: oldPow = oldPow[17:] pythagPower = gridlabd._strClean(oldPow) newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower) tree[key] = newOb elif ob.get("object", "") == "load": # Add to residential_loads too. newOb = dict(loadTemplateR) newOb["name"] = ob.get("name", "") newOb["parent"] = ob.get("parent", "") newOb["phases"] = ob.get("phases", "") newOb["load_class"] = ob.get("load_class", "") newOb["nominal_voltage"] = ob.get("nominal_voltage", "") newOb["latitude"] = ob.get("latitude", "0") newOb["longitude"] = ob.get("longitude", "0") try: oldPow = ob.get("constant_power_A", "").replace("j", "d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_A"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_B", "").replace("j", "d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_B"] = "scadaLoads.value*" + str(pythagPower) except: pass try: oldPow = ob.get("constant_power_C", "").replace("j", "d") pythagPower = gridlabd._strClean(oldPow) newOb["base_power_C"] = "scadaLoads.value*" + str(pythagPower) except: pass tree[key] = newOb # Convert swing bus to a meter. for key in tree: if tree[key].get('bustype', '').lower() == 'swing' and tree[key].get( 'object', '') != 'meter': swingName = tree[key].get('name') regIndex = key tree[key]['object'] = 'meter' # Search for the substation meter and attach a recorder there. for key in tree: if tree[key].get('bustype', '').lower() == 'swing': swingName = tree[key].get('name') recOb = { "object": "recorder", "parent": swingName, "property": "measured_real_power,measured_reactive_power,measured_power", "file": "caliSub.csv", "interval": "3600" } outputRecorderKey = maxKey + 3 tree[outputRecorderKey] = recOb feeder.adjustTime(tree, simLength, simLengthUnits, simStartDate['Date'].strftime("%Y-%m-%d %H:%M:%S")) # Run Gridlabd, calculate scaling constant. def runPowerflowIter(tree, scadaSubPower): '''Runs powerflow once, then iterates.''' # Run initial powerflow to get power. print "Starting calibration." print "Goal of calibration: Error: %s, Iterations: <%s, trim: %s" % ( calibrateError[0], calibrateError[1], trim) output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin(workDir, "gridlabD")) outRealPow = output["caliSub.csv"]["measured_real_power"][ trim:simLength] outImagPower = output["caliSub.csv"]["measured_reactive_power"][ trim:simLength] outAppPowerKw = [(x[0]**2 + x[1]**2)**0.5 / 1000 for x in zip(outRealPow, outImagPower)] lastFile = "subScada.player" nextFile = "subScadaCalibrated.player" nextPower = outAppPowerKw error = (sum(outRealPow) / 1000 - sum(scadaSubPower)) / sum(scadaSubPower) iteration = 1 print "First error:", error while abs(error) > calibrateError[0] and iteration < calibrateError[1]: # Run calibration and iterate up to 5 times. SCAL_CONST = sum(scadaSubPower) / sum(nextPower) print "Calibrating & running again... Error: %s, Iteration: %s, SCAL_CONST: %s" % ( str(round(abs(error * 100), 6)), str(iteration), round(SCAL_CONST, 6)) newPlayData = [] with open(pJoin(pJoin(workDir, "gridlabD"), lastFile), "r") as playerFile: for line in playerFile: (key, val) = line.split(',') newPlayData.append( str(key) + ',' + str(float(val) * SCAL_CONST) + "\n") with open(pJoin(pJoin(workDir, "gridlabD"), nextFile), "w") as playerFile: for row in newPlayData: playerFile.write(row) tree[playerKey]["file"] = nextFile tree[outputRecorderKey]["file"] = "caliSubCheck.csv" nextOutput = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=pJoin( workDir, "gridlabD")) outRealPowIter = nextOutput["caliSubCheck.csv"][ "measured_real_power"][trim:simLength] outImagPowerIter = nextOutput["caliSubCheck.csv"][ "measured_reactive_power"][trim:simLength] nextAppKw = [(x[0]**2 + x[1]**2)**0.5 / 1000 for x in zip(outRealPowIter, outImagPowerIter)] lastFile = nextFile nextFile = "subScadaCalibrated" + str(iteration) + ".player" nextPower = nextAppKw # Compute error and iterate. error = (sum(outRealPowIter) / 1000 - sum(scadaSubPower)) / sum(scadaSubPower) iteration += 1 else: if iteration == 1: outRealPowIter = outRealPow SCAL_CONST = 1.0 print "Calibration done: Error: %s, Iteration: %s, SCAL_CONST: %s" % ( str(round(abs(error * 100), 2)), str(iteration), round(SCAL_CONST, 2)) return outRealPow, outRealPowIter, lastFile, iteration outRealPow, outRealPowIter, lastFile, iteration = runPowerflowIter( tree, scadaSubPower[trim:simLength]) caliPowVectors = [[ float(element) for element in scadaSubPower[trim:simLength] ], [float(element) / 1000 for element in outRealPow], [float(element) / 1000 for element in outRealPowIter]] labels = ["scadaSubPower", "initialGuess", "finalGuess"] colors = ['red', 'lightblue', 'blue'] chartData = { "Title": "Substation Calibration Check (Iterated " + str(iteration + 1) + "X)", "fileName": "caliCheckPlot", "colors": colors, "labels": labels, "timeZone": simStartDate['timeZone'] } # Trimming vectors to make them all the same length as the smallest vector minCaliPowVecLen = min(len(caliPowVectors[0]), len(caliPowVectors[1]), len(caliPowVectors[2])) caliPowVectors[0] = caliPowVectors[0][:minCaliPowVecLen] caliPowVectors[1] = caliPowVectors[1][:minCaliPowVecLen] caliPowVectors[2] = caliPowVectors[2][:minCaliPowVecLen] print "Len:", len(caliPowVectors[0]), len(caliPowVectors[1]), len( caliPowVectors[2]) plotLine(workDir, caliPowVectors, chartData, simStartDate['Date'] + dt.timedelta(hours=trim), simLengthUnits) # Write the final output. with open(pJoin(workDir, "calibratedFeeder.omd"), "w") as outJson: playerString = open(pJoin(pJoin(workDir, "gridlabD"), lastFile)).read() feederJson["attachments"][lastFile] = playerString feederJson["tree"] = tree json.dump(feederJson, outJson, indent=4) return
def runForeground(modelDir): ''' Run the model in its directory. WARNING: GRIDLAB CAN TAKE HOURS TO COMPLETE. ''' inputDict = json.load(open(pJoin(modelDir, 'allInputData.json'))) print "STARTING TO RUN", modelDir beginTime = datetime.datetime.now() # Get prepare of data and clean workspace if re-run, If re-run remove all the data in the subfolders for dirs in os.listdir(modelDir): if os.path.isdir(pJoin(modelDir, dirs)): shutil.rmtree(pJoin(modelDir, dirs)) # Get the names of the feeders from the .omd files: feederNames = [x[0:-4] for x in os.listdir(modelDir) if x.endswith(".omd")] for i, key in enumerate(feederNames): inputDict['feederName' + str(i + 1)] = feederNames[i] # Run GridLAB-D once for each feeder: for feederName in feederNames: try: os.remove(pJoin(modelDir, feederName, "allOutputData.json")) except Exception, e: pass if not os.path.isdir(pJoin(modelDir, feederName)): os.makedirs(pJoin(modelDir, feederName)) # create subfolders for feeders shutil.copy(pJoin(modelDir, feederName + ".omd"), pJoin(modelDir, feederName, "feeder.omd")) inputDict["climateName"] = zipCodeToClimateName(inputDict["zipCode"]) shutil.copy(pJoin(_omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"), pJoin(modelDir, feederName, "climate.tmy2")) try: startTime = datetime.datetime.now() feederJson = json.load(open(pJoin(modelDir, feederName, "feeder.omd"))) tree = feederJson["tree"] # Set up GLM with correct time and recorders: feeder.attachRecorders(tree, "Regulator", "object", "regulator") feeder.attachRecorders(tree, "Capacitor", "object", "capacitor") feeder.attachRecorders(tree, "Inverter", "object", "inverter") feeder.attachRecorders(tree, "Windmill", "object", "windturb_dg") feeder.attachRecorders(tree, "CollectorVoltage", None, None) feeder.attachRecorders(tree, "Climate", "object", "climate") feeder.attachRecorders(tree, "OverheadLosses", None, None) feeder.attachRecorders(tree, "UndergroundLosses", None, None) feeder.attachRecorders(tree, "TriplexLosses", None, None) feeder.attachRecorders(tree, "TransformerLosses", None, None) feeder.groupSwingKids(tree) feeder.adjustTime(tree=tree, simLength=float(inputDict["simLength"]), simLengthUnits=inputDict["simLengthUnits"], simStartDate=inputDict["simStartDate"]) # RUN GRIDLABD IN FILESYSTEM (EXPENSIVE!) rawOut = gridlabd.runInFilesystem(tree, attachments=feederJson["attachments"], keepFiles=True, workDir=pJoin(modelDir, feederName)) cleanOut = {} # Std Err and Std Out cleanOut['stderr'] = rawOut['stderr'] cleanOut['stdout'] = rawOut['stdout'] # Time Stamps for key in rawOut: if '# timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# timestamp'] break elif '# property.. timestamp' in rawOut[key]: cleanOut['timeStamps'] = rawOut[key]['# property.. timestamp'] else: cleanOut['timeStamps'] = [] # Day/Month Aggregation Setup: stamps = cleanOut.get('timeStamps',[]) level = inputDict.get('simLengthUnits','hours') # Climate for key in rawOut: if key.startswith('Climate_') and key.endswith('.csv'): cleanOut['climate'] = {} cleanOut['climate']['Rain Fall (in/h)'] = hdmAgg(rawOut[key].get('rainfall'), sum, level) cleanOut['climate']['Wind Speed (m/s)'] = hdmAgg(rawOut[key].get('wind_speed'), avg, level) cleanOut['climate']['Temperature (F)'] = hdmAgg(rawOut[key].get('temperature'), max, level) cleanOut['climate']['Snow Depth (in)'] = hdmAgg(rawOut[key].get('snowdepth'), max, level) cleanOut['climate']['Direct Insolation (W/m^2)'] = hdmAgg(rawOut[key].get('solar_direct'), sum, level) # Voltage Band if 'VoltageJiggle.csv' in rawOut: cleanOut['allMeterVoltages'] = {} cleanOut['allMeterVoltages']['Min'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['min(voltage_12.mag)']], min, level) cleanOut['allMeterVoltages']['Mean'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['mean(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['StdDev'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['std(voltage_12.mag)']], avg, level) cleanOut['allMeterVoltages']['Max'] = hdmAgg([(i / 2) for i in rawOut['VoltageJiggle.csv']['max(voltage_12.mag)']], max, level) cleanOut['allMeterVoltages']['stdDevPos'] = [(x+y/2) for x,y in zip(cleanOut['allMeterVoltages']['Mean'], cleanOut['allMeterVoltages']['StdDev'])] cleanOut['allMeterVoltages']['stdDevNeg'] = [(x-y/2) for x,y in zip(cleanOut['allMeterVoltages']['Mean'], cleanOut['allMeterVoltages']['StdDev'])] # Total # of meters count = 0 with open(pJoin(modelDir, feederName, "feeder.omd")) as f: for line in f: if "\"objectType\": \"triplex_meter\"" in line: count+=1 # print "count=", count cleanOut['allMeterVoltages']['triplexMeterCount'] = float(count) # Power Consumption cleanOut['Consumption'] = {} # Set default value to be 0, avoiding missing value when computing Loads cleanOut['Consumption']['Power'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['Losses'] = [0] * int(inputDict["simLength"]) cleanOut['Consumption']['DG'] = [0] * int(inputDict["simLength"]) for key in rawOut: if key.startswith('SwingKids_') and key.endswith('.csv'): oneSwingPower = hdmAgg(vecPyth(rawOut[key]['sum(power_in.real)'],rawOut[key]['sum(power_in.imag)']), avg, level) if 'Power' not in cleanOut['Consumption']: cleanOut['Consumption']['Power'] = oneSwingPower else: cleanOut['Consumption']['Power'] = vecSum(oneSwingPower,cleanOut['Consumption']['Power']) elif key.startswith('Inverter_') and key.endswith('.csv'): realA = rawOut[key]['power_A.real'] realB = rawOut[key]['power_B.real'] realC = rawOut[key]['power_C.real'] imagA = rawOut[key]['power_A.imag'] imagB = rawOut[key]['power_B.imag'] imagC = rawOut[key]['power_C.imag'] oneDgPower = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key.startswith('Windmill_') and key.endswith('.csv'): vrA = rawOut[key]['voltage_A.real'] vrB = rawOut[key]['voltage_B.real'] vrC = rawOut[key]['voltage_C.real'] viA = rawOut[key]['voltage_A.imag'] viB = rawOut[key]['voltage_B.imag'] viC = rawOut[key]['voltage_C.imag'] crB = rawOut[key]['current_B.real'] crA = rawOut[key]['current_A.real'] crC = rawOut[key]['current_C.real'] ciA = rawOut[key]['current_A.imag'] ciB = rawOut[key]['current_B.imag'] ciC = rawOut[key]['current_C.imag'] powerA = vecProd(vecPyth(vrA,viA),vecPyth(crA,ciA)) powerB = vecProd(vecPyth(vrB,viB),vecPyth(crB,ciB)) powerC = vecProd(vecPyth(vrC,viC),vecPyth(crC,ciC)) # HACK: multiply by negative one because turbine power sign is opposite all other DG: oneDgPower = [-1.0 * x for x in hdmAgg(vecSum(powerA,powerB,powerC), avg, level)] if 'DG' not in cleanOut['Consumption']: cleanOut['Consumption']['DG'] = oneDgPower else: cleanOut['Consumption']['DG'] = vecSum(oneDgPower,cleanOut['Consumption']['DG']) elif key in ['OverheadLosses.csv', 'UndergroundLosses.csv', 'TriplexLosses.csv', 'TransformerLosses.csv']: realA = rawOut[key]['sum(power_losses_A.real)'] imagA = rawOut[key]['sum(power_losses_A.imag)'] realB = rawOut[key]['sum(power_losses_B.real)'] imagB = rawOut[key]['sum(power_losses_B.imag)'] realC = rawOut[key]['sum(power_losses_C.real)'] imagC = rawOut[key]['sum(power_losses_C.imag)'] oneLoss = hdmAgg(vecSum(vecPyth(realA,imagA),vecPyth(realB,imagB),vecPyth(realC,imagC)), avg, level) if 'Losses' not in cleanOut['Consumption']: cleanOut['Consumption']['Losses'] = oneLoss else: cleanOut['Consumption']['Losses'] = vecSum(oneLoss,cleanOut['Consumption']['Losses']) # Aggregate up the timestamps: if level=='days': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:10], 'days') elif level=='months': cleanOut['timeStamps'] = aggSeries(stamps, stamps, lambda x:x[0][0:7], 'months') # Write the output. with open(pJoin(modelDir, feederName, "allOutputData.json"),"w") as outFile: json.dump(cleanOut, outFile, indent=4) # Update the runTime in the input file. endTime = datetime.datetime.now() inputDict["runTime"] = str(datetime.timedelta(seconds=int((endTime - startTime).total_seconds()))) with open(pJoin(modelDir, feederName, "allInputData.json"),"w") as inFile: json.dump(inputDict, inFile, indent=4) # Clean up the PID file. os.remove(pJoin(modelDir, feederName,"PID.txt")) print "DONE RUNNING GRIDLABMULTI", modelDir, feederName except Exception as e: print "MODEL CRASHED GRIDLABMULTI", e, modelDir, feederName cancel(pJoin(modelDir, feederName)) with open(pJoin(modelDir, feederName, "stderr.txt"), "a+") as stderrFile: traceback.print_exc(file = stderrFile)
def main(): ''' JSON manipulation, Gridlab running, etc. goes here. ''' # Import data. feedJson = json.load(open('./ABEC Frank Calibrated.json')) tree = feedJson['tree'] # Input data, model-style. inputDict = {'simLength':24,'simStartDate':'2011-01-01', 'simLengthUnits':'hours'} # Add recorders. 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[omf.feeder.getMaxKey(tree) + 1] = copyStub feeder.adjustTime(tree, inputDict['simLength'], inputDict['simLengthUnits'], inputDict['simStartDate']) # Run gridlab. allOutputData = runInFilesystem(tree, attachments=feedJson['attachments'], keepFiles=True, workDir='.', glmName='ABEC Frank SolverGen.glm') try: os.remove('PID.txt') except: pass print 'Gridlab ran correctly', allOutputData.keys() # Make plots. #TODO: figure out what to do about neato taking like 2 minutes to run. neatoLayout = True # Detect the feeder nominal voltage: for key in tree: ob = tree[key] if type(ob)==dict and ob.get('bustype','')=='SWING': feedVoltage = float(ob.get('nominal_voltage',1)) # Make a graph object. fGraph = omf.feeder.treeToNxGraph(tree) if neatoLayout: # HACK: work on a new graph without attributes because graphViz tries to read attrs. cleanG = nx.Graph(fGraph.edges()) cleanG.add_nodes_from(fGraph) positions = nx.graphviz_layout(cleanG, prog='neato') else: positions = {n:fGraph.node[n].get('pos',(0,0)) for n in fGraph} # Plot all time steps. for step, stamp in enumerate(allOutputData['aVoltDump.csv']['# timestamp']): # Build voltage map. nodeVolts = {} for nodeName in [x for x in allOutputData['aVoltDump.csv'].keys() if x != '# timestamp']: allVolts = [] for phase in ['A','B','C']: v = complex(allOutputData[phase.lower() + 'VoltDump.csv'][nodeName][step]) phaseVolt = _pythag(v.real, v.imag) if phaseVolt != 0.0: if _digits(phaseVolt)>3: # Normalize to 120 V standard phaseVolt = phaseVolt*(120/feedVoltage) allVolts.append(phaseVolt) # HACK: Take average of all phases to collapse dimensionality. nodeVolts[nodeName] = _avg(allVolts) # Apply voltage map and chart it. voltChart = plt.figure(figsize=(10,10)) plt.axes(frameon = 0) plt.axis('off') edgeIm = nx.draw_networkx_edges(fGraph, positions) nodeIm = nx.draw_networkx_nodes(fGraph, pos = positions, node_color = [nodeVolts.get(n,0) for n in fGraph.nodes()], linewidths = 0, node_size = 30, cmap = plt.cm.jet) plt.sci(nodeIm) plt.clim(110,130) plt.colorbar() plt.title(stamp) voltChart.savefig('./pngs/volts' + str(step).zfill(3) + '.png') # Reclaim memory by closing, deleting and garbage collecting the last chart. voltChart.clf() plt.close() del voltChart gc.collect()