Repository: NREL/bifacial_radiance (language: python)

Issue:
(dev branch) ModuleObj.data parameters accessible
It would be great if the details in ModuleObj.data were accessible directly as ModuleObj attributes.  Maybe get rid of ModuleObj.data, and replace with a function e.g. ModuleObj.returnDict() that pulls specific data details and returns as a dict if that is needed. 


Discussion / hints:

Fix patch:
diff --git a/bifacial_radiance/main.py b/bifacial_radiance/main.py
index 3e3ddd86..5d56b176 100644
--- a/bifacial_radiance/main.py
+++ b/bifacial_radiance/main.py
@@ -2762,7 +2762,7 @@ def __init__(self, moduletype=None):
         elif type(moduletype) == ModuleObj: # try moduleObj
             self.module = moduletype
 
-        self.moduleDict = self.module.data
+        self.moduleDict = self.module.getDataDict()
         self.scenex = self.moduleDict['scenex']
         self.sceney = self.moduleDict['sceney']
         self.offsetfromaxis = self.moduleDict['offsetfromaxis']
@@ -2974,6 +2974,8 @@ class ModuleObj:
     self.sceney : y dimension of the combined module unit including torque 
                   tube, frame, gap, numpanels, etc.
     """
+    def __repr__(self):
+        return str(self.getDataDict())
     
     def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None, 
                  text=None, customtext='', torquetube=False, xgap=0.01, ygap=0.0, 
@@ -3116,7 +3118,11 @@ def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None,
         ========================  ===============================================
         
         '"""
-            
+        self.keys = ['x', 'y', 'z', 'modulematerial', 'scenex','sceney',
+            'scenez','numpanels','bifi','text','modulefile',
+            'offsetfromaxis','xgap','ygap','zgap','cellModule',
+            'torquetube','tubeParams','omegaParams','frameParams']  
+        
         #replace whitespace with underlines. what about \n and other weird characters?
         self.name = str(name).strip().replace(' ', '_') 
         self.customtext = customtext
