Index: src/sas/sascalc/calculator/kiessig_calculator.py
===================================================================
--- src/sas/sascalc/calculator/kiessig_calculator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/calculator/kiessig_calculator.py	(revision 235f514ba93b57fa63e44309f182acef0b98bb55)
@@ -49,5 +49,5 @@
             return None
         # check if delta_q is zero
-        if dq == 0.0 or dq == None:
+        if dq == 0.0 or dq is None:
             return None
         else:
Index: src/sas/sascalc/calculator/resolution_calculator.py
===================================================================
--- src/sas/sascalc/calculator/resolution_calculator.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/calculator/resolution_calculator.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -15,4 +15,6 @@
 import sys
 import logging
+
+logger = logging.getLogger(__name__)
 
 #Plank's constant in cgs unit
@@ -606,5 +608,5 @@
         a_value *= 1.0E-16
         # if lamda is give (broad meanning of A)  return 2* lamda^2 * A
-        if lamda != None:
+        if lamda is not None:
             a_value *= (4 * lamda * lamda)
         return a_value
@@ -1006,5 +1008,5 @@
             detector_offset = self.sample2detector_distance[1]
         except:
-            logging.error(sys.exc_value)
+            logger.error(sys.exc_value)
 
         # detector size in [no of pix_x,no of pix_y]
@@ -1093,5 +1095,5 @@
             output.qy_data = qy_value
         except:
-            logging.error(sys.exc_value)
+            logger.error(sys.exc_value)
 
         return output
Index: src/sas/sascalc/calculator/sas_gen.py
===================================================================
--- src/sas/sascalc/calculator/sas_gen.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/calculator/sas_gen.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -12,4 +12,6 @@
 import sys
 import logging
+
+logger = logging.getLogger(__name__)
 
 MFACTOR_AM = 2.853E-12
@@ -95,5 +97,5 @@
         :Param volume: pixel volume [float]
         """
-        if self.data_vol == None:
+        if self.data_vol is None:
             raise
         self.data_vol = volume
@@ -117,5 +119,5 @@
         pos_z = self.data_z
         len_x = len(pos_x)
-        if self.is_avg == None:
+        if self.is_avg is None:
             len_x *= -1
             pos_x, pos_y, pos_z = transform_center(pos_x, pos_y, pos_z)
@@ -255,9 +257,9 @@
         self.sld_n = np.zeros(length)
 
-        if omfdata.mx == None:
+        if omfdata.mx is None:
             self.mx = np.zeros(length)
-        if omfdata.my == None:
+        if omfdata.my is None:
             self.my = np.zeros(length)
-        if omfdata.mz == None:
+        if omfdata.mz is None:
             self.mz = np.zeros(length)
 
@@ -285,5 +287,5 @@
                 mask = (x_dir2 + y_dir2 + z_dir2) <= 1.0
             except:
-                logging.error(sys.exc_value)
+                logger.error(sys.exc_value)
         self.output = MagSLD(self.pos_x[mask], self.pos_y[mask],
                              self.pos_z[mask], self.sld_n[mask],
@@ -394,5 +396,5 @@
                 except:
                     # Skip non-data lines
-                    logging.error(sys.exc_value)
+                    logger.error(sys.exc_value)
                 #Reading Header; Segment count ignored
                 s_line = line.split(":", 1)
@@ -589,5 +591,5 @@
                         z_lines.append(z_line)
                 except:
-                    logging.error(sys.exc_value)
+                    logger.error(sys.exc_value)
 
             output = MagSLD(pos_x, pos_y, pos_z, sld_n, sld_mx, sld_my, sld_mz)
@@ -683,5 +685,5 @@
                     except:
                         # Skip non-data lines
-                        logging.error(sys.exc_value)
+                        logger.error(sys.exc_value)
             output = MagSLD(pos_x, pos_y, pos_z, sld_n,
                             sld_mx, sld_my, sld_mz)
@@ -689,5 +691,5 @@
             output.set_pix_type('pixel')
             output.set_pixel_symbols('pixel')
-            if vol_pix != None:
+            if vol_pix is not None:
                 output.set_pixel_volumes(vol_pix)
             return output
@@ -701,7 +703,7 @@
         :Param data: MagSLD data object
         """
-        if path == None:
+        if path is None:
             raise ValueError, "Missing the file path."
-        if data == None:
+        if data is None:
             raise ValueError, "Missing the data to save."
         x_val = data.pos_x
@@ -711,8 +713,8 @@
         length = len(x_val)
         sld_n = data.sld_n
-        if sld_n == None:
+        if sld_n is None:
             sld_n = np.zeros(length)
         sld_mx = data.sld_mx
-        if sld_mx == None:
+        if sld_mx is None:
             sld_mx = np.zeros(length)
             sld_my = np.zeros(length)
@@ -865,5 +867,5 @@
         self.sld_theta = None
         self.pix_symbol = None
-        if sld_mx != None and sld_my != None and sld_mz != None:
+        if sld_mx is not None and sld_my is not None and sld_mz is not None:
             self.set_sldms(sld_mx, sld_my, sld_mz)
         self.set_nodes()
@@ -933,5 +935,5 @@
         :Params pixel: str; pixel or atomic symbol, or array of strings
         """
-        if self.sld_n == None:
+        if self.sld_n is None:
             return
         if symbol.__class__.__name__ == 'str':
@@ -945,5 +947,5 @@
         :Params pixel: str; pixel or atomic symbol, or array of strings
         """
-        if self.sld_n == None:
+        if self.sld_n is None:
             return
         if vol.__class__.__name__ == 'ndarray':
Index: src/sas/sascalc/data_util/calcthread.py
===================================================================
--- src/sas/sascalc/data_util/calcthread.py	(revision 934ce6493af6e904cf20f2500ef522b42192adfd)
+++ src/sas/sascalc/data_util/calcthread.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -8,4 +8,5 @@
 import traceback
 import sys
+import logging
 
 if sys.platform.count("darwin") > 0:
@@ -21,4 +22,6 @@
     from time import clock
     from time import sleep
+
+logger = logging.getLogger(__name__)
 
 
@@ -202,5 +205,5 @@
     def update(self, **kwargs):
         """Update GUI with the lastest results from the current work unit."""
-        if self.updatefn != None and clock() > self._time_for_update:
+        if self.updatefn is not None and clock() > self._time_for_update:
             self._lock.acquire()
             self._time_for_update = clock() + self._delay
@@ -218,5 +221,5 @@
     def complete(self, **kwargs):
         """Update the GUI with the completed results from a work unit."""
-        if self.completefn != None:
+        if self.completefn is not None:
             self.completefn(**kwargs)
             sleep(self.yieldtime)
@@ -243,6 +246,5 @@
             except Exception:
                 pass
-        import logging
-        logging.error(traceback.format_exc())
+        logger.error(traceback.format_exc())
         #print 'CalcThread exception',
 
Index: src/sas/sascalc/data_util/qsmearing.py
===================================================================
--- src/sas/sascalc/data_util/qsmearing.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/data_util/qsmearing.py	(revision 235f514ba93b57fa63e44309f182acef0b98bb55)
@@ -42,7 +42,7 @@
     # This checks for 2D data (does not throw exception because fail is common)
     if  data.__class__.__name__ not in ['Data1D', 'Theory1D']:
-        if data == None:
+        if data is None:
             return None
-        elif data.dqx_data == None or data.dqy_data == None:
+        elif data.dqx_data is None or data.dqy_data is None:
             return None
         return PySmear2D(data)
Index: src/sas/sascalc/dataloader/data_info.py
===================================================================
--- src/sas/sascalc/dataloader/data_info.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/dataloader/data_info.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -353,5 +353,6 @@
     details = None
     ## SESANS zacceptance
-    zacceptance = None
+    zacceptance = (0,"")
+    yacceptance = (0,"")
 
     def __init__(self):
@@ -805,10 +806,10 @@
             # create zero vector
             dy_other = other.dy
-            if other.dy == None or (len(other.dy) != len(other.y)):
+            if other.dy is None or (len(other.dy) != len(other.y)):
                 dy_other = np.zeros(len(other.y))
 
         # Check that we have errors, otherwise create zero vector
         dy = self.dy
-        if self.dy == None or (len(self.dy) != len(self.y)):
+        if self.dy is None or (len(self.dy) != len(self.y)):
             dy = np.zeros(len(self.y))
 
@@ -821,9 +822,9 @@
         dy, dy_other = self._validity_check(other)
         result = self.clone_without_data(len(self.x))
-        if self.dxw == None:
+        if self.dxw is None:
             result.dxw = None
         else:
             result.dxw = np.zeros(len(self.x))
-        if self.dxl == None:
+        if self.dxl is None:
             result.dxl = None
         else:
@@ -883,17 +884,17 @@
         self._validity_check_union(other)
         result = self.clone_without_data(len(self.x) + len(other.x))
-        if self.dy == None or other.dy is None:
+        if self.dy is None or other.dy is None:
             result.dy = None
         else:
             result.dy = np.zeros(len(self.x) + len(other.x))
-        if self.dx == None or other.dx is None:
+        if self.dx is None or other.dx is None:
             result.dx = None
         else:
             result.dx = np.zeros(len(self.x) + len(other.x))
-        if self.dxw == None or other.dxw is None:
+        if self.dxw is None or other.dxw is None:
             result.dxw = None
         else:
             result.dxw = np.zeros(len(self.x) + len(other.x))
-        if self.dxl == None or other.dxl is None:
+        if self.dxl is None or other.dxl is None:
             result.dxl = None
         else:
@@ -906,5 +907,5 @@
         result.y = np.append(self.y, other.y)
         result.y = result.y[ind]
-        if result.dy != None:
+        if result.dy is not None:
             result.dy = np.append(self.dy, other.dy)
             result.dy = result.dy[ind]
@@ -1029,5 +1030,5 @@
             # Check that the scales match
             err_other = other.err_data
-            if other.err_data == None or \
+            if other.err_data is None or \
                 (len(other.err_data) != len(other.data)):
                 err_other = np.zeros(len(other.data))
@@ -1035,5 +1036,5 @@
         # Check that we have errors, otherwise create zero vector
         err = self.err_data
-        if self.err_data == None or \
+        if self.err_data is None or \
             (len(self.err_data) != len(self.data)):
             err = np.zeros(len(other.data))
@@ -1050,5 +1051,5 @@
         dy, dy_other = self._validity_check(other)
         result = self.clone_without_data(np.size(self.data))
-        if self.dqx_data == None or self.dqy_data == None:
+        if self.dqx_data is None or self.dqy_data is None:
             result.dqx_data = None
             result.dqy_data = None
@@ -1124,6 +1125,6 @@
         result.ymin = self.ymin
         result.ymax = self.ymax
-        if self.dqx_data == None or self.dqy_data == None or \
-                other.dqx_data == None or other.dqy_data == None:
+        if self.dqx_data is None or self.dqy_data is None or \
+                other.dqx_data is None or other.dqy_data is None:
             result.dqx_data = None
             result.dqy_data = None
Index: src/sas/sascalc/dataloader/loader.py
===================================================================
--- src/sas/sascalc/dataloader/loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/loader.py	(revision 463e7ffce130ae386f93cd18b87a1120743e88b5)
@@ -32,4 +32,6 @@
 from readers import cansas_reader
 
+logger = logging.getLogger(__name__)
+
 class Registry(ExtensionRegistry):
     """
@@ -99,5 +101,5 @@
             msg = "DataLoader couldn't locate DataLoader plugin folder."
             msg += """ "%s" does not exist""" % dir
-            logging.warning(msg)
+            logger.warning(msg)
             return readers_found
 
@@ -117,5 +119,5 @@
                         msg = "Loader: Error importing "
                         msg += "%s\n  %s" % (item, sys.exc_value)
-                        logging.error(msg)
+                        logger.error(msg)
 
                 # Process zip files
@@ -139,10 +141,10 @@
                                 msg = "Loader: Error importing"
                                 msg += " %s\n  %s" % (mfile, sys.exc_value)
-                                logging.error(msg)
+                                logger.error(msg)
 
                     except:
                         msg = "Loader: Error importing "
                         msg += " %s\n  %s" % (item, sys.exc_value)
-                        logging.error(msg)
+                        logger.error(msg)
 
         return readers_found
@@ -190,5 +192,5 @@
                 msg = "Loader: Error accessing"
                 msg += " Reader in %s\n  %s" % (module.__name__, sys.exc_value)
-                logging.error(msg)
+                logger.error(msg)
         return reader_found
 
@@ -223,5 +225,5 @@
             msg = "Loader: Error accessing Reader "
             msg += "in %s\n  %s" % (loader.__name__, sys.exc_value)
-            logging.error(msg)
+            logger.error(msg)
         return reader_found
 
@@ -268,5 +270,5 @@
                 msg = "Loader: Error accessing Reader"
                 msg += " in %s\n  %s" % (module.__name__, sys.exc_value)
-                logging.error(msg)
+                logger.error(msg)
         return reader_found
 
Index: src/sas/sascalc/dataloader/manipulations.py
===================================================================
--- src/sas/sascalc/dataloader/manipulations.py	(revision dd11014add466ed4ba0564fcf61e5880d53e6c8b)
+++ src/sas/sascalc/dataloader/manipulations.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -210,5 +210,5 @@
             y[i_q] += frac * data[npts]
 
-            if err_data == None or err_data[npts] == 0.0:
+            if err_data is None or err_data[npts] == 0.0:
                 if data[npts] < 0:
                     data[npts] = -data[npts]
@@ -333,5 +333,5 @@
                 continue
             y += frac * data[npts]
-            if err_data == None or err_data[npts] == 0.0:
+            if err_data is None or err_data[npts] == 0.0:
                 if data[npts] < 0:
                     data[npts] = -data[npts]
@@ -422,5 +422,5 @@
 
         # Get the dq for resolution averaging
-        if data2D.dqx_data != None and data2D.dqy_data != None:
+        if data2D.dqx_data is not None and data2D.dqy_data is not None:
             # The pinholes and det. pix contribution present
             # in both direction of the 2D which must be subtracted when
@@ -462,5 +462,5 @@
 
         #q_data_max = numpy.max(q_data)
-        if len(data2D.q_data) == None:
+        if len(data2D.q_data) is None:
             msg = "Circular averaging: invalid q_data: %g" % data2D.q_data
             raise RuntimeError, msg
@@ -502,5 +502,5 @@
             # Take dqs from data to get the q_average
             x[i_q] += frac * q_value
-            if err_data == None or err_data[npt] == 0.0:
+            if err_data is None or err_data[npt] == 0.0:
                 if data_n < 0:
                     data_n = -data_n
@@ -508,5 +508,5 @@
             else:
                 err_y[i_q] += frac * frac * err_data[npt] * err_data[npt]
-            if dq_data != None:
+            if dq_data is not None:
                 # To be consistent with dq calculation in 1d reduction,
                 # we need just the averages (not quadratures) because
@@ -523,5 +523,5 @@
                 err_y[n] = -err_y[n]
             err_y[n] = math.sqrt(err_y[n])
-            #if err_x != None:
+            #if err_x is not None:
             #    err_x[n] = math.sqrt(err_x[n])
 
@@ -532,5 +532,5 @@
         idx = (numpy.isfinite(y)) & (numpy.isfinite(x))
 
-        if err_x != None:
+        if err_x is not None:
             d_x = err_x[idx] / y_counts[idx]
         else:
@@ -623,5 +623,5 @@
             phi_bins[i_phi] += frac * data[npt]
 
-            if err_data == None or err_data[npt] == 0.0:
+            if err_data is None or err_data[npt] == 0.0:
                 if data_n < 0:
                     data_n = -data_n
@@ -777,5 +777,5 @@
 
         # Get the dq for resolution averaging
-        if data2D.dqx_data != None and data2D.dqy_data != None:
+        if data2D.dqx_data is not None and data2D.dqy_data is not None:
             # The pinholes and det. pix contribution present
             # in both direction of the 2D which must be subtracted when
@@ -888,5 +888,5 @@
             y[i_bin] += frac * data_n
             x[i_bin] += frac * q_value
-            if err_data[n] == None or err_data[n] == 0.0:
+            if err_data[n] is None or err_data[n] == 0.0:
                 if data_n < 0:
                     data_n = -data_n
@@ -895,5 +895,5 @@
                 y_err[i_bin] += frac * frac * err_data[n] * err_data[n]
 
-            if dq_data != None:
+            if dq_data is not None:
                 # To be consistent with dq calculation in 1d reduction,
                 # we need just the averages (not quadratures) because
@@ -925,5 +925,5 @@
         y_err[y_err == 0] = numpy.average(y_err)
         idx = (numpy.isfinite(y) & numpy.isfinite(y_err))
-        if x_err != None:
+        if x_err is not None:
             d_x = x_err[idx] / y_counts[idx]
         else:
Index: src/sas/sascalc/dataloader/readers/IgorReader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/IgorReader.py	(revision dd11014add466ed4ba0564fcf61e5880d53e6c8b)
+++ src/sas/sascalc/dataloader/readers/IgorReader.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/dataloader/readers/__init__.py
===================================================================
--- src/sas/sascalc/dataloader/readers/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/__init__.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/dataloader/readers/abs_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/abs_reader.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/dataloader/readers/abs_reader.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/dataloader/readers/ascii_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/ascii_reader.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/dataloader/readers/ascii_reader.py	(revision 235f514ba93b57fa63e44309f182acef0b98bb55)
@@ -128,10 +128,10 @@
                         if new_lentoks > 2:
                             _dy = float(toks[2])
-                        has_error_dy = False if _dy == None else True
+                        has_error_dy = False if _dy is None else True
 
                         # If a 4th row is present, consider it dx
                         if new_lentoks > 3:
                             _dx = float(toks[3])
-                        has_error_dx = False if _dx == None else True
+                        has_error_dx = False if _dx is None else True
 
                         # Delete the previously stored lines of data candidates if
Index: src/sas/sascalc/dataloader/readers/associations.py
===================================================================
--- src/sas/sascalc/dataloader/readers/associations.py	(revision e5c09cfdceff8c0c11f340ea2e16c5ebfed5a2a9)
+++ src/sas/sascalc/dataloader/readers/associations.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
@@ -18,4 +18,6 @@
 import logging
 import json
+
+logger = logging.getLogger(__name__)
 
 FILE_NAME = 'defaults.json'
@@ -67,5 +69,5 @@
                     msg = "read_associations: skipping association"
                     msg += " for %s\n  %s" % (ext.lower(), sys.exc_value)
-                    logging.error(msg)
+                    logger.error(msg)
     else:
         print "Could not find reader association settings\n  %s [%s]" % (__file__, os.getcwd())
@@ -81,5 +83,5 @@
     :param registry_function: function to be called to register each reader
     """
