Exemple #1
0
def work(modelDir, inputDict):
	''' Run the model in its directory. '''
	# Copy spcific climate data into model directory
	inputDict["climateName"] = weather.zipCodeToClimateName(inputDict["zipCode"])
	shutil.copy(pJoin(__neoMetaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"),
		pJoin(modelDir, "climate.tmy2"))
	# Set up SAM data structures.
	ssc = nrelsam2013.SSCAPI()
	dat = ssc.ssc_data_create()
	# Required user inputs.
	ssc.ssc_data_set_string(dat, b'file_name', bytes(modelDir + '/climate.tmy2', 'ascii'))
	ssc.ssc_data_set_number(dat, b'system_size', float(inputDict['SystemSize']))
	# SAM options where we take defaults.
	ssc.ssc_data_set_number(dat, b'derate', 0.97)
	ssc.ssc_data_set_number(dat, b'track_mode', 0)
	ssc.ssc_data_set_number(dat, b'azimuth', 180)
	ssc.ssc_data_set_number(dat, b'tilt_eq_lat', 1)
	# Run PV system simulation.
	mod = ssc.ssc_module_create(b'pvwattsv1')
	ssc.ssc_module_exec(mod, dat)
	# Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
	startDateTime = "2013-01-01 00:00:00 UTC"
	# Timestamp output.
	outData = {}
	outData['timeStamps'] = [
		(datetime.datetime.strptime(startDateTime[0:19], '%Y-%m-%d %H:%M:%S') + datetime.timedelta(**{'hours':x})).strftime('%Y-%m-%d %H:%M:%S') + ' UTC'
		for x in range(8760)
	]
	# HACK: makes it easier to calculate some things later.
	outData["pythonTimeStamps"] = [datetime.datetime(2012,1,1,0) + x * datetime.timedelta(hours=1) for x in range(8760)]

	# Geodata output.
	outData['city'] = ssc.ssc_data_get_string(dat, b'city').decode()
	outData['state'] = ssc.ssc_data_get_string(dat, b'state').decode()
	outData['lat'] = ssc.ssc_data_get_number(dat, b'lat')
	outData['lon'] = ssc.ssc_data_get_number(dat, b'lon')
	outData['elev'] = ssc.ssc_data_get_number(dat, b'elev')
	# Weather output.
	outData["climate"] = {}
	outData['climate']['Global Horizontal Radiation (W/m^2)'] = ssc.ssc_data_get_array(dat, b'gh')
	outData['climate']['Plane of Array Irradiance (W/m^2)'] = ssc.ssc_data_get_array(dat, b'poa')
	outData['climate']['Ambient Temperature (F)'] = ssc.ssc_data_get_array(dat, b'tamb')
	outData['climate']['Cell Temperature (F)'] = ssc.ssc_data_get_array(dat, b'tcell')
	outData['climate']['Wind Speed (m/s)'] = ssc.ssc_data_get_array(dat, b'wspd')
	# Power generation.
	outData['powerOutputAc'] = ssc.ssc_data_get_array(dat, b'ac')

	# TODO: INSERT TJ CODE BELOW
	tjCode(inputDict, outData)
	del outData["pythonTimeStamps"]
	# TODO: INSERT TJ CODE ABOVE

	# Stdout/stderr.
	outData["stdout"] = "Success"
	outData["stderr"] = ""
	return outData
Exemple #2
0
def work(modelDir, inputDict):
	''' Run the model in its directory. '''
	# Copy spcific climate data into model directory
	inputDict["climateName"], latforpvwatts = zipCodeToClimateName(inputDict["zipCode"])
	shutil.copy(pJoin(__neoMetaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"),
		pJoin(modelDir, "climate.tmy2"))
	# Set up SAM data structures.
	ssc = nrelsam2013.SSCAPI()
	dat = ssc.ssc_data_create()
	# Required user inputs.
	ssc.ssc_data_set_string(dat, "file_name", modelDir + "/climate.tmy2")
	ssc.ssc_data_set_number(dat, "system_size", float(inputDict["SystemSize"]))
	# SAM options where we take defaults.
	ssc.ssc_data_set_number(dat, "derate", 0.97)
	ssc.ssc_data_set_number(dat, "track_mode", 0)
	ssc.ssc_data_set_number(dat, "azimuth", 180)
	ssc.ssc_data_set_number(dat, "tilt_eq_lat", 1)
	# Run PV system simulation.
	mod = ssc.ssc_module_create("pvwattsv1")
	ssc.ssc_module_exec(mod, dat)
	# Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
	startDateTime = "2013-01-01 00:00:00 UTC"
	# Timestamp output.
	outData = {}
	outData["timeStamps"] = [dt.strftime(
		dt.strptime(startDateTime[0:19],"%Y-%m-%d %H:%M:%S") +
		td(**{"hours":x}),"%Y-%m-%d %H:%M:%S") + " UTC" for x in range(int(8760))]
	# HACK: makes it easier to calculate some things later.
	outData["pythonTimeStamps"] = [dt(2012,1,1,0) + x*td(hours=1) for x in range(8760)]
	# Geodata output.
	outData["city"] = ssc.ssc_data_get_string(dat, "city")
	outData["state"] = ssc.ssc_data_get_string(dat, "state")
	outData["lat"] = ssc.ssc_data_get_number(dat, "lat")
	outData["lon"] = ssc.ssc_data_get_number(dat, "lon")
	outData["elev"] = ssc.ssc_data_get_number(dat, "elev")
	# Weather output.
	outData["climate"] = {}
	outData["climate"]["Global Horizontal Radiation (W/m^2)"] = ssc.ssc_data_get_array(dat, "gh")
	outData["climate"]["Plane of Array Irradiance (W/m^2)"] = ssc.ssc_data_get_array(dat, "poa")
	outData["climate"]["Ambient Temperature (F)"] = ssc.ssc_data_get_array(dat, "tamb")
	outData["climate"]["Cell Temperature (F)"] = ssc.ssc_data_get_array(dat, "tcell")
	outData["climate"]["Wind Speed (m/s)"] = ssc.ssc_data_get_array(dat, "wspd")
	# Power generation.
	outData["powerOutputAc"] = ssc.ssc_data_get_array(dat, "ac")

	# TODO: INSERT TJ CODE BELOW
	tjCode(inputDict, outData)
	del outData["pythonTimeStamps"]
	# TODO: INSERT TJ CODE ABOVE

	# Stdout/stderr.
	outData["stdout"] = "Success"
	outData["stderr"] = ""
	return outData
Exemple #3
0
def work(modelDir, inputDict):
    ''' Run the model in its directory. '''
    # Copy spcific climate data into model directory
    inputDict["climateName"], latforpvwatts = zipCodeToClimateName(
        inputDict["zipCode"])
    shutil.copy(
        pJoin(__neoMetaModel__._omfDir, "data", "Climate",
              inputDict["climateName"] + ".tmy2"),
        pJoin(modelDir, "climate.tmy2"))
    # Set up SAM data structures.
    ssc = nrelsam2013.SSCAPI()
    dat = ssc.ssc_data_create()
    # Required user inputs.
    ssc.ssc_data_set_string(dat, "file_name", modelDir + "/climate.tmy2")
    ssc.ssc_data_set_number(dat, "system_size",
                            float(inputDict.get("systemSize", 100)))
    derate = float(inputDict.get("pvModuleDerate", 99.5))/100 \
     * float(inputDict.get("mismatch", 99.5))/100 \
     * float(inputDict.get("diodes", 99.5))/100 \
     * float(inputDict.get("dcWiring", 99.5))/100 \
     * float(inputDict.get("acWiring", 99.5))/100 \
     * float(inputDict.get("soiling", 99.5))/100 \
     * float(inputDict.get("shading", 99.5))/100 \
     * float(inputDict.get("sysAvail", 99.5))/100 \
     * float(inputDict.get("age", 99.5))/100 \
     * float(inputDict.get("inverterEfficiency", 92))/100
    ssc.ssc_data_set_number(dat, "derate", derate)
    # TODO: Should we move inverter efficiency to 'inv_eff' below (as done in PVWatts?)
    # Doesn't seem to affect output very much
    # ssc.ssc_data_set_number(dat, "inv_eff", float(inputDict.get("inverterEfficiency", 92))/100)
    ssc.ssc_data_set_number(dat, "track_mode",
                            float(inputDict.get("trackingMode", 0)))
    ssc.ssc_data_set_number(dat, "azimuth",
                            float(inputDict.get("azimuth", 180)))
    # Advanced inputs with defaults.
    ssc.ssc_data_set_number(dat, "rotlim", float(inputDict.get("rotlim", 45)))
    ssc.ssc_data_set_number(dat, "gamma",
                            float(inputDict.get("gamma", 0.5)) / 100)
    # Complicated optional inputs.
    if (inputDict.get("tilt", 0) == "-"):
        tilt_eq_lat = 1.0
        manualTilt = 0.0
    else:
        tilt_eq_lat = 0.0
        manualTilt = float(inputDict.get("tilt", 0))
    ssc.ssc_data_set_number(dat, "tilt", manualTilt)
    ssc.ssc_data_set_number(dat, "tilt_eq_lat", tilt_eq_lat)
    # Run PV system simulation.
    mod = ssc.ssc_module_create("pvwattsv1")
    ssc.ssc_module_exec(mod, dat)
    # Setting options for start time.
    simLengthUnits = inputDict.get("simLengthUnits", "hours")
    simStartDate = inputDict.get("simStartDate", "2014-01-01")
    # Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
    startDateTime = simStartDate + " 00:00:00 UTC"
    # Timestamp output.
    outData = {}
    outData["timeStamps"] = [
        dt.datetime.strftime(
            dt.datetime.strptime(startDateTime[0:19], "%Y-%m-%d %H:%M:%S") +
            dt.timedelta(**{simLengthUnits: x}), "%Y-%m-%d %H:%M:%S") + " UTC"
        for x in range(int(inputDict.get("simLength", 8760)))
    ]
    # Geodata output.
    outData["city"] = ssc.ssc_data_get_string(dat, "city")
    outData["state"] = ssc.ssc_data_get_string(dat, "state")
    outData["lat"] = ssc.ssc_data_get_number(dat, "lat")
    outData["lon"] = ssc.ssc_data_get_number(dat, "lon")
    outData["elev"] = ssc.ssc_data_get_number(dat, "elev")
    # Weather output.
    outData["climate"] = {}
    outData["climate"][
        "Global Horizontal Radiation (W/m^2)"] = ssc.ssc_data_get_array(
            dat, "gh")
    outData["climate"][
        "Plane of Array Irradiance (W/m^2)"] = ssc.ssc_data_get_array(
            dat, "poa")
    outData["climate"]["Ambient Temperature (F)"] = ssc.ssc_data_get_array(
        dat, "tamb")
    outData["climate"]["Cell Temperature (F)"] = ssc.ssc_data_get_array(
        dat, "tcell")
    outData["climate"]["Wind Speed (m/s)"] = ssc.ssc_data_get_array(
        dat, "wspd")
    # Power generation and clipping.
    outData["powerOutputAc"] = ssc.ssc_data_get_array(dat, "ac")
    invSizeWatts = float(inputDict.get("inverterSize", 0)) * 1000
    outData["InvClipped"] = [
        x if x < invSizeWatts else invSizeWatts
        for x in outData["powerOutputAc"]
    ]
    try:
        outData["percentClipped"] = 100 * (
            1.0 - sum(outData["InvClipped"]) / sum(outData["powerOutputAc"]))
    except ZeroDivisionError:
        outData["percentClipped"] = 0.0
    # Cashflow outputs.
    lifeSpan = int(inputDict.get("lifeSpan", 30))
    lifeYears = range(1, 1 + lifeSpan)
    retailCost = float(inputDict.get("retailCost", 0.0))
    degradation = float(inputDict.get("degradation", 0.5)) / 100
    installCost = float(inputDict.get("installCost", 0.0))
    discountRate = float(inputDict.get("discountRate", 7)) / 100
    outData["oneYearGenerationWh"] = sum(outData["powerOutputAc"])
    outData["lifeGenerationDollars"] = [
        retailCost * (1.0 / 1000) * outData["oneYearGenerationWh"] *
        (1.0 - (x * degradation)) for x in lifeYears
    ]
    outData["lifeOmCosts"] = [
        -1.0 * float(inputDict["omCost"]) for x in lifeYears
    ]
    outData["lifePurchaseCosts"] = [-1.0 * installCost
                                    ] + [0 for x in lifeYears[1:]]
    srec = inputDict.get("srecCashFlow", "").split(",")
    outData["srecCashFlow"] = map(float,
                                  srec) + [0 for x in lifeYears[len(srec):]]
    outData["netCashFlow"] = [
        x + y + z + a for (
            x, y, z,
            a) in zip(outData["lifeGenerationDollars"], outData["lifeOmCosts"],
                      outData["lifePurchaseCosts"], outData["srecCashFlow"])
    ]
    outData["cumCashFlow"] = map(lambda x: x,
                                 _runningSum(outData["netCashFlow"]))
    outData["ROI"] = roundSig(sum(outData["netCashFlow"]), 3) / (
        -1 * roundSig(sum(outData["lifeOmCosts"]), 3) +
        -1 * roundSig(sum(outData["lifePurchaseCosts"], 3)))
    outData["NPV"] = roundSig(npv(discountRate, outData["netCashFlow"]), 3)
    outData["lifeGenerationWh"] = sum(outData["powerOutputAc"]) * lifeSpan
    outData["lifeEnergySales"] = sum(outData["lifeGenerationDollars"])
    try:
        # The IRR function is very bad.
        outData["IRR"] = roundSig(irr(outData["netCashFlow"]), 3)
    except:
        outData["IRR"] = "Undefined"
    # Monthly aggregation outputs.
    months = {
        "Jan": 0,
        "Feb": 1,
        "Mar": 2,
        "Apr": 3,
        "May": 4,
        "Jun": 5,
        "Jul": 6,
        "Aug": 7,
        "Sep": 8,
        "Oct": 9,
        "Nov": 10,
        "Dec": 11
    }
    totMonNum = lambda x: sum([
        z for (y, z) in zip(outData["timeStamps"], outData["powerOutputAc"])
        if y.startswith(simStartDate[0:4] + "-{0:02d}".format(x + 1))
    ])
    outData["monthlyGeneration"] = [[
        a, totMonNum(b)
    ] for (a, b) in sorted(months.items(), key=lambda x: x[1])]
    # Heatmaped hour+month outputs.
    hours = range(24)
    from calendar import monthrange
    totHourMon = lambda h, m: sum([
        z for (y, z) in zip(outData["timeStamps"], outData["powerOutputAc"])
        if y[5:7] == "{0:02d}".format(m + 1) and y[11:13] == "{0:02d}".format(
            h + 1)
    ])
    outData["seasonalPerformance"] = [[
        x, y,
        totHourMon(x, y) / monthrange(int(simStartDate[:4]), y + 1)[1]
    ] for x in hours for y in months.values()]
    # Stdout/stderr.
    outData["stdout"] = "Success"
    outData["stderr"] = ""
    return outData
Exemple #4
0
 rotlim = 45.0
 gamma = 0.45
 if (inputDict.get("tilt", 0) == "-"):
     manualTilt = latforpvwatts
 else:
     manualTilt = float(inputDict.get("tilt", 0))
 numberInverters = math.ceil(inverterSizeAC / 1000 / 0.5)
 # Copy specific climate data into model directory
 shutil.copy(
     pJoin(__metaModel__._omfDir, "data", "Climate",
           inputDict["climateName"] + ".tmy2"),
     pJoin(modelDir, "climate.tmy2"))
 # Ready to run
 startTime = dt.datetime.now()
 # Set up SAM data structures.
 ssc = nrelsam2013.SSCAPI()
 dat = ssc.ssc_data_create()
 # Required user inputs.
 ssc.ssc_data_set_string(dat, "file_name", modelDir + "/climate.tmy2")
 ssc.ssc_data_set_number(dat, "system_size", arraySizeDC)
 ssc.ssc_data_set_number(
     dat, "derate",
     float(inputDict.get("inverterEfficiency", 96)) / 100 *
     float(inputDict.get("nonInverterEfficiency", 87)) / 100)
 ssc.ssc_data_set_number(dat, "track_mode", float(trackingMode))
 ssc.ssc_data_set_number(dat, "azimuth",
                         float(inputDict.get("azimuth", 180)))
 # Advanced inputs with defaults.
 ssc.ssc_data_set_number(dat, "rotlim", float(rotlim))
 ssc.ssc_data_set_number(dat, "gamma", float(-gamma / 100))
 ssc.ssc_data_set_number(dat, "tilt", manualTilt)
Exemple #5
0
def work(modelDir, inputDict):
    ''' Run the model in its directory. '''
    #Set static input data
    simLength = 8760
    simStartDate = "2013-01-01"
    # Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
    startDateTime = simStartDate + " 00:00:00 UTC"
    simLengthUnits = "hours"
    # Associate zipcode to climate data
    inputDict["climateName"], latforpvwatts = zipCodeToClimateName(
        inputDict["zipCode"])
    inverterSizeAC = float(inputDict.get("inverterSize", 0))
    if (inputDict.get("systemSize", 0) == "-"):
        arraySizeDC = 1.3908 * inverterSizeAC
    else:
        arraySizeDC = float(inputDict.get("systemSize", 0))
    numberPanels = (arraySizeDC * 1000 / 305)
    # Set constants
    panelSize = 305
    trackingMode = 0
    rotlim = 45.0
    gamma = 0.45
    if (inputDict.get("tilt", 0) == "-"):
        manualTilt = latforpvwatts
    else:
        manualTilt = float(inputDict.get("tilt", 0))
    numberInverters = math.ceil(inverterSizeAC / 1000 / 0.5)
    # Copy specific climate data into model directory
    shutil.copy(
        pJoin(__neoMetaModel__._omfDir, "data", "Climate",
              inputDict["climateName"] + ".tmy2"),
        pJoin(modelDir, "climate.tmy2"))
    # Set up SAM data structures.
    ssc = nrelsam2013.SSCAPI()
    dat = ssc.ssc_data_create()
    # Required user inputs.
    ssc.ssc_data_set_string(dat, "file_name", modelDir + "/climate.tmy2")
    ssc.ssc_data_set_number(dat, "system_size", arraySizeDC)
    ssc.ssc_data_set_number(
        dat, "derate",
        float(inputDict.get("inverterEfficiency", 96)) / 100 *
        float(inputDict.get("nonInverterEfficiency", 87)) / 100)
    ssc.ssc_data_set_number(dat, "track_mode", float(trackingMode))
    ssc.ssc_data_set_number(dat, "azimuth",
                            float(inputDict.get("azimuth", 180)))
    # Advanced inputs with defaults.
    ssc.ssc_data_set_number(dat, "rotlim", float(rotlim))
    ssc.ssc_data_set_number(dat, "gamma", float(-gamma / 100))
    ssc.ssc_data_set_number(dat, "tilt", manualTilt)
    ssc.ssc_data_set_number(dat, "tilt_eq_lat", 0.0)
    # Run PV system simulation.
    mod = ssc.ssc_module_create("pvwattsv1")
    ssc.ssc_module_exec(mod, dat)
    # Timestamp output.
    outData = {}
    outData["timeStamps"] = [
        dt.datetime.strftime(
            dt.datetime.strptime(startDateTime[0:19], "%Y-%m-%d %H:%M:%S") +
            dt.timedelta(**{simLengthUnits: x}), "%Y-%m-%d %H:%M:%S") + " UTC"
        for x in range(simLength)
    ]
    # Geodata output.
    # Geodata output.
    outData["minLandSize"] = round(
        (arraySizeDC / 1390.8 * 5 + 1) * math.cos(math.radians(22.5)) /
        math.cos(math.radians(latforpvwatts)), 0)
    landAmount = float(inputDict.get("landAmount", 6.0))
    outData["city"] = ssc.ssc_data_get_string(dat, "city")
    outData["state"] = ssc.ssc_data_get_string(dat, "state")
    outData["lat"] = ssc.ssc_data_get_number(dat, "lat")
    outData["lon"] = ssc.ssc_data_get_number(dat, "lon")
    outData["elev"] = ssc.ssc_data_get_number(dat, "elev")
    # Weather output.
    outData["climate"] = {}
    outData["climate"][
        "Global Horizontal Radiation (W/m^2)"] = ssc.ssc_data_get_array(
            dat, "gh")
    outData["climate"][
        "Plane of Array Irradiance (W/m^2)"] = ssc.ssc_data_get_array(
            dat, "poa")
    outData["climate"]["Ambient Temperature (F)"] = ssc.ssc_data_get_array(
        dat, "tamb")
    outData["climate"]["Cell Temperature (F)"] = ssc.ssc_data_get_array(
        dat, "tcell")
    outData["climate"]["Wind Speed (m/s)"] = ssc.ssc_data_get_array(
        dat, "wspd")
    # Power generation.
    outData["powerOutputAc"] = ssc.ssc_data_get_array(dat, "ac")
    # Calculate clipping.
    outData["powerOutputAc"] = ssc.ssc_data_get_array(dat, "ac")
    invSizeWatts = inverterSizeAC * 1000
    outData["powerOutputAcInvClipped"] = [
        x if x < invSizeWatts else invSizeWatts
        for x in outData["powerOutputAc"]
    ]
    try:
        outData["percentClipped"] = 100 * (
            1.0 - sum(outData["powerOutputAcInvClipped"]) /
            sum(outData["powerOutputAc"]))
    except ZeroDivisionError:
        outData["percentClipped"] = 0.0
    #One year generation
    outData["oneYearGenerationWh"] = sum(outData["powerOutputAcInvClipped"])
    #Annual generation for all years
    loanYears = 25
    outData["allYearGenerationMWh"] = {}
    outData["allYearGenerationMWh"][1] = float(
        outData["oneYearGenerationWh"]) / 1000000
    # outData["allYearGenerationMWh"][1] = float(2019.576)
    for i in range(2, loanYears + 1):
        outData["allYearGenerationMWh"][i] = float(
            outData["allYearGenerationMWh"][i - 1]) * (
                1 - float(inputDict.get("degradation", 0.8)) / 100)
    # Summary of Results.
    ######
    ### Total Costs (sum of): Hardware Costs, Design/Engineering/PM/EPC/Labor Costs, Siteprep Costs, Construction Costs, Installation Costs, Land Costs
    ######
    ### Hardware Costs
    pvModules = arraySizeDC * float(inputDict.get("moduleCost",
                                                  0)) * 1000  #off by 4000
    racking = arraySizeDC * float(inputDict.get("rackCost", 0)) * 1000
    inverters = numberInverters * float(inputDict.get("inverterCost", 0))
    inverterSize = inverterSizeAC
    if (inverterSize <= 250):
        gear = 15000
    elif (inverterSize <= 600):
        gear = 18000
    else:
        gear = inverterSize / 1000 * 22000
    balance = inverterSizeAC * 1.3908 * 134
    combiners = math.ceil(numberPanels / 19 / 24) * float(1800)  #*
    wireManagement = arraySizeDC * 1.5
    transformer = 1 * 28000
    weatherStation = 1 * 12500
    shipping = 1.02
    hardwareCosts = (pvModules + racking + inverters + gear + balance +
                     combiners + wireManagement + transformer +
                     weatherStation) * shipping
    ### Design/Engineering/PM/EPC/Labor Costs
    EPCmarkup = float(inputDict.get("EPCRate", 0)) / 100 * hardwareCosts
    #designCosts = float(inputDict.get("mechLabor",0))*160 + float(inputDict.get("elecLabor",0))*75 + float(inputDict.get("pmCost",0)) + EPCmarkup
    hoursDesign = 160 * math.sqrt(arraySizeDC / 1390)
    hoursElectrical = 80 * math.sqrt(arraySizeDC / 1391)
    designLabor = 65 * hoursDesign
    electricalLabor = 75 * hoursElectrical
    laborDesign = designLabor + electricalLabor + float(
        inputDict.get("pmCost", 0)) + EPCmarkup
    materialDesign = 0
    designCosts = materialDesign + laborDesign
    ### Siteprep Costs
    surveying = 2.25 * 4 * math.sqrt(landAmount * 43560)
    concrete = 8000 * math.ceil(numberInverters / 2)
    fencing = 6.75 * 4 * math.sqrt(landAmount * 43560)
    grading = 2.5 * 4 * math.sqrt(landAmount * 43560)
    landscaping = 750 * landAmount
    siteMaterial = 8000 + 600 + 5500 + 5000 + surveying + concrete + fencing + grading + landscaping + 5600
    blueprints = float(inputDict.get("mechLabor", 0)) * 12
    mobilization = float(inputDict.get("mechLabor", 0)) * 208
    mobilizationMaterial = float(inputDict.get("mechLabor", 0)) * 19.98
    siteLabor = blueprints + mobilization + mobilizationMaterial
    sitePrep = siteMaterial + siteLabor
    ### Construction Costs (Office Trailer, Skid Steer, Storage Containers, etc)
    constrEquip = 6000 + math.sqrt(landAmount) * 16200
    ### Installation Costs
    moduleAndRackingInstall = numberPanels * (15.00 + 12.50 + 1.50)
    pierDriving = 1 * arraySizeDC * 20
    balanceInstall = 1 * arraySizeDC * 100
    installCosts = moduleAndRackingInstall + pierDriving + balanceInstall + float(
        inputDict.get("elecLabor", 0)) * (72 + 60 + 70 + 10 + 5 + 30 + 70)
    ### Land Costs
    if (str(inputDict.get("landOwnership", 0)) == "Owned"
            or (str(inputDict.get("landOwnership", 0)) == "Leased")):
        landCosts = 0
    else:
        landCosts = float(inputDict.get("costAcre", 0)) * landAmount
    ######
    ### Total Costs
    ######
    totalCosts = hardwareCosts + designCosts + sitePrep + constrEquip + installCosts + landCosts
    totalFees = float(inputDict.get("devCost", 0)) / 100 * totalCosts
    outData["totalCost"] = totalCosts + totalFees + float(
        inputDict.get("interCost", 0))
    # Add to Pie Chart
    outData["costsPieChart"] = [
        ["Land", landCosts], ["Design/Engineering/PM/EPC", designCosts],
        ["PV Modules", pvModules * shipping], ["Racking", racking * shipping],
        ["Inverters & Switchgear", (inverters + gear) * shipping],
        [
            "BOS", hardwareCosts - pvModules * shipping - racking * shipping -
            (inverters + gear) * shipping
        ],
        [
            "Site Prep, Constr. Eq. and Installation",
            (siteMaterial + constrEquip) + (siteLabor + installCosts)
        ]
    ]
    # Cost per Wdc
    outData["costWdc"] = (totalCosts + totalFees + float(
        inputDict.get("interCost", 0))) / (arraySizeDC * 1000)
    outData["capFactor"] = float(outData["oneYearGenerationWh"]) / (
        inverterSizeAC * 1000 * 365.25 * 24) * 100
    ######
    ### Loans calculations for Direct, NCREB, Lease, Tax-equity, and PPA
    ######
    ### Full Ownership, Direct Loan
    #Output - Direct Loan [C]
    projectCostsDirect = 0
    #Output - Direct Loan [D]
    netFinancingCostsDirect = 0
    #Output - Direct Loan [E]
    OMInsuranceETCDirect = []
    #Output - Direct Loan [F]
    distAdderDirect = []
    #Output - Direct Loan [G]
    netCoopPaymentsDirect = []
    #Output - Direct Loan [H]
    costToCustomerDirect = []
    #Output - Direct Loan [F53]
    Rate_Levelized_Direct = 0
    ## Output - Direct Loan Formulas
    projectCostsDirect = 0
    #Output - Direct Loan [D]
    payment = pmt(
        float(inputDict.get("loanRate", 0)) / 100, loanYears,
        outData["totalCost"])
    interestDirectPI = outData["totalCost"] * float(
        inputDict.get("loanRate", 0)) / 100
    principleDirectPI = (-payment - interestDirectPI)
    patronageCapitalRetiredDPI = 0
    netFinancingCostsDirect = -(principleDirectPI + interestDirectPI -
                                patronageCapitalRetiredDPI)
    #Output - Direct Loan [E] [F] [G] [H]
    firstYearOPMainCosts = (1.25 * arraySizeDC * 12)
    firstYearInsuranceCosts = (0.37 * outData["totalCost"] / 100)
    if (inputDict.get("landOwnership", 0) == "Leased"):
        firstYearLandLeaseCosts = float(inputDict.get("costAcre",
                                                      0)) * landAmount
    else:
        firstYearLandLeaseCosts = 0
    for i in range(1, len(outData["allYearGenerationMWh"]) + 1):
        OMInsuranceETCDirect.append(
            -firstYearOPMainCosts * math.pow((1 + .01), (i - 1)) -
            firstYearInsuranceCosts * math.pow((1 + .025), (i - 1)) -
            firstYearLandLeaseCosts * math.pow((1 + .01), (i - 1)))
        distAdderDirect.append(
            float(inputDict.get("distAdder", 0)) *
            outData["allYearGenerationMWh"][i])
        netCoopPaymentsDirect.append(OMInsuranceETCDirect[i - 1] +
                                     netFinancingCostsDirect)
        costToCustomerDirect.append(
            (netCoopPaymentsDirect[i - 1] - distAdderDirect[i - 1]))
    #Output - Direct Loan [F53]
    NPVLoanDirect = npv(
        float(inputDict.get("discRate", 0)) / 100,
        [0, 0] + costToCustomerDirect)
    NPVallYearGenerationMWh = npv(
        float(inputDict.get("discRate", 0)) / 100,
        [0, 0] + outData["allYearGenerationMWh"].values())
    Rate_Levelized_Direct = -NPVLoanDirect / NPVallYearGenerationMWh
    #Master Output [Direct Loan]
    outData["levelCostDirect"] = Rate_Levelized_Direct
    outData["costPanelDirect"] = abs(NPVLoanDirect / numberPanels)
    outData["cost10WPanelDirect"] = (float(outData["costPanelDirect"]) /
                                     panelSize) * 10
    ### NCREBs Financing
    ncrebsRate = float(inputDict.get("NCREBRate", 4.060)) / 100
    ncrebBorrowingRate = 1.1 * ncrebsRate
    ncrebPaymentPeriods = 44
    ncrebCostToCustomer = []
    # TODO ASAP: FIX ARRAY OFFSETS START 0
    for i in range(1, len(outData["allYearGenerationMWh"]) + 1):
        coopLoanPayment = 2 * pmt(
            ncrebBorrowingRate / 2.0, ncrebPaymentPeriods,
            outData["totalCost"]) if i <= ncrebPaymentPeriods / 2 else 0
        ncrebsCredit = -0.7 * (
            ipmt(ncrebsRate / 2, 2 * i -
                 1, ncrebPaymentPeriods, outData["totalCost"]) +
            ipmt(ncrebsRate / 2, 2 * i, ncrebPaymentPeriods,
                 outData["totalCost"])) if i <= ncrebPaymentPeriods / 2 else 0
        financingCost = ncrebsCredit + coopLoanPayment
        omCost = OMInsuranceETCDirect[i - 1]
        netCoopPayments = financingCost + omCost
        distrAdder = distAdderDirect[i - 1]
        costToCustomer = netCoopPayments + distrAdder
        ncrebCostToCustomer.append(costToCustomer)
    NPVLoanNCREB = npv(
        float(inputDict.get("discRate", 0)) / 100,
        [0, 0] + ncrebCostToCustomer)
    Rate_Levelized_NCREB = -NPVLoanNCREB / NPVallYearGenerationMWh
    outData["levelCostNCREB"] = Rate_Levelized_NCREB
    outData["costPanelNCREB"] = abs(NPVLoanNCREB / numberPanels)
    outData["cost10WPanelNCREB"] = (float(outData["costPanelNCREB"]) /
                                    panelSize) * 10
    ### Lease Buyback Structure
    #Output - Lease [C]
    projectCostsLease = outData["totalCost"]
    #Output - Lease [D]
    leasePaymentsLease = []
    #Output - Lease [E]
    OMInsuranceETCLease = OMInsuranceETCDirect
    #Output - Lease [F]
    distAdderLease = distAdderDirect
    #Output - Lease [G]
    netCoopPaymentsLease = []
    #Output - Lease [H]
    costToCustomerLease = []
    #Output - Lease [H44]
    NPVLease = 0
    #Output - Lease [H49]
    Rate_Levelized_Lease = 0
    ## Tax Lease Formulas
    #Output - Lease [D]
    for i in range(0, 12):
        leaseRate = float(inputDict.get("taxLeaseRate", 0)) / 100.0
        if i > 8:  # Special behavior in later years:
            leaseRate = leaseRate - 0.0261
        leasePaymentsLease.append(-1 * projectCostsLease /
                                  ((1.0 - (1.0 / (1.0 + leaseRate)**12)) /
                                   (leaseRate)))
    # Last year is different.
    leasePaymentsLease[11] += -0.2 * projectCostsLease
    for i in range(12, 25):
        leasePaymentsLease.append(0)
    #Output - Lease [G]	[H]
    for i in range(1, len(outData["allYearGenerationMWh"]) + 1):
        netCoopPaymentsLease.append(OMInsuranceETCLease[i - 1] +
                                    leasePaymentsLease[i - 1])
        costToCustomerLease.append(netCoopPaymentsLease[i - 1] -
                                   distAdderLease[i - 1])
    #Output - Lease [H44]. Note the extra year at the zero point to get the discounting right.
    NPVLease = npv(
        float(inputDict.get("discRate", 0)) / 100,
        [0, 0] + costToCustomerLease)
    #Output - Lease [H49] (Levelized Cost Three Loops)
    Rate_Levelized_Lease = -NPVLease / NPVallYearGenerationMWh
    #Master Output [Lease]
    outData["levelCostTaxLease"] = Rate_Levelized_Lease
    outData["costPanelTaxLease"] = abs(NPVLease / numberPanels)
    outData["cost10WPanelTaxLease"] = (float(outData["costPanelTaxLease"]) /
                                       float(panelSize)) * 10

    ### Tax Equity Flip Structure
    # Tax Equity Flip Function
    def taxEquityFlip(PPARateSixYearsTE, discRate, totalCost,
                      allYearGenerationMWh, distAdderDirect, loanYears,
                      firstYearLandLeaseCosts, firstYearOPMainCosts,
                      firstYearInsuranceCosts, numberPanels):
        #Output Tax Equity Flip [C]
        coopInvestmentTaxEquity = -totalCost * (1 - 0.53)
        #Output Tax Equity Flip [D]
        financeCostCashTaxEquity = 0
        #Output Tax Equity Flip [E]
        cashToSPEOForPPATE = []
        #Output Tax Equity Flip [F]
        derivedCostEnergyTE = 0
        #Output Tax Equity Flip [G]
        OMInsuranceETCTE = []
        #Output Tax Equity Flip [H]
        cashFromSPEToBlockerTE = []
        #Output Tax Equity Flip [I]
        cashFromBlockerTE = 0
        #Output Tax Equity Flip [J]
        distAdderTaxEquity = distAdderDirect
        #Output Tax Equity Flip [K]
        netCoopPaymentsTaxEquity = []
        #Output Tax Equity Flip [L]
        costToCustomerTaxEquity = []
        #Output Tax Equity Flip [L64]
        NPVLoanTaxEquity = 0
        #Output Tax Equity Flip [F72]
        Rate_Levelized_Equity = 0
        ## Tax Equity Flip Formulas
        #Output Tax Equity Flip [D]
        #TEI Calcs [E]
        financeCostOfCashTE = 0
        coopFinanceRateTE = 2.7 / 100
        if (coopFinanceRateTE == 0):
            financeCostOfCashTE = 0
        else:
            payment = pmt(coopFinanceRateTE, loanYears,
                          -coopInvestmentTaxEquity)
        financeCostCashTaxEquity = payment
        #Output Tax Equity Flip [E]
        SPERevenueTE = []
        for i in range(1, len(allYearGenerationMWh) + 1):
            SPERevenueTE.append(PPARateSixYearsTE * allYearGenerationMWh[i])
            if ((i >= 1) and (i <= 6)):
                cashToSPEOForPPATE.append(-SPERevenueTE[i - 1])
            else:
                cashToSPEOForPPATE.append(0)
        #Output Tax Equity Flip [F]
        derivedCostEnergyTE = cashToSPEOForPPATE[0] / allYearGenerationMWh[1]
        #Output Tax Equity Flip [G]
        #TEI Calcs [F]	[U] [V]
        landLeaseTE = []
        OMTE = []
        insuranceTE = []
        for i in range(1, len(allYearGenerationMWh) + 1):
            landLeaseTE.append(firstYearLandLeaseCosts * math.pow((1 + .01),
                                                                  (i - 1)))
            OMTE.append(-firstYearOPMainCosts * math.pow((1 + .01), (i - 1)))
            insuranceTE.append(-firstYearInsuranceCosts * math.pow((1 + .025),
                                                                   (i - 1)))
            if (i < 7):
                OMInsuranceETCTE.append(float(landLeaseTE[i - 1]))
            else:
                OMInsuranceETCTE.append(
                    float(OMTE[i - 1]) + float(insuranceTE[i - 1]) +
                    float(landLeaseTE[i - 1]))
        #Output Tax Equity Flip [H]
        #TEI Calcs [T]
        SPEMgmtFeeTE = []
        EBITDATE = []
        EBITDATEREDUCED = []
        managementFee = 10000
        for i in range(1, len(SPERevenueTE) + 1):
            SPEMgmtFeeTE.append(-managementFee * math.pow((1 + .01), (i - 1)))
            EBITDATE.append(
                float(SPERevenueTE[i - 1]) + float(OMTE[i - 1]) +
                float(insuranceTE[i - 1]) + float(SPEMgmtFeeTE[i - 1]))
            if (i <= 6):
                cashFromSPEToBlockerTE.append(float(EBITDATE[i - 1]) * .01)
            else:
                cashFromSPEToBlockerTE.append(0)
                EBITDATEREDUCED.append(EBITDATE[i - 1])
        #Output Tax Equity Flip [I]
        #TEI Calcs [Y21]
        cashRevenueTE = -totalCost * (1 - 0.53)
        buyoutAmountTE = 0
        for i in range(1, len(EBITDATEREDUCED) + 1):
            buyoutAmountTE = buyoutAmountTE + EBITDATEREDUCED[i - 1] / (
                math.pow(1 + 0.12, i))
        buyoutAmountTE = buyoutAmountTE * 0.05
        cashFromBlockerTE = -(buyoutAmountTE) + 0.0725 * cashRevenueTE
        #Output Tax Equity Flip [K] [L]
        for i in range(1, len(allYearGenerationMWh) + 1):
            if (i == 6):
                netCoopPaymentsTaxEquity.append(financeCostCashTaxEquity +
                                                cashToSPEOForPPATE[i - 1] +
                                                cashFromSPEToBlockerTE[i - 1] +
                                                OMInsuranceETCTE[i - 1] +
                                                cashFromBlockerTE)
            else:
                netCoopPaymentsTaxEquity.append(financeCostCashTaxEquity +
                                                cashFromSPEToBlockerTE[i - 1] +
                                                cashToSPEOForPPATE[i - 1] +
                                                OMInsuranceETCTE[i - 1])
            costToCustomerTaxEquity.append(netCoopPaymentsTaxEquity[i - 1] -
                                           distAdderTaxEquity[i - 1])
        #Output Tax Equity Flip [L37]
        NPVLoanTaxEquity = npv(
            float(inputDict.get("discRate", 0)) / 100,
            [0, 0] + costToCustomerTaxEquity)
        #Output - Tax Equity [F42]
        Rate_Levelized_TaxEquity = -NPVLoanTaxEquity / NPVallYearGenerationMWh
        #TEI Calcs - Achieved Return [AW 21]
        #[AK]
        MACRDepreciation = []
        MACRDepreciation.append(-0.99 * 0.2 *
                                (totalCost - totalCost * 0.5 * 0.9822 * 0.3))
        MACRDepreciation.append(-0.99 * 0.32 *
                                (totalCost - totalCost * 0.5 * 0.9822 * 0.3))
        MACRDepreciation.append(-0.99 * 0.192 *
                                (totalCost - totalCost * 0.5 * 0.9822 * 0.3))
        MACRDepreciation.append(-0.99 * 0.1152 *
                                (totalCost - totalCost * 0.5 * 0.9822 * 0.3))
        MACRDepreciation.append(-0.99 * 0.1152 *
                                (totalCost - totalCost * 0.5 * 0.9822 * 0.3))
        MACRDepreciation.append(-0.99 * 0.0576 *
                                (totalCost - totalCost * 0.5 * 0.9822 * 0.3))
        #[AI] [AL]	[AN]
        cashRevenueTEI = []  #[AI]
        slDepreciation = []  #[AL]
        totalDistributions = []  #[AN]
        cashRevenueTEI.append(-totalCost * 0.53)
        for i in range(1, 7):
            cashRevenueTEI.append(EBITDATE[i - 1] * 0.99)
            slDepreciation.append(totalCost / 25)
            totalDistributions.append(-cashRevenueTEI[i])
        #[AJ]
        ITC = totalCost * 0.9822 * 0.3 * 0.99
        #[AM]
        taxableIncLoss = [0]
        taxableIncLoss.append(cashRevenueTEI[1] + MACRDepreciation[0])
        #[AO]
        capitalAcct = []
        capitalAcct.append(totalCost * 0.53)
        condition = capitalAcct[0] - 0.5 * ITC + taxableIncLoss[
            1] + totalDistributions[0]
        if condition > 0:
            capitalAcct.append(condition)
        else:
            capitalAcct.append(0)
        #[AQ]
        ratioTE = [0]
        #[AP]
        reallocatedIncLoss = []
        #AO-1 + AN + AI + AK + AJ
        for i in range(0, 5):
            reallocatedIncLoss.append(capitalAcct[i + 1] +
                                      totalDistributions[i + 1] +
                                      MACRDepreciation[i + 1] +
                                      cashRevenueTEI[i + 2])
            ratioTE.append(reallocatedIncLoss[i] /
                           (cashRevenueTEI[i + 2] + MACRDepreciation[i + 1]))
            taxableIncLoss.append(
                cashRevenueTEI[i + 2] + MACRDepreciation[i + 1] -
                ratioTE[i + 1] *
                (MACRDepreciation[i + 1] - totalDistributions[i + 1]))
            condition = capitalAcct[i + 1] + taxableIncLoss[
                i + 2] + totalDistributions[i + 1]
            if condition > 0:
                capitalAcct.append(condition)
            else:
                capitalAcct.append(0)
        #[AR]
        taxesBenefitLiab = [0]
        for i in range(1, 7):
            taxesBenefitLiab.append(-taxableIncLoss[i] * 0.35)
        #[AS] [AT]
        buyoutAmount = 0
        taxFromBuyout = 0
        for i in range(0, len(EBITDATEREDUCED)):
            buyoutAmount = buyoutAmount + .05 * EBITDATEREDUCED[i] / (math.pow(
                1.12, (i + 1)))
        taxFromBuyout = -buyoutAmount * 0.35
        #[AU] [AV]
        totalCashTax = []
        cumulativeCashTax = [0]
        for i in range(0, 7):
            if i == 1:
                totalCashTax.append(cashRevenueTEI[i] + ITC +
                                    taxesBenefitLiab[i] + 0 + 0)
                cumulativeCashTax.append(cumulativeCashTax[i] +
                                         totalCashTax[i])
            elif i == 6:
                totalCashTax.append(cashRevenueTEI[i] + 0 +
                                    taxesBenefitLiab[i] + buyoutAmount +
                                    taxFromBuyout)
                cumulativeCashTax.append(cumulativeCashTax[i] +
                                         totalCashTax[i] + buyoutAmount +
                                         taxFromBuyout)
            else:
                totalCashTax.append(cashRevenueTEI[i] + 0 +
                                    taxesBenefitLiab[i] + 0 + 0)
                cumulativeCashTax.append(cumulativeCashTax[i] +
                                         totalCashTax[i])
        #[AW21]
        if (cumulativeCashTax[7] > 0):
            cumulativeIRR = round(irr(totalCashTax), 4)
        else:
            cumulativeIRR = 0
        # Deleteme: Variable Dump for debugging
        # variableDump = {}
        # variableDump["TaxEquity"] = {}
        # variableDump["TaxEquity"]["coopInvestmentTaxEquity"] = coopInvestmentTaxEquity
        # variableDump["TaxEquity"]["financeCostCashTaxEquity"] = financeCostCashTaxEquity
        # variableDump["TaxEquity"]["cashToSPEOForPPATE"] = cashToSPEOForPPATE
        # variableDump["TaxEquity"]["derivedCostEnergyTE"] = derivedCostEnergyTE
        # variableDump["TaxEquity"]["OMInsuranceETCTE"] = OMInsuranceETCTE
        # variableDump["TaxEquity"]["cashFromSPEToBlockerTE"] = cashFromSPEToBlockerTE
        # variableDump["TaxEquity"]["cashFromBlockerTE"] = cashFromBlockerTE
        # variableDump["TaxEquity"]["distAdderTaxEquity"] = distAdderTaxEquity
        # variableDump["TaxEquity"]["netCoopPaymentsTaxEquity"] = netCoopPaymentsTaxEquity
        # variableDump["TaxEquity"]["NPVLoanTaxEquity"] = NPVLoanTaxEquity
        return cumulativeIRR, Rate_Levelized_TaxEquity, NPVLoanTaxEquity

    # Function Calls Mega Sized Tax Equity Function Above
    z = 0
    PPARateSixYearsTE = z / 100
    nGoal = float(inputDict.get("taxEquityReturn", 0)) / 100
    nValue = 0
    for p in range(0, 3):
        while ((z < 50000) and (nValue < nGoal)):
            achievedReturnTE, Rate_Levelized_TaxEquity, NPVLoanTaxEquity = taxEquityFlip(
                PPARateSixYearsTE, inputDict.get("discRate", 0),
                outData["totalCost"], outData["allYearGenerationMWh"],
                distAdderDirect, loanYears, firstYearLandLeaseCosts,
                firstYearOPMainCosts, firstYearInsuranceCosts, numberPanels)
            nValue = achievedReturnTE
            z = z + math.pow(10, p)
            PPARateSixYearsTE = z / 100.0
    z = z - math.pow(10, p)
    PPARateSixYearsTE = z / 100
    #Master Output [Tax Equity]
    outData["levelCostTaxEquity"] = Rate_Levelized_TaxEquity
    outData["costPanelTaxEquity"] = abs(NPVLoanTaxEquity / numberPanels)
    outData["cost10WPanelTaxEquity"] = (float(outData["costPanelTaxEquity"]) /
                                        panelSize) * 10
    ### PPA Comparison
    #Output - PPA [F]
    distAdderPPA = distAdderDirect
    #Output - PPA [G]
    netCoopPaymentsPPA = []
    #Output - PPA [H]
    costToCustomerPPA = []
    #Output - PPA [I]
    costToCustomerPPA = []
    #Output - PPA [H40]
    NPVLoanPPA = 0
    #Output - PPA [I40]
    Rate_Levelized_PPA = 0
    ## PPA Formulas
    #Output - PPA [G] [H]
    for i in range(1, len(outData["allYearGenerationMWh"]) + 1):
        netCoopPaymentsPPA.append(
            -outData["allYearGenerationMWh"][i] *
            float(inputDict.get("firstYearEnergyCostPPA", 0)) * math.pow(
                (1 + float(inputDict.get("annualEscRatePPA", 0)) / 100),
                (i - 1)))
        costToCustomerPPA.append(netCoopPaymentsPPA[i - 1] -
                                 distAdderPPA[i - 1])
    #Output - PPA [H58]
    NPVLoanPPA = npv(
        float(inputDict.get("discRate", 0)) / 100, [0, 0] + costToCustomerPPA)
    #Output - PPA [F65]
    Rate_Levelized_PPA = -NPVLoanPPA / NPVallYearGenerationMWh
    #Master Output [PPA]
    outData["levelCostPPA"] = Rate_Levelized_PPA
    outData["firstYearCostKWhPPA"] = float(
        inputDict.get("firstYearEnergyCostPPA", 0))
    outData["yearlyEscalationPPA"] = float(inputDict.get(
        "annualEscRatePPA", 0))
    # Add all Levelized Costs to Output
    outData["LevelizedCosts"] = [["Direct Loan", Rate_Levelized_Direct],
                                 ["NCREBs Financing", Rate_Levelized_NCREB],
                                 ["Lease Buyback", Rate_Levelized_Lease],
                                 ["Tax Equity Flip", Rate_Levelized_TaxEquity]]
    outData["LevelizedCosts"].append({
        "name": "PPA Comparison",
        "y": Rate_Levelized_PPA,
        "color": "gold"
    })
    # Stdout/stderr.
    outData["stdout"] = "Success"
    outData["stderr"] = ""
    return outData
Exemple #6
0
def work(modelDir, inputDict):
    # Copy specific climate data into model directory
    inputDict["climateName"], latforpvwatts = zipCodeToClimateName(
        inputDict["zipCode"])
    shutil.copy(
        pJoin(__neoMetaModel__._omfDir, "data", "Climate",
              inputDict["climateName"] + ".tmy2"),
        pJoin(modelDir, "climate.tmy2"))
    # Set up SAM data structures.
    ssc = nrelsam2013.SSCAPI()
    dat = ssc.ssc_data_create()
    # Required user inputs.
    ssc.ssc_data_set_string(dat, "file_name", modelDir + "/climate.tmy2")
    ssc.ssc_data_set_number(dat, "system_size", float(inputDict["systemSize"]))
    ssc.ssc_data_set_number(dat, "derate",
                            0.01 * float(inputDict["nonInverterEfficiency"]))
    ssc.ssc_data_set_number(dat, "track_mode",
                            float(inputDict["trackingMode"]))
    ssc.ssc_data_set_number(dat, "azimuth", float(inputDict["azimuth"]))
    # Advanced inputs with defaults.
    if (inputDict.get("tilt", 0) == "-"):
        tilt_eq_lat = 1.0
        manualTilt = 0.0
    else:
        tilt_eq_lat = 0.0
        manualTilt = float(inputDict.get("tilt", 0))
    ssc.ssc_data_set_number(dat, "tilt_eq_lat", tilt_eq_lat)
    ssc.ssc_data_set_number(dat, "tilt", manualTilt)
    ssc.ssc_data_set_number(dat, "rotlim", float(inputDict["rotlim"]))
    ssc.ssc_data_set_number(dat, "gamma", -1 * float(inputDict["gamma"]))
    ssc.ssc_data_set_number(dat, "inv_eff",
                            0.01 * float(inputDict["inverterEfficiency"]))
    ssc.ssc_data_set_number(dat, "w_stow", float(inputDict["w_stow"]))
    # Complicated optional inputs that we could enable later.
    # ssc.ssc_data_set_array(dat, 'shading_hourly', ...) 	# Hourly beam shading factors
    # ssc.ssc_data_set_matrix(dat, 'shading_mxh', ...) 		# Month x Hour beam shading factors
    # ssc.ssc_data_set_matrix(dat, 'shading_azal', ...) 	# Azimuth x altitude beam shading factors
    # ssc.ssc_data_set_number(dat, 'shading_diff', ...) 	# Diffuse shading factor
    # ssc.ssc_data_set_number(dat, 'enable_user_poa', ...)	# Enable user-defined POA irradiance input = 0 or 1
    # ssc.ssc_data_set_array(dat, 'user_poa', ...) 			# User-defined POA irradiance in W/m2
    # ssc.ssc_data_set_number(dat, 'tilt', 999)
    # ssc.ssc_data_set_number(dat, "t_noct", float(inputDict["t_noct"]))
    # ssc.ssc_data_set_number(dat, "t_ref", float(inputDict["t_ref"]))
    # ssc.ssc_data_set_number(dat, "fd", float(inputDict["fd"]))
    # ssc.ssc_data_set_number(dat, "i_ref", float(inputDict["i_ref"]))
    # ssc.ssc_data_set_number(dat, "poa_cutin", float(inputDict["poa_cutin"]))
    # Run PV system simulation.
    mod = ssc.ssc_module_create("pvwattsv1")
    ssc.ssc_module_exec(mod, dat)
    # Setting options for start time.
    simLengthUnits = inputDict.get("simLengthUnits", "")
    simStartDate = inputDict["simStartDate"]
    # Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
    startDateTime = simStartDate + " 00:00:00 UTC"
    # Set aggregation function constants.
    agg = lambda x, y: _aggData(x, y, inputDict["simStartDate"],
                                int(inputDict["simLength"]), inputDict[
                                    "simLengthUnits"], ssc, dat)
    avg = lambda x: sum(x) / len(x)
    # Timestamp output.
    outData = {}
    outData["timeStamps"] = [
        datetime.datetime.strftime(
            datetime.datetime.strptime(startDateTime[0:19],
                                       "%Y-%m-%d %H:%M:%S") +
            datetime.timedelta(**{simLengthUnits: x}), "%Y-%m-%d %H:%M:%S") +
        " UTC" for x in range(int(inputDict["simLength"]))
    ]
    # Geodata output.
    outData["city"] = ssc.ssc_data_get_string(dat, "city")
    outData["state"] = ssc.ssc_data_get_string(dat, "state")
    outData["lat"] = ssc.ssc_data_get_number(dat, "lat")
    outData["lon"] = ssc.ssc_data_get_number(dat, "lon")
    outData["elev"] = ssc.ssc_data_get_number(dat, "elev")
    # Weather output.
    outData["climate"] = {}
    outData["climate"]["Plane of Array Irradiance (W/m^2)"] = agg("poa", avg)
    outData["climate"]["Beam Normal Irradiance (W/m^2)"] = agg("dn", avg)
    outData["climate"]["Diffuse Irradiance (W/m^2)"] = agg("df", avg)
    outData["climate"]["Ambient Temperature (F)"] = agg("tamb", avg)
    outData["climate"]["Cell Temperature (F)"] = agg("tcell", avg)
    outData["climate"]["Wind Speed (m/s)"] = agg("wspd", avg)
    # Power generation.
    outData["Consumption"] = {}
    outData["Consumption"]["Power"] = [x for x in agg("ac", avg)]
    outData["Consumption"]["Losses"] = [0 for x in agg("ac", avg)]
    outData["Consumption"]["DG"] = agg("ac", avg)
    # Stdout/stderr.
    outData["stdout"] = "Success"
    outData["stderr"] = ""
    return outData
Exemple #7
0
def run(modelDir, inputDict):
    try:
        ''' Run the model in its directory. '''
        # Check whether model exist or not
        if not os.path.isdir(modelDir):
            os.makedirs(modelDir)
            inputDict["created"] = str(dt.now())
        # MAYBEFIX: remove this data dump. Check showModel in web.py and renderTemplate()
        with open(pJoin(modelDir, "allInputData.json"), "w") as inputFile:
            json.dump(inputDict, inputFile, indent=4)
        # Copy spcific climate data into model directory
        inputDict["climateName"], latforpvwatts = zipCodeToClimateName(
            inputDict["zipCode"])
        shutil.copy(
            pJoin(__metaModel__._omfDir, "data", "Climate",
                  inputDict["climateName"] + ".tmy2"),
            pJoin(modelDir, "climate.tmy2"))
        # Ready to run
        startTime = dt.now()
        # Set up SAM data structures.
        ssc = nrelsam2013.SSCAPI()
        dat = ssc.ssc_data_create()
        # Required user inputs.
        ssc.ssc_data_set_string(dat, "file_name", modelDir + "/climate.tmy2")
        ssc.ssc_data_set_number(dat, "system_size",
                                float(inputDict["SystemSize"]))
        # SAM options where we take defaults.
        ssc.ssc_data_set_number(dat, "derate", 0.97)
        ssc.ssc_data_set_number(dat, "track_mode", 0)
        ssc.ssc_data_set_number(dat, "azimuth", 180)
        ssc.ssc_data_set_number(dat, "tilt_eq_lat", 1)
        # Run PV system simulation.
        mod = ssc.ssc_module_create("pvwattsv1")
        ssc.ssc_module_exec(mod, dat)
        # Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
        startDateTime = "2013-01-01 00:00:00 UTC"
        # Timestamp output.
        outData = {}
        outData["timeStamps"] = [
            dt.strftime(
                dt.strptime(startDateTime[0:19], "%Y-%m-%d %H:%M:%S") +
                td(**{"hours": x}), "%Y-%m-%d %H:%M:%S") + " UTC"
            for x in range(int(8760))
        ]
        # HACK: makes it easier to calculate some things later.
        outData["pythonTimeStamps"] = [
            dt(2012, 1, 1, 0) + x * td(hours=1) for x in range(8760)
        ]
        # Geodata output.
        outData["city"] = ssc.ssc_data_get_string(dat, "city")
        outData["state"] = ssc.ssc_data_get_string(dat, "state")
        outData["lat"] = ssc.ssc_data_get_number(dat, "lat")
        outData["lon"] = ssc.ssc_data_get_number(dat, "lon")
        outData["elev"] = ssc.ssc_data_get_number(dat, "elev")
        # Weather output.
        outData["climate"] = {}
        outData["climate"][
            "Global Horizontal Radiation (W/m^2)"] = ssc.ssc_data_get_array(
                dat, "gh")
        outData["climate"][
            "Plane of Array Irradiance (W/m^2)"] = ssc.ssc_data_get_array(
                dat, "poa")
        outData["climate"]["Ambient Temperature (F)"] = ssc.ssc_data_get_array(
            dat, "tamb")
        outData["climate"]["Cell Temperature (F)"] = ssc.ssc_data_get_array(
            dat, "tcell")
        outData["climate"]["Wind Speed (m/s)"] = ssc.ssc_data_get_array(
            dat, "wspd")
        # Power generation.
        outData["powerOutputAc"] = ssc.ssc_data_get_array(dat, "ac")

        # TODO: INSERT TJ CODE BELOW
        tjCode(inputDict, outData)
        del outData["pythonTimeStamps"]
        # TODO: INSERT TJ CODE ABOVE

        # Stdout/stderr.
        outData["stdout"] = "Success"
        outData["stderr"] = ""
        # Write the output.
        with open(pJoin(modelDir, "allOutputData.json"), "w") as outFile:
            json.dump(outData, outFile, indent=4)
        # Update the runTime in the input file.
        endTime = dt.now()
        inputDict["runTime"] = str(
            td(seconds=int((endTime - startTime).total_seconds())))
        with open(pJoin(modelDir, "allInputData.json"), "w") as inFile:
            json.dump(inputDict, inFile, 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)
Exemple #8
0
def samRun(temp_dir):
    '''
	Run NREL's System Advisor Model with the specified parameters and return output vectors and floats in JSON.

	Form parameters:
	:param tmy2: a tmy2 file.
	:param system_size: nameplate capacity.
	:param derate: system derate value.
	:param track_mode: tracking mode.
	:param azimuth: azimuth angle.
	:param tilt: tilt angle.
	There are 40 other additional optional input parameters to the pvwattsv1 module of S.A.M.

	Details:
	:OMF function: omf.solvers.sam.run().
	:run-time: should only be a couple of seconds.
	See pvwattsv1-variable-info.txt for details on other 40 possible inputs to this route.
	'''
    tmy2_path = os.path.join(temp_dir, "in.tmy2")
    request.files["tmy2"].save(tmy2_path)
    # Set up SAM data structures.
    ssc = nrelsam2013.SSCAPI()
    dat = ssc.ssc_data_create()
    # Set the inputs.
    ssc.ssc_data_set_string(dat, b'file_name', bytes(tmy2_path, 'ascii'))
    for key in request.form.keys():
        ssc.ssc_data_set_number(dat, bytes(key, 'ascii'),
                                float(request.form.get(key)))
    # Enter required parameters
    system_size = int(request.form.get('system_size', 4))
    ssc.ssc_data_set_number(dat, b'system_size', system_size)
    derate = float(request.form.get('derate', .77))
    ssc.ssc_data_set_number(dat, b'derate', derate)
    track_mode = int(request.form.get('track_mode', 0))
    ssc.ssc_data_set_number(dat, b'track_mode', track_mode)
    azimuth = float(request.form.get('azimuth', 180))
    ssc.ssc_data_set_number(dat, b'azimuth', azimuth)
    tilt = float(request.form.get('tilt', 30))
    ssc.ssc_data_set_number(dat, b'tilt', tilt)
    # Run PV system simulation.
    mod = ssc.ssc_module_create(b'pvwattsv1')
    ssc.ssc_module_exec(mod, dat)
    # Geodata output.
    outData = {}
    outData['city'] = ssc.ssc_data_get_string(dat, b'city').decode()
    outData['state'] = ssc.ssc_data_get_string(dat, b'state').decode()
    outData['lat'] = ssc.ssc_data_get_number(dat, b'lat')
    outData['lon'] = ssc.ssc_data_get_number(dat, b'lon')
    outData['elev'] = ssc.ssc_data_get_number(dat, b'elev')
    # Weather output.
    outData['climate'] = {}
    outData['climate'][
        'Plane of Array Irradiance (W/m^2)'] = ssc.ssc_data_get_array(
            dat, b'poa')
    outData['climate'][
        'Beam Normal Irradiance (W/m^2)'] = ssc.ssc_data_get_array(dat, b'dn')
    outData['climate']['Diffuse Irradiance (W/m^2)'] = ssc.ssc_data_get_array(
        dat, b'df')
    outData['climate']['Ambient Temperature (F)'] = ssc.ssc_data_get_array(
        dat, b'tamb')
    outData['climate']['Cell Temperature (F)'] = ssc.ssc_data_get_array(
        dat, b'tcell')
    outData['climate']['Wind Speed (m/s)'] = ssc.ssc_data_get_array(
        dat, b'wspd')
    # Power generation.
    outData['Consumption'] = {}
    outData['Consumption']['Power'] = ssc.ssc_data_get_array(dat, b'ac')
    outData['Consumption']['Losses'] = ssc.ssc_data_get_array(dat, b'ac')
    outData['Consumption']['DG'] = ssc.ssc_data_get_array(dat, b'ac')
    with open(os.path.join(temp_dir, filenames['samrun']), 'w') as f:
        json.dump(outData, f)
Exemple #9
0
def work(modelDir, inputDict):
    ''' Run the model in its directory. '''
    inputDict["climateName"] = weather.zipCodeToClimateName(
        inputDict["zipCode"])
    shutil.copy(
        pJoin(__neoMetaModel__._omfDir, "data", "Climate",
              inputDict["climateName"] + ".tmy2"),
        pJoin(modelDir, "climate.tmy2"))
    # Set up SAM data structures.
    ssc = nrelsam2013.SSCAPI()
    dat = ssc.ssc_data_create()
    # Required user inputs.
    ssc.ssc_data_set_string(dat, b'file_name',
                            bytes(modelDir + '/climate.tmy2', 'ascii'))
    systemSize = max(float(inputDict.get('systemSize', 0)), .1)
    ssc.ssc_data_set_number(dat, b'system_size', systemSize)
    # SAM options where we take defaults.
    ssc.ssc_data_set_number(dat, b'derate', 0.97)
    ssc.ssc_data_set_number(dat, b'track_mode', 0)
    ssc.ssc_data_set_number(dat, b'azimuth', 180)
    ssc.ssc_data_set_number(dat, b'tilt_eq_lat', 1)
    # Run PV system simulation.
    mod = ssc.ssc_module_create(b'pvwattsv1')
    ssc.ssc_module_exec(mod, dat)
    # Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
    startDateTime = "2013-01-01 00:00:00 UTC"
    # Timestamp output.
    outData = {}
    outData["timeStamps"] = [
        datetime.datetime.strftime(
            datetime.datetime.strptime(startDateTime[0:19],
                                       "%Y-%m-%d %H:%M:%S") +
            datetime.timedelta(**{"hours": x}), "%Y-%m-%d %H:%M:%S") + " UTC"
        for x in range(int(8760))
    ]
    # Geodata output.
    outData['city'] = ssc.ssc_data_get_string(dat, b'city').decode()
    outData['state'] = ssc.ssc_data_get_string(dat, b'state').decode()
    outData['lat'] = ssc.ssc_data_get_number(dat, b'lat')
    outData['lon'] = ssc.ssc_data_get_number(dat, b'lon')
    outData['elev'] = ssc.ssc_data_get_number(dat, b'elev')
    # Weather output.
    outData["climate"] = {}
    outData['climate'][
        'Global Horizontal Radiation (W/m^2)'] = ssc.ssc_data_get_array(
            dat, b'gh')
    outData['climate'][
        'Plane of Array Irradiance (W/m^2)'] = ssc.ssc_data_get_array(
            dat, b'poa')
    outData['climate']['Ambient Temperature (F)'] = ssc.ssc_data_get_array(
        dat, b'tamb')
    outData['climate']['Cell Temperature (F)'] = ssc.ssc_data_get_array(
        dat, b'tcell')
    outData['climate']['Wind Speed (m/s)'] = ssc.ssc_data_get_array(
        dat, b'wspd')
    # Power generation.
    outData['powerOutputAc'] = ssc.ssc_data_get_array(dat, b'ac')
    solarFraction = float(inputDict.get("resPenetration", .05)) / 100
    fossilFraction = max(1 - solarFraction, 10**-6)
    # Monthly aggregation outputs.
    months = {
        "Jan": 0,
        "Feb": 1,
        "Mar": 2,
        "Apr": 3,
        "May": 4,
        "Jun": 5,
        "Jul": 6,
        "Aug": 7,
        "Sep": 8,
        "Oct": 9,
        "Nov": 10,
        "Dec": 11
    }
    totMonNum = lambda x: sum([
        z for (y, z) in zip(outData["timeStamps"], outData["powerOutputAc"])
        if y.startswith(startDateTime[0:4] + "-{0:02d}".format(x + 1))
    ])
    outData["monthlyGeneration"] = [[
        a, __neoMetaModel__.roundSig(totMonNum(b), 2)
    ] for (a, b) in sorted(months.items(), key=lambda x: x[1])]
    monthlyConsumers = []
    monthlyResidentialkWhLoad = []
    monthlyResidentialRevenue = []
    monthlyTotalkWhLoad = []
    monthlyTotalRevenue = []
    for key in inputDict:
        # MAYBEFIX: data in list may not be ordered by month.
        if key.endswith("Sale"):
            monthlyConsumers.append(
                [key[:3].title(),
                 float(inputDict.get(key, 0))])
        elif key.endswith("KWh"):  # the order of calculation matters
            monthlyResidentialkWhLoad.append(
                [key[:3].title(),
                 float(inputDict.get(key, 0))])
        elif key.endswith("Rev"):
            monthlyResidentialRevenue.append(
                [key[:3].title(),
                 float(inputDict.get(key, 0))])
        elif key.endswith("KWhT"):
            monthlyTotalkWhLoad.append(
                [key[:3].title(),
                 float(inputDict.get(key, 0))])
        elif key.endswith("RevT"):
            monthlyTotalRevenue.append(
                [key[:3].title(),
                 float(inputDict.get(key, 0))])
    outData["monthlyConsumers"] = sorted(monthlyConsumers,
                                         key=lambda x: months[x[0]])
    outData["monthlyResidentialkWhLoad"] = sorted(monthlyResidentialkWhLoad,
                                                  key=lambda x: months[x[0]])
    outData["monthlyResidentialRevenue"] = sorted(monthlyResidentialRevenue,
                                                  key=lambda x: months[x[0]])
    outData["monthlyTotalkWhLoad"] = sorted(monthlyTotalkWhLoad,
                                            key=lambda x: months[x[0]])
    outData["monthlyTotalRevenue"] = sorted(monthlyTotalRevenue,
                                            key=lambda x: months[x[0]])
    outData["monthlySolarkWhGenerated"] = [[
        sorted(months.items(),
               key=lambda x: x[1])[i][0], outData["monthlyGeneration"][i][1] /
        1000 * outData["monthlyConsumers"][i][1] * solarFraction
    ] for i in range(12)]
    outData["monthlyTotalNetLoadAfterSolar"] = [[
        sorted(months.items(), key=lambda x: x[1])[i][0],
        outData["monthlyTotalkWhLoad"][i][1] -
        outData["monthlySolarkWhGenerated"][i][1]
    ] for i in range(12)]

    ## Flow Diagram Calculations, and order of calculation matters
    retailCost = float(inputDict.get("retailCost"))
    solarLCoE = float(inputDict.get('solarLCoE'))
    customerServiceCharge = float(inputDict.get("customServiceCharge", 0))
    solarServiceCharge = float(inputDict.get("solarServiceCharge", 0))
    totalkWhLoad = sum(monthlyTotalkWhLoad[i][1] for i in range(12))
    # BAU case
    outData["BAU"] = {}
    # E23 = E11
    outData["BAU"]["totalKWhPurchased"] = float(
        inputDict.get("totalKWhPurchased", 1))
    # E24 = SUM(E19:P19)
    outData["BAU"]["totalKWhSales"] = totalkWhLoad
    # E25 = E23-E24
    outData["BAU"]["losses"] = float(inputDict.get("totalKWhPurchased",
                                                   0)) - totalkWhLoad
    # E26 = E25/E23
    outData["BAU"]["effectiveLossRate"] = outData["BAU"]["losses"] / outData[
        "BAU"]["totalKWhPurchased"]
    # E27 = 0
    outData["BAU"]["annualSolarGen"] = 0
    # E28 = SUM(E17:P17)
    outData["BAU"]["resNonSolarKWhSold"] = sum(
        [monthlyResidentialkWhLoad[i][1] for i in range(12)])
    # E29 = 0
    outData["BAU"]["solarResDemand"] = 0
    # E30 = 0
    outData["BAU"]["solarResSold"] = 0
    # E31 = E24-E28
    outData["BAU"]["nonResKWhSold"] = outData["BAU"][
        "totalKWhSales"] - outData["BAU"]["resNonSolarKWhSold"]
    # E32 = 0
    outData["BAU"]["costSolarGen"] = 0
    # E33 = SUM(E20:P20)-SUM(E18:P18)+E10
    outData["BAU"]["nonResRev"] = sum([
        monthlyTotalRevenue[i][1] for i in range(12)
    ]) - sum([monthlyResidentialRevenue[i][1]
              for i in range(12)]) + float(inputDict.get("otherElecRevenue"))
    # E34 = (SUM(E18:P18)-SUM(E16:P16)*E6)/SUM(E17:P17):Buggy and complicated line calculating effectiveResRate replaced with retailCost. Was somehow messing up solar fixed charges.
    # outData["BAU"]["effectiveResRate"] = (sum ([monthlyResidentialRevenue[i][1] for i in range(12)]) - sum([monthlyConsumers[i][1] for i in range(12)])*customerServiceCharge)/sum([monthlyResidentialkWhLoad[i][1] for i in range(12)])
    customerMonths = sum([monthlyConsumers[i][1] for i in range(12)])
    # E35 = E34*E28+SUM(E16:P16)*E6
    outData["BAU"]["resNonSolarRev"] = retailCost * outData["BAU"][
        "resNonSolarKWhSold"] + customerMonths * customerServiceCharge
    # E36 = E30*E34
    outData["BAU"]["solarResRev"] = 0
    # E37 = SUM(E48:E54)+SUM(E56:E62)-SUM(E65:E71), update after Form 7 model
    outData["BAU"]["nonPowerCosts"] = 0
    # E38 = E23-E25-E28-E30-E31
    outData["BAU"]["energyAllBal"] = 0
    # E39 = E36+E33+E35-E47-E72-E37
    outData["BAU"]["dollarAllBal"] = 0
    # E40 = 0
    outData["BAU"]["avgMonthlyBillSolarCus"] = 0
    # E41 = E35/SUM(E16:P16)
    avgCustomerCount = (customerMonths / 12)
    outData["BAU"]["avgMonthlyBillNonSolarCus"] = outData["BAU"][
        "resNonSolarRev"] / customerMonths
    # E42 = E63/E24, update after Form 7 model
    outData["BAU"]["costofService"] = 0
    # Solar case
    outData["Solar"] = {}
    # F27 = SUM(E15:P15)
    outData["Solar"]["annualSolarGen"] = sum(
        [outData["monthlySolarkWhGenerated"][i][1] for i in range(12)])
    # F24 = E24-F27
    outData["Solar"][
        "totalKWhSales"] = totalkWhLoad - outData["Solar"]["annualSolarGen"]
    # F23 =F24/(1-E26)
    outData["Solar"]["totalKWhPurchased"] = outData["Solar"][
        "totalKWhSales"] / (1 - outData["BAU"]["effectiveLossRate"])
    outData["totalsolarmonthly"] = [[
        sorted(months.items(), key=lambda x: x[1])[i][0],
        outData["monthlyTotalNetLoadAfterSolar"][i][1] /
        (1 - outData["BAU"]["effectiveLossRate"])
    ] for i in range(12)]
    # F25 = F23-F24
    outData["Solar"]["losses"] = (outData["Solar"]["totalKWhPurchased"] -
                                  outData["Solar"]["totalKWhSales"])
    # F26 = E26
    outData["Solar"]["effectiveLossRate"] = outData["BAU"]["effectiveLossRate"]
    # F28 = (1-E5)*E28
    outData["Solar"]["resNonSolarKWhSold"] = fossilFraction * outData["BAU"][
        "resNonSolarKWhSold"]
    # F29 = E5*E28
    outData["Solar"]["solarResDemand"] = solarFraction * outData["BAU"][
        "resNonSolarKWhSold"]
    # F30 = F29-F27
    outData["Solar"]["solarResSold"] = outData["Solar"][
        "solarResDemand"] - outData["Solar"]["annualSolarGen"]
    # F31 = E31
    outData["Solar"]["nonResKWhSold"] = outData["BAU"]["nonResKWhSold"]
    # F32 = E9*F27
    outData["Solar"][
        "costSolarGen"] = solarLCoE * outData["Solar"]["annualSolarGen"]
    # F33 = E33
    outData["Solar"]["nonResRev"] = outData["BAU"]["nonResRev"]
    # F34 = E34
    outData["Solar"]["effectiveResRate"] = retailCost
    # F35 = E35*(1-E5)
    outData["Solar"][
        "resNonSolarRev"] = outData["BAU"]["resNonSolarRev"] * fossilFraction
    # F30*E34 = Solar revenue from selling at residential rate
    solarSoldRateRev = outData["Solar"]["solarResSold"] * outData["Solar"][
        "effectiveResRate"]
    # (E6+E7)*SUM(E16:P16)*E5 = Solar revenue from charges
    solarChargesRev = (customerServiceCharge +
                       solarServiceCharge) * customerMonths * solarFraction
    # F36 = F30*E34+(E6+E7)*SUM(E16:P16)*E5 = solarSoldRate + solarChargesRev
    outData["Solar"]["solarResRev"] = solarSoldRateRev + solarChargesRev
    # F37 = SUM(E48:E54)+SUM(E56:E62)-SUM(E65:E71) = E37, update after Form 7 model
    outData["Solar"]["nonPowerCosts"] = 0
    # F38 = F23-F25-F28-F30-E31
    outData["Solar"]["energyAllBal"] = 0
    # F39 = F36+E33+F35-F47-F72-E37
    outData["Solar"]["dollarAllBal"] = 0
    if (solarFraction > 0):
        # F41 = (F35)/(SUM(E16:P16)*(1-E5))
        outData["Solar"]["avgMonthlyBillNonSolarCus"] = outData["Solar"][
            "resNonSolarRev"] / (customerMonths * fossilFraction)
        # F42 = F30*E34/(SUM(E16:P16)*E5)+E6+E7
        outData["Solar"]["avgMonthlyBillSolarCus"] = outData["Solar"][
            "solarResSold"] * retailCost / (
                customerMonths *
                solarFraction) + customerServiceCharge + solarServiceCharge
        # F43 = (F27/(SUM(E16:P16)*E5))*E9
        outData["Solar"]["avgMonthlyBillSolarSolarCus"] = (
            outData["Solar"]["annualSolarGen"] /
            (customerMonths * solarFraction)) * solarLCoE
    else:
        outData["Solar"]["avgMonthlyBillNonSolarCus"] = 0
        outData["Solar"]["avgMonthlyBillSolarCus"] = 0
        outData["Solar"]["avgMonthlyBillSolarSolarCus"] = 0
    # Net Average Monthly Bill
    avgMonthlyBillSolarNet = outData["Solar"][
        "avgMonthlyBillSolarCus"] + outData["Solar"][
            "avgMonthlyBillSolarSolarCus"]
    outData["Solar"]["avgMonthlyBillSolarCus"] = avgMonthlyBillSolarNet
    # F45 = F63/F24, update after Form 7 model
    outData["Solar"]["costofService"] = 0
    ## Form 7 Model
    # E46
    outData["Solar"]["powerProExpense"] = outData["BAU"][
        "powerProExpense"] = float(inputDict.get("powerProExpense", 0))
    # E47 != F47
    outData["BAU"]["costPurchasedPower"] = float(
        inputDict.get("costPurchasedPower", 0))
    # E48
    outData["Solar"]["transExpense"] = outData["BAU"]["transExpense"] = float(
        inputDict.get("transExpense", 0))
    # E49
    outData["Solar"]["distriExpenseO"] = outData["BAU"][
        "distriExpenseO"] = float(inputDict.get("distriExpenseO", 0))
    # E50
    outData["Solar"]["distriExpenseM"] = outData["BAU"][
        "distriExpenseM"] = float(inputDict.get("distriExpenseM", 0))
    # E51
    outData["Solar"]["customerAccountExpense"] = outData["BAU"][
        "customerAccountExpense"] = float(
            inputDict.get("customerAccountExpense", 0))
    # E52
    outData["Solar"]["customerServiceExpense"] = outData["BAU"][
        "customerServiceExpense"] = float(
            inputDict.get("customerServiceExpense", 0))
    # E53
    outData["Solar"]["salesExpense"] = outData["BAU"]["salesExpense"] = float(
        inputDict.get("salesExpense", 0))
    # E54
    outData["Solar"]["adminGeneralExpense"] = outData["BAU"][
        "adminGeneralExpense"] = float(inputDict.get("adminGeneralExpense", 0))
    # E56
    outData["Solar"]["depreAmortiExpense"] = outData["BAU"][
        "depreAmortiExpense"] = float(inputDict.get("depreAmortiExpense", 0))
    # E57
    outData["Solar"]["taxExpensePG"] = outData["BAU"]["taxExpensePG"] = float(
        inputDict.get("taxExpensePG", 0))
    # E58
    outData["Solar"]["taxExpense"] = outData["BAU"]["taxExpense"] = float(
        inputDict.get("taxExpense", 0))
    # E59
    outData["Solar"]["interestLongTerm"] = outData["BAU"][
        "interestLongTerm"] = float(inputDict.get("interestLongTerm", 0))
    # E60
    outData["Solar"]["interestConstruction"] = outData["BAU"][
        "interestConstruction"] = float(
            inputDict.get("interestConstruction", 0))
    # E61
    outData["Solar"]["interestExpense"] = outData["BAU"][
        "interestExpense"] = float(inputDict.get("interestExpense", 0))
    # E62
    outData["Solar"]["otherDeductions"] = outData["BAU"][
        "otherDeductions"] = float(inputDict.get("otherDeductions", 0))
    # E65
    outData["Solar"]["nonOpMarginInterest"] = outData["BAU"][
        "nonOpMarginInterest"] = float(inputDict.get("nonOpMarginInterest", 0))
    # E66
    outData["Solar"]["fundsUsedConstruc"] = outData["BAU"][
        "fundsUsedConstruc"] = float(inputDict.get("fundsUsedConstruc", 0))
    # E67
    outData["Solar"]["incomeEquityInvest"] = outData["BAU"][
        "incomeEquityInvest"] = float(inputDict.get("incomeEquityInvest", 0))
    # E68
    outData["Solar"]["nonOpMarginOther"] = outData["BAU"][
        "nonOpMarginOther"] = float(inputDict.get("nonOpMarginOther", 0))
    # E69
    outData["Solar"]["genTransCapCredits"] = outData["BAU"][
        "genTransCapCredits"] = float(inputDict.get("genTransCapCredits", 0))
    # E70
    outData["Solar"]["otherCapCreditsPatroDivident"] = outData["BAU"][
        "otherCapCreditsPatroDivident"] = float(
            inputDict.get("otherCapCreditsPatroDivident", 0))
    # E71
    outData["Solar"]["extraItems"] = outData["BAU"]["extraItems"] = float(
        inputDict.get("extraItems", 0))
    # Calculation
    # E45 = SUM(E20:P20)+E10
    outData["BAU"]["operRevPatroCap"] = sum(
        [monthlyTotalRevenue[i][1]
         for i in range(12)]) + float(inputDict.get("otherElecRevenue", 0))
    # E55 = SUM(E46:E54)
    outData["BAU"]["totalOMExpense"] = float(inputDict.get("powerProExpense")) \
     + float(inputDict.get("costPurchasedPower")) \
     + float(inputDict.get("transExpense")) \
     + float(inputDict.get("distriExpenseO")) \
     + float(inputDict.get("distriExpenseM")) \
     + float(inputDict.get("customerAccountExpense")) \
     + float(inputDict.get("customerServiceExpense")) \
     + float(inputDict.get("salesExpense"))  \
     + float(inputDict.get("adminGeneralExpense"))
    # E63 = SUM(E55:E62)
    outData["BAU"]["totalCostElecService"] = outData["BAU"]["totalOMExpense"] \
     + float(inputDict.get("depreAmortiExpense"))\
     + float(inputDict.get("taxExpensePG"))\
     + float(inputDict.get("taxExpense"))\
     + float(inputDict.get("interestLongTerm"))\
     + float(inputDict.get("interestExpense"))\
     + float(inputDict.get("interestConstruction"))\
     + outData["BAU"]["otherDeductions"]
    # E64 = E45-E63
    outData["BAU"]["patCapOperMargins"] = outData["BAU"][
        "operRevPatroCap"] - outData["BAU"]["totalCostElecService"]
    # E72 = SUM(E64:E71)
    outData["BAU"]["patCapital"] = outData["BAU"]["patCapOperMargins"]\
     + float(inputDict.get("nonOpMarginInterest"))\
     + float(inputDict.get("fundsUsedConstruc"))\
     + float(inputDict.get("incomeEquityInvest"))\
     + float(inputDict.get("nonOpMarginOther"))\
     + float(inputDict.get("genTransCapCredits"))\
     + float(inputDict.get("otherCapCreditsPatroDivident"))\
     + float(inputDict.get("extraItems"))
    # F48 = E48-F27*E34+SUM(E16:P16)*E5*E7
    outData["Solar"]["operRevPatroCap"] = outData["BAU"][
        "operRevPatroCap"] - retailCost * outData["Solar"][
            "annualSolarGen"] + customerMonths * solarFraction * solarServiceCharge
    # F47 = (F23)*E8
    inputDict["costofPower"] = float(inputDict.get(
        "costPurchasedPower", 0)) / float(inputDict.get(
            "totalKWhPurchased", 0))
    outData["Solar"][
        "costPurchasedPower"] = outData["Solar"]["totalKWhPurchased"] * float(
            inputDict.get("costofPower", 0))
    inputDict["costofPower"] = round(inputDict["costofPower"], 3)
    # F55 = SUM(F46:F54)
    outData["Solar"]["totalOMExpense"] = outData["Solar"]["powerProExpense"]\
     + outData["Solar"]["costPurchasedPower"]\
     + outData["Solar"]["transExpense"]\
     + outData["Solar"]["distriExpenseO"]\
     + outData["Solar"]["distriExpenseM"]\
     + outData["Solar"]["customerAccountExpense"]\
     + outData["Solar"]["customerServiceExpense"]\
     + outData["Solar"]["salesExpense"]\
     + outData["Solar"]["adminGeneralExpense"]
    # F63 = E63
    outData["Solar"]["totalCostElecService"] = outData["Solar"]["totalOMExpense"]\
     + outData["Solar"]["depreAmortiExpense"]\
     + outData["Solar"]["taxExpensePG"]\
     + outData["Solar"]["taxExpense"]\
     + outData["Solar"]["interestLongTerm"]\
     + outData["Solar"]["interestConstruction"]\
     + outData["Solar"]["interestExpense"]\
     + outData["Solar"]["otherDeductions"]
    # F64 = F45 - F63
    outData["Solar"]["patCapOperMargins"] = outData["Solar"][
        "operRevPatroCap"] - outData["Solar"]["totalCostElecService"]
    # F72 = SUM(F64:F71)
    outData["Solar"]["patCapital"] = outData["Solar"]["patCapOperMargins"]\
     + outData["Solar"]["nonOpMarginInterest"]\
     + outData["Solar"]["fundsUsedConstruc"]\
     + outData["Solar"]["incomeEquityInvest"]\
     + outData["Solar"]["nonOpMarginOther"]\
     + outData["Solar"]["genTransCapCredits"]\
     + outData["Solar"]["otherCapCreditsPatroDivident"]\
     + outData["Solar"]["extraItems"]
    # E37 = SUM(E48:E54)+SUM(E56:E62)-SUM(E65:E71), update after Form 7 model
    outData["BAU"]["nonPowerCosts"] = outData["BAU"]["transExpense"] \
     + outData["BAU"]["distriExpenseO"] \
     + outData["BAU"]["distriExpenseM"] \
     + outData["BAU"]["customerAccountExpense"] \
     + outData["BAU"]["customerServiceExpense"] \
     + outData["BAU"]["salesExpense"] \
     + outData["BAU"]["adminGeneralExpense"] \
     + outData["BAU"]["depreAmortiExpense"] \
     + outData["BAU"]["taxExpensePG"] \
     + outData["BAU"]["taxExpense"] \
     + outData["BAU"]["interestLongTerm"] \
     + outData["BAU"]["interestConstruction"] \
     + outData["BAU"]["interestExpense"] \
     + outData["BAU"]["otherDeductions"] \
     - (outData["BAU"]["nonOpMarginInterest"] \
     + outData["BAU"]["fundsUsedConstruc"] \
     + outData["BAU"]["incomeEquityInvest"] \
     + outData["BAU"]["nonOpMarginOther"] \
     + outData["BAU"]["genTransCapCredits"] \
     + outData["BAU"]["otherCapCreditsPatroDivident"] \
     + outData["BAU"]["extraItems"])
    # E42 = E63/E24, update after Form 7 model
    outData["BAU"]["costofService"] = outData["BAU"][
        "totalCostElecService"] / outData["BAU"]["totalKWhSales"]
    # F37 = SUM(E48:E54)+SUM(E56:E62)-SUM(E65:E71) = E37, update after Form 7 model
    outData["Solar"]["nonPowerCosts"] = outData["BAU"]["nonPowerCosts"]
    # F42 = F63/F24, update after Form 7 model
    outData["Solar"]["costofService"] = outData["Solar"][
        "totalCostElecService"] / outData["Solar"]["totalKWhSales"]
    # Stdout/stderr.
    outData["stdout"] = "Success"
    outData["stderr"] = ""
    return outData
Exemple #10
0
def work(modelDir, inputDict):
	''' Run the model in its directory. '''
	inputDict["climateName"] = zipCodeToClimateName(inputDict["zipCode"])
	shutil.copy(pJoin(__neoMetaModel__._omfDir, "data", "Climate", inputDict["climateName"] + ".tmy2"),
		pJoin(modelDir, "climate.tmy2"))
	# Set up SAM data structures.
	ssc = nrelsam2013.SSCAPI()
	dat = ssc.ssc_data_create()
	# Required user inputs.
	ssc.ssc_data_set_string(dat, "file_name", modelDir + "/climate.tmy2")
	# TODO: FIX THIS!!!! IT SHOULD BE AVGSYS*PEN*RESCUSTOMERS
	ssc.ssc_data_set_number(dat, "system_size", float(inputDict["systemSize"]))
	# SAM options where we take defaults.
	ssc.ssc_data_set_number(dat, "derate", 0.97)
	ssc.ssc_data_set_number(dat, "track_mode", 0)
	ssc.ssc_data_set_number(dat, "azimuth", 180)
	ssc.ssc_data_set_number(dat, "tilt_eq_lat", 1)
	# Run PV system simulation.
	mod = ssc.ssc_module_create("pvwattsv1")
	ssc.ssc_module_exec(mod, dat)
	# Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
	startDateTime = "2013-01-01 00:00:00 UTC"
	# Timestamp output.
	outData = {}
	outData["timeStamps"] = [dt.datetime.strftime(
		dt.datetime.strptime(startDateTime[0:19],"%Y-%m-%d %H:%M:%S") +
		dt.timedelta(**{"hours":x}),"%Y-%m-%d %H:%M:%S") + " UTC" for x in range(int(8760))]
	# Geodata output.
	outData["city"] = ssc.ssc_data_get_string(dat, "city")
	outData["state"] = ssc.ssc_data_get_string(dat, "state")
	outData["lat"] = ssc.ssc_data_get_number(dat, "lat")
	outData["lon"] = ssc.ssc_data_get_number(dat, "lon")
	outData["elev"] = ssc.ssc_data_get_number(dat, "elev")
	# Weather output.
	outData["climate"] = {}
	outData["climate"]["Global Horizontal Radiation (W/m^2)"] = ssc.ssc_data_get_array(dat, "gh")
	outData["climate"]["Plane of Array Irradiance (W/m^2)"] = ssc.ssc_data_get_array(dat, "poa")
	outData["climate"]["Ambient Temperature (F)"] = ssc.ssc_data_get_array(dat, "tamb")
	outData["climate"]["Cell Temperature (F)"] = ssc.ssc_data_get_array(dat, "tcell")
	outData["climate"]["Wind Speed (m/s)"] = ssc.ssc_data_get_array(dat, "wspd")
	# Power generation.
	outData["powerOutputAc"] = ssc.ssc_data_get_array(dat, "ac")
	# Monthly aggregation outputs.
	months = {"Jan":0,"Feb":1,"Mar":2,"Apr":3,"May":4,"Jun":5,"Jul":6,"Aug":7,"Sep":8,"Oct":9,"Nov":10,"Dec":11}
	totMonNum = lambda x:sum([z for (y,z) in zip(outData["timeStamps"], outData["powerOutputAc"]) if y.startswith(startDateTime[0:4] + "-{0:02d}".format(x+1))])
	outData["monthlyGeneration"] = [[a, roundSig(totMonNum(b),2)] for (a,b) in sorted(months.items(), key=lambda x:x[1])]
	monthlyNoConsumerServedSales = []
	monthlyKWhSold = []
	monthlyRevenue = []
	totalKWhSold = []
	totalRevenue = []
	for key in inputDict:
		# MAYBEFIX: data in list may not be ordered by month.
		if key.endswith("Sale"):
			monthlyNoConsumerServedSales.append([key[:3].title(),float(inputDict.get(key, 0))])
		elif key.endswith("KWh"):# the order of calculation matters
			monthlyKWhSold.append([key[:3].title(),float(inputDict.get(key, 0))])
		elif key.endswith("Rev"):
			monthlyRevenue.append([key[:3].title(),float(inputDict.get(key, 0))])
		elif key.endswith("KWhT"):
			totalKWhSold.append([key[:3].title(),float(inputDict.get(key, 0))])
		elif key.endswith("RevT"):
			totalRevenue.append([key[:3].title(),float(inputDict.get(key, 0))])
	outData["monthlyNoConsumerServedSales"] = sorted(monthlyNoConsumerServedSales, key=lambda x:months[x[0]])
	outData["monthlyKWhSold"] = sorted(monthlyKWhSold, key=lambda x:months[x[0]])
	outData["monthlyRevenue"] = sorted(monthlyRevenue, key=lambda x:months[x[0]])
	outData["totalKWhSold"] = sorted(totalKWhSold, key=lambda x:months[x[0]])
	outData["totalRevenue"] = sorted(totalRevenue, key=lambda x:months[x[0]])
	outData["totalGeneration"] = [[sorted(months.items(), key=lambda x:x[1])[i][0], outData["monthlyGeneration"][i][1]*outData["monthlyNoConsumerServedSales"][i][1]*(float(inputDict.get("resPenetration", 5))/100/1000)] for i in range(12)]
	outData["totalSolarSold"] = [[sorted(months.items(), key=lambda x:x[1])[i][0], outData["totalKWhSold"][i][1] - outData["totalGeneration"][i][1]] for i in range(12)]
	##################
	# TODO: add retailCost to the calculation.
	##################
	## Flow Diagram Calculations, and order of calculation matters
	# BAU case
	outData["BAU"] = {}
	# E23 = E11
	outData["BAU"]["totalKWhPurchased"] = float(inputDict.get("totalKWhPurchased", 1))
	# E24 = SUM(E19:P19)
	outData["BAU"]["totalKWhSales"] = sum([x[1] for x in totalKWhSold])
	# E25 = E23-E24
	outData["BAU"]["losses"] = float(inputDict.get("totalKWhPurchased", 0)) - sum([totalKWhSold[i][1] for i in range(12)])
	# E26 = E25/E23
	outData["BAU"]["effectiveLossRate"] = outData["BAU"]["losses"] / outData["BAU"]["totalKWhPurchased"]
	# E27 = 0
	outData["BAU"]["annualSolarGen"] = 0
	# E28 = SUM(E17:P17)
	outData["BAU"]["resNonSolarKWhSold"] = sum([monthlyKWhSold[i][1] for i in range(12)])
	# E29 = 0
	outData["BAU"]["solarResDemand"] = 0
	# E30 = 0
	outData["BAU"]["solarResSold"] = 0
	# E31 = E24-E28
	outData["BAU"]["nonResKWhSold"] = outData["BAU"]["totalKWhSales"] - outData["BAU"]["resNonSolarKWhSold"]
	# E32 = 0
	outData["BAU"]["costSolarGen"] = 0
	# E33 = SUM(E20:P20)-SUM(E18:P18)+E10
	outData["BAU"]["nonResRev"] = sum([totalRevenue[i][1] for i in range(12)]) - sum([monthlyRevenue[i][1] for i in range(12)]) + float(inputDict.get("otherElecRevenue"))
	# E34 = (SUM(E18:P18)-SUM(E16:P16)*E6)/SUM(E17:P17)
	outData["BAU"]["effectiveResRate"] = (sum ([monthlyRevenue[i][1] for i in range(12)]) - sum([monthlyNoConsumerServedSales[i][1] for i in range(12)])*float(inputDict.get("customServiceCharge", 0)))/sum([monthlyKWhSold[i][1] for i in range(12)])
	# E35 = E34*E28+SUM(E16:P16)*E6
	outData["BAU"]["resNonSolarRev"] = outData["BAU"]["effectiveResRate"] * outData["BAU"]["resNonSolarKWhSold"] + sum([monthlyNoConsumerServedSales[i][1] for i in range(12)])*float(inputDict.get("customServiceCharge", 0))
	# E36 = E30*E34
	outData["BAU"]["solarResRev"] = 0
	# E37 = SUM(E48:E54)+SUM(E56:E62)-SUM(E65:E71), update after Form 7 model
	outData["BAU"]["nonPowerCosts"] = 0
	# E38 = E23-E25-E28-E30-E31
	outData["BAU"]["energyAllBal"] = 0
	# E39 = E36+E33+E35-E47-E72-E37
	outData["BAU"]["dollarAllBal"] = 0
	# E40 = 0
	outData["BAU"]["avgMonthlyBillSolarCus"] = 0
	# E41 = E35/SUM(E16:P16)
	avgCustomerCount = (sum([monthlyNoConsumerServedSales[i][1] for i in range(12)])/12)
	outData["BAU"]["avgMonthlyBillNonSolarCus"] = outData["BAU"]["resNonSolarRev"] / sum([monthlyNoConsumerServedSales[i][1] for i in range(12)])
	# E42 = E63/E24, update after Form 7 model
	outData["BAU"]["costofService"] = 0
	# Solar case
	outData["Solar"] = {}
	# F27 = SUM(E15:P15)
	outData["Solar"]["annualSolarGen"] = sum([outData["totalGeneration"][i][1] for i in range(12)])
	# F24 = E24-F27
	outData["Solar"]["totalKWhSales"] = sum([totalKWhSold[i][1] for i in range(12)]) - outData["Solar"]["annualSolarGen"]
	# F23 =F24/(1-E26)
	outData["Solar"]["totalKWhPurchased"] = outData["Solar"]["totalKWhSales"]/ (1-outData["BAU"]["effectiveLossRate"])
	outData["totalsolarmonthly"] = [[sorted(months.items(), key=lambda x:x[1])[i][0], outData["totalSolarSold"][i][1] / (1-outData["BAU"]["effectiveLossRate"])] for i in range(12)]
	# F25 = F23-F24
	outData["Solar"]["losses"] = (outData["Solar"]["totalKWhPurchased"] - outData["Solar"]["totalKWhSales"])
	# F26 = E26
	outData["Solar"]["effectiveLossRate"] = outData["BAU"]["effectiveLossRate"]
	# F28 = (1-E5)*E28
	outData["Solar"]["resNonSolarKWhSold"] = (1-float(inputDict.get("resPenetration", 0))/100)*outData["BAU"]["resNonSolarKWhSold"]
	# F29 = E5*E28
	outData["Solar"]["solarResDemand"] = float(inputDict.get("resPenetration", 0))/100*outData["BAU"]["resNonSolarKWhSold"]
	# F30 = F29-F27
	outData["Solar"]["solarResSold"] = outData["Solar"]["solarResDemand"] - outData["Solar"]["annualSolarGen"]
	# F31 = E31
	outData["Solar"]["nonResKWhSold"] = outData["BAU"]["nonResKWhSold"]
	# F32 = E9*F27
	outData["Solar"]["costSolarGen"] = float(inputDict.get("solarLCoE", 0.07))*outData["Solar"]["annualSolarGen"]
	# F33 = E33
	outData["Solar"]["nonResRev"] = outData["BAU"]["nonResRev"]
	# F34 = E34
	outData["Solar"]["effectiveResRate"] = outData["BAU"]["effectiveResRate"]
	# F35 = E35*(1-E5)
	outData["Solar"]["resNonSolarRev"] = outData["BAU"]["resNonSolarRev"] * (1 - float(inputDict.get("resPenetration", 0.05))/100)
	# F30*E34 = Solar revenue from selling at residential rate
	solarSoldRateRev = outData["Solar"]["solarResSold"] * outData["Solar"]["effectiveResRate"]
	# (E6+E7)*SUM(E16:P16)*E5 = Solar revenue from charges
	solarChargesRev = (float(inputDict.get("customServiceCharge", 0))+float(inputDict.get("solarServiceCharge", 0)))*sum([monthlyNoConsumerServedSales[i][1] for i in range(12)])*float(inputDict.get("resPenetration", 0.05))/100
	# F36 = F30*E34+(E6+E7)*SUM(E16:P16)*E5 = solarSoldRate + solarChargesRev
	outData["Solar"]["solarResRev"] = solarSoldRateRev + solarChargesRev
	# F37 = SUM(E48:E54)+SUM(E56:E62)-SUM(E65:E71) = E37, update after Form 7 model
	outData["Solar"]["nonPowerCosts"] = 0
	# F38 = F23-F25-F28-F30-E31
	outData["Solar"]["energyAllBal"] = 0
	# F39 = F36+E33+F35-F47-F72-E37
	outData["Solar"]["dollarAllBal"] = 0
	if (float(inputDict.get("resPenetration", 0.05)) > 0):
		# F41 = (F35)/(SUM(E16:P16)*(1-E5))
		outData["Solar"]["avgMonthlyBillNonSolarCus"] = outData["Solar"]["resNonSolarRev"] / (sum([monthlyNoConsumerServedSales[i][1] for i in range(12)])* (1 - float(inputDict.get("resPenetration", 0.05))/100))
		# F42 = F30*E34/(SUM(E16:P16)*E5)+E6+E7
		outData["Solar"]["avgMonthlyBillSolarCus"] = outData["Solar"]["solarResSold"] * outData["BAU"]["effectiveResRate"] / (sum([monthlyNoConsumerServedSales[i][1] for i in range(12)]) * float(inputDict.get("resPenetration", 0.05))/100) + float(inputDict.get("customServiceCharge", 0))+float(inputDict.get("solarServiceCharge", 0))
		# F43 = (F27/(SUM(E16:P16)*E5))*E9
		outData["Solar"]["avgMonthlyBillSolarSolarCus"] = (outData["Solar"]["annualSolarGen"] / (sum([monthlyNoConsumerServedSales[i][1] for i in range(12)]) * float(inputDict.get("resPenetration", 0.05))/100)) * float(inputDict.get("solarLCoE", 0.07))
	else:
		outData["Solar"]["avgMonthlyBillNonSolarCus"] = 0
		outData["Solar"]["avgMonthlyBillSolarCus"] = 0
		outData["Solar"]["avgMonthlyBillSolarSolarCus"] = 0
	# Net Average Monthly Bill
	avgMonthlyBillSolarNet = outData["Solar"]["avgMonthlyBillSolarCus"] + outData["Solar"]["avgMonthlyBillSolarSolarCus"]
	outData["Solar"]["avgMonthlyBillSolarCus"] = avgMonthlyBillSolarNet
	# F45 = F63/F24, update after Form 7 model
	outData["Solar"]["costofService"] = 0
	## Form 7 Model
	# E46
	outData["Solar"]["powerProExpense"] = outData["BAU"]["powerProExpense"] = float(inputDict.get("powerProExpense", 0))
	# E47 != F47
	outData["BAU"]["costPurchasedPower"] = float(inputDict.get("costPurchasedPower", 0))
	# E48
	outData["Solar"]["transExpense"] = outData["BAU"]["transExpense"] = float(inputDict.get("transExpense", 0))
	# E49
	outData["Solar"]["distriExpenseO"] = outData["BAU"]["distriExpenseO"] = float(inputDict.get("distriExpenseO", 0))
	# E50
	outData["Solar"]["distriExpenseM"] = outData["BAU"]["distriExpenseM"] = float(inputDict.get("distriExpenseM", 0))
	# E51
	outData["Solar"]["customerAccountExpense"] = outData["BAU"]["customerAccountExpense"] = float(inputDict.get("customerAccountExpense", 0))
	# E52
	outData["Solar"]["customerServiceExpense"] = outData["BAU"]["customerServiceExpense"] = float(inputDict.get("customerServiceExpense", 0))
	# E53
	outData["Solar"]["salesExpense"] = outData["BAU"]["salesExpense"] = float(inputDict.get("salesExpense", 0))
	# E54
	outData["Solar"]["adminGeneralExpense"] = outData["BAU"]["adminGeneralExpense"] = float(inputDict.get("adminGeneralExpense", 0))
	# E56
	outData["Solar"]["depreAmortiExpense"] = outData["BAU"]["depreAmortiExpense"] = float(inputDict.get("depreAmortiExpense", 0))
	# E57
	outData["Solar"]["taxExpensePG"] = outData["BAU"]["taxExpensePG"] = float(inputDict.get("taxExpensePG", 0))
	# E58
	outData["Solar"]["taxExpense"] = outData["BAU"]["taxExpense"] = float(inputDict.get("taxExpense", 0))
	# E59
	outData["Solar"]["interestLongTerm"] = outData["BAU"]["interestLongTerm"] = float(inputDict.get("interestLongTerm", 0))
	# E60
	outData["Solar"]["interestConstruction"] = outData["BAU"]["interestConstruction"] = float(inputDict.get("interestConstruction", 0))
	# E61
	outData["Solar"]["interestExpense"] = outData["BAU"]["interestExpense"] = float(inputDict.get("interestExpense", 0))
	# E62
	outData["Solar"]["otherDeductions"] = outData["BAU"]["otherDeductions"] = float(inputDict.get("otherDeductions", 0))
	# E65
	outData["Solar"]["nonOpMarginInterest"] = outData["BAU"]["nonOpMarginInterest"] = float(inputDict.get("nonOpMarginInterest", 0))
	# E66
	outData["Solar"]["fundsUsedConstruc"] = outData["BAU"]["fundsUsedConstruc"] = float(inputDict.get("fundsUsedConstruc", 0))
	# E67
	outData["Solar"]["incomeEquityInvest"] = outData["BAU"]["incomeEquityInvest"] = float(inputDict.get("incomeEquityInvest", 0))
	# E68
	outData["Solar"]["nonOpMarginOther"] = outData["BAU"]["nonOpMarginOther"] = float(inputDict.get("nonOpMarginOther", 0))
	# E69
	outData["Solar"]["genTransCapCredits"] = outData["BAU"]["genTransCapCredits"] = float(inputDict.get("genTransCapCredits", 0))
	# E70
	outData["Solar"]["otherCapCreditsPatroDivident"] = outData["BAU"]["otherCapCreditsPatroDivident"] = float(inputDict.get("otherCapCreditsPatroDivident", 0))
	# E71
	outData["Solar"]["extraItems"] = outData["BAU"]["extraItems"] = float(inputDict.get("extraItems", 0))
	# Calculation
	# E45 = SUM(E20:P20)+E10
	outData["BAU"]["operRevPatroCap"] = sum([totalRevenue[i][1] for i in range(12)])+float(inputDict.get("otherElecRevenue", 0))
	# E55 = SUM(E46:E54)
	outData["BAU"]["totalOMExpense"] = float(inputDict.get("powerProExpense")) \
		+ float(inputDict.get("costPurchasedPower")) \
		+ float(inputDict.get("transExpense")) \
		+ float(inputDict.get("distriExpenseO")) \
		+ float(inputDict.get("distriExpenseM")) \
		+ float(inputDict.get("customerAccountExpense")) \
		+ float(inputDict.get("customerServiceExpense")) \
		+ float(inputDict.get("salesExpense"))  \
		+ float(inputDict.get("adminGeneralExpense"))
	# E63 = SUM(E55:E62)
	outData["BAU"]["totalCostElecService"] = outData["BAU"]["totalOMExpense"] \
		+ float(inputDict.get("depreAmortiExpense"))\
		+ float(inputDict.get("taxExpensePG"))\
		+ float(inputDict.get("taxExpense"))\
		+ float(inputDict.get("interestLongTerm"))\
		+ float(inputDict.get("interestExpense"))\
		+ float(inputDict.get("interestConstruction"))\
		+ outData["BAU"]["otherDeductions"]
	# E64 = E45-E63
	outData["BAU"]["patCapOperMargins"] = outData["BAU"]["operRevPatroCap"] - outData["BAU"]["totalCostElecService"]
	# E72 = SUM(E64:E71)
	outData["BAU"]["patCapital"] = outData["BAU"]["patCapOperMargins"]\
		+ float(inputDict.get("nonOpMarginInterest"))\
		+ float(inputDict.get("fundsUsedConstruc"))\
		+ float(inputDict.get("incomeEquityInvest"))\
		+ float(inputDict.get("nonOpMarginOther"))\
		+ float(inputDict.get("genTransCapCredits"))\
		+ float(inputDict.get("otherCapCreditsPatroDivident"))\
		+ float(inputDict.get("extraItems"))
	# F48 = E48-F27*E34+SUM(E16:P16)*E5*E7
	outData["Solar"]["operRevPatroCap"] = outData["BAU"]["operRevPatroCap"] - outData["BAU"]["effectiveResRate"]*outData["Solar"]["annualSolarGen"] + sum([monthlyNoConsumerServedSales[i][1] for i in range(12)])*float(inputDict.get("resPenetration", 0.05))/100*float(inputDict.get("solarServiceCharge", 0))
	# F47 = (F23)*E8
	inputDict["costofPower"] = float(inputDict.get("costPurchasedPower", 0)) /  float(inputDict.get("totalKWhPurchased", 0))
	outData["Solar"]["costPurchasedPower"] = outData["Solar"]["totalKWhPurchased"] * float(inputDict.get("costofPower", 0))
	inputDict["costofPower"] = round(inputDict["costofPower"],3)
	# F55 = SUM(F46:F54)
	outData["Solar"]["totalOMExpense"] = outData["Solar"]["powerProExpense"]\
		+ outData["Solar"]["costPurchasedPower"]\
		+ outData["Solar"]["transExpense"]\
		+ outData["Solar"]["distriExpenseO"]\
		+ outData["Solar"]["distriExpenseM"]\
		+ outData["Solar"]["customerAccountExpense"]\
		+ outData["Solar"]["customerServiceExpense"]\
		+ outData["Solar"]["salesExpense"]\
		+ outData["Solar"]["adminGeneralExpense"]
	# F63 = E63
	outData["Solar"]["totalCostElecService"] = outData["Solar"]["totalOMExpense"]\
		+ outData["Solar"]["depreAmortiExpense"]\
		+ outData["Solar"]["taxExpensePG"]\
		+ outData["Solar"]["taxExpense"]\
		+ outData["Solar"]["interestLongTerm"]\
		+ outData["Solar"]["interestConstruction"]\
		+ outData["Solar"]["interestExpense"]\
		+ outData["Solar"]["otherDeductions"]
	# F64 = F45 - F63
	outData["Solar"]["patCapOperMargins"] = outData["Solar"]["operRevPatroCap"] - outData["Solar"]["totalCostElecService"]
	# F72 = SUM(F64:F71)
	outData["Solar"]["patCapital"] = outData["Solar"]["patCapOperMargins"]\
		+ outData["Solar"]["nonOpMarginInterest"]\
		+ outData["Solar"]["fundsUsedConstruc"]\
		+ outData["Solar"]["incomeEquityInvest"]\
		+ outData["Solar"]["nonOpMarginOther"]\
		+ outData["Solar"]["genTransCapCredits"]\
		+ outData["Solar"]["otherCapCreditsPatroDivident"]\
		+ outData["Solar"]["extraItems"]
	# E37 = SUM(E48:E54)+SUM(E56:E62)-SUM(E65:E71), update after Form 7 model
	outData["BAU"]["nonPowerCosts"] = outData["BAU"]["transExpense"] \
		+ outData["BAU"]["distriExpenseO"] \
		+ outData["BAU"]["distriExpenseM"] \
		+ outData["BAU"]["customerAccountExpense"] \
		+ outData["BAU"]["customerServiceExpense"] \
		+ outData["BAU"]["salesExpense"] \
		+ outData["BAU"]["adminGeneralExpense"] \
		+ outData["BAU"]["depreAmortiExpense"] \
		+ outData["BAU"]["taxExpensePG"] \
		+ outData["BAU"]["taxExpense"] \
		+ outData["BAU"]["interestLongTerm"] \
		+ outData["BAU"]["interestConstruction"] \
		+ outData["BAU"]["interestExpense"] \
		+ outData["BAU"]["otherDeductions"] \
		- (outData["BAU"]["nonOpMarginInterest"] \
		+ outData["BAU"]["fundsUsedConstruc"] \
		+ outData["BAU"]["incomeEquityInvest"] \
		+ outData["BAU"]["nonOpMarginOther"] \
		+ outData["BAU"]["genTransCapCredits"] \
		+ outData["BAU"]["otherCapCreditsPatroDivident"] \
		+ outData["BAU"]["extraItems"])
	# E42 = E63/E24, update after Form 7 model
	outData["BAU"]["costofService"] = outData["BAU"]["totalCostElecService"] / outData["BAU"]["totalKWhSales"]
	# F37 = SUM(E48:E54)+SUM(E56:E62)-SUM(E65:E71) = E37, update after Form 7 model
	outData["Solar"]["nonPowerCosts"] = outData["BAU"]["nonPowerCosts"]
	# F42 = F63/F24, update after Form 7 model
	outData["Solar"]["costofService"] = outData["Solar"]["totalCostElecService"] / outData["Solar"]["totalKWhSales"]
	# Stdout/stderr.
	outData["stdout"] = "Success"
	outData["stderr"] = ""
	return outData
Exemple #11
0
def work(modelDir, inputDict):
    #plotly imports. Here for now so web server starts.
    import plotly
    # from plotly import __version__
    # from plotly.offline import download_plotlyjs, plot
    # from plotly import tools
    import plotly.graph_objs as go
    # Copy specific climate data into model directory
    inputDict["climateName"] = zipCodeToClimateName(inputDict["zipCode"])
    shutil.copy(
        pJoin(__neoMetaModel__._omfDir, "data", "Climate",
              inputDict["climateName"] + ".tmy2"),
        pJoin(modelDir, "climate.tmy2"))
    # Set up SAM data structures.
    ssc = nrelsam2013.SSCAPI()
    dat = ssc.ssc_data_create()
    # Required user inputs.
    ssc.ssc_data_set_string(dat, "file_name", modelDir + "/climate.tmy2")
    ssc.ssc_data_set_number(dat, "system_size", float(inputDict["systemSize"]))
    ssc.ssc_data_set_number(dat, "derate",
                            0.01 * float(inputDict["nonInverterEfficiency"]))
    ssc.ssc_data_set_number(dat, "track_mode",
                            float(inputDict["trackingMode"]))
    ssc.ssc_data_set_number(dat, "azimuth", float(inputDict["azimuth"]))
    # Advanced inputs with defaults.
    if (inputDict.get("tilt", 0) == "-"):
        tilt_eq_lat = 1.0
        manualTilt = 0.0
    else:
        tilt_eq_lat = 0.0
        manualTilt = float(inputDict.get("tilt", 0))
    ssc.ssc_data_set_number(dat, "tilt_eq_lat", tilt_eq_lat)
    ssc.ssc_data_set_number(dat, "tilt", manualTilt)
    ssc.ssc_data_set_number(dat, "rotlim", float(inputDict["rotlim"]))
    ssc.ssc_data_set_number(dat, "gamma", -1 * float(inputDict["gamma"]))
    ssc.ssc_data_set_number(dat, "inv_eff",
                            0.01 * float(inputDict["inverterEfficiency"]))
    ssc.ssc_data_set_number(dat, "w_stow", float(inputDict["w_stow"]))
    # Complicated optional inputs that we could enable later.
    # ssc.ssc_data_set_array(dat, 'shading_hourly', ...) 	# Hourly beam shading factors
    # ssc.ssc_data_set_matrix(dat, 'shading_mxh', ...) 		# Month x Hour beam shading factors
    # ssc.ssc_data_set_matrix(dat, 'shading_azal', ...) 	# Azimuth x altitude beam shading factors
    # ssc.ssc_data_set_number(dat, 'shading_diff', ...) 	# Diffuse shading factor
    # ssc.ssc_data_set_number(dat, 'enable_user_poa', ...)	# Enable user-defined POA irradiance input = 0 or 1
    # ssc.ssc_data_set_array(dat, 'user_poa', ...) 			# User-defined POA irradiance in W/m2
    # ssc.ssc_data_set_number(dat, 'tilt', 999)
    # ssc.ssc_data_set_number(dat, "t_noct", float(inputDict["t_noct"]))
    # ssc.ssc_data_set_number(dat, "t_ref", float(inputDict["t_ref"]))
    # ssc.ssc_data_set_number(dat, "fd", float(inputDict["fd"]))
    # ssc.ssc_data_set_number(dat, "i_ref", float(inputDict["i_ref"]))
    # ssc.ssc_data_set_number(dat, "poa_cutin", float(inputDict["poa_cutin"]))
    # Run PV system simulation.
    mod = ssc.ssc_module_create("pvwattsv1")
    ssc.ssc_module_exec(mod, dat)
    # Setting options for start time.
    simLengthUnits = inputDict.get("simLengthUnits", "")
    simStartDate = inputDict["simStartDate"]
    # Set the timezone to be UTC, it won't affect calculation and display, relative offset handled in pvWatts.html
    startDateTime = simStartDate + " 00:00:00 UTC"
    # Set aggregation function constants.
    agg = lambda x, y: _aggData(x, y, inputDict["simStartDate"],
                                int(inputDict["simLength"]), inputDict[
                                    "simLengthUnits"], ssc, dat)
    avg = lambda x: sum(x) / len(x)
    # Timestamp output.
    outData = {}
    outData["timeStamps"] = [
        datetime.datetime.strftime(
            datetime.datetime.strptime(startDateTime[0:19],
                                       "%Y-%m-%d %H:%M:%S") +
            datetime.timedelta(**{simLengthUnits: x}), "%Y-%m-%d %H:%M:%S") +
        " UTC" for x in range(int(inputDict["simLength"]))
    ]
    # Geodata output.
    outData["city"] = ssc.ssc_data_get_string(dat, "city")
    outData["state"] = ssc.ssc_data_get_string(dat, "state")
    outData["lat"] = ssc.ssc_data_get_number(dat, "lat")
    outData["lon"] = ssc.ssc_data_get_number(dat, "lon")
    outData["elev"] = ssc.ssc_data_get_number(dat, "elev")
    # Weather output.
    outData["climate"] = {}
    outData["climate"]["Plane of Array Irradiance (W/m^2)"] = agg("poa", avg)
    outData["climate"]["Beam Normal Irradiance (W/m^2)"] = agg("dn", avg)
    outData["climate"]["Diffuse Irradiance (W/m^2)"] = agg("df", avg)
    outData["climate"]["Ambient Temperature (F)"] = agg("tamb", avg)
    outData["climate"]["Cell Temperature (F)"] = agg("tcell", avg)
    outData["climate"]["Wind Speed (m/s)"] = agg("wspd", avg)
    # Power generation.
    outData["Consumption"] = {}
    outData["Consumption"]["Power"] = [x for x in agg("ac", avg)]
    outData["Consumption"]["Losses"] = [0 for x in agg("ac", avg)]
    outData["Consumption"]["DG"] = agg("ac", avg)

    #Plotly data sets for power generation graphs
    convertedDateStrings = [
        datetime.datetime.strptime(x, "%Y-%m-%d %H:%M:%S %Z")
        for x in outData["timeStamps"]
    ]
    powerGeneration = go.Scatter(x=convertedDateStrings,
                                 y=outData["Consumption"]["Power"],
                                 line=dict(color=('red')),
                                 name="Power Generated")

    chartInverter = None
    if float(inputDict["inverterSize"]) == 0:
        chartInverter = float(inputDict["systemSize"])
    else:
        chartInverter = float(inputDict["inverterSize"])

    panelsNameplate = go.Scatter(x=convertedDateStrings,
                                 y=[
                                     float(inputDict['systemSize']) * 1000
                                     for x in range(len(convertedDateStrings))
                                 ],
                                 line=dict(dash='dash', color='orange'),
                                 name="Panels Nameplate")
    inverterNameplate = go.Scatter(
        x=convertedDateStrings,
        y=[chartInverter * 1000 for x in range(len(convertedDateStrings))],
        line=dict(dash='dash', color='orange'),
        name="inverter Nameplate")

    #Set Power generation plotly layout
    powerGenerationLayout = go.Layout(width=1000,
                                      height=375,
                                      xaxis=dict(showgrid=False, ),
                                      legend=dict(x=0, y=1.25,
                                                  orientation="h"))
    #Combine all datasets for plotly graph
    powerGenerationData = [powerGeneration, panelsNameplate, inverterNameplate]
    #Example updating go object
    powerGenerationLayout['yaxis'].update(title='Power (W-AC)')
    #fig = go.Figure(data=powerGenerationData, layout=powerGenerationLayout)
    #inlinePlot = plotly.offline.plot(fig, include_plotlyjs=False, output_type='div')
    #outData["plotlyDiv"] = html.escape(json.dumps(inlinePlot, cls=plotly.utils.PlotlyJSONEncoder))

    #Plotly power generation outputs
    outData["powerGenerationData"] = json.dumps(
        powerGenerationData, cls=plotly.utils.PlotlyJSONEncoder)
    outData["powerGenerationLayout"] = json.dumps(
        powerGenerationLayout, cls=plotly.utils.PlotlyJSONEncoder)

    #Irradiance plotly data
    poaIrradiance = go.Scatter(
        x=convertedDateStrings,
        y=outData["climate"]["Plane of Array Irradiance (W/m^2)"],
        line=dict(color='yellow'),
        name="Plane of Array Irradiance (W/m^2)")
    beamNormalIrradiance = go.Scatter(
        x=convertedDateStrings,
        y=outData["climate"]["Beam Normal Irradiance (W/m^2)"],
        line=dict(color='gold'),
        name="Beam Normal Irradiance (W/m^2)")
    diffuseIrradiance = go.Scatter(
        x=convertedDateStrings,
        y=outData["climate"]["Diffuse Irradiance (W/m^2)"],
        line=dict(color='lemonchiffon'),
        name="Diffuse Irradiance (W/m^2)")
    irradianceData = [poaIrradiance, beamNormalIrradiance, diffuseIrradiance]

    #Set Power generation plotly layout
    irradianceLayout = go.Layout(width=1000,
                                 height=375,
                                 xaxis=dict(showgrid=False, ),
                                 yaxis=dict(title="Climate Units", ),
                                 legend=dict(x=0, y=1.25, orientation="h"))
    outData["irradianceData"] = json.dumps(irradianceData,
                                           cls=plotly.utils.PlotlyJSONEncoder)
    outData["irradianceLayout"] = json.dumps(
        irradianceLayout, cls=plotly.utils.PlotlyJSONEncoder)

    #Other Climate Variables plotly data
    ambientTemperature = go.Scatter(
        x=convertedDateStrings,
        y=outData["climate"]["Ambient Temperature (F)"],
        line=dict(color='dimgray'),
        name="Ambient Temperature (F)")
    cellTemperature = go.Scatter(x=convertedDateStrings,
                                 y=outData["climate"]["Cell Temperature (F)"],
                                 line=dict(color='gainsboro'),
                                 name="Cell Temperature (F)")
    windSpeed = go.Scatter(x=convertedDateStrings,
                           y=outData["climate"]["Wind Speed (m/s)"],
                           line=dict(color='darkgray'),
                           name="Wind Speed (m/s)")
    otherClimateData = [ambientTemperature, cellTemperature, windSpeed]

    #Set Power generation plotly layout
    otherClimateLayout = go.Layout(width=1000,
                                   height=375,
                                   xaxis=dict(showgrid=False, ),
                                   yaxis=dict(title="Climate Units", ),
                                   legend=dict(x=0, y=1.25, orientation="h"))
    outData["otherClimateData"] = json.dumps(
        otherClimateData, cls=plotly.utils.PlotlyJSONEncoder)
    outData["otherClimateLayout"] = json.dumps(
        otherClimateLayout, cls=plotly.utils.PlotlyJSONEncoder)
    # Stdout/stderr.
    outData["stdout"] = "Success"
    outData["stderr"] = ""
    return outData