Beispiel #1
0
def run(modelDir, inputDict, fs):
    ''' Run the model in its directory. '''
    logger.info("Running voltageDrop model... modelDir: %s; inputDict: %s", modelDir, inputDict)
    startTime = dt.datetime.now()
    allOutput = {}
    # Check whether model exist or not
    if not os.path.isdir(modelDir):
        os.makedirs(modelDir)
    inputDict["created"] = str(dt.datetime.now())
    with open(pJoin(modelDir, "allInputData.json"), "w") as inputFile:
        json.dump(inputDict, inputFile, indent=4)
    # Copy feeder data into the model directory.
    feederDir, feederName = inputDict["feederName"].split("___")
    fs.export_from_fs_to_local(pJoin("data", "Feeder", feederDir, feederName + ".json"),
                              pJoin(modelDir, "feeder.json"))
    # Create voltage drop plot.
    tree = json.load(open(pJoin(modelDir, "feeder.json"))).get("tree", {})
    if inputDict.get("layoutAlgorithm", "geospatial") == "geospatial":
        neato = False
    else:
        neato = True
    chart = voltPlot(tree, workDir=modelDir, neatoLayout=neato)
    Plot.save_fig(plt, pJoin(modelDir, "output.png"))
    with open(pJoin(modelDir, "output.png"), "rb") as inFile:
        allOutput["voltageDrop"] = inFile.read().encode("base64")
    fs.save(pJoin(modelDir, "allOutputData.json"), json.dumps(allOutput, indent=4))
    # Update the runTime in the input file.
    endTime = dt.datetime.now()
    inputDict["runTime"] = str(
        dt.timedelta(seconds=int((endTime - startTime).total_seconds())))
    fs.save(pJoin(modelDir, "allInputData.json"), json.dumps(inputDict, indent=4))