-    logging.info("register_readers is now obsolete: use read_associations()")
+    logger.info("register_readers is now obsolete: use read_associations()")
     import abs_reader
     import ascii_reader
Index: src/sas/sascalc/dataloader/readers/cansas_constants.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_constants.py	(revision ad4632c3bee7cc6b6d8987a3088e925b98cf41ab)
+++ src/sas/sascalc/dataloader/readers/cansas_constants.py	(revision 63d773c420797b9a40dbfc28b1e602df82410cae)
@@ -135,4 +135,5 @@
                              "Sesans": {"storeas": "content"},
                              "zacceptance": {"storeas": "float"},
+                             "yacceptance": {"storeas": "float"},
                              "<any>" : ANY
                             }
Index: src/sas/sascalc/dataloader/readers/cansas_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision 1b9a36780bc504c40e0517882815efd590ef4c0d)
+++ src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision 1b9a36780bc504c40e0517882815efd590ef4c0d)
@@ -33,4 +33,6 @@
 import xml.dom.minidom
 from xml.dom.minidom import parseString
+
+logger = logging.getLogger(__name__)
 
 PREPROCESS = "xmlpreprocess"
@@ -990,5 +992,5 @@
         :param data1d: presumably a Data1D object
         """
-        if self.current_dataset == None:
+        if self.current_dataset is None:
             x_vals = np.empty(0)
             y_vals = np.empty(0)
@@ -1078,5 +1080,5 @@
         # Write the file
         file_ref = open(filename, 'w')
-        if self.encoding == None:
+        if self.encoding is None:
             self.encoding = "UTF-8"
         doc.write(file_ref, encoding=self.encoding,
@@ -1201,5 +1203,5 @@
         :param entry_node: lxml node ElementTree object to be appended to
         """
-        if datainfo.run == None or datainfo.run == []:
+        if datainfo.run is None or datainfo.run == []:
             datainfo.run.append(RUN_NAME_DEFAULT)
             datainfo.run_name[RUN_NAME_DEFAULT] = RUN_NAME_DEFAULT
@@ -1245,4 +1247,6 @@
             sesans.text = str(datainfo.isSesans)
             node.append(sesans)
+            self.write_node(node, "yacceptance", datainfo.sample.yacceptance[0],
+                             {'unit': datainfo.sample.yacceptance[1]})
             self.write_node(node, "zacceptance", datainfo.sample.zacceptance[0],
                              {'unit': datainfo.sample.zacceptance[1]})
@@ -1317,5 +1321,5 @@
                 self.write_node(point, "T", spectrum.transmission[i],
                                 {'unit': spectrum.transmission_unit})
-                if spectrum.transmission_deviation != None \
+                if spectrum.transmission_deviation is not None \
                 and len(spectrum.transmission_deviation) >= i:
                     self.write_node(point, "Tdev",
@@ -1397,5 +1401,5 @@
                                  str(datainfo.source.name))
         self.append(source, instr)
-        if datainfo.source.radiation == None or datainfo.source.radiation == '':
+        if datainfo.source.radiation is None or datainfo.source.radiation == '':
             datainfo.source.radiation = "neutron"
         self.write_node(source, "radiation", datainfo.source.radiation)
@@ -1438,5 +1442,5 @@
         :param instr: lxml node ElementTree object to be appended to
         """
-        if datainfo.collimation == [] or datainfo.collimation == None:
+        if datainfo.collimation == [] or datainfo.collimation is None:
             coll = Collimation()
             datainfo.collimation.append(coll)
@@ -1483,5 +1487,5 @@
         :param inst: lxml instrument node to be appended to
         """
-        if datainfo.detector == None or datainfo.detector == []:
+        if datainfo.detector is None or datainfo.detector == []:
             det = Detector()
             det.name = ""
@@ -1659,5 +1663,5 @@
                 local_unit = None
                 exec "local_unit = storage.%s_unit" % toks[0]
-                if local_unit != None and units.lower() != local_unit.lower():
+                if local_unit is not None and units.lower() != local_unit.lower():
                     if HAS_CONVERTER == True:
                         try:
@@ -1672,5 +1676,5 @@
                             self.errors.add(err_mess)
                             if optional:
-                                logging.info(err_mess)
+                                logger.info(err_mess)
                             else:
                                 raise ValueError, err_mess
@@ -1681,5 +1685,5 @@
                         self.errors.add(err_mess)
                         if optional:
-                            logging.info(err_mess)
+                            logger.info(err_mess)
                         else:
                             raise ValueError, err_mess
