Repository: pyenergyplus/eppy3000 (language: python)

Issue:
develop conversion functions to convert to IP units
develop conversion functions to convert to IP units


Discussion / hints:

Fix patch:
diff --git a/docs/conf.py b/docs/conf.py
index b3e5f9e..4f2343c 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -53,9 +53,9 @@
 master_doc = "index"
 
 # General information about the project.
-project = u"eppy3000"
-copyright = u"2018-2020, Santosh Philip"
-author = u"Santosh Philip"
+project = "eppy3000"
+copyright = "2018-2020, Santosh Philip"
+author = "Santosh Philip"
 
 # The version info for the project you're documenting, acts as replacement
 # for |version| and |release|, also used in various other places throughout
@@ -71,7 +71,7 @@
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = 'en'
+language = "en"
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
@@ -135,8 +135,8 @@
     (
         master_doc,
         "eppy3000.tex",
-        u"eppy3000 Documentation",
-        u"Santosh Philip",
+        "eppy3000 Documentation",
+        "Santosh Philip",
         "manual",
     ),
 ]
@@ -146,7 +146,7 @@
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [(master_doc, "eppy3000", u"eppy3000 Documentation", [author], 1)]
+man_pages = [(master_doc, "eppy3000", "eppy3000 Documentation", [author], 1)]
 
 
 # -- Options for Texinfo output ----------------------------------------
@@ -158,7 +158,7 @@
     (
         master_doc,
         "eppy3000",
-        u"eppy3000 Documentation",
+        "eppy3000 Documentation",
         author,
         "eppy3000",
         "One line description of project.",
diff --git a/docs/tutorial1.ipynb b/docs/tutorial1.ipynb
index 2cc64a2..35b24f1 100644
--- a/docs/tutorial1.ipynb
+++ b/docs/tutorial1.ipynb
@@ -320,7 +320,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Easy way to open the IDF file"
+    "## Convert to epJSON *with* eppy (in one line)"
    ]
   },
   {
diff --git a/eppy3000/__init__.py b/eppy3000/__init__.py
index dc9340d..d3e3da2 100644
--- a/eppy3000/__init__.py
+++ b/eppy3000/__init__.py
@@ -19,6 +19,7 @@
 from eppy3000.idfjsonconverter import getidfversion
 from eppy3000.idfjsonconverter import idf2json
 
+
 def openidf(idfpath, epjpath=None, schemapath=None, epjext=None):
     """open and idf file as a epj file"""
     if not epjext:
@@ -67,7 +68,8 @@ def newepj(version=None):
             "idf_order": 1
         }}
     }}
-}}""".format(str(version))
+}}""".format(
+        str(version)
+    )
     fhandle = StringIO(idfstring)
-    return EPJ(fhandle) 
-    
\ No newline at end of file
+    return EPJ(fhandle)
diff --git a/eppy3000/dbm_functions/json2dbm.py b/eppy3000/dbm_functions/json2dbm.py
index 8f99348..a84990b 100644
--- a/eppy3000/dbm_functions/json2dbm.py
+++ b/eppy3000/dbm_functions/json2dbm.py
@@ -140,6 +140,7 @@ def create_schemadbm(fname, dbmname):
         for key in d["properties"]:
             db[key] = json.dumps(d["properties"][key])
 
+
 def create_groupsindex(fname, dbmname):
     """create the groupindex for the schema"""
     db = schemaindbm.db_in_memory(fname)
@@ -152,10 +153,11 @@ def create_groupsindex(fname, dbmname):
         for key in dt:
             db[key] = json.dumps(dt[key])
 
+
 if __name__ == "__main__":
     sys.argv[1:]
     fname = sys.argv[1]
     dbmname = sys.argv[2]
     create_schemadbm(fname, dbmname)
-    create_index(fname, f'{dbmname}_ref_index')
-    create_groupsindex(fname, f'{dbmname}_group_index')
\ No newline at end of file
+    create_index(fname, f"{dbmname}_ref_index")
+    create_groupsindex(fname, f"{dbmname}_group_index")
diff --git a/eppy3000/dbm_functions/schemaindbm.py b/eppy3000/dbm_functions/schemaindbm.py
index c2576ff..992c8eb 100644
--- a/eppy3000/dbm_functions/schemaindbm.py
+++ b/eppy3000/dbm_functions/schemaindbm.py
@@ -16,7 +16,7 @@ def db_in_memory(fname=None):
     """creates the dbm in memory
 
     Loads the schema file (fname) as a dict. The dict is structured identical to the dbm. Use this when you want fast operations and don't want to use the dbm. right now it is used to creat the index for the dbm
-    
+
     Parameters
     ----------
     fname: string, StringIO
@@ -41,9 +41,9 @@ def db_in_memory(fname=None):
 
 def get_schemakeys(fname=None):
     """get all the schema keys
-    
+
     Opens the dbm as D and returns D.keys() -> a set-like object providing a view on D's keys
-    
+
     Parameters
     ----------
     fname: string, StringIO
@@ -64,9 +64,9 @@ def get_schemakeys(fname=None):
 
 def get_schemaversion(fname=None):
     """get the schema version / E+ version
-    
+
     Returns the energyplus version, that is stored in the schema
-    
+
     Parameters
     ----------
     fname: string, StringIO
@@ -90,9 +90,9 @@ def get_schemaversion(fname=None):
 
 def get_aschema(key, fname=None):
     """gets a schema
-    
+
     Returns the schema of an EPJObject, when key=EPJObject name
-    
+
     Parameters
     ----------
     key: string
@@ -121,9 +121,9 @@ def get_aschema(key, fname=None):
 
 def get_name(key, aschema=None, fname=None):
     """get the attributes of `name` field of the schema
-    
+
     Returns the attributes of the `name` field of the schema as a dict. Returns None if there is no `name` field. Extracts it from the `aschema`, or from `fname`. Using `aschema` if you already have it avoids disk access, by not using fname.
-    
+
     Parameters
     ----------
     key: string
@@ -150,11 +150,11 @@ def get_name(key, aschema=None, fname=None):
 
 def get_props(key, aschema=None, fname=None):
     """gets the properties of a schema
-    
-    Returns fields of the schema as a dict. In the schema file , it is called `properties`. Hence the name `get_props()`. Some of the fileds may have array inside with array fieldnames. The fieldname within the arrays can be extracted using `get_arrayfieldnames()`. 
-    
+
+    Returns fields of the schema as a dict. In the schema file , it is called `properties`. Hence the name `get_props()`. Some of the fileds may have array inside with array fieldnames. The fieldname within the arrays can be extracted using `get_arrayfieldnames()`.
+
     Extracts the results from the `aschema`, or from `fname`. Using `aschema` if you already have it, avoids disk access, by not using fname.
-    
+
     Parameters
     ----------
     key: string
@@ -227,10 +227,13 @@ def get_refschemakeys(fname=None):
         with dbm.dumb.open("./schema_ref_index", "r") as db:
             return db.keys()
 
+
 def get_groups(fname=None):
     """get the entire groups index"""
+
     def inner_get_groups(db):
-        return {key:json.loads(db[key]) for key in db}
+        return {key: json.loads(db[key]) for key in db}
+
     if fname:
         with dbm.dumb.open(fname, "r") as db:
             return inner_get_groups(db)
@@ -238,6 +241,7 @@ def inner_get_groups(db):
         with dbm.dumb.open("./schema_group_index", "r") as db:
             return inner_get_groups(db)
 
+
 if __name__ == "__main__":
     fname = "../eppy3000viewer/schema"
 
diff --git a/eppy3000/epj_mmapping.py b/eppy3000/epj_mmapping.py
index 17a9141..f269b1b 100644
--- a/eppy3000/epj_mmapping.py
+++ b/eppy3000/epj_mmapping.py
@@ -45,26 +45,29 @@
 from collections.abc import MutableSequence
 
 # TODO - unit tests. use samples from main() and commented out code at the end of file
-# TODO 
+# TODO
+
 
 def epjsequence2dict(epjs):
     """convert the epjsequence to a dict to compare to epj[key]"""
-    return {item['eppyname']:{key:item[key] for key in item} for item in epjs}    
+    return {item["eppyname"]: {key: item[key] for key in item} for item in epjs}
+
 
 class EpjSequence(MutableSequence):
     """simple Mutable Sequence to stes to ut some stuff"""
+
     def __init__(self, adict, theepj=None):
         super(EpjSequence, self).__init__()
         self.theepj = theepj
         self.list1 = [adict[key] for key in adict]
-        
+
     def __getitem__(self, i):
         return self.list1[i]
 
     def __setitem__(self, i, v):
         oldv = self.list1[i]
-        self.theepj[oldv['eppykey']].pop(oldv['eppyname'])
-        self.theepj[v['eppykey']][v['eppyname']] = v
+        self.theepj[oldv["eppykey"]].pop(oldv["eppyname"])
+        self.theepj[v["eppykey"]][v["eppyname"]] = v
         self.list1[i] = v
 
     def __delitem__(self, i):
@@ -74,10 +77,10 @@ def __delitem__(self, i):
 
     def __len__(self):
         return len(self.list1)
-        
+
     # __setitem__ should take care of this.
     def insert(self, i, v):
-        self.theepj[v['eppykey']][v['eppyname']] = v
+        self.theepj[v["eppykey"]][v["eppyname"]] = v
         self.list1.insert(i, v)
 
     def __str__(self):
@@ -90,15 +93,18 @@ def __repr__(self):
     # def __eq__(self, other):
     #     """Test for equality uses the IDF.model.dt list, list2."""
     #     return self.list1 == other.list1
-        
-                
+
+
 class EpjMapping(MutableMapping):
     """simple Mutable mapping to test out some stuff"""
+
     def __init__(self, theepj):
         super(EpjMapping, self).__init__()
         self.theepj = theepj
-        self.store = {key:EpjSequence(self.theepj[key], self.theepj) for key in self.theepj}
-        
+        self.store = {
+            key: EpjSequence(self.theepj[key], self.theepj) for key in self.theepj
+        }
+
     def __getitem__(self, key):
         return self.store[self._keytransform(key)]
 
@@ -112,45 +118,50 @@ def __delitem__(self, key):
 
     def __iter__(self):
         return iter(self.store)
-    
+
     def __len__(self):
         return len(self.store)
 
     def _keytransform(self, key):
         return key
-        
-    # --- not needed, but coded for readability during usage ----    
-        
+
+    # --- not needed, but coded for readability during usage ----
+
     def __str__(self):
         return self.store.__str__()
-        
+
     def __repr__(self):
         return self.store.__repr__()
-        
+
     def keys(self):
         return self.store.keys()
-        
+
+
 def eppyfields(epj):
     """put eppy fields into epj"""
     for key in epj:
         for kkey in epj[key]:
-            epj[key][kkey]['eppykey'] = key
-            epj[key][kkey]['eppyname'] = kkey
+            epj[key][kkey]["eppykey"] = key
+            epj[key][kkey]["eppyname"] = kkey
     return epj
 
+
 def main():
     # print("Main")
     # manufactur an epj
-    epj = dict(Zone=dict(zone1=dict(area=15, type="conditioned")), Wall=dict(wall1=dict(area=23, ZoneName="zone1")))
+    epj = dict(
+        Zone=dict(zone1=dict(area=15, type="conditioned")),
+        Wall=dict(wall1=dict(area=23, ZoneName="zone1")),
+    )
     epj = eppyfields(epj)
     epjmm = EpjMapping(epj)
     # print(epjmm.keys())
     # print(epjmm)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
-    
+
 # print("Main")
 # # manufacture an epj
 # epj = dict(Zone=dict(zone1=dict(area=15, type="conditioned")), Wall=dict(wall1=dict(area=23, ZoneName="zone1")))
