Repository: NREL/hescore-hpxml

Issue:
Foundation wall insulation errors when AssemblyRValue is present
**Describe the bug**

If a foundation wall has _both_ `AssemblyRValue` and `Layer/NominalRValue` elements, it throws an error saying that a `NominalRValue` is required for all `Layer`s. 

**Error Message**

```
ERROR:TranslationError:Every foundation wall insulation layer needs a NominalRValue, fwall_id = ImpFoundationWallCrawl
```

**To Reproduce**

```xml
<FoundationWall>
  <SystemIdentifier id="ImpFoundationWallCrawl" sameas="BaseFoundationWallCrawl" />
  <Length>169.6985563</Length>
  <Height>3</Height>
  <Area>509.0956688</Area>
  <BelowGradeDepth>0.5</BelowGradeDepth>
  <AdjacentTo>ambient</AdjacentTo>
  <Insulation>
    <SystemIdentifier id="ImpConditionedCrawlInsulation1" sameas="BaseConditionedCrawlInsulation1" />
    <InsulationGrade>1</InsulationGrade>
    <AssemblyEffectiveRValue>12.8</AssemblyEffectiveRValue>
    <MisalignedInsulation>false</MisalignedInsulation>
    <Layer>
      <InstallationType>continuous</InstallationType>
      <InsulationMaterial>
        <Rigid>polyisocyanurate</Rigid>
      </InsulationMaterial>
      <NominalRValue>10.8</NominalRValue>
      <Thickness>1.5</Thickness>
    </Layer>
  </Insulation>
</FoundationWall>
```

**Expected behavior**

To not throw an error. 

**Environment (please complete the following information):**
 - OS: Mac, Linux
 - Python Version: 3.9

**Additional context**

cc @gamalielL @torstenglidden 


Discussion:

Patch:
diff --git a/examples/house7.xml b/examples/house7.xml
index b3301351..4de32c3e 100644
--- a/examples/house7.xml
+++ b/examples/house7.xml
@@ -76,7 +76,12 @@
                         <Slab>
                             <SystemIdentifier id="slab1"/>
                             <Area>900</Area>
-                            <!-- omitting insulation to see if it comes out at zero -->
+                            <PerimeterInsulation>
+                                <SystemIdentifier id='slab1perimeterins'/>
+                                <Layer>
+                                    <NominalRValue>0.0</NominalRValue>
+                                </Layer>
+                            </PerimeterInsulation>
                         </Slab>
                     </Foundation>
                 </Foundations>
diff --git a/examples/house7_v3.xml b/examples/house7_v3.xml
index 8ebe06fe..a6483cdf 100644
--- a/examples/house7_v3.xml
+++ b/examples/house7_v3.xml
@@ -89,7 +89,12 @@
                     <Slab>
                         <SystemIdentifier id="slab1"/>
                         <Area>900</Area>
-                        <!-- omitting insulation to see if it comes out at zero -->
+                        <PerimeterInsulation>
+                            <SystemIdentifier id='slab1perimeterins'/>
+                            <Layer>
+                                <NominalRValue>0.0</NominalRValue>
+                            </Layer>
+                        </PerimeterInsulation>
                     </Slab>
                 </Slabs>
                 <Windows>
diff --git a/hescorehpxml/base.py b/hescorehpxml/base.py
index 83c3306a..55ef7dd0 100644
--- a/hescorehpxml/base.py
+++ b/hescorehpxml/base.py
@@ -1698,8 +1698,7 @@ class HPXMLtoHEScoreTranslatorBase(object):
                             else:
                                 raise TranslationError(
                                     'If there is more than one FoundationWall, an Area is required for each.')
-                    fwall_assembly_rvalue = self.get_foundation_wall_assembly_rvalue(fwall, fwall)
-                    if fwall_assembly_rvalue is not None:  # TODO: Allow for AssemblyEffectiveRValue
+                    if not self.every_surface_layer_has_nominal_rvalue(fwall):
                         raise TranslationError(
                             f'Every foundation wall insulation layer needs a NominalRValue, fwall_id = {fwallid}')
                     else:
@@ -1723,8 +1722,7 @@ class HPXMLtoHEScoreTranslatorBase(object):
                         else:
                             raise TranslationError(
                                 'If there is more than one Slab, an ExposedPerimeter is required for each.')
