Skip to content

Commit

Permalink
Merge pull request #704 from NREL/ansi_302_2022_batteries
Browse files Browse the repository at this point in the history
ANSI 301-2022: Batteries
  • Loading branch information
shorowit authored Jan 19, 2024
2 parents 5169bc8 + f33f354 commit 57c0421
Show file tree
Hide file tree
Showing 12 changed files with 1,686 additions and 61 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, including shared batteries ("2022C" or newer).
- **Breaking change**: Each `VentilationFan` must have one (and only one) `UsedFor...` element set to true.
- Ground source heat pump model enhancements.
- Allows `Roof/RadiantBarrier` to be omitted; defaults to false.
Expand Down
91 changes: 62 additions & 29 deletions docs/source/workflow_inputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2250,21 +2250,21 @@ Each solar electric photovoltaic (PV) system is entered as a ``/HPXML/Building/B

Many of the inputs are adopted from the `PVWatts model <https://pvwatts.nrel.gov>`_.

==================================== ======= ===== ============ ======== ======== ============================================
Element Type Units Constraints Required Default Notes
==================================== ======= ===== ============ ======== ======== ============================================
``SystemIdentifier`` id Yes Unique identifier
``IsSharedSystem`` boolean Yes Whether it serves multiple dwelling units
``Location`` string See [#]_ Yes Mounting location
``ModuleType`` string See [#]_ Yes Type of module
``Tracking`` string See [#]_ Yes Type of tracking
``ArrayAzimuth`` integer deg >= 0, <= 359 Yes Direction panels face (clockwise from North)
``ArrayTilt`` double deg >= 0, <= 90 Yes Tilt relative to horizontal
``MaxPowerOutput`` double W >= 0 Yes Peak power
``SystemLossesFraction`` double frac >= 0, <= 1 Yes System losses [#]_
``AttachedToInverter`` idref See [#]_ Yes ID of attached inverter
``extension/NumberofBedroomsServed`` integer > 1 See [#]_ Number of bedrooms served
==================================== ======= ===== ============ ======== ======== ============================================
==================================== ======= ===== ================== ======== ======== ============================================
Element Type Units Constraints Required Default Notes
==================================== ======= ===== ================== ======== ======== ============================================
``SystemIdentifier`` id Yes Unique identifier
``IsSharedSystem`` boolean Yes Whether it serves multiple dwelling units
``Location`` string See [#]_ Yes Mounting location
``ModuleType`` string See [#]_ Yes Type of module
``Tracking`` string See [#]_ Yes Type of tracking
``ArrayAzimuth`` integer deg >= 0, <= 359 Yes Direction panels face (clockwise from North)
``ArrayTilt`` double deg >= 0, <= 90 Yes Tilt relative to horizontal
``MaxPowerOutput`` double W >= 0 Yes Peak power
``SystemLossesFraction`` double frac >= 0, <= 1 Yes System losses [#]_
``AttachedToInverter`` idref See [#]_ Yes ID of attached inverter
``extension/NumberofBedroomsServed`` integer > NumberofBedrooms See [#]_ Number of bedrooms served
==================================== ======= ===== ================== ======== ======== ============================================

.. [#] Location choices are "ground" or "roof" mounted.
.. [#] ModuleType choices are "standard", "premium", or "thin film".
Expand Down Expand Up @@ -2294,8 +2294,8 @@ Many of the inputs are adopted from the `PVWatts model <https://pvwatts.nrel.gov
\- **Availability**: 3%
.. [#] AttachedToInverter must reference an ``Inverter``.
.. [#] NumberofBedroomsServed only required if IsSharedSystem is true, in which case it must be > NumberofBedrooms.
PV generation will be apportioned to the dwelling unit using its number of bedrooms divided by the total number of bedrooms served by the PV system.
.. [#] NumberofBedroomsServed only required if IsSharedSystem is true.
PV generation will be apportioned to the dwelling unit using its number of bedrooms divided by the total number of bedrooms served by the PV system per `ANSI/RESNET/ICC 301-2019 <https://codes.iccsafe.org/content/RESNET3012019P1>`_.
In addition, an inverter must be entered as a ``/HPXML/Building/BuildingDetails/Systems/Photovoltaics/Inverter``.

Expand All @@ -2309,27 +2309,60 @@ 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
``IsSharedSystem`` boolean Yes Whether it serves multiple dwelling units
``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
``extension/NumberofBedroomsServed`` integer > NumberofBedrooms See [#]_ Number of bedrooms served
==================================================== ======= ========= ======================= ======== ======== ============================================

.. [#] 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".
.. [#] NumberofBedroomsServed only required if IsSharedSystem is true.
Battery charging/discharging will be apportioned to the dwelling unit using its number of bedrooms divided by the total number of bedrooms served by the battery per ANSI/RESNET/ICC 301-2022 Addendum C.
.. note::

The battery will charge if PV production is greater than the building electrical load and the battery is below its maximum capacity.
The battery will discharge if the building electrical 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
****************

Each generator that provides on-site power is entered as a ``/HPXML/Building/BuildingDetails/Systems/extension/Generators/Generator``.

========================== ======= ======= =========== ======== ======= ============================================
Element Type Units Constraints Required Default Notes
========================== ======= ======= =========== ======== ======= ============================================
``SystemIdentifier`` id Yes Unique identifier
``IsSharedSystem`` boolean Yes Whether it serves multiple dwelling units
``FuelType`` string See [#]_ Yes Fuel type
``AnnualConsumptionkBtu`` double kBtu/yr > 0 Yes Annual fuel consumed
``AnnualOutputkWh`` double kWh/yr > 0 [#]_ Yes Annual electricity produced
``NumberofBedroomsServed`` integer > 1 See [#]_ Number of bedrooms served
========================== ======= ======= =========== ======== ======= ============================================
========================== ======= ======= ================== ======== ======= ============================================
Element Type Units Constraints Required Default Notes
========================== ======= ======= ================== ======== ======= ============================================
``SystemIdentifier`` id Yes Unique identifier
``IsSharedSystem`` boolean Yes Whether it serves multiple dwelling units
``FuelType`` string See [#]_ Yes Fuel type
``AnnualConsumptionkBtu`` double kBtu/yr > 0 Yes Annual fuel consumed
``AnnualOutputkWh`` double kWh/yr > 0 [#]_ Yes Annual electricity produced
``NumberofBedroomsServed`` integer > NumberofBedrooms See [#]_ Number of bedrooms served
========================== ======= ======= ================== ======== ======= ============================================

.. [#] FuelType choices are "natural gas", "fuel oil", "propane", "wood", or "wood pellets".
.. [#] AnnualOutputkWh must also be < AnnualConsumptionkBtu*3.412 (i.e., the generator must consume more energy than it produces).
.. [#] NumberofBedroomsServed only required if IsSharedSystem is true, in which case it must be > NumberofBedrooms.
Annual consumption and annual production will be apportioned to the dwelling unit using its number of bedrooms divided by the total number of bedrooms served by the generator.
.. [#] NumberofBedroomsServed only required if IsSharedSystem is true.
Annual consumption and annual production will be apportioned to the dwelling unit using its number of bedrooms divided by the total number of bedrooms served by the generator per `ANSI/RESNET/ICC 301-2019 <https://codes.iccsafe.org/content/RESNET3012019P1>`_.
.. note::

Expand Down
21 changes: 13 additions & 8 deletions rulesets/resources/301ruleset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1896,14 +1896,19 @@ 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')
orig_bldg.batteries.each do |orig_battery|
new_bldg.batteries.add(id: orig_battery.id,
is_shared_system: orig_battery.is_shared_system,
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,
number_of_bedrooms_served: orig_battery.number_of_bedrooms_served)
end
end
end

def self.set_systems_batteries_iad(orig_bldg, new_bldg)
Expand Down
22 changes: 15 additions & 7 deletions rulesets/resources/301validator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,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: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 @@ -1504,20 +1501,31 @@ 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'>
<sch:assert role='ERROR' test='count(h:IsSharedSystem) = 1'>Expected 1 element(s) for xpath: IsSharedSystem</sch:assert> <!-- See [BatteryType=Shared] -->
<sch:assert role='ERROR' test='count(h:BatteryType[text()="Li-ion"]) = 1'>Expected 1 element(s) for xpath: BatteryType[text()="Li-ion"]</sch:assert>
<sch:assert role='ERROR' test='count(h:Location) &lt;= 1'>Expected 0 or 1 element(s) for xpath: Location</sch:assert>
<sch:assert role='ERROR' test='h:Location[text()="conditioned space" or text()="basement - conditioned" or text()="basement - unconditioned" or text()="crawlspace - vented" or text()="crawlspace - unvented" or text()="attic - vented" or text()="attic - unvented" or text()="garage" or text()="outside"] or not(h:Location)'>Expected Location to be 'conditioned space' or 'basement - conditioned' or 'basement - unconditioned' or 'crawlspace - vented' or 'crawlspace - unvented' or 'attic - vented' or 'attic - unvented' or 'garage' or 'outside'</sch:assert>
<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>[BatteryType=Shared]</sch:title>
<sch:rule context='/h:HPXML/h:Building/h:BuildingDetails/h:Systems/h:Batteries/h:Battery[h:IsSharedSystem="true"]'>
<sch:assert role='ERROR' test='count(../../../h:BuildingSummary/h:BuildingConstruction[h:ResidentialFacilityType[text()="single-family attached" or text()="apartment unit"]]) = 1'>Expected 1 element(s) for xpath: ../../../BuildingSummary/BuildingConstruction[ResidentialFacilityType[text()="single-family attached" or text()="apartment unit"]]</sch:assert>
<sch:assert role='ERROR' test='count(h:extension/h:NumberofBedroomsServed) = 1'>Expected 1 element(s) for xpath: extension/NumberofBedroomsServed</sch:assert>
<sch:assert role='ERROR' test='number(h:extension/h:NumberofBedroomsServed) &gt; number(../../../h:BuildingSummary/h:BuildingConstruction/h:NumberofBedrooms) or not(h:extension/h:NumberofBedroomsServed) or not(../../../h:BuildingSummary/h:BuildingConstruction/h:NumberofBedrooms)'>Expected extension/NumberofBedroomsServed to be greater than ../../../BuildingSummary/BuildingConstruction/NumberofBedrooms</sch:assert>
</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
Loading

0 comments on commit 57c0421

Please sign in to comment.