diff --git a/eppy3000/experimental/conversiondata.py b/eppy3000/experimental/conversiondata.py
new file mode 100644
index 0000000..36fab5c
--- /dev/null
+++ b/eppy3000/experimental/conversiondata.py
@@ -0,0 +1,214 @@
+TXT = """! Default IP conversions (no \ip-units necessary)
+!      $/(m3/s)               =>   $/(ft3/min)         0.000472000059660808
+!      $/(W/K)                =>   $/(Btu/h-F)         0.52667614683731
+!      $/kW                   =>   $/(kBtuh/h)         0.293083235638921
+!      $/m2                   =>   $/ft2               0.0928939733269818
+!      $/m3                   =>   $/ft3               0.0283127014102352
+!      (kg/s)/W               =>   (lbm/sec)/(Btu/hr)  0.646078115385742
+!      1/K                    =>   1/F                 0.555555555555556
+!      1/m                    =>   1/ft                0.3048
+!      A/K                    =>   A/F                 0.555555555555556
+!      C                      =>   F                   1.8 (plus 32)
+!      cm                     =>   in                  0.3937
+!      cm2                    =>   inch2               0.15500031000062
+!      deltaC                 =>   deltaF              1.8
+!      deltaC/hr              =>   deltaF/hr           1.8
+!      deltaJ/kg              =>   deltaBtu/lb         0.0004299
+!      g/GJ                   =>   lb/MWh              0.00793664091373665
+!      g/kg                   =>   grains/lb           7
+!      g/MJ                   =>   lb/MWh              7.93664091373665
+!      g/mol                  =>   lb/mol              0.0022046
+!      g/m-s                  =>   lb/ft-s             0.000671968949659
+!      g/m-s-K                =>   lb/ft-s-F           0.000373574867724868
+!      GJ                     =>   ton-hrs             78.9889415481832
+!      J                      =>   Wh                  0.000277777777777778
+!      J/K                    =>   Btu/F               526.565
+!      J/kg                   =>   Btu/lb              0.00042986
+!      J/kg-K                 =>   Btu/lb-F            0.000239005736137667
+!      J/kg-K2                =>   Btu/lb-F2           0.000132889924714692
+!      J/kg-K3                =>   Btu/lb-F3           7.38277359526066E-05
+!      J/m2-K                 =>   Btu/ft2-F           4.89224766847393E-05
+!      J/m3                   =>   Btu/ft3             2.68096514745308E-05
+!      J/m3-K                 =>   Btu/ft3-F           1.49237004739337E-05
+!      K                      =>   R                   1.8
+!      K/m                    =>   F/ft                0.54861322767449
+!      kg                     =>   lb                  2.2046
+!      kg/J                   =>   lb/Btu              2325.83774250441
+!      kg/kg-K                =>   lb/lb-F             0.555555555555556
+!      kg/m                   =>   lb/ft               0.67196893069637
+!      kg/m2                  =>   lb/ft2              0.204794053596664
+!      kg/m3                  =>   lb/ft3              0.062428
+!      kg/m-s                 =>   lb/ft-s             0.67196893069637
+!      kg/m-s-K               =>   lb/ft-s-F           0.373316072609094
+!      kg/m-s-K2              =>   lb/ft-s-F2          0.207397818116164
+!      kg/Pa-s-m2             =>   lb/psi-s-ft2        1412.00523459398
+!      kg/s                   =>   lb/s                2.20462247603796
+!      kg/s2                  =>   lb/s2               2.2046
+!      kg/s-m                 =>   lb/s-ft             0.67196893069637
+!      kJ/kg                  =>   Btu/lb              0.429925
+!      kPa                    =>   psi                 0.145038
+!      L/day                  =>   pint/day            2.11337629827348
+!      L/GJ                   =>   gal/kWh             0.000951022349025202
+!      L/kWh                  =>   pint/kWh            2.11337629827348
+!      L/MJ                   =>   gal/kWh             0.951022349025202
+!      lux                    =>   foot-candles        0.092902267
+!      m                      =>   ft                  3.28083989501312
+!      m/hr                   =>   ft/hr               3.28083989501312
+!      m/s                    =>   ft/min              196.850393700787
+!      m/s                    =>   miles/hr            2.2369362920544
+!      m/yr                   =>   inch/yr             39.3700787401575
+!      m2                     =>   ft2                 10.7639104167097
+!      m2/m                   =>   ft2/ft              3.28083989501312
+!      m2/person              =>   ft2/person          10.764961
+!      m2/s                   =>   ft2/s               10.7639104167097
+!      m2-K/W                 =>   ft2-F-hr/Btu        5.678263
+!      m3                     =>   ft3                 35.3146667214886
+!      m3                     =>   gal                 264.172037284185
+!      m3/GJ                  =>   ft3/MWh             127.13292
+!      m3/hr                  =>   ft3/hr              35.3146667214886
+!      m3/hr-m2               =>   ft3/hr-ft2          3.28083989501312
+!      m3/hr-person           =>   ft3/hr-person       35.3146667214886
+!      m3/kg                  =>   ft3/lb              16.018
+!      m3/m2                  =>   ft3/ft2             3.28083989501312
+!      m3/MJ                  =>   ft3/kWh             127.13292
+!      m3/person              =>   ft3/person          35.3146667214886
+!      m3/s                   =>   ft3/min             2118.88000328931
+!      m3/s-m                 =>   ft3/min-ft          645.89
+!      m3/s-m2                =>   ft3/min-ft2         196.85
+!      m3/s-person            =>   ft3/min-person      2118.6438
+!      m3/s-W                 =>   (ft3/min)/(Btu/h)   621.099127332943
+!      N-m                    =>   lbf-in              8.85074900525547
+!      N-s/m2                 =>   lbf-s/ft2           0.0208857913669065
+!      Pa                     =>   psi                 0.000145037743897283
+!      percent/K              =>   percent/F           0.555555555555556
+!      person/m2              =>   person/ft2          0.0928939733269818
+!      s/m                    =>   s/ft                0.3048
+!      V/K                    =>   V/F                 0.555555555555556
+!      W                      =>   Btu/h               3.4121412858518
+!      W/((m3/s)-Pa)          =>   W/((gal/min)-ftH20) 0.188582274697355
+!      W/((m3/s)-Pa)          =>   W/((ft3/min)-inH2O) 0.117556910599482
+!      W/(m3/s)               =>   W/(ft3/min)         0.0004719475
+!      W/K                    =>   Btu/h-F             1.89563404769544
+!      W/m                    =>   Btu/h-ft            1.04072
+!      W/m2                   =>   Btu/h-ft2           0.316957210776545
+!      W/m2                   =>   W/ft2               0.09290304
+!      W/m2-K                 =>   Btu/h-ft2-F         0.176110194261872
+!      W/m2-K2                =>   Btu/h-ft2-F2        0.097826
+!      W/m-K                  =>   Btu-in/h-ft2-F      6.93481276005548
+!      W/m-K2                 =>   Btu/h-F2-ft         0.321418310071648
+!      W/m-K3                 =>   Btu/h-F3-ft         0.178565727817582
+!      W/person               =>   Btu/h-person        3.4121412858518
+!
+! Other conversions supported (needs the \ip-units code)
+!
+!      kPa                    =>   inHg                0.29523
+!      m                      =>   in                  39.3700787401575
+!      m3/hr                  =>   gal/hr              264.172037284185
+!      m3/hr-m2               =>   gal/hr-ft2          24.5423853466941
+!      m3/hr-person           =>   gal/hr-person       264.172037284185
+!      m3/m2                  =>   gal/ft2             24.5423853466941
+!      m3/person              =>   gal/person          264.172037284185
+!      m3/s                   =>   gal/min             15850.3222370511
+!      m3/s-m                 =>   gal/min-ft          4831.17821785317
+!      m3/s-W                 =>   (gal/min)/(Btu/h)   4645.27137336702
+!      Pa                     =>   ftH2O               0.00033455
+!      Pa                     =>   inH2O               0.00401463
+!      Pa                     =>   inHg                0.00029613
+!      Pa                     =>   Pa                  1
+!      W                      =>   W                   1
+!      W/(m3/s)               =>   W/(gal/min)         0.0000630902
+!      W/m2                   =>   W/m2                1
+!      W/m-K                  =>   Btu/h-ft-F          0.577796066000163
+!      W/person               =>   W/person            1
+!
+! Units fields that are not translated
+!      $
+!      1/hr
+!      A
+!      A/V
+!      Ah
+!      Availability
+!      Control
+!      cycles/hr
+!      days
+!      deg
+!      dimensionless
+!      eV
+!      hr
+!      J/J
+!      kg/kg
+!      kg-H2O/kg-air
+!      kmol
+!      kmol/s
+!      m3/m3
+!      micron
+!      minutes
+!      Mode
+!      ms
+!      ohms
+!      percent
+!      ppm
+!      rev/min
+!      s
+!      V
+!      VA
+!      W/m2 or deg C
+!      W/m2, W or deg C
+!      W/s
+!      W/W
+!      years
+! **************************************************************************
+"""
+
+# there are 3 kinds of conversions
+# 1. no ip-units -> use 1st
+# 2. expected ip-units -> use second
+# 3. use the unit as is if it is in the 3rd category
+
+
+def getconversions(msp=TXT):
+    msp = TXT.split("!\n")
+    c1 = msp[0].splitlines()
+    c1.pop(0)
+
+    si = dict()
+    ip = dict()
+
+    for c in c1:
+        cc = c.split()
+        cc.pop(0)
+        cc.pop(1)
+        try:
+            si.setdefault(cc[0], []).append((cc[1], float(cc[-1])))
+            ip.setdefault(cc[1], []).append((cc[0], float(cc[-1])))
+        except ValueError as e:
+            si.setdefault(cc[0], []).append((cc[1], cc[2:]))
+            ip.setdefault(cc[1], []).append((cc[0], cc[2:]))
+    c1 = msp[2].splitlines()
+    for c in c1:
+        cc = c.split()
+        cc.pop(0)
+        cc.pop(1)
+        try:
+            si.setdefault(cc[0], []).append((cc[1], float(cc[-1])))
+            ip.setdefault(cc[1], []).append((cc[0], float(cc[-1])))
+        except ValueError as e:
+            si.setdefault(cc[0], []).append((cc[1], cc[2:]))
+            ip.setdefault(cc[1], []).append((cc[0], cc[2:]))
+    c2 = msp[3].splitlines()
+    c2.pop(0)
+    c2.pop(-1)
+    for c in [c.split()[-1] for c in c2]:
+        si.setdefault(c, []).append((c, None))
+        ip.setdefault(c, []).append((c, None))
+    return si, ip
+
+
+def setdefault(dct):
+    newdct = {}
+    for key in dct:
+        d = dict(defaultkey=dct[key][0][0])
+        d.update(dict(dct[key]))
+        print(d)
+        newdct[key] = d
+    return newdct
diff --git a/eppy3000/experimental/epconversions.py b/eppy3000/experimental/epconversions.py
new file mode 100644
index 0000000..3b5c736
--- /dev/null
+++ b/eppy3000/experimental/epconversions.py
@@ -0,0 +1,786 @@
+# Copyright (c) 2023 Santosh Philip
+# =======================================================================
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# =======================================================================
+
+"""Conversion functions for EnergyPlus"""
+
+TXT = """! Default IP conversions (no ip-units necessary)
+!      $/(m3/s)               =>   $/(ft3/min)         0.000472000059660808
+!      $/(W/K)                =>   $/(Btu/h-F)         0.52667614683731
+!      $/kW                   =>   $/(kBtuh/h)         0.293083235638921
+!      $/m2                   =>   $/ft2               0.0928939733269818
+!      $/m3                   =>   $/ft3               0.0283127014102352
+!      (kg/s)/W               =>   (lbm/sec)/(Btu/hr)  0.646078115385742
+!      1/K                    =>   1/F                 0.555555555555556
+!      1/m                    =>   1/ft                0.3048
+!      A/K                    =>   A/F                 0.555555555555556
+!      C                      =>   F                   1.8 (plus 32)
+!      cm                     =>   in                  0.3937
+!      cm2                    =>   inch2               0.15500031000062
+!      deltaC                 =>   deltaF              1.8
+!      deltaC/hr              =>   deltaF/hr           1.8
+!      deltaJ/kg              =>   deltaBtu/lb         0.0004299
+!      g/GJ                   =>   lb/MWh              0.00793664091373665
+!      g/kg                   =>   grains/lb           7
+!      g/MJ                   =>   lb/MWh              7.93664091373665
+!      g/mol                  =>   lb/mol              0.0022046
+!      g/m-s                  =>   lb/ft-s             0.000671968949659
+!      g/m-s-K                =>   lb/ft-s-F           0.000373574867724868
+!      GJ                     =>   ton-hrs             78.9889415481832
+!      J                      =>   Wh                  0.000277777777777778
+!      J/K                    =>   Btu/F               526.565
+!      J/kg                   =>   Btu/lb              0.00042986
+!      J/kg-K                 =>   Btu/lb-F            0.000239005736137667
+!      J/kg-K2                =>   Btu/lb-F2           0.000132889924714692
+!      J/kg-K3                =>   Btu/lb-F3           7.38277359526066E-05
+!      J/m2-K                 =>   Btu/ft2-F           4.89224766847393E-05
+!      J/m3                   =>   Btu/ft3             2.68096514745308E-05
+!      J/m3-K                 =>   Btu/ft3-F           1.49237004739337E-05
+!      K                      =>   R                   1.8
+!      K/m                    =>   F/ft                0.54861322767449
+!      kg                     =>   lb                  2.2046
+!      kg/J                   =>   lb/Btu              2325.83774250441
+!      kg/kg-K                =>   lb/lb-F             0.555555555555556
+!      kg/m                   =>   lb/ft               0.67196893069637
+!      kg/m2                  =>   lb/ft2              0.204794053596664
+!      kg/m3                  =>   lb/ft3              0.062428
+!      kg/m-s                 =>   lb/ft-s             0.67196893069637
+!      kg/m-s-K               =>   lb/ft-s-F           0.373316072609094
+!      kg/m-s-K2              =>   lb/ft-s-F2          0.207397818116164
+!      kg/Pa-s-m2             =>   lb/psi-s-ft2        1412.00523459398
+!      kg/s                   =>   lb/s                2.20462247603796
+!      kg/s2                  =>   lb/s2               2.2046
+!      kg/s-m                 =>   lb/s-ft             0.67196893069637
+!      kJ/kg                  =>   Btu/lb              0.429925
+!      kPa                    =>   psi                 0.145038
+!      L/day                  =>   pint/day            2.11337629827348
+!      L/GJ                   =>   gal/kWh             0.000951022349025202
+!      L/kWh                  =>   pint/kWh            2.11337629827348
+!      L/MJ                   =>   gal/kWh             0.951022349025202
+!      lux                    =>   foot-candles        0.092902267
+!      m                      =>   ft                  3.28083989501312
+!      m/hr                   =>   ft/hr               3.28083989501312
+!      m/s                    =>   ft/min              196.850393700787
+!      m/s                    =>   miles/hr            2.2369362920544
+!      m/yr                   =>   inch/yr             39.3700787401575
+!      m2                     =>   ft2                 10.7639104167097
+!      m2/m                   =>   ft2/ft              3.28083989501312
+!      m2/person              =>   ft2/person          10.764961
+!      m2/s                   =>   ft2/s               10.7639104167097
+!      m2-K/W                 =>   ft2-F-hr/Btu        5.678263
+!      m3                     =>   ft3                 35.3146667214886
+!      m3                     =>   gal                 264.172037284185
+!      m3/GJ                  =>   ft3/MWh             127.13292
+!      m3/hr                  =>   ft3/hr              35.3146667214886
+!      m3/hr-m2               =>   ft3/hr-ft2          3.28083989501312
+!      m3/hr-person           =>   ft3/hr-person       35.3146667214886
+!      m3/kg                  =>   ft3/lb              16.018
+!      m3/m2                  =>   ft3/ft2             3.28083989501312
+!      m3/MJ                  =>   ft3/kWh             127.13292
+!      m3/person              =>   ft3/person          35.3146667214886
+!      m3/s                   =>   ft3/min             2118.88000328931
+!      m3/s-m                 =>   ft3/min-ft          645.89
+!      m3/s-m2                =>   ft3/min-ft2         196.85
+!      m3/s-person            =>   ft3/min-person      2118.6438
+!      m3/s-W                 =>   (ft3/min)/(Btu/h)   621.099127332943
+!      N-m                    =>   lbf-in              8.85074900525547
+!      N-s/m2                 =>   lbf-s/ft2           0.0208857913669065
+!      Pa                     =>   psi                 0.000145037743897283
+!      percent/K              =>   percent/F           0.555555555555556
+!      person/m2              =>   person/ft2          0.0928939733269818
+!      s/m                    =>   s/ft                0.3048
+!      V/K                    =>   V/F                 0.555555555555556
+!      W                      =>   Btu/h               3.4121412858518
+!      W/((m3/s)-Pa)          =>   W/((gal/min)-ftH20) 0.188582274697355
+!      W/((m3/s)-Pa)          =>   W/((ft3/min)-inH2O) 0.117556910599482
+!      W/(m3/s)               =>   W/(ft3/min)         0.0004719475
+!      W/K                    =>   Btu/h-F             1.89563404769544
+!      W/m                    =>   Btu/h-ft            1.04072
+!      W/m2                   =>   Btu/h-ft2           0.316957210776545
+!      W/m2                   =>   W/ft2               0.09290304
+!      W/m2-K                 =>   Btu/h-ft2-F         0.176110194261872
+!      W/m2-K2                =>   Btu/h-ft2-F2        0.097826
+!      W/m-K                  =>   Btu-in/h-ft2-F      6.93481276005548
+!      W/m-K2                 =>   Btu/h-F2-ft         0.321418310071648
+!      W/m-K3                 =>   Btu/h-F3-ft         0.178565727817582
+!      W/person               =>   Btu/h-person        3.4121412858518
+!
+! Other conversions supported (needs the \ip-units code)
+!
+!      kPa                    =>   inHg                0.29523
+!      m                      =>   in                  39.3700787401575
+!      m3/hr                  =>   gal/hr              264.172037284185
+!      m3/hr-m2               =>   gal/hr-ft2          24.5423853466941
+!      m3/hr-person           =>   gal/hr-person       264.172037284185
+!      m3/m2                  =>   gal/ft2             24.5423853466941
+!      m3/person              =>   gal/person          264.172037284185
+!      m3/s                   =>   gal/min             15850.3222370511
+!      m3/s-m                 =>   gal/min-ft          4831.17821785317
+!      m3/s-W                 =>   (gal/min)/(Btu/h)   4645.27137336702
+!      Pa                     =>   ftH2O               0.00033455
+!      Pa                     =>   inH2O               0.00401463
+!      Pa                     =>   inHg                0.00029613
+!      Pa                     =>   Pa                  1
+!      W                      =>   W                   1
+!      W/(m3/s)               =>   W/(gal/min)         0.0000630902
+!      W/m2                   =>   W/m2                1
+!      W/m-K                  =>   Btu/h-ft-F          0.577796066000163
+!      W/person               =>   W/person            1
+!
+! Units fields that are not translated
+!      $
+!      1/hr
+!      A
+!      A/V
+!      Ah
+!      Availability
+!      Control
+!      cycles/hr
+!      days
+!      deg
+!      dimensionless
+!      eV
+!      hr
+!      J/J
+!      kg/kg
+!      kg-H2O/kg-air
+!      kmol
+!      kmol/s
+!      m3/m3
+!      micron
+!      minutes
+!      Mode
+!      ms
+!      ohms
+!      percent
+!      ppm
+!      rev/min
+!      s
+!      V
+!      VA
+!      W/m2 or deg C
+!      W/m2, W or deg C
+!      W/s
+!      W/W
+!      years
+! **************************************************************************
+"""
+
+# there are 3 kinds of conversions
+# 1. no ip-units -> use 1st
+# 2. expected ip-units -> use second
+# 3. use the unit as is if it is in the 3rd category
+
+SI = {
+    "$": {"$": None, "defaultkey": "$"},
+    "$/(W/K)": {"$/(Btu/h-F)": 0.52667614683731, "defaultkey": "$/(Btu/h-F)"},
+    "$/(m3/s)": {"$/(ft3/min)": 0.000472000059660808, "defaultkey": "$/(ft3/min)"},
+    "$/kW": {"$/(kBtuh/h)": 0.293083235638921, "defaultkey": "$/(kBtuh/h)"},
+    "$/m2": {"$/ft2": 0.0928939733269818, "defaultkey": "$/ft2"},
+    "$/m3": {"$/ft3": 0.0283127014102352, "defaultkey": "$/ft3"},
+    "(kg/s)/W": {
+        "(lbm/sec)/(Btu/hr)": 0.646078115385742,
+        "defaultkey": "(lbm/sec)/(Btu/hr)",
+    },
+    "1/K": {"1/F": 0.555555555555556, "defaultkey": "1/F"},
+    "1/hr": {"1/hr": None, "defaultkey": "1/hr"},
+    "1/m": {"1/ft": 0.3048, "defaultkey": "1/ft"},
+    "A": {"A": None, "defaultkey": "A"},
+    "A/K": {"A/F": 0.555555555555556, "defaultkey": "A/F"},
+    "A/V": {"A/V": None, "defaultkey": "A/V"},
+    "Ah": {"Ah": None, "defaultkey": "Ah"},
+    "Availability": {"Availability": None, "defaultkey": "Availability"},
+    "C": {"C": None, "F": ["1.8", "(plus", "32)"], "defaultkey": "F"},
+    "Control": {"Control": None, "defaultkey": "Control"},
+    "GJ": {"defaultkey": "ton-hrs", "ton-hrs": 78.9889415481832},
+    "J": {"Wh": 0.000277777777777778, "defaultkey": "Wh"},
+    "J/J": {"J/J": None, "defaultkey": "J/J"},
+    "J/K": {"Btu/F": 526.565, "defaultkey": "Btu/F"},
+    "J/kg": {"Btu/lb": 0.00042986, "defaultkey": "Btu/lb"},
+    "J/kg-K": {"Btu/lb-F": 0.000239005736137667, "defaultkey": "Btu/lb-F"},
+    "J/kg-K2": {"Btu/lb-F2": 0.000132889924714692, "defaultkey": "Btu/lb-F2"},
+    "J/kg-K3": {"Btu/lb-F3": 7.38277359526066e-05, "defaultkey": "Btu/lb-F3"},
+    "J/m2-K": {"Btu/ft2-F": 4.89224766847393e-05, "defaultkey": "Btu/ft2-F"},
+    "J/m3": {"Btu/ft3": 2.68096514745308e-05, "defaultkey": "Btu/ft3"},
+    "J/m3-K": {"Btu/ft3-F": 1.49237004739337e-05, "defaultkey": "Btu/ft3-F"},
+    "K": {"R": 1.8, "defaultkey": "R"},
+    "K/m": {"F/ft": 0.54861322767449, "defaultkey": "F/ft"},
+    "L/GJ": {"defaultkey": "gal/kWh", "gal/kWh": 0.000951022349025202},
+    "L/MJ": {"defaultkey": "gal/kWh", "gal/kWh": 0.951022349025202},
+    "L/day": {"defaultkey": "pint/day", "pint/day": 2.11337629827348},
+    "L/kWh": {"defaultkey": "pint/kWh", "pint/kWh": 2.11337629827348},
+    "Mode": {"Mode": None, "defaultkey": "Mode"},
+    "N-m": {"defaultkey": "lbf-in", "lbf-in": 8.85074900525547},
+    "N-s/m2": {"defaultkey": "lbf-s/ft2", "lbf-s/ft2": 0.0208857913669065},
+    "Pa": {
+        "Pa": 1.0,
+        "defaultkey": "psi",
+        "ftH2O": 0.00033455,
+        "inH2O": 0.00401463,
+        "inHg": 0.00029613,
+        "psi": 0.000145037743897283,
+    },
+    "V": {"V": None, "defaultkey": "V"},
+    "V/K": {"V/F": 0.555555555555556, "defaultkey": "V/F"},
+    "VA": {"VA": None, "defaultkey": "VA"},
+    "W": {"Btu/h": 3.4121412858518, "W": 1.0, "defaultkey": "Btu/h"},
+    "W/((m3/s)-Pa)": {
+        "W/((ft3/min)-inH2O)": 0.117556910599482,
+        "W/((gal/min)-ftH20)": 0.188582274697355,
+        "defaultkey": "W/((gal/min)-ftH20)",
+    },
+    "W/(m3/s)": {
+        "W/(ft3/min)": 0.0004719475,
+        "W/(gal/min)": 6.30902e-05,
+        "defaultkey": "W/(ft3/min)",
+    },
+    "W/K": {"Btu/h-F": 1.89563404769544, "defaultkey": "Btu/h-F"},
+    "W/W": {"W/W": None, "defaultkey": "W/W"},
+    "W/m": {"Btu/h-ft": 1.04072, "defaultkey": "Btu/h-ft"},
+    "W/m-K": {
+        "Btu-in/h-ft2-F": 6.93481276005548,
+        "Btu/h-ft-F": 0.577796066000163,
+        "defaultkey": "Btu-in/h-ft2-F",
+    },
+    "W/m-K2": {"Btu/h-F2-ft": 0.321418310071648, "defaultkey": "Btu/h-F2-ft"},
+    "W/m-K3": {"Btu/h-F3-ft": 0.178565727817582, "defaultkey": "Btu/h-F3-ft"},
+    "W/m2": {
+        "Btu/h-ft2": 0.316957210776545,
+        "W/ft2": 0.09290304,
+        "W/m2": 1.0,
+        "defaultkey": "Btu/h-ft2",
+    },
+    "W/m2-K": {"Btu/h-ft2-F": 0.176110194261872, "defaultkey": "Btu/h-ft2-F"},
+    "W/m2-K2": {"Btu/h-ft2-F2": 0.097826, "defaultkey": "Btu/h-ft2-F2"},
+    "W/person": {
+        "Btu/h-person": 3.4121412858518,
+        "W/person": 1.0,
+        "defaultkey": "Btu/h-person",
+    },
+    "W/s": {"W/s": None, "defaultkey": "W/s"},
+    "cm": {"defaultkey": "in", "in": 0.3937},
+    "cm2": {"defaultkey": "inch2", "inch2": 0.15500031000062},
+    "cycles/hr": {"cycles/hr": None, "defaultkey": "cycles/hr"},
+    "days": {"days": None, "defaultkey": "days"},
+    "deg": {"defaultkey": "deg", "deg": None},
+    "deltaC": {"defaultkey": "deltaF", "deltaF": 1.8},
+    "deltaC/hr": {"defaultkey": "deltaF/hr", "deltaF/hr": 1.8},
+    "deltaJ/kg": {"defaultkey": "deltaBtu/lb", "deltaBtu/lb": 0.0004299},
+    "dimensionless": {"defaultkey": "dimensionless", "dimensionless": None},
+    "eV": {"defaultkey": "eV", "eV": None},
+    "g/GJ": {"defaultkey": "lb/MWh", "lb/MWh": 0.00793664091373665},
+    "g/MJ": {"defaultkey": "lb/MWh", "lb/MWh": 7.93664091373665},
+    "g/kg": {"defaultkey": "grains/lb", "grains/lb": 7.0},
+    "g/m-s": {"defaultkey": "lb/ft-s", "lb/ft-s": 0.000671968949659},
+    "g/m-s-K": {"defaultkey": "lb/ft-s-F", "lb/ft-s-F": 0.000373574867724868},
+    "g/mol": {"defaultkey": "lb/mol", "lb/mol": 0.0022046},
+    "hr": {"defaultkey": "hr", "hr": None},
+    "kJ/kg": {"Btu/lb": 0.429925, "defaultkey": "Btu/lb"},
+    "kPa": {"defaultkey": "psi", "inHg": 0.29523, "psi": 0.145038},
+    "kg": {"defaultkey": "lb", "lb": 2.2046},
+    "kg-H2O/kg-air": {"defaultkey": "kg-H2O/kg-air", "kg-H2O/kg-air": None},
+    "kg/J": {"defaultkey": "lb/Btu", "lb/Btu": 2325.83774250441},
+    "kg/Pa-s-m2": {"defaultkey": "lb/psi-s-ft2", "lb/psi-s-ft2": 1412.00523459398},
+    "kg/kg": {"defaultkey": "kg/kg", "kg/kg": None},
+    "kg/kg-K": {"defaultkey": "lb/lb-F", "lb/lb-F": 0.555555555555556},
+    "kg/m": {"defaultkey": "lb/ft", "lb/ft": 0.67196893069637},
+    "kg/m-s": {"defaultkey": "lb/ft-s", "lb/ft-s": 0.67196893069637},
+    "kg/m-s-K": {"defaultkey": "lb/ft-s-F", "lb/ft-s-F": 0.373316072609094},
+    "kg/m-s-K2": {"defaultkey": "lb/ft-s-F2", "lb/ft-s-F2": 0.207397818116164},
+    "kg/m2": {"defaultkey": "lb/ft2", "lb/ft2": 0.204794053596664},
+    "kg/m3": {"defaultkey": "lb/ft3", "lb/ft3": 0.062428},
+    "kg/s": {"defaultkey": "lb/s", "lb/s": 2.20462247603796},
+    "kg/s-m": {"defaultkey": "lb/s-ft", "lb/s-ft": 0.67196893069637},
+    "kg/s2": {"defaultkey": "lb/s2", "lb/s2": 2.2046},
+    "kmol": {"defaultkey": "kmol", "kmol": None},
+    "kmol/s": {"defaultkey": "kmol/s", "kmol/s": None},
+    "lux": {"defaultkey": "foot-candles", "foot-candles": 0.092902267},
+    "m": {"defaultkey": "ft", "ft": 3.28083989501312, "in": 39.3700787401575},
+    "m/hr": {"defaultkey": "ft/hr", "ft/hr": 3.28083989501312},
+    "m/s": {
+        "defaultkey": "ft/min",
+        "ft/min": 196.850393700787,
+        "miles/hr": 2.2369362920544,
+    },
+    "m/yr": {"defaultkey": "inch/yr", "inch/yr": 39.3700787401575},
+    "m2": {"defaultkey": "ft2", "ft2": 10.7639104167097},
+    "m2-K/W": {"defaultkey": "ft2-F-hr/Btu", "ft2-F-hr/Btu": 5.678263},
+    "m2/m": {"defaultkey": "ft2/ft", "ft2/ft": 3.28083989501312},
+    "m2/person": {"defaultkey": "ft2/person", "ft2/person": 10.764961},
+    "m2/s": {"defaultkey": "ft2/s", "ft2/s": 10.7639104167097},
+    "m3": {"defaultkey": "ft3", "ft3": 35.3146667214886, "gal": 264.172037284185},
+    "m3/GJ": {"defaultkey": "ft3/MWh", "ft3/MWh": 127.13292},
+    "m3/MJ": {"defaultkey": "ft3/kWh", "ft3/kWh": 127.13292},
+    "m3/hr": {
+        "defaultkey": "ft3/hr",
+        "ft3/hr": 35.3146667214886,
+        "gal/hr": 264.172037284185,
+    },
+    "m3/hr-m2": {
+        "defaultkey": "ft3/hr-ft2",
+        "ft3/hr-ft2": 3.28083989501312,
+        "gal/hr-ft2": 24.5423853466941,
+    },
+    "m3/hr-person": {
+        "defaultkey": "ft3/hr-person",
+        "ft3/hr-person": 35.3146667214886,
+        "gal/hr-person": 264.172037284185,
+    },
+    "m3/kg": {"defaultkey": "ft3/lb", "ft3/lb": 16.018},
+    "m3/m2": {
+        "defaultkey": "ft3/ft2",
+        "ft3/ft2": 3.28083989501312,
+        "gal/ft2": 24.5423853466941,
+    },
+    "m3/m3": {"defaultkey": "m3/m3", "m3/m3": None},
+    "m3/person": {
+        "defaultkey": "ft3/person",
+        "ft3/person": 35.3146667214886,
+        "gal/person": 264.172037284185,
+    },
+    "m3/s": {
+        "defaultkey": "ft3/min",
+        "ft3/min": 2118.88000328931,
+        "gal/min": 15850.3222370511,
+    },
+    "m3/s-W": {
+        "(ft3/min)/(Btu/h)": 621.099127332943,
+        "(gal/min)/(Btu/h)": 4645.27137336702,
+        "defaultkey": "(ft3/min)/(Btu/h)",
+    },
+    "m3/s-m": {
+        "defaultkey": "ft3/min-ft",
+        "ft3/min-ft": 645.89,
+        "gal/min-ft": 4831.17821785317,
+    },
+    "m3/s-m2": {"defaultkey": "ft3/min-ft2", "ft3/min-ft2": 196.85},
+    "m3/s-person": {"defaultkey": "ft3/min-person", "ft3/min-person": 2118.6438},
+    "micron": {"defaultkey": "micron", "micron": None},
+    "minutes": {"defaultkey": "minutes", "minutes": None},
+    "ms": {"defaultkey": "ms", "ms": None},
+    "ohms": {"defaultkey": "ohms", "ohms": None},
+    "percent": {"defaultkey": "percent", "percent": None},
+    "percent/K": {"defaultkey": "percent/F", "percent/F": 0.555555555555556},
+    "person/m2": {"defaultkey": "person/ft2", "person/ft2": 0.0928939733269818},
+    "ppm": {"defaultkey": "ppm", "ppm": None},
+    "rev/min": {"defaultkey": "rev/min", "rev/min": None},
+    "s": {"defaultkey": "s", "s": None},
+    "s/m": {"defaultkey": "s/ft", "s/ft": 0.3048},
+    "years": {"defaultkey": "years", "years": None},
+}
+
+IP = {
+    "$": {"$": None, "defaultkey": "$"},
+    "$/(Btu/h-F)": {"$/(W/K)": 0.52667614683731, "defaultkey": "$/(W/K)"},
+    "$/(ft3/min)": {"$/(m3/s)": 0.000472000059660808, "defaultkey": "$/(m3/s)"},
+    "$/(kBtuh/h)": {"$/kW": 0.293083235638921, "defaultkey": "$/kW"},
+    "$/ft2": {"$/m2": 0.0928939733269818, "defaultkey": "$/m2"},
+    "$/ft3": {"$/m3": 0.0283127014102352, "defaultkey": "$/m3"},
+    "(ft3/min)/(Btu/h)": {"defaultkey": "m3/s-W", "m3/s-W": 621.099127332943},
+    "(gal/min)/(Btu/h)": {"defaultkey": "m3/s-W", "m3/s-W": 4645.27137336702},
+    "(lbm/sec)/(Btu/hr)": {"(kg/s)/W": 0.646078115385742, "defaultkey": "(kg/s)/W"},
+    "1/F": {"1/K": 0.555555555555556, "defaultkey": "1/K"},
+    "1/ft": {"1/m": 0.3048, "defaultkey": "1/m"},
+    "1/hr": {"1/hr": None, "defaultkey": "1/hr"},
+    "A": {"A": None, "defaultkey": "A"},
+    "A/F": {"A/K": 0.555555555555556, "defaultkey": "A/K"},
+    "A/V": {"A/V": None, "defaultkey": "A/V"},
+    "Ah": {"Ah": None, "defaultkey": "Ah"},
+    "Availability": {"Availability": None, "defaultkey": "Availability"},
+    "Btu-in/h-ft2-F": {"W/m-K": 6.93481276005548, "defaultkey": "W/m-K"},
+    "Btu/F": {"J/K": 526.565, "defaultkey": "J/K"},
+    "Btu/ft2-F": {"J/m2-K": 4.89224766847393e-05, "defaultkey": "J/m2-K"},
+    "Btu/ft3": {"J/m3": 2.68096514745308e-05, "defaultkey": "J/m3"},
+    "Btu/ft3-F": {"J/m3-K": 1.49237004739337e-05, "defaultkey": "J/m3-K"},
+    "Btu/h": {"W": 3.4121412858518, "defaultkey": "W"},
+    "Btu/h-F": {"W/K": 1.89563404769544, "defaultkey": "W/K"},
+    "Btu/h-F2-ft": {"W/m-K2": 0.321418310071648, "defaultkey": "W/m-K2"},
+    "Btu/h-F3-ft": {"W/m-K3": 0.178565727817582, "defaultkey": "W/m-K3"},
+    "Btu/h-ft": {"W/m": 1.04072, "defaultkey": "W/m"},
+    "Btu/h-ft-F": {"W/m-K": 0.577796066000163, "defaultkey": "W/m-K"},
+    "Btu/h-ft2": {"W/m2": 0.316957210776545, "defaultkey": "W/m2"},
+    "Btu/h-ft2-F": {"W/m2-K": 0.176110194261872, "defaultkey": "W/m2-K"},
+    "Btu/h-ft2-F2": {"W/m2-K2": 0.097826, "defaultkey": "W/m2-K2"},
+    "Btu/h-person": {"W/person": 3.4121412858518, "defaultkey": "W/person"},
+    "Btu/lb": {"J/kg": 0.00042986, "defaultkey": "J/kg", "kJ/kg": 0.429925},
+    "Btu/lb-F": {"J/kg-K": 0.000239005736137667, "defaultkey": "J/kg-K"},
+    "Btu/lb-F2": {"J/kg-K2": 0.000132889924714692, "defaultkey": "J/kg-K2"},
+    "Btu/lb-F3": {"J/kg-K3": 7.38277359526066e-05, "defaultkey": "J/kg-K3"},
+    "C": {"C": None, "defaultkey": "C"},
+    "Control": {"Control": None, "defaultkey": "Control"},
+    "F": {"C": ["1.8", "(plus", "32)"], "defaultkey": "C"},
+    "F/ft": {"K/m": 0.54861322767449, "defaultkey": "K/m"},
+    "J/J": {"J/J": None, "defaultkey": "J/J"},
+    "Mode": {"Mode": None, "defaultkey": "Mode"},
+    "Pa": {"Pa": 1.0, "defaultkey": "Pa"},
+    "R": {"K": 1.8, "defaultkey": "K"},
+    "V": {"V": None, "defaultkey": "V"},
+    "V/F": {"V/K": 0.555555555555556, "defaultkey": "V/K"},
+    "VA": {"VA": None, "defaultkey": "VA"},
+    "W": {"W": 1.0, "defaultkey": "W"},
+    "W/((ft3/min)-inH2O)": {
+        "W/((m3/s)-Pa)": 0.117556910599482,
+        "defaultkey": "W/((m3/s)-Pa)",
+    },
+    "W/((gal/min)-ftH20)": {
+        "W/((m3/s)-Pa)": 0.188582274697355,
+        "defaultkey": "W/((m3/s)-Pa)",
+    },
+    "W/(ft3/min)": {"W/(m3/s)": 0.0004719475, "defaultkey": "W/(m3/s)"},
+    "W/(gal/min)": {"W/(m3/s)": 6.30902e-05, "defaultkey": "W/(m3/s)"},
+    "W/W": {"W/W": None, "defaultkey": "W/W"},
+    "W/ft2": {"W/m2": 0.09290304, "defaultkey": "W/m2"},
+    "W/m2": {"W/m2": 1.0, "defaultkey": "W/m2"},
+    "W/person": {"W/person": 1.0, "defaultkey": "W/person"},
+    "W/s": {"W/s": None, "defaultkey": "W/s"},
+    "Wh": {"J": 0.000277777777777778, "defaultkey": "J"},
+    "cycles/hr": {"cycles/hr": None, "defaultkey": "cycles/hr"},
+    "days": {"days": None, "defaultkey": "days"},
+    "deg": {"defaultkey": "deg", "deg": None},
+    "deltaBtu/lb": {"defaultkey": "deltaJ/kg", "deltaJ/kg": 0.0004299},
+    "deltaF": {"defaultkey": "deltaC", "deltaC": 1.8},
+    "deltaF/hr": {"defaultkey": "deltaC/hr", "deltaC/hr": 1.8},
+    "dimensionless": {"defaultkey": "dimensionless", "dimensionless": None},
+    "eV": {"defaultkey": "eV", "eV": None},
+    "foot-candles": {"defaultkey": "lux", "lux": 0.092902267},
+    "ft": {"defaultkey": "m", "m": 3.28083989501312},
+    "ft/hr": {"defaultkey": "m/hr", "m/hr": 3.28083989501312},
+    "ft/min": {"defaultkey": "m/s", "m/s": 196.850393700787},
+    "ft2": {"defaultkey": "m2", "m2": 10.7639104167097},
+    "ft2-F-hr/Btu": {"defaultkey": "m2-K/W", "m2-K/W": 5.678263},
+    "ft2/ft": {"defaultkey": "m2/m", "m2/m": 3.28083989501312},
+    "ft2/person": {"defaultkey": "m2/person", "m2/person": 10.764961},
+    "ft2/s": {"defaultkey": "m2/s", "m2/s": 10.7639104167097},
+    "ft3": {"defaultkey": "m3", "m3": 35.3146667214886},
+    "ft3/MWh": {"defaultkey": "m3/GJ", "m3/GJ": 127.13292},
+    "ft3/ft2": {"defaultkey": "m3/m2", "m3/m2": 3.28083989501312},
+    "ft3/hr": {"defaultkey": "m3/hr", "m3/hr": 35.3146667214886},
+    "ft3/hr-ft2": {"defaultkey": "m3/hr-m2", "m3/hr-m2": 3.28083989501312},
+    "ft3/hr-person": {"defaultkey": "m3/hr-person", "m3/hr-person": 35.3146667214886},
+    "ft3/kWh": {"defaultkey": "m3/MJ", "m3/MJ": 127.13292},
+    "ft3/lb": {"defaultkey": "m3/kg", "m3/kg": 16.018},
+    "ft3/min": {"defaultkey": "m3/s", "m3/s": 2118.88000328931},
+    "ft3/min-ft": {"defaultkey": "m3/s-m", "m3/s-m": 645.89},
+    "ft3/min-ft2": {"defaultkey": "m3/s-m2", "m3/s-m2": 196.85},
+    "ft3/min-person": {"defaultkey": "m3/s-person", "m3/s-person": 2118.6438},
+    "ft3/person": {"defaultkey": "m3/person", "m3/person": 35.3146667214886},
+    "ftH2O": {"Pa": 0.00033455, "defaultkey": "Pa"},
+    "gal": {"defaultkey": "m3", "m3": 264.172037284185},
+    "gal/ft2": {"defaultkey": "m3/m2", "m3/m2": 24.5423853466941},
+    "gal/hr": {"defaultkey": "m3/hr", "m3/hr": 264.172037284185},
+    "gal/hr-ft2": {"defaultkey": "m3/hr-m2", "m3/hr-m2": 24.5423853466941},
+    "gal/hr-person": {"defaultkey": "m3/hr-person", "m3/hr-person": 264.172037284185},
+    "gal/kWh": {
+        "L/GJ": 0.000951022349025202,
+        "L/MJ": 0.951022349025202,
+        "defaultkey": "L/GJ",
+    },
+    "gal/min": {"defaultkey": "m3/s", "m3/s": 15850.3222370511},
+    "gal/min-ft": {"defaultkey": "m3/s-m", "m3/s-m": 4831.17821785317},
+    "gal/person": {"defaultkey": "m3/person", "m3/person": 264.172037284185},
+    "grains/lb": {"defaultkey": "g/kg", "g/kg": 7.0},
+    "hr": {"defaultkey": "hr", "hr": None},
+    "in": {"cm": 0.3937, "defaultkey": "cm", "m": 39.3700787401575},
+    "inH2O": {"Pa": 0.00401463, "defaultkey": "Pa"},
+    "inHg": {"Pa": 0.00029613, "defaultkey": "kPa", "kPa": 0.29523},
+    "inch/yr": {"defaultkey": "m/yr", "m/yr": 39.3700787401575},
+    "inch2": {"cm2": 0.15500031000062, "defaultkey": "cm2"},
+    "kg-H2O/kg-air": {"defaultkey": "kg-H2O/kg-air", "kg-H2O/kg-air": None},
+    "kg/kg": {"defaultkey": "kg/kg", "kg/kg": None},
+    "kmol": {"defaultkey": "kmol", "kmol": None},
+    "kmol/s": {"defaultkey": "kmol/s", "kmol/s": None},
+    "lb": {"defaultkey": "kg", "kg": 2.2046},
+    "lb/Btu": {"defaultkey": "kg/J", "kg/J": 2325.83774250441},
+    "lb/MWh": {
+        "defaultkey": "g/GJ",
+        "g/GJ": 0.00793664091373665,
+        "g/MJ": 7.93664091373665,
+    },
+    "lb/ft": {"defaultkey": "kg/m", "kg/m": 0.67196893069637},
+    "lb/ft-s": {
+        "defaultkey": "g/m-s",
+        "g/m-s": 0.000671968949659,
+        "kg/m-s": 0.67196893069637,
+    },
+    "lb/ft-s-F": {
+        "defaultkey": "g/m-s-K",
+        "g/m-s-K": 0.000373574867724868,
+        "kg/m-s-K": 0.373316072609094,
+    },
+    "lb/ft-s-F2": {"defaultkey": "kg/m-s-K2", "kg/m-s-K2": 0.207397818116164},
+    "lb/ft2": {"defaultkey": "kg/m2", "kg/m2": 0.204794053596664},
+    "lb/ft3": {"defaultkey": "kg/m3", "kg/m3": 0.062428},
+    "lb/lb-F": {"defaultkey": "kg/kg-K", "kg/kg-K": 0.555555555555556},
+    "lb/mol": {"defaultkey": "g/mol", "g/mol": 0.0022046},
+    "lb/psi-s-ft2": {"defaultkey": "kg/Pa-s-m2", "kg/Pa-s-m2": 1412.00523459398},
+    "lb/s": {"defaultkey": "kg/s", "kg/s": 2.20462247603796},
+    "lb/s-ft": {"defaultkey": "kg/s-m", "kg/s-m": 0.67196893069637},
+    "lb/s2": {"defaultkey": "kg/s2", "kg/s2": 2.2046},
+    "lbf-in": {"N-m": 8.85074900525547, "defaultkey": "N-m"},
+    "lbf-s/ft2": {"N-s/m2": 0.0208857913669065, "defaultkey": "N-s/m2"},
+    "m3/m3": {"defaultkey": "m3/m3", "m3/m3": None},
+    "micron": {"defaultkey": "micron", "micron": None},
+    "miles/hr": {"defaultkey": "m/s", "m/s": 2.2369362920544},
+    "minutes": {"defaultkey": "minutes", "minutes": None},
+    "ms": {"defaultkey": "ms", "ms": None},
+    "ohms": {"defaultkey": "ohms", "ohms": None},
+    "percent": {"defaultkey": "percent", "percent": None},
+    "percent/F": {"defaultkey": "percent/K", "percent/K": 0.555555555555556},
+    "person/ft2": {"defaultkey": "person/m2", "person/m2": 0.0928939733269818},
+    "pint/day": {"L/day": 2.11337629827348, "defaultkey": "L/day"},
+    "pint/kWh": {"L/kWh": 2.11337629827348, "defaultkey": "L/kWh"},
+    "ppm": {"defaultkey": "ppm", "ppm": None},
+    "psi": {"Pa": 0.000145037743897283, "defaultkey": "kPa", "kPa": 0.145038},
+    "rev/min": {"defaultkey": "rev/min", "rev/min": None},
+    "s": {"defaultkey": "s", "s": None},
+    "s/ft": {"defaultkey": "s/m", "s/m": 0.3048},
+    "ton-hrs": {"GJ": 78.9889415481832, "defaultkey": "GJ"},
+    "years": {"defaultkey": "years", "years": None},
+}
+
+
+def getconversions(txt=TXT):
+    """create the conversion data structure"""
+    msp = txt.split("!\n")
+    c1 = msp[0].splitlines()
+    c1.pop(0)
+
+    si = dict()
+    ip = dict()
+
+    for c in c1:
+        cc = c.split()
+        cc.pop(0)
+        cc.pop(1)
+        try:
+            si.setdefault(cc[0], []).append((cc[1], float(cc[-1])))
+            ip.setdefault(cc[1], []).append((cc[0], float(cc[-1])))
+        except ValueError as e:
+            si.setdefault(cc[0], []).append((cc[1], cc[2:]))
+            ip.setdefault(cc[1], []).append((cc[0], cc[2:]))
+    c1 = msp[2].splitlines()
+    for c in c1:
+        cc = c.split()
+        cc.pop(0)
+        cc.pop(1)
+        try:
+            si.setdefault(cc[0], []).append((cc[1], float(cc[-1])))
+            ip.setdefault(cc[1], []).append((cc[0], float(cc[-1])))
+        except ValueError as e:
+            si.setdefault(cc[0], []).append((cc[1], cc[2:]))
+            ip.setdefault(cc[1], []).append((cc[0], cc[2:]))
+    c2 = msp[3].splitlines()
+    c2.pop(0)
+    c2.pop(-1)
+    for c in [c.split()[-1] for c in c2]:
+        si.setdefault(c, []).append((c, None))
+        ip.setdefault(c, []).append((c, None))
+    return setdefaultindct(si), setdefaultindct(ip)
+
+
+def setdefaultindct(dct):
+    newdct = {}
+    for key in dct:
+        d = dict(defaultkey=dct[key][0][0])
+        d.update(dict(dct[key]))
+        newdct[key] = d
+    return newdct
+
+
+def convert2ip(val, siunits, ipunits=None, unitstr=True, wrapin=None):
+    """convert val from si units to ip units
+    It can also return a unit string wrapped in something like `[ft]`"""
+    # calculates the new value
+    # get conversion factor
+    if not siunits:
+        conv = 1
+    elif siunits not in SI:
+        conv = 1
+    elif not ipunits:
+        default = SI[siunits]["defaultkey"]
+        conv = SI[siunits][default]
+    else:
+        conv = SI[siunits][ipunits]
+
+    # do conversion
+    new_val = doconversion(val, conv)
+
+    # make the unit string
+    ustr = ""
+    if unitstr:
+        if not siunits:
+            ustr = ""
+        elif siunits not in SI:
+            ustr = siunits
+        elif ipunits:
+            ustr = ipunits
+        else:
+            ustr = default
+        # wrap the unitstr
+        if not wrapin:
+            wrapin = "X"
+        elif ustr:
+            ustr = wrapin.replace("X", ustr)
+        else:
+            pass
+
+    # return the results
+    if unitstr:
+        return new_val, ustr
+    else:
+        return new_val
+
+
+def convert2si(val, ipunits, siunits=None, unitstr=True, wrapin=None):
+    """keep the si units
+    It can also return a unit string wrapped in something like `[C]`"""
+    # calculates the new value
+    # get conversion factor
+    if not ipunits:
+        conv = 1
+    elif ipunits not in IP:
+        conv = 1
+    elif not siunits:
+        default = IP[ipunits]["defaultkey"]
+        conv = IP[ipunits][default]
+    else:
+        conv = IP[ipunits][siunits]
+
+    # do conversion
+    print(conv)
+    new_val = doconversion(val, conv, reverse=True)
+
+    # make the unit string
+    ustr = ""
+    if unitstr:
+        if not ipunits:
+            ustr = ""
+        elif ipunits not in IP:
+            ustr = ipunits
+        elif siunits:
+            ustr = siunits
+        else:
+            ustr = default
+        # wrap the unitstr
+        if not wrapin:
+            wrapin = "X"
+        elif ustr:
+            ustr = wrapin.replace("X", ustr)
+        else:
+            pass
+
+    # return the results
+    if unitstr:
+        return new_val, ustr
+    else:
+        return new_val
+
+
+def doconversion(val, conv, reverse=False):
+    if reverse:
+        try:
+            conv = 1 / conv
+        except TypeError as e:
+            pass
+    try:
+        if conv == ["1.8", "(plus", "32)"]:
+            if reverse:
+                new_val = (val - 32) / 1.8
+            else:
+                new_val = (val * 1.8) + 32
+        else:
+            new_val = val * conv
+    except TypeError as e:
+        new_val = val
+    return new_val
+
+
+def allsiunits():
+    """return a list of all SI units"""
+    return list(SI.keys())
+
+
+def allipunits():
+    """return a list of all the IP units"""
+    return list(IP.keys())
+
+
+def getipunits(siunit):
+    """return all the ip units avaliable for this si unit"""
+    dct = dict(SI[siunit])
+    dct.pop("defaultkey")
+    return list(dct.keys())
+
+
+def noconversion(val, siunits, unitstr=True, wrapin=None):
+    """make no conversion.
+    Used to put the correct units in place"""
+    # calculates the new value
+    # get conversion factor
+    #     if not siunits:
+    #         conv = 1
+    #     elif siunits not in SI:
+    #         conv = 1
+    #     elif not ipunits:
+    #         default = SI[siunits]["defaultkey"]
+    #         conv = SI[siunits][default]
+    #     else:
+    #         conv = SI[siunits][ipunits]
+
+    # do conversion
+    # new_val = doconversion(val, conv)
+    new_val = val
+
+    # make the unit string
+    ustr = ""
+    if unitstr:
+        if not siunits:
+            ustr = ""
+        else:
+            ustr = siunits
+
+        # wrap the unitstr
+        if not wrapin:
+            wrapin = "X"
+        elif ustr:
+            ustr = wrapin.replace("X", ustr)
+        else:
+            pass
+
+    # return the results
+    if unitstr:
+        return new_val, ustr
+    else:
+        return new_val
+
+
+# functions needed:
+#
+# DONE
+# - convert2ip(val, siunits, ipunits=None, unitstr=True, wrapin='[]')
+# - convert2si(val, ipunits, siunits=None, unitstr=True, wrapin='[]')
+# - allsiunits()
+# - allipunits()
+# TODO
+# - getipunits(siunit)
+# - getsiunits(ipunit)
+# - getconversioncategories() # length, volume, u-value etc (make this manually using schema.epJSON)
+#
+# code to get the categories of the units
+# extract and hand edit
+# epj = read an epj file
+# dd = {}
+# for kkey in epj.epschema.epschemaobjects:
+#     o = epj.epschema.epschemaobjects[kkey]
+#     for key in o:
+#         if 'units' in o[key]:
+#             cat = o[key]['units']
+#             print(key, cat)
+#             dd.setdefault(cat, []).append(key)
+#
+# for key in dd:
+#     for item in dd[key]:
+#         print(f"{key},{item}")
diff --git a/eppy3000/experimental/openidf.py b/eppy3000/experimental/openidf.py
index 72b6a41..20f7395 100644
--- a/eppy3000/experimental/openidf.py
+++ b/eppy3000/experimental/openidf.py
@@ -1,3 +1,10 @@
+# Copyright (c) 2023 Santosh Philip
+# =======================================================================
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# =======================================================================
+
 """open an idf file as an epj"""
 
 import os.path