-                    slab_assembly_rvalue = self.get_slab_assembly_rvalue(slab, slab)
-                    if slab_assembly_rvalue is not None:  # TODO: Allow for AssemblyEffectiveRValue
+                    if not self.every_surface_layer_has_nominal_rvalue(slab):
                         raise TranslationError(
                             f"Every slab insulation layer needs a NominalRValue, slab_id = {slabid}")
                     else:
@@ -2519,6 +2517,23 @@ class HPXMLtoHEScoreTranslatorBase(object):
 
         return generation
 
+    def every_surface_layer_has_nominal_rvalue(self, surf_el):
+        # This variable will be true only if every wall layer has a NominalRValue
+        if surf_el.tag.endswith('Slab'):
+            surf_ins_layers = self.xpath(surf_el, 'h:PerimeterInsulation/h:Layer', aslist=True)
+        elif surf_el.tag.endswith('FoundationWall'):
+            surf_ins_layers = self.xpath(surf_el, 'h:Insulation/h:Layer', aslist=True)
+        every_layer_has_nominal_rvalue = True
+        if surf_ins_layers:
+            for layer in surf_ins_layers:
+                if self.xpath(layer, 'h:NominalRValue') is None:
+                    every_layer_has_nominal_rvalue = False
+                    break
+        else:
+            every_layer_has_nominal_rvalue = False
+
+        return every_layer_has_nominal_rvalue
+
     def validate_hescore_inputs(self, hescore_inputs):
 
         def do_bounds_check(fieldname, value, minincl, maxincl):


Test patch:
diff --git a/tests/test_translation.py b/tests/test_translation.py
index 6a04e211..7bd9bfa1 100644
--- a/tests/test_translation.py
+++ b/tests/test_translation.py
@@ -449,6 +449,25 @@ class TestOtherHouses(unittest.TestCase, ComparatorBase):
             tr.hpxml_to_hescore
         )
 
+    def test_missing_nominal_rvalue(self):
+        tr = self._load_xmlfile('house7')
+        slab_perim_ins_nominal_rvalue = self.xpath('//h:Slab/h:PerimeterInsulation/h:Layer/h:NominalRValue')
+        slab_perim_ins_nominal_rvalue.getparent().remove(slab_perim_ins_nominal_rvalue)
+        self.assertRaisesRegex(
+            TranslationError,
+            'Every slab insulation layer needs a NominalRValue, slab_id = slab1',
+            tr.hpxml_to_hescore
+        )
+
+        tr = self._load_xmlfile('house9')
+        fwall_ins_nominal_rvalue = self.xpath('//h:FoundationWall/h:Insulation/h:Layer[2]/h:NominalRValue')
+        fwall_ins_nominal_rvalue.getparent().remove(fwall_ins_nominal_rvalue)
+        self.assertRaisesRegex(
+            TranslationError,
+            'Every foundation wall insulation layer needs a NominalRValue, fwall_id = Surface_13',
+            tr.hpxml_to_hescore
+        )
+
     def test_missing_window_area(self):
         tr = self._load_xmlfile('hescore_min')
         el = self.xpath('//h:Window[1]/h:Area')
@@ -1330,6 +1349,23 @@ class TestOtherHouses(unittest.TestCase, ComparatorBase):
             tr.hpxml_to_hescore
         )
 
+        # ignore assembly effective R-value when both assembly effective R-value and nominal R-value present
+        tr = self._load_xmlfile('house9')
+        E = self.element_maker()
+        fwall_ins = self.xpath('//h:FoundationWall[1]/h:Insulation')
+        sysid = fwall_ins.find(tr.addns('h:SystemIdentifier'))
+        sysid.addnext(E.AssemblyEffectiveRValue('6.0'))
+        hesinp = tr.hpxml_to_hescore()
+        self.assertEqual(hesinp['building']['zone']['zone_floor'][0]['foundation_insulation_level'], 0)
+
+        tr = self._load_xmlfile('house3')
+        E = self.element_maker()
+        slab_perim_ins = self.xpath('//h:Slab/h:PerimeterInsulation')
+        sysid = slab_perim_ins.find(tr.addns('h:SystemIdentifier'))
+        sysid.addnext(E.AssemblyEffectiveRValue('6.0'))
+        hesinp = tr.hpxml_to_hescore()
+        self.assertEqual(hesinp['building']['zone']['zone_floor'][0]['foundation_insulation_level'], 5)
+
     def test_duct_leakage_to_outside(self):
         tr = self._load_xmlfile('house1')
         E = self.element_maker()