@@ -3127,65 +3133,49 @@ def __init__(self, name=None, x=None, y=None, z=None, bifi=1, modulefile=None,
         
         # are we writing to JSON with passed data or just reading existing?
         if (x is None) & (y is None) & (cellModule is None):
-            #just read in file
-            self.data = self.readModule(name=name)
+            #just read in file. ignore dict that is returned.
+            self.readModule(name=name)
 
         else:
-            # set initial data object
-            self.data = {'x':x,
-                  'y':y,
-                  'z':z,
-                  'modulematerial': modulematerial,
-                  'scenex': 0,
-                  'sceney': 0,
-                  'scenez': 0,
-                  'numpanels':numpanels,
-                  'bifi':bifi,
-                  'text':text,
-                  'modulefile':modulefile,
-                  'offsetfromaxis':0,
-                  'xgap':xgap,
-                  'ygap':ygap,
-                  'zgap':zgap,
-                  'cellModule':cellModule,
-                  'torquetube':{'bool':torquetube,
-                                'diameter':tubeParams['diameter'],
-                                'tubetype':tubeParams['tubetype'],
-                                'material':tubeParams['material']
-                                }
-                  }
-            if omegaParams is not None:
-                self.data['omegaParams'] = omegaParams
-            if frameParams is not None:
-                self.data['frameParams'] = frameParams
+            # set initial variables that aren't passed in 
+            scenex = sceney = scenez = offsetfromaxis = 0
+            
+            # TODO: this is kind of confusing and should probably be changed
+            # set torque tube internal dictionary
+            tubeBool = torquetube
+            torquetube = {'bool':tubeBool,
+                          'diameter':tubeParams['diameter'],
+                          'tubetype':tubeParams['tubetype'],
+                          'material':tubeParams['material']
+                          }   
             try:
                 self.axisofrotationTorqueTube = tubeParams['axisofrotation']
             except AttributeError:
                 self.axisofrotationTorqueTube = False
+            
+            # set data object attributes from datakey list. 
+            for key in self.keys:
+                setattr(self, key, eval(key))      
+            
                 
             self._zinc = 0
             self._cc = 0  # cc is an offset given to the module when cells are used
                   # so that the sensors don't fall in air when numcells is even.
                   # For non cell-level modules default is 0.
             
-            if self.data['modulefile'] is None:
-                self.data['modulefile'] = os.path.join('objects',
+            if self.modulefile is None:
+                self.modulefile = os.path.join('objects',
                                                        self.name + '.rad')
                 print("\nModule Name:", self.name)
     
          
             
             if text is None: #text overrides making the module text.
-                self._makeModuleFromDict(**self.data)                
+                self._makeModuleFromDict(**self.getDataDict())                
 
             #write JSON data out and write radfile if it doesn't exist
             self.saveModule(json=True, rewriteModulefile=rewriteModulefile)
-            self.scenex = self.data['scenex']
-            self.sceney = self.data['sceney']
-            self.scenez = self.data['scenez']
-            self.x = self.data['x']
-            self.y = self.data['y']
-            self.z = self.data['z']
+
             
     def readModule(self, name=None):
         """
@@ -3205,7 +3195,7 @@ def readModule(self, name=None):
 
         modulenames = data.keys()
         if name is None:
-            return modulenames
+            return list(modulenames)
 
         if name in modulenames:
             moduleDict = data[name]
@@ -3218,8 +3208,11 @@ def readModule(self, name=None):
             if not 'offsetfromaxis' in moduleDict:
                 moduleDict['offsetfromaxis'] = 0
 
-            self.data = moduleDict
-
+            # set ModuleObj attributes from moduleDict
+            #self.data = moduleDict
+            for keys in moduleDict:
+                setattr(self, keys, moduleDict[keys])
+            
             return moduleDict
         else:
             print('Error: module name {} doesnt exist'.format(name))
@@ -3244,21 +3237,21 @@ def saveModule(self, json=True, rewriteModulefile=True):
             with open(filedir) as configfile:
                 data = jsonmodule.load(configfile)
     
-            data.update({self.name:self.data})
+            data.update({self.name:self.getDataDict()})
             with open(os.path.join(DATA_PATH, 'module.json') ,'w') as configfile:
                 jsonmodule.dump(data, configfile, indent=4, sort_keys=True)
     
             print('Module {} updated in module.json'.format(self.name))
         
-        if rewriteModulefile & os.path.isfile(self.data['modulefile']):
-            print(f"Pre-existing .rad file {self.data['modulefile']} "
+        if rewriteModulefile & os.path.isfile(self.modulefile):
+            print(f"Pre-existing .rad file {self.modulefile} "
                   "will be overwritten")
-            os.remove(self.data['modulefile'])
+            os.remove(self.modulefile)
             
-        if not os.path.isfile(self.data['modulefile']):
+        if not os.path.isfile(self.modulefile):
             # py2 and 3 compatible: binary write, encode text first
-            with open(self.data['modulefile'], 'wb') as f:
-                f.write(self.data['text'].encode('ascii'))
+            with open(self.modulefile, 'wb') as f:
+                f.write(self.text.encode('ascii'))
             
     def showModule(self):
         """ 
@@ -3270,7 +3263,7 @@ def showModule(self):
         """
        
         cmd = 'objview %s %s' % (os.path.join('materials', 'ground.rad'),
-                                         self.data['modulefile'])
+                                         self.modulefile)
         _,err = _popen(cmd,None)
         if err is not None:
             print('Error: {}'.format(err))
@@ -3311,8 +3304,7 @@ def _makeModuleFromDict(self,  x=None, y=None, z=None, xgap=None, ygap=None,
                    **kwargs):
 
         """
-        starting from self.data, go through and generate the text required to
-        make a module
+        go through and generate the text required to make a module
         """
 
         #aliases for equations below
@@ -3331,25 +3323,25 @@ def _makeModuleFromDict(self,  x=None, y=None, z=None, xgap=None, ygap=None,
         # Update values for rotating system around torque tube.
         if self.axisofrotationTorqueTube == True:
             if torquetube_bool == True:
-                self.data['offsetfromaxis'] = np.round(zgap + diam/2.0,8)
+                self.offsetfromaxis = np.round(zgap + diam/2.0,8)
             else:
-                self.data['offsetfromaxis'] = zgap
+                self.offsetfromaxis = zgap
                 
             if frameParams is not None:
                 if 'frame_z' not in frameParams:
                     frameParams['frame_z'] = 0.03
                     _missingKeyWarning('Frame', 'frame_z', frameParams['frame_z'])
-                self.data['offsetfromaxis'] = self.data['offsetfromaxis'] + frameParams['frame_z']
+                self.offsetfromaxis = self.offsetfromaxis + frameParams['frame_z']
 
 
         # Adding the option to replace the module thickess
         if z is None:
             z = 0.020
-            self.data['z'] = 0.020
+            self.z = 0.020
             
         if modulematerial is None:
             modulematerial = 'black'
-            self.data['modulematerial'] = 'black'
+            self.modulematerial = 'black'
             
         if not cellModule:
             try:
@@ -3357,7 +3349,7 @@ def _makeModuleFromDict(self,  x=None, y=None, z=None, xgap=None, ygap=None,
                                                           self.name, x, y, z)
                 text +='| xform -t {} {} {} '.format(-x/2.0,
                                         (-y*Ny/2.0)-(ygap*(Ny-1)/2.0),
-                                        self.data['offsetfromaxis'])
+                                        self.offsetfromaxis)
                 text += '-a {} -t 0 {} 0'.format(Ny, y+ygap)
                 packagingfactor = 100.0
 
@@ -3369,14 +3361,14 @@ def _makeModuleFromDict(self,  x=None, y=None, z=None, xgap=None, ygap=None,
             (text, x, y) = self._cellLevelModule(cellModule, z, Ny, ygap, 
                                                modulematerial)  
             
-        self.data['scenex'] = x + xgap
-        self.data['sceney'] = np.round(y*numpanels + ygap*(numpanels-1), 8)
-        self.data['scenez'] = np.round(zgap + diam / 2.0, 8)
+        self.scenex = x + xgap
+        self.sceney = np.round(y*numpanels + ygap*(numpanels-1), 8)
+        self.scenez = np.round(zgap + diam / 2.0, 8)
         
         if frameParams is not None:
             self._z_inc, frametext, frameParams = self._makeFrames(frameParams = frameParams, 
                                             x=x,y=y, ygap=ygap,numpanels=Ny, 
-                                            offsetfromaxis=self.data['offsetfromaxis'])
+                                            offsetfromaxis=self.offsetfromaxis)
         else:
             frametext = ''
             self._z_inc = 0  # z increment from frame thickness
@@ -3388,13 +3380,13 @@ def _makeModuleFromDict(self,  x=None, y=None, z=None, xgap=None, ygap=None,
             omega2omega_x, omegatext, omegaParams = self._makeOmega(
                                             omegaParams=omegaParams, 
                                             x=x,y=y, xgap=xgap, zgap=zgap, 
-                                            z_inc=self._z_inc, offsetfromaxis=self.data['offsetfromaxis'])
-            if omega2omega_x > self.data['scenex']:
-                self.data['scenex'] =  omega2omega_x
+                                            z_inc=self._z_inc, offsetfromaxis=self.offsetfromaxis)
+            if omega2omega_x > self.scenex:
+                self.scenex =  omega2omega_x
             
             # TODO: is the above line better than below?
             """
-            if self.data['scenex']<x:
+            if self.scenex <x:
                 scenex = x+xgap #overwriting scenex to maintain torquetube continuity
         
                 print ('Warning: Omega values have been provided, but' +
@@ -3418,7 +3410,7 @@ def _makeModuleFromDict(self,  x=None, y=None, z=None, xgap=None, ygap=None,
 
         if torquetube_bool is True:
             text += self._makeTorqueTube(tubetype, self.axisofrotationTorqueTube,
-                                         self._z_inc, zgap, diam, material, self.data['scenex'])
+                                         self._z_inc, zgap, diam, material, self.scenex)
 
         # TODO:  should there be anything updated here like scenez?
         if self.glass: 
@@ -3427,7 +3419,7 @@ def _makeModuleFromDict(self,  x=None, y=None, z=None, xgap=None, ygap=None,
                 text +='| xform -t 0 {} 0 ' . format(-edge/2.0)
                 text +='| xform -t {} {} {} '.format(-x/2.0-edge/2.0 + self._cc,
                                         (-y*Ny/2.0)-(ygap*(Ny-1)/2.0),
-                                        self.data['offsetfromaxis'] - 0.5*edge + 0.5*z)
+                                        self.offsetfromaxis - 0.5*edge + 0.5*z)
                 text += '-a {} -t 0 {} 0'.format(Ny, y+ygap)
             
 
@@ -3435,7 +3427,7 @@ def _makeModuleFromDict(self,  x=None, y=None, z=None, xgap=None, ygap=None,
         text += omegatext    
         text += self.customtext  # For adding any other racking details at the module level that the user might want.
 
-        self.data['text'] = text
+        self.text = text
         return text
     #End of makeModuleFromDict()
     
@@ -3445,7 +3437,7 @@ def _cellLevelModule(self, cellModuleParams, z, Ny, ygap,
                          modulematerial):
         """  Calculate the .radfile generation text for a cell-level module.
         """
-        offsetfromaxis = self.data['offsetfromaxis']
+        offsetfromaxis = self.offsetfromaxis
         c = cellModuleParams
         x = c['numcellsx']*c['xcell'] + (c['numcellsx']-1)*c['xcellgap']
         y = c['numcellsy']*c['ycell'] + (c['numcellsy']-1)*c['ycellgap']
@@ -3483,8 +3475,8 @@ def _cellLevelModule(self, cellModuleParams, z, Ny, ygap,
         print("This is a Cell-Level detailed module with Packaging "+
               "Factor of {} %".format(packagingfactor)) 
         
-        self.data['x'] = x
-        self.data['y'] = y
+        self.x = x
+        self.y = y
         
         return(text, x, y)                           
 
@@ -3901,7 +3893,13 @@ def _makeOmega(self, omegaParams, x, y, xgap, zgap, offsetfromaxis, z_inc = 0, *
             omega2omega_x = -x_translate3*2
         return omega2omega_x,omegatext, omegaParams
     
-    
+    def getDataDict(self):
+        """
+        return dictionary values from self.  Originally stored as self.data
+        """
+        
+        return dict(zip(self.keys,[getattr(self,k) for k in self.keys]))
+        
     
 # end of ModuleObj
         
diff --git a/bifacial_radiance/modelchain.py b/bifacial_radiance/modelchain.py
index 3c04a0a4..d21b8aaa 100644
--- a/bifacial_radiance/modelchain.py
+++ b/bifacial_radiance/modelchain.py
@@ -125,7 +125,7 @@ def runModelChain(simulationParamsDict, sceneParamsDict, timeControlParamsDict=N
 
     
     if 'gcr' not in sceneParamsDict:  # didn't get gcr passed - need to calculate it
-        sceneParamsDict['gcr'] = module.data['sceney'] / \
+        sceneParamsDict['gcr'] = module.sceney / \
             sceneParamsDict['pitch']
 
     if simulationParamsDict['tracking'] == False and simulationParamsDict['cumulativeSky'] == True:
diff --git a/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.ipynb b/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.ipynb
index 704edf9d..9ebe77a6 100644
--- a/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.ipynb
+++ b/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.ipynb
@@ -363,7 +363,7 @@
    "source": [
     "module_type = 'Prism Solar Bi60 landscape' \n",
     "module = demo.makeModule(name=module_type,x=1.695, y=0.984)\n",
-    "print(module.data)"
+    "print(module)"
    ]
   },
   {
diff --git a/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.py b/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.py
index 745b74bc..d229e84d 100644
--- a/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.py
+++ b/docs/tutorials/1 - Introductory Example - Fixed Tilt simple setup.py
@@ -166,7 +166,7 @@
 
 module_type = 'Prism Solar Bi60 landscape' 
 module = demo.makeModule(name=module_type,x=1.695, y=0.984)
-print(module.data)
+print(module)
 
 
 # In case you want to use a pre-defined module or a module you've created previously, they are stored in a JSON format in data/module.json, and the options available can be called with printModules:
diff --git a/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.ipynb b/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.ipynb
index 8a7c7b2f..824a1cd7 100644
--- a/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.ipynb
+++ b/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.ipynb
@@ -433,7 +433,7 @@
     "module = demo.makeModule(name=moduletype, x=x, y=y, \n",
     "                torquetube=torquetube, xgap=xgap, ygap=ygap, zgap=zgap, \n",
     "                numpanels=numpanels, tubeParams=tubeDict)\n",
-    "print(module.data)"
+    "print(module)"
    ]
   },
   {
diff --git a/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.py b/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.py
index 00aba6a5..0780a9da 100644
--- a/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.py
+++ b/docs/tutorials/2 - Introductory Example - Single Axis Tracking with cumulative Sky.py
@@ -197,7 +197,7 @@
 module = demo.makeModule(name=moduletype, x=x, y=y, 
                 torquetube=torquetube, xgap=xgap, ygap=ygap, zgap=zgap, 
                 numpanels=numpanels, tubeParams=tubeDict)
-print(module.data)
+print(module)
 
 
 # <a id='step8'></a>
@@ -342,7 +342,7 @@
 metdata = demo.readWeatherFile(epwfile)
 demo.set1axis(limit_angle=limit_angle, backtrack=backtrack, gcr=gcr, cumulativesky=cumulativesky)
 demo.genCumSky1axis()
-sceneDict = {'gcr': gcr,'height':hub_height, 'nMods': nMods, 'nRows': nRows}  # orientation deprecated on v.0.2.4.
+sceneDict = {'gcr': gcr,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows}  # orientation deprecated on v.0.2.4.
 demo.makeScene1axis(module=moduletype, sceneDict=sceneDict)
 demo.makeOct1axis()
 demo.analysis1axis(modWanted=modWanted, rowWanted=rowWanted)
diff --git a/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.ipynb b/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.ipynb
index 374eff04..9c3e8913 100644
--- a/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.ipynb
+++ b/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.ipynb
@@ -282,11 +282,11 @@
    ],
    "source": [
     "# For more options on makemodule, see the help description of the function.  \n",
-    "# Details about the module are stored in the new ModuleObj in the .data dictionary\n",
-    "CW = mymodule.data['sceney']\n",
+    "# Details about the module are stored in the new ModuleObj \n",
+    "CW = mymodule.sceney\n",
     "gcr = CW / pitch\n",
     "print (\"The GCR is :\", gcr)\n",
-    "print(f\"ModuleObj data keys: {mymodule.data.keys()}\")"
+    "print(f\"ModuleObj data keys: {mymodule.keys}\")"
    ]
   },
   {
diff --git a/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.py b/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.py
index c8a8ed13..5c25db8f 100644
--- a/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.py
+++ b/docs/tutorials/3 - Medium Level Example - Single Axis Tracking - hourly.py
@@ -175,11 +175,11 @@
 
 
 # For more options on makemodule, see the help description of the function.  
-# Details about the module are stored in the new ModuleObj in the .data dictionary
-CW = mymodule.data['sceney']
+# Details about the module are stored in the new ModuleObj 
+CW = mymodule.sceney
 gcr = CW / pitch
 print ("The GCR is :", gcr)
-print(f"ModuleObj data keys: {mymodule.data.keys()}")
+print(f"ModuleObj data keys: {mymodule.keys}")
 
 
 # <a id='step6'></a>
diff --git a/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).ipynb b/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).ipynb
index 34a8bcab..52275224 100644
--- a/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).ipynb
+++ b/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).ipynb
@@ -281,7 +281,7 @@
     "# Specifying the pitch automatically with the collector width (sceney) returned by moduledict.\n",
     "# Height has been deprecated as an input. pass clearance_height or hub_height in the scenedict.\n",
     "\n",
-    "sceneDict = {'tilt':tilt,'pitch': np.round(module.data['sceney'] / gcr,3),\n",
+    "sceneDict = {'tilt':tilt,'pitch': np.round(module.sceney / gcr,3),\n",
     "             'hub_height':hub_height,'azimuth':azimuth_ang, \n",
     "             'module_type':module_type, 'nMods': nMods, 'nRows': nRows}  \n",
     "\n",
diff --git a/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).py b/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).py
index f13f33a3..ae2a7e91 100644
--- a/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).py
+++ b/docs/tutorials/4 - Medium Level Example - Debugging your Scene with Custom Objects (Fixed Tilt 2-up with Torque Tube + CLEAN Routine + CustomObject).py
@@ -160,7 +160,7 @@
 # Specifying the pitch automatically with the collector width (sceney) returned by moduledict.
 # Height has been deprecated as an input. pass clearance_height or hub_height in the scenedict.
 