@@ -5,21 +12,22 @@
 import eppy3000.oldeppy as oldeppy
 
 
-
 def openidf(fname, wfile, epjschema=None):
     """opens an idf into an epj. Just like eppy.openidf
-    
-    TODO : make it as 
-    
+
+    TODO : make it as
+
         - openidf(fname, idd=None, epjschema=None, wfile=None)
         - needs pytest
     """
-    idf = eppy.openidf(fname, epw=wfile) # needs a weather file to work. TODO: open an issue and fix this
+    idf = eppy.openidf(
+        fname, epw=wfile
+    )  # needs a weather file to work. TODO: open an issue and fix this
     if not epjschema:
-        version = idf.idfobjects['version'][0]
+        version = idf.idfobjects["version"][0]
         versionid = version.Version_Identifier
         iddfile = eppy.easyopen.getiddfile(versionid)
         head, tail = os.path.split(iddfile)
         epjschema = f"{head}/Energy+.schema.epJSON"
-    epj = oldeppy.idf2epj(idf, open(epjschema, 'r'))
-    return epj
\ No newline at end of file
+    epj = oldeppy.idf2epj(idf, open(epjschema, "r"))
+    return epj
diff --git a/eppy3000/experimental/script_convertunits.py b/eppy3000/experimental/script_convertunits.py
new file mode 100644
index 0000000..906ece4
--- /dev/null
+++ b/eppy3000/experimental/script_convertunits.py
@@ -0,0 +1,65 @@
+"""print epj with IP units"""
+# runt this script from the root folder of eppy3000
+
+import sys
+
+# pathnameto_eppy = 'c:/eppy'
+pathnameto_eppy = "./"
+sys.path.append(pathnameto_eppy)
+
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+import eppy3000.experimental.epconversions as epconversions
+
+SPACE4 = "    "
+
+fname = "./eppy3000/resources/epJSON/V9_6/ShopWithPVandBattery.idf"
+wfile = "./eppy3000/resources/weatherfiles/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw"
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+# allkeys = ['SizingPeriod:DesignDay']
+# allkeys = ['Schedule:Compact']
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    conv = unitsconv.getconversiondata(siunits, ipunits)
+                    array_convs.append((afield, siunits, ipunits))
+                for item in bld[fname]:
+                    for afield, siunits, ipunits in array_convs:
+                        val = item[afield]
+                        newval, ustr = epconversions.convert2ip(
+                            val, siunits, ipunits, unitstr=True, wrapin="<X>"
+                        )
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            val = bld[fname]
+            newval, ustr = epconversions.convert2ip(
+                val, siunits, ipunits, unitstr=True, wrapin="[X]"
+            )
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+        print()
diff --git a/eppy3000/experimental/script_printunits.py b/eppy3000/experimental/script_printunits.py
new file mode 100644
index 0000000..b2d233c
--- /dev/null
+++ b/eppy3000/experimental/script_printunits.py
@@ -0,0 +1,64 @@
+"""print epj with no conversion"""
+# runt this script from the root folder of eppy3000
+
+import sys
+
+# pathnameto_eppy = 'c:/eppy'
+pathnameto_eppy = "./"
+sys.path.append(pathnameto_eppy)
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+import eppy3000.experimental.epconversions as epconversions
+
+SPACE4 = "    "
+
+fname = "./eppy3000/resources/epJSON/V9_6/ShopWithPVandBattery.idf"
+wfile = "./eppy3000/resources/weatherfiles/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw"
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+# allkeys = ['SizingPeriod:DesignDay']
+# allkeys = ['Schedule:Compact']
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    conv = unitsconv.getconversiondata(siunits, ipunits)
+                    array_convs.append((afield, siunits, ipunits))
+                for item in bld[fname]:
+                    for afield, siunits, ipunits in array_convs:
+                        val = item[afield]
+                        newval, ustr = epconversions.noconversion(
+                            val, siunits, unitstr=True, wrapin="<X>"
+                        )
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            val = bld[fname]
+            newval, ustr = epconversions.noconversion(
+                val, siunits, unitstr=True, wrapin="[X]"
+            )
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+        print()
diff --git a/eppy3000/experimental/unitsconv.py b/eppy3000/experimental/unitsconv.py
new file mode 100644
index 0000000..2da5942
--- /dev/null
+++ b/eppy3000/experimental/unitsconv.py
@@ -0,0 +1,176 @@
+# Copyright (c) 2023 Santosh Philip
+# =======================================================================
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# =======================================================================
+"""function to extract the units of the fields and to do unit conversions"""
+
+import eppy3000.experimental.conversiondata as conversiondata
+
+
+def getfieldunits(epobject, fieldname):
+    try:
+        result = epobject.eppy_objepschema[fieldname]["units"]
+        return result
+    except KeyError as e:
+        return None
+
+
+def getarrayfieldunits(epobject, arrayname, itemname):
+    try:
+        return epobject.eppy_objepschema[arrayname]["items"]["properties"][itemname][
+            "units"
+        ]
+    except KeyError as e:
+        return None
+
+
+def getfield_ipunits(epobject, fieldname):
+    try:
+        return epobject.eppy_objepschema[fieldname]["ip-units"]
+    except KeyError as e:
+        return None
+
+
+def getarrayfield_ipunits(epobject, arrayname, itemname):
+    try:
+        return epobject.eppy_objepschema[arrayname]["items"]["properties"][itemname][
+            "ip-units"
+        ]
+    except KeyError as e:
+        return None
+
+
+def getconvert_factors(epobject, fieldname):
+    """return new units, convertion_factors"""
+    units = getfieldunits(epobject, fieldname)
+    ipunits = getfield_ipunits(epobject, fieldname)
+    return unitconversiondata(units, ipunits)
+
+
+def fieldisarray(epobject, fieldname):
+    """returns True if the field is an array"""
+    try:
+        if epobject.eppy_objepschema[fieldname]["type"] == "array":
+            return True
+        else:
+            return False
+    except KeyError as e:
+        return False
+
+
+def getarraykeys(epobject, arrayname):
+    """return the item names in the array"""
+    if fieldisarray(epobject, arrayname):
+        result = list(
+            epobject.eppy_objepschema[arrayname]["items"]["properties"].keys()
+        )
+        return result
+    else:
+        return []
+
+
+def getarrayconvert_factors(epobject, arrayname):
+    """if the field an array, return new units, convertion_factors of items in array"""
+    convert_factors = []
+    for itemname in getarraykeys(epobject, arrayname):
+        units = getarrayfieldunits(epobject, arrayname, itemname)
+        ipunits = getarrayfield_ipunits(epobject, arrayname, itemname)
+        convert_factors.append(unitconversiondata(units, ipunits))
+    return convert_factors
+
+
+def unitconversiondata(units, ipunits=None):
+    """given the units and ip_units, get the conversiondata for the units"""
+    si, ip = conversiondata.getconversions()
+    try:
+        dctval = si[units]
+        if not ipunits:
+            convval = dctval[0]
+        else:
+            dct = dict(dctval)
+            convval = (ipunits, dct[ipunits])
+    except KeyError as e:
+        dctval = None
+        convval = None
+    return units, convval
+
+
+def getconversiondata(units, ipunits=None):
+    """given the units and ip_units, get the conversiondata for the units"""
+    si, ip = conversiondata.getconversions()
+    if (not units) and (not ipunits):
+        return None
+    try:
+        dctval = si[units]
+        if not ipunits:
+            convval = dctval[0]
+        else:
+            dct = dict(dctval)
+            convval = (ipunits, dct[ipunits])
+    except KeyError as e:
+        dctval = None
+        convval = (units, None)
+    return convval
+
+
+def convertfield(epobject, fieldname):
+    """return converted value and units"""
+    pass
+
+
+def add(a, b):
+    return a + b
+
+
+def doconversions(val, units, conv):
+    if units:
+        try:
+            ustr = f"[{conv[0]}]"
+            convfactor = conv[1]
+            newval = val * convfactor
+        except TypeError as e:
+            newval = val
+            ustr = ""
+    else:
+        newval = val
+        ustr = ""
+    return newval, ustr
+
+
+def do_noconversions(val, units):
+    """do no conversions"""
+    newval = val
+    if not units:
+        ustr = ""
+    else:
+        ustr = f"[{units}]"
+    return newval, ustr
+
+
+def do_conversions(val, conv):
+    if not conv:
+        ustr = ""
+        newval = val
+    else:
+        new_units, factor = conv
+        if factor == ["1.8", "(plus", "32)"]:
+            try:
+                newval = val * 1.8 + 32
+            except TypeError as e:
+                newval = val
+        else:
+            try:
+                newval = val * factor
+            except TypeError as e:
+                newval = val
+        ustr = f"[{new_units}]"
+    return newval, ustr
+
+
+# from
+# https://stackoverflow.com/questions/9640109/allign-left-and-right-in-python
+def align_left_right(left: str, right: str, total_len: int = 80) -> str:
+    left_size = max(0, total_len - len(right) - 1)  # -1 to account for the space
+    return format(left, f"<{left_size}") + " " + right
diff --git a/eppy3000/modelmaker.py b/eppy3000/modelmaker.py
index 1b45d55..30079c6 100644
--- a/eppy3000/modelmaker.py
+++ b/eppy3000/modelmaker.py
@@ -185,7 +185,6 @@ def removeallepobjects(self, epjkey):
         while len(self.epobjects[epjkey]) > 0:
             self.popepobject(epjkey, 0)
 
