Repository: NREL/hescore-hpxml

Issue:
Cooling only mini splits
We are mapping cooling only mini splits to `split_dx` as described [here](https://hescore-hpxml.readthedocs.io/en/latest/translation/cooling_system.html#id2). HEScore has been updated to handle a cooling only mini split and it should be translated as such now. 

- [ ] Code changes
- [ ] Testing
- [ ] Docs 

Discussion:

Patch:
diff --git a/docs/source/translation/cooling_system.rst b/docs/source/translation/cooling_system.rst
index 898bb821..33949281 100644
--- a/docs/source/translation/cooling_system.rst
+++ b/docs/source/translation/cooling_system.rst
@@ -59,7 +59,7 @@ is done according to the following mapping.
    =========================  ====================
    central air conditioning   split_dx
    room air conditioner       packaged_dx
-   mini-split                 split_dx
+   mini-split                 mini_split
    evaporative cooler         dec
    other                      *not translated*
    =========================  ====================
@@ -68,10 +68,6 @@ is done according to the following mapping.
    
    If an HPXML cooling system type maps to *not translated* the translation will fail.
 
-.. note::
-
-   A cooling-only mini-split is not available in HEScore, therefore the mini-split type is translated into a ``split_dx``.
-
 .. note::
 
    Prior to v2016, HEScore did not have an evaporative cooler type and these were translated as high efficiency ``split_dx`` systems.
diff --git a/examples/house4.json b/examples/house4.json
index 151eaf0e..4c5760ee 100644
--- a/examples/house4.json
+++ b/examples/house4.json
@@ -147,15 +147,7 @@
             "efficiency_method": "user",
             "efficiency": 15.0
           },
-          "hvac_distribution": [
-            {
-              "name": "duct1",
-              "location": "cond_space",
-              "fraction": 100,
-              "insulated": true,
-              "sealed": true
-            }
-          ]
+          "hvac_distribution": []
         }
       ],
       "domestic_hot_water": {
diff --git a/hescorehpxml/__init__.py b/hescorehpxml/__init__.py
index 8dd5a6a5..be36f602 100644
--- a/hescorehpxml/__init__.py
+++ b/hescorehpxml/__init__.py
@@ -13,7 +13,6 @@ import logging
 import re
 import json
 import math
-import uuid
 from lxml import etree
 from collections import defaultdict, namedtuple
 from decimal import Decimal
@@ -386,7 +385,7 @@ class HPXMLtoHEScoreTranslator(object):
             try:
                 sys_cooling['type'] = {'central air conditioning': 'split_dx',
                                        'room air conditioner': 'packaged_dx',
-                                       'mini-split': 'split_dx',
+                                       'mini-split': 'mini_split',
                                        'evaporative cooler': 'dec'}[hpxml_cooling_type]
             except KeyError:
                 raise TranslationError('HEScore does not support the HPXML CoolingSystemType %s' % hpxml_cooling_type)
@@ -1846,27 +1845,6 @@ class HPXMLtoHEScoreTranslator(object):
                     )
                 )
 
-        # Connect mini-split heat pumps to "ducts"
-        # Find heating and cooling ids that are in both singleton lists (heat pumps with no ducts)
-        singletons_to_combine = singleton_cooling_systems.intersection(singleton_heating_systems)
-        singleton_heating_systems -= singletons_to_combine
-        singleton_cooling_systems -= singletons_to_combine
-        for heatpump_id in singletons_to_combine:
-            if heating_systems[heatpump_id]['type'] != 'mini_split' or cooling_systems[heatpump_id]['type'] != 'mini_split':  # noqa: E501
-                continue
-            dummy_duct_id = str(uuid.uuid4())
-            dist_heating_cooling_map[dummy_duct_id] = (heatpump_id, heatpump_id)
-            dist_heating_map[dummy_duct_id] = heatpump_id
-            dist_cooling_map[dummy_duct_id] = heatpump_id
-
-            dummy_duct = OrderedDict()
-            dummy_duct['name'] = 'duct1'
-            dummy_duct['location'] = 'cond_space'
-            dummy_duct['fraction'] = 100
-            dummy_duct['insulated'] = True
-            dummy_duct['sealed'] = True
-            distribution_systems[dummy_duct_id] = [dummy_duct]
-
         # Check to make sure heating and cooling systems that need a distribution system have them.
         heating_sys_types_requiring_ducts = ('gchp', 'heat_pump', 'central_furnace')
         for htg_sys_id, htg_sys in list(heating_systems.items()):
@@ -1893,6 +1871,18 @@ class HPXMLtoHEScoreTranslator(object):
             avg_sys_weight = old_div(sum(weights_to_average), len(weights_to_average))
             hvac_systems_ids.add(IDsAndWeights(htg_sys_id, clg_sys_id, dist_sys_id, avg_sys_weight))
 
