Skip to content

Commit

Permalink
First pass.
Browse files Browse the repository at this point in the history
  • Loading branch information
shorowit committed Jan 9, 2024
1 parent 93bd785 commit 01ca453
Show file tree
Hide file tree
Showing 10 changed files with 1,147 additions and 25 deletions.
3 changes: 2 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

__New Features__
- Implements ANSI/RESNET/ICC Standard 301-2022 and Addendum C.
- ERICalculation/Version and CO2IndexCalculation/Version can now be "2022C" or "2022"
- ERICalculation/Version and CO2IndexCalculation/Version can now be "2022C" or "2022".
- Allows modeling electric battery storage ("2022C" or newer).
- Ground source heat pump model enhancements.
- Allows `Roof/RadiantBarrier` to be omitted; defaults to false.
- Adds more error-checking for inappropriate inputs (e.g., HVAC SHR=0 or clothes washer IMEF=0).
Expand Down
29 changes: 29 additions & 0 deletions docs/source/workflow_inputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1976,6 +1976,35 @@ In addition, an inverter must be entered as a ``/HPXML/Building/BuildingDetails/
.. [#] For homes with multiple inverters, all InverterEfficiency elements must have the same value.
.. [#] Default from PVWatts is 0.96.
HPXML Batteries
***************

A single battery can be entered as a ``/HPXML/Building/BuildingDetails/Systems/Batteries/Battery``.

==================================================== ======= ========= ======================= ======== ======== ============================================
Element Type Units Constraints Required Default Notes
==================================================== ======= ========= ======================= ======== ======== ============================================
``SystemIdentifier`` id Yes Unique identifier
``Location`` string See [#]_ No See [#]_ Location
``BatteryType`` string See [#]_ Yes Battery type
``NominalCapacity[Units="kWh"]/Value`` double kWh >= 0 Yes Nominal (total) capacity
``UsableCapacity[Units="kWh"]/Value`` double kWh >= 0, < NominalCapacity Yes Usable capacity
``RatedPowerOutput`` double W >= 0 Yes Power output under non-peak conditions
``RoundTripEfficiency`` double frac >0, <= 1 Yes Round trip efficiency
==================================================== ======= ========= ======================= ======== ======== ============================================

.. [#] Location choices are "conditioned space", "basement - conditioned", "basement - unconditioned", "crawlspace - vented", "crawlspace - unvented", "attic - vented", "attic - unvented", "garage", or "outside".
.. [#] If Location not provided, defaults to "garage" if a garage is present, otherwise "outside".
.. [#] BatteryType only choice is "Li-ion".
.. note::

The battery will charge if PV production is greater than the building load and the battery is below its maximum capacity.
The battery will discharge if the building load is greater than the PV production and the battery is above its minimum capacity.
A battery in a home without PV is not modeled.

For ERI calculations, batteries will result in a small penalty because ERI is calculated using annual energy consumption and batteries increase annual electricity consumption (due to round trip efficiency).
For CO2e Index calculations, batteries can result in a credit because CO2e Index is calculated using hourly electricity emissions factors and batteries shift when electricity consumption occurs.

HPXML Generators
****************
Expand Down
20 changes: 12 additions & 8 deletions rulesets/resources/301ruleset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1899,14 +1899,18 @@ def self.set_systems_batteries_reference(orig_bldg, new_bldg)
end

def self.set_systems_batteries_rated(orig_bldg, new_bldg)
# Temporarily disabled until RESNET allows this.
# orig_bldg.batteries.each do |orig_battery|
# new_bldg.batteries.add(id: orig_battery.id,
# type: orig_battery.type,
# location: orig_battery.location,
# nominal_capacity_kwh: orig_battery.nominal_capacity_kwh,
# usable_capacity_kwh: orig_battery.usable_capacity_kwh)
# end
if Constants.ERIVersions.index(@eri_version) >= Constants.ERIVersions.index('2022C')
# FIXME: Need to pro-rate if a shared battery.
orig_bldg.batteries.each do |orig_battery|
new_bldg.batteries.add(id: orig_battery.id,
type: orig_battery.type,
location: orig_battery.location,
nominal_capacity_kwh: orig_battery.nominal_capacity_kwh,
usable_capacity_kwh: orig_battery.usable_capacity_kwh,
rated_power_output: orig_battery.rated_power_output,
round_trip_efficiency: orig_battery.round_trip_efficiency)
end
end
end

def self.set_systems_batteries_iad(orig_bldg, new_bldg)
Expand Down
10 changes: 4 additions & 6 deletions rulesets/resources/301validator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,7 @@
<sch:assert role='ERROR' test='count(h:Systems/h:WaterHeating/h:WaterFixture) &gt;= 0'>Expected 0 or more element(s) for xpath: Systems/WaterHeating/WaterFixture</sch:assert> <!-- See [WaterFixture] -->
<sch:assert role='ERROR' test='count(h:Systems/h:SolarThermal/h:SolarThermalSystem) &lt;= 1'>Expected 0 or 1 element(s) for xpath: Systems/SolarThermal/SolarThermalSystem</sch:assert> <!-- See [SolarThermalSystem] -->
<sch:assert role='ERROR' test='count(h:Systems/h:Photovoltaics/h:PVSystem) &gt;= 0'>Expected 0 or more element(s) for xpath: Systems/Photovoltaics/PVSystem</sch:assert> <!-- See [PVSystem] -->
<!--
Temporarily disabled until RESNET allows this.
<sch:assert role='ERROR' test='count(h:Systems/h:Batteries/h:Battery) &lt;= 1'>Expected 0 or 1 element(s) for xpath: Systems/Batteries/Battery</sch:assert> See [Battery]
-->
<sch:assert role='ERROR' test='count(h:Systems/h:extension/h:Generators/h:Generator) &gt;= 0'>Expected 0 or more element(s) for xpath: Systems/extension/Generators/Generator</sch:assert> <!-- See [Generator] -->
<sch:assert role='ERROR' test='count(h:Appliances/h:ClothesWasher) &lt;= 1'>Expected 0 or 1 element(s) for xpath: Appliances/ClothesWasher</sch:assert> <!-- See [ClothesWasher] -->
<sch:assert role='ERROR' test='count(h:Appliances/h:ClothesDryer) &lt;= 1'>Expected 0 or 1 element(s) for xpath: Appliances/ClothesDryer</sch:assert> <!-- See [ClothesDryer] -->
Expand Down Expand Up @@ -1406,8 +1403,6 @@ Temporarily disabled until RESNET allows this.
</sch:rule>
</sch:pattern>

<!--
Temporarily disabled until RESNET allows this.
<sch:pattern>
<sch:title>[Battery]</sch:title>
<sch:rule context='/h:HPXML/h:Building/h:BuildingDetails/h:Systems/h:Batteries/h:Battery'>
Expand All @@ -1417,9 +1412,12 @@ Temporarily disabled until RESNET allows this.
<sch:assert role='ERROR' test='count(h:NominalCapacity[h:Units="kWh"]/h:Value) = 1'>Expected 1 element(s) for xpath: NominalCapacity[Units="kWh"]/Value</sch:assert>
<sch:assert role='ERROR' test='count(h:UsableCapacity[h:Units="kWh"]/h:Value) = 1'>Expected 1 element(s) for xpath: UsableCapacity[Units="kWh"]/Value</sch:assert>
<sch:assert role='ERROR' test='number(h:UsableCapacity[h:Units="kWh"]/h:Value) &lt; number(h:NominalCapacity[h:Units="kWh"]/h:Value) or not(h:UsableCapacity[h:Units="kWh"]/h:Value) or not(h:NominalCapacity[h:Units="kWh"]/h:Value)'>Expected UsableCapacity to be less than NominalCapacity</sch:assert>
<sch:assert role='ERROR' test='count(h:RatedPowerOutput) = 1'>Expected 1 element(s) for xpath: RatedPowerOutput</sch:assert>
<sch:assert role='ERROR' test='count(h:RoundTripEfficiency) = 1'>Expected 1 element(s) for xpath: RoundTripEfficiency</sch:assert>
<!-- Warnings -->
<sch:report role='WARN' test='number(h:RatedPowerOutput) &lt;= 1000 and number(h:RatedPowerOutput) &gt; 0'>Rated power output should typically be greater than or equal to 1000 W.</sch:report>
</sch:rule>
</sch:pattern>
-->

<sch:pattern>
<sch:title>[Generator]</sch:title>
Expand Down
1 change: 0 additions & 1 deletion rulesets/tests/test_es_zerh_pv_batteries.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ def test_pv
end

def test_pv_batteries
skip # Temporarily disabled until RESNET allows this.
[*ESConstants.AllVersions, *ZERHConstants.AllVersions].each do |program_version|
_convert_to_es_zerh('base-pv-battery.xml', program_version)
_hpxml, hpxml_bldg = _test_ruleset(program_version)
Expand Down
5 changes: 3 additions & 2 deletions rulesets/tests/test_pv_batteries.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,12 @@ def test_pv_shared
end

def test_pv_batteries
skip # Temporarily disabled until RESNET allows this.
hpxml_name = 'base-pv-battery.xml'

_all_calc_types.each do |calc_type|
_hpxml, hpxml_bldg = _test_ruleset(hpxml_name, calc_type)
if [Constants.CalcTypeERIRatedHome].include? calc_type
_check_battery(hpxml_bldg, [{ type: HPXML::BatteryTypeLithiumIon, location: HPXML::LocationOutside, nominal_capacity_kwh: 20.0, usable_capacity_kwh: 18.0 }])
_check_battery(hpxml_bldg, [{ type: HPXML::BatteryTypeLithiumIon, location: HPXML::LocationOutside, nominal_capacity_kwh: 20.0, usable_capacity_kwh: 18.0, rated_power_output: 6000, round_trip_efficiency: 0.925 }])
else
_check_battery(hpxml_bldg)
end
Expand Down Expand Up @@ -113,6 +112,8 @@ def _check_battery(hpxml_bldg, all_expected_values = [])
assert_equal(expected_values[:location], battery.location)
assert_equal(expected_values[:nominal_capacity_kwh], battery.nominal_capacity_kwh)
assert_equal(expected_values[:usable_capacity_kwh], battery.usable_capacity_kwh)
assert_equal(expected_values[:rated_power_output], battery.rated_power_output)
assert_equal(expected_values[:round_trip_efficiency], battery.round_trip_efficiency)
end
end
end
10 changes: 7 additions & 3 deletions tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2347,7 +2347,7 @@ def create_sample_hpxmls
'base-atticroof-radiant-barrier.xml',
'base-atticroof-unvented-insulated-roof.xml',
'base-atticroof-vented.xml',
# 'base-battery.xml',
'base-battery.xml',
'base-bldgtype-mf-unit.xml',
'base-bldgtype-mf-unit-adjacent-to-multiple.xml',
'base-bldgtype-mf-unit-shared-boiler-only-baseboard.xml',
Expand Down Expand Up @@ -2500,8 +2500,8 @@ def create_sample_hpxmls
'base-mechvent-supply.xml',
'base-mechvent-whole-house-fan.xml',
'base-misc-generators.xml',
'base-pv.xml']
# 'base-pv-battery.xml']
'base-pv.xml',
'base-pv-battery.xml']
include_list.each do |include_file|
if File.exist? "hpxml-measures/workflow/sample_files/#{include_file}"
FileUtils.cp("hpxml-measures/workflow/sample_files/#{include_file}", "workflow/sample_files/#{include_file}")
Expand Down Expand Up @@ -2795,6 +2795,10 @@ def create_sample_hpxmls
fail hpxml_path
end
end
hpxml_bldg.batteries.each do |battery|
battery.location = nil
battery.round_trip_efficiency = 0.925
end
zip_map = { 'USA_CO_Denver.Intl.AP.725650_TMY3.epw' => '80206',
'USA_OR_Portland.Intl.AP.726980_TMY3.epw' => '97214',
'US_CO_Boulder_AMY_2012.epw' => '80305-3447',
Expand Down
10 changes: 6 additions & 4 deletions workflow/energy_rating_index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,8 @@ def get_eec(system, type, is_dfhp_primary = nil)
generation_elec_produced = get_end_use(rated_output, EUT::Generator, FT::Elec)
generation_fuel_consumed = get_end_use(rated_output, EUT::Generator, non_elec_fuels)
results[:opp] = calculate_opp(renewable_energy_limit, renewable_elec_produced, generation_elec_produced, generation_fuel_consumed)
results[:pefrac] = calculate_pefrac(results[:teu], results[:opp])
results[:bsl] = get_end_use(rated_output, EUT::Battery, FT::Elec)
results[:pefrac] = calculate_pefrac(results[:teu], results[:opp], results[:bsl])

results[:eul_dh] = calculate_dh(rated_output)
results[:eul_mv] = calculate_mv(rated_output)
Expand Down Expand Up @@ -508,7 +509,8 @@ def get_eec(system, type, is_dfhp_primary = nil)
results[:eri_cool].map { |c| c.ec_x }.sum(0.0) +
results[:eri_dhw].map { |c| c.ec_x }.sum(0.0) +
results[:eul_la] + results[:eul_mv] + results[:eul_dh] + whf_energy +
renewable_elec_produced + generation_elec_produced + generation_fuel_consumed
renewable_elec_produced + generation_elec_produced + generation_fuel_consumed +
results[:bsl]
total_ec_x = get_fuel_use(rated_output, all_fuels, use_net: true)
if (sum_ec_x - total_ec_x).abs > 0.1
fail "Sum of energy consumptions (#{sum_ec_x.round(2)}) do not match total (#{total_ec_x.round(2)}) for Rated Home."
Expand Down Expand Up @@ -722,10 +724,10 @@ def calculate_opp(renewable_energy_limit, renewable_elec_produced, generation_el
return opp
end

def calculate_pefrac(teu, opp)
def calculate_pefrac(teu, opp, bsl)
pefrac = 1.0
if teu > 0
pefrac = (teu - opp) / teu
pefrac = (teu - opp + bsl) / teu
end
return pefrac
end
Expand Down
Loading

0 comments on commit 01ca453

Please sign in to comment.