-
     def copyepobject(self, key, objname, newname):
         """copy an epj object with a new name"""
         # don't use the function dict.items() since the json for array has
diff --git a/runningnotes.txt b/runningnotes.txt
index 91dcb3b..8f0173e 100644
--- a/runningnotes.txt
+++ b/runningnotes.txt
@@ -1,3 +1,154 @@
+
+2023-09-22
+----------
+
+use <https://marianoguerra.github.io/json.human.js/> to see the format
+
+::
+
+    {
+        "surface_type": {
+            "type": "string",
+            "enum": [
+                "Ceiling",
+                "Floor",
+                "Roof",
+                "Wall"
+            ]
+        },
+        "construction_name": {
+            "type": "string",
+            "note": "To be matched with a construction in this input file",
+            "data_type": "object_list",
+            "object_list": [
+                "ConstructionNames"
+            ]
+        },
+        "zone_name": {
+            "type": "string",
+            "note": "Zone the surface is a part of.",
+            "data_type": "object_list",
+            "object_list": [
+                "ZoneNames"
+            ]
+        },
+        "space_name": {
+            "type": "string",
+            "note": "Space the surface is a part of (optional, see description of Space object for more details).",
+            "data_type": "object_list",
+            "object_list": [
+                "SpaceNames"
+            ]
+        },
+        "outside_boundary_condition": {
+            "type": "string",
+            "enum": [
+                "Adiabatic",
+                "Foundation",
+                "Ground",
+                "GroundBasementPreprocessorAverageFloor",
+                "GroundBasementPreprocessorAverageWall",
+                "GroundBasementPreprocessorLowerWall",
+                "GroundBasementPreprocessorUpperWall",
+                "GroundFCfactorMethod",
+                "GroundSlabPreprocessorAverage",
+                "GroundSlabPreprocessorCore",
+                "GroundSlabPreprocessorPerimeter",
+                "OtherSideCoefficients",
+                "OtherSideConditionsModel",
+                "Outdoors",
+                "Surface",
+                "Zone"
+            ]
+        },
+        "outside_boundary_condition_object": {
+            "type": "string",
+            "data_type": "object_list",
+            "object_list": [
+                "OutFaceEnvNames"
+            ],
+            "note": "Non-blank only if the field Outside Boundary Condition is Surface, Zone, OtherSideCoefficients or OtherSideConditionsModel If Surface, specify name of corresponding surface in adjacent zone or specify current surface name for internal partition separating like zones If Zone, specify the name of the corresponding zone and the program will generate the corresponding interzone surface If Foundation, specify the name of the corresponding Foundation object and the program will calculate the heat transfer appropriately If OtherSideCoefficients, specify name of SurfaceProperty:OtherSideCoefficients If OtherSideConditionsModel, specify name of SurfaceProperty:OtherSideConditionsModel"
+        },
+        "sun_exposure": {
+            "type": "string",
+            "enum": [
+                "",
+                "NoSun",
+                "SunExposed"
+            ],
+            "default": "SunExposed"
+        },
+        "wind_exposure": {
+            "type": "string",
+            "enum": [
+                "",
+                "NoWind",
+                "WindExposed"
+            ],
+            "default": "WindExposed"
+        },
+        "view_factor_to_ground": {
+            "note": "From the exterior of the surface Unused if one uses the reflections options in Solar Distribution in Building input unless a DaylightingDevice:Shelf or DaylightingDevice:Tubular object has been specified. autocalculate will automatically calculate this value from the tilt of the surface",
+            "default": "Autocalculate",
+            "anyOf": [
+                {
+                    "type": "number",
+                    "minimum": 0.0,
+                    "maximum": 1.0
+                },
+                {
+                    "type": "string",
+                    "enum": [
+                        "",
+                        "Autocalculate"
+                    ]
+                }
+            ]
+        },
+        "number_of_vertices": {
+            "note": "shown with 120 vertex coordinates -- extensible object extensible -- duplicate last set of x,y,z coordinates (last 3 fields), remembering to remove ; from inner fields. for clarity in any error messages, renumber the fields as well. (and changing z terminator to a comma , for all but last one which needs a semi-colon ;) vertices are given in GlobalGeometryRules coordinates -- if relative, all surface coordinates are relative to the Zone Origin. If world, then building and zone origins are used for some internal calculations, but all coordinates are given in an absolute system.",
+            "default": "Autocalculate",
+            "anyOf": [
+                {
+                    "type": "number",
+                    "minimum": 3.0
+                },
+                {
+                    "type": "string",
+                    "enum": [
+                        "",
+                        "Autocalculate"
+                    ]
+                }
+            ]
+        },
+        "vertices": {
+            "type": "array",
+            "items": {
+                "properties": {
+                    "vertex_x_coordinate": {
+                        "type": "number",
+                        "units": "m"
+                    },
+                    "vertex_y_coordinate": {
+                        "type": "number",
+                        "units": "m"
+                    },
+                    "vertex_z_coordinate": {
+                        "type": "number",
+                        "units": "m"
+                    }
+                },
+                "type": "object",
+                "required": [
+                    "vertex_x_coordinate",
+                    "vertex_y_coordinate",
+                    "vertex_z_coordinate"
+                ]
+            }
+        }
+    }
+
 2023-04-21
 ----------
 
