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
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