+        singletons_to_combine = singleton_cooling_systems.intersection(singleton_heating_systems)
+        singleton_heating_systems -= singletons_to_combine
+        singleton_cooling_systems -= singletons_to_combine
+        for heatpump_id in singletons_to_combine:
+            if heating_systems[heatpump_id]['type'] != 'mini_split' or cooling_systems[heatpump_id]['type'] != 'mini_split':  # noqa: E501
+                continue
+            hvac_systems_ids.add(IDsAndWeights(
+                heatpump_id,
+                heatpump_id,
+                None,
+                old_div(heating_systems[heatpump_id][heating_weighting_factor], weight_sum)))
+
         # Add the singletons to the list
         for htg_sys_id in singleton_heating_systems:
             hvac_systems_ids.add(IDsAndWeights(


Test patch:
diff --git a/tests/tests.py b/tests/tests.py
index a110d679..378b93ab 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -1535,7 +1535,6 @@ class TestHEScore2019Updates(unittest.TestCase, ComparatorBase):
         clg_sys_test_cooling_type = [
             'central air conditioning',
             'room air conditioner',
-            'mini-split',
             'evaporative cooler']
         hp_test_type = ['water-to-air', 'water-to-water', 'air-to-air', 'mini-split', 'ground-to-air']
 
@@ -1651,7 +1650,6 @@ class TestHEScore2019Updates(unittest.TestCase, ComparatorBase):
         # cooling system map between hpxml and hescore api
         clg_system_map = {'central air conditioning': 'split_dx',
                           'room air conditioner': 'packaged_dx',
-                          'mini-split': 'split_dx',
                           'evaporative cooler': 'dec'}
 
         for clg_system_type in clg_sys_test_cooling_type:
@@ -2140,6 +2138,80 @@ class TestHEScore2019Updates(unittest.TestCase, ComparatorBase):
             'dseab'
         )
 
+    def test_mini_split_cooling_only(self):
+        tr = self._load_xmlfile('hescore_min')
+        E = self.element_maker()
+
+        # cooling system type: mini-split + heating system
+        clg_type = self.xpath('//h:CoolingSystem[h:SystemIdentifier/@id="centralair1"]/h:CoolingSystemType')
+        clg_type.text = 'mini-split'
+
+        d = tr.hpxml_to_hescore_dict()
+        self.assertEqual(d['building']['systems']['hvac'][0]['cooling']['type'], 'mini_split')
+        self.assertEqual(d['building']['systems']['hvac'][0]['heating']['type'], 'central_furnace')
+
+        # heatpump system type: mini-split + heating system
+        heatpump = E.HeatPump(
+            E.SystemIdentifier(id='heatpump1'),
+            E.YearInstalled('2005'),
+            E.HeatPumpType('mini-split'),
+            E.HeatingCapacity('18000'),
+            E.CoolingCapacity('18000'),
+            E.FractionHeatLoadServed('0'),
+            E.FractionCoolLoadServed('1.0'),
+            E.AnnualCoolEfficiency(E.Units('SEER'), E.Value('15')),
+            E.AnnualHeatEfficiency(E.Units('HSPF'), E.Value('8.2'))
+        )
+        clg_sys = self.xpath('//h:CoolingSystem[h:SystemIdentifier/@id="centralair1"]')
+        clg_sys.addnext(heatpump)
+        clg_sys.getparent().remove(clg_sys)
+        # Add fraction to heating system for system weight calculation
+        htg_sys = self.xpath('//h:HeatingSystem[h:SystemIdentifier/@id="furnace1"]')
+        htg_sys.append(E.FractionHeatLoadServed('1.0'))
+        d = tr.hpxml_to_hescore_dict()
+        self.assertEqual(d['building']['systems']['hvac'][0]['cooling']['type'], 'mini_split')
+        self.assertEqual(d['building']['systems']['hvac'][0]['heating']['type'], 'central_furnace')
+
+        # clg system mini-split + heatpump for heating: should give error for two different heat pump systems
+        clg_sys = E.CoolingSystem(
+            E.SystemIdentifier(id='centralair'),
+            E.YearInstalled('2005'),
+            E.CoolingSystemType('mini-split'),
+            E.FractionCoolLoadServed('1.0'),
+            E.AnnualCoolingEfficiency(E.Units('SEER'), E.Value('13')),
+        )
+        heatpump.addprevious(clg_sys)
+        htg_sys.getparent().remove(htg_sys)
+        heatpump_fraction_htg = self.xpath('//h:HeatPump[h:SystemIdentifier/@id="heatpump1"]/h:FractionHeatLoadServed')
+        heatpump_fraction_clg = self.xpath('//h:HeatPump[h:SystemIdentifier/@id="heatpump1"]/h:FractionCoolLoadServed')
+        heatpump_fraction_htg.text = '1.0'
+        heatpump_fraction_clg.text = '0.0'
+        heatpump_type = self.xpath('//h:HeatPump[h:SystemIdentifier/@id="heatpump1"]/h:HeatPumpType')
+        heatpump_type.text = 'air-to-air'
+        heatpump_type.addprevious(E.DistributionSystem(idref='hvacd1'))
+        self.assertRaisesRegexp(
+            TranslationError,
+            r'Two different heat pump systems: .+ for heating, and .+ for cooling are not supported in one hvac system.', # noqa E501
+            tr.hpxml_to_hescore_dict)
+
+        # heatpump system type: mini-split + other cooling system
+        clg_sys_type = self.xpath('//h:CoolingSystem[h:SystemIdentifier/@id="centralair"]/h:CoolingSystemType')
+        clg_sys_type.text = 'central air conditioning'
+        clg_sys_type.addprevious(E.DistributionSystem(idref='hvacd1'))
+        heatpump_type.text = 'mini-split'
+        heatpump.remove(self.xpath('//h:HeatPump[h:SystemIdentifier/@id="heatpump1"]/h:DistributionSystem'))
+        d = tr.hpxml_to_hescore_dict()
+        self.assertEqual(d['building']['systems']['hvac'][0]['cooling']['type'], 'split_dx')
+        self.assertEqual(d['building']['systems']['hvac'][0]['heating']['type'], 'mini_split')
+
+        # heatpump system type: mini-split
+        clg_sys.getparent().remove(clg_sys)
+        heatpump.remove(heatpump_fraction_clg)
+        heatpump.remove(heatpump_fraction_htg)
+        d = tr.hpxml_to_hescore_dict()
+        self.assertEqual(d['building']['systems']['hvac'][0]['cooling']['type'], 'mini_split')
+        self.assertEqual(d['building']['systems']['hvac'][0]['heating']['type'], 'mini_split')
+
 
 if __name__ == "__main__":
     unittest.main()