Skip to content

Commit 8e9dcb0

Browse files
committed
Merge branch 'apply_ratchets_to_per_kw_charges' of https://github.com/NREL/ssc into develop
2 parents 7e84b8d + c4243bf commit 8e9dcb0

File tree

8 files changed

+453
-180
lines changed

8 files changed

+453
-180
lines changed

shared/lib_utility_rate.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ double UtilityRateForecast::forecastCost(std::vector<double>& predicted_loads, s
211211
}
212212

213213
// Get previous peak cost - may need to run two months
214+
rate->set_billing_demands();
214215
double previousDemandCharge = rate->get_demand_charge(month, year);
215216
double previousEnergyCharge = 0;
216217
if (rate->enable_nm)
@@ -268,6 +269,7 @@ double UtilityRateForecast::forecastCost(std::vector<double>& predicted_loads, s
268269
}
269270

270271
// Compute new peak cost - may need to run two months
272+
rate->set_billing_demands();
271273
double newDemandCharge = rate->get_demand_charge(month, year);
272274
// If forecast length is 1, restartMonth won't be triggered on the next forecast. Trigger it now
273275
if (crossing_month && n == 1)

shared/lib_utility_rate_equations.cpp

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,13 @@ rate_data::rate_data() :
129129
dc_hourly_peak(),
130130
monthly_dc_fixed(12),
131131
monthly_dc_tou(12),
132-
en_ec_billing_demand(false),
132+
uses_billing_demand(false),
133+
en_billing_demand_lookback(false),
133134
prev_peak_demand(12),
134-
ec_bd_lookback_percents(12),
135-
ec_bd_minimum(0.0),
136-
ec_bd_lookback_months(1),
135+
bd_lookback_percents(12),
136+
bd_minimum(0.0),
137+
bd_lookback_months(1),
138+
bd_tou_periods(),
137139
billing_demand(12),
138140
tou_demand_single_peak(false),
139141
enable_nm(false),
@@ -160,11 +162,13 @@ rate_data::rate_data(const rate_data& tmp) :
160162
dc_hourly_peak(tmp.dc_hourly_peak),
161163
monthly_dc_fixed(tmp.monthly_dc_fixed),
162164
monthly_dc_tou(tmp.monthly_dc_tou),
163-
en_ec_billing_demand(tmp.en_ec_billing_demand),
165+
uses_billing_demand(tmp.uses_billing_demand),
166+
en_billing_demand_lookback(tmp.en_billing_demand_lookback),
164167
prev_peak_demand(tmp.prev_peak_demand),
165-
ec_bd_lookback_percents(tmp.ec_bd_lookback_percents),
166-
ec_bd_minimum(tmp.ec_bd_minimum),
167-
ec_bd_lookback_months(tmp.ec_bd_lookback_months),
168+
bd_lookback_percents(tmp.bd_lookback_percents),
169+
bd_minimum(tmp.bd_minimum),
170+
bd_lookback_months(tmp.bd_lookback_months),
171+
bd_tou_periods(tmp.bd_tou_periods),
168172
billing_demand(tmp.billing_demand),
169173
tou_demand_single_peak(tmp.tou_demand_single_peak),
170174
enable_nm(tmp.enable_nm),
@@ -215,37 +219,64 @@ bool rate_data::check_for_kwh_per_kw_rate(int units) {
215219

216220
double rate_data::get_billing_demand(int month) {
217221
int m = 0;
218-
double billing_demand = ec_bd_minimum;
219-
int prev_yr_lookback = 11 - (ec_bd_lookback_months - month); // What month do we stop looking back in the prev yr?
222+
double billing_demand = bd_minimum;
223+
int prev_yr_lookback = 11 - (bd_lookback_months - month); // What month do we stop looking back in the prev yr?
220224

221225
for (m = 11; m >= prev_yr_lookback && m >= 0; m--) {
222-
double ratchet_percent = ec_bd_lookback_percents[m] * 0.01;
226+
double ratchet_percent = bd_lookback_percents[m] * 0.01;
223227
double months_demand = prev_peak_demand[m] * ratchet_percent;
224228
if (months_demand > billing_demand) {
225229
billing_demand = months_demand;
226230
}
227231
}
228232

229233
int start_month = 0;
230-
if (month >= ec_bd_lookback_months) {
231-
start_month = month - ec_bd_lookback_months;
234+
if (month >= bd_lookback_months) {
235+
start_month = month - bd_lookback_months;
232236
}
233237

238+
int idx = 0;
234239
for (m = start_month; m <= month; m++) {
235-
double ratchet_percent = ec_bd_lookback_percents[m] * 0.01;
236-
double months_demand = m_month[m].dc_flat_peak * ratchet_percent;
237-
if (months_demand > billing_demand) {
238-
billing_demand = months_demand;
240+
idx = 0;
241+
for (int p : m_month[m].dc_periods) {
242+
if (bd_tou_periods.at(p)) {
243+
double ratchet_percent = bd_lookback_percents[m] * 0.01;
244+
double months_demand = m_month[m].dc_tou_peak[idx] * ratchet_percent;
245+
if (months_demand > billing_demand) {
246+
billing_demand = months_demand;
247+
}
248+
}
249+
idx++;
239250
}
240251
}
241252

242-
if (m_month[month].dc_flat_peak > billing_demand && m_month[month].use_current_month_ratchet) {
243-
billing_demand = m_month[month].dc_flat_peak;
253+
if (m_month[month].use_current_month_ratchet) {
254+
idx = 0;
255+
for (int p : m_month[month].dc_periods) {
256+
if (bd_tou_periods.at(p)) {
257+
double months_demand = m_month[month].dc_tou_peak[idx];
258+
if (months_demand > billing_demand) {
259+
billing_demand = months_demand;
260+
}
261+
}
262+
idx++;
263+
}
244264
}
245265

246266
return billing_demand;
247267
}
248268

269+
void rate_data::set_billing_demands() {
270+
for (int m = 0; m < (int) m_month.size(); m++) {
271+
double flat_peak = m_month[m].dc_flat_peak;
272+
if (en_billing_demand_lookback) {
273+
// If ratchets are present the peak used here might be the actual peak, or something based on a previous month.
274+
flat_peak = get_billing_demand(m);
275+
}
276+
billing_demand[m] = flat_peak;
277+
}
278+
}
279+
249280
void rate_data::setup_prev_demand(ssc_number_t* prev_demand) {
250281
for (size_t i = 0; i < prev_peak_demand.size(); i++) {
251282
prev_peak_demand[i] = prev_demand[i];
@@ -277,13 +308,8 @@ void rate_data::init_energy_rates(bool gen_only) {
277308
std::vector<size_t> tier_numbers;
278309
std::vector<double> tier_kwh;
279310

280-
// track monthly peak to determine which kWh/kW tier
281-
double flat_peak = m_month[m].dc_flat_peak;
282-
if (en_ec_billing_demand) {
283-
// If ratchets are present the peak used here might be the actual peak, or something based on a previous month.
284-
flat_peak = get_billing_demand(m);
285-
}
286-
billing_demand[m] = flat_peak;
311+
// Monthly billing demand is computed prior to this loop
312+
double flat_peak = billing_demand[m];
287313

288314
// get kWh/kW break points based on actual demand
289315
for (size_t i_tier = 0; i_tier < m_month[m].ec_tou_units.ncols(); i_tier++)
@@ -805,7 +831,7 @@ void rate_data::setup_demand_charges(ssc_number_t* dc_weekday, ssc_number_t* dc_
805831
}
806832
}
807833

808-
void rate_data::setup_ratcheting_demand(ssc_number_t* ratchet_percent_matrix)
834+
void rate_data::setup_ratcheting_demand(ssc_number_t* ratchet_percent_matrix, ssc_number_t* bd_tou_period_matrix)
809835
{
810836
// Error checked in SSC variables
811837
size_t nrows = 12;
@@ -814,10 +840,16 @@ void rate_data::setup_ratcheting_demand(ssc_number_t* ratchet_percent_matrix)
814840
ratchet_matrix.assign(ratchet_percent_matrix, nrows, ncols);
815841

816842
for (int i = 0; i < nrows; i++) {
817-
ec_bd_lookback_percents[i] = ratchet_matrix.at(i, 0);
843+
bd_lookback_percents[i] = ratchet_matrix.at(i, 0);
818844
m_month[i].use_current_month_ratchet = ratchet_matrix.at(i, 1) == 1;
819845
}
820846

847+
nrows = m_dc_tou_periods.size();
848+
util::matrix_t<double> tou_matrix(nrows, ncols);
849+
tou_matrix.assign(bd_tou_period_matrix, nrows, ncols);
850+
for (int i = 0; i < nrows; i++) {
851+
bd_tou_periods.emplace((int) tou_matrix.at(i, 0), tou_matrix.at(i, 1) == 1.0);
852+
}
821853
}
822854

823855
void rate_data::sort_energy_to_periods(int month, double energy, size_t step) {
@@ -881,7 +913,7 @@ ssc_number_t rate_data::get_demand_charge(int month, size_t year)
881913
ssc_number_t charge = 0;
882914
ssc_number_t d_lower = 0;
883915
ssc_number_t total_charge = 0;
884-
ssc_number_t demand = curr_month.dc_flat_peak;
916+
ssc_number_t demand = billing_demand[month];
885917
bool found = false;
886918
for (tier = 0; tier < (int)curr_month.dc_flat_ub.size() && !found; tier++)
887919
{
@@ -915,11 +947,19 @@ ssc_number_t rate_data::get_demand_charge(int month, size_t year)
915947
d_lower = 0;
916948
if (tou_demand_single_peak)
917949
{
918-
demand = curr_month.dc_flat_peak;
950+
// If billing demand lookback is not enabled, this will be the flat peak
951+
demand = billing_demand[month];
919952
if (curr_month.dc_flat_peak_hour != curr_month.dc_tou_peak_hour[period]) continue; // only one peak per month.
920953
}
921-
else if (period < curr_month.dc_periods.size())
922-
demand = curr_month.dc_tou_peak[period];
954+
else if (period < curr_month.dc_periods.size()) {
955+
int period_num = curr_month.dc_periods[period];
956+
if (en_billing_demand_lookback && bd_tou_periods.at(period_num)) {
957+
demand = billing_demand[month];
958+
}
959+
else {
960+
demand = curr_month.dc_tou_peak[period];
961+
}
962+
}
923963
// find tier corresponding to peak demand
924964
found = false;
925965
for (tier = 0; tier < (int)curr_month.dc_tou_ub.ncols() && !found; tier++)

shared/lib_utility_rate_equations.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2323
#ifndef _LIB_UTILITY_RATE_EQUATIONS_H_
2424
#define _LIB_UTILITY_RATE_EQUATIONS_H_
2525

26+
#include <map>
2627
#include <vector>
2728

2829
#include "../ssc/core.h"
@@ -113,11 +114,13 @@ class rate_data {
113114
std::vector<ssc_number_t> monthly_dc_fixed;
114115
std::vector<ssc_number_t> monthly_dc_tou;
115116

116-
bool en_ec_billing_demand; // Enable billing demand lookback percentages for kWh/kW energy charges
117+
bool uses_billing_demand; // Has a energy rate with kWh/kw AND/OR has a demand charge. If false, en_billing_demand_lookback must be false
118+
bool en_billing_demand_lookback; // Enable billing demand lookback percentages
117119
std::vector<ssc_number_t> prev_peak_demand; // Set before calling init_energy_rates
118-
std::vector<ssc_number_t> ec_bd_lookback_percents;
119-
double ec_bd_minimum;
120-
int ec_bd_lookback_months;
120+
std::vector<ssc_number_t> bd_lookback_percents;
121+
double bd_minimum;
122+
int bd_lookback_months;
123+
std::unordered_map<int, bool> bd_tou_periods;
121124
std::vector<ssc_number_t> billing_demand; // Store locally for ssc outputs
122125

123126
bool tou_demand_single_peak;
@@ -142,11 +145,12 @@ class rate_data {
142145
/* Optional function if demand charges are present */
143146
void setup_demand_charges(ssc_number_t* dc_weekday, ssc_number_t* dc_weekend, size_t dc_tou_rows, ssc_number_t* dc_tou_in, size_t dc_flat_rows, ssc_number_t* dc_flat_in);
144147
/* Optional function if energy charges use a ratchet for the billing demand */
145-
void setup_ratcheting_demand(ssc_number_t* ratchet_percent_matrix);
148+
void setup_ratcheting_demand(ssc_number_t* ratchet_percent_matrix, ssc_number_t* bd_tou_period_matrix);
146149

147150
void setup_prev_demand(ssc_number_t* prev_demand);
148151
/* call ur_month.update_net_and_peak before this, otherwise you'll get low values back */
149152
double get_billing_demand(int month);
153+
void set_billing_demands(); // Runs get_billing_demand and updates the billing_demand structure
150154

151155
/* Populate ur_month objects from those filled out in setup_energy_rates. Called annually in cmod_utility_rate5, other classes may reset ur_month directly
152156
Can be called right away to create the vectors, but for kWh/kW rates needs to be called once after ur_month.update_net_and_peak to be accurate */

0 commit comments

Comments
 (0)