-sceneDict = {'tilt':tilt,'pitch': np.round(module.data['sceney'] / gcr,3),
+sceneDict = {'tilt':tilt,'pitch': np.round(module.sceney / gcr,3),
              'hub_height':hub_height,'azimuth':azimuth_ang, 
              'module_type':module_type, 'nMods': nMods, 'nRows': nRows}  
 
diff --git a/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.ipynb b/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.ipynb
index b1b44035..2c0b8886 100644
--- a/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.ipynb
+++ b/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.ipynb
@@ -136,7 +136,7 @@
     "# Making module with all the variables\n",
     "mymodule=demo.makeModule(name=moduletype,x=x,y=y,numpanels = numpanels, xgap=xgap, ygap=ygap, torquetube=torquetube)\n",
     "# create a scene with all the variables\n",
-    "sceneDict = {'tilt':tilt,'pitch': round(gcr/mymodule.data['sceney'],3),'clearance_height':clearance_height,'azimuth':azimuth_ang, 'module_type':moduletype, 'nMods': nMods, 'nRows': nRows}  \n",
+    "sceneDict = {'tilt':tilt,'pitch': round(gcr/mymodule.sceney,3),'clearance_height':clearance_height,'azimuth':azimuth_ang, 'module_type':moduletype, 'nMods': nMods, 'nRows': nRows}  \n",
     "scene = demo.makeScene(module=mymodule, sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.\n",
     "octfile = demo.makeOct(demo.getfilelist())  # makeOct combines all of the ground, sky and object fil|es into a .oct file.\n"
    ]