diff --git a/temp.py b/temp.py
new file mode 100644
index 0000000..da116ab
--- /dev/null
+++ b/temp.py
@@ -0,0 +1,39 @@
+"""start working with units"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+allkeys = ["Building"]
+
+
+for eppykey in allkeys:
+    print(eppykey)
+    bld = epj.epobjects[eppykey][0]
+    # print(bld)
+
+    fnames = [key for key in bld.keys() if not key.startswith("eppy")]
+    # fnames = ['loads_convergence_tolerance_value']
+    for fname in fnames:
+        ipunits = unitsconv.getfield_ipunits(bld, fname)
+        siunits = unitsconv.getfieldunits(bld, fname)
+        print(fname, siunits)
+        # if ipunits:
+        #     print(eppykey, fname, siunits, ipunits)
+
+        result = unitsconv.getconvert_factors(bld, fname)
+        # if ipunits:
+        if result[0] and (not result[-1]):
+            print("\t", fname, result)
+            if siunits:
+                print("\t\t", siunits, ipunits)
diff --git a/temp1.py b/temp1.py
new file mode 100644
index 0000000..258cd4e
--- /dev/null
+++ b/temp1.py
@@ -0,0 +1,51 @@
+"""print epj with SI units"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+allkeys = ["Connector:Mixer"]
+allkeys = ["BuildingSurface:Detailed"]
+
+
+for eppykey in allkeys:
+    bld = epj.epobjects[eppykey][0]
+    for bld in epj.epobjects[eppykey][:1]:
+        print(eppykey)
+        print("\t", bld.eppyname)
+
+        fnames = [key for key in bld.keys() if not key.startswith("eppy")]
+        for fname in fnames:
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            result = unitsconv.getconvert_factors(bld, fname)
+            units, conv = result
+            if units:
+                ustr = units
+                val = bld[fname]
+                if isinstance(val, list):
+                    for item in val:
+                        print(f"\t\t {item} {type(item)}")
+                else:
+                    print(f"\t {val} [{ustr}] {type(val)}")
+            else:
+                ustr = ""
+                val = bld[fname]
+                if isinstance(val, list):
+                    for item in val:
+                        print(f"\t\t {item} {type(item)}")
+                else:
+                    print(f"\t {val} {type(val)}")
diff --git a/temp2.py b/temp2.py
new file mode 100644
index 0000000..245828a
--- /dev/null
+++ b/temp2.py
@@ -0,0 +1,73 @@
+"""print epj with IP units"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+
+SPACE4 = "    "
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+
+
+def doconversions(val, units, conv):
+    if units:
+        try:
+            ustr = f"[{conv[0]}]"
+            convfactor = conv[1]
+            newval = val * convfactor
+        except TypeError as e:
+            newval = val
+            ustr = ""
+    else:
+        newval = val
+        ustr = ""
+    return newval, ustr
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    result = unitsconv.unitconversiondata(siunits, ipunits)
+                    units, conv = result
+                    array_convs.append((afield, units, conv))
+                for item in bld[fname]:
+                    for afield, units, conv in array_convs:
+                        val = item[afield]
+                        newval, ustr = doconversions(val, units, conv)
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+
+                # newval, ustr = doconversions(bld[fname][][afield], units, conv)
+                # print(f"{SPACE4}{newval} ! - {afield} {ustr}")
+
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            result = unitsconv.getconvert_factors(bld, fname)
+            units, conv = result
+            val = bld[fname]
+            newval, ustr = doconversions(val, units, conv)
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+
+        # break
diff --git a/temp2_1.py b/temp2_1.py
new file mode 100644
index 0000000..392badf
--- /dev/null
+++ b/temp2_1.py
@@ -0,0 +1,59 @@
+"""print epj with IP units"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+import eppy3000.experimental.epconversions as epconversions
+
+SPACE4 = "    "
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+# allkeys = ['SizingPeriod:DesignDay']
+# allkeys = ['Schedule:Compact']
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    conv = unitsconv.getconversiondata(siunits, ipunits)
+                    array_convs.append((afield, conv))
+                for item in bld[fname]:
+                    for afield, conv in array_convs:
+                        val = item[afield]
+                        newval, ustr = unitsconv.do_conversions(val, conv)
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            #             result = unitsconv.getconvert_factors(bld, fname)
+            #             units, conv = result
+            val = bld[fname]
+            newval, ustr = epconversions.convert2ip(
+                val, siunits, ipunits, unitstr=True, wrapin="[X]"
+            )
+            #             newval, ustr = unitsconv.do_conversions(bld[fname], conv)
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+
+#         break
diff --git a/temp2_1_1.py b/temp2_1_1.py
new file mode 100644
index 0000000..01c3522
--- /dev/null
+++ b/temp2_1_1.py
@@ -0,0 +1,57 @@
+"""print epj with IP units"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+import eppy3000.experimental.epconversions as epconversions
+
+SPACE4 = "    "
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+# allkeys = ['SizingPeriod:DesignDay']
+# allkeys = ['Schedule:Compact']
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    conv = unitsconv.getconversiondata(siunits, ipunits)
+                    array_convs.append((afield, siunits, ipunits))
+                for item in bld[fname]:
+                    for afield, siunits, ipunits in array_convs:
+                        val = item[afield]
+                        newval, ustr = epconversions.convert2ip(
+                            val, siunits, ipunits, unitstr=True, wrapin="<X>"
+                        )
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            val = bld[fname]
+            newval, ustr = epconversions.convert2ip(
+                val, siunits, ipunits, unitstr=True, wrapin="[X]"
+            )
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+        print()
diff --git a/temp2_2.py b/temp2_2.py
new file mode 100644
index 0000000..8d55a52
--- /dev/null
+++ b/temp2_2.py
@@ -0,0 +1,54 @@
+"""print epj with no change in units (so SI units)"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+
+SPACE4 = "    "
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+# allkeys = ['SizingPeriod:DesignDay']
+# allkeys = ['Schedule:Compact']
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    # conv = unitsconv.getconversiondata(siunits, ipunits)
+                    array_convs.append((afield, siunits))
+                for item in bld[fname]:
+                    for afield, siunits in array_convs:
+                        val = item[afield]
+                        newval, ustr = unitsconv.do_noconversions(val, siunits)
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            # result = unitsconv.getconvert_factors(bld, fname)
+            # units, conv = result
+            newval, ustr = unitsconv.do_noconversions(bld[fname], siunits)
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+
+        # break
diff --git a/temp3.py b/temp3.py
new file mode 100644
index 0000000..3b6ac33
--- /dev/null
+++ b/temp3.py
@@ -0,0 +1,79 @@
+"""print epj with IP units"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+
+SPACE4 = "    "
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+
+
+def doconversions(val, units, conv, convert=True):
+    if not convert:
+        if units:
+            ustr = units
+        else:
+            ustr = ""
+        return val, ustr
+    if units:
+        try:
+            ustr = f"[{conv[0]}]"
+            convfactor = conv[1]
+            newval = val * convfactor
+        except TypeError as e:
+            newval = val
+            ustr = ""
+    else:
+        newval = val
+        ustr = ""
+    return newval, ustr
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    result = unitsconv.unitconversiondata(siunits, ipunits)
+                    units, conv = result
+                    array_convs.append((afield, units, conv))
+                for item in bld[fname]:
+                    for afield, units, conv in array_convs:
+                        val = item[afield]
+                        newval, ustr = doconversions(val, units, conv, convert=False)
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+
+                # newval, ustr = doconversions(bld[fname][][afield], units, conv)
+                # print(f"{SPACE4}{newval} ! - {afield} {ustr}")
+
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            result = unitsconv.getconvert_factors(bld, fname)
+            units, conv = result
+            val = bld[fname]
+            newval, ustr = doconversions(val, units, conv, convert=False)
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+
+        # break
diff --git a/temp_conv.py b/temp_conv.py
new file mode 100644
index 0000000..01c3522
--- /dev/null
+++ b/temp_conv.py
@@ -0,0 +1,57 @@
+"""print epj with IP units"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+import eppy3000.experimental.epconversions as epconversions
+
+SPACE4 = "    "
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+# allkeys = ['SizingPeriod:DesignDay']
+# allkeys = ['Schedule:Compact']
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    conv = unitsconv.getconversiondata(siunits, ipunits)
+                    array_convs.append((afield, siunits, ipunits))
+                for item in bld[fname]:
+                    for afield, siunits, ipunits in array_convs:
+                        val = item[afield]
+                        newval, ustr = epconversions.convert2ip(
+                            val, siunits, ipunits, unitstr=True, wrapin="<X>"
+                        )
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            val = bld[fname]
+            newval, ustr = epconversions.convert2ip(
+                val, siunits, ipunits, unitstr=True, wrapin="[X]"
+            )
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+        print()
diff --git a/temp_noconv.py b/temp_noconv.py
new file mode 100644
index 0000000..b4cfd99
--- /dev/null
+++ b/temp_noconv.py
@@ -0,0 +1,60 @@
+"""print epj with no change in units (so SI units)"""
+
+import eppy3000.experimental.openidf as openidf
+import eppy3000.experimental.unitsconv as unitsconv
+import eppy3000.experimental.conversiondata as conversiondata
+
+SPACE4 = "    "
+
+fname = "/Applications/EnergyPlus-22-1-0/ExampleFiles/5ZoneAirCooled.idf"
+wfile = "/Applications/EnergyPlus-8-9-0/WeatherData/USA_CA_San.Francisco.Intl.AP.724940_TMY3.epw "
+
+epj = openidf.openidf(fname, wfile)
+si, ip = conversiondata.getconversions()
+
+eppykey = "Building"
+eppykey = "SizingPeriod:DesignDay"
+eppykey = "Fan:VariableVolume"
+allkeys = epj.epobjects.keys()
+# allkeys = ['Building']
+# allkeys = ['Coil:Heating:Water']
+# allkeys = ['BuildingSurface:Detailed']
+# allkeys = ['SizingPeriod:DesignDay']
+# allkeys = ['Schedule:Compact']
+
+
+for eppykey in allkeys:
+    for bld in epj.epobjects[eppykey]:
+        print(eppykey)
+        print(f"{SPACE4}{bld.eppyname}")
+
+        for fname in bld.epjfieldnames():
+            if unitsconv.fieldisarray(bld, fname):
+                afields = unitsconv.getarraykeys(bld, fname)
+                array_convs = []
+                for afield in afields:
+                    ipunits = unitsconv.getarrayfield_ipunits(bld, fname, afield)
+                    siunits = unitsconv.getarrayfieldunits(bld, fname, afield)
+                    # conv = unitsconv.getconversiondata(siunits, ipunits)
+                    array_convs.append((afield, siunits))
+                for item in bld[fname]:
+                    for afield, siunits in array_convs:
+                        val = item[afield]
+                        newval, ustr = unitsconv.do_noconversions(val, siunits)
+                        left = f"{SPACE4 * 2}{newval}"
+                        right = f"! - {afield} {ustr}"
+                        # print(unitsconv.align_left_right(left, right))
+                        print(f"{SPACE4 * 2}{newval} ! - {afield} {ustr}")
+                continue
+            ipunits = unitsconv.getfield_ipunits(bld, fname)
+            siunits = unitsconv.getfieldunits(bld, fname)
+
+            # result = unitsconv.getconvert_factors(bld, fname)
+            # units, conv = result
+            newval, ustr = unitsconv.do_noconversions(bld[fname], siunits)
+            left = f"{SPACE4}{newval}"
+            right = f"! - {fname} {ustr}"
+            # print(unitsconv.align_left_right(left, right))
+            print(f"{SPACE4}{newval} ! - {fname} {ustr}")
+        print()
+        # break