Beispiel #2
0
def runForeground(modelDir, inputDict, fs):
    ''' Run the model in the foreground. WARNING: can take about a minute. '''
    # Global vars, and load data from the model directory.
    print "STARTING TO RUN", modelDir
    try:
        startTime = datetime.datetime.now()
        feederJson = json.load(open(pJoin(modelDir, "feeder.json")))
        tree = feederJson.get("tree", {})
        attachments = feederJson.get("attachments", {})
        allOutput = {}
        ''' Run CVR analysis. '''
        # Reformate monthData and rates.
        rates = {k: float(inputDict[k]) for k in ["capitalCost", "omCost", "wholesaleEnergyCostPerKwh",
                                                  "retailEnergyCostPerKwh", "peakDemandCostSpringPerKw", "peakDemandCostSummerPerKw",
                                                  "peakDemandCostFallPerKw", "peakDemandCostWinterPerKw"]}
        # print "RATES", rates
        monthNames = ["January", "February", "March", "April", "May", "June", "July", "August",
                      "September", "October", "November", "December"]
        monthToSeason = {'January': 'Winter', 'February': 'Winter', 'March': 'Spring', 'April': 'Spring',
                         'May': 'Spring', 'June': 'Summer', 'July': 'Summer', 'August': 'Summer',
                         'September': 'Fall', 'October': 'Fall', 'November': 'Fall', 'December': 'Winter'}
        monthData = []
        for i, x in enumerate(monthNames):
            monShort = x[0:3].lower()
            season = monthToSeason[x]
            histAvg = float(inputDict.get(monShort + "Avg", 0))
            histPeak = float(inputDict.get(monShort + "Peak", 0))
            monthData.append({"monthId": i, "monthName": x, "histAverage": histAvg,
                              "histPeak": histPeak, "season": season})
        # for row in monthData:
        # 	print row
        # Graph the SCADA data.
        fig = plt.figure(figsize=(10, 6))
        indices = [r['monthName'] for r in monthData]
        d1 = [r['histPeak'] / (10**3) for r in monthData]
        d2 = [r['histAverage'] / (10**3) for r in monthData]
        ticks = range(len(d1))
        bar_peak = plt.bar(ticks, d1, color='gray')
        bar_avg = plt.bar(ticks, d2, color='dimgray')
        plt.legend([bar_peak[0], bar_avg[0]], ['histPeak', 'histAverage'], bbox_to_anchor=(0., 1.015, 1., .102), loc=3,
                   ncol=2, mode="expand", borderaxespad=0.1)
        plt.xticks([t + 0.5 for t in ticks], indices)
        plt.ylabel('Mean and peak historical power consumptions (kW)')
        fig.autofmt_xdate()
        Plot.save_fig(plt, pJoin(modelDir, "scadaChart.png"))
        allOutput["histPeak"] = d1
        allOutput["histAverage"] = d2
        allOutput["monthName"] = [name[0:3] for name in monthNames]
        # Graph feeder.
        fig = plt.figure(figsize=(10, 10))
        myGraph = omf.feeder.treeToNxGraph(tree)
        omf.feeder.latLonNxGraph(myGraph, neatoLayout=False)
        Plot.save_fig(plt, pJoin(modelDir, "feederChart.png"))
        with open(pJoin(modelDir, "feederChart.png"), "rb") as inFile:
            allOutput["feederChart"] = inFile.read().encode("base64")
        # Get the load levels we need to test.
        allLoadLevels = [x.get(
            'histPeak', 0) for x in monthData] + [y.get('histAverage', 0) for y in monthData]
        maxLev = _roundOne(max(allLoadLevels), 'up')
        minLev = _roundOne(min(allLoadLevels), 'down')
        tenLoadLevels = range(
            int(minLev), int(maxLev), int((maxLev - minLev) / 10))
        # Gather variables from the feeder.
        for key in tree.keys():
            # Set clock to single timestep.
            if tree[key].get('clock', '') == 'clock':
                tree[key] = {"timezone": "PST+8PDT",
                             "stoptime": "'2013-01-01 00:00:00'",
                             "starttime": "'2013-01-01 00:00:00'",
                             "clock": "clock"}
            # Save swing node index.
            if tree[key].get('bustype', '').lower() == 'swing':
                swingIndex = key
                swingName = tree[key].get('name')
            # Remove all includes.
            if tree[key].get('omftype', '') == '#include':
                del key
        # Find the substation regulator and config.
        for key in tree:
            if tree[key].get('object', '') == 'regulator' and tree[key].get('from', '') == swingName:
                regIndex = key
                regConfName = tree[key]['configuration']
        for key in tree:
            if tree[key].get('name', '') == regConfName:
                regConfIndex = key
        # Set substation regulator to manual operation.
        # GLOBAL VARIABLE FOR DEFAULT TAP POSITION
        baselineTap = int(inputDict.get("baselineTap"))
        tree[regConfIndex] = {
            'name': tree[regConfIndex]['name'],
            'object': 'regulator_configuration',
            'connect_type': '1',
            'raise_taps': '10',
            'lower_taps': '10',
            'CT_phase': 'ABC',
            'PT_phase': 'ABC',
            # Yo, 0.10 means at tap_pos 10 we're 10% above 120V.
            'regulation': '0.10',
            'Control': 'MANUAL',
            'control_level': 'INDIVIDUAL',
            'Type': 'A',
            'tap_pos_A': str(baselineTap),
            'tap_pos_B': str(baselineTap),
            'tap_pos_C': str(baselineTap)}
        # Attach recorders relevant to CVR.
        recorders = [
            {'object': 'collector',
             'file': 'ZlossesTransformer.csv',
             'group': 'class=transformer',
             'limit': '0',
             'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'},
            {'object': 'collector',
             'file': 'ZlossesUnderground.csv',
             'group': 'class=underground_line',
             'limit': '0',
             'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'},
            {'object': 'collector',
             'file': 'ZlossesOverhead.csv',
             'group': 'class=overhead_line',
             'limit': '0',
             'property': 'sum(power_losses_A.real),sum(power_losses_A.imag),sum(power_losses_B.real),sum(power_losses_B.imag),sum(power_losses_C.real),sum(power_losses_C.imag)'},
            {'object': 'recorder',
             'file': 'Zregulator.csv',
             'limit': '0',
             'parent': tree[regIndex]['name'],
             'property': 'tap_A,tap_B,tap_C,power_in.real,power_in.imag'},
            {'object': 'collector',
             'file': 'ZvoltageJiggle.csv',
             'group': 'class=triplex_meter',
             'limit': '0',
             'property': 'min(voltage_12.mag),mean(voltage_12.mag),max(voltage_12.mag),std(voltage_12.mag)'},
            {'object': 'recorder',
             'file': 'ZsubstationTop.csv',
             'limit': '0',
             'parent': tree[swingIndex]['name'],
             'property': 'voltage_A,voltage_B,voltage_C'},
            {'object': 'recorder',
             'file': 'ZsubstationBottom.csv',
             'limit': '0',
             'parent': tree[regIndex]['to'],
             'property': 'voltage_A,voltage_B,voltage_C'}]
        biggest = 1 + max([int(k) for k in tree.keys()])
        for index, rec in enumerate(recorders):
            tree[biggest + index] = rec
        # Change constant PF loads to ZIP loads. (See evernote for rationale
        # about 50/50 power/impedance mix.)
        blankZipModel = {'object': 'triplex_load',
                         'name': 'NAMEVARIABLE',
                         'base_power_12': 'POWERVARIABLE',
                         'power_fraction_12': str(inputDict.get("p_percent")),
                         'impedance_fraction_12': str(inputDict.get("z_percent")),
                         'current_fraction_12': str(inputDict.get("i_percent")),
                         # MAYBEFIX: we can probably get this PF data from the
                         # Milsoft loads.
                         'power_pf_12': str(inputDict.get("power_factor")),
                         'impedance_pf_12': str(inputDict.get("power_factor")),
                         'current_pf_12': str(inputDict.get("power_factor")),
                         'nominal_voltage': '120',
                         'phases': 'PHASESVARIABLE',
                         'parent': 'PARENTVARIABLE'}

        def powerClean(powerStr):
            ''' take 3339.39+1052.29j to 3339.39 '''
            return powerStr[0:powerStr.find('+')]
        for key in tree:
            if tree[key].get('object', '') == 'triplex_node':
                # Get existing variables.
                name = tree[key].get('name', '')
                power = tree[key].get('power_12', '')
                parent = tree[key].get('parent', '')
                phases = tree[key].get('phases', '')
                # Replace object and reintroduce variables.
                tree[key] = copy(blankZipModel)
                tree[key]['name'] = name
                tree[key]['base_power_12'] = powerClean(power)
                tree[key]['parent'] = parent
                tree[key]['phases'] = phases
        # Function to determine how low we can tap down in the CVR case:

        def loweringPotential(baseLine):
            ''' Given a baseline end of line voltage, how many more percent can we shave off the substation voltage? '''
            ''' testsWePass = [122.0,118.0,200.0,110.0] '''
            lower = int(math.floor((baseLine / 114.0 - 1) * 100)) - 1
            # If lower is negative, we can't return it because we'd be
            # undervolting beyond what baseline already was!
            if lower < 0:
                return baselineTap
            else:
                return baselineTap - lower
        # Run all the powerflows.
        powerflows = []
        for doingCvr in [False, True]:
            # For each load level in the tenLoadLevels, run a powerflow with
            # the load objects scaled to the level.
            for desiredLoad in tenLoadLevels:
                # Find the total load that was defined in Milsoft:
                loadList = []
                for key in tree:
                    if tree[key].get('object', '') == 'triplex_load':
                        loadList.append(tree[key].get('base_power_12', ''))
                totalLoad = sum([float(x) for x in loadList])
                # Rescale each triplex load:
                for key in tree:
                    if tree[key].get('object', '') == 'triplex_load':
                        currentPow = float(tree[key]['base_power_12'])
                        ratio = desiredLoad / totalLoad
                        tree[key]['base_power_12'] = str(currentPow * ratio)
                # If we're doing CVR then lower the voltage.
                if doingCvr:
                    # Find the minimum voltage we can tap down to:
                    newTapPos = baselineTap
                    for row in powerflows:
                        if row.get('loadLevel', '') == desiredLoad:
                            newTapPos = loweringPotential(
                                row.get('lowVoltage', 114))
                    # Tap it down to there.
                    # MAYBEFIX: do each phase separately because that's how
                    # it's done in the field... Oof.
                    tree[regConfIndex]['tap_pos_A'] = str(newTapPos)
                    tree[regConfIndex]['tap_pos_B'] = str(newTapPos)
                    tree[regConfIndex]['tap_pos_C'] = str(newTapPos)
                # Run the model through gridlab and put outputs in the table.
                output = gridlabd.runInFilesystem(tree, attachments=attachments,
                                                  keepFiles=True, workDir=modelDir)
                os.remove(pJoin(modelDir, "PID.txt"))
                p = output['Zregulator.csv']['power_in.real'][0]
                q = output['Zregulator.csv']['power_in.imag'][0]
                s = math.sqrt(p**2 + q**2)
                lossTotal = 0.0
                for device in ['ZlossesOverhead.csv', 'ZlossesTransformer.csv', 'ZlossesUnderground.csv']:
                    for letter in ['A', 'B', 'C']:
                        r = output[device][
                            'sum(power_losses_' + letter + '.real)'][0]
                        i = output[device][
                            'sum(power_losses_' + letter + '.imag)'][0]
                        lossTotal += math.sqrt(r**2 + i**2)
                # Entire output:
                powerflows.append({
                    'doingCvr': doingCvr,
                    'loadLevel': desiredLoad,
                    'realPower': p,
                    'powerFactor': p / s,
                    'losses': lossTotal,
                    'subVoltage': (
                        output['ZsubstationBottom.csv']['voltage_A'][0] +
                        output['ZsubstationBottom.csv']['voltage_B'][0] +
                        output['ZsubstationBottom.csv']['voltage_C'][0]) / 3 / 60,
                    'lowVoltage': output['ZvoltageJiggle.csv']['min(voltage_12.mag)'][0] / 2,
                    'highVoltage': output['ZvoltageJiggle.csv']['max(voltage_12.mag)'][0] / 2})
        # For a given load level, find two points to interpolate on.

        def getInterpPoints(t):
            ''' Find the two points we can interpolate from. '''
            ''' tests pass on [tenLoadLevels[0],tenLoadLevels[5]+499,tenLoadLevels[-1]-988] '''
            loc = sorted(tenLoadLevels + [t]).index(t)
            if loc == 0:
                return (tenLoadLevels[0], tenLoadLevels[1])
            elif loc > len(tenLoadLevels) - 2:
                return (tenLoadLevels[-2], tenLoadLevels[-1])
            else:
                return (tenLoadLevels[loc - 1], tenLoadLevels[loc + 1])
        # Calculate peak reduction.
        for row in monthData:
            peak = row['histPeak']
            peakPoints = getInterpPoints(peak)
            peakTopBase = [x for x in powerflows if x.get(
                'loadLevel', '') == peakPoints[-1] and x.get('doingCvr', '') == False][0]
            peakTopCvr = [x for x in powerflows if x.get(
                'loadLevel', '') == peakPoints[-1] and x.get('doingCvr', '') == True][0]
            peakBottomBase = [x for x in powerflows if x.get(
                'loadLevel', '') == peakPoints[0] and x.get('doingCvr', '') == False][0]
            peakBottomCvr = [x for x in powerflows if x.get(
                'loadLevel', '') == peakPoints[0] and x.get('doingCvr', '') == True][0]
            # Linear interpolation so we aren't running umpteen million
            # loadflows.
            x = (peakPoints[0], peakPoints[1])
            y = (peakTopBase['realPower'] - peakTopCvr['realPower'],
                 peakBottomBase['realPower'] - peakBottomCvr['realPower'])
            peakRed = y[0] + (y[1] - y[0]) * (peak - x[0]) / (x[1] - x[0])
            row['peakReduction'] = peakRed
        # Calculate energy reduction and loss reduction based on average load.
        for row in monthData:
            avgEnergy = row['histAverage']
            energyPoints = getInterpPoints(avgEnergy)
            avgTopBase = [x for x in powerflows if x.get(
                'loadLevel', '') == energyPoints[-1] and x.get('doingCvr', '') == False][0]
            avgTopCvr = [x for x in powerflows if x.get(
                'loadLevel', '') == energyPoints[-1] and x.get('doingCvr', '') == True][0]
            avgBottomBase = [x for x in powerflows if x.get(
                'loadLevel', '') == energyPoints[0] and x.get('doingCvr', '') == False][0]
            avgBottomCvr = [x for x in powerflows if x.get(
                'loadLevel', '') == energyPoints[0] and x.get('doingCvr', '') == True][0]
            # Linear interpolation so we aren't running umpteen million
            # loadflows.
            x = (energyPoints[0], energyPoints[1])
            y = (avgTopBase['realPower'] - avgTopCvr['realPower'],
                 avgBottomBase['realPower'] - avgBottomCvr['realPower'])
            energyRed = y[0] + \
                (y[1] - y[0]) * (avgEnergy - x[0]) / (x[1] - x[0])
            row['energyReduction'] = energyRed
            lossY = (avgTopBase['losses'] - avgTopCvr['losses'],
                     avgBottomBase['losses'] - avgBottomCvr['losses'])
            lossRed = lossY[0] + (lossY[1] - lossY[0]) * \
                (avgEnergy - x[0]) / (x[1] - x[0])
            row['lossReduction'] = lossRed
        # Multiply by dollars.
        for row in monthData:
            row['energyReductionDollars'] = row['energyReduction'] / 1000 * \
                (rates['wholesaleEnergyCostPerKwh'] -
                 rates['retailEnergyCostPerKwh'])
            row['peakReductionDollars'] = row['peakReduction'] / \
                1000 * rates['peakDemandCost' + row['season'] + 'PerKw']
            row['lossReductionDollars'] = row['lossReduction'] / \
                1000 * rates['wholesaleEnergyCostPerKwh']
        # Pretty output

        def plotTable(inData):
            fig = plt.figure(figsize=(10, 5))
            plt.axis('off')
            plt.tight_layout()
            plt.table(cellText=[row for row in inData[1:]],
                      loc='center',
                      rowLabels=range(len(inData) - 1),
                      colLabels=inData[0])

        def dictalToMatrix(dictList):
            ''' Take our dictal format to a matrix. '''
            matrix = [dictList[0].keys()]
            for row in dictList:
                matrix.append(row.values())
            return matrix
        # Powerflow results.
        plotTable(dictalToMatrix(powerflows))
        Plot.save_fig(plt, pJoin(modelDir, "powerflowTable.png"))
        # Monetary results.
        # To print partial money table
        monthDataMat = dictalToMatrix(monthData)
        dimX = len(monthDataMat)
        dimY = len(monthDataMat[0])
        monthDataPart = []
        for k in range(0, dimX):
            monthDatatemp = []
            for m in range(4, dimY):
                monthDatatemp.append(monthDataMat[k][m])
            monthDataPart.append(monthDatatemp)

        plotTable(monthDataPart)
        Plot.save_fig(plt, pJoin(modelDir, "moneyTable.png"))
        allOutput["monthDataMat"] = dictalToMatrix(monthData)
        allOutput["monthDataPart"] = monthDataPart
        # Graph the money data.
        fig = plt.figure(figsize=(10, 8))
        indices = [r['monthName'] for r in monthData]
        d1 = [r['energyReductionDollars'] for r in monthData]
        d2 = [r['lossReductionDollars'] for r in monthData]
        d3 = [r['peakReductionDollars'] for r in monthData]
        ticks = range(len(d1))
        bar_erd = plt.bar(ticks, d1, color='red')
        bar_lrd = plt.bar(ticks, d2, color='green')
        bar_prd = plt.bar(ticks, d3, color='blue', yerr=d2)
        plt.legend([bar_prd[0], bar_lrd[0], bar_erd[0]], ['peakReductionDollars', 'lossReductionDollars', 'energyReductionDollars'], bbox_to_anchor=(0., 1.015, 1., .102), loc=3,
                   ncol=2, mode="expand", borderaxespad=0.1)
        plt.xticks([t + 0.5 for t in ticks], indices)
        plt.ylabel('Utility Savings ($)')
        plt.tight_layout(5.5, 1.3, 1.2)
        fig.autofmt_xdate()
        Plot.save_fig(plt, pJoin(modelDir, "spendChart.png"))
        allOutput["energyReductionDollars"] = d1
        allOutput["lossReductionDollars"] = d2
        allOutput["peakReductionDollars"] = d3
        # Graph the cumulative savings.
        fig = plt.figure(figsize=(10, 5))
        annualSavings = sum(d1) + sum(d2) + sum(d3)
        annualSave = lambda x: (
            annualSavings - rates['omCost']) * x - rates['capitalCost']
        simplePayback = rates['capitalCost'] / \
            (annualSavings - rates['omCost'])
        plt.xlabel('Year After Installation')
        plt.xlim(0, 30)
        plt.ylabel('Cumulative Savings ($)')
        plt.plot([0 for x in range(31)], c='gray')
        plt.axvline(x=simplePayback, ymin=0, ymax=1, c='gray', linestyle='--')
        plt.plot([annualSave(x) for x in range(31)], c='green')
        Plot.save_fig(plt, pJoin(modelDir, "savingsChart.png"))
        allOutput["annualSave"] = [annualSave(x) for x in range(31)]
        # Update the runTime in the input file.
        endTime = datetime.datetime.now()
        inputDict["runTime"] = str(
            datetime.timedelta(seconds=int((endTime - startTime).total_seconds())))
        fs.save(pJoin(modelDir, "allInputData.json"), json.dumps(inputDict, indent=4))
        # Write output file.
        fs.save(pJoin(modelDir, "allOutputData.json"), json.dumps(allOutput, indent=4))
        # For autotest, there won't be such file.
        try:
            os.remove(pJoin(modelDir, "PPID.txt"))
        except:
            pass
        print "DONE RUNNING", modelDir
    except Exception as e:
        print "Oops, Model Crashed!!!"
        print e
        cancel(modelDir)