diff --git a/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.py b/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.py
index 10bef09c..168d3f8a 100644
--- a/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.py
+++ b/docs/tutorials/5 - Medium Level Example - Bifacial Carports and Canopies + sampling across a module!.py
@@ -95,7 +95,7 @@
 # Making module with all the variables
 mymodule=demo.makeModule(name=moduletype,x=x,y=y,numpanels = numpanels, xgap=xgap, ygap=ygap, torquetube=torquetube)
 # create a scene with all the variables
-sceneDict = {'tilt':tilt,'pitch': round(gcr/mymodule.data['sceney'],3),'clearance_height':clearance_height,'azimuth':azimuth_ang, 'module_type':moduletype, 'nMods': nMods, 'nRows': nRows}  
+sceneDict = {'tilt':tilt,'pitch': round(gcr/mymodule.sceney,3),'clearance_height':clearance_height,'azimuth':azimuth_ang, 'module_type':moduletype, 'nMods': nMods, 'nRows': nRows}  
 scene = demo.makeScene(module=mymodule, sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.
 octfile = demo.makeOct(demo.getfilelist())  # makeOct combines all of the ground, sky and object fil|es into a .oct file.
 
diff --git a/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.ipynb b/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.ipynb
index a06d58ae..d2f7101b 100644
--- a/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.ipynb
+++ b/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.ipynb
@@ -1047,7 +1047,7 @@
     "                           numpanels=numpanels, cellModule=cellLevelModuleParams, tubeParams=tubeParams)\n",
     "\n",
     "sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows}  \n",
-    "demo.set1axis(limit_angle=limit_angle, backtrack=backtrack, gcr=mymodule.data['sceney'] / pitch, cumulativesky=cumulativesky)\n",
+    "demo.set1axis(limit_angle=limit_angle, backtrack=backtrack, gcr=mymodule.sceney / pitch, cumulativesky=cumulativesky)\n",
     "demo.gendaylit1axis()\n",
     "demo.makeScene1axis(module=mymodule, sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.\n",
     "demo.makeOct1axis()\n",
diff --git a/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.py b/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.py
index f1c71c26..a4453530 100644
--- a/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.py
+++ b/docs/tutorials/6 - Advanced topics - Understanding trackerdict structure.py
@@ -84,7 +84,7 @@
                            numpanels=numpanels, cellModule=cellLevelModuleParams, tubeParams=tubeParams)
 
 sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows}  
