diff --git a/omf/models/derUtilityCost.html b/omf/models/derUtilityCost.html
index 3476f5355..10b959be2 100644
--- a/omf/models/derUtilityCost.html
+++ b/omf/models/derUtilityCost.html
@@ -104,6 +104,14 @@
+
+
+
+
+
+
+
+
@@ -152,6 +160,14 @@
+
+
+
+
+
+
+
+
@@ -179,14 +195,6 @@
-
-
-
-
-
-
-
-
@@ -417,7 +425,7 @@
insertMetric("monthlySummaryTable","Total Cost of Service ($)", allOutputData.totalCost_service)
insertMetric("monthlySummaryTable","Adjusted Total Cost of Service ($)", allOutputData.totalCostAdjusted_service)
insertMetric("monthlySummaryTable","Total Consumer Compensation ($)", allOutputData.totalCost_paidToConsumer)
- insertMetric("monthlySummaryTable","Net Utility Savings ($)", allOutputData.savings_total)
+ insertMetric("monthlySummaryTable","Net Utility Savings ($)", allOutputData.netSavings_year1)
//insertMetric("monthlySummaryTable","1 kW Consumer Savings ($)", allOutputData.totalSavingsSmallConsumer)
//insertMetric("monthlySummaryTable","10 kW Consumer Savings ($)", allOutputData.totalSavingsLargeConsumer)
@@ -442,6 +450,7 @@
{"name":"Subsidies", "data": allOutputData.subsidies, "color": "black"},
{"name":"Consumer BESS Compensation", "data": allOutputData.BESS_compensation_to_consumer_allyears, "color": "rgb(255,102,102)"},
{"name":"Grid Costs", "data": allOutputData.energyCostAdjusted_gridCharging_annual, "color": "rgb(169,169,169)"},
+ {"name":"Startup Costs", "data": allOutputData.startupCosts, "color": "rgb(0,30,179)"},
{"name":"BESS Operational Costs", "data": allOutputData.operationalCosts_BESS_allyears, "color": "orange"},
{"name":"TESS Operational Costs", "data": allOutputData.operationalCosts_TESS_allyears, "color": "red"},
{"name":"Cumulative Return", "type":"spline", "data": allOutputData.cumulativeCashflow_total, "color": "brown"}
diff --git a/omf/models/derUtilityCost.py b/omf/models/derUtilityCost.py
index d42dc1cc9..d1fefa9c5 100644
--- a/omf/models/derUtilityCost.py
+++ b/omf/models/derUtilityCost.py
@@ -758,15 +758,10 @@ def work(modelDir, inputDict):
outData['savings_demandCharge'] = list(np.array(outData['demandCharge']) - np.array(outData['demandChargeAdjusted_total']))
outData['savings_demandCharge_annual'] = list(np.full(projectionLength, np.sum(outData['savings_demandCharge'])))
- ## Calculate total residential BESS capacity and compensation
- total_kw_residential_BESS = int(inputDict['numberBESS']) * float(inputDict['BESS_kw'])
- total_kwh_residential_BESS = int(inputDict['numberBESS']) * float(inputDict['BESS_kwh'])
- total_residential_BESS_compensation = rateCompensation * np.sum(total_kwh_residential_BESS)
-
- ## Calculate monthly BESS costs and savings
- BESS_monthly_compensation_to_consumer = np.array([sum(BESS[s:f])*rateCompensation for s, f in monthHours])
- BESS_yearly_compensation_to_consumer_total = np.sum(BESS_monthly_compensation_to_consumer)
- BESS_allyears_compensation_to_consumer_array = np.full(projectionLength, BESS_yearly_compensation_to_consumer_total)
+ ## Calculate BESS compensation costs and savings
+ BESS_compensation_year1_array = np.array([sum(BESS[s:f])*rateCompensation for s, f in monthHours])
+ BESS_compensation_year1_total = np.sum(BESS_compensation_year1_array)
+ BESS_compensation_allyears_array = np.full(projectionLength, BESS_compensation_year1_total)
BESS_monthly_if_bought_from_grid = np.array([sum(BESS[s:f])*electricityCost for s, f in monthHours])
## Calculate total BESS and TESS savings
@@ -777,13 +772,6 @@ def work(modelDir, inputDict):
####################################################################################
projectionLength = int(inputDict['projectionLength'])
- ## Calculate residential BESS installation costs
- installed_cost_per_kw = 20
- installed_cost_per_kwh = 80
- total_kw_installed_costs = installed_cost_per_kw * total_kw_residential_BESS
- total_kwh_installed_costs = installed_cost_per_kwh * total_kwh_residential_BESS
- #total_installed_costs = ## TODO: How do we deal with both costs per kw and kWh?
-
## Calculate the BESS subsidy for year 1 and the projection length (all years)
## Year 1 includes the onetime subsidy, but subsequent years do not.
BESS_subsidy_ongoing = float(inputDict['BESS_subsidy_ongoing'])
@@ -819,85 +807,86 @@ def work(modelDir, inputDict):
## Calculate the utility's ongoing and onetime operational costs for each device
## This includes costs for things like API calls to control the DERs
- ## Total combined (TESS+BESS) ongoing and onetime operational costs
- operationalCosts_ongoing_BESS = float(inputDict['BESS_operationalCosts_ongoing'])
- operationalCosts_ongoing_TESS = float(combined_device_results['combinedTESS_operationalCosts_ongoing'])
- operationalCosts_ongoing_total = operationalCosts_ongoing_BESS + operationalCosts_ongoing_TESS
- operationalCosts_onetime_BESS = float(inputDict['BESS_operationalCosts_onetime'])
- operationalCosts_onetime_TESS = float(combined_device_results['combinedTESS_operationalCosts_onetime'])
- operationalCosts_onetime_total = operationalCosts_onetime_BESS + operationalCosts_onetime_TESS
-
- ## Total combined (TESS+BESS) operational costs for 1 year (float value)
- operationalCosts_BESS_year1_total = operationalCosts_onetime_BESS + (operationalCosts_ongoing_BESS*12.0)
- operationalCosts_TESS_year1_total = operationalCosts_onetime_TESS + (operationalCosts_ongoing_TESS*12.0)
- operationalCosts_1year_total = operationalCosts_BESS_year1_total + operationalCosts_TESS_year1_total
-
- ## Total combined (TESS+BESS) operational costs for 1 year (monthly values for 1 year)
- operationalCosts_BESS_year1_array = np.full(12, operationalCosts_ongoing_BESS)
- operationalCosts_TESS_year1_array = np.full(12, operationalCosts_ongoing_TESS)
- operationalCosts_BESS_year1_array[0] += operationalCosts_onetime_BESS
- operationalCosts_TESS_year1_array[0] += operationalCosts_onetime_TESS
- operationalCosts_year1_array = operationalCosts_BESS_year1_array + operationalCosts_TESS_year1_array
-
- ## Total operational costs for BESS and TESS for all years of analysis (yearly values for projectionLength duration)
- operationalCosts_yearly_total = operationalCosts_ongoing_total + operationalCosts_onetime_total
- operationalCosts_BESS_allyears_array = np.full(projectionLength, operationalCosts_ongoing_BESS*12.0)
- operationalCosts_TESS_allyears_array = np.full(projectionLength, operationalCosts_ongoing_TESS*12.0)
- operationalCosts_BESS_allyears_array[0] += operationalCosts_onetime_BESS
- operationalCosts_TESS_allyears_array[0] += operationalCosts_onetime_TESS
+ ## Calculate BESS ongoing and onetime operational costs
+ BESS_operationalCosts_ongoing = float(inputDict['BESS_operationalCosts_ongoing'])
+ BESS_operationalCosts_onetime = float(inputDict['BESS_operationalCosts_onetime'])
+ BESS_operationalCosts_year1_total = BESS_operationalCosts_onetime + (BESS_operationalCosts_ongoing*12.0)
+ BESS_operationalCosts_year1_array = np.full(12, BESS_operationalCosts_ongoing)
+ BESS_operationalCosts_year1_array[0] += BESS_operationalCosts_onetime
+ BESS_operationalCosts_allyears_array = np.full(projectionLength, BESS_operationalCosts_ongoing*12.0)
+ BESS_operationalCosts_allyears_array[0] += BESS_operationalCosts_onetime
+
+ ## Calculate combinedTESS ongoing and onetime operational costs
+ combinedTESS_operationalCosts_ongoing = float(combined_device_results['combinedTESS_operationalCosts_ongoing'])
+ combinedTESS_operationalCosts_onetime = float(combined_device_results['combinedTESS_operationalCosts_onetime'])
+ combinedTESS_operationalCosts_year1_total = combinedTESS_operationalCosts_onetime + (combinedTESS_operationalCosts_ongoing*12.0)
+ combinedTESS_operationalCosts_year1_array = np.full(12, combinedTESS_operationalCosts_ongoing)
+ combinedTESS_operationalCosts_year1_array[0] += combinedTESS_operationalCosts_onetime
+ combinedTESS_operationalCosts_allyears_array = np.full(projectionLength, combinedTESS_operationalCosts_ongoing*12.0)
+ combinedTESS_operationalCosts_allyears_array[0] += combinedTESS_operationalCosts_onetime
+
+ ## Calculate the total ongoing and onetime operational costs for all devices (BESS+TESS) - TODO: potentially add generator?
+ allDevices_operationalCosts_ongoing = BESS_operationalCosts_ongoing + combinedTESS_operationalCosts_ongoing
+ allDevices_operationalCosts_onetime = BESS_operationalCosts_onetime + combinedTESS_operationalCosts_onetime
+ allDevices_operationalCosts_year1_total = BESS_operationalCosts_year1_total + combinedTESS_operationalCosts_year1_total
+ allDevices_operationalCosts_year1_array = BESS_operationalCosts_year1_array + combinedTESS_operationalCosts_year1_array
+ allDevices_operationalCosts_allyears_array = np.full(projectionLength, allDevices_operationalCosts_ongoing*12.0)
+ allDevices_operationalCosts_allyears_array[0] += allDevices_operationalCosts_onetime
## Calculating total utility costs
- utilityCosts_1year_total = operationalCosts_1year_total + allDevices_subsidy_year1_total + total_residential_BESS_compensation
## NOTE: utilityCosts_allyears_total (below) assumes that the REopt BESS array will be the same for every year of the entire projectionLength (entire analysis)
- utilityCosts_allyears_total = utilityCosts_1year_total + (projectionLength-1)*(operationalCosts_ongoing_total+allDevices_subsidy_year1_total+total_residential_BESS_compensation)
- utilityCosts_1year_array = list(np.array(operationalCosts_year1_array) + np.array(allDevices_subsidy_year1_array) + np.array(BESS_monthly_compensation_to_consumer))
+ utilityCosts_total_ongoing = allDevices_operationalCosts_ongoing + allDevices_subsidy_ongoing + BESS_compensation_year1_total/12 ## NOTE: the BESS compensation variable is divided by 12 to match the ongoing costs/subsidy variables, which are monthly values
+ utilityCosts_total_onetime = allDevices_operationalCosts_onetime + allDevices_subsidy_onetime + float(inputDict['startupCosts'])
+ utilityCosts_year1_total = allDevices_operationalCosts_year1_total + allDevices_subsidy_year1_total + BESS_compensation_year1_total + float(inputDict['startupCosts'])
+ utilityCosts_year1_array = list(np.array(allDevices_operationalCosts_year1_array) + np.array(allDevices_subsidy_year1_array) + np.array(BESS_compensation_year1_array))
+ utilityCosts_year1_array[0] += float(inputDict['startupCosts'])
+ utilityCosts_allyears_total = utilityCosts_year1_total + float(inputDict['startupCosts']) + ((projectionLength-1) * (allDevices_operationalCosts_ongoing*12.0 + allDevices_subsidy_ongoing*12.0 + BESS_compensation_year1_total)) ##NOTE: The projectionLength-1 represents all years except the first year. The first year includes the one-time costs.
+ utilityCosts_allyears_array = np.full(projectionLength, utilityCosts_total_ongoing*12.0)
+ utilityCosts_allyears_array[0] += utilityCosts_total_onetime
## Calculating total utility savings
- utilitySavings_1year_total = np.sum(utilitySavings_from_BESS_TESS)
- utilitySavings_1year_array = utilitySavings_from_BESS_TESS
- utilitySavings_allyears_total = utilitySavings_1year_total*projectionLength
- utilitySavings_allyears_array = np.full(projectionLength, utilitySavings_1year_total)
+ utilitySavings_year1_total = np.sum(outData['savings_adjusted'])
+ utilitySavings_allyears_array = np.full(projectionLength, utilitySavings_year1_total)
+ utilitySavings_allyears_total = np.sum(utilitySavings_allyears_array)
## Calculating total utility net savings (savings minus costs)
- utilityNetSavings_1year_total = utilitySavings_1year_total - utilityCosts_1year_total
- utilityNetSavings_1year_array = np.array(utilitySavings_1year_array) - np.array(utilityCosts_1year_array)
- utilityNetSavings_allyears_total = utilitySavings_allyears_total - utilityCosts_allyears_total
- utilityNetSavings_allyears_array = np.full(projectionLength, utilityNetSavings_1year_total)
-
- ## Update utility savings to include subsidies and BESS compensation to conumers
- #outData['savings_total'] = list(np.array(outData['savings_adjusted']) - np.array(utilityCosts_1year_array))
+ #utilityNetSavings_1year_total = utilitySavings_year1_total - utilityCosts_year1_total
+ #utilityNetSavings_1year_array = np.array(utilitySavings_1year_array) - np.array(utilityCosts_year1_array)
+ #utilityNetSavings_allyears_total = utilitySavings_allyears_total - utilityCosts_allyears_total
+ #utilityNetSavings_allyears_array = np.full(projectionLength, utilityNetSavings_1year_total)
## Update financial parameters
- #outData['savings'] = utilityNetSavings_1year_array
- #outData['totalCost_TESS'] = float(inputDict['unitUpkeepCost'])*float(inputDict['number_devices'])
- #outData['totalCost_annual'] = list((outData['totalCost_TESS'] + np.array(utilityCosts_1year_array))) # + np.array(outData['energyCostAdjusted_gridCharging'] + np.array(outData['demandChargeAdjusted_total'])))
- #outData['totalCost_annual'] = list((outData['totalCost_TESS'] + np.array(utilityCosts_1year_array)) + np.array(outData['energyCostAdjusted_total']) + np.array(outData['demandChargeAdjusted_total']))
- outData['totalCost_annual'] = list(np.array(utilityCosts_1year_array))
- outData['totalCost_paidToConsumer'] = list(BESS_monthly_compensation_to_consumer + allDevices_subsidy_year1_array)
- outData['NPV'] = npv(float(inputDict['discountRate'])/100., utilityNetSavings_allyears_array)
- if utilitySavings_1year_total == 0:
+ outData['totalCost_annual'] = list(np.array(utilityCosts_year1_array))
+ outData['totalCost_paidToConsumer'] = list(BESS_compensation_year1_array + allDevices_subsidy_year1_array)
+ annualEarnings_year1_total = np.sum(outData['savings_adjusted']) - np.sum(outData['totalCost_annual']) ##includes all devices: BESS, generator, combined TESSs
+ annualEarnings_year1_array = np.array(outData['savings_adjusted']) - np.array(outData['totalCost_annual']) - utilityCosts_year1_array ##includes all devices: BESS, generator, combined TESSs
+ #annualEarnings_allyears_array = np.full(projectionLength, annualEarnings_year1_total)
+ annualEarnings_allyears_array = utilitySavings_allyears_array - utilityCosts_allyears_array
+ annualEarnings_allyears_total = np.sum(annualEarnings_allyears_array)
+
+ ## Calculate Net Present Value (NPV) and Simple Payback Period (SPP)
+ outData['NPV'] = npv(float(inputDict['discountRate'])/100., annualEarnings_allyears_array)
+ if annualEarnings_year1_total == 0: ## Handle division by $0 in savings
outData['SPP'] = 0.
else:
- outData['SPP'] = utilityCosts_allyears_total / utilitySavings_1year_total
-
- annualEarnings = np.sum(outData['savings_adjusted']) - np.sum(outData['totalCost_annual'])
- annualEarnings_array = np.array(outData['savings_adjusted']) - np.array(outData['totalCost_annual'])
- outData['savings_total'] = list(annualEarnings_array) ## (total cost of service - adjusted total cost of service) - (operational costs + subsidies + BESS compensation to consumer)
+ outData['SPP'] = utilityCosts_allyears_total / annualEarnings_year1_total
+ outData['netSavings_year1'] = list(annualEarnings_year1_array) ## (total cost of service - adjusted total cost of service) - (operational costs + subsidies + BESS compensation to consumer)
outData['annualEarnings_BESS'] = list(np.full(projectionLength, np.sum(outData['energyCostAdjusted_BESS'])))
outData['annualEarnings_TESS'] = list(np.full(projectionLength, np.sum(outData['energyCostAdjusted_TESS'])))
outData['annualEarnings_generator'] = list(np.full(projectionLength, np.sum(outData['energyCostAdjusted_generator'])))
-
- cashFlowList_total = np.full(projectionLength, annualEarnings)
- outData['cashFlowList_total'] = list(cashFlowList_total)
+ outData['cashFlowList_total'] = list(annualEarnings_allyears_array)
#outData['cumulativeCashflow_total'] = [sum(cashFlowList_total[:i+1]) for i in cashFlowList_total]
- outData['cumulativeCashflow_total'] = list(np.cumsum(cashFlowList_total))
+ outData['cumulativeCashflow_total'] = list(np.cumsum(annualEarnings_allyears_array))
outData['savingsAllYears'] = list(utilitySavings_allyears_array)
## Show the costs to the utility as negative values in the Cash Flow Projection plot (costs to the utility)
outData['subsidies'] = list(allDevices_subsidy_allyears_array*-1.)
- outData['BESS_compensation_to_consumer_allyears'] = list(BESS_allyears_compensation_to_consumer_array*-1.)
- outData['operationalCosts_BESS_allyears'] = list(operationalCosts_BESS_allyears_array*-1.)
- outData['operationalCosts_TESS_allyears'] = list(operationalCosts_TESS_allyears_array*-1.)
+ outData['BESS_compensation_to_consumer_allyears'] = list(BESS_compensation_allyears_array*-1.)
+ outData['operationalCosts_BESS_allyears'] = list(BESS_operationalCosts_allyears_array*-1.)
+ outData['operationalCosts_TESS_allyears'] = list(combinedTESS_operationalCosts_allyears_array*-1.)
+ startupCosts_arr = np.zeros(12)
+ startupCosts_arr[0] += float(inputDict['startupCosts'])
+ outData['startupCosts'] = list(startupCosts_arr*-1.)
# Model operations typically ends here.
# Stdout/stderr.
@@ -947,6 +936,8 @@ def new(modelDir):
'projectionLength': '25',
'electricityCost': '0.04',
'rateCompensation': '0.02', ## unit: $/kWh
+ 'discountRate': '2',
+ 'startupCosts': '2000000',
'TESS_subsidy_onetime_ac': '5.0',
'TESS_subsidy_ongoing_ac': '1.0',
'TESS_subsidy_onetime_hp': '10.0',
@@ -963,7 +954,6 @@ def new(modelDir):
'TESS_operationalCosts_onetime_ac': '1000',
'TESS_operationalCosts_ongoing_wh': '10.0',
'TESS_operationalCosts_onetime_wh': '1000',
- 'discountRate': '2',
## Home Air Conditioner inputs (vbatDispatch):
'load_type_ac': '1',