Test patch:
diff --git a/tests/experimental/__init__.py b/tests/experimental/__init__.py
index 0519ecb..e69de29 100644
--- a/tests/experimental/__init__.py
+++ b/tests/experimental/__init__.py
@@ -1,1 +0,0 @@
- 
\ No newline at end of file
diff --git a/tests/experimental/test_epconversions.py b/tests/experimental/test_epconversions.py
new file mode 100644
index 0000000..1f22c63
--- /dev/null
+++ b/tests/experimental/test_epconversions.py
@@ -0,0 +1,773 @@
+# Copyright (c) 2023 Santosh Philip
+# =======================================================================
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# =======================================================================
+"""pytest for epconversions"""
+import pytest
+import eppy3000.experimental.epconversions as epconversions
+
+
+@pytest.mark.parametrize(
+    "val, siunits, ipunits, unitstr, wrapin, expected",
+    [
+        # unitstr=False
+        (
+            3,
+            "m",
+            None,
+            False,
+            None,
+            3 * 3.28083989501312,
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            "autocalculate",
+            "m",
+            None,
+            False,
+            None,
+            "autocalculate",
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            3,
+            "m",
+            "in",
+            False,
+            None,
+            3 * 39.3700787401575,
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            3,
+            None,
+            None,
+            False,
+            None,
+            3,
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            30,
+            "deg",
+            None,
+            False,
+            None,
+            30,
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            30,
+            "unknown",
+            None,
+            False,
+            None,
+            30,
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            30,
+            "C",
+            None,
+            False,
+            None,
+            30 * 1.8 + 32,
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        # unitstr=True
+        (
+            3,
+            "m",
+            None,
+            True,
+            None,
+            (3 * 3.28083989501312, "ft"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            "autocalculate",
+            "m",
+            None,
+            True,
+            None,
+            ("autocalculate", "ft"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            3,
+            "m",
+            "in",
+            True,
+            None,
+            (3 * 39.3700787401575, "in"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            3,
+            None,
+            None,
+            True,
+            None,
+            (3, ""),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            30,
+            "deg",
+            None,
+            True,
+            None,
+            (30, "deg"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            30,
+            "unknown",
+            None,
+            True,
+            None,
+            (30, "unknown"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        # unitstr=True, wrapin='[X]'
+        (
+            3,
+            "m",
+            None,
+            True,
+            "[X]",
+            (3 * 3.28083989501312, "[ft]"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            "autocalculate",
+            "m",
+            None,
+            True,
+            "[X]",
+            ("autocalculate", "[ft]"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            3,
+            "m",
+            "in",
+            True,
+            "[X]",
+            (3 * 39.3700787401575, "[in]"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            3,
+            None,
+            None,
+            True,
+            "[X]",
+            (3, ""),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            30,
+            "deg",
+            None,
+            True,
+            "[X]",
+            (30, "[deg]"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            30,
+            "unknown",
+            None,
+            True,
+            None,
+            (30, "unknown"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+        (
+            30,
+            "dimensionless",
+            None,
+            True,
+            None,
+            (30, "dimensionless"),
+        ),  # val, siunits, ipunits, unitstr, wrapin, expected
+    ],
+)
+def test_convert2ip(val, siunits, ipunits, unitstr, wrapin, expected):
+    result = epconversions.convert2ip(val, siunits, ipunits, unitstr, wrapin)
+    assert result == expected
+
+
+@pytest.mark.parametrize(
+    "val, ipunits, isunits, unitstr, wrapin, expected",
+    [
+        # unitstr=False
+        (
+            3,
+            "lb/MWh",
+            None,
+            False,
+            None,
+            3 / 0.00793664091373665,
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            "autocalculate",
+            "ft",
+            None,
+            False,
+            None,
+            "autocalculate",
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            3,
+            "lb/MWh",
+            "g/MJ",
+            False,
+            None,
+            3 / 7.93664091373665,
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            3,
+            None,
+            None,
+            False,
+            None,
+            3,
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            30,
+            "deg",
+            None,
+            False,
+            None,
+            30,
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            30,
+            "unknown",
+            None,
+            False,
+            None,
+            30,
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            80,
+            "F",
+            None,
+            False,
+            None,
+            (80 - 32) / 1.8,
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        # unitstr=True
+        (
+            3,
+            "ft",
+            None,
+            True,
+            None,
+            (3 / 3.28083989501312, "m"),
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            "autocalculate",
+            "ft",
+            None,
+            True,
+            "[X]",
+            ("autocalculate", "[m]"),
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            3,
+            "lb/MWh",
+            "g/MJ",
+            True,
+            "[X]",
+            (3 / 7.93664091373665, "[g/MJ]"),
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            3,
+            None,
+            None,
+            True,
+            "[X]",
+            (3, ""),
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            30,
+            "deg",
+            None,
+            True,
+            "[X]",
+            (30, "[deg]"),
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            30,
+            "unknown",
+            None,
+            True,
+            None,
+            (30, "unknown"),
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+        (
+            30,
+            "dimensionless",
+            None,
+            True,
+            None,
+            (30, "dimensionless"),
+        ),  # val, ipunits, isunits, unitstr, wrapin, expected
+    ],
+)
+def test_convert2si(val, ipunits, isunits, unitstr, wrapin, expected):
+    """pytest for convert2si"""
+    result = epconversions.convert2si(val, ipunits, isunits, unitstr, wrapin)
+    assert result == expected
+
+
+@pytest.mark.parametrize(
+    "expected",
+    [
+        (
+            [
+                "$/(W/K)",
+                "$/(m3/s)",
+                "$/kW",
+                "$/m2",
+                "$/m3",
+                "(kg/s)/W",
+                "1/K",
+                "1/hr",
+                "1/m",
+                "A",
+                "A/K",
+                "A/V",
+                "Ah",
+                "Availability",
+                "C",
+                "Control",
+                "GJ",
+                "J",
+                "J/J",
+                "J/K",
+                "J/kg",
+                "J/kg-K",
+                "J/kg-K2",
+                "J/kg-K3",
+                "J/m2-K",
+                "J/m3",
+                "J/m3-K",
+                "K",
+                "K/m",
+                "L/GJ",
+                "L/MJ",
+                "L/day",
+                "L/kWh",
+                "Mode",
+                "N-m",
+                "N-s/m2",
+                "Pa",
+                "V",
+                "V/K",
+                "VA",
+                "W",
+                "W/((m3/s)-Pa)",
+                "W/(m3/s)",
+                "W/K",
+                "W/W",
+                "W/m",
+                "W/m-K",
+                "W/m-K2",
+                "W/m-K3",
+                "W/m2",
+                "W/m2-K",
+                "W/m2-K2",
+                "W/person",
+                "W/s",
+                "cm",
+                "cm2",
+                "cycles/hr",
+                "days",
+                "deg",
+                "deltaC",
+                "deltaC/hr",
+                "deltaJ/kg",
+                "dimensionless",
+                "eV",
+                "g/GJ",
+                "g/MJ",
+                "g/kg",
+                "g/m-s",
+                "g/m-s-K",
+                "g/mol",
+                "hr",
+                "kJ/kg",
+                "kPa",
+                "kg",
+                "kg-H2O/kg-air",
+                "kg/J",
+                "kg/Pa-s-m2",
+                "kg/kg",
+                "kg/kg-K",
+                "kg/m",
+                "kg/m-s",
+                "kg/m-s-K",
+                "kg/m-s-K2",
+                "kg/m2",
+                "kg/m3",
+                "kg/s",
+                "kg/s-m",
+                "kg/s2",
+                "kmol",
+                "kmol/s",
+                "lux",
+                "m",
+                "m/hr",
+                "m/s",
+                "m/yr",
+                "m2",
+                "m2-K/W",
+                "m2/m",
+                "m2/person",
+                "m2/s",
+                "m3",
+                "m3/GJ",
+                "m3/MJ",
+                "m3/hr",
+                "m3/hr-m2",
+                "m3/hr-person",
+                "m3/kg",
+                "m3/m2",
+                "m3/m3",
+                "m3/person",
+                "m3/s",
+                "m3/s-W",
+                "m3/s-m",
+                "m3/s-m2",
+                "m3/s-person",
+                "micron",
+                "minutes",
+                "ms",
+                "ohms",
+                "percent",
+                "percent/K",
+                "person/m2",
+                "ppm",
+                "rev/min",
+                "s",
+                "s/m",
+                "years",
+            ]
+        ),  # expected
+    ],
+)
+def tAest_allsiunits(expected):
+    """pytest for allsiunits"""
+    result = epconversions.allsiunits()
+    assert result == expected
+
+
+@pytest.mark.parametrize(
+    "expected",
+    [
+        (
+            [
+                "$",
+                "$/(Btu/h-F)",
+                "$/(ft3/min)",
+                "$/(kBtuh/h)",
+                "$/ft2",
+                "$/ft3",
+                "(ft3/min)/(Btu/h)",
+                "(gal/min)/(Btu/h)",
+                "(lbm/sec)/(Btu/hr)",
+                "1/F",
+                "1/ft",
+                "1/hr",
+                "A",
+                "A/F",
+                "A/V",
+                "Ah",
+                "Availability",
+                "Btu-in/h-ft2-F",
+                "Btu/F",
+                "Btu/ft2-F",
+                "Btu/ft3",
+                "Btu/ft3-F",
+                "Btu/h",
+                "Btu/h-F",
+                "Btu/h-F2-ft",
+                "Btu/h-F3-ft",
+                "Btu/h-ft",
+                "Btu/h-ft-F",
+                "Btu/h-ft2",
+                "Btu/h-ft2-F",
+                "Btu/h-ft2-F2",
+                "Btu/h-person",
+                "Btu/lb",
+                "Btu/lb-F",
+                "Btu/lb-F2",
+                "Btu/lb-F3",
+                "C",
+                "Control",
+                "F",
+                "F/ft",
+                "J/J",
+                "Mode",
+                "Pa",
+                "R",
+                "V",
+                "V/F",
+                "VA",
+                "W",
+                "W/((ft3/min)-inH2O)",
+                "W/((gal/min)-ftH20)",
+                "W/(ft3/min)",
+                "W/(gal/min)",
+                "W/W",
+                "W/ft2",
+                "W/m2",
+                "W/person",
+                "W/s",
+                "Wh",
+                "cycles/hr",
+                "days",
+                "deg",
+                "deltaBtu/lb",
+                "deltaF",
+                "deltaF/hr",
+                "dimensionless",
+                "eV",
+                "foot-candles",
+                "ft",
+                "ft/hr",
+                "ft/min",
+                "ft2",
+                "ft2-F-hr/Btu",
+                "ft2/ft",
+                "ft2/person",
+                "ft2/s",
+                "ft3",
+                "ft3/MWh",
+                "ft3/ft2",
+                "ft3/hr",
+                "ft3/hr-ft2",
+                "ft3/hr-person",
+                "ft3/kWh",
+                "ft3/lb",
+                "ft3/min",
+                "ft3/min-ft",
+                "ft3/min-ft2",
+                "ft3/min-person",
+                "ft3/person",
+                "ftH2O",
+                "gal",
+                "gal/ft2",
+                "gal/hr",
+                "gal/hr-ft2",
+                "gal/hr-person",
+                "gal/kWh",
+                "gal/min",
+                "gal/min-ft",
+                "gal/person",
+                "grains/lb",
+                "hr",
+                "in",
+                "inH2O",
+                "inHg",
+                "inch/yr",
+                "inch2",
+                "kg-H2O/kg-air",
+                "kg/kg",
+                "kmol",
+                "kmol/s",
+                "lb",
+                "lb/Btu",
+                "lb/MWh",
+                "lb/ft",
+                "lb/ft-s",
+                "lb/ft-s-F",
+                "lb/ft-s-F2",
+                "lb/ft2",
+                "lb/ft3",
+                "lb/lb-F",
+                "lb/mol",
+                "lb/psi-s-ft2",
+                "lb/s",
+                "lb/s-ft",
+                "lb/s2",
+                "lbf-in",
+                "lbf-s/ft2",
+                "m3/m3",
+                "micron",
+                "miles/hr",
+                "minutes",
+                "ms",
+                "ohms",
+                "percent",
+                "percent/F",
+                "person/ft2",
+                "pint/day",
+                "pint/kWh",
+                "ppm",
+                "psi",
+                "rev/min",
+                "s",
+                "s/ft",
+                "ton-hrs",
+                "years",
+            ]
+        ),  # expected
+    ],
+)
+def tAest_allipunits(expected):
+    """pytest for allipunits"""
+    result = epconversions.allipunits()
+    assert result == expected
+
+
+@pytest.mark.parametrize(
+    "siunits, expected",
+    [
+        ("m", ["ft", "in"]),  # siunits, expected
+    ],
+)
+def test_getipunits(siunits, expected):
+    """py.test for getipunits"""
+    result = epconversions.getipunits(siunits)
+    assert set(result) == set(expected)  # using set since order is undefined
+
+
+@pytest.mark.parametrize(
+    "val, siunits, unitstr, wrapin, expected",
+    [
+        # unitstr=False
+        (
+            3,
+            "m",
+            False,
+            None,
+            3,
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            "autocalculate",
+            "m",
+            False,
+            None,
+            "autocalculate",
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            3,
+            "m",
+            False,
+            None,
+            3,
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            3,
+            None,
+            False,
+            None,
+            3,
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            30,
+            "deg",
+            False,
+            None,
+            30,
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            30,
+            "unknown",
+            False,
+            None,
+            30,
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            30,
+            "C",
+            False,
+            None,
+            30,
+        ),  # val, siunits, unitstr, wrapin, expected
+        # unitstr=True
+        (
+            3,
+            "m",
+            True,
+            None,
+            (3, "m"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            "autocalculate",
+            "m",
+            True,
+            None,
+            ("autocalculate", "m"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            3,
+            "m",
+            True,
+            None,
+            (3, "m"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            3,
+            None,
+            True,
+            None,
+            (3, ""),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            30,
+            "deg",
+            True,
+            None,
+            (30, "deg"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            30,
+            "unknown",
+            True,
+            None,
+            (30, "unknown"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        # unitstr=True, wrapin='[X]'
+        (
+            3,
+            "m",
+            True,
+            "[X]",
+            (3, "[m]"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            "autocalculate",
+            "m",
+            True,
+            "[X]",
+            ("autocalculate", "[m]"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            3,
+            "m",
+            True,
+            "[X]",
+            (3, "[m]"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            3,
+            None,
+            True,
+            "[X]",
+            (3, ""),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            30,
+            "deg",
+            True,
+            "[X]",
+            (30, "[deg]"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            30,
+            "unknown",
+            True,
+            None,
+            (30, "unknown"),
+        ),  # val, siunits, unitstr, wrapin, expected
+        (
+            30,
+            "dimensionless",
+            True,
+            None,
+            (30, "dimensionless"),
+        ),  # val, siunits, unitstr, wrapin, expected
+    ],
+)
+def test_noconversion(val, siunits, unitstr, wrapin, expected):
+    result = epconversions.noconversion(val, siunits, unitstr, wrapin)
+    assert result == expected
diff --git a/tests/experimental/test_sample.py b/tests/experimental/test_sample.py
new file mode 100644
index 0000000..65f07ce
--- /dev/null
+++ b/tests/experimental/test_sample.py
@@ -0,0 +1,18 @@
+"""sample template for tests"""
+# copy this and use when you are doing tests
+import pytest
+
+
+def add(a, b):
+    return a + b
+
+
+@pytest.mark.parametrize(
+    "a, b, expected",
+    [
+        (2, 3, 5),  # a, b, expected
+    ],
+)
+def test_add(a, b, expected):
+    result = add(a, b)
+    assert result == expected
diff --git a/tests/experimental/test_unitsconv.py b/tests/experimental/test_unitsconv.py
new file mode 100644
index 0000000..196fc3d
--- /dev/null
+++ b/tests/experimental/test_unitsconv.py
@@ -0,0 +1,56 @@
+# Copyright (c) 2023 Santosh Philip
+# =======================================================================
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+# =======================================================================
+"""pytest for unitsconv.py"""
+
+import pytest
+import eppy3000.experimental.unitsconv as unitsconv
+
+
+@pytest.mark.parametrize(
+    "a, b, expected",
+    [
+        (2, 3, 5),  # a, b, expected
+    ],
+)
+def test_add(a, b, expected):
+    result = unitsconv.add(a, b)
+    assert result == expected
+
+
+@pytest.mark.parametrize(
+    "units, ipunits, expected",
+    [
+        ("m", None, ("ft", 3.28083989501312)),  # units, ipunits, expected
+        ("m", "ft", ("ft", 3.28083989501312)),  # units, ipunits, expected
+        ("m", "in", ("in", 39.3700787401575)),  # units, ipunits, expected
+        ("$", None, ("$", None)),  # units, ipunits, expected
+        (None, None, None),  # units, ipunits, expected
+    ],
+)
+def test_getconversiondata(units, ipunits, expected):
+    """py.test for getconversiondata"""
+    result = unitsconv.getconversiondata(units, ipunits)
+    assert result == expected
+
+
+@pytest.mark.parametrize(
+    "val, conv, expected",
+    [
+        #     (3, ('ft', 3.28083989501312), (3 * 3.28083989501312, '[ft]')), # val, conv, expected
+        #     (3, None, (3, '')), # val, conv, expected
+        #     ('autocalculate', ('ft', 3.28083989501312), ('autocalculate', '[ft]')), # val, conv, expected
+        (
+            3,
+            ("F", ["1.8", "(plus", "32)"]),
+            (3 * 1.8 + 32, "[F]"),
+        ),  # val, conv, expected
+    ],
+)
+def test_do_conversions(val, conv, expected):
+    """pytest for do_conversions"""
+    result = unitsconv.do_conversions(val, conv)
+    assert result == expected
diff --git a/tests/test_epMunch.py b/tests/test_epMunch.py
index e4e7ad3..124ae3e 100644
--- a/tests/test_epMunch.py
+++ b/tests/test_epMunch.py
@@ -144,7 +144,6 @@ def test_printmunch_ofEPSchema():
 class TestEPMunch_simple(object):
     """py.test for EPMunch"""
 
-
     def test_repr(self):
         """py.test for EPMunch.__repr__"""
         lst = [
diff --git a/tests/test_epj_mmapping.py b/tests/test_epj_mmapping.py
index 3554d69..4331191 100644
--- a/tests/test_epj_mmapping.py
+++ b/tests/test_epj_mmapping.py
@@ -11,46 +11,50 @@
 from eppy3000.epj_mmapping import EpjMapping
 from eppy3000.epj_mmapping import EpjSequence
 
+
 def eppyfields(epj):
     """put eppy fields into epj"""
     for key in epj:
         for kkey in epj[key]:
-            epj[key][kkey]['eppykey'] = key
-            epj[key][kkey]['eppyname'] = kkey
+            epj[key][kkey]["eppykey"] = key
+            epj[key][kkey]["eppyname"] = kkey
     return epj
-    
+
+
 def epjsequence2dict(epjs):
     """convert the epjsequence to a dict to compare to epj[key]"""
-    return {item['eppyname']:{key:item[key] for key in item} for item in epjs}    
+    return {item["eppyname"]: {key: item[key] for key in item} for item in epjs}
+
 
 @pytest.fixture
 def make_epj():
     epj = dict(
         Zone=dict(
-                zone1=dict(area=15, type="conditioned"),
-                 ), 
+            zone1=dict(area=15, type="conditioned"),
+        ),
         Wall=dict(
-                wall1=dict(area=23, ZoneName="zone1"),
-                wall2=dict(area=24, ZoneName="zone1"),
-                 ), 
-             )
+            wall1=dict(area=23, ZoneName="zone1"),
+            wall2=dict(area=24, ZoneName="zone1"),
+        ),
+    )
     return eppyfields(epj)
-    
+
 
 def test_epj_mapping_init(make_epj):
     """py.test for EpjMapping init"""
     epj = make_epj
     mm = EpjMapping(epj)
-    expectedkeys =  ['Zone', 'Wall']
+    expectedkeys = ["Zone", "Wall"]
     result = list(mm.keys())
     assert result == expectedkeys
-    result = mm['Zone']
+    result = mm["Zone"]
     assert isinstance(result, EpjSequence)
     assert len(result) == 1
-    result = mm['Wall']
+    result = mm["Wall"]
     assert isinstance(result, EpjSequence)
     assert len(result) == 2
-    
+
+
 def test_epj_mapping_functions(make_epj):
     """apy.test for EpjMapping functions
         - __getitem__ -> a = d['a']
@@ -62,30 +66,37 @@ def test_epj_mapping_functions(make_epj):
     epj = make_epj
     mm = EpjMapping(epj)
     # - __getitem__ -> a = d['a']
-    result = mm['Zone']
-    expected = [{'area': 15, 'type': 'conditioned', 'eppykey': 'Zone', 'eppyname': 'zone1'}]
+    result = mm["Zone"]
+    expected = [
+        {"area": 15, "type": "conditioned", "eppykey": "Zone", "eppyname": "zone1"}
+    ]
     for r1, e1 in zip(result, expected):
         assert r1 == e1
-    assert epjsequence2dict(result) == epj['Zone']
+    assert epjsequence2dict(result) == epj["Zone"]
     # - len()
     result = len(mm)
     assert result == len(epj)
     # - __setitem__ -> d['a'] = 1
     # completely new item
-    newval = [{'long': 15, 'lat': 32, 'eppykey': 'Location', 'eppyname': 'City'}, 
-        {'long': 25, 'lat': 42, 'eppykey': 'Location', 'eppyname': 'London'}]
-    mm['Location'] = newval
+    newval = [
+        {"long": 15, "lat": 32, "eppykey": "Location", "eppyname": "City"},
+        {"long": 25, "lat": 42, "eppykey": "Location", "eppyname": "London"},
+    ]
+    mm["Location"] = newval
     expected = epjsequence2dict(newval)
-    assert epj['Location'] == expected
+    assert epj["Location"] == expected
     # replace an existing item
-    newval = [{'area': 105, 'type': 'unconditioned', 'eppykey': 'Zone', 'eppyname': 'zone2'}]
-    mm['Zone'] = newval
+    newval = [
+        {"area": 105, "type": "unconditioned", "eppykey": "Zone", "eppyname": "zone2"}
+    ]
+    mm["Zone"] = newval
     expected = epjsequence2dict(newval)
-    assert epj['Zone'] == expected
+    assert epj["Zone"] == expected
     # - __delitem__ -> pop('a')
-    mm.pop('Zone')
-    assert set(epj.keys()) == {'Wall', 'Location'} # unordered
-    
+    mm.pop("Zone")
+    assert set(epj.keys()) == {"Wall", "Location"}  # unordered
+
+
 def test_epj_sequence_functions(make_epj):
     """py.test for EpjSequence functions
         - __getitem__ -> a = d[i]
@@ -95,50 +106,74 @@ def test_epj_sequence_functions(make_epj):
         - __delitem__ -> remove('a'), pop(i), clear()
     ensure that the change is reflected in the epj
     """
-    pass    
+    pass
     epj = make_epj
     mm = EpjMapping(epj)
     # - __getitem__ -> a = d[i]
-    zones = mm['Zone']
+    zones = mm["Zone"]
     result = epjsequence2dict(zones)
-    assert result == epj['Zone']
+    assert result == epj["Zone"]
     # - len()
-    walls = mm['Wall']
-    assert len(walls) == len(epj['Wall'])
+    walls = mm["Wall"]
+    assert len(walls) == len(epj["Wall"])
     # - __setitem__ -> d.append('a')
-    newval = {'area': 105, 'type': 'unconditioned', 'eppykey': 'Zone', 'eppyname': 'zone2'}
-    zones = mm['Zone']
+    newval = {
+        "area": 105,
+        "type": "unconditioned",
+        "eppykey": "Zone",
+        "eppyname": "zone2",
+    }
+    zones = mm["Zone"]
     expected = epjsequence2dict([zone for zone in zones] + [newval])
     zones.append(newval)
-    result = epj['Zone']
+    result = epj["Zone"]
     assert result == expected
     # - __getitem__ -> a = d[i]
-    zones = mm['Zone']
+    zones = mm["Zone"]
     zone = zones[-1]
-    assert zone == epj['Zone']['zone2']
+    assert zone == epj["Zone"]["zone2"]
     # - __delitem__ -> remove('a')
-    newval = {'area': 105, 'type': 'unconditioned', 'eppykey': 'Zone', 'eppyname': 'zone2'} # same as before
-    zones = mm['Zone']
+    newval = {
+        "area": 105,
+        "type": "unconditioned",
+        "eppykey": "Zone",
+        "eppyname": "zone2",
+    }  # same as before
+    zones = mm["Zone"]
     zones.remove(newval)
-    assert len(epj['Zone']) == 1
+    assert len(epj["Zone"]) == 1
     # - __setitem__ -> d.extend(['a'])
-    newval = {'area': 105, 'type': 'unconditioned', 'eppykey': 'Zone', 'eppyname': 'zone2'}
-    zones = mm['Zone']
-    zones.extend([newval,])
-    assert len(epj['Zone']) == 2
+    newval = {
+        "area": 105,
+        "type": "unconditioned",
+        "eppykey": "Zone",
+        "eppyname": "zone2",
+    }
+    zones = mm["Zone"]
+    zones.extend(
+        [
+            newval,
+        ]
+    )
+    assert len(epj["Zone"]) == 2
     # - __setitem__ -> d[i] = 1
-    newval = {'area': 105, 'type': 'unconditioned', 'eppykey': 'Zone', 'eppyname': 'zone3'}
-    zones = mm['Zone']
+    newval = {
+        "area": 105,
+        "type": "unconditioned",
+        "eppykey": "Zone",
+        "eppyname": "zone3",
+    }
+    zones = mm["Zone"]
     zones[0] = newval
-    assert len(epj['Zone']) == 2
-    assert set(epj['Zone'].keys()) == {'zone2', 'zone3'}
+    assert len(epj["Zone"]) == 2
+    assert set(epj["Zone"].keys()) == {"zone2", "zone3"}
     # - __delitem__ -> pop(i)
-    zones = mm['Zone']
+    zones = mm["Zone"]
     popedval = zones.pop(0)
     assert popedval == newval
-    assert len(epj['Zone']) == 1
-    assert epj['Zone']['zone2'] == zones[0]
+    assert len(epj["Zone"]) == 1
+    assert epj["Zone"]["zone2"] == zones[0]
     # - __delitem__ -> clear()
-    walls = mm['Wall']
+    walls = mm["Wall"]
     walls.clear()
-    assert not epj['Wall'] # empty list
+    assert not epj["Wall"]  # empty list
diff --git a/tests/test_modelmaker.py b/tests/test_modelmaker.py
index da1e00b..aff8c28 100644
--- a/tests/test_modelmaker.py
+++ b/tests/test_modelmaker.py
@@ -80,6 +80,7 @@ def epj_txt1():
     }"""
     return txt
 
+
 @pytest.fixture
 def txt4remove_pop():
     """fixture to test remove and pop of epobjects"""
@@ -118,7 +119,8 @@ def txt4remove_pop():
         }
     }"""
     return txt
-    
+
+
 def test_EPJ(epj_txt1):
     """py.test for EPJ"""
     # test without epschema
@@ -155,14 +157,15 @@ def test_EPJ_repr_(version_txt):
             1                                    !-  idf_order"""
     epj = modelmaker.EPJ(epjname=StringIO(version_txt))
     assert expected == epj.__repr__()
-    
+
+
 def test_popepobject(txt4remove_pop):
     """pytest for popepobject"""
-    epj = modelmaker.EPJ(epjname=StringIO(txt4remove_pop)) 
+    epj = modelmaker.EPJ(epjname=StringIO(txt4remove_pop))
     mats = epj.epobjects["Material"]
     assert len(mats) == 3
-    mat = epj.popepobject('Material', 1) 
-    assert mat.eppyname == "I01 25mm insulation board"  
+    mat = epj.popepobject("Material", 1)
+    assert mat.eppyname == "I01 25mm insulation board"
     mats = epj.epobjects["Material"]
     assert len(mats) == 2
     # test for failure, if you save the file
@@ -173,11 +176,11 @@ def test_popepobject(txt4remove_pop):
     epj1 = modelmaker.EPJ(StringIO(savedtxt))
     mats = epj1.epobjects["Material"]
     assert len(mats) == 2
-    
+
 
 def test_removeepobject(txt4remove_pop):
     """pytest for removeepobject"""
-    epj = modelmaker.EPJ(epjname=StringIO(txt4remove_pop)) 
+    epj = modelmaker.EPJ(epjname=StringIO(txt4remove_pop))
     mats = epj.epobjects["Material"]
     assert len(mats) == 3
     mat0 = mats[0]
@@ -188,7 +191,7 @@ def test_removeepobject(txt4remove_pop):
     epj.removeepobject(mat2)
     mats = epj.epobjects["Material"]
     assert len(mats) == 1
-    assert mats[0].eppyname  == "I01 25mm insulation board"
+    assert mats[0].eppyname == "I01 25mm insulation board"
     # test for failure, if you save the file
     fhandle = StringIO()
     epj.saveas(fhandle)
@@ -197,11 +200,12 @@ def test_removeepobject(txt4remove_pop):
     epj1 = modelmaker.EPJ(StringIO(savedtxt))
     mats = epj1.epobjects["Material"]
     assert len(mats) == 1
-    assert mats[0].eppyname  == "I01 25mm insulation board"
-    
+    assert mats[0].eppyname == "I01 25mm insulation board"
+
+
 def test_removeallepobjects(txt4remove_pop):
     """pytest for removeallepobjects"""
-    epj = modelmaker.EPJ(epjname=StringIO(txt4remove_pop)) 
+    epj = modelmaker.EPJ(epjname=StringIO(txt4remove_pop))
     mats = epj.epobjects["Material"]
     assert len(mats) == 3
     epj.removeallepobjects("Material")
@@ -215,4 +219,3 @@ def test_removeallepobjects(txt4remove_pop):
     epj1 = modelmaker.EPJ(StringIO(savedtxt))
     mats = epj1.epobjects["Material"]
     assert len(mats) == 0
-    
\ No newline at end of file
diff --git a/tests/test_schemaindbm.py b/tests/test_schemaindbm.py
index 4f06cdf..245f57a 100644
--- a/tests/test_schemaindbm.py
+++ b/tests/test_schemaindbm.py
@@ -18,36 +18,37 @@
 from eppy3000.dbm_functions import json2dbm
 from eppy3000.dbm_functions import schemaindbm
 
+
 @pytest.fixture
 def make_refdbm(scope="module"):
     """creates a temporary folder in which I can keep the json and dbm files
     The folder is removed after the test function is run
     This fuxture makes is used to test the index dbm"""
     tempdir = Path(tempfile.mkdtemp())
-    schema =             {
-                "epJSON_schema_version": "3.2",
-                "properties": {
-                    "construction": {
-                        "name": {"reference": ["CNames", "ConstrNames"]},
-                        "patternProperties": {
-                            ".*": {"properties": {"surface_type": {"type": "string"}}}
-                        },
-                    },
-                    "surface": {
-                        "name": {"reference": ["SNames", "SurfNames"]},
-                        "patternProperties": {
-                            ".*": {
-                                "properties": {
-                                    "surface_type": {
-                                        "type": "string",
-                                        "object_list": ["ConstrNames"],
-                                    }
-                                }
+    schema = {
+        "epJSON_schema_version": "3.2",
+        "properties": {
+            "construction": {
+                "name": {"reference": ["CNames", "ConstrNames"]},
+                "patternProperties": {
+                    ".*": {"properties": {"surface_type": {"type": "string"}}}
+                },
+            },
+            "surface": {
+                "name": {"reference": ["SNames", "SurfNames"]},
+                "patternProperties": {
+                    ".*": {
+                        "properties": {
+                            "surface_type": {
+                                "type": "string",
+                                "object_list": ["ConstrNames"],
                             }
-                        },
-                    },
+                        }
+                    }
                 },
-            }
+            },
+        },
+    }
     jsonstr = json.dumps(schema)
     jsonname = str(tempdir / "schema.json")
     with open(jsonname, "w") as fhandle:
@@ -194,11 +195,12 @@ def test_get_arrayfield(make_dbm):
 def test_get_refschemakeys(make_refdbm):
     """py.test for get_refschemakeys"""
     result = schemaindbm.get_refschemakeys(fname=make_refdbm)
-    expected = [b'CNames', b'ConstrNames', b'SNames', b'SurfNames']
+    expected = [b"CNames", b"ConstrNames", b"SNames", b"SurfNames"]
     assert set(result) == set(expected)
-    
+
+
 def test_get_a_refschema(make_refdbm):
     """py.test for get_a_refschema"""
-    result = schemaindbm.get_a_refschema("ConstrNames", fname=make_refdbm) 
-    expected = {'objlist': ['construction'], 'reflist': ['surface.surface_type']}   
-    assert result == expected
\ No newline at end of file
+    result = schemaindbm.get_a_refschema("ConstrNames", fname=make_refdbm)
+    expected = {"objlist": ["construction"], "reflist": ["surface.surface_type"]}
+    assert result == expected