-demo.set1axis(limit_angle=limit_angle, backtrack=backtrack, gcr=mymodule.data['sceney'] / pitch, cumulativesky=cumulativesky)
+demo.set1axis(limit_angle=limit_angle, backtrack=backtrack, gcr=mymodule.sceney / pitch, cumulativesky=cumulativesky)
 demo.gendaylit1axis()
 demo.makeScene1axis(module=mymodule, sceneDict=sceneDict) #makeScene creates a .rad file with 20 modules per row, 7 rows.
 demo.makeOct1axis()
diff --git a/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.ipynb b/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.ipynb
index 0b7a169c..8e123abf 100644
--- a/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.ipynb
+++ b/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.ipynb
@@ -1238,7 +1238,7 @@
     "metdata = demo.readWeatherFile(epwfile, starttime=startdate, endtime=enddate) \n",
     "mymodule = demo.makeModule(name=moduletype, torquetube=torquetube,x=x, y=y, xgap=xgap,\n",
     "                           ygap = ygap, zgap=zgap, numpanels=numpanels, tubeParams=tubeParams)\n",
-    "pitch = mymodule.data['sceney']/gcr\n",
+    "pitch = mymodule.sceney/gcr\n",
     "sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows}  \n",
     "demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = gcr, cumulativesky = cumulativesky)\n",
     "demo.gendaylit1axis()\n",
