Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReportUtilityBills: account for all production credit #1555

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
9 changes: 7 additions & 2 deletions ReportUtilityBills/measure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,16 @@ def energyPlusOutputRequests(runner, user_arguments)
has_fuel = hpxml.has_fuels(Constants.FossilFuels, hpxml.to_doc)
has_fuel[HPXML::FuelTypeElectricity] = true

# Fuel outputs
# Has production
has_pv = @hpxml_buildings.select { |hpxml_bldg| !hpxml_bldg.pv_systems.empty? }.size > 0
has_battery = @model.getElectricLoadCenterStorageLiIonNMCBatterys.size > 0 # has a modeled battery
has_generator = @hpxml_buildings.select { |hpxml_bldg| !hpxml_bldg.generators.empty? }.size > 0

# Fuel outputs
fuels.each do |(fuel_type, is_production), fuel|
fuel.meters.each do |meter|
next unless has_fuel[hpxml_fuel_map[fuel_type]]
next if is_production && !has_pv
next if is_production && !has_pv && !has_battery && !has_generator

result << OpenStudio::IdfObject.load("Output:Meter,#{meter},monthly;").get
if fuel_type == FT::Elec && @hpxml_header.utility_bill_scenarios.has_detailed_electric_rates
Expand Down Expand Up @@ -546,6 +550,7 @@ def get_utility_rates(hpxml_path, fuels, utility_rates, bill_scenario, monthly_f
end

# Net Metering
# TODO: if battery or generator and no PV, default to HPXML::PVAnnualExcessSellbackRateTypeRetailElectricityCost?
rate.net_metering_excess_sellback_type = bill_scenario.pv_net_metering_annual_excess_sellback_rate_type if bill_scenario.pv_compensation_type == HPXML::PVCompensationTypeNetMetering
rate.net_metering_user_excess_sellback_rate = bill_scenario.pv_net_metering_annual_excess_sellback_rate if rate.net_metering_excess_sellback_type == HPXML::PVAnnualExcessSellbackRateTypeUserSpecified

Expand Down
10 changes: 5 additions & 5 deletions ReportUtilityBills/measure.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<schema_version>3.1</schema_version>
<name>report_utility_bills</name>
<uid>ca88a425-e59a-4bc4-af51-c7e7d1e960fe</uid>
<version_id>71567c67-85c0-4d09-8220-85cc8a1897d6</version_id>
<version_modified>2024-05-27T15:54:13Z</version_modified>
<version_id>6504a2cd-0b30-4d3b-8366-5c424deab9cb</version_id>
<version_modified>2024-05-28T20:06:47Z</version_modified>
<xml_checksum>15BF4E57</xml_checksum>
<class_name>ReportUtilityBills</class_name>
<display_name>Utility Bills Report</display_name>
Expand Down Expand Up @@ -180,7 +180,7 @@
<filename>measure.rb</filename>
<filetype>rb</filetype>
<usage_type>script</usage_type>
<checksum>4E7FF5BB</checksum>
<checksum>67FE9928</checksum>
</file>
<file>
<filename>detailed_rates/Sample Flat Rate Min Annual Charge.json</filename>
Expand Down Expand Up @@ -300,7 +300,7 @@
<filename>util.rb</filename>
<filetype>rb</filetype>
<usage_type>resource</usage_type>
<checksum>CD3334FF</checksum>
<checksum>ED1141FF</checksum>
</file>
<file>
<filename>Contains Demand Charges.json</filename>
Expand Down Expand Up @@ -342,7 +342,7 @@
<filename>test_report_utility_bills.rb</filename>
<filetype>rb</filetype>
<usage_type>test</usage_type>
<checksum>CDF8B24F</checksum>
<checksum>7B65C730</checksum>
</file>
</files>
</measure>
24 changes: 13 additions & 11 deletions ReportUtilityBills/resources/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ def self.simple(fuel_type, header, fuel_time_series, is_production, rate, bill,

def self.detailed_electric(header, fuels, rate, bill)
fuel_time_series = fuels[[FT::Elec, false]].timeseries
pv_fuel_time_series = fuels[[FT::Elec, true]].timeseries
production_fuel_time_series = fuels[[FT::Elec, true]].timeseries

if fuel_time_series.size < 24 || pv_fuel_time_series.size < 24
if fuel_time_series.size < 24 || production_fuel_time_series.size < 24
# Must be at least 24 hours worth of simulation data
fail 'Incorrect timeseries data.'
end
Expand All @@ -121,7 +121,8 @@ def self.detailed_electric(header, fuels, rate, bill)

net_monthly_energy_charge = [0] * 12
production_fit_month = [0] * 12
has_pv = (pv_fuel_time_series.sum != 0)
has_production = (production_fuel_time_series.sum != 0)

elec_month = [0] * 12
net_elec_month = [0] * 12

Expand All @@ -142,7 +143,7 @@ def self.detailed_electric(header, fuels, rate, bill)
net_tier = 0
elec_period = [0] * num_periods
elec_tier = [0] * num_tiers
if has_pv
if has_production
net_elec_period = [0] * num_periods
net_elec_tier = [0] * num_tiers
end
Expand All @@ -156,8 +157,8 @@ def self.detailed_electric(header, fuels, rate, bill)
elec_hour = fuel_time_series[hour]
elec_month[month] += elec_hour

if has_pv
pv_hour = pv_fuel_time_series[hour]
if has_production
pv_hour = production_fuel_time_series[hour]
net_elec_hour = elec_hour - pv_hour
net_elec_month[month] += net_elec_hour
end
Expand All @@ -166,7 +167,7 @@ def self.detailed_electric(header, fuels, rate, bill)
# Real-Time Pricing
bill.monthly_energy_charge[month] += elec_hour * rate.realtimeprice[hour]

if has_pv
if has_production
if rate.feed_in_tariff_rate
production_fit_month[month] += pv_hour * rate.feed_in_tariff_rate
else
Expand Down Expand Up @@ -227,7 +228,7 @@ def self.detailed_electric(header, fuels, rate, bill)
bill.monthly_energy_charge[month] += elec_hour * rate.energyratestructure[0][0][:rate]
end

if has_pv
if has_production
if rate.feed_in_tariff_rate
production_fit_month[month] += pv_hour * rate.feed_in_tariff_rate
else
Expand Down Expand Up @@ -312,7 +313,7 @@ def self.detailed_electric(header, fuels, rate, bill)
tier = 0
end

if has_pv && !rate.feed_in_tariff_rate # has PV
if has_production && !rate.feed_in_tariff_rate # has PV
if (num_periods > 1) || (num_tiers > 1) # tiered or TOU

if num_periods > 1 && num_tiers > 1 # tiered and TOU
Expand All @@ -333,7 +334,7 @@ def self.detailed_electric(header, fuels, rate, bill)
end
end

if has_pv
if has_production
if rate.feed_in_tariff_rate
bill.monthly_production_credit[month] = production_fit_month[month]
else
Expand All @@ -348,11 +349,12 @@ def self.detailed_electric(header, fuels, rate, bill)

annual_total_charge = bill.monthly_energy_charge.sum + bill.monthly_fixed_charge.sum

if has_pv && !rate.feed_in_tariff_rate # Net metering calculations
if has_production && !rate.feed_in_tariff_rate # Net metering calculations
annual_payments, monthly_min_charges, end_of_year_bill_credit = apply_min_charges(bill.monthly_fixed_charge, net_monthly_energy_charge, rate.minannualcharge, rate.minmonthlycharge)
end_of_year_bill_credit, excess_sellback = apply_excess_sellback(end_of_year_bill_credit, rate.net_metering_excess_sellback_type, rate.net_metering_user_excess_sellback_rate, net_elec_month.sum(0.0))

annual_total_charge_with_pv = annual_payments + end_of_year_bill_credit - excess_sellback
# TODO: if battery or generator and no PV, put this toward annual_energy_charge instead of annual_production credit?
bill.annual_production_credit = annual_total_charge - annual_total_charge_with_pv

for m in 0..11
Expand Down
12 changes: 12 additions & 0 deletions ReportUtilityBills/tests/test_report_utility_bills.rb
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,18 @@ def test_workflow_detailed_calculations
_check_monthly_bills(actual_bills, actual_monthly_bills)
end

def test_workflow_detailed_calculations_scheduled_battery
# Detailed Rate.json was renamed from Jackson Electric Member Corp - A Residential Service Senior Citizen Low Income Assistance (Effective 2017-01-01).json
# See https://github.com/NREL/OpenStudio-HPXML/issues/1444
@args_hash['hpxml_path'] = File.absolute_path(@tmp_hpxml_path)
hpxml = HPXML.new(hpxml_path: File.join(@sample_files_path, 'base-battery-scheduled.xml'))
hpxml.header.utility_bill_scenarios.add(name: 'Test 1', elec_tariff_filepath: '../../ReportUtilityBills/tests/Detailed Rate.json')
XMLHelper.write_file(hpxml.to_doc, @tmp_hpxml_path)
actual_bills, actual_monthly_bills = _test_measure()
assert_operator(actual_bills['Test 1: Total (USD)'], :>, 0)
_check_monthly_bills(actual_bills, actual_monthly_bills)
end

def test_workflow_detailed_calculations_all_electric
# Detailed Rate.json was renamed from Jackson Electric Member Corp - A Residential Service Senior Citizen Low Income Assistance (Effective 2017-01-01).json
# See https://github.com/NREL/OpenStudio-HPXML/issues/1444
Expand Down
5 changes: 5 additions & 0 deletions workflow/hpxml_inputs.json
Original file line number Diff line number Diff line change
Expand Up @@ -3145,6 +3145,11 @@
"utility_bill_electricity_marginal_rates": 0.12,
"utility_bill_natural_gas_marginal_rates": 1.1
},
"sample_files/base-misc-bills-battery-scheduled-detailed-only.xml": {
"parent_hpxml": "sample_files/base-battery-scheduled.xml",
"utility_bill_scenario_names": "Tiered, TOU, Tiered and TOU, Real-Time Pricing",
"utility_bill_electricity_filepaths": "../../ReportUtilityBills/resources/detailed_rates/Sample Tiered Rate.json, ../../ReportUtilityBills/resources/detailed_rates/Sample Time-of-Use Rate.json, ../../ReportUtilityBills/resources/detailed_rates/Sample Tiered Time-of-Use Rate.json, ../../ReportUtilityBills/resources/detailed_rates/Sample Real-Time Pricing Rate.json"
},
"sample_files/base-misc-bills-pv.xml": {
"parent_hpxml": "sample_files/base-pv.xml",
"pv_system_max_power_output": 10000,
Expand Down