Index: src/sas/sascalc/dataloader/readers/cansas_reader.py.orig
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader.py.orig	(revision None)
+++ src/sas/sascalc/dataloader/readers/cansas_reader.py.orig	(revision None)
@@ -0,0 +1,1963 @@
+"""
+    CanSAS data reader - new recursive cansas_version.
+"""
+############################################################################
+#This software was developed by the University of Tennessee as part of the
+#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
+#project funded by the US National Science Foundation.
+#If you use DANSE applications to do scientific research that leads to
+#publication, we ask that you acknowledge the use of the software with the
+#following sentence:
+#This work benefited from DANSE software developed under NSF award DMR-0520547.
+#copyright 2008,2009 University of Tennessee
+#############################################################################
+
+import logging
+import numpy as np
+import os
+import sys
+import datetime
+import inspect
+# For saving individual sections of data
+from sas.sascalc.dataloader.data_info import Data1D, Data2D, DataInfo, \
+    plottable_1D, plottable_2D
+from sas.sascalc.dataloader.data_info import Collimation, TransmissionSpectrum, \
+    Detector, Process, Aperture
+from sas.sascalc.dataloader.data_info import \
+    combine_data_info_with_plottable as combine_data
+import sas.sascalc.dataloader.readers.xml_reader as xml_reader
+from sas.sascalc.dataloader.readers.xml_reader import XMLreader
+from sas.sascalc.dataloader.readers.cansas_constants import CansasConstants, CurrentLevel
+
+# The following 2 imports *ARE* used. Do not remove either.
+import xml.dom.minidom
+from xml.dom.minidom import parseString
+
+logger = logging.getLogger(__name__)
+
+PREPROCESS = "xmlpreprocess"
+ENCODING = "encoding"
+RUN_NAME_DEFAULT = "None"
+INVALID_SCHEMA_PATH_1_1 = "{0}/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_1.xsd"
+INVALID_SCHEMA_PATH_1_0 = "{0}/sas/sascalc/dataloader/readers/schema/cansas1d_invalid_v1_0.xsd"
+INVALID_XML = "\n\nThe loaded xml file, {0} does not fully meet the CanSAS v1.x specification. SasView loaded " + \
+              "as much of the data as possible.\n\n"
+HAS_CONVERTER = True
+try:
+    from sas.sascalc.data_util.nxsunit import Converter
+except ImportError:
+    HAS_CONVERTER = False
+
+CONSTANTS = CansasConstants()
+CANSAS_FORMAT = CONSTANTS.format
+CANSAS_NS = CONSTANTS.names
+ALLOW_ALL = True
+
+class Reader(XMLreader):
+    """
+    Class to load cansas 1D XML files
+
+    :Dependencies:
+        The CanSAS reader requires PyXML 0.8.4 or later.
+    """
+    # CanSAS version - defaults to version 1.0
+    cansas_version = "1.0"
+    base_ns = "{cansas1d/1.0}"
+    cansas_defaults = None
+    type_name = "canSAS"
+    invalid = True
+    frm = ""
+    # Log messages and errors
+    logging = None
+    errors = set()
+    # Namespace hierarchy for current xml_file object
+    names = None
+    ns_list = None
+    # Temporary storage location for loading multiple data sets in a single file
+    current_datainfo = None
+    current_dataset = None
+    current_data1d = None
+    data = None
+    # List of data1D objects to be sent back to SasView
+    output = None
+    # Wildcards
+    type = ["XML files (*.xml)|*.xml", "SasView Save Files (*.svs)|*.svs"]
+    # List of allowed extensions
+    ext = ['.xml', '.XML', '.svs', '.SVS']
+    # Flag to bypass extension check
+    allow_all = True
+
+    def reset_state(self):
+        """
+        Resets the class state to a base case when loading a new data file so previous
+        data files do not appear a second time
+        """
+        self.current_datainfo = None
+        self.current_dataset = None
+        self.current_data1d = None
+        self.data = []
+        self.process = Process()
+        self.transspectrum = TransmissionSpectrum()
+        self.aperture = Aperture()
+        self.collimation = Collimation()
+        self.detector = Detector()
+        self.names = []
+        self.cansas_defaults = {}
+        self.output = []
+        self.ns_list = None
+        self.logging = []
+        self.encoding = None
+
+    def read(self, xml_file, schema_path="", invalid=True):
+        """
+        Validate and read in an xml_file file in the canSAS format.
+
+        :param xml_file: A canSAS file path in proper XML format
+        :param schema_path: A file path to an XML schema to validate the xml_file against
+        """
+        # For every file loaded, reset everything to a base state
+        self.reset_state()
+        self.invalid = invalid
+        # Check that the file exists
+        if os.path.isfile(xml_file):
+            basename, extension = os.path.splitext(os.path.basename(xml_file))
+            # If the file type is not allowed, return nothing
+            if extension in self.ext or self.allow_all:
+                # Get the file location of
+                self.load_file_and_schema(xml_file, schema_path)
+                self.add_data_set()
+                # Try to load the file, but raise an error if unable to.
+                # Check the file matches the XML schema
+                try:
+                    self.is_cansas(extension)
+                    self.invalid = False
+                    # Get each SASentry from XML file and add it to a list.
+                    entry_list = self.xmlroot.xpath(
+                            '/ns:SASroot/ns:SASentry',
+                            namespaces={'ns': self.cansas_defaults.get("ns")})
+                    self.names.append("SASentry")
+
+                    # Get all preprocessing events and encoding
+                    self.set_processing_instructions()
+
+                    # Parse each <SASentry> item
+                    for entry in entry_list:
+                        # Create a new DataInfo object for every <SASentry>
+
+                        # Set the file name and then parse the entry.
+                        self.current_datainfo.filename = basename + extension
+                        self.current_datainfo.meta_data["loader"] = "CanSAS XML 1D"
+                        self.current_datainfo.meta_data[PREPROCESS] = \
+                            self.processing_instructions
+
+                        # Parse the XML SASentry
+                        self._parse_entry(entry)
+                        # Combine datasets with datainfo
+                        self.add_data_set()
+                except RuntimeError:
+                    # If the file does not match the schema, raise this error
+                    invalid_xml = self.find_invalid_xml()
+                    invalid_xml = INVALID_XML.format(basename + extension) + invalid_xml
+                    self.errors.add(invalid_xml)
+                    # Try again with an invalid CanSAS schema, that requires only a data set in each
+                    base_name = xml_reader.__file__
+                    base_name = base_name.replace("\\", "/")
+                    base = base_name.split("/sas/")[0]
+                    if self.cansas_version == "1.1":
+                        invalid_schema = INVALID_SCHEMA_PATH_1_1.format(base, self.cansas_defaults.get("schema"))
+                    else:
+                        invalid_schema = INVALID_SCHEMA_PATH_1_0.format(base, self.cansas_defaults.get("schema"))
+                    self.set_schema(invalid_schema)
+                    try:
+                        if self.invalid:
+                            if self.is_cansas():
+                                self.output = self.read(xml_file, invalid_schema, False)
+                            else:
+                                raise RuntimeError
+                        else:
+                            raise RuntimeError
+                    except RuntimeError:
+                        x = np.zeros(1)
+                        y = np.zeros(1)
+                        self.current_data1d = Data1D(x,y)
+                        self.current_data1d.errors = self.errors
+                        return [self.current_data1d]
+        else:
+            self.output.append("Not a valid file path.")
+        # Return a list of parsed entries that dataloader can manage
+        return self.output
+
+    def _parse_entry(self, dom, recurse=False):
+        """
+        Parse a SASEntry - new recursive method for parsing the dom of
+            the CanSAS data format. This will allow multiple data files
+            and extra nodes to be read in simultaneously.
+
+        :param dom: dom object with a namespace base of names
+        """
+
+        self._check_for_empty_data()
+        self._initialize_new_data_set(dom)
+
+        self.names.append("SASentry")
+        self.parent_class = "SASentry"
+
+        self.base_ns = "{0}{1}{2}".format("{", \
+                            CANSAS_NS.get(self.cansas_version).get("ns"), "}")
+
+        self.ns_list = CONSTANTS.iterate_namespace(self.names)
+        
+        # Go through each child in the parent element
+        for sasNode in dom:
+            # Get the element name and set the current name's level
+            currentTagName = sasNode.tag.replace(self.base_ns, "")
+            # As this is the most likely tag to examine, lets put it first!
+            if currentTagName == "SASdata":
+                # Are there multiple entries here?
+                if len(sasNode) <= 1:
+                    multipleEntries = False
+                else:
+                    multipleEntries = True
+
+                for setupNode in sasNode[0]:
+                    # Iterating through the tags in the unit node, getting their tag name and respective unit
+                    setupTagName = setupNode.tag.replace(self.base_ns, "")
+                    units = setupNode.attrib.get("unit", "")
+
+                    # Creating our data array first, if there's only one dataNode we will handle this...
+                    startArray = np.fromstring(setupNode.text, dtype=float, sep=",")
+                    
+                    if multipleEntries == True:
+                        setupArray = np.zeros((len(sasNode), len(startArray)))
+                        setupArray[0] = startArray
+                    else:
+                        setupArray = startArray
+
+                    # Now put this into the relevant location
+                    if setupTagName == "I":
+                        self.current_dataset.yaxis("Intensity", units)
+                        self.current_dataset.y = setupArray
+                    elif setupTagName == "Q":
+                        self.current_dataset.xaxis("Q", units)
+                        self.current_dataset.x = setupArray
+
+                    elif setupTagName == "Idev":
+                        self.current_dataset.dy = setupArray  
+                    elif setupTagName == "Qdev":
+                        self.current_dataset.dx = setupArray 
+
+                    elif setupTagName == "Qx":
+                        self.current_dataset.xaxis("Qx", units)
+                        self.current_dataset.qx_data = setupArray
+                    elif setupTagName == "Qy":
+                        self.current_dataset.yaxis("Qy", units)
+                        self.current_dataset.qy_data = setupArray
+                    elif setupTagName == "Qxdev":
+                        self.current_dataset.xaxis("Qxdev", units)
+                        self.current_dataset.dqx_data = setupArray
+                    elif setupTagName == "Qydev":
+                        self.current_dataset.yaxis("Qydev", units)
+                        self.current_dataset.dqy_data = setupArray
+                    elif setupTagName == "dQw":
+                        self.current_dataset.dxw = setupArray
+                    elif setupTagName == "dQl":
+                        self.current_dataset.dxl = setupArray
+
+                    elif setupTagName == "Mask":
+                        self.current_dataset.mask = np.ndarray.astype(setupArray, dtype=bool)
+                    elif setupTagName == "Sesans":
+                        self.current_datainfo.isSesans = bool(setupNode.text)
+
+                    elif setupTagName == "zacceptance":
+                        self.current_datainfo.sample.zacceptance = (setupNode.text, units)
+                    elif setupTagName == "Qmean":
+                        pass
+                    elif setupTagName == "Shadowfactor":
+                        pass
+
+                # If there's more data present, let's deal with that too
+                for loopIter in range(1, len(sasNode)):
+                    for dataNode in sasNode[loopIter]:
+                        # Iterating through the tags in the unit node, getting their tag name and respective unit
+                        dataTagName = dataNode.tag.replace(self.base_ns, "")
+                        # Creating our data array first
+                        dataArray = np.fromstring(dataNode.text, dtype=float, sep=",")
+
+                        if dataTagName == "I":
+                            self.current_dataset.y[loopIter] = dataArray
+                        elif dataTagName == "Q":
+                            self.current_dataset.x[loopIter] = dataArray
+                        elif dataTagName == "Idev":
+                            self.current_dataset.dy[loopIter] = dataArray
+                        elif dataTagName == "Qdev":
+                            self.current_dataset.dx[loopIter] = dataArray
+                        elif dataTagName == "Qx":
+                            self.current_dataset.qx_data[loopIter] = dataArray
+                        elif dataTagName == "Qy":
+                            self.current_dataset.qy_data[loopIter] = dataArray
+                        elif dataTagName == "Qxdev":
+                            self.current_dataset.dqx_data[loopIter] = dataArray
+                        elif dataTagName == "Qydev":
+                            self.current_dataset.dqy_data[loopIter] = dataArray
+                        elif dataTagName == "dQw":
+                            self.current_dataset.dxw[loopIter] = dataArray
+                        elif dataTagName == "dQl":
+                            self.current_dataset.dxl[loopIter] = dataArray
+
+                self._check_for_empty_resolution()
+                self.data.append(self.current_dataset)
+
+            # If it's not data, let's check for other tags starting with skippable ones...
+            elif currentTagName == "fitting_plug_in" or currentTagName == "pr_inversion" or currentTagName == "invariant":
+                continue
+
+            # If we'e dealing with a title node then extract the text of the node and put it in the right place
+            elif currentTagName == "Title":
+                self.current_datainfo.title = sasNode.text
+
+
+            # If we'e dealing with a run node then extract the name and text of the node and put it in the right place
+            elif currentTagName == "Run":
+                    self.current_datainfo.run_name[sasNode.text] = sasNode.attrib.get("name", "")
+                    self.current_datainfo.run.append(sasNode.text)
+
+            # If we'e dealing with a sample node
+            elif currentTagName == "SASsample":
+                for sampleNode in sasNode:
+                    # Get the variables
+                    sampleTagName = sampleNode.tag.replace(self.base_ns, "")
+                    sampleUnits = sampleNode.attrib.get("unit", "")
+                    sampleData = sampleNode.text
+
+                    # Populate it via if switching
+                    if sampleTagName == "ID":
+                        self.current_datainfo.sample.ID = sampleData
+                    elif sampleTagName == "Title":
+                        self.current_datainfo.sample.name = sampleData
+                    elif sampleTagName == "thickness":
+                        self.current_datainfo.sample.thickness = sampleData
+                        self.current_datainfo.sample.thickness_unit = sampleUnits
+                    elif sampleTagName == "transmission":
+                        self.current_datainfo.sample.transmission = sampleData
+                    elif sampleTagName == "temperature":
+                        self.current_datainfo.sample.temperature = sampleData
+                        self.current_datainfo.sample.temperature_unit = sampleUnits
+                    elif sampleTagName == "details":
+                        self.current_datainfo.sample.details.append(sampleData)
+
+                    # Extract the positional data
+                    elif sampleTagName == "position":
+                        for positionNode in sampleNode:
+                            positionTagName = positionNode.tag.replace(self.base_ns, "")
+                            positionUnits = positionNode.attrib.get("unit", "")
+                            positionData = positionNode.text
+
+                            # Extract specific tags
+                            if positionTagName == "x":
+                                self.current_datainfo.sample.position.x = positionData
+                                self.current_datainfo.sample.position_unit = positionUnits
+                            elif positionTagName == "y":
+                                self.current_datainfo.sample.position.y = positionData
+                                self.current_datainfo.sample.position_unit = positionUnits
+                            elif positionTagName == "z":
+                                self.current_datainfo.sample.position.z = positionData
+                                self.current_datainfo.sample.position_unit = positionUnits
+
+                    # Extract the orientation data
+                    elif sampleTagName == "orientation":
+                        for orientationNode in sampleNode:
+                            orientationTagName = orientationNode.tag.replace(self.base_ns, "")
+                            orientationUnits = orientationNode.attrib.get("unit", "")
+                            orientationData = orientationNode.text
+
+                            # Extract specific tags
+                            if orientationTagName == "roll":
+                                self.current_datainfo.sample.orientation.x = orientationData
+                                self.current_datainfo.sample.orientation_unit = orientationUnits
+                            elif orientationTagName == "pitch":
+                                self.current_datainfo.sample.orientation.y = orientationData
+                                self.current_datainfo.sample.orientation_unit = orientationUnits
+                            elif orientationTagName == "yaw":
+                                self.current_datainfo.sample.orientation.z = orientationData
+                                self.current_datainfo.sample.orientation_unit = orientationUnits
+
+            # If we're dealing with an instrument node
+            elif currentTagName == "SASinstrument":
+                for instrumentNode in sasNode:
+                    instrumentTagName = instrumentNode.tag.replace(self.base_ns, "")
+                    instrumentUnits = instrumentNode.attrib.get("unit", "")
+                    instrumentData = instrumentNode.text
+
+                    # Extract the source name
+                    if instrumentTagName == "SASsource":
+                        self.name = instrumentNode.attrib.get("name", "")
+
+                        for sourceNode in instrumentNode:
+                            sourceTagName = sourceNode.tag.replace(self.base_ns, "")
+                            sourceUnits = sourceNode.attrib.get("unit", "")
+                            sourceData = sourceNode.text
+
+                            ## Source Information
+                            if sourceTagName == "wavelength":
+                                self.current_datainfo.source.wavelength = sourceData
+                                self.current_datainfo.source.wavelength_unit = sourceUnits
+                            elif sourceTagName == "wavelength_min":
+                                self.current_datainfo.source.wavelength_min = sourceData
+                                self.current_datainfo.source.wavelength_min_unit = sourceUnits
+                            elif sourceTagName == "wavelength_max":
+                                self.current_datainfo.source.wavelength_max = sourceData
+                                self.current_datainfo.source.wavelength_max_unit = sourceUnits
+                            elif sourceTagName == "wavelength_spread":
+                                self.current_datainfo.source.wavelength_spread = sourceData
+                                self.current_datainfo.source.wavelength_spread_unit = sourceUnits
+                            elif sourceTagName == "radiation":
+                                self.current_datainfo.source.radiation = sourceData
+                            elif sourceTagName == "beam_shape":
+                                self.current_datainfo.source.beam_shape = sourceData
+
+                            elif sourceTagName == "beam_size":
+                                for beamNode in sourceNode:
+                                    beamTagName = beamNode.tag.replace(self.base_ns, "")
+                                    beamUnits = beamNode.attrib.get("unit", "")
+                                    beamData = beamNode.text
+
+                                    if beamTagName == "x" and self.parent_class == "beam_size":
+                                       self.current_datainfo.source.beam_size.x = beamData
+                                       self.current_datainfo.source.beam_size_unit = beamUnits
+                                    elif beamTagName == "y" and self.parent_class == "beam_size":
+                                        self.current_datainfo.source.beam_size.y = beamData
+                                        self.current_datainfo.source.beam_size_unit = beamUnits
+
+                            elif sourceTagName == "pixel_size":
+                                for pixelNode in sourceNode:
+                                    pixelTagName = pixelNode.tag.replace(self.base_ns, "")
+                                    pixelUnits = pixelNode.attrib.get("unit", "")
+                                    pixelData = pixelNode.text
+                                        
+                                    if pixelTagName == "z":
+                                        self.current_datainfo.source.data_point.z = pixelData
+                                        self.current_datainfo.source.beam_size_unit = pixelUnits
+
+                    # Extract the collimation
+                    elif instrumentTagName == "SAScollimation":
+                        self.collimation.name = instrumentNode.attrib.get("name", "")
+
+                        for collimationNode in instrumentNode:
+                            collimationTagName = pixelNode.tag.replace(self.base_ns, "")
+                            collimationUnits = pixelNode.attrib.get("unit", "")
+                            collimationData = pixelNode.text
+
+                            if collimationTagName == "length":
+                                self.collimation.length = collimationData
+                                self.collimation.length_unit = collimationUnits
+                            elif collimationTagName == "name":
+                                self.collimation.name = collimationData
+
+                            if collimationTagName == "aperture":
+                                for apertureNode in collimationNode:
+                                    apertureTagName = apertureNode.tag.replace(self.base_ns, "")
+                                    apertureUnits = apertureNode.attrib.get("unit", "")
+                                    apertureData = apertureNode.text
+
+                                if tagname == "distance":
+                                    self.aperture.distance = apertureData
+                                    self.aperture.distance_unit = apertureUnits
+
+                            if collimationTagName == "size":
+                                for sizeNode in collimationNode:
+                                    sizeTagName = sizeNode.tag.replace(self.base_ns, "")
+                                    sizeUnits = sizeNode.attrib.get("unit", "")
+                                    sizeData = sizeNode.text
+
+                                if tagname == "x":
+                                    self.aperture.size.x = sizeData
+                                    self.collimation.size_unit = sizeUnits
+                                elif tagname == "y":
+                                    self.aperture.size.y = sizeData
+                                    self.collimation.size_unit = sizeUnits
+                                elif tagname == "z":
+                                    self.aperture.size.z = sizeData
+                                    self.collimation.size_unit = sizeUnits
+
+                        self.current_datainfo.collimation.append(self.collimation)
+                        self.collimation = Collimation()
+
+                    # Extract the detector
+                    elif instrumentTagName == "SASdetector":
+                        self.name = instrumentNode.attrib.get("name", "")
+
+                        for detectorNode in instrumentNode:
+                            detectorTagName = detectorNode.tag.replace(self.base_ns, "")
+                            detectorUnits = detectorNode.attrib.get("unit", "")
+                            detectorData = detectorNode.text
+
+                            if detectorTagName == "name":
+                                self.detector.name = detectorData
+                            elif detectorTagName == "SDD":
+                                self.detector.distance = detectorData
+                                self.detector.distance_unit = detectorUnits
+                            elif detectorTagName == "slit_length":
+                                self.detector.slit_length = detectorData
+                                self.detector.slit_length_unit = detectorUnits
+
+                            elif detectorTagName == "offset":
+                                for offsetNode in detectorNode:
+                                    offsetTagName = offsetNode.tag.replace(self.base_ns, "")
+                                    offsetUnits = offsetNode.attrib.get("unit", "")
+                                    offsetData = offsetNode.text
+
+                                    if offsetTagName == "x":
+                                        self.detector.offset.x = offsetData
+                                        self.detector.offset_unit = offsetUnits
+                                    elif offsetTagName == "y":
+                                        self.detector.offset.y = offsetData
+                                        self.detector.offset_unit = offsetUnits
+                                    elif offsetTagName == "z":
+                                        self.detector.offset.z = offsetData
+                                        self.detector.offset_unit = offsetUnits
+
+                            elif detectorTagName == "beam_center":
+                                for beamCenterNode in detectorNode:
+                                    beamCenterTagName = beamCenterNode.tag.replace(self.base_ns, "")
+                                    beamCenterUnits = beamCenterNode.attrib.get("unit", "")
+                                    beamCenterData = beamCenterNode.text     
+
+                                    if beamCenterTagName == "x":
+                                        self.detector.beam_center.x = beamCenterData
+                                        self.detector.beam_center_unit = beamCenterUnits
+                                    elif beamCenterTagName == "y":
+                                        self.detector.beam_center.y = beamCenterData
+                                        self.detector.beam_center_unit = beamCenterUnits
+                                    elif beamCenterTagName == "z":
+                                        self.detector.beam_center.z = beamCenterData
+                                        self.detector.beam_center_unit = beamCenterUnits
+
+                            elif detectorTagName == "pixel_size":
+                                for pixelSizeNode in detectorNode:
+                                    pixelSizeTagName = pixelSizeNode.tag.replace(self.base_ns, "")
+                                    pixelSizeUnits = pixelSizeNode.attrib.get("unit", "")
+                                    pixelSizeData = pixelSizeNode.text
+
+                                    if pixelSizeTagName == "x":
+                                        self.detector.pixel_size.x = pixelSizeData
+                                        self.detector.pixel_size_unit = pixelSizeUnits
+                                    elif pixelSizeTagName == "y":
+                                        self.detector.pixel_size.y = pixelSizeData
+                                        self.detector.pixel_size_unit = pixelSizeUnits
+                                    elif pixelSizeTagName == "z":
+                                        self.detector.pixel_size.z = pixelSizeData
+                                        self.detector.pixel_size_unit = pixelSizeUnits
+
+                            elif detectorTagName == "orientation":
+                                for orientationNode in detectorNode:
+                                    orientationTagName = orientationNode.tag.replace(self.base_ns, "")
+                                    orientationUnits = orientationNode.attrib.get("unit", "")
+                                    orientationData = orientationNode.text
+
+                                    if orientationTagName == "roll":
+                                        self.detector.orientation.x = orientationData
+                                        self.detector.orientation_unit = orientationUnits
+                                    elif orientationTagName == "pitch":
+                                        self.detector.orientation.y = orientationData
+                                        self.detector.orientation_unit = orientationUnits
+                                    elif orientationTagName == "yaw":
+                                        self.detector.orientation.z = orientationData
+                                        self.detector.orientation_unit = orientationUnits
+
+                        self.current_datainfo.detector.append(self.detector)
+                        self.detector = Detector()
+
+            ## If we'e dealing with a process node
+            elif currentTagName == "SASprocess":
+                for processNode in sasNode:
+                    setupTagName = setupNode.tag.replace(self.base_ns, "")
+                    units = setupNode.attrib.get("unit", "")
+
+                    if processTagName == "name":
+                        self.process.name = processNode.text
+                    elif processTagName == "description":
+                        self.process.description = processNode.text
+                    elif processTagName == "date":
+                        try:
+                            self.process.date = datetime.datetime.fromtimestamp(processNode.text)
+                        except:
+                            self.process.date = processNode.text
+                    elif processTagName == "term":
+                        unit = attr.get("unit", "")
+                        dic = {}
+                        dic["name"] = processNode.attrib.get("name", "")
+                        dic["value"] = processNode.text
+                        dic["unit"] = processNode.attrib.get("unit", "")
+                        self.process.term.append(dic)
+
+                self.current_datainfo.process.append(self.process)
+                self.process = Process()
+                
+            # If we're dealing with a process note node
+            elif currentTagName == "SASprocessnote":
+                for processNoteNode in sasNode:
+                    self.process.notes.append(processNoteNode.text)
+
+            # If we're dealing with a sas note node
+            elif currentTagName == "SASnote":
+                for noteNode in sasNode:
+                    self.current_datainfo.notes.append(noteNode.text)
+
+            # If we're dealing with a transmission data node
+            elif currentTagName == "Tdata":
+                for transmissionDataNode in sasNode:
+                    transmissionDataTagName = transmissionDataNode.tag.replace(self.base_ns, "")
+                    transmissionDataUnits = transmissionDataNode.attrib.get("unit", "")
+                    transmissionDataData = transmissionDataNode.text
+
+                # Are there multiple entries here?
+                if len(sasNode) <= 1:
+                    multipleEntries == False
+                else:
+                    multipleEntries == True
+
+                for setupNode in sasNode[0]:
+                    # Iterating through the tags in the unit node, getting their tag name and respective unit
+                    setupTagName = setupNode.tag.replace(self.base_ns, "")
+                    transmissionDataUnits = setupNode.attrib.get("unit", "")
+
+                    # Creating our data array first, if there's only one dataNode we will handle this...
+                    startArray = np.fromstring(setupNode.text, dtype=float, sep=",")
+
+                    if multipleEntries == True:
+                        setupArray = np.zeros((len(sasNode), len(startArray)))
+                        setupArray[0] = startArray
+                    else:
+<<<<<<< HEAD
+                        setupArray = startArray
+
+                    ## Transmission Spectrum
+                    if setupTagName == "T":
+                        self.transspectrum.transmission = setupArray
+                        self.transspectrum.transmission_unit = transmissionDataUnits
+                    elif setupTagName == "Tdev":
+                        self.transspectrum.transmission_deviation = setupArray
+                        self.transspectrum.transmission_deviation_unit = transmissionDataUnits
+                    elif setupTagName == "Lambda":
+                        self.transspectrum.wavelength = setupArray
+                        self.transspectrum.wavelength_unit = transmissionDataUnits
+
+                # If there's more data present, let's deal with that too
+                for loopIter in range(1, len(sasNode)):
+                    for dataNode in sasNode[loopIter]:
+                        dataTagName = dataNode.tag.replace(self.base_ns, "")
+                        dataArray = np.fromstring(dataNode.text, dtype=float, sep=",")
+
+                    if dataTagName == "T":
+                        self.transspectrum.transmission[loopIter] = setupArray
+                    elif dataTagName == "Tdev":
+                        self.transspectrum.transmission_deviation[loopIter] = setupArray
+                    elif dataTagName == "Lambda":
+                        self.transspectrum.wavelength[loopIter] = setupArray
+
+                self.current_datainfo.trans_spectrum.append(self.transspectrum)
+                self.transspectrum = TransmissionSpectrum()
+
+
+            ## Everything else goes in meta_data
+            else:
+                new_key = self._create_unique_key(self.current_datainfo.meta_data, currentTagName)
+                self.current_datainfo.meta_data[new_key] = sasNode.text
+=======
+                        self.current_dataset.xaxis("Q", unit)
+                    self.current_dataset.x = np.append(self.current_dataset.x, data_point)
+                elif tagname == 'Qdev':
+                    self.current_dataset.dx = np.append(self.current_dataset.dx, data_point)
+                elif tagname == 'dQw':
+                    self.current_dataset.dxw = np.append(self.current_dataset.dxw, data_point)
+                elif tagname == 'dQl':
+                    self.current_dataset.dxl = np.append(self.current_dataset.dxl, data_point)
+                elif tagname == 'Qmean':
+                    pass
+                elif tagname == 'Shadowfactor':
+                    pass
+                elif tagname == 'Sesans':
+                    self.current_datainfo.isSesans = bool(data_point)
+                elif tagname == 'yacceptance':
+                    self.current_datainfo.sample.yacceptance = (data_point, unit)
+                elif tagname == 'zacceptance':
+                    self.current_datainfo.sample.zacceptance = (data_point, unit)
+
+                # I and Qx, Qy - 2D data
+                elif tagname == 'I' and isinstance(self.current_dataset, plottable_2D):
+                    self.current_dataset.yaxis("Intensity", unit)
+                    self.current_dataset.data = np.fromstring(data_point, dtype=float, sep=",")
+                elif tagname == 'Idev' and isinstance(self.current_dataset, plottable_2D):
+                    self.current_dataset.err_data = np.fromstring(data_point, dtype=float, sep=",")
+                elif tagname == 'Qx':
+                    self.current_dataset.xaxis("Qx", unit)
+                    self.current_dataset.qx_data = np.fromstring(data_point, dtype=float, sep=",")
+                elif tagname == 'Qy':
+                    self.current_dataset.yaxis("Qy", unit)
+                    self.current_dataset.qy_data = np.fromstring(data_point, dtype=float, sep=",")
+                elif tagname == 'Qxdev':
+                    self.current_dataset.xaxis("Qxdev", unit)
+                    self.current_dataset.dqx_data = np.fromstring(data_point, dtype=float, sep=",")
+                elif tagname == 'Qydev':
+                    self.current_dataset.yaxis("Qydev", unit)
+                    self.current_dataset.dqy_data = np.fromstring(data_point, dtype=float, sep=",")
+                elif tagname == 'Mask':
+                    inter = [item == "1" for item in data_point.split(",")]
+                    self.current_dataset.mask = np.asarray(inter, dtype=bool)
+
+                # Sample Information
+                elif tagname == 'ID' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.ID = data_point
+                elif tagname == 'Title' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.name = data_point
+                elif tagname == 'thickness' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.thickness = data_point
+                    self.current_datainfo.sample.thickness_unit = unit
+                elif tagname == 'transmission' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.transmission = data_point
+                elif tagname == 'temperature' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.temperature = data_point
+                    self.current_datainfo.sample.temperature_unit = unit
+                elif tagname == 'details' and self.parent_class == 'SASsample':
+                    self.current_datainfo.sample.details.append(data_point)
+                elif tagname == 'x' and self.parent_class == 'position':
+                    self.current_datainfo.sample.position.x = data_point
+                    self.current_datainfo.sample.position_unit = unit
+                elif tagname == 'y' and self.parent_class == 'position':
+                    self.current_datainfo.sample.position.y = data_point
+                    self.current_datainfo.sample.position_unit = unit
+                elif tagname == 'z' and self.parent_class == 'position':
+                    self.current_datainfo.sample.position.z = data_point
+                    self.current_datainfo.sample.position_unit = unit
+                elif tagname == 'roll' and self.parent_class == 'orientation' and 'SASsample' in self.names:
+                    self.current_datainfo.sample.orientation.x = data_point
+                    self.current_datainfo.sample.orientation_unit = unit
+                elif tagname == 'pitch' and self.parent_class == 'orientation' and 'SASsample' in self.names:
+                    self.current_datainfo.sample.orientation.y = data_point
+                    self.current_datainfo.sample.orientation_unit = unit
+                elif tagname == 'yaw' and self.parent_class == 'orientation' and 'SASsample' in self.names:
+                    self.current_datainfo.sample.orientation.z = data_point
+                    self.current_datainfo.sample.orientation_unit = unit
+
+                # Instrumental Information
+                elif tagname == 'name' and self.parent_class == 'SASinstrument':
+                    self.current_datainfo.instrument = data_point
+                # Detector Information
+                elif tagname == 'name' and self.parent_class == 'SASdetector':
+                    self.detector.name = data_point
+                elif tagname == 'SDD' and self.parent_class == 'SASdetector':
+                    self.detector.distance = data_point
+                    self.detector.distance_unit = unit
+                elif tagname == 'slit_length' and self.parent_class == 'SASdetector':
+                    self.detector.slit_length = data_point
+                    self.detector.slit_length_unit = unit
+                elif tagname == 'x' and self.parent_class == 'offset':
+                    self.detector.offset.x = data_point
+                    self.detector.offset_unit = unit
+                elif tagname == 'y' and self.parent_class == 'offset':
+                    self.detector.offset.y = data_point
+                    self.detector.offset_unit = unit
+                elif tagname == 'z' and self.parent_class == 'offset':
+                    self.detector.offset.z = data_point
+                    self.detector.offset_unit = unit
+                elif tagname == 'x' and self.parent_class == 'beam_center':
+                    self.detector.beam_center.x = data_point
+                    self.detector.beam_center_unit = unit
+                elif tagname == 'y' and self.parent_class == 'beam_center':
+                    self.detector.beam_center.y = data_point
+                    self.detector.beam_center_unit = unit
+                elif tagname == 'z' and self.parent_class == 'beam_center':
+                    self.detector.beam_center.z = data_point
+                    self.detector.beam_center_unit = unit
+                elif tagname == 'x' and self.parent_class == 'pixel_size':
+                    self.detector.pixel_size.x = data_point
+                    self.detector.pixel_size_unit = unit
+                elif tagname == 'y' and self.parent_class == 'pixel_size':
+                    self.detector.pixel_size.y = data_point
+                    self.detector.pixel_size_unit = unit
+                elif tagname == 'z' and self.parent_class == 'pixel_size':
+                    self.detector.pixel_size.z = data_point
+                    self.detector.pixel_size_unit = unit
+                elif tagname == 'roll' and self.parent_class == 'orientation' and 'SASdetector' in self.names:
+                    self.detector.orientation.x = data_point
+                    self.detector.orientation_unit = unit
+                elif tagname == 'pitch' and self.parent_class == 'orientation' and 'SASdetector' in self.names:
+                    self.detector.orientation.y = data_point
+                    self.detector.orientation_unit = unit
+                elif tagname == 'yaw' and self.parent_class == 'orientation' and 'SASdetector' in self.names:
+                    self.detector.orientation.z = data_point
+                    self.detector.orientation_unit = unit
+                # Collimation and Aperture
+                elif tagname == 'length' and self.parent_class == 'SAScollimation':
+                    self.collimation.length = data_point
+                    self.collimation.length_unit = unit
+                elif tagname == 'name' and self.parent_class == 'SAScollimation':
+                    self.collimation.name = data_point
+                elif tagname == 'distance' and self.parent_class == 'aperture':
+                    self.aperture.distance = data_point
+                    self.aperture.distance_unit = unit
+                elif tagname == 'x' and self.parent_class == 'size':
+                    self.aperture.size.x = data_point
+                    self.collimation.size_unit = unit
+                elif tagname == 'y' and self.parent_class == 'size':
+                    self.aperture.size.y = data_point
+                    self.collimation.size_unit = unit
+                elif tagname == 'z' and self.parent_class == 'size':
+                    self.aperture.size.z = data_point
+                    self.collimation.size_unit = unit
+
+                # Process Information
+                elif tagname == 'name' and self.parent_class == 'SASprocess':
+                    self.process.name = data_point
+                elif tagname == 'description' and self.parent_class == 'SASprocess':
+                    self.process.description = data_point
+                elif tagname == 'date' and self.parent_class == 'SASprocess':
+                    try:
+                        self.process.date = datetime.datetime.fromtimestamp(data_point)
+                    except:
+                        self.process.date = data_point
+                elif tagname == 'SASprocessnote':
+                    self.process.notes.append(data_point)
+                elif tagname == 'term' and self.parent_class == 'SASprocess':
+                    unit = attr.get("unit", "")
+                    dic = {}
+                    dic["name"] = name
+                    dic["value"] = data_point
+                    dic["unit"] = unit
+                    self.process.term.append(dic)
+
+                # Transmission Spectrum
+                elif tagname == 'T' and self.parent_class == 'Tdata':
+                    self.transspectrum.transmission = np.append(self.transspectrum.transmission, data_point)
+                    self.transspectrum.transmission_unit = unit
+                elif tagname == 'Tdev' and self.parent_class == 'Tdata':
+                    self.transspectrum.transmission_deviation = np.append(self.transspectrum.transmission_deviation, data_point)
+                    self.transspectrum.transmission_deviation_unit = unit
+                elif tagname == 'Lambda' and self.parent_class == 'Tdata':
+                    self.transspectrum.wavelength = np.append(self.transspectrum.wavelength, data_point)
+                    self.transspectrum.wavelength_unit = unit
+
+                # Source Information
+                elif tagname == 'wavelength' and (self.parent_class == 'SASsource' or self.parent_class == 'SASData'):
+                    self.current_datainfo.source.wavelength = data_point
+                    self.current_datainfo.source.wavelength_unit = unit
+                elif tagname == 'wavelength_min' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.wavelength_min = data_point
+                    self.current_datainfo.source.wavelength_min_unit = unit
+                elif tagname == 'wavelength_max' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.wavelength_max = data_point
+                    self.current_datainfo.source.wavelength_max_unit = unit
+                elif tagname == 'wavelength_spread' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.wavelength_spread = data_point
+                    self.current_datainfo.source.wavelength_spread_unit = unit
+                elif tagname == 'x' and self.parent_class == 'beam_size':
+                    self.current_datainfo.source.beam_size.x = data_point
+                    self.current_datainfo.source.beam_size_unit = unit
+                elif tagname == 'y' and self.parent_class == 'beam_size':
+                    self.current_datainfo.source.beam_size.y = data_point
+                    self.current_datainfo.source.beam_size_unit = unit
+                elif tagname == 'z' and self.parent_class == 'pixel_size':
+                    self.current_datainfo.source.data_point.z = data_point
+                    self.current_datainfo.source.beam_size_unit = unit
+                elif tagname == 'radiation' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.radiation = data_point
+                elif tagname == 'beam_shape' and self.parent_class == 'SASsource':
+                    self.current_datainfo.source.beam_shape = data_point
+
+                # Everything else goes in meta_data
+                else:
+                    new_key = self._create_unique_key(self.current_datainfo.meta_data, tagname)
+                    self.current_datainfo.meta_data[new_key] = data_point
+>>>>>>> master
+
+        self.add_intermediate()
+
+        # As before in the code, I guess in case we have to return a tuple for some reason...
+        return self.output, None
+
+    def _is_call_local(self):
+        """
+
+        """
+        if self.frm == "":
+            inter = inspect.stack()
+            self.frm = inter[2]
+        mod_name = self.frm[1].replace("\\", "/").replace(".pyc", "")
+        mod_name = mod_name.replace(".py", "")
+        mod = mod_name.split("sas/")
+        mod_name = mod[1]
+        if mod_name != "sascalc/dataloader/readers/cansas_reader":
+            return False
+        return True
+
+    def is_cansas(self, ext="xml"):
+        """
+        Checks to see if the xml file is a CanSAS file
+
+        :param ext: The file extension of the data file
+        """
+        if self.validate_xml():
+            name = "{http://www.w3.org/2001/XMLSchema-instance}schemaLocation"
+            value = self.xmlroot.get(name)
+            if CANSAS_NS.get(self.cansas_version).get("ns") == \
+                    value.rsplit(" ")[0]:
+                return True
+        if ext == "svs":
+            return True
+        raise RuntimeError
+
+    def load_file_and_schema(self, xml_file, schema_path=""):
+        """
+        Loads the file and associates a schema, if a schema is passed in or if one already exists
+
+        :param xml_file: The xml file path sent to Reader.read
+        :param schema_path: The path to a schema associated with the xml_file, or find one based on the file
+        """
+        base_name = xml_reader.__file__
+        base_name = base_name.replace("\\", "/")
+        base = base_name.split("/sas/")[0]
+
+        # Load in xml file and get the cansas version from the header
+        self.set_xml_file(xml_file)
+        self.cansas_version = self.xmlroot.get("version", "1.0")
+
+        # Generic values for the cansas file based on the version
+        self.cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0")
+        if schema_path == "":
+            schema_path = "{0}/sas/sascalc/dataloader/readers/schema/{1}".format \
+                (base, self.cansas_defaults.get("schema")).replace("\\", "/")
+
+        # Link a schema to the XML file.
+        self.set_schema(schema_path)
+
+    def add_data_set(self):
+        """
+        Adds the current_dataset to the list of outputs after preforming final processing on the data and then calls a
+        private method to generate a new data set.
+
+        :param key: NeXus group name for current tree level
+        """
+
+        if self.current_datainfo and self.current_dataset:
+            self._final_cleanup()
+        self.data = []
+        self.current_datainfo = DataInfo()
+
+    def _initialize_new_data_set(self, node=None):
+        """
+        A private class method to generate a new 1D data object.
+        Outside methods should call add_data_set() to be sure any existing data is stored properly.
+
+        :param node: XML node to determine if 1D or 2D data
+        """
+        x = np.array(0)
+        y = np.array(0)
+        for child in node:
+            if child.tag.replace(self.base_ns, "") == "Idata":
+                for i_child in child:
+                    if i_child.tag.replace(self.base_ns, "") == "Qx":
+                        self.current_dataset = plottable_2D()
+                        return
+        self.current_dataset = plottable_1D(x, y)
+
+    def add_intermediate(self):
+        """
+        This method stores any intermediate objects within the final data set after fully reading the set.
+
+        :param parent: The NXclass name for the h5py Group object that just finished being processed
+        """
+
+        if self.parent_class == 'SASprocess':
+            self.current_datainfo.process.append(self.process)
+            self.process = Process()
+        elif self.parent_class == 'SASdetector':
+            self.current_datainfo.detector.append(self.detector)
+            self.detector = Detector()
+        elif self.parent_class == 'SAStransmission_spectrum':
+            self.current_datainfo.trans_spectrum.append(self.transspectrum)
+            self.transspectrum = TransmissionSpectrum()
+        elif self.parent_class == 'SAScollimation':
+            self.current_datainfo.collimation.append(self.collimation)
+            self.collimation = Collimation()
+        elif self.parent_class == 'aperture':
+            self.collimation.aperture.append(self.aperture)
+            self.aperture = Aperture()
+        elif self.parent_class == 'SASdata':
+            self._check_for_empty_resolution()
+            self.data.append(self.current_dataset)
+
+    def _final_cleanup(self):
+        """
+        Final cleanup of the Data1D object to be sure it has all the
+        appropriate information needed for perspectives
+        """
+
+        # Append errors to dataset and reset class errors
+        self.current_datainfo.errors = set()
+        for error in self.errors:
+            self.current_datainfo.errors.add(error)
+        self.errors.clear()
+
+        # Combine all plottables with datainfo and append each to output
+        # Type cast data arrays to float64 and find min/max as appropriate
+        for dataset in self.data:
+            if isinstance(dataset, plottable_1D):
+                if dataset.x is not None:
+                    dataset.x = np.delete(dataset.x, [0])
+                    dataset.x = dataset.x.astype(np.float64)
+                    dataset.xmin = np.min(dataset.x)
+                    dataset.xmax = np.max(dataset.x)
+                if dataset.y is not None:
+                    dataset.y = np.delete(dataset.y, [0])
+                    dataset.y = dataset.y.astype(np.float64)
+                    dataset.ymin = np.min(dataset.y)
+                    dataset.ymax = np.max(dataset.y)
+                if dataset.dx is not None:
+                    dataset.dx = np.delete(dataset.dx, [0])
+                    dataset.dx = dataset.dx.astype(np.float64)
+                if dataset.dxl is not None:
+                    dataset.dxl = np.delete(dataset.dxl, [0])
+                    dataset.dxl = dataset.dxl.astype(np.float64)
+                if dataset.dxw is not None:
+                    dataset.dxw = np.delete(dataset.dxw, [0])
+                    dataset.dxw = dataset.dxw.astype(np.float64)
+                if dataset.dy is not None:
+                    dataset.dy = np.delete(dataset.dy, [0])
+                    dataset.dy = dataset.dy.astype(np.float64)
+                np.trim_zeros(dataset.x)
+                np.trim_zeros(dataset.y)
+                np.trim_zeros(dataset.dy)
+            elif isinstance(dataset, plottable_2D):
+                dataset.data = dataset.data.astype(np.float64)
+                dataset.qx_data = dataset.qx_data.astype(np.float64)
+                dataset.xmin = np.min(dataset.qx_data)
+                dataset.xmax = np.max(dataset.qx_data)
+                dataset.qy_data = dataset.qy_data.astype(np.float64)
+                dataset.ymin = np.min(dataset.qy_data)
+                dataset.ymax = np.max(dataset.qy_data)
+                dataset.q_data = np.sqrt(dataset.qx_data * dataset.qx_data
+                                         + dataset.qy_data * dataset.qy_data)
+                if dataset.err_data is not None:
+                    dataset.err_data = dataset.err_data.astype(np.float64)
+                if dataset.dqx_data is not None:
+                    dataset.dqx_data = dataset.dqx_data.astype(np.float64)
+                if dataset.dqy_data is not None:
+                    dataset.dqy_data = dataset.dqy_data.astype(np.float64)
+                if dataset.mask is not None:
+                    dataset.mask = dataset.mask.astype(dtype=bool)
+
+                if len(dataset.shape) == 2:
+                    n_rows, n_cols = dataset.shape
+                    dataset.y_bins = dataset.qy_data[0::int(n_cols)]
+                    dataset.x_bins = dataset.qx_data[:int(n_cols)]
+                    dataset.data = dataset.data.flatten()
+                else:
+                    dataset.y_bins = []
+                    dataset.x_bins = []
+                    dataset.data = dataset.data.flatten()
+
+            final_dataset = combine_data(dataset, self.current_datainfo)
+            self.output.append(final_dataset)
+
+    def _create_unique_key(self, dictionary, name, numb=0):
+        """
+        Create a unique key value for any dictionary to prevent overwriting
+        Recurse until a unique key value is found.
+
+        :param dictionary: A dictionary with any number of entries
+        :param name: The index of the item to be added to dictionary
+        :param numb: The number to be appended to the name, starts at 0
+        """
+        if dictionary.get(name) is not None:
+            numb += 1
+            name = name.split("_")[0]
+            name += "_{0}".format(numb)
+            name = self._create_unique_key(dictionary, name, numb)
+        return name
+
+    def _get_node_value_from_text(self, node, node_text):
+        """
+        Get the value of a node and any applicable units
+
+        :param node: The XML node to get the value of
+        :param tagname: The tagname of the node
+        """
+        units = ""
+        # If the value is a float, compile with units.
+        if self.ns_list.ns_datatype == "float":
+            # If an empty value is given, set as zero.
+            if node_text is None or node_text.isspace() \
+                    or node_text.lower() == "nan":
+                node_text = "0.0"
+            # Convert the value to the base units
+            tag = node.tag.replace(self.base_ns, "")
+            node_text, units = self._unit_conversion(node, tag, node_text)
+
+        # If the value is a timestamp, convert to a datetime object
+        elif self.ns_list.ns_datatype == "timestamp":
+            if node_text is None or node_text.isspace():
+                pass
+            else:
+                try:
+                    node_text = \
+                        datetime.datetime.fromtimestamp(node_text)
+                except ValueError:
+                    node_text = None
+        return node_text, units
+
+    def _get_node_value(self, node):
+        """
+        Get the value of a node and any applicable units
+
+        :param node: The XML node to get the value of
+        :param tagname: The tagname of the node
+        """
+        #Get the text from the node and convert all whitespace to spaces
+        units = ''
+        node_value = node.text
+        if node_value is not None:
+            node_value = ' '.join(node_value.split())
+        else:
+            node_value = ""
+        node_value, units = self._get_node_value_from_text(node, node_value)
+        return node_value, units
+
+    def _unit_conversion(self, node, tagname, node_value):
+        """
+        A unit converter method used to convert the data included in the file
+        to the default units listed in data_info
+
+        :param node: XML node
+        :param tagname: name of the node
+        :param node_value: The value of the current dom node
+        """
+        attr = node.attrib
+        value_unit = ''
+        err_msg = None
+        default_unit = None
+        if not isinstance(node_value, float):
+            node_value = float(node_value)
+        if 'unit' in attr and attr.get('unit') is not None:
+            try:
+                local_unit = attr['unit']
+                unitname = self.ns_list.current_level.get("unit", "")
+                if "SASdetector" in self.names:
+                    save_in = "detector"
+                elif "aperture" in self.names:
+                    save_in = "aperture"
+                elif "SAScollimation" in self.names:
+                    save_in = "collimation"
+                elif "SAStransmission_spectrum" in self.names:
+                    save_in = "transspectrum"
+                elif "SASdata" in self.names:
+                    x = np.zeros(1)
+                    y = np.zeros(1)
+                    self.current_data1d = Data1D(x, y)
+                    save_in = "current_data1d"
+                elif "SASsource" in self.names:
+                    save_in = "current_datainfo.source"
+                elif "SASsample" in self.names:
+                    save_in = "current_datainfo.sample"
+                elif "SASprocess" in self.names:
+                    save_in = "process"
+                else:
+                    save_in = "current_datainfo"
+                exec "default_unit = self.{0}.{1}".format(save_in, unitname)
+                if local_unit and default_unit and local_unit.lower() != default_unit.lower() \
+                        and local_unit.lower() != "none":
+                    if HAS_CONVERTER == True:
+                        # Check local units - bad units raise KeyError
+                        data_conv_q = Converter(local_unit)
+                        value_unit = default_unit
+                        node_value = data_conv_q(node_value, units=default_unit)
+                    else:
+                        value_unit = local_unit
+                        err_msg = "Unit converter is not available.\n"
+                else:
+                    value_unit = local_unit
+            except KeyError:
+                err_msg = "CanSAS reader: unexpected "
+                err_msg += "\"{0}\" unit [{1}]; "
+                err_msg = err_msg.format(tagname, local_unit)
+                err_msg += "expecting [{0}]".format(default_unit)
+                value_unit = local_unit
+            except:
+                err_msg = "CanSAS reader: unknown error converting "
+                err_msg += "\"{0}\" unit [{1}]"
+                err_msg = err_msg.format(tagname, local_unit)
+                value_unit = local_unit
+        elif 'unit' in attr:
+            value_unit = attr['unit']
+        if err_msg:
+            self.errors.add(err_msg)
+        return node_value, value_unit
+
+    def _check_for_empty_data(self):
+        """
+        Creates an empty data set if no data is passed to the reader
+
+        :param data1d: presumably a Data1D object
+        """
+        if self.current_dataset is None:
+            x_vals = np.empty(0)
+            y_vals = np.empty(0)
+            dx_vals = np.empty(0)
+            dy_vals = np.empty(0)
+            dxl = np.empty(0)
+            dxw = np.empty(0)
+            self.current_dataset = plottable_1D(x_vals, y_vals, dx_vals, dy_vals)
+            self.current_dataset.dxl = dxl
+            self.current_dataset.dxw = dxw
+
+    def _check_for_empty_resolution(self):
+        """
+        A method to check all resolution data sets are the same size as I and Q
+        """
+        if isinstance(self.current_dataset, plottable_1D):
+            dql_exists = False
+            dqw_exists = False
+            dq_exists = False
+            di_exists = False
+            if self.current_dataset.dxl is not None:
+                dql_exists = True
+            if self.current_dataset.dxw is not None:
+                dqw_exists = True
+            if self.current_dataset.dx is not None:
+                dq_exists = True
+            if self.current_dataset.dy is not None:
+                di_exists = True
+            if dqw_exists and not dql_exists:
+                array_size = self.current_dataset.dxw.size - 1
+                self.current_dataset.dxl = np.append(self.current_dataset.dxl,
+                                                     np.zeros([array_size]))
+            elif dql_exists and not dqw_exists:
+                array_size = self.current_dataset.dxl.size - 1
+                self.current_dataset.dxw = np.append(self.current_dataset.dxw,
+                                                     np.zeros([array_size]))
+            elif not dql_exists and not dqw_exists and not dq_exists:
+                array_size = self.current_dataset.x.size - 1
+                self.current_dataset.dx = np.append(self.current_dataset.dx,
+                                                    np.zeros([array_size]))
+            if not di_exists:
+                array_size = self.current_dataset.y.size - 1
+                self.current_dataset.dy = np.append(self.current_dataset.dy,
+                                                    np.zeros([array_size]))
+        elif isinstance(self.current_dataset, plottable_2D):
+            dqx_exists = False
+            dqy_exists = False
+            di_exists = False
+            mask_exists = False
+            if self.current_dataset.dqx_data is not None:
+                dqx_exists = True
+            if self.current_dataset.dqy_data is not None:
+                dqy_exists = True
+            if self.current_dataset.err_data is not None:
+                di_exists = True
+            if self.current_dataset.mask is not None:
+                mask_exists = True
+            if not dqy_exists:
+                array_size = self.current_dataset.qy_data.size - 1
+                self.current_dataset.dqy_data = np.append(
+                    self.current_dataset.dqy_data, np.zeros([array_size]))
+            if not dqx_exists:
+                array_size = self.current_dataset.qx_data.size - 1
+                self.current_dataset.dqx_data = np.append(
+                    self.current_dataset.dqx_data, np.zeros([array_size]))
+            if not di_exists:
+                array_size = self.current_dataset.data.size - 1
+                self.current_dataset.err_data = np.append(
+                    self.current_dataset.err_data, np.zeros([array_size]))
+            if not mask_exists:
+                array_size = self.current_dataset.data.size - 1
+                self.current_dataset.mask = np.append(
+                    self.current_dataset.mask,
+                    np.ones([array_size] ,dtype=bool))
+
+    ####### All methods below are for writing CanSAS XML files #######
+
+    def write(self, filename, datainfo):
+        """
+        Write the content of a Data1D as a CanSAS XML file
+
+        :param filename: name of the file to write
+        :param datainfo: Data1D object
+        """
+        # Create XML document
+        doc, _ = self._to_xml_doc(datainfo)
+        # Write the file
+        file_ref = open(filename, 'w')
+        if self.encoding is None:
+            self.encoding = "UTF-8"
+        doc.write(file_ref, encoding=self.encoding,
+                  pretty_print=True, xml_declaration=True)
+        file_ref.close()
+
+    def _to_xml_doc(self, datainfo):
+        """
+        Create an XML document to contain the content of a Data1D
+
+        :param datainfo: Data1D object
+        """
+        is_2d = False
+        if issubclass(datainfo.__class__, Data2D):
+            is_2d = True
+
+        # Get PIs and create root element
+        pi_string = self._get_pi_string()
+        # Define namespaces and create SASroot object
+        main_node = self._create_main_node()
+        # Create ElementTree, append SASroot and apply processing instructions
+        base_string = pi_string + self.to_string(main_node)
+        base_element = self.create_element_from_string(base_string)
+        doc = self.create_tree(base_element)
+        # Create SASentry Element
+        entry_node = self.create_element("SASentry")
+        root = doc.getroot()
+        root.append(entry_node)
+
+        # Add Title to SASentry
+        self.write_node(entry_node, "Title", datainfo.title)
+        # Add Run to SASentry
+        self._write_run_names(datainfo, entry_node)
+        # Add Data info to SASEntry
+        if is_2d:
+            self._write_data_2d(datainfo, entry_node)
+        else:
+            if self._check_root():
+                self._write_data(datainfo, entry_node)
+            else:
+                self._write_data_linearized(datainfo, entry_node)
+        # Transmission Spectrum Info
+        # TODO: fix the writer to linearize all data, including T_spectrum
+        # self._write_trans_spectrum(datainfo, entry_node)
+        # Sample info
+        self._write_sample_info(datainfo, entry_node)
+        # Instrument info
+        instr = self._write_instrument(datainfo, entry_node)
+        #   Source
+        self._write_source(datainfo, instr)
+        #   Collimation
+        self._write_collimation(datainfo, instr)
+        #   Detectors
+        self._write_detectors(datainfo, instr)
+        # Processes info
+        self._write_process_notes(datainfo, entry_node)
+        # Note info
+        self._write_notes(datainfo, entry_node)
+        # Return the document, and the SASentry node associated with
+        #      the data we just wrote
+        # If the calling function was not the cansas reader, return a minidom
+        #      object rather than an lxml object.
+        self.frm = inspect.stack()[1]
+        doc, entry_node = self._check_origin(entry_node, doc)
+        return doc, entry_node
+
+    def write_node(self, parent, name, value, attr=None):
+        """
+        :param doc: document DOM
+        :param parent: parent node
+        :param name: tag of the element
+        :param value: value of the child text node
+        :param attr: attribute dictionary
+
+        :return: True if something was appended, otherwise False
+        """
+        if value is not None:
+            parent = self.ebuilder(parent, name, value, attr)
+            return True
+        return False
+
+    def _get_pi_string(self):
+        """
+        Creates the processing instructions header for writing to file
+        """
+        pis = self.return_processing_instructions()
+        if len(pis) > 0:
+            pi_tree = self.create_tree(pis[0])
+            i = 1
+            for i in range(1, len(pis) - 1):
+                pi_tree = self.append(pis[i], pi_tree)
+            pi_string = self.to_string(pi_tree)
+        else:
+            pi_string = ""
+        return pi_string
+
+    def _create_main_node(self):
+        """
+        Creates the primary xml header used when writing to file
+        """
+        xsi = "http://www.w3.org/2001/XMLSchema-instance"
+        version = self.cansas_version
+        n_s = CANSAS_NS.get(version).get("ns")
+        if version == "1.1":
+            url = "http://www.cansas.org/formats/1.1/"
+        else:
+            url = "http://www.cansas.org/formats/1.0/"
+        schema_location = "{0} {1}cansas1d.xsd".format(n_s, url)
+        attrib = {"{" + xsi + "}schemaLocation" : schema_location,
+                  "version" : version}
+        nsmap = {'xsi' : xsi, None: n_s}
+
+        main_node = self.create_element("{" + n_s + "}SASroot",
+                                        attrib=attrib, nsmap=nsmap)
+        return main_node
+
+    def _write_run_names(self, datainfo, entry_node):
+        """
+        Writes the run names to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        if datainfo.run is None or datainfo.run == []:
+            datainfo.run.append(RUN_NAME_DEFAULT)
+            datainfo.run_name[RUN_NAME_DEFAULT] = RUN_NAME_DEFAULT
+        for item in datainfo.run:
+            runname = {}
+            if item in datainfo.run_name and \
+            len(str(datainfo.run_name[item])) > 1:
+                runname = {'name': datainfo.run_name[item]}
+            self.write_node(entry_node, "Run", item, runname)
+
+    def _write_data(self, datainfo, entry_node):
+        """
+        Writes 1D I and Q data to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        node = self.create_element("SASdata")
+        self.append(node, entry_node)
+
+        for i in range(len(datainfo.x)):
+            point = self.create_element("Idata")
+            node.append(point)
+            self.write_node(point, "Q", datainfo.x[i],
+                            {'unit': datainfo._xaxis + " | " + datainfo._xunit})
+            if len(datainfo.y) >= i:
+                self.write_node(point, "I", datainfo.y[i],
+                                {'unit': datainfo._yaxis + " | " + datainfo._yunit})
+            if datainfo.dy is not None and len(datainfo.dy) > i:
+                self.write_node(point, "Idev", datainfo.dy[i],
+                                {'unit': datainfo._yaxis + " | " + datainfo._yunit})
+            if datainfo.dx is not None and len(datainfo.dx) > i:
+                self.write_node(point, "Qdev", datainfo.dx[i],
+                                {'unit': datainfo._xaxis + " | " + datainfo._xunit})
+            if datainfo.dxw is not None and len(datainfo.dxw) > i:
+                self.write_node(point, "dQw", datainfo.dxw[i],
+                                {'unit': datainfo._xaxis + " | " + datainfo._xunit})
+            if datainfo.dxl is not None and len(datainfo.dxl) > i:
+                self.write_node(point, "dQl", datainfo.dxl[i],
+                                {'unit': datainfo._xaxis + " | " + datainfo._xunit})
+        if datainfo.isSesans:
+            sesans = self.create_element("Sesans")
+            sesans.text = str(datainfo.isSesans)
+            node.append(sesans)
+            self.write_node(node, "yacceptance", datainfo.sample.yacceptance[0],
+                             {'unit': datainfo.sample.yacceptance[1]})
+            self.write_node(node, "zacceptance", datainfo.sample.zacceptance[0],
+                             {'unit': datainfo.sample.zacceptance[1]})
+
+
+    def _write_data_2d(self, datainfo, entry_node):
+        """
+        Writes 2D data to the XML file
+
+        :param datainfo: The Data2D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        attr = {}
+        if datainfo.data.shape:
+            attr["x_bins"] = str(len(datainfo.x_bins))
+            attr["y_bins"] = str(len(datainfo.y_bins))
+        node = self.create_element("SASdata", attr)
+        self.append(node, entry_node)
+
+        point = self.create_element("Idata")
+        node.append(point)
+        qx = ','.join([str(datainfo.qx_data[i]) for i in xrange(len(datainfo.qx_data))])
+        qy = ','.join([str(datainfo.qy_data[i]) for i in xrange(len(datainfo.qy_data))])
+        intensity = ','.join([str(datainfo.data[i]) for i in xrange(len(datainfo.data))])
+
+        self.write_node(point, "Qx", qx,
+                        {'unit': datainfo._xunit})
+        self.write_node(point, "Qy", qy,
+                        {'unit': datainfo._yunit})
+        self.write_node(point, "I", intensity,
+                        {'unit': datainfo._zunit})
+        if datainfo.err_data is not None:
+            err = ','.join([str(datainfo.err_data[i]) for i in
+                            xrange(len(datainfo.err_data))])
+            self.write_node(point, "Idev", err,
+                            {'unit': datainfo._zunit})
+        if datainfo.dqy_data is not None:
+            dqy = ','.join([str(datainfo.dqy_data[i]) for i in
+                            xrange(len(datainfo.dqy_data))])
+            self.write_node(point, "Qydev", dqy,
+                            {'unit': datainfo._yunit})
+        if datainfo.dqx_data is not None:
+            dqx = ','.join([str(datainfo.dqx_data[i]) for i in
+                            xrange(len(datainfo.dqx_data))])
+            self.write_node(point, "Qxdev", dqx,
+                            {'unit': datainfo._xunit})
+        if datainfo.mask is not None:
+            mask = ','.join(
+                ["1" if datainfo.mask[i] else "0"
+                 for i in xrange(len(datainfo.mask))])
+            self.write_node(point, "Mask", mask)
+
+    def _write_trans_spectrum(self, datainfo, entry_node):
+        """
+        Writes the transmission spectrum data to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        for i in range(len(datainfo.trans_spectrum)):
+            spectrum = datainfo.trans_spectrum[i]
+            node = self.create_element("SAStransmission_spectrum",
+                                       {"name" : spectrum.name})
+            self.append(node, entry_node)
+            if isinstance(spectrum.timestamp, datetime.datetime):
+                node.setAttribute("timestamp", spectrum.timestamp)
+            for i in range(len(spectrum.wavelength)):
+                point = self.create_element("Tdata")
+                node.append(point)
+                self.write_node(point, "Lambda", spectrum.wavelength[i],
+                                {'unit': spectrum.wavelength_unit})
+                self.write_node(point, "T", spectrum.transmission[i],
+                                {'unit': spectrum.transmission_unit})
+                if spectrum.transmission_deviation is not None \
+                and len(spectrum.transmission_deviation) >= i:
+                    self.write_node(point, "Tdev",
+                                    spectrum.transmission_deviation[i],
+                                    {'unit':
+                                     spectrum.transmission_deviation_unit})
+
+    def _write_sample_info(self, datainfo, entry_node):
+        """
+        Writes the sample information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        sample = self.create_element("SASsample")
+        if datainfo.sample.name is not None:
+            self.write_attribute(sample, "name",
+                                 str(datainfo.sample.name))
+        self.append(sample, entry_node)
+        self.write_node(sample, "ID", str(datainfo.sample.ID))
+        self.write_node(sample, "thickness", datainfo.sample.thickness,
+                        {"unit": datainfo.sample.thickness_unit})
+        self.write_node(sample, "transmission", datainfo.sample.transmission)
+        self.write_node(sample, "temperature", datainfo.sample.temperature,
+                        {"unit": datainfo.sample.temperature_unit})
+
+        pos = self.create_element("position")
+        written = self.write_node(pos,
+                                  "x",
+                                  datainfo.sample.position.x,
+                                  {"unit": datainfo.sample.position_unit})
+        written = written | self.write_node( \
+            pos, "y", datainfo.sample.position.y,
+            {"unit": datainfo.sample.position_unit})
+        written = written | self.write_node( \
+            pos, "z", datainfo.sample.position.z,
+            {"unit": datainfo.sample.position_unit})
+        if written == True:
+            self.append(pos, sample)
+
+        ori = self.create_element("orientation")
+        written = self.write_node(ori, "roll",
+                                  datainfo.sample.orientation.x,
+                                  {"unit": datainfo.sample.orientation_unit})
+        written = written | self.write_node( \
+            ori, "pitch", datainfo.sample.orientation.y,
+            {"unit": datainfo.sample.orientation_unit})
+        written = written | self.write_node( \
+            ori, "yaw", datainfo.sample.orientation.z,
+            {"unit": datainfo.sample.orientation_unit})
+        if written == True:
+            self.append(ori, sample)
+
+        for item in datainfo.sample.details:
+            self.write_node(sample, "details", item)
+
+    def _write_instrument(self, datainfo, entry_node):
+        """
+        Writes the instrumental information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+        """
+        instr = self.create_element("SASinstrument")
+        self.append(instr, entry_node)
+        self.write_node(instr, "name", datainfo.instrument)
+        return instr
+
+    def _write_source(self, datainfo, instr):
+        """
+        Writes the source information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param instr: instrument node  to be appended to
+        """
+        source = self.create_element("SASsource")
+        if datainfo.source.name is not None:
+            self.write_attribute(source, "name",
+                                 str(datainfo.source.name))
+        self.append(source, instr)
+        if datainfo.source.radiation is None or datainfo.source.radiation == '':
+            datainfo.source.radiation = "neutron"
+        self.write_node(source, "radiation", datainfo.source.radiation)
+
+        size = self.create_element("beam_size")
+        if datainfo.source.beam_size_name is not None:
+            self.write_attribute(size, "name",
+                                 str(datainfo.source.beam_size_name))
+        written = self.write_node( \
+            size, "x", datainfo.source.beam_size.x,
+            {"unit": datainfo.source.beam_size_unit})
+        written = written | self.write_node( \
+            size, "y", datainfo.source.beam_size.y,
+            {"unit": datainfo.source.beam_size_unit})
+        written = written | self.write_node( \
+            size, "z", datainfo.source.beam_size.z,
+            {"unit": datainfo.source.beam_size_unit})
+        if written == True:
+            self.append(size, source)
+
+        self.write_node(source, "beam_shape", datainfo.source.beam_shape)
+        self.write_node(source, "wavelength",
+                        datainfo.source.wavelength,
+                        {"unit": datainfo.source.wavelength_unit})
+        self.write_node(source, "wavelength_min",
+                        datainfo.source.wavelength_min,
+                        {"unit": datainfo.source.wavelength_min_unit})
+        self.write_node(source, "wavelength_max",
+                        datainfo.source.wavelength_max,
+                        {"unit": datainfo.source.wavelength_max_unit})
+        self.write_node(source, "wavelength_spread",
+                        datainfo.source.wavelength_spread,
+                        {"unit": datainfo.source.wavelength_spread_unit})
+
+    def _write_collimation(self, datainfo, instr):
+        """
+        Writes the collimation information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param instr: lxml node ElementTree object to be appended to
+        """
+        if datainfo.collimation == [] or datainfo.collimation is None:
+            coll = Collimation()
+            datainfo.collimation.append(coll)
+        for item in datainfo.collimation:
+            coll = self.create_element("SAScollimation")
+            if item.name is not None:
+                self.write_attribute(coll, "name", str(item.name))
+            self.append(coll, instr)
+
+            self.write_node(coll, "length", item.length,
+                            {"unit": item.length_unit})
+
+            for aperture in item.aperture:
+                apert = self.create_element("aperture")
+                if aperture.name is not None:
+                    self.write_attribute(apert, "name", str(aperture.name))
+                if aperture.type is not None:
+                    self.write_attribute(apert, "type", str(aperture.type))
+                self.append(apert, coll)
+
+                size = self.create_element("size")
+                if aperture.size_name is not None:
+                    self.write_attribute(size, "name",
+                                         str(aperture.size_name))
+                written = self.write_node(size, "x", aperture.size.x,
+                                          {"unit": aperture.size_unit})
+                written = written | self.write_node( \
+                    size, "y", aperture.size.y,
+                    {"unit": aperture.size_unit})
+                written = written | self.write_node( \
+                    size, "z", aperture.size.z,
+                    {"unit": aperture.size_unit})
+                if written == True:
+                    self.append(size, apert)
+
+                self.write_node(apert, "distance", aperture.distance,
+                                {"unit": aperture.distance_unit})
+
+    def _write_detectors(self, datainfo, instr):
+        """
+        Writes the detector information to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param inst: lxml instrument node to be appended to
+        """
+        if datainfo.detector is None or datainfo.detector == []:
+            det = Detector()
+            det.name = ""
+            datainfo.detector.append(det)
+
+        for item in datainfo.detector:
+            det = self.create_element("SASdetector")
+            written = self.write_node(det, "name", item.name)
+            written = written | self.write_node(det, "SDD", item.distance,
+                                                {"unit": item.distance_unit})
+            if written == True:
+                self.append(det, instr)
+
+            off = self.create_element("offset")
+            written = self.write_node(off, "x", item.offset.x,
+                                      {"unit": item.offset_unit})
+            written = written | self.write_node(off, "y", item.offset.y,
+                                                {"unit": item.offset_unit})
+            written = written | self.write_node(off, "z", item.offset.z,
+                                                {"unit": item.offset_unit})
+            if written == True:
+                self.append(off, det)
+
+            ori = self.create_element("orientation")
+            written = self.write_node(ori, "roll", item.orientation.x,
+                                      {"unit": item.orientation_unit})
+            written = written | self.write_node(ori, "pitch",
+                                                item.orientation.y,
+                                                {"unit": item.orientation_unit})
+            written = written | self.write_node(ori, "yaw",
+                                                item.orientation.z,
+                                                {"unit": item.orientation_unit})
+            if written == True:
+                self.append(ori, det)
+
+            center = self.create_element("beam_center")
+            written = self.write_node(center, "x", item.beam_center.x,
+                                      {"unit": item.beam_center_unit})
+            written = written | self.write_node(center, "y",
+                                                item.beam_center.y,
+                                                {"unit": item.beam_center_unit})
+            written = written | self.write_node(center, "z",
+                                                item.beam_center.z,
+                                                {"unit": item.beam_center_unit})
+            if written == True:
+                self.append(center, det)
+
+            pix = self.create_element("pixel_size")
+            written = self.write_node(pix, "x", item.pixel_size.x,
+                                      {"unit": item.pixel_size_unit})
+            written = written | self.write_node(pix, "y", item.pixel_size.y,
+                                                {"unit": item.pixel_size_unit})
+            written = written | self.write_node(pix, "z", item.pixel_size.z,
+                                                {"unit": item.pixel_size_unit})
+            if written == True:
+                self.append(pix, det)
+            self.write_node(det, "slit_length", item.slit_length,
+                {"unit": item.slit_length_unit})
+
+    def _write_process_notes(self, datainfo, entry_node):
+        """
+        Writes the process notes to the XML file
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+
+        """
+        for item in datainfo.process:
+            node = self.create_element("SASprocess")
+            self.append(node, entry_node)
+            self.write_node(node, "name", item.name)
+            self.write_node(node, "date", item.date)
+            self.write_node(node, "description", item.description)
+            for term in item.term:
+                if isinstance(term, list):
+                    value = term['value']
+                    del term['value']
+                elif isinstance(term, dict):
+                    value = term.get("value")
+                    del term['value']
+                else:
+                    value = term
+                self.write_node(node, "term", value, term)
+            for note in item.notes:
+                self.write_node(node, "SASprocessnote", note)
+            if len(item.notes) == 0:
+                self.write_node(node, "SASprocessnote", "")
+
+    def _write_notes(self, datainfo, entry_node):
+        """
+        Writes the notes to the XML file and creates an empty note if none
+        exist
+
+        :param datainfo: The Data1D object the information is coming from
+        :param entry_node: lxml node ElementTree object to be appended to
+
+        """
+        if len(datainfo.notes) == 0:
+            node = self.create_element("SASnote")
+            self.append(node, entry_node)
+        else:
+            for item in datainfo.notes:
+                node = self.create_element("SASnote")
+                self.write_text(node, item)
+                self.append(node, entry_node)
+
+    def _check_root(self):
+        """
+        Return the document, and the SASentry node associated with
+        the data we just wrote.
+        If the calling function was not the cansas reader, return a minidom
+        object rather than an lxml object.
+
+        :param entry_node: lxml node ElementTree object to be appended to
+        :param doc: entire xml tree
+        """
+        if not self.frm:
+            self.frm = inspect.stack()[2]
+        mod_name = self.frm[1].replace("\\", "/").replace(".pyc", "")
+        mod_name = mod_name.replace(".py", "")
+        mod = mod_name.split("sas/")
+        mod_name = mod[1]
+        return mod_name == "sascalc/dataloader/readers/cansas_reader"
+
+    def _check_origin(self, entry_node, doc):
+        """
+        Return the document, and the SASentry node associated with
+        the data we just wrote.
+        If the calling function was not the cansas reader, return a minidom
+        object rather than an lxml object.
+
+        :param entry_node: lxml node ElementTree object to be appended to
+        :param doc: entire xml tree
+        """
+        if not self._check_root():
+            string = self.to_string(doc, pretty_print=False)
+            doc = parseString(string)
+            node_name = entry_node.tag
+            node_list = doc.getElementsByTagName(node_name)
+            entry_node = node_list.item(0)
+        return doc, entry_node
+
+    # DO NOT REMOVE - used in saving and loading panel states.
+    def _store_float(self, location, node, variable, storage, optional=True):
+        """
+        Get the content of a xpath location and store
+        the result. Check that the units are compatible
+        with the destination. The value is expected to
+        be a float.
+
+        The xpath location might or might not exist.
+        If it does not exist, nothing is done
+
+        :param location: xpath location to fetch
+        :param node: node to read the data from
+        :param variable: name of the data member to store it in [string]
+        :param storage: data object that has the 'variable' data member
+        :param optional: if True, no exception will be raised
+            if unit conversion can't be done
+
+        :raise ValueError: raised when the units are not recognized
+        """
+        entry = get_content(location, node)
+        try:
+            value = float(entry.text)
+        except:
+            value = None
+
+        if value is not None:
+            # If the entry has units, check to see that they are
+            # compatible with what we currently have in the data object
+            units = entry.get('unit')
+            if units is not None:
+                toks = variable.split('.')
+                local_unit = None
+                exec "local_unit = storage.%s_unit" % toks[0]
+                if local_unit is not None and units.lower() != local_unit.lower():
+                    if HAS_CONVERTER == True:
+                        try:
+                            conv = Converter(units)
+                            exec "storage.%s = %g" % \
+                                (variable, conv(value, units=local_unit))
+                        except:
+                            _, exc_value, _ = sys.exc_info()
+                            err_mess = "CanSAS reader: could not convert"
+                            err_mess += " %s unit [%s]; expecting [%s]\n  %s" \
+                                % (variable, units, local_unit, exc_value)
+                            self.errors.add(err_mess)
+                            if optional:
+                                logger.info(err_mess)
+                            else:
+                                raise ValueError, err_mess
+                    else:
+                        err_mess = "CanSAS reader: unrecognized %s unit [%s];"\
+                        % (variable, units)
+                        err_mess += " expecting [%s]" % local_unit
+                        self.errors.add(err_mess)
+                        if optional:
+                            logger.info(err_mess)
+                        else:
+                            raise ValueError, err_mess
+                else:
+                    exec "storage.%s = value" % variable
+            else:
+                exec "storage.%s = value" % variable
+
+    # DO NOT REMOVE - used in saving and loading panel states.
+    def _store_content(self, location, node, variable, storage):
+        """
+        Get the content of a xpath location and store
+        the result. The value is treated as a string.
+
+        The xpath location might or might not exist.
+        If it does not exist, nothing is done
+
+        :param location: xpath location to fetch
+        :param node: node to read the data from
+        :param variable: name of the data member to store it in [string]
+        :param storage: data object that has the 'variable' data member
+
+        :return: return a list of errors
+        """
+        entry = get_content(location, node)
+        if entry is not None and entry.text is not None:
+            exec "storage.%s = entry.text.strip()" % variable
+
+
+# DO NOT REMOVE Called by outside packages:
+#    sas.sasgui.perspectives.invariant.invariant_state
+#    sas.sasgui.perspectives.fitting.pagestate
+def get_content(location, node):
+    """
+    Get the first instance of the content of a xpath location.
+
+    :param location: xpath location
+    :param node: node to start at
+
+    :return: Element, or None
+    """
+    nodes = node.xpath(location,
+                       namespaces={'ns': CANSAS_NS.get("1.0").get("ns")})
+    if len(nodes) > 0:
+        return nodes[0]
+    else:
+        return None
+
+# DO NOT REMOVE Called by outside packages:
+#    sas.sasgui.perspectives.fitting.pagestate
+def write_node(doc, parent, name, value, attr=None):
+    """
+    :param doc: document DOM
+    :param parent: parent node
+    :param name: tag of the element
+    :param value: value of the child text node
+    :param attr: attribute dictionary
+
+    :return: True if something was appended, otherwise False
+    """
+    if attr is None:
+        attr = {}
+    if value is not None:
+        node = doc.createElement(name)
+        node.appendChild(doc.createTextNode(str(value)))
+        for item in attr:
+            node.setAttribute(item, attr[item])
+        parent.appendChild(node)
+        return True
+    return False
Index: src/sas/sascalc/dataloader/readers/danse_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/danse_reader.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/dataloader/readers/danse_reader.py	(revision 235f514ba93b57fa63e44309f182acef0b98bb55)
@@ -19,4 +19,6 @@
 from sas.sascalc.dataloader.data_info import Data2D, Detector
 from sas.sascalc.dataloader.manipulations import reader2D_converter
+
+logger = logging.getLogger(__name__)
 
 # Look for unit converter
@@ -142,5 +144,5 @@
                             error.append(err)
                         except:
-                            logging.info("Skipping line:%s,%s" %(data_str,
+                            logger.info("Skipping line:%s,%s" %(data_str,
                                                                 sys.exc_value))
             
@@ -164,7 +166,7 @@
                 
                 x_vals.append(qx)
-                if xmin == None or qx < xmin:
+                if xmin is None or qx < xmin:
                     xmin = qx
-                if xmax == None or qx > xmax:
+                if xmax is None or qx > xmax:
                     xmax = qx
             
@@ -179,7 +181,7 @@
                 
                 y_vals.append(qy)
-                if ymin == None or qy < ymin:
+                if ymin is None or qy < ymin:
                     ymin = qy
-                if ymax == None or qy > ymax:
+                if ymax is None or qy > ymax:
                     ymax = qy
             
@@ -196,5 +198,5 @@
                     msg = "Skipping entry (v1.0):%s,%s" % (str(data[i_pt]),
                                                            sys.exc_value)
-                    logging.info(msg)
+                    logger.info(msg)
                 
                 # Get bin number
@@ -271,5 +273,5 @@
                 raise ValueError, msg
             else:
-                logging.info("Danse_reader Reading %s \n" % filename)
+                logger.info("Danse_reader Reading %s \n" % filename)
             
             # Store loading process information
Index: src/sas/sascalc/dataloader/readers/hfir1d_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/hfir1d_reader.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/dataloader/readers/hfir1d_reader.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/dataloader/readers/nexus_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/nexus_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/nexus_reader.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/dataloader/readers/red2d_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/red2d_reader.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/dataloader/readers/red2d_reader.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/dataloader/readers/tiff_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/tiff_reader.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/dataloader/readers/tiff_reader.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
@@ -16,5 +16,7 @@
 from sas.sascalc.dataloader.data_info import Data2D
 from sas.sascalc.dataloader.manipulations import reader2D_converter
-    
+
+logger = logging.getLogger(__name__)
+
 class Reader:
     """