diff --git a/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.py b/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.py
index d765871a..2d24172a 100644
--- a/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.py
+++ b/docs/tutorials/8 - Advanced topics - Calculating Power Output and Electrical Mismatch.py
@@ -88,7 +88,7 @@
 metdata = demo.readWeatherFile(epwfile, starttime=startdate, endtime=enddate) 
 mymodule = demo.makeModule(name=moduletype, torquetube=torquetube,x=x, y=y, xgap=xgap,
                            ygap = ygap, zgap=zgap, numpanels=numpanels, tubeParams=tubeParams)
-pitch = mymodule.data['sceney']/gcr
+pitch = mymodule.sceney/gcr
 sceneDict = {'pitch':pitch,'hub_height':hub_height, 'nMods': nMods, 'nRows': nRows}  
 demo.set1axis(limit_angle = limit_angle, backtrack = backtrack, gcr = gcr, cumulativesky = cumulativesky)
 demo.gendaylit1axis()


Test patch:
diff --git a/tests/test_bifacial_radiance.py b/tests/test_bifacial_radiance.py
index d1c7dcd4..030a9bd9 100644
--- a/tests/test_bifacial_radiance.py
+++ b/tests/test_bifacial_radiance.py
@@ -178,7 +178,7 @@ def test_1axis_gencumSky():
     demo.setGround(albedo) # input albedo number or material name like 'concrete'.  To see options, run this without any input.
     metdata = demo.readWeatherFile(weatherFile=MET_FILENAME, starttime='01_01_01', endtime = '01_01_23', coerce_year=2001) # read in the EPW weather data from above
     module=demo.makeModule(name='test',x=0.984,y=1.95, numpanels = 2, ygap = 0.1)
