Esempio n. 1
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
Esempio n. 2
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.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, b'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, b'track_mode',
                            float(inputDict.get('trackingMode', 0)))
    ssc.ssc_data_set_number(dat, b'azimuth',
                            float(inputDict.get('azimuth', 180)))
    # Advanced inputs with defaults.
    ssc.ssc_data_set_number(dat, b'rotlim', float(inputDict.get('rotlim', 45)))
    ssc.ssc_data_set_number(dat, b'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, b'tilt', manualTilt)
    ssc.ssc_data_set_number(dat, b'tilt_eq_lat', tilt_eq_lat)
    # Run PV system simulation.
    mod = ssc.ssc_module_create(b'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, 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 and clipping.
    outData['powerOutputAc'] = ssc.ssc_data_get_array(dat, b'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 = list(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"] = list(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"] = [x for x in _runningSum(outData["netCashFlow"])]
    outData["ROI"] = __neoMetaModel__.roundSig(
        sum(outData["netCashFlow"]), 3
    ) / (-1 * __neoMetaModel__.roundSig(sum(outData["lifeOmCosts"]), 3) +
         -1 * __neoMetaModel__.roundSig(sum(outData["lifePurchaseCosts"], 3)))
    outData["NPV"] = __neoMetaModel__.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"] = __neoMetaModel__.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 = list(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