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
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
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
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)
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
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
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)
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)
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. ''' 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
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