-    pitch= np.round(module.data['sceney'] / gcr,3)
+    pitch= np.round(module.sceney / gcr,3)
     trackerdict = demo.set1axis(cumulativesky = True, gcr=gcr)
     demo.genCumSky1axis()
     assert trackerdict[-45.0]['skyfile'][0:5] == 'skies' #  # Having trouble with the \ or //    'skies\\1axis_-45.0.rad'
@@ -309,24 +309,24 @@ def test_CellLevelModule():
     #moduleDict = demo.makeModule(name=name, cellLevelModule=True, xcell=0.156, rewriteModulefile=True, ycell=0.156,  
     #                             numcellsx=6, numcellsy=10, xcellgap=0.02, ycellgap=0.02)
     module = demo.makeModule(name='test', rewriteModulefile=True, cellModule=cellParams)
-    assert module.data['x'] == 1.036
-    assert module.data['y'] == 1.74
-    assert module.data['scenex'] == 1.046
-    assert module.data['sceney'] == 1.74
-    assert module.data['text'] == '! genbox black cellPVmodule 0.156 0.156 0.02 | xform -t -0.44 -0.87 0 -a 6 -t 0.176 0 0 -a 10 -t 0 0.176 0 -a 1 -t 0 1.74 0'
+    assert module.x == 1.036
+    assert module.y == 1.74
+    assert module.scenex == 1.046
+    assert module.sceney == 1.74
+    assert module.text == '! genbox black cellPVmodule 0.156 0.156 0.02 | xform -t -0.44 -0.87 0 -a 6 -t 0.176 0 0 -a 10 -t 0 0.176 0 -a 1 -t 0 1.74 0'
     
 def test_TorqueTubes_Module():
     name = "_test_TorqueTubes"
     demo = bifacial_radiance.RadianceObj(name)  # Create a RadianceObj 'object'
     module = demo.makeModule(name='square', y=0.95,x=1.59, rewriteModulefile=True, torquetube=True, tubeParams={'tubetype':'square', 'axisofrotation':False})