@@ -76,5 +78,5 @@
                 value = float(val)
             except:
-                logging.error("tiff_reader: had to skip a non-float point")
+                logger.error("tiff_reader: had to skip a non-float point")
                 continue
             
Index: src/sas/sascalc/dataloader/readers/xml_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/xml_reader.py	(revision a235f71571822c52f74fd1221fcd542805cf1277)
+++ src/sas/sascalc/dataloader/readers/xml_reader.py	(revision 235f514ba93b57fa63e44309f182acef0b98bb55)
@@ -18,4 +18,6 @@
 from lxml import etree
 from lxml.builder import E
+
+logger = logging.getLogger(__name__)
 
 PARSER = etree.ETCompatXMLParser(remove_comments=True, remove_pis=False)
@@ -71,5 +73,5 @@
             self.xmlroot = self.xmldoc.getroot()
         except etree.XMLSyntaxError as xml_error:
-            logging.info(xml_error)
+            logger.info(xml_error)
         except Exception:
             self.xml = None
@@ -88,5 +90,5 @@
             self.xmlroot = etree.fromstring(tag_soup)
         except etree.XMLSyntaxError as xml_error:
-            logging.info(xml_error)
+            logger.info(xml_error)
         except Exception:
             self.xml = None
@@ -102,5 +104,5 @@
             self.schemadoc = etree.parse(self.schema, parser=PARSER)
         except etree.XMLSyntaxError as xml_error:
-            logging.info(xml_error)
+            logger.info(xml_error)
         except Exception:
             self.schema = None
@@ -238,5 +240,5 @@
         :param name: The name of the element to be created
         """
-        if attrib == None:
+        if attrib is None:
             attrib = {}
         return etree.Element(name, attrib, nsmap)
@@ -297,5 +299,5 @@
         """
         text = str(text)
-        if attrib == None:
+        if attrib is None:
             attrib = {}
         elem = E(elementname, attrib, text)
Index: src/sas/sascalc/file_converter/cansas_writer.py
===================================================================
--- src/sas/sascalc/file_converter/cansas_writer.py	(revision 0b1a677a6bec9b3b30979ce36e64645b3028908b)
+++ src/sas/sascalc/file_converter/cansas_writer.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -17,5 +17,5 @@
         # Write the file
         file_ref = open(filename, 'w')
-        if self.encoding == None:
+        if self.encoding is None:
             self.encoding = "UTF-8"
         doc.write(file_ref, encoding=self.encoding,
@@ -96,14 +96,14 @@
                 self.write_node(point, "I", datainfo.y[i],
                                 {'unit': datainfo.y_unit})
-            if datainfo.dy != None and len(datainfo.dy) > i:
+            if datainfo.dy is not None and len(datainfo.dy) > i:
                 self.write_node(point, "Idev", datainfo.dy[i],
                                 {'unit': datainfo.y_unit})
-            if datainfo.dx != None and len(datainfo.dx) > i:
+            if datainfo.dx is not None and len(datainfo.dx) > i:
                 self.write_node(point, "Qdev", datainfo.dx[i],
                                 {'unit': datainfo.x_unit})
-            if datainfo.dxw != None and len(datainfo.dxw) > i:
+            if datainfo.dxw is not None and len(datainfo.dxw) > i:
                 self.write_node(point, "dQw", datainfo.dxw[i],
                                 {'unit': datainfo.x_unit})
-            if datainfo.dxl != None and len(datainfo.dxl) > i:
+            if datainfo.dxl is not None and len(datainfo.dxl) > i:
                 self.write_node(point, "dQl", datainfo.dxl[i],
                                 {'unit': datainfo.x_unit})
Index: src/sas/sascalc/fit/AbstractFitEngine.py
===================================================================
--- src/sas/sascalc/fit/AbstractFitEngine.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/fit/AbstractFitEngine.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -190,7 +190,7 @@
         if qmin == 0.0 and not np.isfinite(self.y[qmin]):
             self.qmin = min(self.x[self.x != 0])
-        elif qmin != None:
+        elif qmin is not None:
             self.qmin = qmin
-        if qmax != None:
+        if qmax is not None:
             self.qmax = qmax
         # Determine the range needed in unsmeared-Q to cover
@@ -202,5 +202,5 @@
         self._last_unsmeared_bin = len(self.x) - 1
         
-        if self.smearer != None:
+        if self.smearer is not None:
             self._first_unsmeared_bin, self._last_unsmeared_bin = \
                     self.smearer.get_bin_range(self.qmin, self.qmax)
@@ -294,10 +294,10 @@
         
         ## fitting range
-        if qmin == None:
+        if qmin is None:
             self.qmin = 1e-16
-        if qmax == None:
+        if qmax is None:
             self.qmax = math.sqrt(x_max * x_max + y_max * y_max)
         ## new error image for fitting purpose
-        if self.err_data == None or self.err_data == []:
+        if self.err_data is None or self.err_data == []:
             self.res_err_data = np.ones(len(self.data))
         else:
@@ -318,5 +318,5 @@
             Set smearer
         """
-        if smearer == None:
+        if smearer is None:
             return
         self.smearer = smearer
@@ -330,7 +330,7 @@
         if qmin == 0.0:
             self.qmin = 1e-16
-        elif qmin != None:
+        elif qmin is not None:
             self.qmin = qmin
-        if qmax != None:
+        if qmax is not None:
             self.qmax = qmax
         self.radius = np.sqrt(self.qx_data**2 + self.qy_data**2)
@@ -357,5 +357,5 @@
         return the residuals
         """
-        if self.smearer != None:
+        if self.smearer is not None:
             fn.set_index(self.idx)
             gn = fn.get_value()
@@ -612,5 +612,5 @@
         """
         """
-        if self.pvec == None and self.model is None and self.param_list is None:
+        if self.pvec is None and self.model is None and self.param_list is None:
             return "No results"
 
Index: src/sas/sascalc/fit/Loader.py
===================================================================
--- src/sas/sascalc/fit/Loader.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/fit/Loader.py	(revision ac07a3acc20dbf79c564bf642aece71a02c2c862)
@@ -34,5 +34,5 @@
     def set_values(self):
         """ Store the values loaded from file in local variables"""
-        if not self.filename == None:
+        if self.filename is not None:
             input_f =  open(self.filename, 'r')
             buff = input_f.read()
Index: src/sas/sascalc/fit/MultiplicationModel.py
===================================================================
--- src/sas/sascalc/fit/MultiplicationModel.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/fit/MultiplicationModel.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -178,7 +178,7 @@
         """
         value = self.params['volfraction']
-        if value != None:
+        if value is not None:
             factor = self.p_model.calculate_VR()
-            if factor == None or factor == NotImplemented or factor == 0.0:
+            if factor is None or factor == NotImplemented or factor == 0.0:
                 val = value
             else:
@@ -195,5 +195,5 @@
         effective_radius = self.p_model.calculate_ER()
         #Reset the effective_radius of s_model just before the run
-        if effective_radius != None and effective_radius != NotImplemented:
+        if effective_radius is not None and effective_radius != NotImplemented:
             self.s_model.setParam('radius_effective', effective_radius)
 
Index: src/sas/sascalc/invariant/invariant.py
===================================================================
--- src/sas/sascalc/invariant/invariant.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/invariant/invariant.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -329,5 +329,5 @@
 
         ##power is given only for function = power_law
-        if power != None:
+        if power is not None:
             sigma2 = linearized_data.dy * linearized_data.dy
             a = -(power)
@@ -389,5 +389,5 @@
         self._data = self._get_data(data)
         # get the dxl if the data is smeared: This is done only once on init.
-        if self._data.dxl != None and self._data.dxl.all() > 0:
+        if self._data.dxl is not None and self._data.dxl.all() > 0:
             # assumes constant dxl
             self._smeared = self._data.dxl[0]
@@ -579,5 +579,5 @@
 
         result_data = LoaderData1D(x=q, y=iq, dy=diq)
-        if self._smeared != None:
+        if self._smeared is not None:
             result_data.dxl = self._smeared * np.ones(len(q))
         return result_data
Index: src/sas/sascalc/invariant/invariant_mapper.py
===================================================================
--- src/sas/sascalc/invariant/invariant_mapper.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/invariant/invariant_mapper.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/pr/__init__.py
===================================================================
--- src/sas/sascalc/pr/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/__init__.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/pr/c_extensions/Cinvertor.c
===================================================================
--- src/sas/sascalc/pr/c_extensions/Cinvertor.c	(revision b523c0e4093c0c8e82cebd10672473d88adeaee9)
+++ src/sas/sascalc/pr/c_extensions/Cinvertor.c	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/pr/c_extensions/__init__.py
===================================================================
--- src/sas/sascalc/pr/c_extensions/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/c_extensions/__init__.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/pr/c_extensions/invertor.c
===================================================================
--- src/sas/sascalc/pr/c_extensions/invertor.c	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/c_extensions/invertor.c	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/pr/c_extensions/invertor.h
===================================================================
--- src/sas/sascalc/pr/c_extensions/invertor.h	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/c_extensions/invertor.h	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/pr/distance_explorer.py
===================================================================
--- src/sas/sascalc/pr/distance_explorer.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/distance_explorer.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/pr/fit/AbstractFitEngine.py
===================================================================
--- src/sas/sascalc/pr/fit/AbstractFitEngine.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/pr/fit/AbstractFitEngine.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -190,7 +190,7 @@
         if qmin == 0.0 and not np.isfinite(self.y[qmin]):
             self.qmin = min(self.x[self.x != 0])
-        elif qmin != None:
+        elif qmin is not None:
             self.qmin = qmin
-        if qmax != None:
+        if qmax is not None:
             self.qmax = qmax
         # Determine the range needed in unsmeared-Q to cover
@@ -202,5 +202,5 @@
         self._last_unsmeared_bin = len(self.x) - 1
         
-        if self.smearer != None:
+        if self.smearer is not None:
             self._first_unsmeared_bin, self._last_unsmeared_bin = \
                     self.smearer.get_bin_range(self.qmin, self.qmax)
@@ -294,10 +294,10 @@
         
         ## fitting range
-        if qmin == None:
+        if qmin is None:
             self.qmin = 1e-16
-        if qmax == None:
+        if qmax is None:
             self.qmax = math.sqrt(x_max * x_max + y_max * y_max)
         ## new error image for fitting purpose
-        if self.err_data == None or self.err_data == []:
+        if self.err_data is None or self.err_data == []:
             self.res_err_data = np.ones(len(self.data))
         else:
@@ -318,5 +318,5 @@
             Set smearer
         """
-        if smearer == None:
+        if smearer is None:
             return
         self.smearer = smearer
@@ -330,7 +330,7 @@
         if qmin == 0.0:
             self.qmin = 1e-16
-        elif qmin != None:
+        elif qmin is not None:
             self.qmin = qmin
-        if qmax != None:
+        if qmax is not None:
             self.qmax = qmax
         self.radius = np.sqrt(self.qx_data**2 + self.qy_data**2)
@@ -357,5 +357,5 @@
         return the residuals
         """
-        if self.smearer != None:
+        if self.smearer is not None:
             fn.set_index(self.idx)
             # Get necessary data from self.data and set the data for smearing
@@ -615,5 +615,5 @@
         """
         """
-        if self.pvec == None and self.model is None and self.param_list is None:
+        if self.pvec is None and self.model is None and self.param_list is None:
             return "No results"
 
Index: src/sas/sascalc/pr/fit/Loader.py
===================================================================
--- src/sas/sascalc/pr/fit/Loader.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/pr/fit/Loader.py	(revision ac07a3acc20dbf79c564bf642aece71a02c2c862)
@@ -34,5 +34,5 @@
     def set_values(self):
         """ Store the values loaded from file in local variables"""
-        if not self.filename == None:
+        if self.filename is not None:
             input_f =  open(self.filename, 'r')
             buff = input_f.read()
Index: src/sas/sascalc/pr/invertor.py
===================================================================
--- src/sas/sascalc/pr/invertor.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/pr/invertor.py	(revision 45dffa69cb5935e2da7c77a2f040debb07ceb1c6)
@@ -18,4 +18,6 @@
 from scipy import optimize
 from sas.sascalc.pr.core.pr_inversion import Cinvertor
+
+logger = logging.getLogger(__name__)
 
 def help():
@@ -160,9 +162,9 @@
             return self.set_dmax(value)
         elif name == 'q_min':
-            if value == None:
+            if value is None:
                 return self.set_qmin(-1.0)
             return self.set_qmin(value)
         elif name == 'q_max':
-            if value == None:
+            if value is None:
                 return self.set_qmax(-1.0)
             return self.set_qmax(value)
@@ -393,7 +395,7 @@
         Check q-value against user-defined range
         """
-        if not self.q_min == None and q < self.q_min:
+        if self.q_min is not None and q < self.q_min:
             return False
-        if not self.q_max == None and q > self.q_max:
+        if self.q_max is not None and q > self.q_max:
             return False
         return True
@@ -495,5 +497,5 @@
             # We were not able to estimate the errors
             # Return an empty error matrix
-            logging.error(sys.exc_value)
+            logger.error(sys.exc_value)
 
         # Keep a copy of the last output
@@ -541,5 +543,5 @@
             # number of terms
             best_alpha, _, _ = self.estimate_alpha(self.nfunc)
-            logging.warning("Invertor.estimate_numterms: %s" % sys.exc_value)
+            logger.warning("Invertor.estimate_numterms: %s" % sys.exc_value)
             return self.nfunc, best_alpha, "Could not estimate number of terms"
 
@@ -656,5 +658,5 @@
             file.write("#has_bck=0\n")
         file.write("#alpha_estimate=%g\n" % self.suggested_alpha)
-        if not self.out == None:
+        if self.out is not None:
             if len(self.out) == len(self.cov):
                 for i in range(len(self.out)):
Index: src/sas/sascalc/pr/num_term.py
===================================================================
--- src/sas/sascalc/pr/num_term.py	(revision 9a5097cd1c76689d1f997b85ba43d12a95d8f243)
+++ src/sas/sascalc/pr/num_term.py	(revision 7432acb919f4013119ca918a73350297e0d4c93f)
@@ -5,4 +5,6 @@
 import logging
 from sas.sascalc.pr.invertor import Invertor
+
+logger = logging.getLogger(__name__)
 
 class NTermEstimator(object):
@@ -65,5 +67,5 @@
         self.alpha_list = []
         for k in range(self.nterm_min, self.nterm_max, 1):
-            if self.isquit_func != None:
+            if self.isquit_func is not None:
                 self.isquit_func()
             best_alpha, message, _ = inver.estimate_alpha(k)
@@ -157,5 +159,5 @@
     scale = None
     min_err = 0.0
-    if not path == None:
+    if path is not None:
         input_f = open(path, 'r')
         buff = input_f.read()
@@ -169,5 +171,5 @@
                     err = float(toks[2])
                 else:
-                    if scale == None:
+                    if scale is None:
                         scale = 0.05 * math.sqrt(test_y)
                         #scale = 0.05/math.sqrt(y)
@@ -180,5 +182,5 @@
                 data_err = np.append(data_err, err)
             except:
-                logging.error(sys.exc_value)
+                logger.error(sys.exc_value)
 
     return data_x, data_y, data_err
Index: src/sas/sascalc/realspace/VolumeCanvas.py
===================================================================
--- src/sas/sascalc/realspace/VolumeCanvas.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sascalc/realspace/VolumeCanvas.py	(revision 235f514ba93b57fa63e44309f182acef0b98bb55)
@@ -293,5 +293,5 @@
         """
         # If the handle is not provided, create one
-        if id == None:
+        if id is None:
             id = shapeDesc.params["type"]+str(self.shapecount)
          
@@ -303,5 +303,5 @@
         self.shapecount += 1
 
-        #model changed, need to recalculate P(r)
+        # model changed, need to recalculate P(r)
         self._model_changed()
 
@@ -325,8 +325,8 @@
         """
         # If the handle is not provided, create one
-        if id == None:
+        if id is None:
             id = "shape"+str(self.shapecount)
  
-        #shapeDesc = ShapeDescriptor(shape.lower())
+        # shapeDesc = ShapeDescriptor(shape.lower())
         if shape.lower() in shape_dict:
             shapeDesc = shape_dict[shape.lower()]()
@@ -335,5 +335,5 @@
             shapeDesc = PDBDescriptor(shape)
         else:
-            raise ValueError, "VolumeCanvas.add: Unknown shape %s" % shape
+            raise ValueError("VolumeCanvas.add: Unknown shape %s" % shape)
         
         return self.addObject(shapeDesc, id)
@@ -345,10 +345,10 @@
         """
 
-        if self.shapes.has_key(id):
+        if id in self.shapes:
             del self.shapes[id]
         else:
-            raise KeyError, "VolumeCanvas.delete: could not find shape ID"
-
-        #model changed, need to recalculate P(r)
+            raise KeyError("VolumeCanvas.delete: could not find shape ID")
+
+        # model changed, need to recalculate P(r)
         self._model_changed()
 
@@ -379,6 +379,6 @@
         # If a shape identifier was given, look the shape up
         # in the dictionary
-        if len(toks)>1:
-            if toks[0] in self.shapes.keys():
+        if len(toks):
+            if toks[0] in self.shapes:
                 # The shape was found, now look for the parameter
                 if toks[1] in self.shapes[toks[0]].params:
@@ -387,7 +387,7 @@
                     self._model_changed()
                 else:
-                    raise ValueError, "Could not find parameter %s" % name
+                    raise ValueError("Could not find parameter %s" % name)
             else:
-                raise ValueError, "Could not find shape %s" % toks[0]
+                raise ValueError("Could not find shape %s" % toks[0])
         
         else:
@@ -410,13 +410,11 @@
         if len(toks) == 1:
             try:
-                self.params.has_key(toks[0])
+                value = self.params[toks[0]]
             except KeyError:
-                raise ValueError, \
-                    "VolumeCanvas.getParam: Could not find %s" % name
-
-            value = self.params[toks[0]]
+                raise ValueError("VolumeCanvas.getParam: Could not find"
+                                 " %s" % name)
             if isinstance(value, ShapeDescriptor):
-                raise ValueError, \
-                    "VolumeCanvas.getParam: Cannot get parameter value." 
+                raise ValueError("VolumeCanvas.getParam: Cannot get parameter"
+                                 " value.")
             else:
                 return value
@@ -424,24 +422,19 @@
         elif len(toks) == 2:
             try:
-                self.shapes.has_key(toks[0])
+                shapeinstance = self.shapes[toks[0]]
             except KeyError:
-                raise ValueError, \
-                    "VolumeCanvas.getParam: Could not find %s" % name
-
-            shapeinstance = self.shapes[toks[0]]
-
-            try:
-                shapeinstance.params.has_key(toks[1])
-            except KeyError:
-                raise ValueError, \
-                    "VolumeCanvas.getParam: Could not find %s" % name
+                raise ValueError("VolumeCanvas.getParam: Could not find "
+                                 "%s" % name)
+
+            if not toks[1] in shapeinstance.params:
+                raise ValueError("VolumeCanvas.getParam: Could not find "
+                                 "%s" % name)
 
             return shapeinstance.params[toks[1]]
 
         else:
-            raise ValueError, \
-                "VolumeCanvas.getParam: Could not find %s" % name
-            
-    def getParamList(self, shapeid = None):
+            raise ValueError("VolumeCanvas.getParam: Could not find %s" % name)
+            
+    def getParamList(self, shapeid=None):
         """
 	       return a full list of all available parameters from 
@@ -455,26 +448,22 @@
 
         param_list = []
-        if shapeid == None:        
-            for key1 in self.params.keys():
+        if shapeid is None:
+            for key1 in self.params:
                 #value1 = self.params[key1]
                 param_list.append(key1)
-            for key2 in self.shapes.keys():
+            for key2 in self.shapes:
                 value2 = self.shapes[key2]
                 header = key2 + '.'
-                for key3 in value2.params.keys():   
+                for key3 in value2.params:
                     fullname = header + key3                 
                     param_list.append(fullname)
      
         else:
-            try:
-                self.shapes.has_key(shapeid)
-            except KeyError:
-                raise ValueError, \
-                    "VolumeCanvas: getParamList: Could not find %s" % shapeid
+            if not shapeid in self.shapes:
+                raise ValueError("VolumeCanvas: getParamList: Could not find "
+                                 "%s" % shapeid)
+
             header = shapeid + '.'
-            param_list = self.shapes[shapeid].params.keys()  
-            for i in range(len(param_list)):
-                param_list[i] = header + param_list[i]
-
+            param_list = [header + param for param in self.shapes[shapeid].params]
         return param_list
 
@@ -490,5 +479,5 @@
             @param shapeDesc: shape description
         """
-        #Create the object model
+        # Create the object model
         shapeDesc.create()
                     
@@ -605,5 +594,5 @@
         # type we recognize
         else:
-            raise ValueError, "run(q): bad type for q"
+            raise ValueError("run(q): bad type for q")
     
     def runXY(self, q = 0):
@@ -625,5 +614,5 @@
         # type we recognize
         else:
-            raise ValueError, "runXY(q): bad type for q"
+            raise ValueError("runXY(q): bad type for q")
     
     def _create_modelObject(self):
@@ -674,9 +663,9 @@
         # If this is the first simulation call, we need to generate the
         # space points
-        if self.points == None:
+        if self.points is None:
             self._create_modelObject()
             
             # Protect against empty model
-            if self.points == None:
+            if self.points is None:
                 return 0
                
Index: src/sas/sascalc/realspace/__init__.py
===================================================================
--- src/sas/sascalc/realspace/__init__.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sascalc/realspace/__init__.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/simulation/geoshapespy/libgeoshapespy/minmax.h
===================================================================
--- src/sas/sascalc/simulation/geoshapespy/libgeoshapespy/minmax.h	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sascalc/simulation/geoshapespy/libgeoshapespy/minmax.h	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/simulation/pointsmodelpy/tests/test2dui.py
===================================================================
--- src/sas/sascalc/simulation/pointsmodelpy/tests/test2dui.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sascalc/simulation/pointsmodelpy/tests/test2dui.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/simulation/pointsmodelpy/tests/testcomplexmodel.py
===================================================================
--- src/sas/sascalc/simulation/pointsmodelpy/tests/testcomplexmodel.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sascalc/simulation/pointsmodelpy/tests/testcomplexmodel.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/simulation/pointsmodelpy/tests/testlores.py
===================================================================
--- src/sas/sascalc/simulation/pointsmodelpy/tests/testlores.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sascalc/simulation/pointsmodelpy/tests/testlores.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/simulation/pointsmodelpy/tests/testlores2d.py
===================================================================
--- src/sas/sascalc/simulation/pointsmodelpy/tests/testlores2d.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sascalc/simulation/pointsmodelpy/tests/testlores2d.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
Index: src/sas/sascalc/simulation/pointsmodelpy/tests/testnegativepr.py
===================================================================
--- src/sas/sascalc/simulation/pointsmodelpy/tests/testnegativepr.py	(revision d85c194ea2f78cb2b9b3de9c969e1b8ebea6b6d2)
+++ src/sas/sascalc/simulation/pointsmodelpy/tests/testnegativepr.py	(revision 959eb0181ad06828a748f04512f953aad5b31659)
