diff --git a/omf/models/derUtilityCost.html b/omf/models/derUtilityCost.html
index f1b9e7e7d..a19493043 100644
--- a/omf/models/derUtilityCost.html
+++ b/omf/models/derUtilityCost.html
@@ -146,12 +146,12 @@
-
-
+
+
-
-
+
+
@@ -328,7 +328,7 @@
new Highcharts.Chart({"credits":{"enabled":false},
"plotOptions":{"column":{"stacking":'normal'},"series":{"animation":false,"shadow":false},"spline":{"animation":false,"shadow":false}},
"xAxis":{"title":{"text":"Year After Installation","style":{"color":"gray"}},"type":"linear","tickColor":"gray","tickInterval":1,"lineColor":"gray","minorTickColor":"gray", "minorTickInterval":5},
- "title":{"text":"NPV:$" + allOutputData.NPV.toFixed(0) + " ; SPP:" + allOutputData.SPP.toFixed(3), "verticalAlign":"top", "align":"right", "y":5, "x":-10, "style":{"color":"#333333", "fontSize":"12px"}},
+ "title":{"text":"NPV:$" + allOutputData.NPV.toFixed(0) + ", SPP:" + allOutputData.SPP.toFixed(3), "verticalAlign":"top", "align":"right", "y":5, "x":-10, "style":{"color":"#333333", "fontSize":"12px"}},
//"title":{"text":""},
"series":[{"name":"Net Benefits", "data":allOutputData.netCashflow},
{"name":"Cumulative Return", "type":"spline", "data":allOutputData.cumulativeCashflow}
diff --git a/omf/models/derUtilityCost.py b/omf/models/derUtilityCost.py
index 15af657b6..a671fca1a 100644
--- a/omf/models/derUtilityCost.py
+++ b/omf/models/derUtilityCost.py
@@ -88,8 +88,8 @@ def work(modelDir, inputDict):
'can_grid_charge': can_grid_charge_bool,
'total_rebate_per_kw': 0,
'macrs_option_years': 0,
- 'installed_cost_per_kw': float(inputDict['installed_cost_per_kw']),
- 'installed_cost_per_kwh': float(inputDict['installed_cost_per_kwh']),
+ 'installed_cost_per_kw': 0,
+ 'installed_cost_per_kwh': 0,
'battery_replacement_year': 0,
'inverter_replacement_year': 0,
'replace_cost_per_kwh': 0.0,
@@ -145,7 +145,12 @@ def work(modelDir, inputDict):
vbatPower = vbpower_series
subsidyUpfront = float(inputDict['subsidyUpfront'])
- subsidyRecurring = float(inputDict['subsidyRecurring'])
+ subsidyRecurring_1year_total = float(inputDict['subsidyRecurring'])
+ subsidyRecurring_1month_total = subsidyRecurring_1year_total / 12
+ subsidyRecurring_total = subsidyRecurring_1year_total * int(inputDict['projectionLength'])
+ total_subsidy_1year = subsidyUpfront + subsidyRecurring_1year_total
+ total_subsidy_1year_array = np.full(12, subsidyRecurring_1month_total)
+ total_subsidy_1year_array[0] += subsidyUpfront
## NOTE: temporarily comment out the two derConsumer runs to run the code quicker
"""
@@ -293,7 +298,7 @@ def work(modelDir, inputDict):
fillcolor='rgba(100,200,210,1)',
showlegend=showlegend))
## Make original load and its legend name hidden in the plot by default
- fig.update_traces(legendgroup='Original Load (kW)', visible='legendonly', selector=dict(name='Original Load (kW)'))
+ fig.update_traces(legendgroup='Original Load', visible='legendonly', selector=dict(name='Original Load'))
## Additional load (Charging BESS and vbat)
## NOTE: demand is added here for plotting purposes, so that the additional load shows up above the demand curve.
@@ -557,30 +562,58 @@ def work(modelDir, inputDict):
rateCompensation = float(inputDict['rateCompensation'])
total_residential_BESS_compensation = rateCompensation * np.sum(total_kwh_residential_BESS)
- ## Update utility savings to include BESS savings
- ## Currently, outData['savings'] is the vbatDispatch result savings only
- BESS = reoptResults['ElectricStorage']['storage_to_load_series_kw']
+ ## Calculate monthly BESS costs and savings
monthHours = [(0, 744), (744, 1416), (1416, 2160), (2160, 2880),
- (2880, 3624), (3624, 4344), (4344, 5088), (5088, 5832),
- (5832, 6552), (6552, 7296), (7296, 8016), (8016, 8760)]
+ (2880, 3624), (3624, 4344), (4344, 5088), (5088, 5832),
+ (5832, 6552), (6552, 7296), (7296, 8016), (8016, 8760)]
+ BESS = reoptResults['ElectricStorage']['storage_to_load_series_kw']
electricityCost = float(inputDict['electricityCost'])
- BESS_savings_monthly = np.array([sum(BESS[s:f])*electricityCost for s, f in monthHours])
- savings_from_BESS_TESS = np.array(outData['savings']) + BESS_savings_monthly
- outData['savings'] = list(savings_from_BESS_TESS)
-
- ## Update the financial cost ouput to include REopt BESS
- ## TODO: combine these with the same variables from vbatDispatch. Currently this just replaces the vbatDispatch variables.
- installed_cost_per_kw = float(inputDict['installed_cost_per_kw'])
- installed_cost_per_kwh = float(inputDict['installed_cost_per_kwh'])
- operational_costs_per_kw = installed_cost_per_kw * total_kw_residential_BESS
- operational_costs_per_kwh = installed_cost_per_kwh * total_kwh_residential_BESS
- #total_operational_costs = # can you add operational costs per kW + operational costs per kWh together?
- netCashflow = np.sum(savings_from_BESS_TESS) - total_residential_BESS_compensation - operational_costs_per_kwh - subsidyUpfront - (subsidyRecurring*int(inputDict['projectionLength']))
- #initialCosts = 1.
- outData['NPV'] = netCashflow
- #outData['SPP'] = initialCosts / netCashflow
- outData['cumulativeCashflow'] = reoptResults['Financial']['offtaker_annual_free_cashflows'] #list(accumulate(reoptResults['Financial']['offtaker_annual_free_cashflows']))
- outData['netCashflow'] = reoptResults['Financial']['offtaker_discounted_annual_free_cashflows'] #list(accumulate(reoptResults['Financial']['offtaker_annual_free_cashflows'])) ## or alternatively: offtaker_annual_free_cashflows
+ BESS_monthly_compensation_to_consumer = np.array([sum(BESS[s:f])*rateCompensation for s, f in monthHours])
+ BESS_monthly_if_bought_from_grid = np.array([sum(BESS[s:f])*electricityCost for s, f in monthHours])
+ utilitySavings_from_BESS_TESS = np.array(outData['savings']) + BESS_monthly_if_bought_from_grid
+
+ ####################################################################################
+ ## Calculate the financial benefit from controlling member-consumer DERs
+ ####################################################################################
+
+ ## 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?
+
+ ## Total operational costs (e.g. utility costs from API calls)
+ operationalCosts_ongoing = float(inputDict['operationalCosts_ongoing'])
+ operationalCosts_onetime = float(inputDict['operationalCosts_onetime'])
+ total_operationalCosts_1year = operationalCosts_onetime + operationalCosts_ongoing*12
+ total_operationalCosts_1year_array = np.full(12, operationalCosts_ongoing)
+ total_operationalCosts_1year_array[0] += operationalCosts_onetime
+
+ ## Calculating total utility costs
+ projectionLength = int(inputDict['projectionLength'])
+ utilityCosts_1year_total = total_operationalCosts_1year + operationalCosts_ongoing + total_subsidy_1year + 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 + (operationalCosts_ongoing+subsidyRecurring_1year_total+total_residential_BESS_compensation)*(projectionLength-1)
+ utilityCosts_1year_array = list(np.array(total_operationalCosts_1year_array) + np.array(total_subsidy_1year_array) + np.array(BESS_monthly_compensation_to_consumer))
+
+ ## 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
+
+ ## Calculating total utility net savings (savings minus costs)
+ utilityNetSavings_1year = utilitySavings_1year_total - utilityCosts_1year_total
+ utilityNetSavings_1year_array = list(np.array(utilitySavings_1year_array) - np.array(utilityCosts_1year_array))
+ utilityNetSavings_allyears_total = utilitySavings_allyears_total - utilityCosts_allyears_total
+
+ ## Update financial parameters to include BESS costs
+ outData['savings'] = utilityNetSavings_1year_array
+ outData['totalCost'] = list(np.array(outData['totalCost']) + np.array(utilityCosts_1year_array))
+ outData['NPV'] = utilityNetSavings_allyears_total
+ outData['SPP'] = utilityCosts_allyears_total / utilitySavings_1year_total
+ #outData['cumulativeCashflow'] = reoptResults['Financial']['offtaker_annual_free_cashflows'] #list(accumulate(reoptResults['Financial']['offtaker_annual_free_cashflows']))
+ #outData['netCashflow'] = reoptResults['Financial']['offtaker_discounted_annual_free_cashflows'] #list(accumulate(reoptResults['Financial']['offtaker_annual_free_cashflows'])) ## or alternatively: offtaker_annual_free_cashflows
# Model operations typically ends here.
# Stdout/stderr.
@@ -617,20 +650,20 @@ def new(modelDir):
'existing_gen_kw': '7000',
## Chemical Battery Inputs
- 'numberBESS': '100', ## Number of residential Tesla Powerwall 2 batteries
+ 'numberBESS': '1000', ## Number of residential Tesla Powerwall 3 batteries
'chemBESSgridcharge': 'Yes',
- 'installed_cost_per_kw': '20.0', #'300.0', ## (Residential: $1,000-$1,500 per kW, Utility: $300-$700 per kW)
- 'installed_cost_per_kwh': '60.0', #'480.0', ## Cost per kWh reflecting Powerwall’s installed cost (Residential: $400-$900 per kWh, Utility: $200-$400 per kWh)
+ 'operationalCosts_ongoing': '20.0',
+ 'operationalCosts_onetime': '1000',
'BESS_kw': '5',
'BESS_kwh': '13.5',
## Financial Inputs
- 'demandChargeCost': '25',
+ 'demandChargeCost': '25', ## used by vbatDispatch
'projectionLength': '25',
'electricityCost': '0.04',
'rateCompensation': '0.02', ## unit: $/kWh
'subsidyUpfront': '100.0',
- 'subsidyRecurring': '0',
+ 'subsidyRecurring': '24.0',
## vbatDispatch inputs:
'load_type': '1', ## Air Conditioner