-    assert module.data['x'] == 1.59
-    assert module.data['text'] == '! genbox black square 1.59 0.95 0.02 | xform -t -0.795 -0.475 0 -a 1 -t 0 0.95 0\r\n! genbox Metal_Grey tube1 1.6 0.1 0.1 | xform -t -0.8 -0.05 -0.2'
+    assert module.x == 1.59
+    assert module.text == '! genbox black square 1.59 0.95 0.02 | xform -t -0.795 -0.475 0 -a 1 -t 0 0.95 0\r\n! genbox Metal_Grey tube1 1.6 0.1 0.1 | xform -t -0.8 -0.05 -0.2'
     module = demo.makeModule(name='round', y=0.95,x=1.59, rewriteModulefile=True, torquetube=True, tubeParams={'tubetype':'round', 'axisofrotation':False})
-    assert module.data['text'][0:30] == '! genbox black round 1.59 0.95'
+    assert module.text[0:30] == '! genbox black round 1.59 0.95'
     module = demo.makeModule(name='hex', y=0.95,x=1.59, rewriteModulefile=True, torquetube=True, tubeParams={'tubetype':'hex', 'axisofrotation':False})
-    assert module.data['text'][0:30] == '! genbox black hex 1.59 0.95 0'
+    assert module.text[0:30] == '! genbox black hex 1.59 0.95 0'
     module = demo.makeModule(name='oct', y=0.95,x=1.59, rewriteModulefile=True, torquetube=True, tubeParams={'tubetype':'oct', 'axisofrotation':False})
-    assert module.data['text'][0:30] == '! genbox black oct 1.59 0.95 0'
+    assert module.text[0:30] == '! genbox black oct 1.59 0.95 0'
 
 def test_gendaylit2manual():
     name = "_test_gendaylit2manual"
@@ -400,7 +400,7 @@ def test_moduleFrameandOmegas():
     name = "_test_moduleFrameandOmegas"
     demo = bifacial_radiance.RadianceObj(name)
     demo.setGround(0.2)
-    metdata = demo.readWeatherFile(weatherFile = MET_FILENAME)    
+    #metdata = demo.readWeatherFile(weatherFile = MET_FILENAME)    
     zgap = 0.10
    
     frameParams = {'frame_material' : 'Metal_Grey', 
@@ -423,33 +423,29 @@ def test_moduleFrameandOmegas():
     loopOmega = [omegaParams, omegaParams, None, None, omegaParams, omegaParams, None, None]
     loopFrame = [frameParams, None, frameParams, None, frameParams,  None, frameParams, None]
     expectedModuleZ = [3.179, 3.149, 3.179, 3.149, 3.129, 3.099, 3.129, 3.099]
-
-    sceneDict = {'tilt':0, 'pitch':3, 'clearance_height':3,'azimuth':90, 
-                 'nMods': 1, 'nRows': 1} 
+    
+    # test inverted=True on the first test
+    loopOmega[0]['inverted'] = True
+    sceneDict = {'tilt':0, 'pitch':3, 'clearance_height':3,'azimuth':90,
+                 'nMods': 1, 'nRows': 1}
 
     for ii in range (0, len(loopOmega)):
-        omegaParams = loopOmega[ii]
-        frameParams = loopFrame[ii]
-        axisofrotationTorqueTube = loopaxisofRotation[ii]
-        torquetube = loopTorquetube[ii]
-        
-        diam = 0.1
-        if torquetube is False:
+
+        if loopTorquetube[ii] is False:
             diam = 0.0
-            
-        demo.makeModule(name='test',x=2, y=1, torquetube = torquetube, 
-                        tubeParams={'diameter':diam,'axisofrotation':axisofrotationTorqueTube},
-                        zgap = zgap, frameParams=frameParams, omegaParams=omegaParams
+        else:  diam = 0.1
+
+        demo.makeModule(name='test',x=2, y=1, torquetube = loopTorquetube[ii], 
+                        tubeParams={'diameter':diam,'axisofrotation':loopaxisofRotation[ii]},
+                        zgap = zgap, frameParams=loopFrame[ii], omegaParams=loopOmega[ii]
                         )
         
         scene = demo.makeScene('test',sceneDict)
-        octfile = demo.makeOct()
+        #octfile = demo.makeOct()
         analysis = bifacial_radiance.AnalysisObj()  # return an analysis object including the scan dimensions for back irradiance
         frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy=1) # Gives us the dictionaries with coordinates
         assert backscan['zstart'] == expectedModuleZ[ii]
     
-
-
 def test_analyzeRow():  
     # test analyzeRow. Requires metdata for boulder.