Beispiel #3
0
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
Beispiel #4
0
 outData['demand'] = [t['power'] * 1000.0 for t in dc]
 outData['demandAfterBattery'] = [t['netpower'] * 1000.0 for t in dc]
 # outData['batterySoc'] = [t['battSoC']/battCapacity*100.0 for t in dc]
 outData['batterySoc'] = [t['battSoC'] / battCapacity *
                          100.0 * dodFactor + (100 - 100 * dodFactor) for t in dc]
 # Estimate number of cyles the battery went through.
 SoC = outData['batterySoc']
 outData['cycleEquivalents'] = sum(
     [SoC[i] - SoC[i + 1] for i, x in enumerate(SoC[0:-1]) if SoC[i + 1] < SoC[i]]) / 100.0
 # Output some matplotlib results as well.
 plt.plot([t['power'] for t in dc])
 plt.plot([t['netpower'] for t in dc])
 plt.plot([t['battSoC'] for t in dc])
 for month in range(12):
     plt.axvline(hoursThroughTheMonth[month])
 Plot.save_fig(plt, pJoin(modelDir, "plot.png"))
 # DRDAN: Summary of results
 outData['months'] = ["Jan", "Feb", "Mar", "Apr", "May",
                      "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
 totMonNum = []
 monthlyDemand = []
 for x in range(0, len(dcGroupByMonth)):
     totMonNum.append(sum(dcGroupByMonth[x]) / 1000)
     monthlyDemand.append([outData['months'][x], totMonNum[x]])
 outData['monthlyDemand'] = totMonNum
 outData['ps'] = ps		# TODO: [Battery Capacity Left]
 outData['monthlyDemandRed'] = [
     totMonNum - ps for totMonNum, ps in zip(totMonNum, ps)]
 outData['benefitMonthly'] = [x * demandCharge for x in outData['ps']]
 outData['kWhtoRecharge'] = [battCapacity - x for x in outData['ps']]
 outData['costtoRecharge'] = [
Beispiel #5
0
def omfCalibrate(workDir, feederPath, scadaPath):
    '''calibrates a feeder and saves the calibrated tree at a location'''
    logger.info('Calibrating feeder... work dir: %s; feeder path: %s; scada path: %s', workDir, feederPath, scadaPath)
    with open(feederPath, "r") as jsonIn:
        feederJson = json.load(jsonIn)
        tree = feederJson.get("tree", {})
    scadaSubPower, firstDateTime = _processScadaData(workDir, scadaPath)
    # Force FBS powerflow, because NR fails a lot.
    for key in tree:
        if tree[key].get("module", "").lower() == "powerflow":
            tree[key] = {"module": "powerflow", "solver_method": "FBS"}
    # Attach player.
    classOb = {"class": "player", "variable_names": [
        "value"], "variable_types": ["double"]}
    playerOb = {"object": "player", "property": "value",
                "name": "scadaLoads", "file": "subScada.player", "loop": "0"}
    maxKey = omf.feeder.getMaxKey(tree)
    tree[maxKey + 1] = classOb
    tree[maxKey + 2] = playerOb
    # Make loads reference player.
    loadTemplate = {"object": "triplex_load",
                    "power_pf_12": "0.95",
                    "impedance_pf_12": "0.98",
                    "power_pf_12": "0.90",
                    "impedance_fraction_12": "0.7",
                    "power_fraction_12": "0.3"}
    for key in tree:
        ob = tree[key]
        if ob.get("object", "") == "triplex_node" and ob.get("power_12", "") != "":
            newOb = dict(loadTemplate)
            newOb["name"] = ob.get("name", "")
            newOb["parent"] = ob.get("parent", "")
            newOb["phases"] = ob.get("phases", "")
            newOb["nominal_voltage"] = ob.get("nominal_voltage", "")
            newOb["latitude"] = ob.get("latitude", "0")
            newOb["longitude"] = ob.get("longitude", "0")
            oldPow = ob.get("power_12", "").replace("j", "d")
            pythagPower = gridlabd._strClean(oldPow)
            newOb["base_power_12"] = "scadaLoads.value*" + str(pythagPower)
            tree[key] = newOb
    # Search for the substation regulator and attach a recorder there.
    for key in tree:
        if tree[key].get('bustype', '').lower() == 'swing':
            swingName = tree[key].get('name')
    for key in tree:
        if tree[key].get('object', '') in ['regulator', 'overhead_line', 'underground_line', 'transformer', 'fuse'] and tree[key].get('from', '') == swingName:
            SUB_REG_NAME = tree[key]['name']
    recOb = {"object": "recorder",
             "parent": SUB_REG_NAME,
             "property": "power_in.real,power_in.imag",
             "file": "caliSub.csv",
             "interval": "900"}
    tree[maxKey + 3] = recOb
    HOURS = 100
    omf.feeder.adjustTime(tree, HOURS, "hours", firstDateTime.strftime("%Y-%m-%d"))
    # Run Gridlabd.
    output = gridlabd.runInFilesystem(tree, keepFiles=True, workDir=workDir)
    # Calculate scaling constant.
    outRealPow = output["caliSub.csv"]["power_in.real"]
    outImagPower = output["caliSub.csv"]["power_in.imag"]
    outAppPowerKw = [
        (x[0]**2 + x[1]**2)**0.5 / 1000 for x in zip(outRealPow, outImagPower)]
    # HACK: ignore first time step in output and input because GLD sometimes
    # breaks the first step.
    SCAL_CONST = sum(scadaSubPower[1:HOURS]) / sum(outAppPowerKw[1:HOURS])
    # Rewrite the subScada.player file so all the power values are multiplied
    # by the SCAL_CONSTANT.
    newPlayData = []
    with open(pJoin(workDir, "subScada.player"), "r") as playerFile:
        for line in playerFile:
            (key, val) = line.split(',')
            newPlayData.append(
                str(key) + ',' + str(float(val) * SCAL_CONST) + "\n")
    with open(pJoin(workDir, "subScadaCalibrated.player"), "w") as playerFile:
        for row in newPlayData:
            playerFile.write(row)
    # Test by running a glm with subScadaCalibrated.player and caliSub.csv2.
    tree[maxKey + 2]["file"] = "subScadaCalibrated.player"
    tree[maxKey + 3]["file"] = "caliSubCheck.csv"
    secondOutput = gridlabd.runInFilesystem(
        tree, keepFiles=True, workDir=workDir)
    plt.figure()
    plt.plot(outAppPowerKw[1:HOURS], label="initialGuess")
    plt.plot(scadaSubPower[1:HOURS], label="scadaSubPower")
    secondAppKw = [(x[0]**2 + x[1]**2)**0.5 / 1000
                   for x in zip(secondOutput["caliSubCheck.csv"]["power_in.real"], secondOutput["caliSubCheck.csv"]["power_in.imag"])]
    plt.plot(secondAppKw[1:HOURS], label="finalGuess")
    plt.legend(loc=3)
    Plot.save_fig(plt, pJoin(workDir, "caliCheckPlot.png"))
    # Write the final output.
    with open(pJoin(workDir, "calibratedFeeder.json"), "w") as outJson:
        playerString = open(pJoin(workDir, "subScadaCalibrated.player")).read()
        feederJson["attachments"]["subScadaCalibrated.player"] = playerString
        feederJson["tree"] = tree
        json.dump(feederJson, outJson, indent=4)
    return