Index: docs/sphinx-docs/source/conf.py
===================================================================
--- docs/sphinx-docs/source/conf.py	(revision 792e6be8e13f67657de88e5fb9a9afb91a750c30)
+++ docs/sphinx-docs/source/conf.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -82,5 +82,5 @@
 # running Sphinx.
 exclude_patterns = ["*sas.perspectives.simulation.rst",
-                    "*sas.pr.c_extensions.rst",
+                    "*sas.sascalc.pr.c_extensions.rst",
                     "*sas.realspace.rst",
                     "*sas.simulation.rst",
Index: run.py
===================================================================
--- run.py	(revision f9393dce019d0fabc6363eb2942e8fbc5fbb6424)
+++ run.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -114,7 +114,7 @@
     # Compiled modules need to be pulled from the build directory.
     # Some packages are not where they are needed, so load them explicitly.
-    import sas.pr
-    sas.pr.core = import_package('sas.pr.core',
-                                  joinpath(build_path, 'sas', 'pr', 'core'))
+    import sas.sascalc.pr
+    sas.sascalc.pr.core = import_package('sas.sascalc.pr.core',
+                                  joinpath(build_path, 'sas', 'sascalc', 'pr', 'core'))
 
 
Index: sasview/installer_generator.py
===================================================================
--- sasview/installer_generator.py	(revision 3a39c2e89f90377b2d15fa13b9ba9cf3b4144070)
+++ sasview/installer_generator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -45,5 +45,5 @@
         
         #(ext, type, name, flags)
-        from sas.dataloader.loader import Loader
+        from sas.sascalc.dataloader.loader import Loader
         wild_cards = Loader().get_wildcards()
         for item in wild_cards:
Index: sasview/sasview.py
===================================================================
--- sasview/sasview.py	(revision 85130cb3582eef4c433e40ac08435fe6040abbf1)
+++ sasview/sasview.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -137,5 +137,5 @@
             invariant_plug = module.Plugin()
             self.gui.add_perspective(invariant_plug)
-        except:
+        except Exception as e :
             logging.error("%s: could not find Invariant plug-in module"% \
                           APP_NAME)
Index: sasview/setup_exe.py
===================================================================
--- sasview/setup_exe.py	(revision 24d3a40a3b3e8227589849da0ca7df6eaacf3fd9)
+++ sasview/setup_exe.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -264,5 +264,5 @@
 
 # Copy the settings file for the sas.dataloader file extension associations
-import sas.dataloader.readers
+import sas.sascalc.dataloader.readers
 f = os.path.join(sas.dataloader.readers.get_data_path(), 'defaults.json')
 if os.path.isfile(f):
Index: sasview/setup_mac.py
===================================================================
--- sasview/setup_mac.py	(revision e8e3e38e06438ded9098922accbc5b4405395a39)
+++ sasview/setup_mac.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -13,5 +13,5 @@
 from setuptools import setup
 import periodictable.xsf
-import sas.dataloader.readers 
+import sas.sascalc.dataloader.readers 
 import os
 import string
@@ -83,5 +83,5 @@
         list = []
         EXCEPTION_LIST = ['*', '.', '']
-        from sas.dataloader.loader import Loader
+        from sas.sascalc.dataloader.loader import Loader
         wild_cards = Loader().get_wildcards()
         for item in wild_cards:
Index: setup.py
===================================================================
--- setup.py	(revision 038ccfe6ad2eb536e4120eb8a5bcd1c584ed0260)
+++ setup.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -165,5 +165,5 @@
 
 # sas.invariant
-package_dir["sas.invariant"] = os.path.join("src", "sas", "invariant")
+package_dir["sas.invariant"] = os.path.join("src", "sas", "sascalc", "invariant")
 packages.extend(["sas.invariant"])
 
@@ -182,19 +182,19 @@
 
 # sas.dataloader
-package_dir["sas.dataloader"] = os.path.join("src", "sas", "dataloader")
+package_dir["sas.dataloader"] = os.path.join("src", "sas", "sascalc", "dataloader")
 package_data["sas.dataloader.readers"] = ['defaults.json','schema/*.xsd']
 packages.extend(["sas.dataloader","sas.dataloader.readers","sas.dataloader.readers.schema"])
 
 # sas.calculator
-package_dir["sas.calculator"] =os.path.join("src", "sas", "calculator")
-packages.extend(["sas.calculator"])
+package_dir["sas.calculator"] =os.path.join("src", "sas", "sascalc", "calculator")
+packages.extend(["sas.sascalc.calculator"])
     
-# sas.pr
+# sas.sascalc.pr
 numpy_incl_path = os.path.join(NUMPY_INC, "numpy")
-srcdir  = os.path.join("src", "sas", "pr", "c_extensions")
-package_dir["sas.pr.core"] = srcdir
-package_dir["sas.pr"] = os.path.join("src","sas", "pr")
-packages.extend(["sas.pr","sas.pr.core"])
-ext_modules.append( Extension("sas.pr.core.pr_inversion",
+srcdir  = os.path.join("src", "sas", "sascalc", "pr", "c_extensions")
+package_dir["sas.sascalc.pr.core"] = srcdir
+package_dir["sas.sascalc.pr"] = os.path.join("src","sas", "sascalc", "pr")
+packages.extend(["sas.sascalc.pr","sas.sascalc.pr.core"])
+ext_modules.append( Extension("sas.sascalc.pr.core.pr_inversion",
                               sources = [os.path.join(srcdir, "Cinvertor.c"),
                                          os.path.join(srcdir, "invertor.c"),
@@ -203,7 +203,7 @@
                               ) )
         
-# sas.fit
-package_dir["sas.fit"] = os.path.join("src", "sas", "fit")
-packages.append("sas.fit")
+# sas.sascalc.fit
+package_dir["sas.sascalc.fit"] = os.path.join("src", "sas", "sascalc", "fit")
+packages.append("sas.sascalc.fit")
 
 # Perspectives
@@ -227,6 +227,6 @@
     
 # Data util
-package_dir["data_util"] = os.path.join("src", "sas", "data_util")
-packages.append("sas.data_util")
+package_dir["data_util"] = os.path.join("src", "sas", "sascalc", "data_util")
+packages.append("sas.sascalc.data_util")
 
 # Plottools
Index: src/examples/test_panel2D.py
===================================================================
--- src/examples/test_panel2D.py	(revision 3a39c2e89f90377b2d15fa13b9ba9cf3b4144070)
+++ src/examples/test_panel2D.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -200,5 +200,5 @@
             path = dlg.GetPath()
             mypath = os.path.basename(path)
-            from sas.dataloader.loader import  Loader
+            from sas.sascalc.dataloader.loader import  Loader
            
             #Instantiate a loader 
Index: c/sas/calculator/instrument.py
===================================================================
--- src/sas/calculator/instrument.py	(revision b6627d95d435a7f25f17862e4ac78ae26bdb6393)
+++ 	(revision )
@@ -1,390 +1,0 @@
-"""
-This module is a small tool to allow user to
-control instrumental parameters
-"""
-import numpy
-
-# defaults in cgs unit
-_SAMPLE_A_SIZE = [1.27]
-_SOURCE_A_SIZE = [3.81]
-_SAMPLE_DISTANCE = [1627, 0]
-_SAMPLE_OFFSET = [0, 0]
-_SAMPLE_SIZE = [2.54]
-_SAMPLE_THICKNESS = 0.2
-_D_DISTANCE = [1000, 0]
-_D_SIZE = [128, 128]
-_D_PIX_SIZE = [0.5, 0.5]
-
-_MIN = 0.0
-_MAX = 50.0
-_INTENSITY = 368428
-_WAVE_LENGTH = 6.0
-_WAVE_SPREAD = 0.125
-_MASS = 1.67492729E-24  # [gr]
-_LAMBDA_ARRAY = [[0, 1e+16], [_INTENSITY, _INTENSITY]]
-
-
-class Aperture(object):
-    """
-    An object class that defines the aperture variables
-    """
-    def __init__(self):
-
-        # assumes that all aligned at the centers
-        # aperture_size [diameter] for pinhole, [dx, dy] for rectangular
-        self.sample_size = _SAMPLE_A_SIZE
-        self.source_size = _SOURCE_A_SIZE
-        self.sample_distance = _SAMPLE_DISTANCE
-
-    def set_source_size(self, size=[]):
-        """
-        Set the source aperture size
-        """
-        if len(size) == 0:
-            self.source_size = 0.0
-        else:
-            self.source_size = size
-            validate(size[0])
-
-    def set_sample_size(self, size=[]):
-        """
-        Set the sample aperture size
-        """
-        if len(size) == 0:
-            self.sample_size = 0.0
-        else:
-            self.sample_size = size
-            validate(size[0])
-
-    def set_sample_distance(self, distance=[]):
-        """
-        Set the sample aperture distance
-        """
-        if len(distance) == 0:
-            self.sample_distance = 0.0
-        else:
-            self.sample_distance = distance
-            validate(distance[0])
-
-
-class Sample(object):
-    """
-    An object class that defines the sample variables
-    """
-    def __init__(self):
-
-        # assumes that all aligned at the centers
-        # source2sample or sample2detector distance
-        self.distance = _SAMPLE_OFFSET
-        self.size = _SAMPLE_SIZE
-        self.thickness = _SAMPLE_THICKNESS
-
-    def set_size(self, size=[]):
-        """
-        Set the sample size
-        """
-        if len(size) == 0:
-            self.size = 0.0
-        else:
-            self.size = size
-            validate(size[0])
-
-    def set_thickness(self, thickness=0.0):
-        """
-        Set the sample thickness
-        """
-        self.thickness = thickness
-        validate(thickness)
-
-    def set_distance(self, distance=[]):
-        """
-        Set the sample distance
-        """
-        if len(distance) == 0:
-            self.distance = 0.0
-        else:
-            self.distance = distance
-            if distance[0] != 0.0:
-                validate(distance[0])
-
-
-class Detector(object):
-    """
-    An object class that defines the detector variables
-    """
-    def __init__(self):
-
-        # assumes that all aligned at the centers
-        # source2sample or sample2detector distance
-        self.distance = _D_DISTANCE
-        self.size = _D_SIZE
-        self.pix_size = _D_PIX_SIZE
-
-    def set_size(self, size=[]):
-        """
-        Set the detector  size
-        """
-        if len(size) == 0:
-            self.size = 0
-        else:
-            self.size = size
-            validate(size[0])
-
-    def set_pix_size(self, size=[]):
-        """
-        Set the detector pix_size
-        """
-        if len(size) == 0:
-            self.pix_size = 0
-        else:
-            self.pix_size = size
-            validate(size[0])
-
-    def set_distance(self, distance=[]):
-        """
-        Set the detector distance
-        """
-        if len(distance) == 0:
-            self.distance = 0
-        else:
-            self.distance = distance
-            validate(distance[0])
-
-
-class Neutron(object):
-    """
-    An object that defines the wavelength variables
-    """
-    def __init__(self):
-
-        # neutron mass in cgs unit
-        self.mass = _MASS
-
-        # wavelength
-        self.wavelength = _WAVE_LENGTH
-        # wavelength spread (FWHM)
-        self.wavelength_spread = _WAVE_SPREAD
-        # wavelength spectrum
-        self.spectrum = self.get_default_spectrum()
-        # intensity in counts/sec
-        self.intensity = numpy.interp(self.wavelength,
-                                      self.spectrum[0],
-                                      self.spectrum[1],
-                                      0.0,
-                                      0.0)
-        # min max range of the spectrum
-        self.min = min(self.spectrum[0])
-        self.max = max(self.spectrum[0])
-        # wavelength band
-        self.band = [self.min, self.max]
-
-        # default unit of the thickness
-        self.wavelength_unit = 'A'
-
-    def set_full_band(self):
-        """
-        set band to default value
-        """
-        self.band = self.spectrum
-
-    def set_spectrum(self, spectrum):
-        """
-        Set spectrum
-
-        :param spectrum: numpy array
-        """
-        self.spectrum = spectrum
-        self.setup_spectrum()
-
-    def setup_spectrum(self):
-        """
-        To set the wavelength spectrum, and intensity, assumes
-        wavelength is already within the spectrum
-        """
-        spectrum = self.spectrum
-        intensity = numpy.interp(self.wavelength,
-                                 spectrum[0],
-                                 spectrum[1],
-                                 0.0,
-                                 0.0)
-        self.set_intensity(intensity)
-        # min max range of the spectrum
-        self.min = min(self.spectrum[0])
-        self.max = max(self.spectrum[0])
-        # set default band
-        self.set_band([self.min, self.max])
-
-    def set_band(self, band=[]):
-        """
-        To set the wavelength band
-
-        :param band: array of [min, max]
-        """
-        # check if the wavelength is in range
-        if min(band) < self.min or\
-                max(band) > self.max:
-            raise
-        self.band = band
-
-    def set_intensity(self, intensity=368428):
-        """
-        Sets the intensity in counts/sec
-        """
-        self.intensity = intensity
-        validate(intensity)
-
-    def set_wavelength(self, wavelength=_WAVE_LENGTH):
-        """
-        Sets the wavelength
-        """
-        # check if the wavelength is in range
-        if wavelength < min(self.band) or\
-                wavelength > max(self.band):
-            raise
-        self.wavelength = wavelength
-        validate(wavelength)
-        self.intensity = numpy.interp(self.wavelength,
-                                      self.spectrum[0],
-                                      self.spectrum[1],
-                                      0.0,
-                                      0.0)
-
-    def set_mass(self, mass=_MASS):
-        """
-        Sets the wavelength
-        """
-        self.mass = mass
-        validate(mass)
-
-    def set_wavelength_spread(self, spread=_WAVE_SPREAD):
-        """
-        Sets the wavelength spread
-        """
-        self.wavelength_spread = spread
-        if spread != 0.0:
-            validate(spread)
-
-    def get_intensity(self):
-        """
-        To get the value of intensity
-        """
-        return self.intensity
-
-    def get_wavelength(self):
-        """
-        To get the value of wavelength
-        """
-        return self.wavelength
-
-    def get_mass(self):
-        """
-        To get the neutron mass
-        """
-        return self.mass
-
-    def get_wavelength_spread(self):
-        """
-        To get the value of wavelength spread
-        """
-        return self.wavelength_spread
-
-    def get_ramdom_value(self):
-        """
-        To get the value of wave length
-        """
-        return self.wavelength
-
-    def get_spectrum(self):
-        """
-        To get the wavelength spectrum
-        """
-        return self.spectrum
-
-    def get_default_spectrum(self):
-        """
-        get default spectrum
-        """
-        return numpy.array(_LAMBDA_ARRAY)
-
-    def get_band(self):
-        """
-        To get the wavelength band
-        """
-        return self.band
-
-    def plot_spectrum(self):
-        """
-        To plot the wavelength spactrum
-        : requirment: matplotlib.pyplot
-        """
-        try:
-            import matplotlib.pyplot as plt
-            plt.plot(self.spectrum[0], self.spectrum[1], linewidth=2, color='r')
-            plt.legend(['Spectrum'], loc='best')
-            plt.show()
-        except:
-            raise RuntimeError, "Can't import matplotlib required to plot..."
-
-
-class TOF(Neutron):
-    """
-    TOF: make list of wavelength and wave length spreads
-    """
-    def __init__(self):
-        """
-        Init
-        """
-        Neutron.__init__(self)
-        #self.switch = switch
-        self.wavelength_list = [self.wavelength]
-        self.wavelength_spread_list = [self.wavelength_spread]
-        self.intensity_list = self.get_intensity_list()
-
-    def get_intensity_list(self):
-        """
-        get list of the intensity wrt wavelength_list
-        """
-        out = numpy.interp(self.wavelength_list,
-                           self.spectrum[0],
-                           self.spectrum[1],
-                           0.0,
-                           0.0)
-        return out
-
-    def get_wave_list(self):
-        """
-        Get wavelength and wavelength_spread list
-        """
-        return self.wavelength_list, self.wavelength_spread_list
-
-    def set_wave_list(self, wavelength=[]):
-        """
-        Set wavelength list
-
-        :param wavelength: list of wavelengths
-        """
-        self.wavelength_list = wavelength
-
-    def set_wave_spread_list(self, wavelength_spread=[]):
-        """
-        Set wavelength_spread list
-
-        :param wavelength_spread: list of wavelength spreads
-        """
-        self.wavelength_spread_list = wavelength_spread
-
-
-def validate(value=None):
-    """
-    Check if the value is folat > 0.0
-
-    :return value: True / False
-    """
-    try:
-        val = float(value)
-        if val >= 0:
-            val = True
-        else:
-            val = False
-    except:
-        val = False
Index: c/sas/calculator/kiessig_calculator.py
===================================================================
--- src/sas/calculator/kiessig_calculator.py	(revision 6bd3a8d1ff8ae5d31daee4cf6f10dfaeb0d72131)
+++ 	(revision )
@@ -1,64 +1,0 @@
-"""
-This module is a small tool to allow user to quickly
-determine the size value in real space  from the
-fringe width in q space.
-"""
-from math import pi, fabs
-_DQ_DEFAULT = 0.05
-
-
-class KiessigThicknessCalculator(object):
-    """
-    compute thickness from the fringe width of data
-    """
-    def __init__(self):
-
-        # dq value
-        self.deltaq = _DQ_DEFAULT
-        # thickenss value
-        self.thickness = None
-        # unit of the thickness
-        self.thickness_unit = 'A'
-
-    def set_deltaq(self, dq=None):
-        """
-        Receive deltaQ value
-
-        :param dq: q fringe width in 1/A unit
-        """
-        # set dq
-        self.deltaq = dq
-
-    def get_deltaq(self):
-        """
-        return deltaQ value in 1/A unit
-        """
-        # return dq
-        return self.deltaq
-
-    def compute_thickness(self):
-        """
-        Calculate thickness.
-
-        :return: the thickness.
-        """
-        # check if it is float
-        try:
-            dq = float(self.deltaq)
-        except:
-            return None
-        # check if delta_q is zero
-        if dq == 0.0 or dq == None:
-            return None
-        else:
-            # calculate thickness
-            thickness = 2*pi/fabs(dq)
-            # return thickness value
-            return thickness
-
-    def get_thickness_unit(self):
-        """
-        :return: the thickness unit.
-        """
-        # unit of thickness
-        return self.thickness_unit
Index: c/sas/calculator/resolution_calculator.py
===================================================================
--- src/sas/calculator/resolution_calculator.py	(revision b6627d95d435a7f25f17862e4ac78ae26bdb6393)
+++ 	(revision )
@@ -1,1167 +1,0 @@
-"""
-This object is a small tool to allow user to quickly
-determine the variance in q  from the
-instrumental parameters.
-"""
-from instrument import Sample
-from instrument import Detector
-from instrument import TOF as Neutron
-from instrument import Aperture
-# import math stuffs
-from math import pi
-from math import sqrt
-import math
-import numpy
-import sys
-import logging
-
-#Plank's constant in cgs unit
-_PLANK_H = 6.62606896E-27
-#Gravitational acc. in cgs unit
-_GRAVITY = 981.0
-
-
-class ResolutionCalculator(object):
-    """
-    compute resolution in 2D
-    """
-    def __init__(self):
-
-        # wavelength
-        self.wave = Neutron()
-        # sample
-        self.sample = Sample()
-        # aperture
-        self.aperture = Aperture()
-        # detector
-        self.detector = Detector()
-        # 2d image of the resolution
-        self.image = []
-        self.image_lam = []
-        # resolutions
-        # lamda in r-direction
-        self.sigma_lamd = 0
-        # x-dir (no lamda)
-        self.sigma_1 = 0
-        #y-dir (no lamda)
-        self.sigma_2 = 0
-        # 1D total
-        self.sigma_1d = 0
-        self.gravity_phi = None
-        # q min and max
-        self.qx_min = -0.3
-        self.qx_max = 0.3
-        self.qy_min = -0.3
-        self.qy_max = 0.3
-        # q min and max of the detector
-        self.detector_qx_min = -0.3
-        self.detector_qx_max = 0.3
-        self.detector_qy_min = -0.3
-        self.detector_qy_max = 0.3
-        # possible max qrange
-        self.qxmin_limit = 0
-        self.qxmax_limit = 0
-        self.qymin_limit = 0
-        self.qymax_limit = 0
-
-        # plots
-        self.plot = None
-        # instrumental params defaults
-        self.mass = 0
-        self.intensity = 0
-        self.wavelength = 0
-        self.wavelength_spread = 0
-        self.source_aperture_size = []
-        self.source2sample_distance = []
-        self.sample2sample_distance = []
-        self.sample_aperture_size = []
-        self.sample2detector_distance = []
-        self.detector_pix_size = []
-        self.detector_size = []
-        self.get_all_instrument_params()
-        # max q range for all lambdas
-        self.qxrange = []
-        self.qyrange = []
-
-    def compute_and_plot(self, qx_value, qy_value, qx_min, qx_max,
-                         qy_min, qy_max, coord='cartesian'):
-        """
-        Compute the resolution
-        : qx_value: x component of q
-        : qy_value: y component of q
-        """
-        # make sure to update all the variables need.
-        # except lambda, dlambda, and intensity
-        self.get_all_instrument_params()
-        # wavelength etc.
-        lamda_list, dlamb_list = self.get_wave_list()
-        intens_list = []
-        sig1_list = []
-        sig2_list = []
-        sigr_list = []
-        sigma1d_list = []
-        num_lamda = len(lamda_list)
-        for num in range(num_lamda):
-            lam = lamda_list[num]
-            # wavelength spread
-            dlam = dlamb_list[num]
-            intens = self.setup_tof(lam, dlam)
-            intens_list.append(intens)
-            # cehck if tof
-            if num_lamda > 1:
-                tof = True
-            else:
-                tof = False
-            # compute 2d resolution
-            _, _, sigma_1, sigma_2, sigma_r, sigma1d = \
-                        self.compute(lam, dlam, qx_value, qy_value, coord, tof)
-            # make image
-            image = self.get_image(qx_value, qy_value, sigma_1, sigma_2,
-                                   sigma_r, qx_min, qx_max, qy_min, qy_max,
-                                   coord, False)
-            if qx_min > self.qx_min:
-                qx_min = self.qx_min
-            if qx_max < self.qx_max:
-                qx_max = self.qx_max
-            if qy_min > self.qy_min:
-                qy_min = self.qy_min
-            if qy_max < self.qy_max:
-                qy_max = self.qy_max
-
-            # set max qranges
-            self.qxrange = [qx_min, qx_max]
-            self.qyrange = [qy_min, qy_max]
-            sig1_list.append(sigma_1)
-            sig2_list.append(sigma_2)
-            sigr_list.append(sigma_r)
-            sigma1d_list.append(sigma1d)
-        # redraw image in global 2d q-space.
-        self.image_lam = []
-        total_intensity = 0
-        sigma_1 = 0
-        sigma_r = 0
-        sigma_2 = 0
-        sigma1d = 0
-        for ind in range(num_lamda):
-            lam = lamda_list[ind]
-            dlam = dlamb_list[ind]
-            intens = self.setup_tof(lam, dlam)
-            out = self.get_image(qx_value, qy_value, sig1_list[ind],
-                                 sig2_list[ind], sigr_list[ind],
-                                 qx_min, qx_max, qy_min, qy_max, coord)
-            # this is the case of q being outside the detector
-            #if numpy.all(out==0.0):
-            #    continue
-            image = out
-            # set variance as sigmas
-            sigma_1 += sig1_list[ind] * sig1_list[ind] * self.intensity
-            sigma_r += sigr_list[ind] * sigr_list[ind] * self.intensity
-            sigma_2 += sig2_list[ind] * sig2_list[ind] * self.intensity
-            sigma1d += sigma1d_list[ind] * sigma1d_list[ind] * self.intensity
-            total_intensity += self.intensity
-
-        if total_intensity != 0:
-            # average variance
-            image_out = image / total_intensity
-            sigma_1 = sigma_1 / total_intensity
-            sigma_r = sigma_r / total_intensity
-            sigma_2 = sigma_2 / total_intensity
-            sigma1d = sigma1d / total_intensity
-            # set sigmas
-            self.sigma_1 = sqrt(sigma_1)
-            self.sigma_lamd = sqrt(sigma_r)
-            self.sigma_2 = sqrt(sigma_2)
-            self.sigma_1d = sqrt(sigma1d)
-            # rescale
-            max_im_val = 1
-            if max_im_val > 0:
-                image_out /= max_im_val
-        else:
-            image_out = image * 0.0
-            # Don't calculate sigmas nor set self.sigmas!
-            sigma_1 = 0
-            sigma_r = 0
-            sigma_2 = 0
-            sigma1d = 0
-        if len(self.image) > 0:
-            self.image += image_out
-        else:
-            self.image = image_out
-
-        # plot image
-        return self.plot_image(self.image)
-
-    def setup_tof(self, wavelength, wavelength_spread):
-        """
-        Setup all parameters in instrument
-
-        : param ind: index of lambda, etc
-        """
-
-        # set wave.wavelength
-        self.set_wavelength(wavelength)
-        self.set_wavelength_spread(wavelength_spread)
-        self.intensity = self.wave.get_intensity()
-
-        if wavelength == 0:
-            msg = "Can't compute the resolution: the wavelength is zero..."
-            raise RuntimeError, msg
-        return self.intensity
-
-    def compute(self, wavelength, wavelength_spread, qx_value, qy_value,
-                coord='cartesian', tof=False):
-        """
-        Compute the Q resoltuion in || and + direction of 2D
-        : qx_value: x component of q
-        : qy_value: y component of q
-        """
-        coord = 'cartesian'
-        lamb = wavelength
-        lamb_spread = wavelength_spread
-        # the shape of wavelength distribution
-
-        if tof:
-            # rectangular
-            tof_factor = 2
-        else:
-            # triangular
-            tof_factor = 1
-        # Find polar values
-        qr_value, phi = self._get_polar_value(qx_value, qy_value)
-        # vacuum wave transfer
-        knot = 2*pi/lamb
-        # scattering angle theta; always true for plane detector
-        # aligned vertically to the ko direction
-        if qr_value > knot:
-            theta = pi/2
-        else:
-            theta = math.asin(qr_value/knot)
-        # source aperture size
-        rone = self.source_aperture_size
-        # sample aperture size
-        rtwo = self.sample_aperture_size
-        # detector pixel size
-        rthree = self.detector_pix_size
-        # source to sample(aperture) distance
-        l_ssa = self.source2sample_distance[0]
-        # sample(aperture) to detector distance
-        l_sad = self.sample2detector_distance[0]
-        # sample (aperture) to sample distance
-        l_sas = self.sample2sample_distance[0]
-        # source to sample distance
-        l_one = l_ssa + l_sas
-        # sample to detector distance
-        l_two = l_sad - l_sas
-
-        # Sample offset correction for l_one and Lp on variance calculation
-        l1_cor = (l_ssa * l_two) / (l_sas + l_two)
-        lp_cor = (l_ssa * l_two) / (l_one + l_two)
-        # the radial distance to the pixel from the center of the detector
-        radius = math.tan(theta) * l_two
-        #Lp = l_one*l_two/(l_one+l_two)
-        # default polar coordinate
-        comp1 = 'radial'
-        comp2 = 'phi'
-        # in the case of the cartesian coordinate
-        if coord == 'cartesian':
-            comp1 = 'x'
-            comp2 = 'y'
-
-        # sigma in the radial/x direction
-        # for source aperture
-        sigma_1 = self.get_variance(rone, l1_cor, phi, comp1)
-        # for sample apperture
-        sigma_1 += self.get_variance(rtwo, lp_cor, phi, comp1)
-        # for detector pix
-        sigma_1 += self.get_variance(rthree, l_two, phi, comp1)
-        # for gravity term for 1d
-        sigma_1grav1d = self.get_variance_gravity(l_ssa, l_sad, lamb,
-                                                  lamb_spread, phi, comp1, 'on') / tof_factor
-        # for wavelength spread
-        # reserve for 1d calculation
-        A_value = self._cal_A_value(lamb, l_ssa, l_sad)
-        sigma_wave_1, sigma_wave_1_1d = self.get_variance_wave(A_value,
-                                                               radius, l_two, lamb_spread,
-                                                               phi, 'radial', 'on')
-        sigma_wave_1 /= tof_factor
-        sigma_wave_1_1d /= tof_factor
-        # for 1d
-        variance_1d_1 = (sigma_1 + sigma_1grav1d) / 2 + sigma_wave_1_1d
-        # normalize
-        variance_1d_1 = knot * knot * variance_1d_1 / 12
-
-        # for 2d
-        #sigma_1 += sigma_wave_1
-        # normalize
-        sigma_1 = knot * sqrt(sigma_1 / 12)
-        sigma_r = knot * sqrt(sigma_wave_1 / (tof_factor *12))
-        # sigma in the phi/y direction
-        # for source apperture
-        sigma_2 = self.get_variance(rone, l1_cor, phi, comp2)
-
-        # for sample apperture
-        sigma_2 += self.get_variance(rtwo, lp_cor, phi, comp2)
-
-        # for detector pix
-        sigma_2 += self.get_variance(rthree, l_two, phi, comp2)
-
-        # for gravity term for 1d
-        sigma_2grav1d = self.get_variance_gravity(l_ssa, l_sad, lamb,
-                                                  lamb_spread, phi, comp2, 'on') / tof_factor
-
-        # for wavelength spread
-        # reserve for 1d calculation
-        sigma_wave_2, sigma_wave_2_1d = self.get_variance_wave(A_value,
-                                                               radius, l_two, lamb_spread,
-                                                               phi, 'phi', 'on')
-        sigma_wave_2 /= tof_factor
-        sigma_wave_2_1d /= tof_factor
-        # for 1d
-        variance_1d_2 = (sigma_2 + sigma_2grav1d) / 2 + sigma_wave_2_1d
-        # normalize
-        variance_1d_2 = knot * knot * variance_1d_2 / 12
-
-        # for 2d
-        #sigma_2 =  knot*sqrt(sigma_2/12)
-        #sigma_2 += sigma_wave_2
-        # normalize
-        sigma_2 = knot * sqrt(sigma_2 / 12)
-        sigma1d = sqrt(variance_1d_1 + variance_1d_2)
-        # set sigmas
-        self.sigma_1 = sigma_1
-        self.sigma_lamd = sigma_r
-        self.sigma_2 = sigma_2
-        self.sigma_1d = sigma1d
-        return qr_value, phi, sigma_1, sigma_2, sigma_r, sigma1d
-
-    def _within_detector_range(self, qx_value, qy_value):
-        """
-        check if qvalues are within detector range
-        """
-        # detector range
-        detector_qx_min = self.detector_qx_min
-        detector_qx_max = self.detector_qx_max
-        detector_qy_min = self.detector_qy_min
-        detector_qy_max = self.detector_qy_max
-        if self.qxmin_limit > detector_qx_min:
-            self.qxmin_limit = detector_qx_min
-        if self.qxmax_limit < detector_qx_max:
-            self.qxmax_limit = detector_qx_max
-        if self.qymin_limit > detector_qy_min:
-            self.qymin_limit = detector_qy_min
-        if self.qymax_limit < detector_qy_max:
-            self.qymax_limit = detector_qy_max
-        if qx_value < detector_qx_min or qx_value > detector_qx_max:
-            return False
-        if qy_value < detector_qy_min or qy_value > detector_qy_max:
-            return False
-        return True
-
-    def get_image(self, qx_value, qy_value, sigma_1, sigma_2, sigma_r,
-                  qx_min, qx_max, qy_min, qy_max,
-                  coord='cartesian', full_cal=True):
-        """
-        Get the resolution in polar coordinate ready to plot
-        : qx_value: qx_value value
-        : qy_value: qy_value value
-        : sigma_1: variance in r direction
-        : sigma_2: variance in phi direction
-        : coord: coordinate system of image, 'polar' or 'cartesian'
-        """
-        # Get  qx_max and qy_max...
-        self._get_detector_qxqy_pixels()
-
-        qr_value, phi = self._get_polar_value(qx_value, qy_value)
-
-        # Check whether the q value is within the detector range
-        if qx_min < self.qx_min:
-            self.qx_min = qx_min
-            #raise ValueError, msg
-        if qx_max > self.qx_max:
-            self.qx_max = qx_max
-            #raise ValueError, msg
-        if qy_min < self.qy_min:
-            self.qy_min = qy_min
-            #raise ValueError, msg
-        if qy_max > self.qy_max:
-            self.qy_max = qy_max
-            #raise ValueError, msg
-        if not full_cal:
-            return None
-
-        # Make an empty graph in the detector scale
-        dx_size = (self.qx_max - self.qx_min) / (1000 - 1)
-        dy_size = (self.qy_max - self.qy_min) / (1000 - 1)
-        x_val = numpy.arange(self.qx_min, self.qx_max, dx_size)
-        y_val = numpy.arange(self.qy_max, self.qy_min, -dy_size)
-        q_1, q_2 = numpy.meshgrid(x_val, y_val)
-        #q_phi = numpy.arctan(q_1,q_2)
-        # check whether polar or cartesian
-        if coord == 'polar':
-            # Find polar values
-            qr_value, phi = self._get_polar_value(qx_value, qy_value)
-            q_1, q_2 = self._rotate_z(q_1, q_2, phi)
-            qc_1 = qr_value
-            qc_2 = 0.0
-            # Calculate the 2D Gaussian distribution image
-            image = self._gaussian2d_polar(q_1, q_2, qc_1, qc_2,
-                                           sigma_1, sigma_2, sigma_r)
-        else:
-            # catesian coordinate
-            # qx_center
-            qc_1 = qx_value
-            # qy_center
-            qc_2 = qy_value
-
-            # Calculate the 2D Gaussian distribution image
-            image = self._gaussian2d(q_1, q_2, qc_1, qc_2,
-                                     sigma_1, sigma_2, sigma_r)
-        # out side of detector
-        if not self._within_detector_range(qx_value, qy_value):
-            image *= 0.0
-            self.intensity = 0.0
-            #return self.image
-
-        # Add it if there are more than one inputs.
-        if len(self.image_lam) > 0:
-            self.image_lam += image * self.intensity
-        else:
-            self.image_lam = image * self.intensity
-
-        return self.image_lam
-
-    def plot_image(self, image):
-        """
-        Plot image using pyplot
-        : image: 2d resolution image
-
-        : return plt: pylab object
-        """
-        import matplotlib.pyplot as plt
-
-        self.plot = plt
-        plt.xlabel('$\\rm{Q}_{x} [A^{-1}]$')
-        plt.ylabel('$\\rm{Q}_{y} [A^{-1}]$')
-        # Max value of the image
-        # max = numpy.max(image)
-        qx_min, qx_max, qy_min, qy_max = self.get_detector_qrange()
-
-        # Image
-        im = plt.imshow(image,
-                        extent=[qx_min, qx_max, qy_min, qy_max])
-
-        # bilinear interpolation to make it smoother
-        im.set_interpolation('bilinear')
-
-        return plt
-
-    def reset_image(self):
-        """
-        Reset image to default (=[])
-        """
-        self.image = []
-
-    def get_variance(self, size=[], distance=0, phi=0, comp='radial'):
-        """
-        Get the variance when the slit/pinhole size is given
-        : size: list that can be one(diameter for circular) or two components(lengths for rectangular)
-        : distance: [z, x] where z along the incident beam, x // qx_value
-        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
-
-        : return variance: sigma^2
-        """
-        # check the length of size (list)
-        len_size = len(size)
-
-        # define sigma component direction
-        if comp == 'radial':
-            phi_x = math.cos(phi)
-            phi_y = math.sin(phi)
-        elif comp == 'phi':
-            phi_x = math.sin(phi)
-            phi_y = math.cos(phi)
-        elif comp == 'x':
-            phi_x = 1
-            phi_y = 0
-        elif comp == 'y':
-            phi_x = 0
-            phi_y = 1
-        else:
-            phi_x = 0
-            phi_y = 0
-        # calculate each component
-        # for pinhole w/ radius = size[0]/2
-        if len_size == 1:
-            x_comp = (0.5 * size[0]) * sqrt(3)
-            y_comp = 0
-        # for rectangular slit
-        elif len_size == 2:
-            x_comp = size[0] * phi_x
-            y_comp = size[1] * phi_y
-        # otherwise
-        else:
-            raise ValueError, " Improper input..."
-        # get them squared
-        sigma = x_comp * x_comp
-        sigma += y_comp * y_comp
-        # normalize by distance
-        sigma /= (distance * distance)
-
-        return sigma
-
-    def get_variance_wave(self, A_value, radius, distance, spread, phi,
-                          comp='radial', switch='on'):
-        """
-        Get the variance when the wavelength spread is given
-
-        : radius: the radial distance from the beam center to the pix of q
-        : distance: sample to detector distance
-        : spread: wavelength spread (ratio)
-        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
-
-        : return variance: sigma^2 for 2d, sigma^2 for 1d [tuple]
-        """
-        if switch.lower() == 'off':
-            return 0, 0
-        # check the singular point
-        if distance == 0 or comp == 'phi':
-            return 0, 0
-        else:
-            # calculate sigma^2 for 1d
-            sigma1d = 2 * math.pow(radius/distance*spread, 2)
-            if comp == 'x':
-                sigma1d *= (math.cos(phi)*math.cos(phi))
-            elif comp == 'y':
-                sigma1d *= (math.sin(phi)*math.sin(phi))
-            else:
-                sigma1d *= 1
-            # sigma^2 for 2d
-            # shift the coordinate due to the gravitational shift
-            rad_x = radius * math.cos(phi)
-            rad_y = A_value - radius * math.sin(phi)
-            radius = math.sqrt(rad_x * rad_x + rad_y * rad_y)
-            # new phi
-            phi = math.atan2(-rad_y, rad_x)
-            self.gravity_phi = phi
-            # calculate sigma^2
-            sigma = 2 * math.pow(radius/distance*spread, 2)
-            if comp == 'x':
-                sigma *= (math.cos(phi)*math.cos(phi))
-            elif comp == 'y':
-                sigma *= (math.sin(phi)*math.sin(phi))
-            else:
-                sigma *= 1
-
-            return sigma, sigma1d
-
-    def get_variance_gravity(self, s_distance, d_distance, wavelength, spread,
-                             phi, comp='radial', switch='on'):
-        """
-        Get the variance from gravity when the wavelength spread is given
-
-        : s_distance: source to sample distance
-        : d_distance: sample to detector distance
-        : wavelength: wavelength
-        : spread: wavelength spread (ratio)
-        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
-
-        : return variance: sigma^2
-        """
-        if switch.lower() == 'off':
-            return 0
-        if self.mass == 0.0:
-            return 0
-        # check the singular point
-        if d_distance == 0 or comp == 'x':
-            return 0
-        else:
-            a_value = self._cal_A_value(None, s_distance, d_distance)
-            # calculate sigma^2
-            sigma = math.pow(a_value / d_distance, 2)
-            sigma *= math.pow(wavelength, 4)
-            sigma *= math.pow(spread, 2)
-            sigma *= 8
-            return sigma
-
-    def _cal_A_value(self, lamda, s_distance, d_distance):
-        """
-        Calculate A value for gravity
-
-        : s_distance: source to sample distance
-        : d_distance: sample to detector distance
-        """
-        # neutron mass in cgs unit
-        self.mass = self.get_neutron_mass()
-        # plank constant in cgs unit
-        h_constant = _PLANK_H
-        # gravity in cgs unit
-        gravy = _GRAVITY
-        # m/h
-        m_over_h = self.mass / h_constant
-        # A value
-        a_value = d_distance * (s_distance + d_distance)
-        a_value *= math.pow(m_over_h / 2, 2)
-        a_value *= gravy
-        # unit correction (1/cm to 1/A) for A and d_distance below
-        a_value *= 1.0E-16
-        # if lamda is give (broad meanning of A)  return 2* lamda^2 * A
-        if lamda != None:
-            a_value *= (4 * lamda * lamda)
-        return a_value
-
-    def get_intensity(self):
-        """
-        Get intensity
-        """
-        return self.wave.intensity
-
-    def get_wavelength(self):
-        """
-        Get wavelength
-        """
-        return self.wave.wavelength
-
-    def get_default_spectrum(self):
-        """
-        Get default_spectrum
-        """
-        return self.wave.get_default_spectrum()
-
-    def get_spectrum(self):
-        """
-        Get _spectrum
-        """
-        return self.wave.get_spectrum()
-
-    def get_wavelength_spread(self):
-        """
-        Get wavelength spread
-        """
-        return self.wave.wavelength_spread
-
-    def get_neutron_mass(self):
-        """
-        Get Neutron mass
-        """
-        return self.wave.mass
-
-    def get_source_aperture_size(self):
-        """
-        Get source aperture size
-        """
-        return self.aperture.source_size
-
-    def get_sample_aperture_size(self):
-        """
-        Get sample aperture size
-        """
-        return self.aperture.sample_size
-
-    def get_detector_pix_size(self):
-        """
-        Get detector pixel size
-        """
-        return self.detector.pix_size
-
-    def get_detector_size(self):
-        """
-        Get detector size
-        """
-        return self.detector.size
-
-    def get_source2sample_distance(self):
-        """
-        Get detector source2sample_distance
-        """
-        return self.aperture.sample_distance
-
-    def get_sample2sample_distance(self):
-        """
-        Get detector sampleslitsample_distance
-        """
-        return self.sample.distance
-
-    def get_sample2detector_distance(self):
-        """
-        Get detector sample2detector_distance
-        """
-        return self.detector.distance
-
-    def set_intensity(self, intensity):
-        """
-        Set intensity
-        """
-        self.wave.set_intensity(intensity)
-
-    def set_wave(self, wavelength):
-        """
-        Set wavelength list or wavelength
-        """
-        if wavelength.__class__.__name__ == 'list':
-            self.wave.set_wave_list(wavelength)
-        elif wavelength.__class__.__name__ == 'float':
-            self.wave.set_wave_list([wavelength])
-            #self.set_wavelength(wavelength)
-        else:
-            raise
-
-    def set_wave_spread(self, wavelength_spread):
-        """
-        Set wavelength spread  or wavelength spread
-        """
-        if wavelength_spread.__class__.__name__ == 'list':
-            self.wave.set_wave_spread_list(wavelength_spread)
-        elif wavelength_spread.__class__.__name__ == 'float':
-            self.wave.set_wave_spread_list([wavelength_spread])
-        else:
-            raise
-
-    def set_wavelength(self, wavelength):
-        """
-        Set wavelength
-        """
-        self.wavelength = wavelength
-        self.wave.set_wavelength(wavelength)
-
-    def set_spectrum(self, spectrum):
-        """
-        Set spectrum
-        """
-        self.spectrum = spectrum
-        self.wave.set_spectrum(spectrum)
-
-    def set_wavelength_spread(self, wavelength_spread):
-        """
-        Set wavelength spread
-        """
-        self.wavelength_spread = wavelength_spread
-        self.wave.set_wavelength_spread(wavelength_spread)
-
-    def set_wave_list(self, wavelength_list, wavelengthspread_list):
-        """
-        Set wavelength and its spread list
-        """
-        self.wave.set_wave_list(wavelength_list)
-        self.wave.set_wave_spread_list(wavelengthspread_list)
-
-    def get_wave_list(self):
-        """
-        Set wavelength spread
-        """
-        return self.wave.get_wave_list()
-
-    def get_intensity_list(self):
-        """
-        Set wavelength spread
-        """
-        return self.wave.get_intensity_list()
-
-    def set_source_aperture_size(self, size):
-        """
-        Set source aperture size
-
-        : param size: [dia_value] or [x_value, y_value]
-        """
-        if len(size) < 1 or len(size) > 2:
-            raise RuntimeError, "The length of the size must be one or two."
-        self.aperture.set_source_size(size)
-
-    def set_neutron_mass(self, mass):
-        """
-        Set Neutron mass
-        """
-        self.wave.set_mass(mass)
-        self.mass = mass
-
-    def set_sample_aperture_size(self, size):
-        """
-        Set sample aperture size
-
-        : param size: [dia_value] or [xheight_value, yheight_value]
-        """
-        if len(size) < 1 or len(size) > 2:
-            raise RuntimeError, "The length of the size must be one or two."
-        self.aperture.set_sample_size(size)
-
-    def set_detector_pix_size(self, size):
-        """
-        Set detector pixel size
-        """
-        self.detector.set_pix_size(size)
-
-    def set_detector_size(self, size):
-        """
-        Set detector size in number of pixels
-        : param size: [pixel_nums] or [x_pix_num, yx_pix_num]
-        """
-        self.detector.set_size(size)
-
-    def set_source2sample_distance(self, distance):
-        """
-        Set detector source2sample_distance
-
-        : param distance: [distance, x_offset]
-        """
-        if len(distance) < 1 or len(distance) > 2:
-            raise RuntimeError, "The length of the size must be one or two."
-        self.aperture.set_sample_distance(distance)
-
-    def set_sample2sample_distance(self, distance):
-        """
-        Set detector sample_slit2sample_distance
-
-        : param distance: [distance, x_offset]
-        """
-        if len(distance) < 1 or len(distance) > 2:
-            raise RuntimeError, "The length of the size must be one or two."
-        self.sample.set_distance(distance)
-
-    def set_sample2detector_distance(self, distance):
-        """
-        Set detector sample2detector_distance
-
-        : param distance: [distance, x_offset]
-        """
-        if len(distance) < 1 or len(distance) > 2:
-            raise RuntimeError, "The length of the size must be one or two."
-        self.detector.set_distance(distance)
-
-    def get_all_instrument_params(self):
-        """
-        Get all instrumental parameters
-        """
-        self.mass = self.get_neutron_mass()
-        self.spectrum = self.get_spectrum()
-        self.source_aperture_size = self.get_source_aperture_size()
-        self.sample_aperture_size = self.get_sample_aperture_size()
-        self.detector_pix_size = self.get_detector_pix_size()
-        self.detector_size = self.get_detector_size()
-        self.source2sample_distance = self.get_source2sample_distance()
-        self.sample2sample_distance = self.get_sample2sample_distance()
-        self.sample2detector_distance = self.get_sample2detector_distance()
-
-    def get_detector_qrange(self):
-        """
-        get max detector q ranges
-
-        : return: qx_min, qx_max, qy_min, qy_max tuple
-        """
-        if len(self.qxrange) != 2 or len(self.qyrange) != 2:
-            return None
-        qx_min = self.qxrange[0]
-        qx_max = self.qxrange[1]
-        qy_min = self.qyrange[0]
-        qy_max = self.qyrange[1]
-
-        return qx_min, qx_max, qy_min, qy_max
-
-    def _rotate_z(self, x_value, y_value, theta=0.0):
-        """
-        Rotate x-y cordinate around z-axis by theta
-        : x_value: numpy array of x values
-        : y_value: numpy array of y values
-        : theta: angle to rotate by in rad
-
-        :return: x_prime, y-prime
-        """
-        # rotate by theta
-        x_prime = x_value * math.cos(theta) + y_value * math.sin(theta)
-        y_prime = -x_value * math.sin(theta) + y_value * math.cos(theta)
-
-        return x_prime, y_prime
-
-    def _gaussian2d(self, x_val, y_val, x0_val, y0_val,
-                    sigma_x, sigma_y, sigma_r):
-        """
-        Calculate 2D Gaussian distribution
-        : x_val: x value
-        : y_val: y value
-        : x0_val: mean value in x-axis
-        : y0_val: mean value in y-axis
-        : sigma_x: variance in x-direction
-        : sigma_y: variance in y-direction
-
-        : return: gaussian (value)
-        """
-        # phi values at each points (not at the center)
-        x_value = x_val - x0_val
-        y_value = y_val - y0_val
-        phi_i = numpy.arctan2(y_val, x_val)
-
-        # phi correction due to the gravity shift (in phi)
-        phi_0 = math.atan2(y0_val, x0_val)
-        phi_i = phi_i - phi_0 + self.gravity_phi
-
-        sin_phi = numpy.sin(self.gravity_phi)
-        cos_phi = numpy.cos(self.gravity_phi)
-
-        x_p = x_value * cos_phi + y_value * sin_phi
-        y_p = -x_value * sin_phi + y_value * cos_phi
-
-        new_sig_x = sqrt(sigma_r * sigma_r / (sigma_x * sigma_x) + 1)
-        new_sig_y = sqrt(sigma_r * sigma_r / (sigma_y * sigma_y) + 1)
-        new_x = x_p * cos_phi / new_sig_x - y_p * sin_phi
-        new_x /= sigma_x
-        new_y = x_p * sin_phi / new_sig_y + y_p * cos_phi
-        new_y /= sigma_y
-
-        nu_value = -0.5 * (new_x * new_x + new_y * new_y)
-
-        gaussian = numpy.exp(nu_value)
-        # normalizing factor correction
-        gaussian /= gaussian.sum()
-
-        return gaussian
-
-    def _gaussian2d_polar(self, x_val, y_val, x0_val, y0_val,
-                          sigma_x, sigma_y, sigma_r):
-        """
-        Calculate 2D Gaussian distribution for polar coodinate
-        : x_val: x value
-        : y_val: y value
-        : x0_val: mean value in x-axis
-        : y0_val: mean value in y-axis
-        : sigma_x: variance in r-direction
-        : sigma_y: variance in phi-direction
-        : sigma_r: wavelength variance in r-direction
-
-        : return: gaussian (value)
-        """
-        sigma_x = sqrt(sigma_x * sigma_x + sigma_r * sigma_r)
-        # call gaussian1d
-        gaussian = self._gaussian1d(x_val, x0_val, sigma_x)
-        gaussian *= self._gaussian1d(y_val, y0_val, sigma_y)
-
-        # normalizing factor correction
-        if sigma_x != 0 and sigma_y != 0:
-            gaussian *= sqrt(2 * pi)
-        return gaussian
-
-    def _gaussian1d(self, value, mean, sigma):
-        """
-        Calculate 1D Gaussian distribution
-        : value: value
-        : mean: mean value
-        : sigma: variance
-
-        : return: gaussian (value)
-        """
-        # default
-        gaussian = 1.0
-        if sigma != 0:
-            # get exponent
-            nu_value = (value - mean) / sigma
-            nu_value *= nu_value
-            nu_value *= -0.5
-            gaussian *= numpy.exp(nu_value)
-            gaussian /= sigma
-            # normalize
-            gaussian /= sqrt(2 * pi)
-
-        return gaussian
-
-    def _atan_phi(self, qy_value, qx_value):
-        """
-        Find the angle phi of q on the detector plane for qx_value, qy_value given
-        : qx_value: x component of q
-        : qy_value: y component of q
-
-        : return phi: the azimuthal angle of q on x-y plane
-        """
-        phi = math.atan2(qy_value, qx_value)
-        return phi
-
-    def _get_detector_qxqy_pixels(self):
-        """
-        Get the pixel positions of the detector in the qx_value-qy_value space
-        """
-
-        # update all param values
-        self.get_all_instrument_params()
-
-        # wavelength
-        wavelength = self.wave.wavelength
-        # Gavity correction
-        delta_y = self._get_beamcenter_drop()  # in cm
-
-        # detector_pix size
-        detector_pix_size = self.detector_pix_size
-        # Square or circular pixel
-        if len(detector_pix_size) == 1:
-            pix_x_size = detector_pix_size[0]
-            pix_y_size = detector_pix_size[0]
-        # rectangular pixel pixel
-        elif len(detector_pix_size) == 2:
-            pix_x_size = detector_pix_size[0]
-            pix_y_size = detector_pix_size[1]
-        else:
-            raise ValueError, " Input value format error..."
-        # Sample to detector distance = sample slit to detector
-        # minus sample offset
-        sample2detector_distance = self.sample2detector_distance[0] - \
-                                    self.sample2sample_distance[0]
-        # detector offset in x-direction
-        detector_offset = 0
-        try:
-            detector_offset = self.sample2detector_distance[1]
-        except:
-            logging.error(sys.exc_value)
-
-        # detector size in [no of pix_x,no of pix_y]
-        detector_pix_nums_x = self.detector_size[0]
-
-        # get pix_y if it exists, otherwse take it from [0]
-        try:
-            detector_pix_nums_y = self.detector_size[1]
-        except:
-            detector_pix_nums_y = self.detector_size[0]
-
-        # detector offset in pix number
-        offset_x = detector_offset / pix_x_size
-        offset_y = delta_y / pix_y_size
-
-        # beam center position in pix number (start from 0)
-        center_x, center_y = self._get_beamcenter_position(detector_pix_nums_x,
-                                                           detector_pix_nums_y,
-                                                           offset_x, offset_y)
-        # distance [cm] from the beam center on detector plane
-        detector_ind_x = numpy.arange(detector_pix_nums_x)
-        detector_ind_y = numpy.arange(detector_pix_nums_y)
-
-        # shif 0.5 pixel so that pix position is at the center of the pixel
-        detector_ind_x = detector_ind_x + 0.5
-        detector_ind_y = detector_ind_y + 0.5
-
-        # the relative postion from the beam center
-        detector_ind_x = detector_ind_x - center_x
-        detector_ind_y = detector_ind_y - center_y
-
-        # unit correction in cm
-        detector_ind_x = detector_ind_x * pix_x_size
-        detector_ind_y = detector_ind_y * pix_y_size
-
-        qx_value = numpy.zeros(len(detector_ind_x))
-        qy_value = numpy.zeros(len(detector_ind_y))
-        i = 0
-
-        for indx in detector_ind_x:
-            qx_value[i] = self._get_qx(indx, sample2detector_distance, wavelength)
-            i += 1
-        i = 0
-        for indy in detector_ind_y:
-            qy_value[i] = self._get_qx(indy, sample2detector_distance, wavelength)
-            i += 1
-
-        # qx_value and qy_value values in array
-        qx_value = qx_value.repeat(detector_pix_nums_y)
-        qx_value = qx_value.reshape(detector_pix_nums_x, detector_pix_nums_y)
-        qy_value = qy_value.repeat(detector_pix_nums_x)
-        qy_value = qy_value.reshape(detector_pix_nums_y, detector_pix_nums_x)
-        qy_value = qy_value.transpose()
-
-        # p min and max values among the center of pixels
-        self.qx_min = numpy.min(qx_value)
-        self.qx_max = numpy.max(qx_value)
-        self.qy_min = numpy.min(qy_value)
-        self.qy_max = numpy.max(qy_value)
-
-        # Appr. min and max values of the detector display limits
-        # i.e., edges of the last pixels.
-        self.qy_min += self._get_qx(-0.5 * pix_y_size,
-                                    sample2detector_distance, wavelength)
-        self.qy_max += self._get_qx(0.5 * pix_y_size,
-                                    sample2detector_distance, wavelength)
-        #if self.qx_min == self.qx_max:
-        self.qx_min += self._get_qx(-0.5 * pix_x_size,
-                                    sample2detector_distance, wavelength)
-        self.qx_max += self._get_qx(0.5 * pix_x_size,
-                                    sample2detector_distance, wavelength)
-
-        # min and max values of detecter
-        self.detector_qx_min = self.qx_min
-        self.detector_qx_max = self.qx_max
-        self.detector_qy_min = self.qy_min
-        self.detector_qy_max = self.qy_max
-
-        # try to set it as a Data2D otherwise pass (not required for now)
-        try:
-            from sas.dataloader.data_info import Data2D
-            output = Data2D()
-            inten = numpy.zeros_like(qx_value)
-            output.data = inten
-            output.qx_data = qx_value
-            output.qy_data = qy_value
-        except:
-            logging.error(sys.exc_value)
-
-        return output
-
-    def _get_qx(self, dx_size, det_dist, wavelength):
-        """
-        :param dx_size: x-distance from beam center [cm]
-        :param det_dist: sample to detector distance [cm]
-
-        :return: q-value at the given position
-        """
-        # Distance from beam center in the plane of detector
-        plane_dist = dx_size
-        # full scattering angle on the x-axis
-        theta = numpy.arctan(plane_dist / det_dist)
-        qx_value = (2.0 * pi / wavelength) * numpy.sin(theta)
-        return qx_value
-
-    def _get_polar_value(self, qx_value, qy_value):
-        """
-        Find qr_value and phi from qx_value and qy_value values
-
-        : return qr_value, phi
-        """
-        # find |q| on detector plane
-        qr_value = sqrt(qx_value*qx_value + qy_value*qy_value)
-        # find angle phi
-        phi = self._atan_phi(qy_value, qx_value)
-
-        return qr_value, phi
-
-    def _get_beamcenter_position(self, num_x, num_y, offset_x, offset_y):
-        """
-        :param num_x: number of pixel in x-direction
-        :param num_y: number of pixel in y-direction
-        :param offset: detector offset in x-direction in pix number
-
-        :return: pix number; pos_x, pos_y in pix index
-        """
-        # beam center position
-        pos_x = num_x / 2
-        pos_y = num_y / 2
-
-        # correction for offset
-        pos_x += offset_x
-        # correction for gravity that is always negative
-        pos_y -= offset_y
-
-        return pos_x, pos_y
-
-    def _get_beamcenter_drop(self):
-        """
-        Get the beam center drop (delta y) in y diection due to gravity
-
-        :return delta y: the beam center drop in cm
-        """
-        # Check if mass == 0 (X-ray).
-        if self.mass == 0:
-            return 0
-        # Covert unit from A to cm
-        unit_cm = 1e-08
-        # Velocity of neutron in horizontal direction (~ actual velocity)
-        velocity = _PLANK_H / (self.mass * self.wave.wavelength * unit_cm)
-        # Compute delta y
-        delta_y = 0.5
-        delta_y *= _GRAVITY
-        sampletodetector = self.sample2detector_distance[0] - \
-                                    self.sample2sample_distance[0]
-        delta_y *= sampletodetector
-        delta_y *= (self.source2sample_distance[0] + self.sample2detector_distance[0])
-        delta_y /= (velocity * velocity)
-
-        return delta_y
Index: c/sas/calculator/sas_gen.py
===================================================================
--- src/sas/calculator/sas_gen.py	(revision ac7be54ccf932e813cbe6e41e05b682a1630a03e)
+++ 	(revision )
@@ -1,1116 +1,0 @@
-# pylint: disable=invalid-name
-"""
-SAS generic computation and sld file readers
-"""
-from sas.models.BaseComponent import BaseComponent
-import sas.models.sas_extension.sld2i as mod
-from periodictable import formula
-from periodictable import nsf
-import numpy
-import os
-import copy
-import sys
-import logging
-
-MFACTOR_AM = 2.853E-12
-MFACTOR_MT = 2.3164E-9
-METER2ANG = 1.0E+10
-#Avogadro constant [1/mol]
-NA = 6.02214129e+23
-
-def mag2sld(mag, v_unit=None):
-    """
-    Convert magnetization to magnatic SLD
-    sldm = Dm * mag where Dm = gamma * classical elec. radius/(2*Bohr magneton)
-    Dm ~ 2.853E-12 [A^(-2)] ==> Shouldn't be 2.90636E-12 [A^(-2)]???
-    """
-    if v_unit == "A/m":
-        factor = MFACTOR_AM
-    elif v_unit == "mT":
-        factor = MFACTOR_MT
-    else:
-        raise ValueError, "Invalid valueunit"
-    sld_m = factor * mag
-    return sld_m
-
-def transform_center(pos_x, pos_y, pos_z):
-    """
-    re-center
-    :return: posx, posy, posz   [arrays]
-    """
-    posx = pos_x - (min(pos_x) + max(pos_x)) / 2.0
-    posy = pos_y - (min(pos_y) + max(pos_y)) / 2.0
-    posz = pos_z - (min(pos_z) + max(pos_z)) / 2.0
-    return posx, posy, posz
-
-class GenSAS(BaseComponent):
-    """
-    Generic SAS computation Model based on sld (n & m) arrays
-    """
-    def __init__(self):
-        """
-        Init
-        :Params sld_data: MagSLD object
-        """
-        # Initialize BaseComponent
-        BaseComponent.__init__(self)
-        self.sld_data = None
-        self.data_pos_unit = None
-        self.data_x = None
-        self.data_y = None
-        self.data_z = None
-        self.data_sldn = None
-        self.data_mx = None
-        self.data_my = None
-        self.data_mz = None
-        self.data_vol = None #[A^3]
-        self.is_avg = False
-        ## Name of the model
-        self.name = "GenSAS"
-        ## Define parameters
-        self.params = {}
-        self.params['scale'] = 1.0
-        self.params['background'] = 0.0
-        self.params['solvent_SLD'] = 0.0
-        self.params['total_volume'] = 1.0
-        self.params['Up_frac_in'] = 1.0
-        self.params['Up_frac_out'] = 1.0
-        self.params['Up_theta'] = 0.0
-        self.description = 'GenSAS'
-        ## Parameter details [units, min, max]
-        self.details = {}
-        self.details['scale'] = ['', None, None]
-        self.details['background'] = ['[1/cm]', None, None]
-        self.details['solvent_SLD'] = ['1/A^(2)', None, None]
-        self.details['total_volume'] = ['A^(3)', None, None]
-        self.details['Up_frac_in'] = ['[u/(u+d)]', None, None]
-        self.details['Up_frac_out'] = ['[u/(u+d)]', None, None]
-        self.details['Up_theta'] = ['[deg]', None, None]
-        # fixed parameters
-        self.fixed = []
-
-    def set_pixel_volumes(self, volume):
-        """
-        Set the volume of a pixel in (A^3) unit
-        :Param volume: pixel volume [float]
-        """
-        if self.data_vol == None:
-            raise
-        self.data_vol = volume
-
-    def set_is_avg(self, is_avg=False):
-        """
-        Sets is_avg: [bool]
-        """
-        self.is_avg = is_avg
-
-    def _gen(self, x, y, i):
-        """
-        Evaluate the function
-        :Param x: array of x-values
-        :Param y: array of y-values
-        :Param i: array of initial i-value
-        :return: function value
-        """
-        pos_x = self.data_x
-        pos_y = self.data_y
-        pos_z = self.data_z
-        len_x = len(pos_x)
-        if self.is_avg == None:
-            len_x *= -1
-            pos_x, pos_y, pos_z = transform_center(pos_x, pos_y, pos_z)
-        len_q = len(x)
-        sldn = copy.deepcopy(self.data_sldn)
-        sldn -= self.params['solvent_SLD']
-        model = mod.new_GenI(len_x, pos_x, pos_y, pos_z,
-                             sldn, self.data_mx, self.data_my,
-                             self.data_mz, self.data_vol,
-                             self.params['Up_frac_in'],
-                             self.params['Up_frac_out'],
-                             self.params['Up_theta'])
-        if y == []:
-            mod.genicom(model, len_q, x, i)
-        else:
-            mod.genicomXY(model, len_q, x, y, i)
-        vol_correction = self.data_total_volume / self.params['total_volume']
-        return  self.params['scale'] * vol_correction * i + \
-                        self.params['background']
-
-    def set_sld_data(self, sld_data=None):
-        """
-        Sets sld_data
-        """
-        self.sld_data = sld_data
-        self.data_pos_unit = sld_data.pos_unit
-        self.data_x = sld_data.pos_x
-        self.data_y = sld_data.pos_y
-        self.data_z = sld_data.pos_z
-        self.data_sldn = sld_data.sld_n
-        self.data_mx = sld_data.sld_mx
-        self.data_my = sld_data.sld_my
-        self.data_mz = sld_data.sld_mz
-        self.data_vol = sld_data.vol_pix
-        self.data_total_volume = sum(sld_data.vol_pix)
-        self.params['total_volume'] = sum(sld_data.vol_pix)
-
-    def getProfile(self):
-        """
-        Get SLD profile
-        : return: sld_data
-        """
-        return self.sld_data
-
-    def run(self, x=0.0):
-        """
-        Evaluate the model
-        :param x: simple value
-        :return: (I value)
-        """
-        if x.__class__.__name__ == 'list':
-            if len(x[1]) > 0:
-                msg = "Not a 1D."
-                raise ValueError, msg
-            i_out = numpy.zeros_like(x[0])
-            # 1D I is found at y =0 in the 2D pattern
-            out = self._gen(x[0], [], i_out)
-            return out
-        else:
-            msg = "Q must be given as list of qx's and qy's"
-            raise ValueError, msg
-
-    def runXY(self, x=0.0):
-        """
-        Evaluate the model
-        :param x: simple value
-        :return: I value
-        :Use this runXY() for the computation
-        """
-        if x.__class__.__name__ == 'list':
-            i_out = numpy.zeros_like(x[0])
-            out = self._gen(x[0], x[1], i_out)
-            return out
-        else:
-            msg = "Q must be given as list of qx's and qy's"
-            raise ValueError, msg
-
-    def evalDistribution(self, qdist):
-        """
-        Evaluate a distribution of q-values.
-
-        :param qdist: ndarray of scalar q-values (for 1D) or list [qx,qy]
-                      where qx,qy are 1D ndarrays (for 2D).
-        """
-        if qdist.__class__.__name__ == 'list':
-            if len(qdist[1]) < 1:
-                out = self.run(qdist)
-            else:
-                out = self.runXY(qdist)
-            return out
-        else:
-            mesg = "evalDistribution is expecting an ndarray of "
-            mesg += "a list [qx,qy] where qx,qy are arrays."
-            raise RuntimeError, mesg
-
-class OMF2SLD(object):
-    """
-    Convert OMFData to MAgData
-    """
-    def __init__(self):
-        """
-        Init
-        """
-        self.pos_x = None
-        self.pos_y = None
-        self.pos_z = None
-        self.mx = None
-        self.my = None
-        self.mz = None
-        self.sld_n = None
-        self.vol_pix = None
-        self.output = None
-        self.omfdata = None
-
-    def set_data(self, omfdata, shape='rectangular'):
-        """
-        Set all data
-        """
-        self.omfdata = omfdata
-        length = int(omfdata.xnodes * omfdata.ynodes * omfdata.znodes)
-        pos_x = numpy.arange(omfdata.xmin,
-                             omfdata.xnodes*omfdata.xstepsize + omfdata.xmin,
-                             omfdata.xstepsize)
-        pos_y = numpy.arange(omfdata.ymin,
-                             omfdata.ynodes*omfdata.ystepsize + omfdata.ymin,
-                             omfdata.ystepsize)
-        pos_z = numpy.arange(omfdata.zmin,
-                             omfdata.znodes*omfdata.zstepsize + omfdata.zmin,
-                             omfdata.zstepsize)
-        self.pos_x = numpy.tile(pos_x, int(omfdata.ynodes * omfdata.znodes))
-        self.pos_y = pos_y.repeat(int(omfdata.xnodes))
-        self.pos_y = numpy.tile(self.pos_y, int(omfdata.znodes))
-        self.pos_z = pos_z.repeat(int(omfdata.xnodes * omfdata.ynodes))
-        self.mx = omfdata.mx
-        self.my = omfdata.my
-        self.mz = omfdata.mz
-        self.sld_n = numpy.zeros(length)
-
-        if omfdata.mx == None:
-            self.mx = numpy.zeros(length)
-        if omfdata.my == None:
-            self.my = numpy.zeros(length)
-        if omfdata.mz == None:
-            self.mz = numpy.zeros(length)
-
-        self._check_data_length(length)
-        self.remove_null_points(False, False)
-        mask = numpy.ones(len(self.sld_n), dtype=bool)
-        if shape.lower() == 'ellipsoid':
-            try:
-                # Pixel (step) size included
-                x_c = max(self.pos_x) + min(self.pos_x)
-                y_c = max(self.pos_y) + min(self.pos_y)
-                z_c = max(self.pos_z) + min(self.pos_z)
-                x_d = max(self.pos_x) - min(self.pos_x)
-                y_d = max(self.pos_y) - min(self.pos_y)
-                z_d = max(self.pos_z) - min(self.pos_z)
-                x_r = (x_d + omfdata.xstepsize) / 2.0
-                y_r = (y_d + omfdata.ystepsize) / 2.0
-                z_r = (z_d + omfdata.zstepsize) / 2.0
-                x_dir2 = ((self.pos_x - x_c / 2.0) / x_r)
-                x_dir2 *= x_dir2
-                y_dir2 = ((self.pos_y - y_c / 2.0) / y_r)
-                y_dir2 *= y_dir2
-                z_dir2 = ((self.pos_z - z_c / 2.0) / z_r)
-                z_dir2 *= z_dir2
-                mask = (x_dir2 + y_dir2 + z_dir2) <= 1.0
-            except:
-                logging.error(sys.exc_value)
-        self.output = MagSLD(self.pos_x[mask], self.pos_y[mask],
-                             self.pos_z[mask], self.sld_n[mask],
-                             self.mx[mask], self.my[mask], self.mz[mask])
-        self.output.set_pix_type('pixel')
-        self.output.set_pixel_symbols('pixel')
-
-    def get_omfdata(self):
-        """
-        Return all data
-        """
-        return self.omfdata
-
-    def get_output(self):
-        """
-        Return output
-        """
-        return self.output
-
-    def _check_data_length(self, length):
-        """
-        Check if the data lengths are consistent
-        :Params length: data length
-        """
-        msg = "Error: Inconsistent data length."
-        if len(self.pos_x) != length:
-            raise ValueError, msg
-        if len(self.pos_y) != length:
-            raise ValueError, msg
-        if len(self.pos_z) != length:
-            raise ValueError, msg
-        if len(self.mx) != length:
-            raise ValueError, msg
-        if len(self.my) != length:
-            raise ValueError, msg
-        if len(self.mz) != length:
-            raise ValueError, msg
-
-    def remove_null_points(self, remove=False, recenter=False):
-        """
-        Removes any mx, my, and mz = 0 points
-        """
-        if remove:
-            is_nonzero = (numpy.fabs(self.mx) + numpy.fabs(self.my) +
-                          numpy.fabs(self.mz)).nonzero()
-            if len(is_nonzero[0]) > 0:
-                self.pos_x = self.pos_x[is_nonzero]
-                self.pos_y = self.pos_y[is_nonzero]
-                self.pos_z = self.pos_z[is_nonzero]
-                self.sld_n = self.sld_n[is_nonzero]
-                self.mx = self.mx[is_nonzero]
-                self.my = self.my[is_nonzero]
-                self.mz = self.mz[is_nonzero]
-        if recenter:
-            self.pos_x -= (min(self.pos_x) + max(self.pos_x)) / 2.0
-            self.pos_y -= (min(self.pos_y) + max(self.pos_y)) / 2.0
-            self.pos_z -= (min(self.pos_z) + max(self.pos_z)) / 2.0
-
-    def get_magsld(self):
-        """
-        return MagSLD
-        """
-        return self.output
-
-
-class OMFReader(object):
-    """
-    Class to load omf/ascii files (3 columns w/header).
-    """
-    ## File type
-    type_name = "OMF ASCII"
-
-    ## Wildcards
-    type = ["OMF files (*.OMF, *.omf)|*.omf"]
-    ## List of allowed extensions
-    ext = ['.omf', '.OMF']
-
-    def read(self, path):
-        """
-        Load data file
-        :param path: file path
-        :return: x, y, z, sld_n, sld_mx, sld_my, sld_mz
-        """
-        desc = ""
-        mx = numpy.zeros(0)
-        my = numpy.zeros(0)
-        mz = numpy.zeros(0)
-        try:
-            input_f = open(path, 'rb')
-            buff = input_f.read()
-            lines = buff.split('\n')
-            input_f.close()
-            output = OMFData()
-            valueunit = None
-            for line in lines:
-                toks = line.split()
-                # Read data
-                try:
-                    _mx = float(toks[0])
-                    _my = float(toks[1])
-                    _mz = float(toks[2])
-                    _mx = mag2sld(_mx, valueunit)
-                    _my = mag2sld(_my, valueunit)
-                    _mz = mag2sld(_mz, valueunit)
-                    mx = numpy.append(mx, _mx)
-                    my = numpy.append(my, _my)
-                    mz = numpy.append(mz, _mz)
-                except:
-                    # Skip non-data lines
-                    logging.error(sys.exc_value)
-                #Reading Header; Segment count ignored
-                s_line = line.split(":", 1)
-                if s_line[0].lower().count("oommf") > 0:
-                    oommf = s_line[1].lstrip()
-                if s_line[0].lower().count("title") > 0:
-                    title = s_line[1].lstrip()
-                if s_line[0].lower().count("desc") > 0:
-                    desc += s_line[1].lstrip()
-                    desc += '\n'
-                if s_line[0].lower().count("meshtype") > 0:
-                    meshtype = s_line[1].lstrip()
-                if s_line[0].lower().count("meshunit") > 0:
-                    meshunit = s_line[1].lstrip()
-                    if meshunit.count("m") < 1:
-                        msg = "Error: \n"
-                        msg += "We accept only m as meshunit"
-                        raise ValueError, msg
-                if s_line[0].lower().count("xbase") > 0:
-                    xbase = s_line[1].lstrip()
-                if s_line[0].lower().count("ybase") > 0:
-                    ybase = s_line[1].lstrip()
-                if s_line[0].lower().count("zbase") > 0:
-                    zbase = s_line[1].lstrip()
-                if s_line[0].lower().count("xstepsize") > 0:
-                    xstepsize = s_line[1].lstrip()
-                if s_line[0].lower().count("ystepsize") > 0:
-                    ystepsize = s_line[1].lstrip()
-                if s_line[0].lower().count("zstepsize") > 0:
-                    zstepsize = s_line[1].lstrip()
-                if s_line[0].lower().count("xnodes") > 0:
-                    xnodes = s_line[1].lstrip()
-                if s_line[0].lower().count("ynodes") > 0:
-                    ynodes = s_line[1].lstrip()
-                if s_line[0].lower().count("znodes") > 0:
-                    znodes = s_line[1].lstrip()
-                if s_line[0].lower().count("xmin") > 0:
-                    xmin = s_line[1].lstrip()
-                if s_line[0].lower().count("ymin") > 0:
-                    ymin = s_line[1].lstrip()
-                if s_line[0].lower().count("zmin") > 0:
-                    zmin = s_line[1].lstrip()
-                if s_line[0].lower().count("xmax") > 0:
-                    xmax = s_line[1].lstrip()
-                if s_line[0].lower().count("ymax") > 0:
-                    ymax = s_line[1].lstrip()
-                if s_line[0].lower().count("zmax") > 0:
-                    zmax = s_line[1].lstrip()
-                if s_line[0].lower().count("valueunit") > 0:
-                    valueunit = s_line[1].lstrip().rstrip()
-                if s_line[0].lower().count("valuemultiplier") > 0:
-                    valuemultiplier = s_line[1].lstrip()
-                if s_line[0].lower().count("valuerangeminmag") > 0:
-                    valuerangeminmag = s_line[1].lstrip()
-                if s_line[0].lower().count("valuerangemaxmag") > 0:
-                    valuerangemaxmag = s_line[1].lstrip()
-                if s_line[0].lower().count("end") > 0:
-                    output.filename = os.path.basename(path)
-                    output.oommf = oommf
-                    output.title = title
-                    output.desc = desc
-                    output.meshtype = meshtype
-                    output.xbase = float(xbase) * METER2ANG
-                    output.ybase = float(ybase) * METER2ANG
-                    output.zbase = float(zbase) * METER2ANG
-                    output.xstepsize = float(xstepsize) * METER2ANG
-                    output.ystepsize = float(ystepsize) * METER2ANG
-                    output.zstepsize = float(zstepsize) * METER2ANG
-                    output.xnodes = float(xnodes)
-                    output.ynodes = float(ynodes)
-                    output.znodes = float(znodes)
-                    output.xmin = float(xmin) * METER2ANG
-                    output.ymin = float(ymin) * METER2ANG
-                    output.zmin = float(zmin) * METER2ANG
-                    output.xmax = float(xmax) * METER2ANG
-                    output.ymax = float(ymax) * METER2ANG
-                    output.zmax = float(zmax) * METER2ANG
-                    output.valuemultiplier = valuemultiplier
-                    output.valuerangeminmag = mag2sld(float(valuerangeminmag), \
-                                                      valueunit)
-                    output.valuerangemaxmag = mag2sld(float(valuerangemaxmag), \
-                                                      valueunit)
-            output.set_m(mx, my, mz)
-            return output
-        except:
-            msg = "%s is not supported: \n" % path
-            msg += "We accept only Text format OMF file."
-            raise RuntimeError, msg
-
-class PDBReader(object):
-    """
-    PDB reader class: limited for reading the lines starting with 'ATOM'
-    """
-    type_name = "PDB"
-    ## Wildcards
-    type = ["pdb files (*.PDB, *.pdb)|*.pdb"]
-    ## List of allowed extensions
-    ext = ['.pdb', '.PDB']
-
-    def read(self, path):
-        """
-        Load data file
-
-        :param path: file path
-        :return: MagSLD
-        :raise RuntimeError: when the file can't be opened
-        """
-        pos_x = numpy.zeros(0)
-        pos_y = numpy.zeros(0)
-        pos_z = numpy.zeros(0)
-        sld_n = numpy.zeros(0)
-        sld_mx = numpy.zeros(0)
-        sld_my = numpy.zeros(0)
-        sld_mz = numpy.zeros(0)
-        vol_pix = numpy.zeros(0)
-        pix_symbol = numpy.zeros(0)
-        x_line = []
-        y_line = []
-        z_line = []
-        x_lines = []
-        y_lines = []
-        z_lines = []
-        try:
-            input_f = open(path, 'rb')
-            buff = input_f.read()
-            lines = buff.split('\n')
-            input_f.close()
-            num = 0
-            for line in lines:
-                try:
-                    # check if line starts with "ATOM"
-                    if line[0:6].strip().count('ATM') > 0 or \
-                                line[0:6].strip() == 'ATOM':
-                        # define fields of interest
-                        atom_name = line[12:16].strip()
-                        try:
-                            float(line[12])
-                            atom_name = atom_name[1].upper()
-                        except:
-                            if len(atom_name) == 4:
-                                atom_name = atom_name[0].upper()
-                            elif line[12] != ' ':
-                                atom_name = atom_name[0].upper() + \
-                                        atom_name[1].lower()
-                            else:
-                                atom_name = atom_name[0].upper()
-                        _pos_x = float(line[30:38].strip())
-                        _pos_y = float(line[38:46].strip())
-                        _pos_z = float(line[46:54].strip())
-                        pos_x = numpy.append(pos_x, _pos_x)
-                        pos_y = numpy.append(pos_y, _pos_y)
-                        pos_z = numpy.append(pos_z, _pos_z)
-                        try:
-                            val = nsf.neutron_sld(atom_name)[0]
-                            # sld in Ang^-2 unit
-                            val *= 1.0e-6
-                            sld_n = numpy.append(sld_n, val)
-                            atom = formula(atom_name)
-                            # cm to A units
-                            vol = 1.0e+24 * atom.mass / atom.density / NA
-                            vol_pix = numpy.append(vol_pix, vol)
-                        except:
-                            print "Error: set the sld of %s to zero"% atom_name
-                            sld_n = numpy.append(sld_n, 0.0)
-                        sld_mx = numpy.append(sld_mx, 0)
-                        sld_my = numpy.append(sld_my, 0)
-                        sld_mz = numpy.append(sld_mz, 0)
-                        pix_symbol = numpy.append(pix_symbol, atom_name)
-                    elif line[0:6].strip().count('CONECT') > 0:
-                        toks = line.split()
-                        num = int(toks[1]) - 1
-                        val_list = []
-                        for val in toks[2:]:
-                            try:
-                                int_val = int(val)
-                            except:
-                                break
-                            if int_val == 0:
-                                break
-                            val_list.append(int_val)
-                        #need val_list ordered
-                        for val in val_list:
-                            index = val - 1
-                            if (pos_x[index], pos_x[num]) in x_line and \
-                               (pos_y[index], pos_y[num]) in y_line and \
-                               (pos_z[index], pos_z[num]) in z_line:
-                                continue
-                            x_line.append((pos_x[num], pos_x[index]))
-                            y_line.append((pos_y[num], pos_y[index]))
-                            z_line.append((pos_z[num], pos_z[index]))
-                    if len(x_line) > 0:
-                        x_lines.append(x_line)
-                        y_lines.append(y_line)
-                        z_lines.append(z_line)
-                except:
-                    logging.error(sys.exc_value)
-
-            output = MagSLD(pos_x, pos_y, pos_z, sld_n, sld_mx, sld_my, sld_mz)
-            output.set_conect_lines(x_line, y_line, z_line)
-            output.filename = os.path.basename(path)
-            output.set_pix_type('atom')
-            output.set_pixel_symbols(pix_symbol)
-            output.set_nodes()
-            output.set_pixel_volumes(vol_pix)
-            output.sld_unit = '1/A^(2)'
-            return output
-        except:
-            raise RuntimeError, "%s is not a sld file" % path
-
-    def write(self, path, data):
-        """
-        Write
-        """
-        print "Not implemented... "
-
-class SLDReader(object):
-    """
-    Class to load ascii files (7 columns).
-    """
-    ## File type
-    type_name = "SLD ASCII"
-    ## Wildcards
-    type = ["sld files (*.SLD, *.sld)|*.sld",
-            "txt files (*.TXT, *.txt)|*.txt",
-            "all files (*.*)|*.*"]
-    ## List of allowed extensions
-    ext = ['.sld', '.SLD', '.txt', '.TXT', '.*']
-    def read(self, path):
-        """
-        Load data file
-        :param path: file path
-        :return MagSLD: x, y, z, sld_n, sld_mx, sld_my, sld_mz
-        :raise RuntimeError: when the file can't be opened
-        :raise ValueError: when the length of the data vectors are inconsistent
-        """
-        try:
-            pos_x = numpy.zeros(0)
-            pos_y = numpy.zeros(0)
-            pos_z = numpy.zeros(0)
-            sld_n = numpy.zeros(0)
-            sld_mx = numpy.zeros(0)
-            sld_my = numpy.zeros(0)
-            sld_mz = numpy.zeros(0)
-            try:
-                # Use numpy to speed up loading
-                input_f = numpy.loadtxt(path, dtype='float', skiprows=1,
-                                        ndmin=1, unpack=True)
-                pos_x = numpy.array(input_f[0])
-                pos_y = numpy.array(input_f[1])
-                pos_z = numpy.array(input_f[2])
-                sld_n = numpy.array(input_f[3])
-                sld_mx = numpy.array(input_f[4])
-                sld_my = numpy.array(input_f[5])
-                sld_mz = numpy.array(input_f[6])
-                ncols = len(input_f)
-                if ncols == 8:
-                    vol_pix = numpy.array(input_f[7])
-                elif ncols == 7:
-                    vol_pix = None
-            except:
-                # For older version of numpy
-                input_f = open(path, 'rb')
-                buff = input_f.read()
-                lines = buff.split('\n')
-                input_f.close()
-                for line in lines:
-                    toks = line.split()
-                    try:
-                        _pos_x = float(toks[0])
-                        _pos_y = float(toks[1])
-                        _pos_z = float(toks[2])
-                        _sld_n = float(toks[3])
-                        _sld_mx = float(toks[4])
-                        _sld_my = float(toks[5])
-                        _sld_mz = float(toks[6])
-                        pos_x = numpy.append(pos_x, _pos_x)
-                        pos_y = numpy.append(pos_y, _pos_y)
-                        pos_z = numpy.append(pos_z, _pos_z)
-                        sld_n = numpy.append(sld_n, _sld_n)
-                        sld_mx = numpy.append(sld_mx, _sld_mx)
-                        sld_my = numpy.append(sld_my, _sld_my)
-                        sld_mz = numpy.append(sld_mz, _sld_mz)
-                        try:
-                            _vol_pix = float(toks[7])
-                            vol_pix = numpy.append(vol_pix, _vol_pix)
-                        except:
-                            vol_pix = None
-                    except:
-                        # Skip non-data lines
-                        logging.error(sys.exc_value)
-            output = MagSLD(pos_x, pos_y, pos_z, sld_n,
-                            sld_mx, sld_my, sld_mz)
-            output.filename = os.path.basename(path)
-            output.set_pix_type('pixel')
-            output.set_pixel_symbols('pixel')
-            if vol_pix != None:
-                output.set_pixel_volumes(vol_pix)
-            return output
-        except:
-            raise RuntimeError, "%s is not a sld file" % path
-
-    def write(self, path, data):
-        """
-        Write sld file
-        :Param path: file path
-        :Param data: MagSLD data object
-        """
-        if path == None:
-            raise ValueError, "Missing the file path."
-        if data == None:
-            raise ValueError, "Missing the data to save."
-        x_val = data.pos_x
-        y_val = data.pos_y
-        z_val = data.pos_z
-        vol_pix = data.vol_pix
-        length = len(x_val)
-        sld_n = data.sld_n
-        if sld_n == None:
-            sld_n = numpy.zeros(length)
-        sld_mx = data.sld_mx
-        if sld_mx == None:
-            sld_mx = numpy.zeros(length)
-            sld_my = numpy.zeros(length)
-            sld_mz = numpy.zeros(length)
-        else:
-            sld_my = data.sld_my
-            sld_mz = data.sld_mz
-        out = open(path, 'w')
-        # First Line: Column names
-        out.write("X  Y  Z  SLDN SLDMx  SLDMy  SLDMz VOLUMEpix")
-        for ind in range(length):
-            out.write("\n%g  %g  %g  %g  %g  %g  %g %g" % \
-                      (x_val[ind], y_val[ind], z_val[ind], sld_n[ind],
-                       sld_mx[ind], sld_my[ind], sld_mz[ind], vol_pix[ind]))
-        out.close()
-
-
-class OMFData(object):
-    """
-    OMF Data.
-    """
-    _meshunit = "A"
-    _valueunit = "A^(-2)"
-    def __init__(self):
-        """
-        Init for mag SLD
-        """
-        self.filename = 'default'
-        self.oommf = ''
-        self.title = ''
-        self.desc = ''
-        self.meshtype = ''
-        self.meshunit = self._meshunit
-        self.valueunit = self._valueunit
-        self.xbase = 0.0
-        self.ybase = 0.0
-        self.zbase = 0.0
-        self.xstepsize = 6.0
-        self.ystepsize = 6.0
-        self.zstepsize = 6.0
-        self.xnodes = 10.0
-        self.ynodes = 10.0
-        self.znodes = 10.0
-        self.xmin = 0.0
-        self.ymin = 0.0
-        self.zmin = 0.0
-        self.xmax = 60.0
-        self.ymax = 60.0
-        self.zmax = 60.0
-        self.mx = None
-        self.my = None
-        self.mz = None
-        self.valuemultiplier = 1.
-        self.valuerangeminmag = 0
-        self.valuerangemaxmag = 0
-
-    def __str__(self):
-        """
-        doc strings
-        """
-        _str = "Type:            %s\n" % self.__class__.__name__
-        _str += "File:            %s\n" % self.filename
-        _str += "OOMMF:           %s\n" % self.oommf
-        _str += "Title:           %s\n" % self.title
-        _str += "Desc:            %s\n" % self.desc
-        _str += "meshtype:        %s\n" % self.meshtype
-        _str += "meshunit:        %s\n" % str(self.meshunit)
-        _str += "xbase:           %s [%s]\n" % (str(self.xbase), self.meshunit)
-        _str += "ybase:           %s [%s]\n" % (str(self.ybase), self.meshunit)
-        _str += "zbase:           %s [%s]\n" % (str(self.zbase), self.meshunit)
-        _str += "xstepsize:       %s [%s]\n" % (str(self.xstepsize),
-                                                self.meshunit)
-        _str += "ystepsize:       %s [%s]\n" % (str(self.ystepsize),
-                                                self.meshunit)
-        _str += "zstepsize:       %s [%s]\n" % (str(self.zstepsize),
-                                                self.meshunit)
-        _str += "xnodes:          %s\n" % str(self.xnodes)
-        _str += "ynodes:          %s\n" % str(self.ynodes)
-        _str += "znodes:          %s\n" % str(self.znodes)
-        _str += "xmin:            %s [%s]\n" % (str(self.xmin), self.meshunit)
-        _str += "ymin:            %s [%s]\n" % (str(self.ymin), self.meshunit)
-        _str += "zmin:            %s [%s]\n" % (str(self.zmin), self.meshunit)
-        _str += "xmax:            %s [%s]\n" % (str(self.xmax), self.meshunit)
-        _str += "ymax:            %s [%s]\n" % (str(self.ymax), self.meshunit)
-        _str += "zmax:            %s [%s]\n" % (str(self.zmax), self.meshunit)
-        _str += "valueunit:       %s\n" % self.valueunit
-        _str += "valuemultiplier: %s\n" % str(self.valuemultiplier)
-        _str += "ValueRangeMinMag:%s [%s]\n" % (str(self.valuerangeminmag),
-                                                self.valueunit)
-        _str += "ValueRangeMaxMag:%s [%s]\n" % (str(self.valuerangemaxmag),
-                                                self.valueunit)
-        return _str
-
-    def set_m(self, mx, my, mz):
-        """
-        Set the Mx, My, Mz values
-        """
-        self.mx = mx
-        self.my = my
-        self.mz = mz
-
-class MagSLD(object):
-    """
-    Magnetic SLD.
-    """
-    pos_x = None
-    pos_y = None
-    pos_z = None
-    sld_n = None
-    sld_mx = None
-    sld_my = None
-    sld_mz = None
-    # Units
-    _pos_unit = 'A'
-    _sld_unit = '1/A^(2)'
-    _pix_type = 'pixel'
-
-    def __init__(self, pos_x, pos_y, pos_z, sld_n=None,
-                 sld_mx=None, sld_my=None, sld_mz=None, vol_pix=None):
-        """
-        Init for mag SLD
-        :params : All should be numpy 1D array
-        """
-        self.is_data = True
-        self.filename = ''
-        self.xstepsize = 6.0
-        self.ystepsize = 6.0
-        self.zstepsize = 6.0
-        self.xnodes = 10.0
-        self.ynodes = 10.0
-        self.znodes = 10.0
-        self.has_stepsize = False
-        self.has_conect = False
-        self.pos_unit = self._pos_unit
-        self.sld_unit = self._sld_unit
-        self.pix_type = 'pixel'
-        self.pos_x = pos_x
-        self.pos_y = pos_y
-        self.pos_z = pos_z
-        self.sld_n = sld_n
-        self.line_x = None
-        self.line_y = None
-        self.line_z = None
-        self.sld_mx = sld_mx
-        self.sld_my = sld_my
-        self.sld_mz = sld_mz
-        self.vol_pix = vol_pix
-        self.sld_m = None
-        self.sld_phi = None
-        self.sld_theta = None
-        self.pix_symbol = None
-        if sld_mx != None and sld_my != None and sld_mz != None:
-            self.set_sldms(sld_mx, sld_my, sld_mz)
-        self.set_nodes()
-
-    def __str__(self):
-        """
-        doc strings
-        """
-        _str = "Type:       %s\n" % self.__class__.__name__
-        _str += "File:       %s\n" % self.filename
-        _str += "Axis_unit:  %s\n" % self.pos_unit
-        _str += "SLD_unit:   %s\n" % self.sld_unit
-        return _str
-
-    def set_pix_type(self, pix_type):
-        """
-        Set pixel type
-        :Param pix_type: string, 'pixel' or 'atom'
-        """
-        self.pix_type = pix_type
-
-    def set_sldn(self, sld_n):
-        """
-        Sets neutron SLD
-        """
-        if sld_n.__class__.__name__ == 'float':
-            if self.is_data:
-                # For data, put the value to only the pixels w non-zero M
-                is_nonzero = (numpy.fabs(self.sld_mx) +
-                              numpy.fabs(self.sld_my) +
-                              numpy.fabs(self.sld_mz)).nonzero()
-                self.sld_n = numpy.zeros(len(self.pos_x))
-                if len(self.sld_n[is_nonzero]) > 0:
-                    self.sld_n[is_nonzero] = sld_n
-                else:
-                    self.sld_n.fill(sld_n)
-            else:
-                # For non-data, put the value to all the pixels
-                self.sld_n = numpy.ones(len(self.pos_x)) * sld_n
-        else:
-            self.sld_n = sld_n
-
-    def set_sldms(self, sld_mx, sld_my, sld_mz):
-        r"""
-        Sets (\|m\|, m_theta, m_phi)
-        """
-        if sld_mx.__class__.__name__ == 'float':
-            self.sld_mx = numpy.ones(len(self.pos_x)) * sld_mx
-        else:
-            self.sld_mx = sld_mx
-        if sld_my.__class__.__name__ == 'float':
-            self.sld_my = numpy.ones(len(self.pos_x)) * sld_my
-        else:
-            self.sld_my = sld_my
-        if sld_mz.__class__.__name__ == 'float':
-            self.sld_mz = numpy.ones(len(self.pos_x)) * sld_mz
-        else:
-            self.sld_mz = sld_mz
-
-        sld_m = numpy.sqrt(sld_mx * sld_mx + sld_my * sld_my + \
-                                sld_mz * sld_mz)
-        self.sld_m = sld_m
-
-    def set_pixel_symbols(self, symbol='pixel'):
-        """
-        Set pixel
-        :Params pixel: str; pixel or atomic symbol, or array of strings
-        """
-        if self.sld_n == None:
-            return
-        if symbol.__class__.__name__ == 'str':
-            self.pix_symbol = numpy.repeat(symbol, len(self.sld_n))
-        else:
-            self.pix_symbol = symbol
-
-    def set_pixel_volumes(self, vol):
-        """
-        Set pixel volumes
-        :Params pixel: str; pixel or atomic symbol, or array of strings
-        """
-        if self.sld_n == None:
-            return
-        if vol.__class__.__name__ == 'ndarray':
-            self.vol_pix = vol
-        elif vol.__class__.__name__.count('float') > 0:
-            self.vol_pix = numpy.repeat(vol, len(self.sld_n))
-        else:
-            self.vol_pix = None
-
-    def get_sldn(self):
-        """
-        Returns nuclear sld
-        """
-        return self.sld_n
-
-    def set_nodes(self):
-        """
-        Set xnodes, ynodes, and znodes
-        """
-        self.set_stepsize()
-        if self.pix_type == 'pixel':
-            try:
-                xdist = (max(self.pos_x) - min(self.pos_x)) / self.xstepsize
-                ydist = (max(self.pos_y) - min(self.pos_y)) / self.ystepsize
-                zdist = (max(self.pos_z) - min(self.pos_z)) / self.zstepsize
-                self.xnodes = int(xdist) + 1
-                self.ynodes = int(ydist) + 1
-                self.znodes = int(zdist) + 1
-            except:
-                self.xnodes = None
-                self.ynodes = None
-                self.znodes = None
-        else:
-            self.xnodes = None
-            self.ynodes = None
-            self.znodes = None
-
-    def set_stepsize(self):
-        """
-        Set xtepsize, ystepsize, and zstepsize
-        """
-        if self.pix_type == 'pixel':
-            try:
-                xpos_pre = self.pos_x[0]
-                ypos_pre = self.pos_y[0]
-                zpos_pre = self.pos_z[0]
-                for x_pos in self.pos_x:
-                    if xpos_pre != x_pos:
-                        self.xstepsize = numpy.fabs(x_pos - xpos_pre)
-                        break
-                for y_pos in self.pos_y:
-                    if ypos_pre != y_pos:
-                        self.ystepsize = numpy.fabs(y_pos - ypos_pre)
-                        break
-                for z_pos in self.pos_z:
-                    if zpos_pre != z_pos:
-                        self.zstepsize = numpy.fabs(z_pos - zpos_pre)
-                        break
-                #default pix volume
-                self.vol_pix = numpy.ones(len(self.pos_x))
-                vol = self.xstepsize * self.ystepsize * self.zstepsize
-                self.set_pixel_volumes(vol)
-                self.has_stepsize = True
-            except:
-                self.xstepsize = None
-                self.ystepsize = None
-                self.zstepsize = None
-                self.vol_pix = None
-                self.has_stepsize = False
-        else:
-            self.xstepsize = None
-            self.ystepsize = None
-            self.zstepsize = None
-            self.has_stepsize = True
-        return self.xstepsize, self.ystepsize, self.zstepsize
-
-    def set_conect_lines(self, line_x, line_y, line_z):
-        """
-        Set bonding line data if taken from pdb
-        """
-        if line_x.__class__.__name__ != 'list' or len(line_x) < 1:
-            return
-        if line_y.__class__.__name__ != 'list' or len(line_y) < 1:
-            return
-        if line_z.__class__.__name__ != 'list' or len(line_z) < 1:
-            return
-        self.has_conect = True
-        self.line_x = line_x
-        self.line_y = line_y
-        self.line_z = line_z
-
-def test_load():
-    """
-        Test code
-    """
-    from sas.plottools.arrow3d import Arrow3D
-    current_dir = os.path.abspath(os.path.curdir)
-    print current_dir
-    for i in range(6):
-        current_dir, _ = os.path.split(current_dir)
-        tfile = os.path.join(current_dir, "test", "CoreXY_ShellZ.txt")
-        ofile = os.path.join(current_dir, "test", "A_Raw_Example-1.omf")
-        if os.path.isfile(tfile):
-            tfpath = tfile
-            ofpath = ofile
-            break
-    reader = SLDReader()
-    oreader = OMFReader()
-    output = reader.read(tfpath)
-    ooutput = oreader.read(ofpath)
-    foutput = OMF2SLD()
-    foutput.set_data(ooutput)
-
-    import matplotlib.pyplot as plt
-    fig = plt.figure()
-    ax = fig.gca(projection='3d')
-    ax.plot(output.pos_x, output.pos_y, output.pos_z, '.', c="g",
-            alpha=0.7, markeredgecolor='gray', rasterized=True)
-    gap = 7
-    max_mx = max(output.sld_mx)
-    max_my = max(output.sld_my)
-    max_mz = max(output.sld_mz)
-    max_m = max(max_mx, max_my, max_mz)
-    x2 = output.pos_x+output.sld_mx/max_m * gap
-    y2 = output.pos_y+output.sld_my/max_m * gap
-    z2 = output.pos_z+output.sld_mz/max_m * gap
-    x_arrow = numpy.column_stack((output.pos_x, x2))
-    y_arrow = numpy.column_stack((output.pos_y, y2))
-    z_arrow = numpy.column_stack((output.pos_z, z2))
-    unit_x2 = output.sld_mx / max_m
-    unit_y2 = output.sld_my / max_m
-    unit_z2 = output.sld_mz / max_m
-    color_x = numpy.fabs(unit_x2 * 0.8)
-    color_y = numpy.fabs(unit_y2 * 0.8)
-    color_z = numpy.fabs(unit_z2 * 0.8)
-    colors = numpy.column_stack((color_x, color_y, color_z))
-    a = Arrow3D(None, x_arrow, y_arrow, z_arrow, colors,
-                mutation_scale=10, lw=1, arrowstyle="->",
-                color="y", alpha=0.5)
-    ax.add_artist(a)
-    plt.show()
-
-def test():
-    """
-        Test code
-    """
-    current_dir = os.path.abspath(os.path.curdir)
-    for i in range(3):
-        current_dir, _ = os.path.split(current_dir)
-        ofile = os.path.join(current_dir, "test", "A_Raw_Example-1.omf")
-        if os.path.isfile(ofile):
-            ofpath = ofile
-            break
-    oreader = OMFReader()
-    ooutput = oreader.read(ofpath)
-    foutput = OMF2SLD()
-    foutput.set_data(ooutput)
-    writer = SLDReader()
-    writer.write(os.path.join(os.path.dirname(ofpath), "out.txt"),
-                 foutput.output)
-    model = GenSAS()
-    model.set_sld_data(foutput.output)
-    x = numpy.arange(1000)/10000. + 1e-5
-    y = numpy.arange(1000)/10000. + 1e-5
-    i = numpy.zeros(1000)
-    model.runXY([x, y, i])
-
-if __name__ == "__main__":
-    test()
-    test_load()
Index: c/sas/calculator/slit_length_calculator.py
===================================================================
--- src/sas/calculator/slit_length_calculator.py	(revision b6627d95d435a7f25f17862e4ac78ae26bdb6393)
+++ 	(revision )
@@ -1,108 +1,0 @@
-"""
-This module is a small tool to allow user to quickly
-determine the slit length value of data.
-"""
-
-
-class SlitlengthCalculator(object):
-    """
-    compute slit length from SAXSess beam profile (1st col. Q , 2nd col. I ,
-    and 3rd col. dI.: don't need the 3rd)
-    """
-    def __init__(self):
-
-        # x data
-        self.x = None
-        # y data
-        self.y = None
-        #default slit length
-        self.slit_length = 0.0
-
-        # The unit is unknown from SAXSess profile:
-        # It seems 1/nm but it could be not fixed,
-        # so users should be notified to determine the unit by themselves.
-        self.slit_length_unit = "unknown"
-
-    def set_data(self, x=None, y=None):
-        """
-         Receive two vector x, y and prepare the slit calculator for
-         computation.
-
-        :param x: array
-        :param y: array
-        """
-        self.x = x
-        self.y = y
-
-    def calculate_slit_length(self):
-        """
-        Calculate slit length.
-
-        :return: the slit length calculated value.
-        """
-        # None data do nothing
-        if self.y == None or self.x == None:
-            return
-        # set local variable
-        y = self.y
-        x = self.x
-
-        # find max y
-        max_y = y.max()
-
-        # initial values
-        y_sum = 0.0
-        y_max = 0.0
-        ind = 0.0
-
-        # sum 10 or more y values until getting max_y,
-        while True:
-            if ind >= 10 and y_max == max_y:
-                break
-            y_sum = y_sum + y[ind]
-            if y[ind] > y_max:
-                y_max = y[ind]
-            ind += 1
-
-        # find the average value/2 of the top values
-        y_half = y_sum/(2.0*ind)
-
-        # defaults
-        y_half_d = 0.0
-        ind = 0.0
-        # find indices where it crosses y = y_half.
-        while True:
-            # no need to check when ind == 0
-            ind += 1
-            # y value and ind just after passed the spot of the half height
-            y_half_d = y[ind]
-            if y[ind] < y_half:
-                break
-
-        # y value and ind just before passed the spot of the half height
-        y_half_u = y[ind-1]
-
-        # get corresponding x values
-        x_half_d = x[ind]
-        x_half_u = x[ind-1]
-
-        # calculate x at y = y_half using linear interpolation
-        if y_half_u == y_half_d:
-            x_half = (x_half_d + x_half_u)/2.0
-        else:
-            x_half = (x_half_u * (y_half - y_half_d)  \
-                       + x_half_d * (y_half_u - y_half)) \
-                        / (y_half_u - y_half_d)
-
-        # Our slit length is half width, so just give half beam value
-        slit_length = x_half
-
-        # set slit_length
-        self.slit_length = slit_length
-        return self.slit_length
-
-    def get_slit_length_unit(self):
-        """
-        :return: the slit length unit.
-        """
-        return self.slit_length_unit
Index: c/sas/data_util/calcthread.py
===================================================================
--- src/sas/data_util/calcthread.py	(revision 373d4eebe3a548f70f917fec87bdb5280e67d1e0)
+++ 	(revision )
@@ -1,305 +1,0 @@
-# This program is public domain
-
-## \file
-#  \brief Abstract class for defining calculation threads.
-#
-
-import thread
-import traceback
-import sys
-
-if sys.platform.count("darwin") > 0:
-    import time
-    stime = time.time()
-    
-    def clock():
-        return time.time() - stime
-    
-    def sleep(t):
-        return time.sleep(t)
-else:
-    from time import clock
-    from time import sleep
-
-
-class CalcThread:
-    """Threaded calculation class.  Inherit from here and specialize
-    the compute() method to perform the appropriate operations for the
-    class.
-
-    If you specialize the __init__ method be sure to call
-    CalcThread.__init__, passing it the keyword arguments for
-    yieldtime, worktime, update and complete.
-    
-    When defining the compute() method you need to include code which
-    allows the GUI to run.  They are as follows: ::
-
-        self.isquit()          # call frequently to check for interrupts
-        self.update(kw=...)    # call when the GUI could be updated
-        self.complete(kw=...)  # call before exiting compute()
-
-    The update() and complete() calls accept field=value keyword
-    arguments which are passed to the called function.  complete()
-    should be called before exiting the GUI function.  A KeyboardInterrupt
-    event is triggered if the GUI signals that the computation should
-    be halted.
-
-    The following documentation should be included in the description
-    of the derived class.
-
-    The user of this class will call the following: ::
-
-        thread = Work(...,kw=...)  # prepare the work thread.
-        thread.queue(...,kw=...)   # queue a work unit
-        thread.requeue(...,kw=...) # replace work unit on the end of queue
-        thread.reset(...,kw=...)   # reset the queue to the given work unit
-        thread.stop()              # clear the queue and halt
-        thread.interrupt()         # halt the current work unit but continue
-        thread.ready(delay=0.)     # request an update signal after delay
-        thread.isrunning()         # returns true if compute() is running
-
-    Use queue() when all work must be done.  Use requeue() when intermediate
-    work items don't need to be done (e.g., in response to a mouse move
-    event).  Use reset() when the current item doesn't need to be completed
-    before the new event (e.g., in response to a mouse release event).  Use
-    stop() to halt the current and pending computations (e.g., in response to
-    a stop button).
-
-    The methods queue(), requeue() and reset() are proxies for the compute()
-    method in the subclass.  Look there for a description of the arguments.
-    The compute() method can be called directly to run the computation in
-    the main thread, but it should not be called if isrunning() returns true.
-
-    The constructor accepts additional keywords yieldtime=0.01 and
-    worktime=0.01 which determine the cooperative multitasking
-    behaviour.  Yield time is the duration of the sleep period
-    required to give other processes a chance to run.  Work time
-    is the duration between sleep periods.
-
-    Notifying the GUI thread of work in progress and work complete
-    is done with updatefn=updatefn and completefn=completefn arguments
-    to the constructor.  Details of the parameters to the functions
-    depend on the particular calculation class, but they will all
-    be passed as keyword arguments.  Details of how the functions
-    should be implemented vary from framework to framework.
-
-    For wx, something like the following is needed::
-
-        import wx, wx.lib.newevent
-        (CalcCompleteEvent, EVT_CALC_COMPLETE) = wx.lib.newevent.NewEvent()
-
-        # methods in the main window class of your application
-        def __init__():
-            ...
-            # Prepare the calculation in the GUI thread.
-            self.work = Work(completefn=self.CalcComplete)
-            self.Bind(EVT_CALC_COMPLETE, self.OnCalcComplete)
-            ...
-            # Bind work queue to a menu event.
-            self.Bind(wx.EVT_MENU, self.OnCalcStart, id=idCALCSTART)
-            ...
-
-        def OnCalcStart(self,event):
-            # Start the work thread from the GUI thread.
-            self.work.queue(...work unit parameters...)
-
-        def CalcComplete(self,**kwargs):
-            # Generate CalcComplete event in the calculation thread.
-            # kwargs contains field1, field2, etc. as defined by
-            # the Work thread class.
-            event = CalcCompleteEvent(**kwargs)
-            wx.PostEvent(self, event)
-
-        def OnCalcComplete(self,event):
-            # Process CalcComplete event in GUI thread.
-            # Use values from event.field1, event.field2 etc. as
-            # defined by the Work thread class to show the results.
-            ...
-    """
-
-    def __init__(self, completefn=None, updatefn=None,
-                 yieldtime=0.01, worktime=0.01):
-        """Prepare the calculator"""
-        self.yieldtime     = yieldtime
-        self.worktime      = worktime
-        self.completefn    = completefn
-        self.updatefn      = updatefn
-        self._interrupting = False
-        self._running      = False
-        self._queue        = []
-        self._lock         = thread.allocate_lock()
-        self._delay        = 1e6
-
-    def queue(self,*args,**kwargs):
-        """Add a work unit to the end of the queue.  See the compute()
-        method for details of the arguments to the work unit."""
-        self._lock.acquire()
-        self._queue.append((args, kwargs))
-        # Cannot do start_new_thread call within the lock
-        self._lock.release()
-        if not self._running:
-            self._time_for_update = clock() + 1e6
-            thread.start_new_thread(self._run, ())
-
-    def requeue(self, *args, **kwargs):
-        """Replace the work unit on the end of the queue.  See the compute()
-        method for details of the arguments to the work unit."""
-        self._lock.acquire()
-        self._queue = self._queue[:-1]
-        self._lock.release()
-        self.queue(*args, **kwargs)
-
-    def reset(self, *args, **kwargs):
-        """Clear the queue and start a new work unit.  See the compute()
-        method for details of the arguments to the work unit."""
-        self.stop()
-        self.queue(*args, **kwargs)
-
-    def stop(self):
-        """Clear the queue and stop the thread.  New items may be
-        queued after stop.  To stop just the current work item, and
-        continue the rest of the queue call the interrupt method"""
-        self._lock.acquire()
-        self._interrupting = True
-        self._queue = []
-        self._lock.release()
-
-    def interrupt(self):
-        """Stop the current work item.  To clear the work queue as
-        well call the stop() method."""
-        self._lock.acquire()
-        self._interrupting = True
-        self._lock.release()
-
-    def isrunning(self):
-        return self._running
-
-    def ready(self, delay=0.):
-        """Ready for another update after delay=t seconds.  Call
-        this for threads which can show intermediate results from
-        long calculations."""
-        self._delay = delay
-        self._lock.acquire()
-        self._time_for_update = clock() + delay
-        # print "setting _time_for_update to ",self._time_for_update
-        self._lock.release()
-
-    def isquit(self):
-        """Check for interrupts.  Should be called frequently to
-        provide user responsiveness.  Also yields to other running
-        threads, which is required for good performance on OS X."""
-
-        # Only called from within the running thread so no need to lock
-        if self._running and self.yieldtime > 0 \
-            and clock() > self._time_for_nap:
-            sleep(self.yieldtime)
-            self._time_for_nap = clock() + self.worktime
-        if self._interrupting:
-            raise KeyboardInterrupt
-
-    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:
-            self._lock.acquire()
-            self._time_for_update = clock() + self._delay
-            self._lock.release()
-            self._time_for_update += 1e6  # No more updates
-            
-            self.updatefn(**kwargs)
-            sleep(self.yieldtime)
-            if self._interrupting:
-                raise KeyboardInterrupt
-        else:
-            self.isquit()
-        return
-
-    def complete(self, **kwargs):
-        """Update the GUI with the completed results from a work unit."""
-        if self.completefn != None:
-            self.completefn(**kwargs)
-            sleep(self.yieldtime)
-        return
-
-    def compute(self, *args, **kwargs):
-        """Perform a work unit.  The subclass will provide details of
-        the arguments."""
-        raise NotImplemented, "Calculation thread needs compute method"
-
-    def _run(self):
-        """Internal function to manage the thread."""
-        # The code for condition wait in the threading package is
-        # implemented using polling.  I'll accept for now that the
-        # authors of this code are clever enough that polling is
-        # difficult to avoid.  Rather than polling, I will exit the
-        # thread when the queue is empty and start a new thread when
-        # there is more work to be done.
-        while 1:
-            self._lock.acquire()
-            self._time_for_nap = clock() + self.worktime
-            self._running = True
-            if self._queue == []:
-                break
-            self._interrupting = False
-            args, kwargs = self._queue[0]
-            self._queue = self._queue[1:]
-            self._lock.release()
-            try:
-                self.compute(*args, **kwargs)
-            except KeyboardInterrupt:
-                pass
-            except:
-                traceback.print_exc()
-                #print 'CalcThread exception',
-        self._running = False
-
-
-# ======================================================================
-# Demonstration of calcthread in action
-class CalcDemo(CalcThread):
-    """Example of a calculation thread."""
-    def compute(self, n):
-        total = 0.
-        for i in range(n):
-            self.update(i=i)
-            for j in range(n):
-                self.isquit()
-                total += j
-        self.complete(total=total)
-
-
-class CalcCommandline:
-    """
-        Test method
-    """
-    def __init__(self, n=20000):
-        print thread.get_ident()
-        self.starttime = clock()
-        self.done = False
-        self.work = CalcDemo(completefn=self.complete,
-                             updatefn=self.update, yieldtime=0.001)
-        self.work2 = CalcDemo(completefn=self.complete,
-                             updatefn=self.update)
-        self.work3 = CalcDemo(completefn=self.complete,
-                             updatefn=self.update)
-        self.work.queue(n)
-        self.work2.queue(n)
-        self.work3.queue(n)
-        print "Expect updates from Main every second and from thread every 2.5 seconds"
-        print ""
-        self.work.ready(.5)
-        while not self.done:
-            sleep(1)
-            print "Main thread %d at %.2f" % (thread.get_ident(),
-                                              clock() - self.starttime)
-
-    def update(self, i=0):
-        print "Update i=%d from thread %d at %.2f" % (i, thread.get_ident(),
-                                                      clock() - self.starttime)
-        self.work.ready(2.5)
-
-    def complete(self, total=0.0):
-        print "Complete total=%g from thread %d at %.2f" % (total,
-                                                    thread.get_ident(),
-                                                    clock() - self.starttime)
-        self.done = True
Index: c/sas/data_util/err1d.py
===================================================================
--- src/sas/data_util/err1d.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,155 +1,0 @@
-# This program is public domain
-"""
-Error propogation algorithms for simple arithmetic
-
-Warning: like the underlying numpy library, the inplace operations
-may return values of the wrong type if some of the arguments are
-integers, so be sure to create them with floating point inputs.
-"""
-from __future__ import division  # Get true division
-import numpy
-
-
-def div(X, varX, Y, varY):
-    """Division with error propagation"""
-    # Direct algorithm:
-    #   Z = X/Y
-    #   varZ = (varX/X**2 + varY/Y**2) * Z**2
-    #        = (varX + varY * Z**2) / Y**2
-    # Indirect algorithm to minimize intermediates
-    Z = X/Y      # truediv => Z is a float
-    varZ = Z**2  # Z is a float => varZ is a float
-    varZ *= varY
-    varZ += varX
-    T = Y**2     # Doesn't matter if T is float or int
-    varZ /= T
-    return Z, varZ
-
-
-def mul(X, varX, Y, varY):
-    """Multiplication with error propagation"""
-    # Direct algorithm:
-    Z = X * Y
-    varZ = Y**2 * varX + X**2 * varY
-    # Indirect algorithm won't ensure floating point results
-    #   varZ = Y**2
-    #   varZ *= varX
-    #   Z = X**2   # Using Z to hold the temporary
-    #   Z *= varY
-    #   varZ += Z
-    #   Z[:] = X
-    #   Z *= Y
-    return Z, varZ
-
-
-def sub(X, varX, Y, varY):
-    """Subtraction with error propagation"""
-    Z = X - Y
-    varZ = varX + varY
-    return Z, varZ
-
-
-def add(X, varX, Y, varY):
-    """Addition with error propagation"""
-    Z = X + Y
-    varZ = varX + varY
-    return Z, varZ
-
-
-def exp(X, varX):
-    """Exponentiation with error propagation"""
-    Z = numpy.exp(X)
-    varZ = varX * Z**2
-    return Z, varZ
-
-
-def log(X, varX):
-    """Logarithm with error propagation"""
-    Z = numpy.log(X)
-    varZ = varX / X**2
-    return Z, varZ
-
-# Confirm this formula before using it
-# def pow(X,varX, Y,varY):
-#    Z = X**Y
-#    varZ = (Y**2 * varX/X**2 + varY * numpy.log(X)**2) * Z**2
-#    return Z,varZ
-#
-
-
-def pow(X, varX, n):
-    """X**n with error propagation"""
-    # Direct algorithm
-    #   Z = X**n
-    #   varZ = n*n * varX/X**2 * Z**2
-    # Indirect algorithm to minimize intermediates
-    Z = X**n
-    varZ = varX / X
-    varZ /= X
-    varZ *= Z
-    varZ *= Z
-    varZ *= n**2
-    return Z, varZ
-
-
-def div_inplace(X, varX, Y, varY):
-    """In-place division with error propagation"""
-    # Z = X/Y
-    # varZ = (varX + varY * (X/Y)**2) / Y**2 = (varX + varY * Z**2) / Y**2
-    X /= Y     # X now has Z = X/Y
-    T = X**2   # create T with Z**2
-    T *= varY  # T now has varY * Z**2
-    varX += T  # varX now has varX + varY*Z**2
-    del T   # may want to use T[:] = Y for vectors
-    T = Y   # reuse T for Y
-    T **= 2     # T now has Y**2
-    varX /= T  # varX now has varZ
-    return X, varX
-
-
-def mul_inplace(X, varX, Y, varY):
-    """In-place multiplication with error propagation"""
-    # Z = X * Y
-    # varZ = Y**2 * varX + X**2 * varY
-    T = Y**2   # create T with Y**2
-    varX *= T  # varX now has Y**2 * varX
-    del T   # may want to use T[:] = X for vectors
-    T = X   # reuse T for X**2 * varY
-    T **=2     # T now has X**2
-    T *= varY  # T now has X**2 * varY
-    varX += T  # varX now has varZ
-    X *= Y     # X now has Z
-    return X, varX
-
-
-def sub_inplace(X, varX, Y, varY):
-    """In-place subtraction with error propagation"""
-    # Z = X - Y
-    # varZ = varX + varY
-    X -= Y
-    varX += varY
-    return X, varX
-
-
-def add_inplace(X, varX, Y, varY):
-    """In-place addition with error propagation"""
-    # Z = X + Y
-    # varZ = varX + varY
-    X += Y
-    varX += varY
-    return X, varX
-
-
-def pow_inplace(X, varX, n):
-    """In-place X**n with error propagation"""
-    # Direct algorithm
-    #   Z = X**n
-    #   varZ = abs(n) * varX/X**2 * Z**2
-    # Indirect algorithm to minimize intermediates
-    varX /= X
-    varX /= X     # varX now has varX/X**2
-    X **= n       # X now has Z = X**n
-    varX *= X
-    varX *= X     # varX now has varX/X**2 * Z**2
-    varX *= n**2  # varX now has varZ
-    return X, varX
Index: c/sas/data_util/formatnum.py
===================================================================
--- src/sas/data_util/formatnum.py	(revision ac7be54ccf932e813cbe6e41e05b682a1630a03e)
+++ 	(revision )
@@ -1,441 +1,0 @@
-# This program is public domain
-# Author: Paul Kienzle
-"""
-Format values and uncertainties nicely for printing.
-
-:func:`format_uncertainty_pm` produces the expanded format v +/- err.
-
-:func:`format_uncertainty_compact` produces the compact format v(##),
-where the number in parenthesis is the uncertainty in the last two digits of v.
-
-:func:`format_uncertainty` uses the compact format by default, but this
-can be changed to use the expanded +/- format by setting 
-format_uncertainty.compact to False.
-    
-The formatted string uses only the number of digits warranted by
-the uncertainty in the measurement.
-
-If the uncertainty is 0 or not otherwise provided, the simple
-%g floating point format option is used.
-
-Infinite and indefinite numbers are represented as inf and NaN.
-
-Example::
-
-    >>> v,dv = 757.2356,0.01032
-    >>> print format_uncertainty_pm(v,dv)
-    757.236 +/- 0.010
-    >>> print format_uncertainty_compact(v,dv)
-    757.236(10)
-    >>> print format_uncertainty(v,dv)
-    757.236(10)
-    >>> format_uncertainty.compact = False
-    >>> print format_uncertainty(v,dv)
-    757.236 +/- 0.010
-
-UncertaintyFormatter() returns a private formatter with its own
-formatter.compact flag.
-"""
-from __future__ import division
-
-import math
-import numpy
-__all__ = ['format_uncertainty', 'format_uncertainty_pm',
-           'format_uncertainty_compact']
-
-# Coordinating scales across a set of numbers is not supported.  For easy
-# comparison a set of numbers should be shown in the same scale.  One could
-# force this from the outside by adding scale parameter (either 10**n, n, or
-# a string representing the desired SI prefix) and having a separate routine
-# which computes the scale given a set of values.
-
-# Coordinating scales with units offers its own problems.  Again, the user
-# may want to force particular units.  This can be done by outside of the
-# formatting routines by scaling the numbers to the appropriate units then
-# forcing them to print with scale 10**0.  If this is a common operation,
-# however, it may want to happen inside.
-
-# The value e<n> is currently formatted into the number.  Alternatively this
-# scale factor could be returned so that the user can choose the appropriate
-# SI prefix when printing the units.  This gets tricky when talking about
-# composite units such as 2.3e-3 m**2 -> 2300 mm**2, and with volumes
-# such as 1 g/cm**3 -> 1 kg/L.
-
-
-def format_uncertainty_pm(value, uncertainty):
-    """
-    Given *value* v and *uncertainty* dv, return a string v +/- dv.
-    """
-    return _format_uncertainty(value, uncertainty, compact=False)
-
-
-def format_uncertainty_compact(value, uncertainty):
-    """
-    Given *value* v and *uncertainty* dv, return the compact
-    representation v(##), where ## are the first two digits of
-    the uncertainty.
-    """
-    return _format_uncertainty(value, uncertainty, compact=True)
-
-
-class UncertaintyFormatter:
-    """
-    Value and uncertainty formatter.
-
-    The *formatter* instance will use either the expanded v +/- dv form
-    or the compact v(##) form depending on whether *formatter.compact* is
-    True or False.  The default is True.
-    """
-    compact = True
-    
-    def __call__(self, value, uncertainty):
-        """
-        Given *value* and *uncertainty*, return a string representation.
-        """
-        return _format_uncertainty(value, uncertainty, self.compact)
-format_uncertainty = UncertaintyFormatter()
-
-
-def _format_uncertainty(value, uncertainty, compact):
-    """
-    Implementation of both the compact and the +/- formats.
-    """
-    # Handle indefinite value
-    if numpy.isinf(value):
-        return "inf" if value > 0 else "-inf"
-    if numpy.isnan(value):
-        return "NaN"
-
-    # Handle indefinite uncertainty
-    if uncertainty is None or uncertainty <= 0 or numpy.isnan(uncertainty):
-        return "%g" % value
-    if numpy.isinf(uncertainty):
-        if compact:
-            return "%.2g(inf)" % value
-        else:
-            return "%.2g +/- inf" % value
-
-    # Handle zero and negative values
-    sign = "-" if value < 0 else ""
-    value = abs(value)
-
-    # Determine scale of value and error
-    err_place = int(math.floor(math.log10(uncertainty)))
-    if value == 0:
-        val_place = err_place - 1
-    else:
-        val_place = int(math.floor(math.log10(value)))
-
-    if err_place > val_place:
-        # Degenerate case: error bigger than value
-        # The mantissa is 0.#(##)e#, 0.0#(##)e# or 0.00#(##)e#
-        val_place = err_place + 2
-    elif err_place == val_place:
-        # Degenerate case: error and value the same order of magnitude
-        # The value is ##(##)e#, #.#(##)e# or 0.##(##)e#
-        val_place = err_place + 1
-    elif err_place <= 1 and val_place >= -3:
-        # Normal case: nice numbers and errors
-        # The value is ###.###(##)
-        val_place = 0
-    else:
-        # Extreme cases: zeros before value or after error
-        # The value is ###.###(##)e#, ##.####(##)e# or #.#####(##)e#
-        pass
-    
-    # Force engineering notation, with exponent a multiple of 3
-    val_place = int(math.floor(val_place / 3.)) * 3
-
-    # Format the result
-    digits_after_decimal = abs(val_place - err_place + 1)
-    val_str = "%.*f" % (digits_after_decimal, value / 10.**val_place)
-    exp_str = "e%d" % val_place if val_place != 0 else ""
-    if compact:
-        err_str = "(%2d)" % int(uncertainty / 10.**(err_place - 1) + 0.5)
-        result = "".join((sign, val_str, err_str, exp_str))
-    else:
-        err_str = "%.*f" % (digits_after_decimal, uncertainty / 10.**val_place)
-        result = "".join((sign, val_str, exp_str + " +/- ", err_str, exp_str))
-    return result
-
-
-def test_compact():
-    # Oops... renamed function after writing tests
-    value_str = format_uncertainty_compact
-
-    # val_place > err_place
-    assert value_str(1235670,766000) == "1.24(77)e6"
-    assert value_str(123567.,76600) == "124(77)e3"
-    assert value_str(12356.7,7660) == "12.4(77)e3"
-    assert value_str(1235.67,766) == "1.24(77)e3"
-    assert value_str(123.567,76.6) == "124(77)"
-    assert value_str(12.3567,7.66) == "12.4(77)"
-    assert value_str(1.23567,.766) == "1.24(77)"
-    assert value_str(.123567,.0766) == "0.124(77)"
-    assert value_str(.0123567,.00766) == "0.0124(77)"
-    assert value_str(.00123567,.000766) == "0.00124(77)"
-    assert value_str(.000123567,.0000766) == "124(77)e-6"
-    assert value_str(.0000123567,.00000766) == "12.4(77)e-6"
-    assert value_str(.00000123567,.000000766) == "1.24(77)e-6"
-    assert value_str(.000000123567,.0000000766) == "124(77)e-9"
-    assert value_str(.00000123567,.0000000766) == "1.236(77)e-6"
-    assert value_str(.0000123567,.0000000766) == "12.357(77)e-6"
-    assert value_str(.000123567,.0000000766) == "123.567(77)e-6"
-    assert value_str(.00123567,.000000766) == "0.00123567(77)"
-    assert value_str(.0123567,.00000766) == "0.0123567(77)"
-    assert value_str(.123567,.0000766) == "0.123567(77)"
-    assert value_str(1.23567,.000766) == "1.23567(77)"
-    assert value_str(12.3567,.00766) == "12.3567(77)"
-    assert value_str(123.567,.0764) == "123.567(76)"
-    assert value_str(1235.67,.764) == "1235.67(76)"
-    assert value_str(12356.7,7.64) == "12356.7(76)"
-    assert value_str(123567,76.4) == "123567(76)"
-    assert value_str(1235670,764) == "1.23567(76)e6"
-    assert value_str(12356700,764) == "12.35670(76)e6"
-    assert value_str(123567000,764) == "123.56700(76)e6"
-    assert value_str(123567000,7640) == "123.5670(76)e6"
-    assert value_str(1235670000,76400) == "1.235670(76)e9"
-
-    # val_place == err_place
-    assert value_str(123567,764000) == "0.12(76)e6"
-    assert value_str(12356.7,76400) == "12(76)e3"
-    assert value_str(1235.67,7640) == "1.2(76)e3"
-    assert value_str(123.567,764) == "0.12(76)e3"
-    assert value_str(12.3567,76.4) == "12(76)"
-    assert value_str(1.23567,7.64) == "1.2(76)"
-    assert value_str(.123567,.764) == "0.12(76)"
-    assert value_str(.0123567,.0764) == "12(76)e-3"
-    assert value_str(.00123567,.00764) == "1.2(76)e-3"
-    assert value_str(.000123567,.000764) == "0.12(76)e-3"
-
-    # val_place == err_place-1
-    assert value_str(123567,7640000) == "0.1(76)e6"
-    assert value_str(12356.7,764000) == "0.01(76)e6"
-    assert value_str(1235.67,76400) == "0.001(76)e6"
-    assert value_str(123.567,7640) == "0.1(76)e3"
-    assert value_str(12.3567,764) == "0.01(76)e3"
-    assert value_str(1.23567,76.4) == "0.001(76)e3"
-    assert value_str(.123567,7.64) == "0.1(76)"
-    assert value_str(.0123567,.764) == "0.01(76)"
-    assert value_str(.00123567,.0764) == "0.001(76)"
-    assert value_str(.000123567,.00764) == "0.1(76)e-3"
-
-    # val_place == err_place-2
-    assert value_str(12356700,7640000000) == "0.0(76)e9"
-    assert value_str(1235670,764000000) == "0.00(76)e9"
-    assert value_str(123567,76400000) == "0.000(76)e9"
-    assert value_str(12356,7640000) == "0.0(76)e6"
-    assert value_str(1235,764000) == "0.00(76)e6"
-    assert value_str(123,76400) == "0.000(76)e6"
-    assert value_str(12,7640) == "0.0(76)e3"
-    assert value_str(1,764) == "0.00(76)e3"
-    assert value_str(0.1,76.4) == "0.000(76)e3"
-    assert value_str(0.01,7.64) == "0.0(76)"
-    assert value_str(0.001,0.764) == "0.00(76)"
-    assert value_str(0.0001,0.0764) == "0.000(76)"
-    assert value_str(0.00001,0.00764) == "0.0(76)e-3"
-
-    # val_place == err_place-3
-    assert value_str(12356700,76400000000) == "0.000(76)e12"
-    assert value_str(1235670,7640000000) == "0.0(76)e9"
-    assert value_str(123567,764000000) == "0.00(76)e9"
-    assert value_str(12356,76400000) == "0.000(76)e9"
-    assert value_str(1235,7640000) == "0.0(76)e6"
-    assert value_str(123,764000) == "0.00(76)e6"
-    assert value_str(12,76400) == "0.000(76)e6"
-    assert value_str(1,7640) == "0.0(76)e3"
-    assert value_str(0.1,764) == "0.00(76)e3"
-    assert value_str(0.01,76.4) == "0.000(76)e3"
-    assert value_str(0.001,7.64) == "0.0(76)"
-    assert value_str(0.0001,0.764) == "0.00(76)"
-    assert value_str(0.00001,0.0764) == "0.000(76)"
-    assert value_str(0.000001,0.00764) == "0.0(76)e-3"
-
-    # Zero values
-    assert value_str(0,7640000) == "0.0(76)e6"
-    assert value_str(0, 764000) == "0.00(76)e6"
-    assert value_str(0,  76400) == "0.000(76)e6"
-    assert value_str(0,   7640) == "0.0(76)e3"
-    assert value_str(0,    764) == "0.00(76)e3"
-    assert value_str(0,     76.4) == "0.000(76)e3"
-    assert value_str(0,      7.64) == "0.0(76)"
-    assert value_str(0,      0.764) == "0.00(76)"
-    assert value_str(0,      0.0764) == "0.000(76)"
-    assert value_str(0,      0.00764) == "0.0(76)e-3"
-    assert value_str(0,      0.000764) == "0.00(76)e-3"
-    assert value_str(0,      0.0000764) == "0.000(76)e-3"
-
-    # negative values
-    assert value_str(-1235670,765000) == "-1.24(77)e6"
-    assert value_str(-1.23567,.766) == "-1.24(77)"
-    assert value_str(-.00000123567,.0000000766) == "-1.236(77)e-6"
-    assert value_str(-12356.7,7.64) == "-12356.7(76)"
-    assert value_str(-123.567,764) == "-0.12(76)e3"
-    assert value_str(-1235.67,76400) == "-0.001(76)e6"
-    assert value_str(-.000123567,.00764) == "-0.1(76)e-3"
-    assert value_str(-12356,7640000) == "-0.0(76)e6"
-    assert value_str(-12,76400) == "-0.000(76)e6"
-    assert value_str(-0.0001,0.764) == "-0.00(76)"
-
-    # non-finite values
-    assert value_str(-numpy.inf,None) == "-inf"
-    assert value_str(numpy.inf,None) == "inf"
-    assert value_str(numpy.NaN,None) == "NaN"
-    
-    # bad or missing uncertainty
-    assert value_str(-1.23567,numpy.NaN) == "-1.23567"
-    assert value_str(-1.23567,-numpy.inf) == "-1.23567"
-    assert value_str(-1.23567,-0.1) == "-1.23567"
-    assert value_str(-1.23567,0) == "-1.23567"
-    assert value_str(-1.23567,None) == "-1.23567"
-    assert value_str(-1.23567,numpy.inf) == "-1.2(inf)"
-
-def test_pm():
-    # Oops... renamed function after writing tests
-    value_str = format_uncertainty_pm
-
-    # val_place > err_place
-    assert value_str(1235670,766000) == "1.24e6 +/- 0.77e6"
-    assert value_str(123567., 76600) == "124e3 +/- 77e3"
-    assert value_str(12356.7,  7660) == "12.4e3 +/- 7.7e3"
-    assert value_str(1235.67,   766) == "1.24e3 +/- 0.77e3"
-    assert value_str(123.567,    76.6) == "124 +/- 77"
-    assert value_str(12.3567,     7.66) == "12.4 +/- 7.7"
-    assert value_str(1.23567,      .766) == "1.24 +/- 0.77"
-    assert value_str(.123567,      .0766) == "0.124 +/- 0.077"
-    assert value_str(.0123567,     .00766) == "0.0124 +/- 0.0077"
-    assert value_str(.00123567,    .000766) == "0.00124 +/- 0.00077"
-    assert value_str(.000123567,   .0000766) == "124e-6 +/- 77e-6"
-    assert value_str(.0000123567,  .00000766) == "12.4e-6 +/- 7.7e-6"
-    assert value_str(.00000123567, .000000766) == "1.24e-6 +/- 0.77e-6"
-    assert value_str(.000000123567,.0000000766) == "124e-9 +/- 77e-9"
-    assert value_str(.00000123567, .0000000766) == "1.236e-6 +/- 0.077e-6"
-    assert value_str(.0000123567,  .0000000766) == "12.357e-6 +/- 0.077e-6"
-    assert value_str(.000123567,   .0000000766) == "123.567e-6 +/- 0.077e-6"
-    assert value_str(.00123567,    .000000766) == "0.00123567 +/- 0.00000077"
-    assert value_str(.0123567,     .00000766) == "0.0123567 +/- 0.0000077"
-    assert value_str(.123567,      .0000766) == "0.123567 +/- 0.000077"
-    assert value_str(1.23567,      .000766) == "1.23567 +/- 0.00077"
-    assert value_str(12.3567,      .00766) == "12.3567 +/- 0.0077"
-    assert value_str(123.567,      .0764) == "123.567 +/- 0.076"
-    assert value_str(1235.67,      .764) == "1235.67 +/- 0.76"
-    assert value_str(12356.7,     7.64) == "12356.7 +/- 7.6"
-    assert value_str(123567,     76.4) == "123567 +/- 76"
-    assert value_str(1235670,   764) == "1.23567e6 +/- 0.00076e6"
-    assert value_str(12356700,  764) == "12.35670e6 +/- 0.00076e6"
-    assert value_str(123567000, 764) == "123.56700e6 +/- 0.00076e6"
-    assert value_str(123567000,7640) == "123.5670e6 +/- 0.0076e6"
-    assert value_str(1235670000,76400) == "1.235670e9 +/- 0.000076e9"
-
-    # val_place == err_place
-    assert value_str(123567,764000) == "0.12e6 +/- 0.76e6"
-    assert value_str(12356.7,76400) == "12e3 +/- 76e3"
-    assert value_str(1235.67,7640) == "1.2e3 +/- 7.6e3"
-    assert value_str(123.567,764) == "0.12e3 +/- 0.76e3"
-    assert value_str(12.3567,76.4) == "12 +/- 76"
-    assert value_str(1.23567,7.64) == "1.2 +/- 7.6"
-    assert value_str(.123567,.764) == "0.12 +/- 0.76"
-    assert value_str(.0123567,.0764) == "12e-3 +/- 76e-3"
-    assert value_str(.00123567,.00764) == "1.2e-3 +/- 7.6e-3"
-    assert value_str(.000123567,.000764) == "0.12e-3 +/- 0.76e-3"
-
-    # val_place == err_place-1
-    assert value_str(123567,7640000) == "0.1e6 +/- 7.6e6"
-    assert value_str(12356.7,764000) == "0.01e6 +/- 0.76e6"
-    assert value_str(1235.67,76400) == "0.001e6 +/- 0.076e6"
-    assert value_str(123.567,7640) == "0.1e3 +/- 7.6e3"
-    assert value_str(12.3567,764) == "0.01e3 +/- 0.76e3"
-    assert value_str(1.23567,76.4) == "0.001e3 +/- 0.076e3"
-    assert value_str(.123567,7.64) == "0.1 +/- 7.6"
-    assert value_str(.0123567,.764) == "0.01 +/- 0.76"
-    assert value_str(.00123567,.0764) == "0.001 +/- 0.076"
-    assert value_str(.000123567,.00764) == "0.1e-3 +/- 7.6e-3"
-
-    # val_place == err_place-2
-    assert value_str(12356700,7640000000) == "0.0e9 +/- 7.6e9"
-    assert value_str(1235670,764000000) == "0.00e9 +/- 0.76e9"
-    assert value_str(123567,76400000) == "0.000e9 +/- 0.076e9"
-    assert value_str(12356,7640000) == "0.0e6 +/- 7.6e6"
-    assert value_str(1235,764000) == "0.00e6 +/- 0.76e6"
-    assert value_str(123,76400) == "0.000e6 +/- 0.076e6"
-    assert value_str(12,7640) == "0.0e3 +/- 7.6e3"
-    assert value_str(1,764) == "0.00e3 +/- 0.76e3"
-    assert value_str(0.1,76.4) == "0.000e3 +/- 0.076e3"
-    assert value_str(0.01,7.64) == "0.0 +/- 7.6"
-    assert value_str(0.001,0.764) == "0.00 +/- 0.76"
-    assert value_str(0.0001,0.0764) == "0.000 +/- 0.076"
-    assert value_str(0.00001,0.00764) == "0.0e-3 +/- 7.6e-3"
-
-    # val_place == err_place-3
-    assert value_str(12356700,76400000000) == "0.000e12 +/- 0.076e12"
-    assert value_str(1235670,7640000000) == "0.0e9 +/- 7.6e9"
-    assert value_str(123567,764000000) == "0.00e9 +/- 0.76e9"
-    assert value_str(12356,76400000) == "0.000e9 +/- 0.076e9"
-    assert value_str(1235,7640000) == "0.0e6 +/- 7.6e6"
-    assert value_str(123,764000) == "0.00e6 +/- 0.76e6"
-    assert value_str(12,76400) == "0.000e6 +/- 0.076e6"
-    assert value_str(1,7640) == "0.0e3 +/- 7.6e3"
-    assert value_str(0.1,764) == "0.00e3 +/- 0.76e3"
-    assert value_str(0.01,76.4) == "0.000e3 +/- 0.076e3"
-    assert value_str(0.001,7.64) == "0.0 +/- 7.6"
-    assert value_str(0.0001,0.764) == "0.00 +/- 0.76"
-    assert value_str(0.00001,0.0764) == "0.000 +/- 0.076"
-    assert value_str(0.000001,0.00764) == "0.0e-3 +/- 7.6e-3"
-
-    # Zero values
-    assert value_str(0,7640000) == "0.0e6 +/- 7.6e6"
-    assert value_str(0, 764000) == "0.00e6 +/- 0.76e6"
-    assert value_str(0,  76400) == "0.000e6 +/- 0.076e6"
-    assert value_str(0,   7640) == "0.0e3 +/- 7.6e3"
-    assert value_str(0,    764) == "0.00e3 +/- 0.76e3"
-    assert value_str(0,     76.4) == "0.000e3 +/- 0.076e3"
-    assert value_str(0,      7.64) == "0.0 +/- 7.6"
-    assert value_str(0,      0.764) == "0.00 +/- 0.76"
-    assert value_str(0,      0.0764) == "0.000 +/- 0.076"
-    assert value_str(0,      0.00764) == "0.0e-3 +/- 7.6e-3"
-    assert value_str(0,      0.000764) == "0.00e-3 +/- 0.76e-3"
-    assert value_str(0,      0.0000764) == "0.000e-3 +/- 0.076e-3"
-
-    # negative values
-    assert value_str(-1235670,766000) == "-1.24e6 +/- 0.77e6"
-    assert value_str(-1.23567,.766) == "-1.24 +/- 0.77"
-    assert value_str(-.00000123567,.0000000766) == "-1.236e-6 +/- 0.077e-6"
-    assert value_str(-12356.7,7.64) == "-12356.7 +/- 7.6"
-    assert value_str(-123.567,764) == "-0.12e3 +/- 0.76e3"
-    assert value_str(-1235.67,76400) == "-0.001e6 +/- 0.076e6"
-    assert value_str(-.000123567,.00764) == "-0.1e-3 +/- 7.6e-3"
-    assert value_str(-12356,7640000) == "-0.0e6 +/- 7.6e6"
-    assert value_str(-12,76400) == "-0.000e6 +/- 0.076e6"
-    assert value_str(-0.0001,0.764) == "-0.00 +/- 0.76"
-
-    # non-finite values
-    assert value_str(-numpy.inf,None) == "-inf"
-    assert value_str(numpy.inf,None) == "inf"
-    assert value_str(numpy.NaN,None) == "NaN"
-    
-    # bad or missing uncertainty
-    assert value_str(-1.23567,numpy.NaN) == "-1.23567"
-    assert value_str(-1.23567,-numpy.inf) == "-1.23567"
-    assert value_str(-1.23567,-0.1) == "-1.23567"
-    assert value_str(-1.23567,0) == "-1.23567"
-    assert value_str(-1.23567,None) == "-1.23567"
-    assert value_str(-1.23567,numpy.inf) == "-1.2 +/- inf"
-
-def test_default():
-    # Check that the default is the compact format
-    assert format_uncertainty(-1.23567,0.766) == "-1.24(77)"
-
-def main():
-    """
-    Run all tests.
-
-    This is equivalent to "nosetests --with-doctest"
-    """
-    test_compact()
-    test_pm()
-    test_default()
-    
-    import doctest
-    doctest.testmod()
-
-if __name__ == "__main__": main()
Index: c/sas/data_util/nxsunit.py
===================================================================
--- src/sas/data_util/nxsunit.py	(revision b3efb7dfc293a955756eccf52e0bbd30d3a2c32d)
+++ 	(revision )
@@ -1,210 +1,0 @@
-# This program is public domain
-# Author: Paul Kienzle
-"""
-Define unit conversion support for NeXus style units.
-
-The unit format is somewhat complicated.  There are variant spellings
-and incorrect capitalization to worry about, as well as forms such as
-"mili*metre" and "1e-7 seconds".
-
-This is a minimal implementation of units including only what I happen to
-need now.  It does not support the complete dimensional analysis provided
-by the package udunits on which NeXus is based, or even the units used
-in the NeXus definition files.
-
-Unlike other units packages, this package does not carry the units along with 
-the value but merely provides a conversion function for transforming values.
-
-Usage example::
-
-    import nxsunit
-    u = nxsunit.Converter('mili*metre')  # Units stored in mm
-    v = u(3000,'m')  # Convert the value 3000 mm into meters
-
-NeXus example::
-
-    # Load sample orientation in radians regardless of how it is stored.
-    # 1. Open the path
-    file.openpath('/entry1/sample/sample_orientation')
-    # 2. scan the attributes, retrieving 'units'
-    units = [for attr,value in file.attrs() if attr == 'units']
-    # 3. set up the converter (assumes that units actually exists)
-    u = nxsunit.Converter(units[0])
-    # 4. read the data and convert to the correct units
-    v = u(file.read(),'radians')
-
-This is a standalone module, not relying on either DANSE or NeXus, and
-can be used for other unit conversion tasks.
-
-Note: minutes are used for angle and seconds are used for time.  We
-cannot tell what the correct interpretation is without knowing something
-about the fields themselves.  If this becomes an issue, we will need to
-allow the application to set the dimension for the unit rather than
-inferring the dimension from an example unit.
-"""
-
-# TODO: Add udunits to NAPI rather than reimplementing it in python
-# TODO: Alternatively, parse the udunits database directly
-# UDUnits:
-#  http://www.unidata.ucar.edu/software/udunits/udunits-1/udunits.txt
-
-from __future__ import division
-import math
-
-__all__ = ['Converter']
-
-# Limited form of units for returning objects of a specific type.
-# Maybe want to do full units handling with e.g., pyre's
-# unit class. For now lets keep it simple.  Note that
-def _build_metric_units(unit,abbr):
-    """
-    Construct standard SI names for the given unit.
-    Builds e.g.,
-        s, ns
-        second, nanosecond, nano*second
-        seconds, nanoseconds
-    Includes prefixes for femto through peta.
-
-    Ack! Allows, e.g., Coulomb and coulomb even though Coulomb is not
-    a unit because some NeXus files store it that way!
-    
-    Returns a dictionary of names and scales.
-    """
-    prefix = dict(peta=1e15,tera=1e12,giga=1e9,mega=1e6,kilo=1e3,
-                  deci=1e-1,centi=1e-2,milli=1e-3,mili=1e-3,micro=1e-6,
-                  nano=1e-9,pico=1e-12,femto=1e-15)
-    short_prefix = dict(P=1e15,T=1e12,G=1e9,M=1e6,k=1e3,
-                        d=1e-1,c=1e-2,m=1e-3,u=1e-6,
-                        n=1e-9,p=1e-12,f=1e-15)
-    map = {abbr:1}
-    map.update([(P+abbr,scale) for (P,scale) in short_prefix.iteritems()])
-    for name in [unit,unit.capitalize()]:
-        map.update({name:1,name+'s':1})
-        map.update([(P+name,scale) for (P,scale) in prefix.iteritems()])
-        map.update([(P+'*'+name,scale) for (P,scale) in prefix.iteritems()])
-        map.update([(P+name+'s',scale) for (P,scale) in prefix.iteritems()])
-    return map
-
-def _build_plural_units(**kw):
-    """
-    Construct names for the given units.  Builds singular and plural form.
-    """
-    map = {}
-    map.update([(name,scale) for name,scale in kw.iteritems()])
-    map.update([(name+'s',scale) for name,scale in kw.iteritems()])
-    return map
-
-def _caret_optional(s):
-    """
-    Strip '^' from unit names.
-
-    * WARNING * this will incorrect transform 10^3 to 103.
-    """
-    s.update((k.replace('^',''),v) 
-             for k,v in s.items()
-             if '^' in k)
-
-def _build_all_units():
-    distance = _build_metric_units('meter','m')
-    distance.update(_build_metric_units('metre','m'))
-    distance.update(_build_plural_units(micron=1e-6, Angstrom=1e-10))
-    distance.update({'A':1e-10, 'Ang':1e-10})
-
-    # Note: minutes are used for angle
-    time = _build_metric_units('second','s')
-    time.update(_build_plural_units(hour=3600,day=24*3600,week=7*24*3600))
-
-    # Note: seconds are used for time
-    angle = _build_plural_units(degree=1, minute=1/60.,
-                  arcminute=1/60., arcsecond=1/3600., radian=180/math.pi)
-    angle.update(deg=1, arcmin=1/60., arcsec=1/3600., rad=180/math.pi)
-
-    frequency = _build_metric_units('hertz','Hz')
-    frequency.update(_build_metric_units('Hertz','Hz'))
-    frequency.update(_build_plural_units(rpm=1/60.))
-
-    # Note: degrees are used for angle
-    # Note: temperature needs an offset as well as a scale
-    temperature = _build_metric_units('kelvin','K')
-    temperature.update(_build_metric_units('Kelvin','K'))
-    temperature.update(_build_metric_units('Celcius', 'C'))
-    temperature.update(_build_metric_units('celcius', 'C'))
-    
-    charge = _build_metric_units('coulomb','C')
-    charge.update({'microAmp*hour':0.0036})
-
-    sld = { '10^-6 Angstrom^-2': 1e-6, 'Angstrom^-2': 1 }
-    Q = { 'invA': 1, 'invAng': 1, 'invAngstroms': 1, '1/A': 1, 
-          '10^-3 Angstrom^-1': 1e-3, '1/cm': 1e-8,
-          'nm^-1': 0.1, '1/nm': 0.1, 'n_m^-1': 0.1 }
-
-    _caret_optional(sld)
-    _caret_optional(Q)
-
-    dims = [distance, time, angle, frequency, temperature, charge, sld, Q]
-    return dims
-
-class Converter(object):
-    """
-    Unit converter for NeXus style units.
-    """
-    # Define the units, using both American and European spelling.
-    scalemap = None
-    scalebase = 1
-    dims = _build_all_units()
-
-    # Note: a.u. stands for arbitrary units, which should return the default
-    # units for that particular dimension.
-    # Note: don't have support for dimensionless units.
-    unknown = {None:1, '???':1, '': 1, 'a.u.': 1}
-
-    def __init__(self, name):
-        self.base = name
-        for map in self.dims:
-            if name in map:
-                self.scalemap = map
-                self.scalebase = self.scalemap[name]
-                return
-        if name in self.unknown:
-            return # default scalemap and scalebase correspond to unknown
-        else:
-            raise KeyError("Unknown unit %s"%name)
-
-    def scale(self, units=""):
-        if units == "" or self.scalemap is None: return 1
-        return self.scalebase/self.scalemap[units]
-
-    def __call__(self, value, units=""):
-        # Note: calculating a*1 rather than simply returning a would produce
-        # an unnecessary copy of the array, which in the case of the raw
-        # counts array would be bad.  Sometimes copying and other times
-        # not copying is also bad, but copy on modify semantics isn't
-        # supported.
-        if units == "" or self.scalemap is None: return value
-        try:
-            return value * (self.scalebase/self.scalemap[units])
-        except KeyError:
-            possible_units = ", ".join(str(k) for k in self.scalemap.keys())
-            raise KeyError("%s not in %s"%(units,possible_units))
-
-def _check(expect,get):
-    if expect != get: raise ValueError, "Expected %s but got %s"%(expect,get)
-     #print expect,"==",get
-
-def test():
-    _check(1,Converter('n_m^-1')(10,'invA')) # 10 nm^-1 = 1 inv Angstroms
-    _check(2,Converter('mm')(2000,'m')) # 2000 mm -> 2 m
-    _check(0.003,Converter('microseconds')(3,units='ms')) # 3 us -> 0.003 ms
-    _check(45,Converter('nanokelvin')(45))  # 45 nK -> 45 nK
-    _check(0.5,Converter('seconds')(1800,units='hours')) # 1800 s -> 0.5 hr
-    _check(123,Converter('a.u.')(123,units='mm')) # arbitrary units always returns the same value
-    _check(123,Converter('a.u.')(123,units='s')) # arbitrary units always returns the same value
-    _check(123,Converter('a.u.')(123,units='')) # arbitrary units always returns the same value
-    try: Converter('help')
-    except KeyError: pass
-    else: raise Exception("unknown unit did not raise an error")
-
-    # TODO: more tests
-
-if __name__ == "__main__":
-    test()
Index: c/sas/data_util/odict.py
===================================================================
--- src/sas/data_util/odict.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,1399 +1,0 @@
-# odict.py
-# An Ordered Dictionary object
-# Copyright (C) 2005 Nicola Larosa, Michael Foord
-# E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk
-
-# This software is licensed under the terms of the BSD license.
-# http://www.voidspace.org.uk/python/license.shtml
-# Basically you're free to copy, modify, distribute and relicense it,
-# So long as you keep a copy of the license with it.
-
-# Documentation at http://www.voidspace.org.uk/python/odict.html
-# For information about bugfixes, updates and support, please join the
-# Pythonutils mailing list:
-# http://groups.google.com/group/pythonutils/
-# Comments, suggestions and bug reports welcome.
-
-"""A dict that keeps keys in insertion order"""
-from __future__ import generators
-
-__author__ = ('Nicola Larosa <nico-NoSp@m-tekNico.net>,'
-    'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>')
-
-__docformat__ = "restructuredtext en"
-
-__revision__ = '$Id: odict.py 58 2008-09-02 14:09:54Z farrowch $'
-
-__version__ = '0.2.2'
-
-__all__ = ['OrderedDict', 'SequenceOrderedDict']
-
-import sys
-INTP_VER = sys.version_info[:2]
-if INTP_VER < (2, 2):
-    raise RuntimeError("Python v.2.2 or later required")
-
-import types, warnings
-
-class OrderedDict(dict):
-    """
-    A class of dictionary that keeps the insertion order of keys.
-    
-    All appropriate methods return keys, items, or values in an ordered way.
-    
-    All normal dictionary methods are available. Update and comparison is
-    restricted to other OrderedDict objects.
-    
-    Various sequence methods are available, including the ability to explicitly
-    mutate the key ordering.
-    
-    __contains__ tests:
-    
-    >>> d = OrderedDict(((1, 3),))
-    >>> 1 in d
-    1
-    >>> 4 in d
-    0
-    
-    __getitem__ tests:
-    
-    >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2]
-    1
-    >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4]
-    Traceback (most recent call last):
-    KeyError: 4
-    
-    __len__ tests:
-    
-    >>> len(OrderedDict())
-    0
-    >>> len(OrderedDict(((1, 3), (3, 2), (2, 1))))
-    3
-    
-    get tests:
-    
-    >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-    >>> d.get(1)
-    3
-    >>> d.get(4) is None
-    1
-    >>> d.get(4, 5)
-    5
-    >>> d
-    OrderedDict([(1, 3), (3, 2), (2, 1)])
-    
-    has_key tests:
-    
-    >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-    >>> d.has_key(1)
-    1
-    >>> d.has_key(4)
-    0
-    """
-
-    def __init__(self, init_val=(), strict=False):
-        """
-        Create a new ordered dictionary. Cannot init from a normal dict,
-        nor from kwargs, since items order is undefined in those cases.
-        
-        If the ``strict`` keyword argument is ``True`` (``False`` is the
-        default) then when doing slice assignment - the ``OrderedDict`` you are
-        assigning from *must not* contain any keys in the remaining dict.
-        
-        >>> OrderedDict()
-        OrderedDict([])
-        >>> OrderedDict({1: 1})
-        Traceback (most recent call last):
-        TypeError: undefined order, cannot get items from dict
-        >>> OrderedDict({1: 1}.items())
-        OrderedDict([(1, 1)])
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d
-        OrderedDict([(1, 3), (3, 2), (2, 1)])
-        >>> OrderedDict(d)
-        OrderedDict([(1, 3), (3, 2), (2, 1)])
-        """
-        self.strict = strict
-        dict.__init__(self)
-        if isinstance(init_val, OrderedDict):
-            self._sequence = init_val.keys()
-            dict.update(self, init_val)
-        elif isinstance(init_val, dict):
-            # we lose compatibility with other ordered dict types this way
-            raise TypeError('undefined order, cannot get items from dict')
-        else:
-            self._sequence = []
-            self.update(init_val)
-
-### Special methods ###
-
-    def __delitem__(self, key):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> del d[3]
-        >>> d
-        OrderedDict([(1, 3), (2, 1)])
-        >>> del d[3]
-        Traceback (most recent call last):
-        KeyError: 3
-        >>> d[3] = 2
-        >>> d
-        OrderedDict([(1, 3), (2, 1), (3, 2)])
-        >>> del d[0:1]
-        >>> d
-        OrderedDict([(2, 1), (3, 2)])
-        """
-        if isinstance(key, types.SliceType):
-            # FIXME: efficiency?
-            keys = self._sequence[key]
-            for entry in keys:
-                dict.__delitem__(self, entry)
-            del self._sequence[key]
-        else:
-            # do the dict.__delitem__ *first* as it raises
-            # the more appropriate error
-            dict.__delitem__(self, key)
-            self._sequence.remove(key)
-
-    def __eq__(self, other):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d == OrderedDict(d)
-        True
-        >>> d == OrderedDict(((1, 3), (2, 1), (3, 2)))
-        False
-        >>> d == OrderedDict(((1, 0), (3, 2), (2, 1)))
-        False
-        >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
-        False
-        >>> d == dict(d)
-        False
-        >>> d == False
-        False
-        """
-        if isinstance(other, OrderedDict):
-            # FIXME: efficiency?
-            #   Generate both item lists for each compare
-            return (self.items() == other.items())
-        else:
-            return False
-
-    def __lt__(self, other):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
-        >>> c < d
-        True
-        >>> d < c
-        False
-        >>> d < dict(c)
-        Traceback (most recent call last):
-        TypeError: Can only compare with other OrderedDicts
-        """
-        if not isinstance(other, OrderedDict):
-            raise TypeError('Can only compare with other OrderedDicts')
-        # FIXME: efficiency?
-        #   Generate both item lists for each compare
-        return (self.items() < other.items())
-
-    def __le__(self, other):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
-        >>> e = OrderedDict(d)
-        >>> c <= d
-        True
-        >>> d <= c
-        False
-        >>> d <= dict(c)
-        Traceback (most recent call last):
-        TypeError: Can only compare with other OrderedDicts
-        >>> d <= e
-        True
-        """
-        if not isinstance(other, OrderedDict):
-            raise TypeError('Can only compare with other OrderedDicts')
-        # FIXME: efficiency?
-        #   Generate both item lists for each compare
-        return (self.items() <= other.items())
-
-    def __ne__(self, other):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d != OrderedDict(d)
-        False
-        >>> d != OrderedDict(((1, 3), (2, 1), (3, 2)))
-        True
-        >>> d != OrderedDict(((1, 0), (3, 2), (2, 1)))
-        True
-        >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
-        False
-        >>> d != dict(d)
-        True
-        >>> d != False
-        True
-        """
-        if isinstance(other, OrderedDict):
-            # FIXME: efficiency?
-            #   Generate both item lists for each compare
-            return not (self.items() == other.items())
-        else:
-            return True
-
-    def __gt__(self, other):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
-        >>> d > c
-        True
-        >>> c > d
-        False
-        >>> d > dict(c)
-        Traceback (most recent call last):
-        TypeError: Can only compare with other OrderedDicts
-        """
-        if not isinstance(other, OrderedDict):
-            raise TypeError('Can only compare with other OrderedDicts')
-        # FIXME: efficiency?
-        #   Generate both item lists for each compare
-        return (self.items() > other.items())
-
-    def __ge__(self, other):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
-        >>> e = OrderedDict(d)
-        >>> c >= d
-        False
-        >>> d >= c
-        True
-        >>> d >= dict(c)
-        Traceback (most recent call last):
-        TypeError: Can only compare with other OrderedDicts
-        >>> e >= d
-        True
-        """
-        if not isinstance(other, OrderedDict):
-            raise TypeError('Can only compare with other OrderedDicts')
-        # FIXME: efficiency?
-        #   Generate both item lists for each compare
-        return (self.items() >= other.items())
-
-    def __repr__(self):
-        """
-        Used for __repr__ and __str__
-        
-        >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
-        >>> r1
-        "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])"
-        >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
-        >>> r2
-        "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])"
-        >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
-        True
-        >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
-        True
-        """
-        return '%s([%s])' % (self.__class__.__name__, ', '.join(
-            ['(%r, %r)' % (key, self[key]) for key in self._sequence]))
-
-    def __setitem__(self, key, val):
-        """
-        Allows slice assignment, so long as the slice is an OrderedDict
-        >>> d = OrderedDict()
-        >>> d['a'] = 'b'
-        >>> d['b'] = 'a'
-        >>> d[3] = 12
-        >>> d
-        OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)])
-        >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4)))
-        >>> d
-        OrderedDict([(1, 2), (2, 3), (3, 4)])
-        >>> d[::2] = OrderedDict(((7, 8), (9, 10)))
-        >>> d
-        OrderedDict([(7, 8), (2, 3), (9, 10)])
-        >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)))
-        >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
-        >>> d
-        OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
-        >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True)
-        >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
-        >>> d
-        OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
-        
-        >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True)
-        >>> a[3] = 4
-        >>> a
-        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a
-        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)])
-        Traceback (most recent call last):
-        ValueError: slice assignment must be from unique keys
-        >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)))
-        >>> a[3] = 4
-        >>> a
-        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a
-        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a
-        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> a
-        OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)])
-        
-        >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> d[:1] = 3
-        Traceback (most recent call last):
-        TypeError: slice assignment requires an OrderedDict
-        
-        >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
-        >>> d[:1] = OrderedDict([(9, 8)])
-        >>> d
-        OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)])
-        """
-        if isinstance(key, types.SliceType):
-            if not isinstance(val, OrderedDict):
-                # FIXME: allow a list of tuples?
-                raise TypeError('slice assignment requires an OrderedDict')
-            keys = self._sequence[key]
-            # NOTE: Could use ``range(*key.indices(len(self._sequence)))``
-            indexes = range(len(self._sequence))[key]
-            if key.step is None:
-                # NOTE: new slice may not be the same size as the one being
-                #   overwritten !
-                # NOTE: What is the algorithm for an impossible slice?
-                #   e.g. d[5:3]
-                pos = key.start or 0
-                del self[key]
-                newkeys = val.keys()
-                for k in newkeys:
-                    if k in self:
-                        if self.strict:
-                            raise ValueError('slice assignment must be from '
-                                'unique keys')
-                        else:
-                            # NOTE: This removes duplicate keys *first*
-                            #   so start position might have changed?
-                            del self[k]
-                self._sequence = (self._sequence[:pos] + newkeys +
-                    self._sequence[pos:])
-                dict.update(self, val)
-            else:
-                # extended slice - length of new slice must be the same
-                # as the one being replaced
-                if len(keys) != len(val):
-                    raise ValueError('attempt to assign sequence of size %s '
-                        'to extended slice of size %s' % (len(val), len(keys)))
-                # FIXME: efficiency?
-                del self[key]
-                item_list = zip(indexes, val.items())
-                # smallest indexes first - higher indexes not guaranteed to
-                # exist
-                item_list.sort()
-                for pos, (newkey, newval) in item_list:
-                    if self.strict and newkey in self:
-                        raise ValueError('slice assignment must be from unique'
-                            ' keys')
-                    self.insert(pos, newkey, newval)
-        else:
-            if key not in self:
-                self._sequence.append(key)
-            dict.__setitem__(self, key, val)
-
-    def __getitem__(self, key):
-        """
-        Allows slicing. Returns an OrderedDict if you slice.
-        >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)])
-        >>> b[::-1]
-        OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)])
-        >>> b[2:5]
-        OrderedDict([(5, 2), (4, 3), (3, 4)])
-        >>> type(b[2:4])
-        <class '__main__.OrderedDict'>
-        """
-        if isinstance(key, types.SliceType):
-            # FIXME: does this raise the error we want?
-            keys = self._sequence[key]
-            # FIXME: efficiency?
-            return OrderedDict([(entry, self[entry]) for entry in keys])
-        else:
-            return dict.__getitem__(self, key)
-
-    __str__ = __repr__
-
-    def __setattr__(self, name, value):
-        """
-        Implemented so that accesses to ``sequence`` raise a warning and are
-        diverted to the new ``setkeys`` method.
-        """
-        if name == 'sequence':
-            warnings.warn('Use of the sequence attribute is deprecated.'
-                ' Use the keys method instead.', DeprecationWarning)
-            # NOTE: doesn't return anything
-            self.setkeys(value)
-        else:
-            # FIXME: do we want to allow arbitrary setting of attributes?
-            #   Or do we want to manage it?
-            object.__setattr__(self, name, value)
-
-    def __getattr__(self, name):
-        """
-        Implemented so that access to ``sequence`` raises a warning.
-        
-        >>> d = OrderedDict()
-        >>> d.sequence
-        []
-        """
-        if name == 'sequence':
-            warnings.warn('Use of the sequence attribute is deprecated.'
-                ' Use the keys method instead.', DeprecationWarning)
-            # NOTE: Still (currently) returns a direct reference. Need to
-            #   because code that uses sequence will expect to be able to
-            #   mutate it in place.
-            return self._sequence
-        else:
-            # raise the appropriate error
-            raise AttributeError("OrderedDict has no '%s' attribute" % name)
-
-    def __deepcopy__(self, memo):
-        """
-        To allow deepcopy to work with OrderedDict.
-        
-        >>> from copy import deepcopy
-        >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)])
-        >>> a['test'] = {}
-        >>> b = deepcopy(a)
-        >>> b == a
-        True
-        >>> b is a
-        False
-        >>> a['test'] is b['test']
-        False
-        """
-        from copy import deepcopy
-        return self.__class__(deepcopy(self.items(), memo), self.strict)
-
-
-### Read-only methods ###
-
-    def copy(self):
-        """
-        >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy()
-        OrderedDict([(1, 3), (3, 2), (2, 1)])
-        """
-        return OrderedDict(self)
-
-    def items(self):
-        """
-        ``items`` returns a list of tuples representing all the 
-        ``(key, value)`` pairs in the dictionary.
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.items()
-        [(1, 3), (3, 2), (2, 1)]
-        >>> d.clear()
-        >>> d.items()
-        []
-        """
-        return zip(self._sequence, self.values())
-
-    def keys(self):
-        """
-        Return a list of keys in the ``OrderedDict``.
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.keys()
-        [1, 3, 2]
-        """
-        return self._sequence[:]
-
-    def values(self, values=None):
-        """
-        Return a list of all the values in the OrderedDict.
-        
-        Optionally you can pass in a list of values, which will replace the
-        current list. The value list must be the same len as the OrderedDict.
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.values()
-        [3, 2, 1]
-        """
-        return [self[key] for key in self._sequence]
-
-    def iteritems(self):
-        """
-        >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems()
-        >>> ii.next()
-        (1, 3)
-        >>> ii.next()
-        (3, 2)
-        >>> ii.next()
-        (2, 1)
-        >>> ii.next()
-        Traceback (most recent call last):
-        StopIteration
-        """
-        def make_iter(self=self):
-            keys = self.iterkeys()
-            while True:
-                key = keys.next()
-                yield (key, self[key])
-        return make_iter()
-
-    def iterkeys(self):
-        """
-        >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys()
-        >>> ii.next()
-        1
-        >>> ii.next()
-        3
-        >>> ii.next()
-        2
-        >>> ii.next()
-        Traceback (most recent call last):
-        StopIteration
-        """
-        return iter(self._sequence)
-
-    __iter__ = iterkeys
-
-    def itervalues(self):
-        """
-        >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues()
-        >>> iv.next()
-        3
-        >>> iv.next()
-        2
-        >>> iv.next()
-        1
-        >>> iv.next()
-        Traceback (most recent call last):
-        StopIteration
-        """
-        def make_iter(self=self):
-            keys = self.iterkeys()
-            while True:
-                yield self[keys.next()]
-        return make_iter()
-
-### Read-write methods ###
-
-    def clear(self):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.clear()
-        >>> d
-        OrderedDict([])
-        """
-        dict.clear(self)
-        self._sequence = []
-
-    def pop(self, key, *args):
-        """
-        No dict.pop in Python 2.2, gotta reimplement it
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.pop(3)
-        2
-        >>> d
-        OrderedDict([(1, 3), (2, 1)])
-        >>> d.pop(4)
-        Traceback (most recent call last):
-        KeyError: 4
-        >>> d.pop(4, 0)
-        0
-        >>> d.pop(4, 0, 1)
-        Traceback (most recent call last):
-        TypeError: pop expected at most 2 arguments, got 3
-        """
-        if len(args) > 1:
-            raise TypeError, ('pop expected at most 2 arguments, got %s' %
-                (len(args) + 1))
-        if key in self:
-            val = self[key]
-            del self[key]
-        else:
-            try:
-                val = args[0]
-            except IndexError:
-                raise KeyError(key)
-        return val
-
-    def popitem(self, i=-1):
-        """
-        Delete and return an item specified by index, not a random one as in
-        dict. The index is -1 by default (the last item).
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.popitem()
-        (2, 1)
-        >>> d
-        OrderedDict([(1, 3), (3, 2)])
-        >>> d.popitem(0)
-        (1, 3)
-        >>> OrderedDict().popitem()
-        Traceback (most recent call last):
-        KeyError: 'popitem(): dictionary is empty'
-        >>> d.popitem(2)
-        Traceback (most recent call last):
-        IndexError: popitem(): index 2 not valid
-        """
-        if not self._sequence:
-            raise KeyError('popitem(): dictionary is empty')
-        try:
-            key = self._sequence[i]
-        except IndexError:
-            raise IndexError('popitem(): index %s not valid' % i)
-        return (key, self.pop(key))
-
-    def setdefault(self, key, defval = None):
-        """
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.setdefault(1)
-        3
-        >>> d.setdefault(4) is None
-        True
-        >>> d
-        OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)])
-        >>> d.setdefault(5, 0)
-        0
-        >>> d
-        OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)])
-        """
-        if key in self:
-            return self[key]
-        else:
-            self[key] = defval
-            return defval
-
-    def update(self, from_od):
-        """
-        Update from another OrderedDict or sequence of (key, value) pairs
-        
-        >>> d = OrderedDict(((1, 0), (0, 1)))
-        >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1))))
-        >>> d
-        OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)])
-        >>> d.update({4: 4})
-        Traceback (most recent call last):
-        TypeError: undefined order, cannot get items from dict
-        >>> d.update((4, 4))
-        Traceback (most recent call last):
-        TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence
-        """
-        if isinstance(from_od, OrderedDict):
-            for key, val in from_od.items():
-                self[key] = val
-        elif isinstance(from_od, dict):
-            # we lose compatibility with other ordered dict types this way
-            raise TypeError('undefined order, cannot get items from dict')
-        else:
-            # FIXME: efficiency?
-            # sequence of 2-item sequences, or error
-            for item in from_od:
-                try:
-                    key, val = item
-                except TypeError:
-                    raise TypeError('cannot convert dictionary update'
-                        ' sequence element "%s" to a 2-item sequence' % item)
-                self[key] = val
-
-    def rename(self, old_key, new_key):
-        """
-        Rename the key for a given value, without modifying sequence order.
-        
-        For the case where new_key already exists this raise an exception,
-        since if new_key exists, it is ambiguous as to what happens to the
-        associated values, and the position of new_key in the sequence.
-        
-        >>> od = OrderedDict()
-        >>> od['a'] = 1
-        >>> od['b'] = 2
-        >>> od.items()
-        [('a', 1), ('b', 2)]
-        >>> od.rename('b', 'c')
-        >>> od.items()
-        [('a', 1), ('c', 2)]
-        >>> od.rename('c', 'a')
-        Traceback (most recent call last):
-        ValueError: New key already exists: 'a'
-        >>> od.rename('d', 'b')
-        Traceback (most recent call last):
-        KeyError: 'd'
-        """
-        if new_key == old_key:
-            # no-op
-            return
-        if new_key in self:
-            raise ValueError("New key already exists: %r" % new_key)
-        # rename sequence entry
-        value = self[old_key] 
-        old_idx = self._sequence.index(old_key)
-        self._sequence[old_idx] = new_key
-        # rename internal dict entry
-        dict.__delitem__(self, old_key)
-        dict.__setitem__(self, new_key, value)
-
-    def setitems(self, items):
-        """
-        This method allows you to set the items in the dict.
-        
-        It takes a list of tuples - of the same sort returned by the ``items``
-        method.
-        
-        >>> d = OrderedDict()
-        >>> d.setitems(((3, 1), (2, 3), (1, 2)))
-        >>> d
-        OrderedDict([(3, 1), (2, 3), (1, 2)])
-        """
-        self.clear()
-        # FIXME: this allows you to pass in an OrderedDict as well :-)
-        self.update(items)
-
-    def setkeys(self, keys):
-        """
-        ``setkeys`` all ows you to pass in a new list of keys which will
-        replace the current set. This must contain the same set of keys, but
-        need not be in the same order.
-        
-        If you pass in new keys that don't match, a ``KeyError`` will be
-        raised.
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.keys()
-        [1, 3, 2]
-        >>> d.setkeys((1, 2, 3))
-        >>> d
-        OrderedDict([(1, 3), (2, 1), (3, 2)])
-        >>> d.setkeys(['a', 'b', 'c'])
-        Traceback (most recent call last):
-        KeyError: 'Keylist is not the same as current keylist.'
-        """
-        # FIXME: Efficiency? (use set for Python 2.4 :-)
-        # NOTE: list(keys) rather than keys[:] because keys[:] returns
-        #   a tuple, if keys is a tuple.
-        kcopy = list(keys)
-        kcopy.sort()
-        self._sequence.sort()
-        if kcopy != self._sequence:
-            raise KeyError('Keylist is not the same as current keylist.')
-        # NOTE: This makes the _sequence attribute a new object, instead
-        #       of changing it in place.
-        # FIXME: efficiency?
-        self._sequence = list(keys)
-
-    def setvalues(self, values):
-        """
-        You can pass in a list of values, which will replace the
-        current list. The value list must be the same len as the OrderedDict.
-        
-        (Or a ``ValueError`` is raised.)
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.setvalues((1, 2, 3))
-        >>> d
-        OrderedDict([(1, 1), (3, 2), (2, 3)])
-        >>> d.setvalues([6])
-        Traceback (most recent call last):
-        ValueError: Value list is not the same length as the OrderedDict.
-        """
-        if len(values) != len(self):
-            # FIXME: correct error to raise?
-            raise ValueError('Value list is not the same length as the '
-                'OrderedDict.')
-        self.update(zip(self, values))
-
-### Sequence Methods ###
-
-    def index(self, key):
-        """
-        Return the position of the specified key in the OrderedDict.
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.index(3)
-        1
-        >>> d.index(4)
-        Traceback (most recent call last):
-        ValueError: list.index(x): x not in list
-        """
-        return self._sequence.index(key)
-
-    def insert(self, index, key, value):
-        """
-        Takes ``index``, ``key``, and ``value`` as arguments.
-        
-        Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in
-        the OrderedDict.
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.insert(0, 4, 0)
-        >>> d
-        OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)])
-        >>> d.insert(0, 2, 1)
-        >>> d
-        OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)])
-        >>> d.insert(8, 8, 1)
-        >>> d
-        OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)])
-        """
-        if key in self:
-            # FIXME: efficiency?
-            del self[key]
-        self._sequence.insert(index, key)
-        dict.__setitem__(self, key, value)
-
-    def reverse(self):
-        """
-        Reverse the order of the OrderedDict.
-        
-        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
-        >>> d.reverse()
-        >>> d
-        OrderedDict([(2, 1), (3, 2), (1, 3)])
-        """
-        self._sequence.reverse()
-
-    def sort(self, *args, **kwargs):
-        """
-        Sort the key order in the OrderedDict.
-        
-        This method takes the same arguments as the ``list.sort`` method on
-        your version of Python.
-        
-        >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4)))
-        >>> d.sort()
-        >>> d
-        OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)])
-        """
-        self._sequence.sort(*args, **kwargs)
-
-class Keys(object):
-    # FIXME: should this object be a subclass of list?
-    """
-    Custom object for accessing the keys of an OrderedDict.
-    
-    Can be called like the normal ``OrderedDict.keys`` method, but also
-    supports indexing and sequence methods.
-    """
-
-    def __init__(self, main):
-        self._main = main
-
-    def __call__(self):
-        """Pretend to be the keys method."""
-        return self._main._keys()
-
-    def __getitem__(self, index):
-        """Fetch the key at position i."""
-        # NOTE: this automatically supports slicing :-)
-        return self._main._sequence[index]
-
-    def __setitem__(self, index, name):
-        """
-        You cannot assign to keys, but you can do slice assignment to re-order
-        them.
-        
-        You can only do slice assignment if the new set of keys is a reordering
-        of the original set.
-        """
-        if isinstance(index, types.SliceType):
-            # FIXME: efficiency?
-            # check length is the same
-            indexes = range(len(self._main._sequence))[index]
-            if len(indexes) != len(name):
-                raise ValueError('attempt to assign sequence of size %s '
-                    'to slice of size %s' % (len(name), len(indexes)))
-            # check they are the same keys
-            # FIXME: Use set
-            old_keys = self._main._sequence[index]
-            new_keys = list(name)
-            old_keys.sort()
-            new_keys.sort()
-            if old_keys != new_keys:
-                raise KeyError('Keylist is not the same as current keylist.')
-            orig_vals = [self._main[k] for k in name]
-            del self._main[index]
-            vals = zip(indexes, name, orig_vals)
-            vals.sort()
-            for i, k, v in vals:
-                if self._main.strict and k in self._main:
-                    raise ValueError('slice assignment must be from '
-                        'unique keys')
-                self._main.insert(i, k, v)
-        else:
-            raise ValueError('Cannot assign to keys')
-
-    ### following methods pinched from UserList and adapted ###
-    def __repr__(self): return repr(self._main._sequence)
-
-    # FIXME: do we need to check if we are comparing with another ``Keys``
-    #   object? (like the __cast method of UserList)
-    def __lt__(self, other): return self._main._sequence <  other
-    def __le__(self, other): return self._main._sequence <= other
-    def __eq__(self, other): return self._main._sequence == other
-    def __ne__(self, other): return self._main._sequence != other
-    def __gt__(self, other): return self._main._sequence >  other
-    def __ge__(self, other): return self._main._sequence >= other
-    # FIXME: do we need __cmp__ as well as rich comparisons?
-    def __cmp__(self, other): return cmp(self._main._sequence, other)
-
-    def __contains__(self, item): return item in self._main._sequence
-    def __len__(self): return len(self._main._sequence)
-    def __iter__(self): return self._main.iterkeys()
-    def count(self, item): return self._main._sequence.count(item)
-    def index(self, item, *args): return self._main._sequence.index(item, *args)
-    def reverse(self): self._main._sequence.reverse()
-    def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds)
-    def __mul__(self, n): return self._main._sequence*n
-    __rmul__ = __mul__
-    def __add__(self, other): return self._main._sequence + other
-    def __radd__(self, other): return other + self._main._sequence
-
-    ## following methods not implemented for keys ##
-    def __delitem__(self, i): raise TypeError('Can\'t delete items from keys')
-    def __iadd__(self, other): raise TypeError('Can\'t add in place to keys')
-    def __imul__(self, n): raise TypeError('Can\'t multiply keys in place')
-    def append(self, item): raise TypeError('Can\'t append items to keys')
-    def insert(self, i, item): raise TypeError('Can\'t insert items into keys')
-    def pop(self, i=-1): raise TypeError('Can\'t pop items from keys')
-    def remove(self, item): raise TypeError('Can\'t remove items from keys')
-    def extend(self, other): raise TypeError('Can\'t extend keys')
-
-class Items(object):
-    """
-    Custom object for accessing the items of an OrderedDict.
-    
-    Can be called like the normal ``OrderedDict.items`` method, but also
-    supports indexing and sequence methods.
-    """
-
-    def __init__(self, main):
-        self._main = main
-
-    def __call__(self):
-        """Pretend to be the items method."""
-        return self._main._items()
-
-    def __getitem__(self, index):
-        """Fetch the item at position i."""
-        if isinstance(index, types.SliceType):
-            # fetching a slice returns an OrderedDict
-            return self._main[index].items()
-        key = self._main._sequence[index]
-        return (key, self._main[key])
-
-    def __setitem__(self, index, item):
-        """Set item at position i to item."""
-        if isinstance(index, types.SliceType):
-            # NOTE: item must be an iterable (list of tuples)
-            self._main[index] = OrderedDict(item)
-        else:
-            # FIXME: Does this raise a sensible error?
-            orig = self._main.keys[index]
-            key, value = item
-            if self._main.strict and key in self and (key != orig):
-                raise ValueError('slice assignment must be from '
-                        'unique keys')
-            # delete the current one
-            del self._main[self._main._sequence[index]]
-            self._main.insert(index, key, value)
-
-    def __delitem__(self, i):
-        """Delete the item at position i."""
-        key = self._main._sequence[i]
-        if isinstance(i, types.SliceType):
-            for k in key:
-                # FIXME: efficiency?
-                del self._main[k]
-        else:
-            del self._main[key]
-
-    ### following methods pinched from UserList and adapted ###
-    def __repr__(self): return repr(self._main.items())
-
-    # FIXME: do we need to check if we are comparing with another ``Items``
-    #   object? (like the __cast method of UserList)
-    def __lt__(self, other): return self._main.items() <  other
-    def __le__(self, other): return self._main.items() <= other
-    def __eq__(self, other): return self._main.items() == other
-    def __ne__(self, other): return self._main.items() != other
-    def __gt__(self, other): return self._main.items() >  other
-    def __ge__(self, other): return self._main.items() >= other
-    def __cmp__(self, other): return cmp(self._main.items(), other)
-
-    def __contains__(self, item): return item in self._main.items()
-    def __len__(self): return len(self._main._sequence) # easier :-)
-    def __iter__(self): return self._main.iteritems()
-    def count(self, item): return self._main.items().count(item)
-    def index(self, item, *args): return self._main.items().index(item, *args)
-    def reverse(self): self._main.reverse()
-    def sort(self, *args, **kwds): self._main.sort(*args, **kwds)
-    def __mul__(self, n): return self._main.items()*n
-    __rmul__ = __mul__
-    def __add__(self, other): return self._main.items() + other
-    def __radd__(self, other): return other + self._main.items()
-
-    def append(self, item):
-        """Add an item to the end."""
-        # FIXME: this is only append if the key isn't already present
-        key, value = item
-        self._main[key] = value
-
-    def insert(self, i, item):
-        key, value = item
-        self._main.insert(i, key, value)
-
-    def pop(self, i=-1):
-        key = self._main._sequence[i]
-        return (key, self._main.pop(key))
-
-    def remove(self, item):
-        key, value = item
-        try:
-            assert value == self._main[key]
-        except (KeyError, AssertionError):
-            raise ValueError('ValueError: list.remove(x): x not in list')
-        else:
-            del self._main[key]
-
-    def extend(self, other):
-        # FIXME: is only a true extend if none of the keys already present
-        for item in other:
-            key, value = item
-            self._main[key] = value
-
-    def __iadd__(self, other):
-        self.extend(other)
-
-    ## following methods not implemented for items ##
-
-    def __imul__(self, n): raise TypeError('Can\'t multiply items in place')
-
-class Values(object):
-    """
-    Custom object for accessing the values of an OrderedDict.
-    
-    Can be called like the normal ``OrderedDict.values`` method, but also
-    supports indexing and sequence methods.
-    """
-
-    def __init__(self, main):
-        self._main = main
-
-    def __call__(self):
-        """Pretend to be the values method."""
-        return self._main._values()
-
-    def __getitem__(self, index):
-        """Fetch the value at position i."""
-        if isinstance(index, types.SliceType):
-            return [self._main[key] for key in self._main._sequence[index]]
-        else:
-            return self._main[self._main._sequence[index]]
-
-    def __setitem__(self, index, value):
-        """
-        Set the value at position i to value.
-        
-        You can only do slice assignment to values if you supply a sequence of
-        equal length to the slice you are replacing.
-        """
-        if isinstance(index, types.SliceType):
-            keys = self._main._sequence[index]
-            if len(keys) != len(value):
-                raise ValueError('attempt to assign sequence of size %s '
-                    'to slice of size %s' % (len(name), len(keys)))
-            # FIXME: efficiency?  Would be better to calculate the indexes
-            #   directly from the slice object
-            # NOTE: the new keys can collide with existing keys (or even
-            #   contain duplicates) - these will overwrite
-            for key, val in zip(keys, value):
-                self._main[key] = val
-        else:
-            self._main[self._main._sequence[index]] = value
-
-    ### following methods pinched from UserList and adapted ###
-    def __repr__(self): return repr(self._main.values())
-
-    # FIXME: do we need to check if we are comparing with another ``Values``
-    #   object? (like the __cast method of UserList)
-    def __lt__(self, other): return self._main.values() <  other
-    def __le__(self, other): return self._main.values() <= other
-    def __eq__(self, other): return self._main.values() == other
-    def __ne__(self, other): return self._main.values() != other
-    def __gt__(self, other): return self._main.values() >  other
-    def __ge__(self, other): return self._main.values() >= other
-    def __cmp__(self, other): return cmp(self._main.values(), other)
-
-    def __contains__(self, item): return item in self._main.values()
-    def __len__(self): return len(self._main._sequence) # easier :-)
-    def __iter__(self): return self._main.itervalues()
-    def count(self, item): return self._main.values().count(item)
-    def index(self, item, *args): return self._main.values().index(item, *args)
-
-    def reverse(self):
-        """Reverse the values"""
-        vals = self._main.values()
-        vals.reverse()
-        # FIXME: efficiency
-        self[:] = vals
-
-    def sort(self, *args, **kwds):
-        """Sort the values."""
-        vals = self._main.values()
-        vals.sort(*args, **kwds)
-        self[:] = vals
-
-    def __mul__(self, n): return self._main.values()*n
-    __rmul__ = __mul__
-    def __add__(self, other): return self._main.values() + other
-    def __radd__(self, other): return other + self._main.values()
-
-    ## following methods not implemented for values ##
-    def __delitem__(self, i): raise TypeError('Can\'t delete items from values')
-    def __iadd__(self, other): raise TypeError('Can\'t add in place to values')
-    def __imul__(self, n): raise TypeError('Can\'t multiply values in place')
-    def append(self, item): raise TypeError('Can\'t append items to values')
-    def insert(self, i, item): raise TypeError('Can\'t insert items into values')
-    def pop(self, i=-1): raise TypeError('Can\'t pop items from values')
-    def remove(self, item): raise TypeError('Can\'t remove items from values')
-    def extend(self, other): raise TypeError('Can\'t extend values')
-
-class SequenceOrderedDict(OrderedDict):
-    """
-    Experimental version of OrderedDict that has a custom object for ``keys``,
-    ``values``, and ``items``.
-    
-    These are callable sequence objects that work as methods, or can be
-    manipulated directly as sequences.
-    
-    Test for ``keys``, ``items`` and ``values``.
-    
-    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
-    >>> d
-    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
-    >>> d.keys
-    [1, 2, 3]
-    >>> d.keys()
-    [1, 2, 3]
-    >>> d.setkeys((3, 2, 1))
-    >>> d
-    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
-    >>> d.setkeys((1, 2, 3))
-    >>> d.keys[0]
-    1
-    >>> d.keys[:]
-    [1, 2, 3]
-    >>> d.keys[-1]
-    3
-    >>> d.keys[-2]
-    2
-    >>> d.keys[0:2] = [2, 1]
-    >>> d
-    SequenceOrderedDict([(2, 3), (1, 2), (3, 4)])
-    >>> d.keys.reverse()
-    >>> d.keys
-    [3, 1, 2]
-    >>> d.keys = [1, 2, 3]
-    >>> d
-    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
-    >>> d.keys = [3, 1, 2]
-    >>> d
-    SequenceOrderedDict([(3, 4), (1, 2), (2, 3)])
-    >>> a = SequenceOrderedDict()
-    >>> b = SequenceOrderedDict()
-    >>> a.keys == b.keys
-    1
-    >>> a['a'] = 3
-    >>> a.keys == b.keys
-    0
-    >>> b['a'] = 3
-    >>> a.keys == b.keys
-    1
-    >>> b['b'] = 3
-    >>> a.keys == b.keys
-    0
-    >>> a.keys > b.keys
-    0
-    >>> a.keys < b.keys
-    1
-    >>> 'a' in a.keys
-    1
-    >>> len(b.keys)
-    2
-    >>> 'c' in d.keys
-    0
-    >>> 1 in d.keys
-    1
-    >>> [v for v in d.keys]
-    [3, 1, 2]
-    >>> d.keys.sort()
-    >>> d.keys
-    [1, 2, 3]
-    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True)
-    >>> d.keys[::-1] = [1, 2, 3]
-    >>> d
-    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
-    >>> d.keys[:2]
-    [3, 2]
-    >>> d.keys[:2] = [1, 3]
-    Traceback (most recent call last):
-    KeyError: 'Keylist is not the same as current keylist.'
-
-    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
-    >>> d
-    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
-    >>> d.values
-    [2, 3, 4]
-    >>> d.values()
-    [2, 3, 4]
-    >>> d.setvalues((4, 3, 2))
-    >>> d
-    SequenceOrderedDict([(1, 4), (2, 3), (3, 2)])
-    >>> d.values[::-1]
-    [2, 3, 4]
-    >>> d.values[0]
-    4
-    >>> d.values[-2]
-    3
-    >>> del d.values[0]
-    Traceback (most recent call last):
-    TypeError: Can't delete items from values
-    >>> d.values[::2] = [2, 4]
-    >>> d
-    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
-    >>> 7 in d.values
-    0
-    >>> len(d.values)
-    3
-    >>> [val for val in d.values]
-    [2, 3, 4]
-    >>> d.values[-1] = 2
-    >>> d.values.count(2)
-    2
-    >>> d.values.index(2)
-    0
-    >>> d.values[-1] = 7
-    >>> d.values
-    [2, 3, 7]
-    >>> d.values.reverse()
-    >>> d.values
-    [7, 3, 2]
-    >>> d.values.sort()
-    >>> d.values
-    [2, 3, 7]
-    >>> d.values.append('anything')
-    Traceback (most recent call last):
-    TypeError: Can't append items to values
-    >>> d.values = (1, 2, 3)
-    >>> d
-    SequenceOrderedDict([(1, 1), (2, 2), (3, 3)])
-    
-    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
-    >>> d
-    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
-    >>> d.items()
-    [(1, 2), (2, 3), (3, 4)]
-    >>> d.setitems([(3, 4), (2 ,3), (1, 2)])
-    >>> d
-    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
-    >>> d.items[0]
-    (3, 4)
-    >>> d.items[:-1]
-    [(3, 4), (2, 3)]
-    >>> d.items[1] = (6, 3)
-    >>> d.items
-    [(3, 4), (6, 3), (1, 2)]
-    >>> d.items[1:2] = [(9, 9)]
-    >>> d
-    SequenceOrderedDict([(3, 4), (9, 9), (1, 2)])
-    >>> del d.items[1:2]
-    >>> d
-    SequenceOrderedDict([(3, 4), (1, 2)])
-    >>> (3, 4) in d.items
-    1
-    >>> (4, 3) in d.items
-    0
-    >>> len(d.items)
-    2
-    >>> [v for v in d.items]
-    [(3, 4), (1, 2)]
-    >>> d.items.count((3, 4))
-    1
-    >>> d.items.index((1, 2))
-    1
-    >>> d.items.index((2, 1))
-    Traceback (most recent call last):
-    ValueError: list.index(x): x not in list
-    >>> d.items.reverse()
-    >>> d.items
-    [(1, 2), (3, 4)]
-    >>> d.items.reverse()
-    >>> d.items.sort()
-    >>> d.items
-    [(1, 2), (3, 4)]
-    >>> d.items.append((5, 6))
-    >>> d.items
-    [(1, 2), (3, 4), (5, 6)]
-    >>> d.items.insert(0, (0, 0))
-    >>> d.items
-    [(0, 0), (1, 2), (3, 4), (5, 6)]
-    >>> d.items.insert(-1, (7, 8))
-    >>> d.items
-    [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)]
-    >>> d.items.pop()
-    (5, 6)
-    >>> d.items
-    [(0, 0), (1, 2), (3, 4), (7, 8)]
-    >>> d.items.remove((1, 2))
-    >>> d.items
-    [(0, 0), (3, 4), (7, 8)]
-    >>> d.items.extend([(1, 2), (5, 6)])
-    >>> d.items
-    [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)]
-    """
-
-    def __init__(self, init_val=(), strict=True):
-        OrderedDict.__init__(self, init_val, strict=strict)
-        self._keys = self.keys
-        self._values = self.values
-        self._items = self.items
-        self.keys = Keys(self)
-        self.values = Values(self)
-        self.items = Items(self)
-        self._att_dict = {
-            'keys': self.setkeys,
-            'items': self.setitems,
-            'values': self.setvalues,
-        }
-
-    def __setattr__(self, name, value):
-        """Protect keys, items, and values."""
-        if not '_att_dict' in self.__dict__:
-            object.__setattr__(self, name, value)
-        else:
-            try:
-                fun = self._att_dict[name]
-            except KeyError:
-                OrderedDict.__setattr__(self, name, value)
-            else:
-                fun(value)
-
-if __name__ == '__main__':
-    if INTP_VER < (2, 3):
-        raise RuntimeError("Tests require Python v.2.3 or later")
-    # turn off warnings for tests
-    warnings.filterwarnings('ignore')
-    # run the code tests in doctest format
-    import doctest
-    m = sys.modules.get('__main__')
-    globs = m.__dict__.copy()
-    globs.update({
-        'INTP_VER': INTP_VER,
-    })
-    doctest.testmod(m, globs=globs)
-
Index: c/sas/data_util/ordereddict.py
===================================================================
--- src/sas/data_util/ordereddict.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,128 +1,0 @@
-#!/usr/bin/env python
-"""Backport from python2.7 to python <= 2.6."""
-
-from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
-
-try:
-    from itertools import izip_longest as _zip_longest
-except ImportError:
-
-    from itertools import izip
-
-    def _zip_longest(*args, **kwds):
-        # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
-        fillvalue = kwds.get('fillvalue')
-        def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
-            yield counter()         # yields the fillvalue, or raises IndexError
-        fillers = _repeat(fillvalue)
-        iters = [_chain(it, sentinel(), fillers) for it in args]
-        try:
-            for tup in izip(*iters):
-                yield tup
-        except IndexError:
-            pass
-
-class OrderedDict(dict):
-
-    def __init__(self, *args, **kwds):
-        if len(args) > 1:
-            raise TypeError('expected at most 1 arguments, got %d' % len(args))
-        if not hasattr(self, '_keys'):
-            self._keys = []
-        self.update(*args, **kwds)
-
-    def clear(self):
-        del self._keys[:]
-        dict.clear(self)
-
-    def __setitem__(self, key, value):
-        if key not in self:
-            self._keys.append(key)
-        dict.__setitem__(self, key, value)
-
-    def __delitem__(self, key):
-        dict.__delitem__(self, key)
-        self._keys.remove(key)
-
-    def __iter__(self):
-        return iter(self._keys)
-
-    def __reversed__(self):
-        return reversed(self._keys)
-
-    def popitem(self):
-        if not self:
-            raise KeyError('dictionary is empty')
-        key = self._keys.pop()
-        value = dict.pop(self, key)
-        return key, value
-
-    def __reduce__(self):
-        items = [[k, self[k]] for k in self]
-        inst_dict = vars(self).copy()
-        inst_dict.pop('_keys', None)
-        return (self.__class__, (items,), inst_dict)
-
-    def setdefault(self, key, default=None):
-        try:
-            return self[key]
-        except KeyError:
-            self[key] = default
-        return default
-
-    def update(self, other=(), **kwds):
-        if hasattr(other, "keys"):
-            for key in other.keys():
-                self[key] = other[key]
-        else:
-            for key, value in other:
-                self[key] = value
-        for key, value in kwds.items():
-            self[key] = value
-
-    __marker = object()
-
-    def pop(self, key, default=__marker):
-        try:
-            value = self[key]
-        except KeyError:
-            if default is self.__marker:
-                raise
-            return default
-        else:
-            del self[key]
-            return value
-
-    def keys(self):
-        return list(self)
-
-    def values(self):
-        return [self[key] for key in self]
-
-    def items(self):
-        return [(key, self[key]) for key in self]
-
-    def __repr__(self):
-        if not self:
-            return '%s()' % (self.__class__.__name__,)
-        return '%s(%r)' % (self.__class__.__name__, list(self.items()))
-
-    def copy(self):
-        return self.__class__(self)
-
-    @classmethod
-    def fromkeys(cls, iterable, value=None):
-        d = cls()
-        for key in iterable:
-            d[key] = value
-        return d
-
-    def __eq__(self, other):
-        if isinstance(other, OrderedDict):
-            return all(p==q for p, q in  _zip_longest(self.items(), other.items()))
-        return dict.__eq__(self, other)
-
-
-
-# End class OrderedDict
-
Index: c/sas/data_util/ordereddicttest.py
===================================================================
--- src/sas/data_util/ordereddicttest.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,186 +1,0 @@
-#!/usr/bin/env python
-
-from random import shuffle
-import copy
-import inspect
-import pickle
-import unittest
-
-
-from ordereddict import OrderedDict
-
-
-class TestOrderedDict(unittest.TestCase):
-
-    def test_init(self):
-        self.assertRaises(TypeError, OrderedDict, ([('a', 1), ('b', 2)], None))
-                # too many args
-        pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
-        self.assertEqual(sorted(OrderedDict(dict(pairs)).items()), pairs)           # dict input
-        self.assertEqual(sorted(OrderedDict(**dict(pairs)).items()), pairs)         # kwds input
-        self.assertEqual(list(OrderedDict(pairs).items()), pairs)                   # pairs input
-        self.assertEqual(list(OrderedDict([('a', 1), ('b', 2), ('c', 9), ('d', 4)],
-                                          c=3, e=5).items()), pairs)                # mixed input
-
-        # make sure no positional args conflict with possible kwdargs
-        self.assertEqual(inspect.getargspec(OrderedDict.__dict__['__init__'])[0],
-                         ['self'])
-
-        # Make sure that direct calls to __init__ do not clear previous contents
-        d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
-        d.__init__([('e', 5), ('f', 6)], g=7, d=4)
-        self.assertEqual(list(d.items()),
-            [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
-
-    def test_update(self):
-        self.assertRaises(TypeError, OrderedDict().update, [('a', 1), ('b',
-            2)], None)                        # too many args
-        pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
-        od = OrderedDict()
-        od.update(dict(pairs))
-        self.assertEqual(sorted(od.items()), pairs)                                 # dict input
-        od = OrderedDict()
-        od.update(**dict(pairs))
-        self.assertEqual(sorted(od.items()), pairs)                                 # kwds input
-        od = OrderedDict()
-        od.update(pairs)
-        self.assertEqual(list(od.items()), pairs)                                   # pairs input
-        od = OrderedDict()
-        od.update([('a', 1), ('b', 2), ('c', 9), ('d', 4)], c=3, e=5)
-        self.assertEqual(list(od.items()), pairs)                                   # mixed input
-
-        # Make sure that direct calls to update do not clear previous contents
-        # add that updates items are not moved to the end
-        d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
-        d.update([('e', 5), ('f', 6)], g=7, d=4)
-        self.assertEqual(list(d.items()),
-            [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
-
-    def test_clear(self):
-        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
-        shuffle(pairs)
-        od = OrderedDict(pairs)
-        self.assertEqual(len(od), len(pairs))
-        od.clear()
-        self.assertEqual(len(od), 0)
-
-    def test_delitem(self):
-        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
-        od = OrderedDict(pairs)
-        del od['a']
-        self.assert_('a' not in od)
-        self.assertRaises(KeyError, od.__delitem__, 'a')
-        self.assertEqual(list(od.items()), pairs[:2] + pairs[3:])
-
-    def test_setitem(self):
-        od = OrderedDict([('d', 1), ('b', 2), ('c', 3), ('a', 4), ('e', 5)])
-        od['c'] = 10           # existing element
-        od['f'] = 20           # new element
-        self.assertEqual(list(od.items()),
-                         [('d', 1), ('b', 2), ('c', 10), ('a', 4), ('e', 5), ('f', 20)])
-
-    def test_iterators(self):
-        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
-        shuffle(pairs)
-        od = OrderedDict(pairs)
-        self.assertEqual(list(od), [t[0] for t in pairs])
-        self.assertEqual(list(od.keys()), [t[0] for t in pairs])
-        self.assertEqual(list(od.values()), [t[1] for t in pairs])
-        self.assertEqual(list(od.items()), pairs)
-        self.assertEqual(list(reversed(od)),
-                         [t[0] for t in reversed(pairs)])
-
-    def test_popitem(self):
-        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
-        shuffle(pairs)
-        od = OrderedDict(pairs)
-        while pairs:
-            self.assertEqual(od.popitem(), pairs.pop())
-        self.assertRaises(KeyError, od.popitem)
-        self.assertEqual(len(od), 0)
-
-    def test_pop(self):
-        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
-        shuffle(pairs)
-        od = OrderedDict(pairs)
-        shuffle(pairs)
-        while pairs:
-            k, v = pairs.pop()
-            self.assertEqual(od.pop(k), v)
-        self.assertRaises(KeyError, od.pop, 'xyz')
-        self.assertEqual(len(od), 0)
-        self.assertEqual(od.pop(k, 12345), 12345)
-
-    def test_equality(self):
-        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
-        shuffle(pairs)
-        od1 = OrderedDict(pairs)
-        od2 = OrderedDict(pairs)
-        self.assertEqual(od1, od2)          # same order implies equality
-        pairs = pairs[2:] + pairs[:2]
-        od2 = OrderedDict(pairs)
-        self.assertNotEqual(od1, od2)       # different order implies inequality
-        # comparison to regular dict is not order sensitive
-        self.assertEqual(od1, dict(od2))
-        self.assertEqual(dict(od2), od1)
-        # different length implied inequality
-        self.assertNotEqual(od1, OrderedDict(pairs[:-1]))
-
-    def test_copying(self):
-        # Check that ordered dicts are copyable, deepcopyable, picklable,
-        # and have a repr/eval round-trip
-        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
-        od = OrderedDict(pairs)
-        update_test = OrderedDict()
-        update_test.update(od)
-        for i, dup in enumerate([
-                    od.copy(),
-                    copy.copy(od),
-                    copy.deepcopy(od),
-                    pickle.loads(pickle.dumps(od, 0)),
-                    pickle.loads(pickle.dumps(od, 1)),
-                    pickle.loads(pickle.dumps(od, 2)),
-                    pickle.loads(pickle.dumps(od, -1)),
-                    eval(repr(od)),
-                    update_test,
-                    OrderedDict(od),
-                    ]):
-            self.assert_(dup is not od)
-            self.assertEquals(dup, od)
-            self.assertEquals(list(dup.items()), list(od.items()))
-            self.assertEquals(len(dup), len(od))
-            self.assertEquals(type(dup), type(od))
-
-    def test_repr(self):
-        od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])
-        self.assertEqual(repr(od),
-            "OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
-        self.assertEqual(eval(repr(od)), od)
-        self.assertEqual(repr(OrderedDict()), "OrderedDict()")
-
-    def test_setdefault(self):
-        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
-        shuffle(pairs)
-        od = OrderedDict(pairs)
-        pair_order = list(od.items())
-        self.assertEqual(od.setdefault('a', 10), 3)
-        # make sure order didn't change
-        self.assertEqual(list(od.items()), pair_order)
-        self.assertEqual(od.setdefault('x', 10), 10)
-        # make sure 'x' is added to the end
-        self.assertEqual(list(od.items())[-1], ('x', 10))
-
-    def test_reinsert(self):
-        # Given insert a, insert b, delete a, re-insert a,
-        # verify that a is now later than b.
-        od = OrderedDict()
-        od['a'] = 1
-        od['b'] = 2
-        del od['a']
-        od['a'] = 1
-        self.assertEqual(list(od.items()), [('b', 2), ('a', 1)])
-
-if __name__ == "__main__":
-
-    unittest.main()
-
Index: c/sas/data_util/pathutils.py
===================================================================
--- src/sas/data_util/pathutils.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,49 +1,0 @@
-#!/usr/bin/env python
-"""
-Utilities for path manipulation. Not to be confused with the pathutils module
-from the pythonutils package (http://groups.google.com/group/pythonutils).
-"""
-
-# NOTE: If enough of _that_ pathutils functionality is required, we can switch
-# this module for that one.
-
-# TODO: Make algorithm more robust and complete; consider using abspath.
-
-__all__ = ['relpath']
-
-from os.path import join
-from os.path import sep
-
-def relpath(p1, p2):
-    """Compute the relative path of p1 with respect to p2."""
-
-    def commonpath(L1, L2, common=[]):
-        if len(L1) < 1: return (common, L1, L2)
-        if len(L2) < 1: return (common, L1, L2)
-        if L1[0] != L2[0]: return (common, L1, L2)
-        return commonpath(L1[1:], L2[1:], common=common+[L1[0]])
-
-    # if the strings are equal, then return "."
-    if p1 == p2: return "."
-    (common,L1,L2) = commonpath(p2.split(sep), p1.split(sep))
-    # if there is nothing in common, then return an empty string
-    if not common: return ""
-    # otherwise, replace the common pieces with "../" (or "..\")
-    p = [(".."+sep) * len(L1)] + L2
-    return join(*p)
-
-def test():
-    p1 = sep.join(["a","b","c","d"])
-    p2 = sep.join(["a","b","c1","d1"])
-    p3 = sep.join(["a","b","c","d","e"])
-    p4 = sep.join(["a","b","c","d1","e"])
-    p5 = sep.join(["w","x","y","z"])
-
-    assert relpath(p1, p1) == "."
-    assert relpath(p2, p1) ==  sep.join(["..", "..", "c1", "d1"])
-    assert relpath(p3, p1) == "e"
-    assert relpath(p4, p1) ==  sep.join(["..", "d1", "e"])
-    assert relpath(p5, p1) == ""
-
-if __name__ == '__main__':
-    test()
Index: c/sas/data_util/registry.py
===================================================================
--- src/sas/data_util/registry.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,174 +1,0 @@
-# This program is public domain
-"""
-File extension registry.
-
-This provides routines for opening files based on extension,
-and registers the built-in file extensions.
-"""
-
-import os.path
-
-class ExtensionRegistry(object):
-    """
-    Associate a file loader with an extension.
-
-    Note that there may be multiple loaders for the same extension.
-
-    Example: ::
-
-        registry = ExtensionRegistry()
-
-        # Add an association by setting an element
-        registry['.zip'] = unzip
-        
-        # Multiple extensions for one loader
-        registry['.tgz'] = untar
-        registry['.tar.gz'] = untar
-
-        # Generic extensions to use after trying more specific extensions; 
-        # these will be checked after the more specific extensions fail.
-        registry['.gz'] = gunzip
-
-        # Multiple loaders for one extension
-        registry['.cx'] = cx1
-        registry['.cx'] = cx2
-        registry['.cx'] = cx3
-
-        # Show registered extensions
-        print registry.extensions()
-        
-        # Can also register a format name for explicit control from caller
-        registry['cx3'] = cx3
-        print registry.formats()
-
-        # Retrieve loaders for a file name
-        registry.lookup('hello.cx') -> [cx3,cx2,cx1]
-
-        # Run loader on a filename
-        registry.load('hello.cx') ->
-            try:
-                return cx3('hello.cx')
-            except:
-                try:
-                    return cx2('hello.cx')
-                except:
-                    return cx1('hello.cx')
-
-        # Load in a specific format ignoring extension
-        registry.load('hello.cx',format='cx3') ->
-            return cx3('hello.cx')
-    """
-    def __init__(self, **kw):
-        self.loaders = {}
-    def __setitem__(self, ext, loader):
-        if ext not in self.loaders:
-            self.loaders[ext] = []
-        self.loaders[ext].insert(0,loader)
-    def __getitem__(self, ext):
-        return self.loaders[ext]
-    def __contains__(self, ext):
-        return ext in self.loaders
-    def formats(self):
-        """
-        Return a sorted list of the registered formats.
-        """
-        names = [a for a in self.loaders.keys() if not a.startswith('.')]
-        names.sort()
-        return names
-    def extensions(self):
-        """
-        Return a sorted list of registered extensions.
-        """
-        exts = [a for a in self.loaders.keys() if a.startswith('.')]
-        exts.sort()
-        return exts
-    def lookup(self, path):
-        """
-        Return the loader associated with the file type of path.
-        
-        Raises ValueError if file type is not known.
-        """        
-        # Find matching extensions
-        extlist = [ext for ext in self.extensions() if path.endswith(ext)]
-        # Sort matching extensions by decreasing order of length
-        extlist.sort(lambda a,b: len(a)<len(b))
-        # Combine loaders for matching extensions into one big list
-        loaders = []
-        for L in [self.loaders[ext] for ext in extlist]:
-            loaders.extend(L)
-        # Remove duplicates if they exist
-        if len(loaders) != len(set(loaders)):
-            result = []
-            for L in loaders:
-                if L not in result: result.append(L)
-            loaders = L
-        # Raise an error if there are no matching extensions
-        if len(loaders) == 0:
-            raise ValueError, "Unknown file type for "+path
-        # All done
-        return loaders
-    def load(self, path, format=None):
-        """
-        Call the loader for the file type of path.
-
-        Raises ValueError if no loader is available.
-        Raises KeyError if format is not available.
-        May raise a loader-defined exception if loader fails.        
-        """
-        if format is None:
-            loaders = self.lookup(path)
-        else:
-            loaders = self.loaders[format]
-        for fn in loaders:
-            try:
-                return fn(path)
-            except:
-                pass # give other loaders a chance to succeed
-        # If we get here it is because all loaders failed
-        raise # reraises last exception
-
-def test():
-    reg = ExtensionRegistry()
-    class CxError(Exception): pass
-    def cx(file): return 'cx'
-    def new_cx(file): return 'new_cx'
-    def fail_cx(file): raise CxError
-    def cat(file): return 'cat'
-    def gunzip(file): return 'gunzip'
-    reg['.cx'] = cx
-    reg['.cx1'] = cx
-    reg['.cx'] = new_cx
-    reg['.gz'] = gunzip
-    reg['.cx.gz'] = new_cx
-    reg['.cx1.gz'] = fail_cx
-    reg['.cx1'] = fail_cx
-    reg['.cx2'] = fail_cx
-    reg['new_cx'] = new_cx
-
-    # Two loaders associated with .cx
-    assert reg.lookup('hello.cx') == [new_cx,cx]
-    # Make sure the last loader applies first
-    assert reg.load('hello.cx') == 'new_cx'
-    # Make sure the next loader applies if the first fails
-    assert reg.load('hello.cx1') == 'cx'
-    # Make sure the format override works
-    assert reg.load('hello.cx1',format='.cx.gz') == 'new_cx'
-    # Make sure the format override works
-    assert reg.load('hello.cx1',format='new_cx') == 'new_cx'
-    # Make sure the case of all loaders failing is correct
-    try:  reg.load('hello.cx2')
-    except CxError: pass # correct failure
-    else: raise AssertError,"Incorrect error on load failure"
-    # Make sure the case of no loaders fails correctly
-    try: reg.load('hello.missing')
-    except ValueError,msg:
-        assert str(msg)=="Unknown file type for hello.missing",'Message: <%s>'%(msg)
-    else: raise AssertError,"No error raised for missing extension"
-    assert reg.formats() == ['new_cx']
-    assert reg.extensions() == ['.cx','.cx.gz','.cx1','.cx1.gz','.cx2','.gz']
-    # make sure that it supports multiple '.' in filename
-    assert reg.load('hello.extra.cx1') == 'cx'
-    assert reg.load('hello.gz') == 'gunzip'
-    assert reg.load('hello.cx1.gz') == 'gunzip' # Since .cx1.gz fails
-
-if __name__ == "__main__": test()
Index: c/sas/data_util/release_notes.txt
===================================================================
--- src/sas/data_util/release_notes.txt	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,60 +1,0 @@
-Release Notes
-=============
-
-Package name: data_util 0.1.5
-
-1-	Version 0.1.5
-	- Release date: 28/4/2010
-	- Added OrderedDict (backport from 2.7)
-	- Improved NeXus unit support
-	- Number/uncertainty formats fixes
- 
-	Version 0.1.2
-	- Release date: 7/4/2009
-	- Added calcthread
-	
- 	Version 0.1.1
-	- Release date: 4/21/2009
-	
-	Version 0.1
-	- Release date: 8/1/2008
-	- Contains useful data handling utilities from reflectometry group. 
-
-2- Downloading and Installing
-
-	2.1- System Requirements:
-		- Python version >= 2.4 should be running on the system
-
-	2.2- Installing:
-		- Get the code from svn://danse.us/common/releases/data_util-0.1.1
-		- The following modules are required:
-			* numpy
-
-3- Known Issues
-
-	3.1- All systems:
-		- None
-
-	3.2- Windows:
-		- None
-
-	3.3- Linux:
-		- None
-
-4- Troubleshooting
-
-	- None
-
-5- Frequently Asked Questions
-
-	- None
-
-6- Other Resources
-
-	- None
-
-	
-
-	
-
-
Index: c/sas/data_util/uncertainty.py
===================================================================
--- src/sas/data_util/uncertainty.py	(revision ac7be54ccf932e813cbe6e41e05b682a1630a03e)
+++ 	(revision )
@@ -1,316 +1,0 @@
-r"""
-Uncertainty propagation class for arithmetic, log and exp.
-
-Based on scalars or numpy vectors, this class allows you to store and 
-manipulate values+uncertainties, with propagation of gaussian error for
-addition, subtraction, multiplication, division, power, exp and log.
-
-Storage properties are determined by the numbers used to set the value
-and uncertainty.  Be sure to use floating point uncertainty vectors 
-for inplace operations since numpy does not do automatic type conversion.
-Normal operations can use mixed integer and floating point.  In place
-operations such as *a \*= b* create at most one extra copy for each operation.
-By contrast, *c = a\*b* uses four intermediate vectors, so shouldn't be used
-for huge arrays.
-"""
-
-from __future__ import division
-
-import numpy
-import err1d
-from formatnum import format_uncertainty
-
-__all__ = ['Uncertainty']
-
-# TODO: rename to Measurement and add support for units?
-# TODO: C implementation of *,/,**?
-class Uncertainty(object):
-    # Make standard deviation available
-    def _getdx(self): return numpy.sqrt(self.variance)
-    def _setdx(self,dx): 
-        # Direct operation
-        #    variance = dx**2
-        # Indirect operation to avoid temporaries
-        self.variance[:] = dx
-        self.variance **= 2
-    dx = property(_getdx,_setdx,doc="standard deviation")
-
-    # Constructor
-    def __init__(self, x, variance=None):
-        self.x, self.variance = x, variance    
- 
-    # Numpy array slicing operations
-    def __len__(self): 
-        return len(self.x)
-    def __getitem__(self,key): 
-        return Uncertainty(self.x[key],self.variance[key])
-    def __setitem__(self,key,value):
-        self.x[key] = value.x
-        self.variance[key] = value.variance
-    def __delitem__(self, key):
-        del self.x[key]
-        del self.variance[key]
-    #def __iter__(self): pass # Not sure we need iter
-
-    # Normal operations: may be of mixed type
-    def __add__(self, other):
-        if isinstance(other,Uncertainty):
-            return Uncertainty(*err1d.add(self.x,self.variance,other.x,other.variance))
-        else:
-            return Uncertainty(self.x+other, self.variance+0) # Force copy
-    def __sub__(self, other):
-        if isinstance(other,Uncertainty):
-            return Uncertainty(*err1d.sub(self.x,self.variance,other.x,other.variance))
-        else:
-            return Uncertainty(self.x-other, self.variance+0) # Force copy
-    def __mul__(self, other):
-        if isinstance(other,Uncertainty):
-            return Uncertainty(*err1d.mul(self.x,self.variance,other.x,other.variance))
-        else:
-            return Uncertainty(self.x*other, self.variance*other**2)
-    def __truediv__(self, other):
-        if isinstance(other,Uncertainty):
-            return Uncertainty(*err1d.div(self.x,self.variance,other.x,other.variance))
-        else:
-            return Uncertainty(self.x/other, self.variance/other**2)
-    def __pow__(self, other):
-        if isinstance(other,Uncertainty):
-            # Haven't calcuated variance in (a+/-da) ** (b+/-db)
-            return NotImplemented
-        else:
-            return Uncertainty(*err1d.pow(self.x,self.variance,other))
-
-    # Reverse operations
-    def __radd__(self, other):
-        return Uncertainty(self.x+other, self.variance+0) # Force copy
-    def __rsub__(self, other):
-        return Uncertainty(other-self.x, self.variance+0)
-    def __rmul__(self, other):
-        return Uncertainty(self.x*other, self.variance*other**2)
-    def __rtruediv__(self, other):
-        x,variance = err1d.pow(self.x,self.variance,-1)
-        return Uncertainty(x*other,variance*other**2)
-    def __rpow__(self, other): return NotImplemented
-
-    # In-place operations: may be of mixed type
-    def __iadd__(self, other):
-        if isinstance(other,Uncertainty):
-            self.x,self.variance \
-                = err1d.add_inplace(self.x,self.variance,other.x,other.variance)
-        else:
-            self.x+=other
-        return self
-    def __isub__(self, other):
-        if isinstance(other,Uncertainty):
-            self.x,self.variance \
-                = err1d.sub_inplace(self.x,self.variance,other.x,other.variance)
-        else:
-            self.x-=other
-        return self
-    def __imul__(self, other):
-        if isinstance(other,Uncertainty):
-            self.x, self.variance \
-                = err1d.mul_inplace(self.x,self.variance,other.x,other.variance)
-        else:
-            self.x *= other
-            self.variance *= other**2
-        return self
-    def __itruediv__(self, other):
-        if isinstance(other,Uncertainty):
-            self.x,self.variance \
-                = err1d.div_inplace(self.x,self.variance,other.x,other.variance)
-        else:
-            self.x /= other
-            self.variance /= other**2
-        return self
-    def __ipow__(self, other):
-        if isinstance(other,Uncertainty):
-            # Haven't calcuated variance in (a+/-da) ** (b+/-db)
-            return NotImplemented
-        else:
-            self.x,self.variance = err1d.pow_inplace(self.x, self.variance, other)
-        return self
-
-    # Use true division instead of integer division
-    def __div__(self, other): return self.__truediv__(other)
-    def __rdiv__(self, other): return self.__rtruediv__(other)
-    def __idiv__(self, other): return self.__itruediv__(other)
-
-        
-    # Unary ops
-    def __neg__(self):
-        return Uncertainty(-self.x,self.variance)
-    def __pos__(self):
-        return self
-    def __abs__(self):
-        return Uncertainty(numpy.abs(self.x),self.variance)
-
-    def __str__(self):
-        #return str(self.x)+" +/- "+str(numpy.sqrt(self.variance))
-        if numpy.isscalar(self.x):
-            return format_uncertainty(self.x,numpy.sqrt(self.variance))
-        else:
-            return [format_uncertainty(v,dv) 
-                    for v,dv in zip(self.x,numpy.sqrt(self.variance))]
-    def __repr__(self):
-        return "Uncertainty(%s,%s)"%(str(self.x),str(self.variance))
-
-    # Not implemented
-    def __floordiv__(self, other): return NotImplemented
-    def __mod__(self, other): return NotImplemented
-    def __divmod__(self, other): return NotImplemented
-    def __mod__(self, other): return NotImplemented
-    def __lshift__(self, other): return NotImplemented
-    def __rshift__(self, other): return NotImplemented
-    def __and__(self, other): return NotImplemented
-    def __xor__(self, other): return NotImplemented
-    def __or__(self, other): return NotImplemented
-
-    def __rfloordiv__(self, other): return NotImplemented
-    def __rmod__(self, other): return NotImplemented
-    def __rdivmod__(self, other): return NotImplemented
-    def __rmod__(self, other): return NotImplemented
-    def __rlshift__(self, other): return NotImplemented
-    def __rrshift__(self, other): return NotImplemented
-    def __rand__(self, other): return NotImplemented
-    def __rxor__(self, other): return NotImplemented
-    def __ror__(self, other): return NotImplemented
-
-    def __ifloordiv__(self, other): return NotImplemented
-    def __imod__(self, other): return NotImplemented
-    def __idivmod__(self, other): return NotImplemented
-    def __imod__(self, other): return NotImplemented
-    def __ilshift__(self, other): return NotImplemented
-    def __irshift__(self, other): return NotImplemented
-    def __iand__(self, other): return NotImplemented
-    def __ixor__(self, other): return NotImplemented
-    def __ior__(self, other): return NotImplemented
-
-    def __invert__(self): return NotImplmented  # For ~x
-    def __complex__(self): return NotImplmented
-    def __int__(self): return NotImplmented
-    def __long__(self): return NotImplmented
-    def __float__(self): return NotImplmented
-    def __oct__(self): return NotImplmented
-    def __hex__(self): return NotImplmented
-    def __index__(self): return NotImplmented
-    def __coerce__(self): return NotImplmented
-
-    def log(self):
-        return Uncertainty(*err1d.log(self.x,self.variance))
-
-    def exp(self):
-        return Uncertainty(*err1d.exp(self.x,self.variance))
-
-def log(val): return self.log()
-def exp(val): return self.exp()
-
-def test():
-    a = Uncertainty(5,3)
-    b = Uncertainty(4,2)
-
-    # Scalar operations
-    z = a+4
-    assert z.x == 5+4 and z.variance == 3
-    z = a-4
-    assert z.x == 5-4 and z.variance == 3
-    z = a*4
-    assert z.x == 5*4 and z.variance == 3*4**2
-    z = a/4
-    assert z.x == 5./4 and z.variance == 3./4**2
-    
-    # Reverse scalar operations
-    z = 4+a
-    assert z.x == 4+5 and z.variance == 3
-    z = 4-a
-    assert z.x == 4-5 and z.variance == 3
-    z = 4*a
-    assert z.x == 4*5 and z.variance == 3*4**2
-    z = 4/a
-    assert z.x == 4./5 and abs(z.variance - 3./5**4 * 4**2) < 1e-15
-    
-    # Power operations
-    z = a**2
-    assert z.x == 5**2 and z.variance == 4*3*5**2
-    z = a**1
-    assert z.x == 5**1 and z.variance == 3
-    z = a**0
-    assert z.x == 5**0 and z.variance == 0
-    z = a**-1
-    assert z.x == 5**-1 and abs(z.variance - 3./5**4) < 1e-15
-
-    # Binary operations
-    z = a+b
-    assert z.x == 5+4 and z.variance == 3+2
-    z = a-b
-    assert z.x == 5-4 and z.variance == 3+2
-    z = a*b
-    assert z.x == 5*4 and z.variance == (5**2*2 + 4**2*3)
-    z = a/b
-    assert z.x == 5./4 and abs(z.variance - (3./5**2 + 2./4**2)*(5./4)**2) < 1e-15
-
-    # ===== Inplace operations =====    
-    # Scalar operations
-    y = a+0; y += 4
-    z = a+4
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-    y = a+0; y -= 4
-    z = a-4
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-    y = a+0; y *= 4
-    z = a*4
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-    y = a+0; y /= 4
-    z = a/4
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-
-    # Power operations
-    y = a+0; y **= 4
-    z = a**4
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-
-    # Binary operations
-    y = a+0; y += b
-    z = a+b
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-    y = a+0; y -= b
-    z = a-b
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-    y = a+0; y *= b
-    z = a*b
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-    y = a+0; y /= b
-    z = a/b
-    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
-
-
-    # =============== vector operations ================
-    # Slicing
-    z = Uncertainty(numpy.array([1,2,3,4,5]),numpy.array([2,1,2,3,2]))
-    assert z[2].x == 3 and z[2].variance == 2
-    assert (z[2:4].x == [3,4]).all()
-    assert (z[2:4].variance == [2,3]).all()
-    z[2:4] = Uncertainty(numpy.array([8,7]),numpy.array([4,5]))
-    assert z[2].x == 8 and z[2].variance == 4
-    A = Uncertainty(numpy.array([a.x]*2),numpy.array([a.variance]*2))
-    B = Uncertainty(numpy.array([b.x]*2),numpy.array([b.variance]*2))
-
-    # TODO complete tests of copy and inplace operations for vectors and slices.
-
-    # Binary operations
-    z = A+B
-    assert (z.x == 5+4).all() and (z.variance == 3+2).all()
-    z = A-B
-    assert (z.x == 5-4).all() and (z.variance == 3+2).all()
-    z = A*B
-    assert (z.x == 5*4).all() and (z.variance == (5**2*2 + 4**2*3)).all()
-    z = A/B
-    assert (z.x == 5./4).all()
-    assert (abs(z.variance - (3./5**2 + 2./4**2)*(5./4)**2) < 1e-15).all()
-    
-    # printing; note that sqrt(3) ~ 1.7
-    assert str(Uncertainty(5,3)) == "5.0(17)"
-    assert str(Uncertainty(15,3)) == "15.0(17)"
-    assert str(Uncertainty(151.23356,0.324185**2)) == "151.23(32)"
-
-if __name__ == "__main__": test()
Index: c/sas/data_util/uniquelist.py
===================================================================
--- src/sas/data_util/uniquelist.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,40 +1,0 @@
-#
-
-def uniquelist(inputlist, hash=None): 
-    '''remove redunduant elements from the give list
-    and return a list of unique elements.
-
-    inputlist: input list
-    hash: use this function to make the items in the list hashable.
-
-    Implementation details:
-        This function is order-preserving.
-    '''
-    if hash is None:
-        hash = __builtins__.hash
-    seen = {}
-    result = []
-    for item in inputlist:
-        marker = hash(item)
-        if marker in seen: continue
-        seen[marker] = 1
-        result.append(item)
-    return result
-
-
-def test():
-    assert uniquelist([1,2,2,3])==[1,2,3]
-    return
-
-
-def main():
-    test()
-    return
-
-if __name__ == '__main__': main()
-
-
-# version
-__id__ = "$Id$"
-
-# End of file 
Index: c/sas/dataloader/__init__.py
===================================================================
--- src/sas/dataloader/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,3 +1,0 @@
-from data_info import *
-from manipulations import *
-from readers import *
Index: c/sas/dataloader/data_info.py
===================================================================
--- src/sas/dataloader/data_info.py	(revision 3a5f7c82274a6d5e083ee2f7c32b483e06ee818d)
+++ 	(revision )
@@ -1,1227 +1,0 @@
-"""
-    Module that contains classes to hold information read from
-    reduced data files.
-
-    A good description of the data members can be found in
-    the CanSAS 1D XML data format:
-
-    http://www.smallangles.net/wgwiki/index.php/cansas1d_documentation
-"""
-#####################################################################
-#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.
-#See the license text in license.txt
-#copyright 2008, University of Tennessee
-######################################################################
-
-
-#TODO: Keep track of data manipulation in the 'process' data structure.
-#TODO: This module should be independent of plottables. We should write
-#        an adapter class for plottables when needed.
-
-#from sas.guitools.plottables import Data1D as plottable_1D
-from sas.data_util.uncertainty import Uncertainty
-import numpy
-import math
-
-class plottable_sesans1D(object):
-    """
-    SESANS is a place holder for 1D SESANS plottables.
-
-    #TODO: This was directly copied from the plottables_1D. Modified Somewhat.
-    #Class has been updated.
-    """
-    # The presence of these should be mutually
-    # exclusive with the presence of Qdev (dx)
-    x = None
-    y = None
-    lam = None
-    dx = None
-    dy = None
-    dlam = None
-    ## Slit smearing length
-    dxl = None
-    ## Slit smearing width
-    dxw = None
-
-    # Units
-    _xaxis = ''
-    _xunit = ''
-    _yaxis = ''
-    _yunit = ''
-
-    def __init__(self, x, y, lam, dx=None, dy=None, dlam=None):
-#        print "SESANS plottable working"
-        self.x = numpy.asarray(x)
-        self.y = numpy.asarray(y)
-        self.lam = numpy.asarray(lam)
-        if dx is not None:
-            self.dx = numpy.asarray(dx)
-        if dy is not None:
-            self.dy = numpy.asarray(dy)
-        if dlam is not None:
-            self.dlam = numpy.asarray(dlam)
-
-    def xaxis(self, label, unit):
-        """
-        set the x axis label and unit
-        """
-        self._xaxis = label
-        self._xunit = unit
-
-    def yaxis(self, label, unit):
-        """
-        set the y axis label and unit
-        """
-        self._yaxis = label
-        self._yunit = unit
-
-
-class plottable_1D(object):
-    """
-    Data1D is a place holder for 1D plottables.
-    """
-    # The presence of these should be mutually
-    # exclusive with the presence of Qdev (dx)
-    x = None
-    y = None
-    dx = None
-    dy = None
-    ## Slit smearing length
-    dxl = None
-    ## Slit smearing width
-    dxw = None
-
-    # Units
-    _xaxis = ''
-    _xunit = ''
-    _yaxis = ''
-    _yunit = ''
-
-    def __init__(self, x, y, dx=None, dy=None, dxl=None, dxw=None):
-        self.x = numpy.asarray(x)
-        self.y = numpy.asarray(y)
-        if dx is not None:
-            self.dx = numpy.asarray(dx)
-        if dy is not None:
-            self.dy = numpy.asarray(dy)
-        if dxl is not None:
-            self.dxl = numpy.asarray(dxl)
-        if dxw is not None:
-            self.dxw = numpy.asarray(dxw)
-
-    def xaxis(self, label, unit):
-        """
-        set the x axis label and unit
-        """
-        self._xaxis = label
-        self._xunit = unit
-
-    def yaxis(self, label, unit):
-        """
-        set the y axis label and unit
-        """
-        self._yaxis = label
-        self._yunit = unit
-
-
-class plottable_2D(object):
-    """
-    Data2D is a place holder for 2D plottables.
-    """
-    xmin = None
-    xmax = None
-    ymin = None
-    ymax = None
-    data = None
-    qx_data = None
-    qy_data = None
-    q_data = None
-    err_data = None
-    dqx_data = None
-    dqy_data = None
-    mask = None
-
-    # Units
-    _xaxis = ''
-    _xunit = ''
-    _yaxis = ''
-    _yunit = ''
-    _zaxis = ''
-    _zunit = ''
-
-    def __init__(self, data=None, err_data=None, qx_data=None,
-                 qy_data=None, q_data=None, mask=None,
-                 dqx_data=None, dqy_data=None):
-        self.data = numpy.asarray(data)
-        self.qx_data = numpy.asarray(qx_data)
-        self.qy_data = numpy.asarray(qy_data)
-        self.q_data = numpy.asarray(q_data)
-        self.mask = numpy.asarray(mask)
-        self.err_data = numpy.asarray(err_data)
-        if dqx_data is not None:
-            self.dqx_data = numpy.asarray(dqx_data)
-        if dqy_data is not None:
-            self.dqy_data = numpy.asarray(dqy_data)
-
-    def xaxis(self, label, unit):
-        """
-        set the x axis label and unit
-        """
-        self._xaxis = label
-        self._xunit = unit
-
-    def yaxis(self, label, unit):
-        """
-        set the y axis label and unit
-        """
-        self._yaxis = label
-        self._yunit = unit
-
-    def zaxis(self, label, unit):
-        """
-        set the z axis label and unit
-        """
-        self._zaxis = label
-        self._zunit = unit
-
-
-class Vector(object):
-    """
-    Vector class to hold multi-dimensional objects
-    """
-    ## x component
-    x = None
-    ## y component
-    y = None
-    ## z component
-    z = None
-
-    def __init__(self, x=None, y=None, z=None):
-        """
-        Initialization. Components that are not
-        set a set to None by default.
-
-        :param x: x component
-        :param y: y component
-        :param z: z component
-        """
-        self.x = x
-        self.y = y
-        self.z = z
-
-    def __str__(self):
-        msg = "x = %s\ty = %s\tz = %s" % (str(self.x), str(self.y), str(self.z))
-        return msg
-
-
-class Detector(object):
-    """
-    Class to hold detector information
-    """
-    ## Name of the instrument [string]
-    name = None
-    ## Sample to detector distance [float] [mm]
-    distance = None
-    distance_unit = 'mm'
-    ## Offset of this detector position in X, Y,
-    #(and Z if necessary) [Vector] [mm]
-    offset = None
-    offset_unit = 'm'
-    ## Orientation (rotation) of this detector in roll,
-    # pitch, and yaw [Vector] [degrees]
-    orientation = None
-    orientation_unit = 'degree'
-    ## Center of the beam on the detector in X and Y
-    #(and Z if necessary) [Vector] [mm]
-    beam_center = None
-    beam_center_unit = 'mm'
-    ## Pixel size in X, Y, (and Z if necessary) [Vector] [mm]
-    pixel_size = None
-    pixel_size_unit = 'mm'
-    ## Slit length of the instrument for this detector.[float] [mm]
-    slit_length = None
-    slit_length_unit = 'mm'
-
-    def __init__(self):
-        """
-        Initialize class attribute that are objects...
-        """
-        self.offset = Vector()
-        self.orientation = Vector()
-        self.beam_center = Vector()
-        self.pixel_size = Vector()
-
-    def __str__(self):
-        _str = "Detector:\n"
-        _str += "   Name:         %s\n" % self.name
-        _str += "   Distance:     %s [%s]\n" % \
-            (str(self.distance), str(self.distance_unit))
-        _str += "   Offset:       %s [%s]\n" % \
-            (str(self.offset), str(self.offset_unit))
-        _str += "   Orientation:  %s [%s]\n" % \
-            (str(self.orientation), str(self.orientation_unit))
-        _str += "   Beam center:  %s [%s]\n" % \
-            (str(self.beam_center), str(self.beam_center_unit))
-        _str += "   Pixel size:   %s [%s]\n" % \
-            (str(self.pixel_size), str(self.pixel_size_unit))
-        _str += "   Slit length:  %s [%s]\n" % \
-            (str(self.slit_length), str(self.slit_length_unit))
-        return _str
-
-
-class Aperture(object):
-    ## Name
-    name = None
-    ## Type
-    type = None
-    ## Size name
-    size_name = None
-    ## Aperture size [Vector]
-    size = None
-    size_unit = 'mm'
-    ## Aperture distance [float]
-    distance = None
-    distance_unit = 'mm'
-
-    def __init__(self):
-        self.size = Vector()
-
-
-class Collimation(object):
-    """
-    Class to hold collimation information
-    """
-    ## Name
-    name = None
-    ## Length [float] [mm]
-    length = None
-    length_unit = 'mm'
-    ## Aperture
-    aperture = None
-
-    def __init__(self):
-        self.aperture = []
-
-    def __str__(self):
-        _str = "Collimation:\n"
-        _str += "   Length:       %s [%s]\n" % \
-            (str(self.length), str(self.length_unit))
-        for item in self.aperture:
-            _str += "   Aperture size:%s [%s]\n" % \
-                (str(item.size), str(item.size_unit))
-            _str += "   Aperture_dist:%s [%s]\n" % \
-                (str(item.distance), str(item.distance_unit))
-        return _str
-
-
-class Source(object):
-    """
-    Class to hold source information
-    """
-    ## Name
-    name = None
-    ## Radiation type [string]
-    radiation = None
-    ## Beam size name
-    beam_size_name = None
-    ## Beam size [Vector] [mm]
-    beam_size = None
-    beam_size_unit = 'mm'
-    ## Beam shape [string]
-    beam_shape = None
-    ## Wavelength [float] [Angstrom]
-    wavelength = None
-    wavelength_unit = 'A'
-    ## Minimum wavelength [float] [Angstrom]
-    wavelength_min = None
-    wavelength_min_unit = 'nm'
-    ## Maximum wavelength [float] [Angstrom]
-    wavelength_max = None
-    wavelength_max_unit = 'nm'
-    ## Wavelength spread [float] [Angstrom]
-    wavelength_spread = None
-    wavelength_spread_unit = 'percent'
-
-    def __init__(self):
-        self.beam_size = Vector()
-
-    def __str__(self):
-        _str = "Source:\n"
-        _str += "   Radiation:    %s\n" % str(self.radiation)
-        _str += "   Shape:        %s\n" % str(self.beam_shape)
-        _str += "   Wavelength:   %s [%s]\n" % \
-            (str(self.wavelength), str(self.wavelength_unit))
-        _str += "   Waveln_min:   %s [%s]\n" % \
-            (str(self.wavelength_min), str(self.wavelength_min_unit))
-        _str += "   Waveln_max:   %s [%s]\n" % \
-            (str(self.wavelength_max), str(self.wavelength_max_unit))
-        _str += "   Waveln_spread:%s [%s]\n" % \
-            (str(self.wavelength_spread), str(self.wavelength_spread_unit))
-        _str += "   Beam_size:    %s [%s]\n" % \
-            (str(self.beam_size), str(self.beam_size_unit))
-        return _str
-
-
-"""
-Definitions of radiation types
-"""
-NEUTRON = 'neutron'
-XRAY = 'x-ray'
-MUON = 'muon'
-ELECTRON = 'electron'
-
-
-class Sample(object):
-    """
-    Class to hold the sample description
-    """
-    ## Short name for sample
-    name = ''
-    ## ID
-    ID = ''
-    ## Thickness [float] [mm]
-    thickness = None
-    thickness_unit = 'mm'
-    ## Transmission [float] [fraction]
-    transmission = None
-    ## Temperature [float] [No Default]
-    temperature = None
-    temperature_unit = None
-    ## Position [Vector] [mm]
-    position = None
-    position_unit = 'mm'
-    ## Orientation [Vector] [degrees]
-    orientation = None
-    orientation_unit = 'degree'
-    ## Details
-    details = None
-
-    def __init__(self):
-        self.position = Vector()
-        self.orientation = Vector()
-        self.details = []
-
-    def __str__(self):
-        _str = "Sample:\n"
-        _str += "   ID:           %s\n" % str(self.ID)
-        _str += "   Transmission: %s\n" % str(self.transmission)
-        _str += "   Thickness:    %s [%s]\n" % \
-            (str(self.thickness), str(self.thickness_unit))
-        _str += "   Temperature:  %s [%s]\n" % \
-            (str(self.temperature), str(self.temperature_unit))
-        _str += "   Position:     %s [%s]\n" % \
-            (str(self.position), str(self.position_unit))
-        _str += "   Orientation:  %s [%s]\n" % \
-            (str(self.orientation), str(self.orientation_unit))
-
-        _str += "   Details:\n"
-        for item in self.details:
-            _str += "      %s\n" % item
-
-        return _str
-
-
-class Process(object):
-    """
-    Class that holds information about the processes
-    performed on the data.
-    """
-    name = ''
-    date = ''
-    description = ''
-    term = None
-    notes = None
-
-    def __init__(self):
-        self.term = []
-        self.notes = []
-
-    def is_empty(self):
-        """
-            Return True if the object is empty
-        """
-        return len(self.name) == 0 and len(self.date) == 0 and len(self.description) == 0 \
-            and len(self.term) == 0 and len(self.notes) == 0
-            
-    def single_line_desc(self):
-        """
-            Return a single line string representing the process
-        """
-        return "%s %s %s" % (self.name, self.date, self.description)
-     
-    def __str__(self):
-        _str = "Process:\n"
-        _str += "   Name:         %s\n" % self.name
-        _str += "   Date:         %s\n" % self.date
-        _str += "   Description:  %s\n" % self.description
-        for item in self.term:
-            _str += "   Term:         %s\n" % item
-        for item in self.notes:
-            _str += "   Note:         %s\n" % item
-        return _str
-
-
-class TransmissionSpectrum(object):
-    """
-    Class that holds information about transmission spectrum
-    for white beams and spallation sources.
-    """
-    name = ''
-    timestamp = ''
-    ## Wavelength (float) [A]
-    wavelength = None
-    wavelength_unit = 'A'
-    ## Transmission (float) [unit less]
-    transmission = None
-    transmission_unit = ''
-    ## Transmission Deviation (float) [unit less]
-    transmission_deviation = None
-    transmission_deviation_unit = ''
-
-    def __init__(self):
-        self.wavelength = []
-        self.transmission = []
-        self.transmission_deviation = []
-
-    def __str__(self):
-        _str = "Transmission Spectrum:\n"
-        _str += "   Name:             \t{0}\n".format(self.name)
-        _str += "   Timestamp:        \t{0}\n".format(self.timestamp)
-        _str += "   Wavelength unit:  \t{0}\n".format(self.wavelength_unit)
-        _str += "   Transmission unit:\t{0}\n".format(self.transmission_unit)
-        _str += "   Trans. Dev. unit:  \t{0}\n".format(\
-                                            self.transmission_deviation_unit)
-        length_list = [len(self.wavelength), len(self.transmission), \
-                len(self.transmission_deviation)]
-        _str += "   Number of Pts:    \t{0}\n".format(max(length_list))
-        return _str
-
-
-class DataInfo(object):
-    """
-    Class to hold the data read from a file.
-    It includes four blocks of data for the
-    instrument description, the sample description,
-    the data itself and any other meta data.
-    """
-    ## Title
-    title = ''
-    ## Run number
-    run = None
-    ## Run name
-    run_name = None
-    ## File name
-    filename = ''
-    ## Notes
-    notes = None
-    ## Processes (Action on the data)
-    process = None
-    ## Instrument name
-    instrument = ''
-    ## Detector information
-    detector = None
-    ## Sample information
-    sample = None
-    ## Source information
-    source = None
-    ## Collimation information
-    collimation = None
-    ## Transmission Spectrum INfo
-    trans_spectrum = None
-    ## Additional meta-data
-    meta_data = None
-    ## Loading errors
-    errors = None
-
-    def __init__(self):
-        """
-        Initialization
-        """
-        ## Title
-        self.title = ''
-        ## Run number
-        self.run = []
-        self.run_name = {}
-        ## File name
-        self.filename = ''
-        ## Notes
-        self.notes = []
-        ## Processes (Action on the data)
-        self.process = []
-        ## Instrument name
-        self.instrument = ''
-        ## Detector information
-        self.detector = []
-        ## Sample information
-        self.sample = Sample()
-        ## Source information
-        self.source = Source()
-        ## Collimation information
-        self.collimation = []
-        ## Transmission Spectrum
-        self.trans_spectrum = []
-        ## Additional meta-data
-        self.meta_data = {}
-        ## Loading errors
-        self.errors = []
-
-    def append_empty_process(self):
-        """
-        """
-        self.process.append(Process())
-
-    def add_notes(self, message=""):
-        """
-        Add notes to datainfo
-        """
-        self.notes.append(message)
-
-    def __str__(self):
-        """
-        Nice printout
-        """
-        _str = "File:            %s\n" % self.filename
-        _str += "Title:           %s\n" % self.title
-        _str += "Run:             %s\n" % str(self.run)
-        _str += "Instrument:      %s\n" % str(self.instrument)
-        _str += "%s\n" % str(self.sample)
-        _str += "%s\n" % str(self.source)
-        for item in self.detector:
-            _str += "%s\n" % str(item)
-        for item in self.collimation:
-            _str += "%s\n" % str(item)
-        for item in self.process:
-            _str += "%s\n" % str(item)
-        for item in self.notes:
-            _str += "%s\n" % str(item)
-        for item in self.trans_spectrum:
-            _str += "%s\n" % str(item)
-        return _str
-
-    # Private method to perform operation. Not implemented for DataInfo,
-    # but should be implemented for each data class inherited from DataInfo
-    # that holds actual data (ex.: Data1D)
-    def _perform_operation(self, other, operation):
-        """
-        Private method to perform operation. Not implemented for DataInfo,
-        but should be implemented for each data class inherited from DataInfo
-        that holds actual data (ex.: Data1D)
-        """
-        return NotImplemented
-
-    def _perform_union(self, other):
-        """
-        Private method to perform union operation. Not implemented for DataInfo,
-        but should be implemented for each data class inherited from DataInfo
-        that holds actual data (ex.: Data1D)
-        """
-        return NotImplemented
-
-    def __add__(self, other):
-        """
-        Add two data sets
-
-        :param other: data set to add to the current one
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        def operation(a, b):
-            return a + b
-        return self._perform_operation(other, operation)
-
-    def __radd__(self, other):
-        """
-        Add two data sets
-
-        :param other: data set to add to the current one
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        def operation(a, b):
-            return b + a
-        return self._perform_operation(other, operation)
-
-    def __sub__(self, other):
-        """
-        Subtract two data sets
-
-        :param other: data set to subtract from the current one
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        def operation(a, b):
-            return a - b
-        return self._perform_operation(other, operation)
-
-    def __rsub__(self, other):
-        """
-        Subtract two data sets
-
-        :param other: data set to subtract from the current one
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        def operation(a, b):
-            return b - a
-        return self._perform_operation(other, operation)
-
-    def __mul__(self, other):
-        """
-        Multiply two data sets
-
-        :param other: data set to subtract from the current one
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        def operation(a, b):
-            return a * b
-        return self._perform_operation(other, operation)
-
-    def __rmul__(self, other):
-        """
-        Multiply two data sets
-
-        :param other: data set to subtract from the current one
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        def operation(a, b):
-            return b * a
-        return self._perform_operation(other, operation)
-
-    def __div__(self, other):
-        """
-        Divided a data set by another
-
-        :param other: data set that the current one is divided by
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        def operation(a, b):
-            return a/b
-        return self._perform_operation(other, operation)
-
-    def __rdiv__(self, other):
-        """
-        Divided a data set by another
-
-        :param other: data set that the current one is divided by
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        def operation(a, b):
-            return b/a
-        return self._perform_operation(other, operation)
-
-    def __or__(self, other):
-        """
-        Union a data set with another
-
-        :param other: data set to be unified
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        return self._perform_union(other)
-
-    def __ror__(self, other):
-        """
-        Union a data set with another
-
-        :param other: data set to be unified
-        :return: new data set
-        :raise ValueError: raised when two data sets are incompatible
-        """
-        return self._perform_union(other)
-
-class SESANSData1D(plottable_sesans1D, DataInfo):
-    """
-    SESANS 1D data class
-    """
-    x_unit = 'nm'
-    y_unit = 'pol'
-
-    def __init__(self, x=None, y=None, lam=None, dx=None, dy=None, dlam=None):
-        DataInfo.__init__(self)
-        plottable_sesans1D.__init__(self, x, y, lam, dx, dy, dlam)
-
-    def __str__(self):
-        """
-        Nice printout
-        """
-        _str = "%s\n" % DataInfo.__str__(self)
-        _str += "Data:\n"
-        _str += "   Type:         %s\n" % self.__class__.__name__
-        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit)
-        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit)
-        _str += "   Length:       %g\n" % len(self.x)
-        return _str
-
-    def clone_without_data(self, length=0, clone=None):
-        """
-        Clone the current object, without copying the data (which
-        will be filled out by a subsequent operation).
-        The data arrays will be initialized to zero.
-
-        :param length: length of the data array to be initialized
-        :param clone: if provided, the data will be copied to clone
-        """
-        from copy import deepcopy
-        if clone is None or not issubclass(clone.__class__, Data1D):
-            x = numpy.zeros(length)
-            dx = numpy.zeros(length)
-            y = numpy.zeros(length)
-            dy = numpy.zeros(length)
-            clone = Data1D(x, y, dx=dx, dy=dy)
-
-        clone.title = self.title
-        clone.run = self.run
-        clone.filename = self.filename
-        clone.instrument = self.instrument
-        clone.notes = deepcopy(self.notes)
-        clone.process = deepcopy(self.process)
-        clone.detector = deepcopy(self.detector)
-        clone.sample = deepcopy(self.sample)
-        clone.source = deepcopy(self.source)
-        clone.collimation = deepcopy(self.collimation)
-        clone.trans_spectrum = deepcopy(self.trans_spectrum)
-        clone.meta_data = deepcopy(self.meta_data)
-        clone.errors = deepcopy(self.errors)
-
-        return clone
-
-class Data1D(plottable_1D, DataInfo):
-    """
-    1D data class
-    """
-    x_unit = '1/A'
-    y_unit = '1/cm'
-
-    def __init__(self, x, y, dx=None, dy=None):
-        DataInfo.__init__(self)
-        plottable_1D.__init__(self, x, y, dx, dy)
-
-    def __str__(self):
-        """
-        Nice printout
-        """
-        _str = "%s\n" % DataInfo.__str__(self)
-        _str += "Data:\n"
-        _str += "   Type:         %s\n" % self.__class__.__name__
-        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit)
-        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit)
-        _str += "   Length:       %g\n" % len(self.x)
-        return _str
-
-    def is_slit_smeared(self):
-        """
-        Check whether the data has slit smearing information
-        :return: True is slit smearing info is present, False otherwise
-        """
-        def _check(v):
-            if (v.__class__ == list or v.__class__ == numpy.ndarray) \
-                and len(v) > 0 and min(v) > 0:
-                return True
-            return False
-        return _check(self.dxl) or _check(self.dxw)
-
-    def clone_without_data(self, length=0, clone=None):
-        """
-        Clone the current object, without copying the data (which
-        will be filled out by a subsequent operation).
-        The data arrays will be initialized to zero.
-
-        :param length: length of the data array to be initialized
-        :param clone: if provided, the data will be copied to clone
-        """
-        from copy import deepcopy
-
-        if clone is None or not issubclass(clone.__class__, Data1D):
-            x = numpy.zeros(length)
-            dx = numpy.zeros(length)
-            y = numpy.zeros(length)
-            dy = numpy.zeros(length)
-            clone = Data1D(x, y, dx=dx, dy=dy)
-
-        clone.title = self.title
-        clone.run = self.run
-        clone.filename = self.filename
-        clone.instrument = self.instrument
-        clone.notes = deepcopy(self.notes)
-        clone.process = deepcopy(self.process)
-        clone.detector = deepcopy(self.detector)
-        clone.sample = deepcopy(self.sample)
-        clone.source = deepcopy(self.source)
-        clone.collimation = deepcopy(self.collimation)
-        clone.trans_spectrum = deepcopy(self.trans_spectrum)
-        clone.meta_data = deepcopy(self.meta_data)
-        clone.errors = deepcopy(self.errors)
-
-        return clone
-
-    def _validity_check(self, other):
-        """
-        Checks that the data lengths are compatible.
-        Checks that the x vectors are compatible.
-        Returns errors vectors equal to original
-        errors vectors if they were present or vectors
-        of zeros when none was found.
-
-        :param other: other data set for operation
-        :return: dy for self, dy for other [numpy arrays]
-        :raise ValueError: when lengths are not compatible
-        """
-        dy_other = None
-        if isinstance(other, Data1D):
-            # Check that data lengths are the same
-            if len(self.x) != len(other.x) or \
-                len(self.y) != len(other.y):
-                msg = "Unable to perform operation: data length are not equal"
-                raise ValueError, msg
-            # Here we could also extrapolate between data points
-            ZERO = 1.0e-12
-            for i in range(len(self.x)):
-                if math.fabs(self.x[i] - other.x[i]) > ZERO:
-                    msg = "Incompatible data sets: x-values do not match"
-                    raise ValueError, msg
-
-            # Check that the other data set has errors, otherwise
-            # create zero vector
-            dy_other = other.dy
-            if other.dy == None or (len(other.dy) != len(other.y)):
-                dy_other = numpy.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)):
-            dy = numpy.zeros(len(self.y))
-
-        return dy, dy_other
-
-    def _perform_operation(self, other, operation):
-        """
-        """
-        # First, check the data compatibility
-        dy, dy_other = self._validity_check(other)
-        result = self.clone_without_data(len(self.x))
-        if self.dxw == None:
-            result.dxw = None
-        else:
-            result.dxw = numpy.zeros(len(self.x))
-        if self.dxl == None:
-            result.dxl = None
-        else:
-            result.dxl = numpy.zeros(len(self.x))
-
-        for i in range(len(self.x)):
-            result.x[i] = self.x[i]
-            if self.dx is not None and len(self.x) == len(self.dx):
-                result.dx[i] = self.dx[i]
-            if self.dxw is not None and len(self.x) == len(self.dxw):
-                result.dxw[i] = self.dxw[i]
-            if self.dxl is not None and len(self.x) == len(self.dxl):
-                result.dxl[i] = self.dxl[i]
-
-            a = Uncertainty(self.y[i], dy[i]**2)
-            if isinstance(other, Data1D):
-                b = Uncertainty(other.y[i], dy_other[i]**2)
-                if other.dx is not None:
-                    result.dx[i] *= self.dx[i]
-                    result.dx[i] += (other.dx[i]**2)
-                    result.dx[i] /= 2
-                    result.dx[i] = math.sqrt(result.dx[i])
-                if result.dxl is not None and other.dxl is not None:
-                    result.dxl[i] *= self.dxl[i]
-                    result.dxl[i] += (other.dxl[i]**2)
-                    result.dxl[i] /= 2
-                    result.dxl[i] = math.sqrt(result.dxl[i])
-            else:
-                b = other
-
-            output = operation(a, b)
-            result.y[i] = output.x
-            result.dy[i] = math.sqrt(math.fabs(output.variance))
-        return result
-
-    def _validity_check_union(self, other):
-        """
-        Checks that the data lengths are compatible.
-        Checks that the x vectors are compatible.
-        Returns errors vectors equal to original
-        errors vectors if they were present or vectors
-        of zeros when none was found.
-
-        :param other: other data set for operation
-        :return: bool
-        :raise ValueError: when data types are not compatible
-        """
-        if not isinstance(other, Data1D):
-            msg = "Unable to perform operation: different types of data set"
-            raise ValueError, msg
-        return True
-
-    def _perform_union(self, other):
-        """
-        """
-        # First, check the data compatibility
-        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:
-            result.dy = None
-        else:
-            result.dy = numpy.zeros(len(self.x) + len(other.x))
-        if self.dx == None or other.dx is None:
-            result.dx = None
-        else:
-            result.dx = numpy.zeros(len(self.x) + len(other.x))
-        if self.dxw == None or other.dxw is None:
-            result.dxw = None
-        else:
-            result.dxw = numpy.zeros(len(self.x) + len(other.x))
-        if self.dxl == None or other.dxl is None:
-            result.dxl = None
-        else:
-            result.dxl = numpy.zeros(len(self.x) + len(other.x))
-
-        result.x = numpy.append(self.x, other.x)
-        #argsorting
-        ind = numpy.argsort(result.x)
-        result.x = result.x[ind]
-        result.y = numpy.append(self.y, other.y)
-        result.y = result.y[ind]
-        if result.dy != None:
-            result.dy = numpy.append(self.dy, other.dy)
-            result.dy = result.dy[ind]
-        if result.dx is not None:
-            result.dx = numpy.append(self.dx, other.dx)
-            result.dx = result.dx[ind]
-        if result.dxw is not None:
-            result.dxw = numpy.append(self.dxw, other.dxw)
-            result.dxw = result.dxw[ind]
-        if result.dxl is not None:
-            result.dxl = numpy.append(self.dxl, other.dxl)
-            result.dxl = result.dxl[ind]
-        return result
-
-
-class Data2D(plottable_2D, DataInfo):
-    """
-    2D data class
-    """
-    ## Units for Q-values
-    Q_unit = '1/A'
-    ## Units for I(Q) values
-    I_unit = '1/cm'
-    ## Vector of Q-values at the center of each bin in x
-    x_bins = None
-    ## Vector of Q-values at the center of each bin in y
-    y_bins = None
-
-    def __init__(self, data=None, err_data=None, qx_data=None,
-                 qy_data=None, q_data=None, mask=None,
-                 dqx_data=None, dqy_data=None):
-        self.y_bins = []
-        self.x_bins = []
-        DataInfo.__init__(self)
-        plottable_2D.__init__(self, data, err_data, qx_data,
-                              qy_data, q_data, mask, dqx_data, dqy_data)
-        if len(self.detector) > 0:
-            raise RuntimeError, "Data2D: Detector bank already filled at init"
-
-    def __str__(self):
-        _str = "%s\n" % DataInfo.__str__(self)
-        _str += "Data:\n"
-        _str += "   Type:         %s\n" % self.__class__.__name__
-        _str += "   X- & Y-axis:  %s\t[%s]\n" % (self._yaxis, self._yunit)
-        _str += "   Z-axis:       %s\t[%s]\n" % (self._zaxis, self._zunit)
-        _str += "   Length:       %g \n" % (len(self.data))
-        return _str
-
-    def clone_without_data(self, length=0, clone=None):
-        """
-        Clone the current object, without copying the data (which
-        will be filled out by a subsequent operation).
-        The data arrays will be initialized to zero.
-
-        :param length: length of the data array to be initialized
-        :param clone: if provided, the data will be copied to clone
-        """
-        from copy import deepcopy
-
-        if clone is None or not issubclass(clone.__class__, Data2D):
-            data = numpy.zeros(length)
-            err_data = numpy.zeros(length)
-            qx_data = numpy.zeros(length)
-            qy_data = numpy.zeros(length)
-            q_data = numpy.zeros(length)
-            mask = numpy.zeros(length)
-            dqx_data = None
-            dqy_data = None
-            clone = Data2D(data=data, err_data=err_data,
-                           qx_data=qx_data, qy_data=qy_data,
-                           q_data=q_data, mask=mask)
-
-        clone.title = self.title
-        clone.run = self.run
-        clone.filename = self.filename
-        clone.instrument = self.instrument
-        clone.notes = deepcopy(self.notes)
-        clone.process = deepcopy(self.process)
-        clone.detector = deepcopy(self.detector)
-        clone.sample = deepcopy(self.sample)
-        clone.source = deepcopy(self.source)
-        clone.collimation = deepcopy(self.collimation)
-        clone.meta_data = deepcopy(self.meta_data)
-        clone.errors = deepcopy(self.errors)
-
-        return clone
-
-    def _validity_check(self, other):
-        """
-        Checks that the data lengths are compatible.
-        Checks that the x vectors are compatible.
-        Returns errors vectors equal to original
-        errors vectors if they were present or vectors
-        of zeros when none was found.
-
-        :param other: other data set for operation
-        :return: dy for self, dy for other [numpy arrays]
-        :raise ValueError: when lengths are not compatible
-        """
-        err_other = None
-        if isinstance(other, Data2D):
-            # Check that data lengths are the same
-            if len(self.data) != len(other.data) or \
-                len(self.qx_data) != len(other.qx_data) or \
-                len(self.qy_data) != len(other.qy_data):
-                msg = "Unable to perform operation: data length are not equal"
-                raise ValueError, msg
-            for ind in range(len(self.data)):
-                if self.qx_data[ind] != other.qx_data[ind]:
-                    msg = "Incompatible data sets: qx-values do not match"
-                    raise ValueError, msg
-                if self.qy_data[ind] != other.qy_data[ind]:
-                    msg = "Incompatible data sets: qy-values do not match"
-                    raise ValueError, msg
-
-            # Check that the scales match
-            err_other = other.err_data
-            if other.err_data == None or \
-                (len(other.err_data) != len(other.data)):
-                err_other = numpy.zeros(len(other.data))
-
-        # Check that we have errors, otherwise create zero vector
-        err = self.err_data
-        if self.err_data == None or \
-            (len(self.err_data) != len(self.data)):
-            err = numpy.zeros(len(other.data))
-        return err, err_other
-
-    def _perform_operation(self, other, operation):
-        """
-        Perform 2D operations between data sets
-
-        :param other: other data set
-        :param operation: function defining the operation
-        """
-        # First, check the data compatibility
-        dy, dy_other = self._validity_check(other)
-        result = self.clone_without_data(numpy.size(self.data))
-        if self.dqx_data == None or self.dqy_data == None:
-            result.dqx_data = None
-            result.dqy_data = None
-        else:
-            result.dqx_data = numpy.zeros(len(self.data))
-            result.dqy_data = numpy.zeros(len(self.data))
-        for i in range(numpy.size(self.data)):
-            result.data[i] = self.data[i]
-            if self.err_data is not None and \
-                numpy.size(self.data) == numpy.size(self.err_data):
-                result.err_data[i] = self.err_data[i]
-            if self.dqx_data is not None:
-                result.dqx_data[i] = self.dqx_data[i]
-            if self.dqy_data is not None:
-                result.dqy_data[i] = self.dqy_data[i]
-            result.qx_data[i] = self.qx_data[i]
-            result.qy_data[i] = self.qy_data[i]
-            result.q_data[i] = self.q_data[i]
-            result.mask[i] = self.mask[i]
-
-            a = Uncertainty(self.data[i], dy[i]**2)
-            if isinstance(other, Data2D):
-                b = Uncertainty(other.data[i], dy_other[i]**2)
-                if other.dqx_data is not None and \
-                        result.dqx_data is not None:
-                    result.dqx_data[i] *= self.dqx_data[i]
-                    result.dqx_data[i] += (other.dqx_data[i]**2)
-                    result.dqx_data[i] /= 2
-                    result.dqx_data[i] = math.sqrt(result.dqx_data[i])
-                if other.dqy_data is not None and \
-                        result.dqy_data is not None:
-                    result.dqy_data[i] *= self.dqy_data[i]
-                    result.dqy_data[i] += (other.dqy_data[i]**2)
-                    result.dqy_data[i] /= 2
-                    result.dqy_data[i] = math.sqrt(result.dqy_data[i])
-            else:
-                b = other
-            output = operation(a, b)
-            result.data[i] = output.x
-            result.err_data[i] = math.sqrt(math.fabs(output.variance))
-        return result
-
-    def _validity_check_union(self, other):
-        """
-        Checks that the data lengths are compatible.
-        Checks that the x vectors are compatible.
-        Returns errors vectors equal to original
-        errors vectors if they were present or vectors
-        of zeros when none was found.
-
-        :param other: other data set for operation
-        :return: bool
-        :raise ValueError: when data types are not compatible
-        """
-        if not isinstance(other, Data2D):
-            msg = "Unable to perform operation: different types of data set"
-            raise ValueError, msg
-        return True
-
-    def _perform_union(self, other):
-        """
-        Perform 2D operations between data sets
-
-        :param other: other data set
-        :param operation: function defining the operation
-        """
-        # First, check the data compatibility
-        self._validity_check_union(other)
-        result = self.clone_without_data(numpy.size(self.data) + \
-                                         numpy.size(other.data))
-        result.xmin = self.xmin
-        result.xmax = self.xmax
-        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:
-            result.dqx_data = None
-            result.dqy_data = None
-        else:
-            result.dqx_data = numpy.zeros(len(self.data) + \
-                                         numpy.size(other.data))
-            result.dqy_data = numpy.zeros(len(self.data) + \
-                                         numpy.size(other.data))
-
-        result.data = numpy.append(self.data, other.data)
-        result.qx_data = numpy.append(self.qx_data, other.qx_data)
-        result.qy_data = numpy.append(self.qy_data, other.qy_data)
-        result.q_data = numpy.append(self.q_data, other.q_data)
-        result.mask = numpy.append(self.mask, other.mask)
-        if result.err_data is not None:
-            result.err_data = numpy.append(self.err_data, other.err_data) 
-        if self.dqx_data is not None:
-            result.dqx_data = numpy.append(self.dqx_data, other.dqx_data)
-        if self.dqy_data is not None:
-            result.dqy_data = numpy.append(self.dqy_data, other.dqy_data)
-
-        return result
Index: c/sas/dataloader/loader.py
===================================================================
--- src/sas/dataloader/loader.py	(revision f06d7fc5ba7d17d52edee3930be23f50d285aafc)
+++ 	(revision )
@@ -1,385 +1,0 @@
-"""
-    File handler to support different file extensions.
-    Uses reflectometry's registry utility.
-
-    The default readers are found in the 'readers' sub-module
-    and registered by default at initialization time.
-
-    To add a new default reader, one must register it in
-    the register_readers method found in readers/__init__.py.
-
-    A utility method (find_plugins) is available to inspect
-    a directory (for instance, a user plug-in directory) and
-    look for new readers/writers.
-"""
-#####################################################################
-#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.
-#See the license text in license.txt
-#copyright 2008, University of Tennessee
-######################################################################
-
-import os
-import sys
-import logging
-import time
-from zipfile import ZipFile
-from sas.data_util.registry import ExtensionRegistry
-# Default readers are defined in the readers sub-module
-import readers
-from readers import ascii_reader
-from readers import cansas_reader
-
-class Registry(ExtensionRegistry):
-    """
-    Registry class for file format extensions.
-    Readers and writers are supported.
-    """
-
-    def __init__(self):
-        super(Registry, self).__init__()
-
-        ## Writers
-        self.writers = {}
-
-        ## List of wildcards
-        self.wildcards = ['All (*.*)|*.*']
-
-        ## Creation time, for testing
-        self._created = time.time()
-
-        # Register default readers
-        readers.read_associations(self)
-
-    def load(self, path, format=None):
-        """
-        Call the loader for the file type of path.
-
-        :param path: file path
-        :param format: explicit extension, to force the use
-            of a particular reader
-
-        Defaults to the ascii (multi-column) reader
-        if no reader was registered for the file's
-        extension.
-        """
-        try:
-            return super(Registry, self).load(path, format=format)
-        except:
-            try:
-                # No reader was found. Default to the ascii reader.
-                ascii_loader = ascii_reader.Reader()
-                return ascii_loader.read(path)
-            except:
-                cansas_loader = cansas_reader.Reader()
-                return cansas_loader.read(path)
-
-    def find_plugins(self, dir):
-        """
-        Find readers in a given directory. This method
-        can be used to inspect user plug-in directories to
-        find new readers/writers.
-
-        :param dir: directory to search into
-        :return: number of readers found
-        """
-        readers_found = 0
-        temp_path = os.path.abspath(dir)
-        if not os.path.isdir(temp_path):
-            temp_path = os.path.join(os.getcwd(), dir)
-        if not os.path.isdir(temp_path):
-            temp_path = os.path.join(os.path.dirname(__file__), dir)
-        if not os.path.isdir(temp_path):
-            temp_path = os.path.join(os.path.dirname(sys.path[0]), dir)
-
-        dir = temp_path
-        # Check whether the directory exists
-        if not os.path.isdir(dir):
-            msg = "DataLoader couldn't locate DataLoader plugin folder."
-            msg += """ "%s" does not exist""" % dir
-            logging.warning(msg)
-            return readers_found
-
-        for item in os.listdir(dir):
-            full_path = os.path.join(dir, item)
-            if os.path.isfile(full_path):
-
-                # Process python files
-                if item.endswith('.py'):
-                    toks = os.path.splitext(os.path.basename(item))
-                    try:
-                        sys.path.insert(0, os.path.abspath(dir))
-                        module = __import__(toks[0], globals(), locals())
-                        if self._identify_plugin(module):
-                            readers_found += 1
-                    except:
-                        msg = "Loader: Error importing "
-                        msg += "%s\n  %s" % (item, sys.exc_value)
-                        logging.error(msg)
-
-                # Process zip files
-                elif item.endswith('.zip'):
-                    try:
-                        # Find the modules in the zip file
-                        zfile = ZipFile(item)
-                        nlist = zfile.namelist()
-
-                        sys.path.insert(0, item)
-                        for mfile in nlist:
-                            try:
-                                # Change OS path to python path
-                                fullname = mfile.replace('/', '.')
-                                fullname = os.path.splitext(fullname)[0]
-                                module = __import__(fullname, globals(),
-                                                    locals(), [""])
-                                if self._identify_plugin(module):
-                                    readers_found += 1
-                            except:
-                                msg = "Loader: Error importing"
-                                msg += " %s\n  %s" % (mfile, sys.exc_value)
-                                logging.error(msg)
-
-                    except:
-                        msg = "Loader: Error importing "
-                        msg += " %s\n  %s" % (item, sys.exc_value)
-                        logging.error(msg)
-
-        return readers_found
-
-    def associate_file_type(self, ext, module):
-        """
-        Look into a module to find whether it contains a
-        Reader class. If so, APPEND it to readers and (potentially)
-        to the list of writers for the given extension
-
-        :param ext: file extension [string]
-        :param module: module object
-        """
-        reader_found = False
-
-        if hasattr(module, "Reader"):
-            try:
-                # Find supported extensions
-                loader = module.Reader()
-                if ext not in self.loaders:
-                    self.loaders[ext] = []
-                # Append the new reader to the list
-                self.loaders[ext].append(loader.read)
-
-                reader_found = True
-
-                # Keep track of wildcards
-                type_name = module.__name__
-                if hasattr(loader, 'type_name'):
-                    type_name = loader.type_name
-
-                wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
-                                                ext.lower())
-                if wcard not in self.wildcards:
-                    self.wildcards.append(wcard)
-
-                # Check whether writing is supported
-                if hasattr(loader, 'write'):
-                    if ext not in self.writers:
-                        self.writers[ext] = []
-                    # Append the new writer to the list
-                    self.writers[ext].append(loader.write)
-
-            except:
-                msg = "Loader: Error accessing"
-                msg += " Reader in %s\n  %s" % (module.__name__, sys.exc_value)
-                logging.error(msg)
-        return reader_found
-
-    def associate_file_reader(self, ext, loader):
-        """
-        Append a reader object to readers
-
-        :param ext: file extension [string]
-        :param module: reader object
-        """
-        reader_found = False
-
-        try:
-            # Find supported extensions
-            if ext not in self.loaders:
-                self.loaders[ext] = []
-            # Append the new reader to the list
-            self.loaders[ext].append(loader.read)
-
-            reader_found = True
-
-            # Keep track of wildcards
-            if hasattr(loader, 'type_name'):
-                type_name = loader.type_name
-
-                wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
-                                                ext.lower())
-                if wcard not in self.wildcards:
-                    self.wildcards.append(wcard)
-
-        except:
-            msg = "Loader: Error accessing Reader "
-            msg += "in %s\n  %s" % (loader.__name__, sys.exc_value)
-            logging.error(msg)
-        return reader_found
-
-    def _identify_plugin(self, module):
-        """
-        Look into a module to find whether it contains a
-        Reader class. If so, add it to readers and (potentially)
-        to the list of writers.
-        :param module: module object
-
-        """
-        reader_found = False
-
-        if hasattr(module, "Reader"):
-            try:
-                # Find supported extensions
-                loader = module.Reader()
-                for ext in loader.ext:
-                    if ext not in self.loaders:
-                        self.loaders[ext] = []
-                    # When finding a reader at run time,
-                    # treat this reader as the new default
-                    self.loaders[ext].insert(0, loader.read)
-
-                    reader_found = True
-
-                    # Keep track of wildcards
-                    type_name = module.__name__
-                    if hasattr(loader, 'type_name'):
-                        type_name = loader.type_name
-                    wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
-                                                    ext.lower())
-                    if wcard not in self.wildcards:
-                        self.wildcards.append(wcard)
-
-                # Check whether writing is supported
-                if hasattr(loader, 'write'):
-                    for ext in loader.ext:
-                        if ext not in self.writers:
-                            self.writers[ext] = []
-                        self.writers[ext].insert(0, loader.write)
-
-            except:
-                msg = "Loader: Error accessing Reader"
-                msg += " in %s\n  %s" % (module.__name__, sys.exc_value)
-                logging.error(msg)
-        return reader_found
-
-    def lookup_writers(self, path):
-        """
-        :return: the loader associated with the file type of path.
-        :Raises ValueError: if file type is not known.
-        """
-        # Find matching extensions
-        extlist = [ext for ext in self.extensions() if path.endswith(ext)]
-        # Sort matching extensions by decreasing order of length
-        extlist.sort(lambda a, b: len(a) < len(b))
-        # Combine loaders for matching extensions into one big list
-        writers = []
-        for L in [self.writers[ext] for ext in extlist]:
-            writers.extend(L)
-        # Remove duplicates if they exist
-        if len(writers) != len(set(writers)):
-            result = []
-            for L in writers:
-                if L not in result:
-                    result.append(L)
-            writers = L
-        # Raise an error if there are no matching extensions
-        if len(writers) == 0:
-            raise ValueError, "Unknown file type for " + path
-        # All done
-        return writers
-
-    def save(self, path, data, format=None):
-        """
-        Call the writer for the file type of path.
-
-        Raises ValueError if no writer is available.
-        Raises KeyError if format is not available.
-        May raise a writer-defined exception if writer fails.
-        """
-        if format is None:
-            writers = self.lookup_writers(path)
-        else:
-            writers = self.writers[format]
-        for fn in writers:
-            try:
-                return fn(path, data)
-            except:
-                pass  # give other loaders a chance to succeed
-        # If we get here it is because all loaders failed
-        raise  # reraises last exception
-
-
-class Loader(object):
-    """
-    Utility class to use the Registry as a singleton.
-    """
-    ## Registry instance
-    __registry = Registry()
-
-    def associate_file_type(self, ext, module):
-        """
-        Look into a module to find whether it contains a
-        Reader class. If so, append it to readers and (potentially)
-        to the list of writers for the given extension
-
-        :param ext: file extension [string]
-        :param module: module object
-        """
-        return self.__registry.associate_file_type(ext, module)
-
-    def associate_file_reader(self, ext, loader):
-        """
-        Append a reader object to readers
-
-        :param ext: file extension [string]
-        :param module: reader object
-        """
-        return self.__registry.associate_file_reader(ext, loader)
-
-    def load(self, file, format=None):
-        """
-        Load a file
-
-        :param file: file name (path)
-        :param format: specified format to use (optional)
-        :return: DataInfo object
-        """
-        return self.__registry.load(file, format)
-
-    def save(self, file, data, format):
-        """
-        Save a DataInfo object to file
-        :param file: file name (path)
-        :param data: DataInfo object
-        :param format: format to write the data in
-        """
-        return self.__registry.save(file, data, format)
-
-    def _get_registry_creation_time(self):
-        """
-        Internal method used to test the uniqueness
-        of the registry object
-        """
-        return self.__registry._created
-
-    def find_plugins(self, directory):
-        """
-        Find plugins in a given directory
-
-        :param dir: directory to look into to find new readers/writers
-        """
-        return self.__registry.find_plugins(directory)
-
-    def get_wildcards(self):
-        """
-        Return the list of wildcards
-        """
-        return self.__registry.wildcards
Index: c/sas/dataloader/manipulations.py
===================================================================
--- src/sas/dataloader/manipulations.py	(revision 3c3a440abe3cdea4d16690492b0d8b0756b1c4a0)
+++ 	(revision )
@@ -1,1140 +1,0 @@
-"""
-Data manipulations for 2D data sets.
-Using the meta data information, various types of averaging
-are performed in Q-space
-"""
-#####################################################################
-#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.
-#See the license text in license.txt
-#copyright 2008, University of Tennessee
-######################################################################
-
-#TODO: copy the meta data from the 2D object to the resulting 1D object
-import math
-import numpy
-
-#from data_info import plottable_2D
-from data_info import Data1D
-
-
-def get_q(dx, dy, det_dist, wavelength):
-    """
-    :param dx: x-distance from beam center [mm]
-    :param dy: y-distance from beam center [mm]
-    :return: q-value at the given position
-    """
-    # Distance from beam center in the plane of detector
-    plane_dist = math.sqrt(dx * dx + dy * dy)
-    # Half of the scattering angle
-    theta = 0.5 * math.atan(plane_dist / det_dist)
-    return (4.0 * math.pi / wavelength) * math.sin(theta)
-
-
-def get_q_compo(dx, dy, det_dist, wavelength, compo=None):
-    """
-    This reduces tiny error at very large q.
-    Implementation of this func is not started yet.<--ToDo
-    """
-    if dy == 0:
-        if dx >= 0:
-            angle_xy = 0
-        else:
-            angle_xy = math.pi
-    else:
-        angle_xy = math.atan(dx / dy)
-
-    if compo == "x":
-        out = get_q(dx, dy, det_dist, wavelength) * math.cos(angle_xy)
-    elif compo == "y":
-        out = get_q(dx, dy, det_dist, wavelength) * math.sin(angle_xy)
-    else:
-        out = get_q(dx, dy, det_dist, wavelength)
-    return out
-
-
-def flip_phi(phi):
-    """
-    Correct phi to within the 0 <= to <= 2pi range
-
-    :return: phi in >=0 and <=2Pi
-    """
-    Pi = math.pi
-    if phi < 0:
-        phi_out = phi + (2 * Pi)
-    elif phi > (2 * Pi):
-        phi_out = phi - (2 * Pi)
-    else:
-        phi_out = phi
-    return phi_out
-
-
-def reader2D_converter(data2d=None):
-    """
-    convert old 2d format opened by IhorReader or danse_reader
-    to new Data2D format
-
-    :param data2d: 2d array of Data2D object
-    :return: 1d arrays of Data2D object
-
-    """
-    if data2d.data == None or data2d.x_bins == None or data2d.y_bins == None:
-        raise ValueError, "Can't convert this data: data=None..."
-    new_x = numpy.tile(data2d.x_bins, (len(data2d.y_bins), 1))
-    new_y = numpy.tile(data2d.y_bins, (len(data2d.x_bins), 1))
-    new_y = new_y.swapaxes(0, 1)
-
-    new_data = data2d.data.flatten()
-    qx_data = new_x.flatten()
-    qy_data = new_y.flatten()
-    q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
-    if data2d.err_data == None or numpy.any(data2d.err_data <= 0):
-        new_err_data = numpy.sqrt(numpy.abs(new_data))
-    else:
-        new_err_data = data2d.err_data.flatten()
-    mask = numpy.ones(len(new_data), dtype=bool)
-
-    #TODO: make sense of the following two lines...
-    #from sas.dataloader.data_info import Data2D
-    #output = Data2D()
-    output = data2d
-    output.data = new_data
-    output.err_data = new_err_data
-    output.qx_data = qx_data
-    output.qy_data = qy_data
-    output.q_data = q_data
-    output.mask = mask
-
-    return output
-
-
-class _Slab(object):
-    """
-    Compute average I(Q) for a region of interest
-    """
-    def __init__(self, x_min=0.0, x_max=0.0, y_min=0.0,
-                 y_max=0.0, bin_width=0.001):
-        # Minimum Qx value [A-1]
-        self.x_min = x_min
-        # Maximum Qx value [A-1]
-        self.x_max = x_max
-        # Minimum Qy value [A-1]
-        self.y_min = y_min
-        # Maximum Qy value [A-1]
-        self.y_max = y_max
-        # Bin width (step size) [A-1]
-        self.bin_width = bin_width
-        # If True, I(|Q|) will be return, otherwise,
-        # negative q-values are allowed
-        self.fold = False
-
-    def __call__(self, data2D):
-        return NotImplemented
-
-    def _avg(self, data2D, maj):
-        """
-        Compute average I(Q_maj) for a region of interest.
-        The major axis is defined as the axis of Q_maj.
-        The minor axis is the axis that we average over.
-
-        :param data2D: Data2D object
-        :param maj_min: min value on the major axis
-        :return: Data1D object
-        """
-        if len(data2D.detector) != 1:
-            msg = "_Slab._avg: invalid number of "
-            msg += " detectors: %g" % len(data2D.detector)
-            raise RuntimeError, msg
-
-        # Get data
-        data = data2D.data[numpy.isfinite(data2D.data)]
-        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
-        qx_data = data2D.qx_data[numpy.isfinite(data2D.data)]
-        qy_data = data2D.qy_data[numpy.isfinite(data2D.data)]
-
-        # Build array of Q intervals
-        if maj == 'x':
-            if self.fold:
-                x_min = 0
-            else:
-                x_min = self.x_min
-            nbins = int(math.ceil((self.x_max - x_min) / self.bin_width))
-        elif maj == 'y':
-            if self.fold:
-                y_min = 0
-            else:
-                y_min = self.y_min
-            nbins = int(math.ceil((self.y_max - y_min) / self.bin_width))
-        else:
-            raise RuntimeError, "_Slab._avg: unrecognized axis %s" % str(maj)
-
-        x = numpy.zeros(nbins)
-        y = numpy.zeros(nbins)
-        err_y = numpy.zeros(nbins)
-        y_counts = numpy.zeros(nbins)
-
-        # Average pixelsize in q space
-        for npts in range(len(data)):
-            # default frac
-            frac_x = 0
-            frac_y = 0
-            # get ROI
-            if self.x_min <= qx_data[npts] and self.x_max > qx_data[npts]:
-                frac_x = 1
-            if self.y_min <= qy_data[npts] and self.y_max > qy_data[npts]:
-                frac_y = 1
-            frac = frac_x * frac_y
-
-            if frac == 0:
-                continue
-            # binning: find axis of q
-            if maj == 'x':
-                q_value = qx_data[npts]
-                min_value = x_min
-            if maj == 'y':
-                q_value = qy_data[npts]
-                min_value = y_min
-            if self.fold and q_value < 0:
-                q_value = -q_value
-            # bin
-            i_q = int(math.ceil((q_value - min_value) / self.bin_width)) - 1
-
-            # skip outside of max bins
-            if i_q < 0 or i_q >= nbins:
-                continue
-
-            #TODO: find better definition of x[i_q] based on q_data
-            # min_value + (i_q + 1) * self.bin_width / 2.0
-            x[i_q] += frac * q_value
-            y[i_q] += frac * data[npts]
-
-            if err_data == None or err_data[npts] == 0.0:
-                if data[npts] < 0:
-                    data[npts] = -data[npts]
-                err_y[i_q] += frac * frac * data[npts]
-            else:
-                err_y[i_q] += frac * frac * err_data[npts] * err_data[npts]
-            y_counts[i_q] += frac
-
-        # Average the sums
-        for n in range(nbins):
-            err_y[n] = math.sqrt(err_y[n])
-
-        err_y = err_y / y_counts
-        y = y / y_counts
-        x = x / y_counts
-        idx = (numpy.isfinite(y) & numpy.isfinite(x))
-
-        if not idx.any():
-            msg = "Average Error: No points inside ROI to average..."
-            raise ValueError, msg
-        return Data1D(x=x[idx], y=y[idx], dy=err_y[idx])
-
-
-class SlabY(_Slab):
-    """
-    Compute average I(Qy) for a region of interest
-    """
-    def __call__(self, data2D):
-        """
-        Compute average I(Qy) for a region of interest
-
-        :param data2D: Data2D object
-        :return: Data1D object
-        """
-        return self._avg(data2D, 'y')
-
-
-class SlabX(_Slab):
-    """
-    Compute average I(Qx) for a region of interest
-    """
-    def __call__(self, data2D):
-        """
-        Compute average I(Qx) for a region of interest
-        :param data2D: Data2D object
-        :return: Data1D object
-        """
-        return self._avg(data2D, 'x')
-
-
-class Boxsum(object):
-    """
-    Perform the sum of counts in a 2D region of interest.
-    """
-    def __init__(self, x_min=0.0, x_max=0.0, y_min=0.0, y_max=0.0):
-        # Minimum Qx value [A-1]
-        self.x_min = x_min
-        # Maximum Qx value [A-1]
-        self.x_max = x_max
-        # Minimum Qy value [A-1]
-        self.y_min = y_min
-        # Maximum Qy value [A-1]
-        self.y_max = y_max
-
-    def __call__(self, data2D):
-        """
-        Perform the sum in the region of interest
-
-        :param data2D: Data2D object
-        :return: number of counts, error on number of counts,
-            number of points summed
-        """
-        y, err_y, y_counts = self._sum(data2D)
-
-        # Average the sums
-        counts = 0 if y_counts == 0 else y
-        error = 0 if y_counts == 0 else math.sqrt(err_y)
-
-        # Added y_counts to return, SMK & PDB, 04/03/2013
-        return counts, error, y_counts
-
-    def _sum(self, data2D):
-        """
-        Perform the sum in the region of interest
-
-        :param data2D: Data2D object
-        :return: number of counts,
-            error on number of counts, number of entries summed
-        """
-        if len(data2D.detector) != 1:
-            msg = "Circular averaging: invalid number "
-            msg += "of detectors: %g" % len(data2D.detector)
-            raise RuntimeError, msg
-        # Get data
-        data = data2D.data[numpy.isfinite(data2D.data)]
-        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
-        qx_data = data2D.qx_data[numpy.isfinite(data2D.data)]
-        qy_data = data2D.qy_data[numpy.isfinite(data2D.data)]
-
-        y = 0.0
-        err_y = 0.0
-        y_counts = 0.0
-
-        # Average pixelsize in q space
-        for npts in range(len(data)):
-            # default frac
-            frac_x = 0
-            frac_y = 0
-
-            # get min and max at each points
-            qx = qx_data[npts]
-            qy = qy_data[npts]
-
-            # get the ROI
-            if self.x_min <= qx and self.x_max > qx:
-                frac_x = 1
-            if self.y_min <= qy and self.y_max > qy:
-                frac_y = 1
-            #Find the fraction along each directions
-            frac = frac_x * frac_y
-            if frac == 0:
-                continue
-            y += frac * data[npts]
-            if err_data == None or err_data[npts] == 0.0:
-                if data[npts] < 0:
-                    data[npts] = -data[npts]
-                err_y += frac * frac * data[npts]
-            else:
-                err_y += frac * frac * err_data[npts] * err_data[npts]
-            y_counts += frac
-        return y, err_y, y_counts
-
-
-class Boxavg(Boxsum):
-    """
-    Perform the average of counts in a 2D region of interest.
-    """
-    def __init__(self, x_min=0.0, x_max=0.0, y_min=0.0, y_max=0.0):
-        super(Boxavg, self).__init__(x_min=x_min, x_max=x_max,
-                                     y_min=y_min, y_max=y_max)
-
-    def __call__(self, data2D):
-        """
-        Perform the sum in the region of interest
-
-        :param data2D: Data2D object
-        :return: average counts, error on average counts
-
-        """
-        y, err_y, y_counts = self._sum(data2D)
-
-        # Average the sums
-        counts = 0 if y_counts == 0 else y / y_counts
-        error = 0 if y_counts == 0 else math.sqrt(err_y) / y_counts
-
-        return counts, error
-
-
-def get_pixel_fraction_square(x, xmin, xmax):
-    """
-    Return the fraction of the length
-    from xmin to x.::
-
-           A            B
-       +-----------+---------+
-       xmin        x         xmax
-
-    :param x: x-value
-    :param xmin: minimum x for the length considered
-    :param xmax: minimum x for the length considered
-    :return: (x-xmin)/(xmax-xmin) when xmin < x < xmax
-
-    """
-    if x <= xmin:
-        return 0.0
-    if x > xmin and x < xmax:
-        return (x - xmin) / (xmax - xmin)
-    else:
-        return 1.0
-
-
-class CircularAverage(object):
-    """
-    Perform circular averaging on 2D data
-
-    The data returned is the distribution of counts
-    as a function of Q
-    """
-    def __init__(self, r_min=0.0, r_max=0.0, bin_width=0.0005):
-        # Minimum radius included in the average [A-1]
-        self.r_min = r_min
-        # Maximum radius included in the average [A-1]
-        self.r_max = r_max
-        # Bin width (step size) [A-1]
-        self.bin_width = bin_width
-
-    def __call__(self, data2D, ismask=False):
-        """
-        Perform circular averaging on the data
-
-        :param data2D: Data2D object
-        :return: Data1D object
-        """
-        # Get data W/ finite values
-        data = data2D.data[numpy.isfinite(data2D.data)]
-        q_data = data2D.q_data[numpy.isfinite(data2D.data)]
-        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
-        mask_data = data2D.mask[numpy.isfinite(data2D.data)]
-
-        dq_data = None
-
-        # Get the dq for resolution averaging
-        if data2D.dqx_data != None and data2D.dqy_data != None:
-            # The pinholes and det. pix contribution present
-            # in both direction of the 2D which must be subtracted when
-            # converting to 1D: dq_overlap should calculated ideally at
-            # q = 0. Note This method works on only pinhole geometry.
-            # Extrapolate dqx(r) and dqy(phi) at q = 0, and take an average.
-            z_max = max(data2D.q_data)
-            z_min = min(data2D.q_data)
-            x_max = data2D.dqx_data[data2D.q_data[z_max]]
-            x_min = data2D.dqx_data[data2D.q_data[z_min]]
-            y_max = data2D.dqy_data[data2D.q_data[z_max]]
-            y_min = data2D.dqy_data[data2D.q_data[z_min]]
-            # Find qdx at q = 0
-            dq_overlap_x = (x_min * z_max - x_max * z_min) / (z_max - z_min)
-            # when extrapolation goes wrong
-            if dq_overlap_x > min(data2D.dqx_data):
-                dq_overlap_x = min(data2D.dqx_data)
-            dq_overlap_x *= dq_overlap_x
-            # Find qdx at q = 0
-            dq_overlap_y = (y_min * z_max - y_max * z_min) / (z_max - z_min)
-            # when extrapolation goes wrong
-            if dq_overlap_y > min(data2D.dqy_data):
-                dq_overlap_y = min(data2D.dqy_data)
-            # get dq at q=0.
-            dq_overlap_y *= dq_overlap_y
-
-            dq_overlap = numpy.sqrt((dq_overlap_x + dq_overlap_y) / 2.0)
-            # Final protection of dq
-            if dq_overlap < 0:
-                dq_overlap = y_min
-            dqx_data = data2D.dqx_data[numpy.isfinite(data2D.data)]
-            dqy_data = data2D.dqy_data[numpy.isfinite(data2D.data)] - dq_overlap
-            # def; dqx_data = dq_r dqy_data = dq_phi
-            # Convert dq 2D to 1D here
-            dqx = dqx_data * dqx_data
-            dqy = dqy_data * dqy_data
-            dq_data = numpy.add(dqx, dqy)
-            dq_data = numpy.sqrt(dq_data)
-
-        #q_data_max = numpy.max(q_data)
-        if len(data2D.q_data) == None:
-            msg = "Circular averaging: invalid q_data: %g" % data2D.q_data
-            raise RuntimeError, msg
-
-        # Build array of Q intervals
-        nbins = int(math.ceil((self.r_max - self.r_min) / self.bin_width))
-
-        x = numpy.zeros(nbins)
-        y = numpy.zeros(nbins)
-        err_y = numpy.zeros(nbins)
-        err_x = numpy.zeros(nbins)
-        y_counts = numpy.zeros(nbins)
-
-        for npt in range(len(data)):
-
-            if ismask and not mask_data[npt]:
-                continue
-
-            frac = 0
-
-            # q-value at the pixel (j,i)
-            q_value = q_data[npt]
-            data_n = data[npt]
-
-            ## No need to calculate the frac when all data are within range
-            if self.r_min >= self.r_max:
-                raise ValueError, "Limit Error: min > max"
-
-            if self.r_min <= q_value and q_value <= self.r_max:
-                frac = 1
-            if frac == 0:
-                continue
-            i_q = int(math.floor((q_value - self.r_min) / self.bin_width))
-
-            # Take care of the edge case at phi = 2pi.
-            if i_q == nbins:
-                i_q = nbins - 1
-            y[i_q] += frac * data_n
-            # 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 data_n < 0:
-                    data_n = -data_n
-                err_y[i_q] += frac * frac * data_n
-            else:
-                err_y[i_q] += frac * frac * err_data[npt] * err_data[npt]
-            if dq_data != None:
-                # To be consistent with dq calculation in 1d reduction,
-                # we need just the averages (not quadratures) because
-                # it should not depend on the number of the q points
-                # in the qr bins.
-                err_x[i_q] += frac * dq_data[npt]
-            else:
-                err_x = None
-            y_counts[i_q] += frac
-
-        # Average the sums
-        for n in range(nbins):
-            if err_y[n] < 0:
-                err_y[n] = -err_y[n]
-            err_y[n] = math.sqrt(err_y[n])
-            #if err_x != None:
-            #    err_x[n] = math.sqrt(err_x[n])
-
-        err_y = err_y / y_counts
-        err_y[err_y == 0] = numpy.average(err_y)
-        y = y / y_counts
-        x = x / y_counts
-        idx = (numpy.isfinite(y)) & (numpy.isfinite(x))
-
-        if err_x != None:
-            d_x = err_x[idx] / y_counts[idx]
-        else:
-            d_x = None
-
-        if not idx.any():
-            msg = "Average Error: No points inside ROI to average..."
-            raise ValueError, msg
-
-        return Data1D(x=x[idx], y=y[idx], dy=err_y[idx], dx=d_x)
-
-
-class Ring(object):
-    """
-    Defines a ring on a 2D data set.
-    The ring is defined by r_min, r_max, and
-    the position of the center of the ring.
-
-    The data returned is the distribution of counts
-    around the ring as a function of phi.
-
-    Phi_min and phi_max should be defined between 0 and 2*pi
-    in anti-clockwise starting from the x- axis on the left-hand side
-    """
-    #Todo: remove center.
-    def __init__(self, r_min=0, r_max=0, center_x=0, center_y=0, nbins=36):
-        # Minimum radius
-        self.r_min = r_min
-        # Maximum radius
-        self.r_max = r_max
-        # Center of the ring in x
-        self.center_x = center_x
-        # Center of the ring in y
-        self.center_y = center_y
-        # Number of angular bins
-        self.nbins_phi = nbins
-
-
-    def __call__(self, data2D):
-        """
-        Apply the ring to the data set.
-        Returns the angular distribution for a given q range
-
-        :param data2D: Data2D object
-
-        :return: Data1D object
-        """
-        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
-            raise RuntimeError, "Ring averaging only take plottable_2D objects"
-
-        Pi = math.pi
-
-        # Get data
-        data = data2D.data[numpy.isfinite(data2D.data)]
-        q_data = data2D.q_data[numpy.isfinite(data2D.data)]
-        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
-        qx_data = data2D.qx_data[numpy.isfinite(data2D.data)]
-        qy_data = data2D.qy_data[numpy.isfinite(data2D.data)]
-
-        # Set space for 1d outputs
-        phi_bins = numpy.zeros(self.nbins_phi)
-        phi_counts = numpy.zeros(self.nbins_phi)
-        phi_values = numpy.zeros(self.nbins_phi)
-        phi_err = numpy.zeros(self.nbins_phi)
-
-        # Shift to apply to calculated phi values in order
-        # to center first bin at zero
-        phi_shift = Pi / self.nbins_phi
-
-        for npt in range(len(data)):
-            frac = 0
-            # q-value at the point (npt)
-            q_value = q_data[npt]
-            data_n = data[npt]
-
-            # phi-value at the point (npt)
-            phi_value = math.atan2(qy_data[npt], qx_data[npt]) + Pi
-
-            if self.r_min <= q_value and q_value <= self.r_max:
-                frac = 1
-            if frac == 0:
-                continue
-            # binning
-            i_phi = int(math.floor((self.nbins_phi) * \
-                                   (phi_value + phi_shift) / (2 * Pi)))
-
-            # Take care of the edge case at phi = 2pi.
-            if i_phi >= self.nbins_phi:
-                i_phi = 0
-            phi_bins[i_phi] += frac * data[npt]
-
-            if err_data == None or err_data[npt] == 0.0:
-                if data_n < 0:
-                    data_n = -data_n
-                phi_err[i_phi] += frac * frac * math.fabs(data_n)
-            else:
-                phi_err[i_phi] += frac * frac * err_data[npt] * err_data[npt]
-            phi_counts[i_phi] += frac
-
-        for i in range(self.nbins_phi):
-            phi_bins[i] = phi_bins[i] / phi_counts[i]
-            phi_err[i] = math.sqrt(phi_err[i]) / phi_counts[i]
-            phi_values[i] = 2.0 * math.pi / self.nbins_phi * (1.0 * i)
-
-        idx = (numpy.isfinite(phi_bins))
-
-        if not idx.any():
-            msg = "Average Error: No points inside ROI to average..."
-            raise ValueError, msg
-        #elif len(phi_bins[idx])!= self.nbins_phi:
-        #    print "resulted",self.nbins_phi- len(phi_bins[idx])
-        #,"empty bin(s) due to tight binning..."
-        return Data1D(x=phi_values[idx], y=phi_bins[idx], dy=phi_err[idx])
-
-
-def get_pixel_fraction(qmax, q_00, q_01, q_10, q_11):
-    """
-    Returns the fraction of the pixel defined by
-    the four corners (q_00, q_01, q_10, q_11) that
-    has q < qmax.::
-
-                q_01                q_11
-        y=1         +--------------+
-                    |              |
-                    |              |
-                    |              |
-        y=0         +--------------+
-                q_00                q_10
-
-                    x=0            x=1
-
-    """
-    # y side for x = minx
-    x_0 = get_intercept(qmax, q_00, q_01)
-    # y side for x = maxx
-    x_1 = get_intercept(qmax, q_10, q_11)
-
-    # x side for y = miny
-    y_0 = get_intercept(qmax, q_00, q_10)
-    # x side for y = maxy
-    y_1 = get_intercept(qmax, q_01, q_11)
-
-    # surface fraction for a 1x1 pixel
-    frac_max = 0
-
-    if x_0 and x_1:
-        frac_max = (x_0 + x_1) / 2.0
-    elif y_0 and y_1:
-        frac_max = (y_0 + y_1) / 2.0
-    elif x_0 and y_0:
-        if q_00 < q_10:
-            frac_max = x_0 * y_0 / 2.0
-        else:
-            frac_max = 1.0 - x_0 * y_0 / 2.0
-    elif x_0 and y_1:
-        if q_00 < q_10:
-            frac_max = x_0 * y_1 / 2.0
-        else:
-            frac_max = 1.0 - x_0 * y_1 / 2.0
-    elif x_1 and y_0:
-        if q_00 > q_10:
-            frac_max = x_1 * y_0 / 2.0
-        else:
-            frac_max = 1.0 - x_1 * y_0 / 2.0
-    elif x_1 and y_1:
-        if q_00 < q_10:
-            frac_max = 1.0 - (1.0 - x_1) * (1.0 - y_1) / 2.0
-        else:
-            frac_max = (1.0 - x_1) * (1.0 - y_1) / 2.0
-
-    # If we make it here, there is no intercept between
-    # this pixel and the constant-q ring. We only need
-    # to know if we have to include it or exclude it.
-    elif (q_00 + q_01 + q_10 + q_11) / 4.0 < qmax:
-        frac_max = 1.0
-
-    return frac_max
-
-
-def get_intercept(q, q_0, q_1):
-    """
-    Returns the fraction of the side at which the
-    q-value intercept the pixel, None otherwise.
-    The values returned is the fraction ON THE SIDE
-    OF THE LOWEST Q. ::
-
-            A           B
-        +-----------+--------+    <--- pixel size
-        0                    1
-        Q_0 -------- Q ----- Q_1   <--- equivalent Q range
-        if Q_1 > Q_0, A is returned
-        if Q_1 < Q_0, B is returned
-        if Q is outside the range of [Q_0, Q_1], None is returned
-
-    """
-    if q_1 > q_0:
-        if q > q_0 and q <= q_1:
-            return (q - q_0) / (q_1 - q_0)
-    else:
-        if q > q_1 and q <= q_0:
-            return (q - q_1) / (q_0 - q_1)
-    return None
-
-
-class _Sector(object):
-    """
-    Defines a sector region on a 2D data set.
-    The sector is defined by r_min, r_max, phi_min, phi_max,
-    and the position of the center of the ring
-    where phi_min and phi_max are defined by the right
-    and left lines wrt central line
-    and phi_max could be less than phi_min.
-
-    Phi is defined between 0 and 2*pi in anti-clockwise
-    starting from the x- axis on the left-hand side
-    """
-    def __init__(self, r_min, r_max, phi_min=0, phi_max=2 * math.pi, nbins=20):
-        self.r_min = r_min
-        self.r_max = r_max
-        self.phi_min = phi_min
-        self.phi_max = phi_max
-        self.nbins = nbins
-
-    def _agv(self, data2D, run='phi'):
-        """
-        Perform sector averaging.
-
-        :param data2D: Data2D object
-        :param run:  define the varying parameter ('phi' , 'q' , or 'q2')
-
-        :return: Data1D object
-        """
-        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
-            raise RuntimeError, "Ring averaging only take plottable_2D objects"
-        Pi = math.pi
-
-        # Get the all data & info
-        data = data2D.data[numpy.isfinite(data2D.data)]
-        q_data = data2D.q_data[numpy.isfinite(data2D.data)]
-        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
-        qx_data = data2D.qx_data[numpy.isfinite(data2D.data)]
-        qy_data = data2D.qy_data[numpy.isfinite(data2D.data)]
-        dq_data = None
-
-        # Get the dq for resolution averaging
-        if data2D.dqx_data != None and data2D.dqy_data != None:
-            # The pinholes and det. pix contribution present
-            # in both direction of the 2D which must be subtracted when
-            # converting to 1D: dq_overlap should calculated ideally at
-            # q = 0.
-            # Extrapolate dqy(perp) at q = 0
-            z_max = max(data2D.q_data)
-            z_min = min(data2D.q_data)
-            x_max = data2D.dqx_data[data2D.q_data[z_max]]
-            x_min = data2D.dqx_data[data2D.q_data[z_min]]
-            y_max = data2D.dqy_data[data2D.q_data[z_max]]
-            y_min = data2D.dqy_data[data2D.q_data[z_min]]
-            # Find qdx at q = 0
-            dq_overlap_x = (x_min * z_max - x_max * z_min) / (z_max - z_min)
-            # when extrapolation goes wrong
-            if dq_overlap_x > min(data2D.dqx_data):
-                dq_overlap_x = min(data2D.dqx_data)
-            dq_overlap_x *= dq_overlap_x
-            # Find qdx at q = 0
-            dq_overlap_y = (y_min * z_max - y_max * z_min) / (z_max - z_min)
-            # when extrapolation goes wrong
-            if dq_overlap_y > min(data2D.dqy_data):
-                dq_overlap_y = min(data2D.dqy_data)
-            # get dq at q=0.
-            dq_overlap_y *= dq_overlap_y
-
-            dq_overlap = numpy.sqrt((dq_overlap_x + dq_overlap_y) / 2.0)
-            if dq_overlap < 0:
-                dq_overlap = y_min
-            dqx_data = data2D.dqx_data[numpy.isfinite(data2D.data)]
-            dqy_data = data2D.dqy_data[numpy.isfinite(data2D.data)] - dq_overlap
-            # def; dqx_data = dq_r dqy_data = dq_phi
-            # Convert dq 2D to 1D here
-            dqx = dqx_data * dqx_data
-            dqy = dqy_data * dqy_data
-            dq_data = numpy.add(dqx, dqy)
-            dq_data = numpy.sqrt(dq_data)
-
-        #set space for 1d outputs
-        x = numpy.zeros(self.nbins)
-        y = numpy.zeros(self.nbins)
-        y_err = numpy.zeros(self.nbins)
-        x_err = numpy.zeros(self.nbins)
-        y_counts = numpy.zeros(self.nbins)
-
-        # Get the min and max into the region: 0 <= phi < 2Pi
-        phi_min = flip_phi(self.phi_min)
-        phi_max = flip_phi(self.phi_max)
-
-        for n in range(len(data)):
-            frac = 0
-
-            # q-value at the pixel (j,i)
-            q_value = q_data[n]
-            data_n = data[n]
-
-            # Is pixel within range?
-            is_in = False
-
-            # phi-value of the pixel (j,i)
-            phi_value = math.atan2(qy_data[n], qx_data[n]) + Pi
-
-            ## No need to calculate the frac when all data are within range
-            if self.r_min <= q_value and q_value <= self.r_max:
-                frac = 1
-            if frac == 0:
-                continue
-            #In case of two ROIs (symmetric major and minor regions)(for 'q2')
-            if run.lower() == 'q2':
-                ## For minor sector wing
-                # Calculate the minor wing phis
-                phi_min_minor = flip_phi(phi_min - Pi)
-                phi_max_minor = flip_phi(phi_max - Pi)
-                # Check if phis of the minor ring is within 0 to 2pi
-                if phi_min_minor > phi_max_minor:
-                    is_in = (phi_value > phi_min_minor or \
-                              phi_value < phi_max_minor)
-                else:
-                    is_in = (phi_value > phi_min_minor and \
-                              phi_value < phi_max_minor)
-
-            #For all cases(i.e.,for 'q', 'q2', and 'phi')
-            #Find pixels within ROI
-            if phi_min > phi_max:
-                is_in = is_in or (phi_value > phi_min or \
-                                   phi_value < phi_max)
-            else:
-                is_in = is_in or (phi_value >= phi_min  and \
-                                    phi_value < phi_max)
-
-            if not is_in:
-                frac = 0
-            if frac == 0:
-                continue
-            # Check which type of averaging we need
-            if run.lower() == 'phi':
-                temp_x = (self.nbins) * (phi_value - self.phi_min)
-                temp_y = (self.phi_max - self.phi_min)
-                i_bin = int(math.floor(temp_x / temp_y))
-            else:
-                temp_x = (self.nbins) * (q_value - self.r_min)
-                temp_y = (self.r_max - self.r_min)
-                i_bin = int(math.floor(temp_x / temp_y))
-
-            # Take care of the edge case at phi = 2pi.
-            if i_bin == self.nbins:
-                i_bin = self.nbins - 1
-
-            ## Get the total y
-            y[i_bin] += frac * data_n
-            x[i_bin] += frac * q_value
-            if err_data[n] == None or err_data[n] == 0.0:
-                if data_n < 0:
-                    data_n = -data_n
-                y_err[i_bin] += frac * frac * data_n
-            else:
-                y_err[i_bin] += frac * frac * err_data[n] * err_data[n]
-
-            if dq_data != None:
-                # To be consistent with dq calculation in 1d reduction,
-                # we need just the averages (not quadratures) because
-                # it should not depend on the number of the q points
-                # in the qr bins.
-                x_err[i_bin] += frac * dq_data[n]
-            else:
-                x_err = None
-            y_counts[i_bin] += frac
-
-        # Organize the results
-        for i in range(self.nbins):
-            y[i] = y[i] / y_counts[i]
-            y_err[i] = math.sqrt(y_err[i]) / y_counts[i]
-
-            # The type of averaging: phi,q2, or q
-            # Calculate x[i]should be at the center of the bin
-            if run.lower() == 'phi':
-                x[i] = (self.phi_max - self.phi_min) / self.nbins * \
-                    (1.0 * i + 0.5) + self.phi_min
-            else:
-                # We take the center of ring area, not radius.
-                # This is more accurate than taking the radial center of ring.
-                #delta_r = (self.r_max - self.r_min) / self.nbins
-                #r_inner = self.r_min + delta_r * i
-                #r_outer = r_inner + delta_r
-                #x[i] = math.sqrt((r_inner * r_inner + r_outer * r_outer) / 2)
-                x[i] = x[i] / y_counts[i]
-        y_err[y_err == 0] = numpy.average(y_err)
-        idx = (numpy.isfinite(y) & numpy.isfinite(y_err))
-        if x_err != None:
-            d_x = x_err[idx] / y_counts[idx]
-        else:
-            d_x = None
-        if not idx.any():
-            msg = "Average Error: No points inside sector of ROI to average..."
-            raise ValueError, msg
-        #elif len(y[idx])!= self.nbins:
-        #    print "resulted",self.nbins- len(y[idx]),
-        #"empty bin(s) due to tight binning..."
-        return Data1D(x=x[idx], y=y[idx], dy=y_err[idx], dx=d_x)
-
-
-class SectorPhi(_Sector):
-    """
-    Sector average as a function of phi.
-    I(phi) is return and the data is averaged over Q.
-
-    A sector is defined by r_min, r_max, phi_min, phi_max.
-    The number of bin in phi also has to be defined.
-    """
-    def __call__(self, data2D):
-        """
-        Perform sector average and return I(phi).
-
-        :param data2D: Data2D object
-        :return: Data1D object
-        """
-        return self._agv(data2D, 'phi')
-
-
-class SectorQ(_Sector):
-    """
-    Sector average as a function of Q for both symatric wings.
-    I(Q) is return and the data is averaged over phi.
-
-    A sector is defined by r_min, r_max, phi_min, phi_max.
-    r_min, r_max, phi_min, phi_max >0.
-    The number of bin in Q also has to be defined.
-    """
-    def __call__(self, data2D):
-        """
-        Perform sector average and return I(Q).
-
-        :param data2D: Data2D object
-
-        :return: Data1D object
-        """
-        return self._agv(data2D, 'q2')
-
-
-class Ringcut(object):
-    """
-    Defines a ring on a 2D data set.
-    The ring is defined by r_min, r_max, and
-    the position of the center of the ring.
-
-    The data returned is the region inside the ring
-
-    Phi_min and phi_max should be defined between 0 and 2*pi
-    in anti-clockwise starting from the x- axis on the left-hand side
-    """
-    def __init__(self, r_min=0, r_max=0, center_x=0, center_y=0):
-        # Minimum radius
-        self.r_min = r_min
-        # Maximum radius
-        self.r_max = r_max
-        # Center of the ring in x
-        self.center_x = center_x
-        # Center of the ring in y
-        self.center_y = center_y
-
-    def __call__(self, data2D):
-        """
-        Apply the ring to the data set.
-        Returns the angular distribution for a given q range
-
-        :param data2D: Data2D object
-
-        :return: index array in the range
-        """
-        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
-            raise RuntimeError, "Ring cut only take plottable_2D objects"
-
-        # Get data
-        qx_data = data2D.qx_data
-        qy_data = data2D.qy_data
-        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
-
-        # check whether or not the data point is inside ROI
-        out = (self.r_min <= q_data) & (self.r_max >= q_data)
-        return out
-
-
-class Boxcut(object):
-    """
-    Find a rectangular 2D region of interest.
-    """
-    def __init__(self, x_min=0.0, x_max=0.0, y_min=0.0, y_max=0.0):
-        # Minimum Qx value [A-1]
-        self.x_min = x_min
-        # Maximum Qx value [A-1]
-        self.x_max = x_max
-        # Minimum Qy value [A-1]
-        self.y_min = y_min
-        # Maximum Qy value [A-1]
-        self.y_max = y_max
-
-    def __call__(self, data2D):
-        """
-       Find a rectangular 2D region of interest.
-
-       :param data2D: Data2D object
-       :return: mask, 1d array (len = len(data))
-           with Trues where the data points are inside ROI, otherwise False
-        """
-        mask = self._find(data2D)
-
-        return mask
-
-    def _find(self, data2D):
-        """
-        Find a rectangular 2D region of interest.
-
-        :param data2D: Data2D object
-
-        :return: out, 1d array (length = len(data))
-           with Trues where the data points are inside ROI, otherwise Falses
-        """
-        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
-            raise RuntimeError, "Boxcut take only plottable_2D objects"
-        # Get qx_ and qy_data
-        qx_data = data2D.qx_data
-        qy_data = data2D.qy_data
-
-        # check whether or not the data point is inside ROI
-        outx = (self.x_min <= qx_data) & (self.x_max > qx_data)
-        outy = (self.y_min <= qy_data) & (self.y_max > qy_data)
-
-        return outx & outy
-
-
-class Sectorcut(object):
-    """
-    Defines a sector (major + minor) region on a 2D data set.
-    The sector is defined by phi_min, phi_max,
-    where phi_min and phi_max are defined by the right
-    and left lines wrt central line.
-
-    Phi_min and phi_max are given in units of radian
-    and (phi_max-phi_min) should not be larger than pi
-    """
-    def __init__(self, phi_min=0, phi_max=math.pi):
-        self.phi_min = phi_min
-        self.phi_max = phi_max
-
-    def __call__(self, data2D):
-        """
-        Find a rectangular 2D region of interest.
-
-        :param data2D: Data2D object
-
-        :return: mask, 1d array (len = len(data))
-
-        with Trues where the data points are inside ROI, otherwise False
-        """
-        mask = self._find(data2D)
-
-        return mask
-
-    def _find(self, data2D):
-        """
-        Find a rectangular 2D region of interest.
-
-        :param data2D: Data2D object
-
-        :return: out, 1d array (length = len(data))
-
-        with Trues where the data points are inside ROI, otherwise Falses
-        """
-        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
-            raise RuntimeError, "Sectorcut take only plottable_2D objects"
-        Pi = math.pi
-        # Get data
-        qx_data = data2D.qx_data
-        qy_data = data2D.qy_data
-
-        # get phi from data
-        phi_data = numpy.arctan2(qy_data, qx_data)
-
-        # Get the min and max into the region: -pi <= phi < Pi
-        phi_min_major = flip_phi(self.phi_min + Pi) - Pi
-        phi_max_major = flip_phi(self.phi_max + Pi) - Pi
-        # check for major sector
-        if phi_min_major > phi_max_major:
-            out_major = (phi_min_major <= phi_data) + (phi_max_major > phi_data)
-        else:
-            out_major = (phi_min_major <= phi_data) & (phi_max_major > phi_data)
-
-        # minor sector
-        # Get the min and max into the region: -pi <= phi < Pi
-        phi_min_minor = flip_phi(self.phi_min) - Pi
-        phi_max_minor = flip_phi(self.phi_max) - Pi
-
-        # check for minor sector
-        if phi_min_minor > phi_max_minor:
-            out_minor = (phi_min_minor <= phi_data) + \
-                            (phi_max_minor >= phi_data)
-        else:
-            out_minor = (phi_min_minor <= phi_data) & \
-                            (phi_max_minor >= phi_data)
-        out = out_major + out_minor
-
-        return out
Index: c/sas/dataloader/readers/IgorReader.py
===================================================================
--- src/sas/dataloader/readers/IgorReader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,304 +1,0 @@
-"""
-    IGOR 2D reduced file reader
-"""
-############################################################################
-#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, University of Tennessee
-#############################################################################
-import os
-import numpy
-import math
-#import logging
-from sas.dataloader.data_info import Data2D
-from sas.dataloader.data_info import Detector
-from sas.dataloader.manipulations import reader2D_converter
-
-# Look for unit converter
-has_converter = True
-try:
-    from sas.data_util.nxsunit import Converter
-except:
-    has_converter = False
-
-
-class Reader:
-    """ Simple data reader for Igor data files """
-    ## File type
-    type_name = "IGOR 2D"
-    ## Wildcards
-    type = ["IGOR 2D files (*.ASC)|*.ASC"]
-    ## Extension
-    ext=['.ASC', '.asc']
-
-    def read(self, filename=None):
-        """ Read file """
-        if not os.path.isfile(filename):
-            raise ValueError, \
-            "Specified file %s is not a regular file" % filename
-        
-        # Read file
-        f = open(filename, 'r')
-        buf = f.read()
-        
-        # Instantiate data object
-        output = Data2D()
-        output.filename = os.path.basename(filename)
-        detector = Detector()
-        if len(output.detector) > 0:
-            print str(output.detector[0])
-        output.detector.append(detector)
-                
-        # Get content
-        dataStarted = False
-        
-        lines = buf.split('\n')
-        itot = 0
-        x = []
-        y = []
-        
-        ncounts = 0
-        
-        xmin = None
-        xmax = None
-        ymin = None
-        ymax = None
-        
-        i_x = 0
-        i_y = -1
-        i_tot_row = 0
-        
-        isInfo = False
-        isCenter = False
-       
-        data_conv_q = None
-        data_conv_i = None
-        
-        if has_converter == True and output.Q_unit != '1/A':
-            data_conv_q = Converter('1/A')
-            # Test it
-            data_conv_q(1.0, output.Q_unit)
-            
-        if has_converter == True and output.I_unit != '1/cm':
-            data_conv_i = Converter('1/cm')
-            # Test it
-            data_conv_i(1.0, output.I_unit)
-         
-        for line in lines:
-            
-            # Find setup info line
-            if isInfo:
-                isInfo = False
-                line_toks = line.split()
-                # Wavelength in Angstrom
-                try:
-                    wavelength = float(line_toks[1])
-                except:
-                    msg = "IgorReader: can't read this file, missing wavelength"
-                    raise ValueError, msg
-                
-            #Find # of bins in a row assuming the detector is square.
-            if dataStarted == True:
-                try:
-                    value = float(line)
-                except:
-                    # Found a non-float entry, skip it
-                    continue
-                
-                # Get total bin number
-                
-            i_tot_row += 1
-        i_tot_row = math.ceil(math.sqrt(i_tot_row)) - 1
-        #print "i_tot", i_tot_row
-        size_x = i_tot_row  # 192#128
-        size_y = i_tot_row  # 192#128
-        output.data = numpy.zeros([size_x, size_y])
-        output.err_data = numpy.zeros([size_x, size_y])
-     
-        #Read Header and 2D data
-        for line in lines:
-            # Find setup info line
-            if isInfo:
-                isInfo = False
-                line_toks = line.split()
-                # Wavelength in Angstrom
-                try:
-                    wavelength = float(line_toks[1])
-                except:
-                    msg = "IgorReader: can't read this file, missing wavelength"
-                    raise ValueError, msg
-                # Distance in meters
-                try:
-                    distance = float(line_toks[3])
-                except:
-                    msg = "IgorReader: can't read this file, missing distance"
-                    raise ValueError, msg
-                
-                # Distance in meters
-                try:
-                    transmission = float(line_toks[4])
-                except:
-                    msg = "IgorReader: can't read this file, "
-                    msg += "missing transmission"
-                    raise ValueError, msg
-                                            
-            if line.count("LAMBDA") > 0:
-                isInfo = True
-                
-            # Find center info line
-            if isCenter:
-                isCenter = False
-                line_toks = line.split()
-                
-                # Center in bin number: Must substrate 1 because
-                #the index starts from 1
-                center_x = float(line_toks[0]) - 1
-                center_y = float(line_toks[1]) - 1
-
-            if line.count("BCENT") > 0:
-                isCenter = True
-                
-            # Find data start
-            if line.count("***")>0:
-                dataStarted = True
-                
-                # Check that we have all the info
-                if wavelength == None \
-                    or distance == None \
-                    or center_x == None \
-                    or center_y == None:
-                    msg = "IgorReader:Missing information in data file"
-                    raise ValueError, msg
-                
-            if dataStarted == True:
-                try:
-                    value = float(line)
-                except:
-                    # Found a non-float entry, skip it
-                    continue
-                
-                # Get bin number
-                if math.fmod(itot, i_tot_row) == 0:
-                    i_x = 0
-                    i_y += 1
-                else:
-                    i_x += 1
-                    
-                output.data[i_y][i_x] = value
-                ncounts += 1
-                
-                # Det 640 x 640 mm
-                # Q = 4pi/lambda sin(theta/2)
-                # Bin size is 0.5 cm 
-                #REmoved +1 from theta = (i_x-center_x+1)*0.5 / distance
-                # / 100.0 and 
-                #REmoved +1 from theta = (i_y-center_y+1)*0.5 /
-                # distance / 100.0
-                #ToDo: Need  complete check if the following
-                # covert process is consistent with fitting.py.
-                theta = (i_x - center_x) * 0.5 / distance / 100.0
-                qx = 4.0 * math.pi / wavelength * math.sin(theta/2.0)
-
-                if has_converter == True and output.Q_unit != '1/A':
-                    qx = data_conv_q(qx, units=output.Q_unit)
-
-                if xmin == None or qx < xmin:
-                    xmin = qx
-                if xmax == None or qx > xmax:
-                    xmax = qx
-                
-                theta = (i_y - center_y) * 0.5 / distance / 100.0
-                qy = 4.0 * math.pi / wavelength * math.sin(theta / 2.0)
-
-                if has_converter == True and output.Q_unit != '1/A':
-                    qy = data_conv_q(qy, units=output.Q_unit)
-                
-                if ymin == None or qy < ymin:
-                    ymin = qy
-                if ymax == None or qy > ymax:
-                    ymax = qy
-                
-                if not qx in x:
-                    x.append(qx)
-                if not qy in y:
-                    y.append(qy)
-                
-                itot += 1
-                  
-                  
-        theta = 0.25 / distance / 100.0
-        xstep = 4.0 * math.pi / wavelength * math.sin(theta / 2.0)
-        
-        theta = 0.25 / distance / 100.0
-        ystep = 4.0 * math.pi/ wavelength * math.sin(theta / 2.0)
-        
-        # Store all data ######################################
-        # Store wavelength
-        if has_converter == True and output.source.wavelength_unit != 'A':
-            conv = Converter('A')
-            wavelength = conv(wavelength, units=output.source.wavelength_unit)
-        output.source.wavelength = wavelength
-
-        # Store distance
-        if has_converter == True and detector.distance_unit != 'm':
-            conv = Converter('m')
-            distance = conv(distance, units=detector.distance_unit)
-        detector.distance = distance
-  
-        # Store transmission
-        output.sample.transmission = transmission
-        
-        # Store pixel size
-        pixel = 5.0
-        if has_converter == True and detector.pixel_size_unit != 'mm':
-            conv = Converter('mm')
-            pixel = conv(pixel, units=detector.pixel_size_unit)
-        detector.pixel_size.x = pixel
-        detector.pixel_size.y = pixel
-  
-        # Store beam center in distance units
-        detector.beam_center.x = center_x * pixel
-        detector.beam_center.y = center_y * pixel
-        
-        # Store limits of the image (2D array)
-        xmin = xmin - xstep / 2.0
-        xmax = xmax + xstep / 2.0
-        ymin = ymin - ystep / 2.0
-        ymax = ymax + ystep / 2.0
-        if has_converter == True and output.Q_unit != '1/A':
-            xmin = data_conv_q(xmin, units=output.Q_unit)
-            xmax = data_conv_q(xmax, units=output.Q_unit)
-            ymin = data_conv_q(ymin, units=output.Q_unit)
-            ymax = data_conv_q(ymax, units=output.Q_unit)
-        output.xmin = xmin
-        output.xmax = xmax
-        output.ymin = ymin
-        output.ymax = ymax
-        
-        # Store x and y axis bin centers
-        output.x_bins = x
-        output.y_bins = y
-        
-        # Units
-        if data_conv_q is not None:
-            output.xaxis("\\rm{Q_{x}}", output.Q_unit)
-            output.yaxis("\\rm{Q_{y}}", output.Q_unit)
-        else:
-            output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
-            output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
-            
-        if data_conv_i is not None:
-            output.zaxis("\\rm{Intensity}", output.I_unit)
-        else:
-            output.zaxis("\\rm{Intensity}", "cm^{-1}")
-    
-        # Store loading process information
-        output.meta_data['loader'] = self.type_name
-        output = reader2D_converter(output)
-
-        return output
Index: c/sas/dataloader/readers/__init__.py
===================================================================
--- src/sas/dataloader/readers/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,13 +1,0 @@
-# Backward compatibility with the previous implementation of the default readers
-from associations import register_readers
-
-# Method to associate extensions to default readers 
-from associations import read_associations
-
-# Method to return the location of the XML settings file
-def get_data_path():
-    """
-        Return the location of the settings file for the data readers.
-    """
-    import os
-    return os.path.dirname(__file__)
Index: c/sas/dataloader/readers/abs_reader.py
===================================================================
--- src/sas/dataloader/readers/abs_reader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,249 +1,0 @@
-"""
-"""
-#####################################################################
-#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.
-#See the license text in license.txt
-#copyright 2008, University of Tennessee
-######################################################################
-
-import numpy
-import os
-from sas.dataloader.data_info import Data1D
-from sas.dataloader.data_info import Detector
-
-has_converter = True
-try:
-    from sas.data_util.nxsunit import Converter
-except:
-    has_converter = False
-    
-    
-class Reader:
-    """
-    Class to load IGOR reduced .ABS files
-    """
-    ## File type
-    type_name = "IGOR 1D"
-    ## Wildcards
-    type = ["IGOR 1D files (*.abs)|*.abs"]
-    ## List of allowed extensions
-    ext = ['.abs', '.ABS']
-    
-    def read(self, path):
-        """ 
-        Load data file.
-        
-        :param path: file path
-        
-        :return: Data1D object, or None
-        
-        :raise RuntimeError: when the file can't be opened
-        :raise ValueError: when the length of the data vectors are inconsistent
-        """
-        if os.path.isfile(path):
-            basename = os.path.basename(path)
-            root, extension = os.path.splitext(basename)
-            if extension.lower() in self.ext:
-                try:
-                    input_f = open(path,'r')
-                except:
-                    raise  RuntimeError, "abs_reader: cannot open %s" % path
-                buff = input_f.read()
-                lines = buff.split('\n')
-                x  = numpy.zeros(0)
-                y  = numpy.zeros(0)
-                dy = numpy.zeros(0)
-                dx = numpy.zeros(0)
-                output = Data1D(x, y, dy=dy, dx=dx)
-                detector = Detector()
-                output.detector.append(detector)
-                output.filename = basename
-                
-                is_info = False
-                is_center = False
-                is_data_started = False
-                
-                data_conv_q = None
-                data_conv_i = None
-                
-                if has_converter == True and output.x_unit != '1/A':
-                    data_conv_q = Converter('1/A')
-                    # Test it
-                    data_conv_q(1.0, output.x_unit)
-                    
-                if has_converter == True and output.y_unit != '1/cm':
-                    data_conv_i = Converter('1/cm')
-                    # Test it
-                    data_conv_i(1.0, output.y_unit)
-                
-                for line in lines:
-                    
-                    # Information line 1
-                    if is_info == True:
-                        is_info = False
-                        line_toks = line.split()
-                        
-                        # Wavelength in Angstrom
-                        try:
-                            value = float(line_toks[1])
-                            if has_converter == True and \
-                                output.source.wavelength_unit != 'A':
-                                conv = Converter('A')
-                                output.source.wavelength = conv(value,
-                                        units=output.source.wavelength_unit)
-                            else:
-                                output.source.wavelength = value
-                        except:
-                            #goes to ASC reader
-                            msg = "abs_reader: cannot open %s" % path
-                            raise  RuntimeError, msg
-                        
-                        # Distance in meters
-                        try:
-                            value = float(line_toks[3])
-                            if has_converter == True and \
-                                detector.distance_unit != 'm':
-                                conv = Converter('m')
-                                detector.distance = conv(value,
-                                                units=detector.distance_unit)
-                            else:
-                                detector.distance = value
-                        except:
-                            #goes to ASC reader
-                            msg = "abs_reader: cannot open %s" % path
-                            raise  RuntimeError, msg
-                        # Transmission
-                        try:
-                            output.sample.transmission = float(line_toks[4])
-                        except:
-                            # Transmission is not a mandatory entry
-                            pass
-                    
-                        # Thickness in mm
-                        try:
-                            value = float(line_toks[5])
-                            if has_converter == True and \
-                                output.sample.thickness_unit != 'cm':
-                                conv = Converter('cm')
-                                output.sample.thickness = conv(value,
-                                            units=output.sample.thickness_unit)
-                            else:
-                                output.sample.thickness = value
-                        except:
-                            # Thickness is not a mandatory entry
-                            pass
-                    
-                    #MON CNT   LAMBDA   DET ANG   DET DIST   TRANS   THICK 
-                    #  AVE   STEP
-                    if line.count("LAMBDA") > 0:
-                        is_info = True
-                        
-                    # Find center info line
-                    if is_center == True:
-                        is_center = False
-                        line_toks = line.split()
-                        # Center in bin number
-                        center_x = float(line_toks[0])
-                        center_y = float(line_toks[1])
-                        
-                        # Bin size
-                        if has_converter == True and \
-                            detector.pixel_size_unit != 'mm':
-                            conv = Converter('mm')
-                            detector.pixel_size.x = conv(5.0,
-                                                units=detector.pixel_size_unit)
-                            detector.pixel_size.y = conv(5.0,
-                                                units=detector.pixel_size_unit)
-                        else:
-                            detector.pixel_size.x = 5.0
-                            detector.pixel_size.y = 5.0
-                        
-                        # Store beam center in distance units
-                        # Det 640 x 640 mm
-                        if has_converter == True and \
-                            detector.beam_center_unit != 'mm':
-                            conv = Converter('mm')
-                            detector.beam_center.x = conv(center_x * 5.0,
-                                             units=detector.beam_center_unit)
-                            detector.beam_center.y = conv(center_y * 5.0,
-                                            units=detector.beam_center_unit)
-                        else:
-                            detector.beam_center.x = center_x * 5.0
-                            detector.beam_center.y = center_y * 5.0
-                        
-                        # Detector type
-                        try:
-                            detector.name = line_toks[7]
-                        except:
-                            # Detector name is not a mandatory entry
-                            pass
-                    
-                    #BCENT(X,Y)   A1(mm)   A2(mm)   A1A2DIST(m)   DL/L
-                    #  BSTOP(mm)   DET_TYP
-                    if line.count("BCENT") > 0:
-                        is_center = True
-                        
-                    # Parse the data
-                    if is_data_started == True:
-                        toks = line.split()
-
-                        try:
-                            _x  = float(toks[0])
-                            _y  = float(toks[1])
-                            _dy = float(toks[2])
-                            _dx = float(toks[3])
-                            
-                            if data_conv_q is not None:
-                                _x = data_conv_q(_x, units=output.x_unit)
-                                _dx = data_conv_i(_dx, units=output.x_unit)
-                                
-                            if data_conv_i is not None:
-                                _y = data_conv_i(_y, units=output.y_unit)
-                                _dy = data_conv_i(_dy, units=output.y_unit)
-                           
-                            x = numpy.append(x, _x)
-                            y = numpy.append(y, _y)
-                            dy = numpy.append(dy, _dy)
-                            dx = numpy.append(dx, _dx)
-                            
-                        except:
-                            # Could not read this data line. If we are here
-                            # it is because we are in the data section. Just
-                            # skip it.
-                            pass
-                            
-                    #The 6 columns are | Q (1/A) | I(Q) (1/cm) | std. dev.
-                    # I(Q) (1/cm) | sigmaQ | meanQ | ShadowFactor|
-                    if line.count("The 6 columns") > 0:
-                        is_data_started = True
-            
-                # Sanity check
-                if not len(y) == len(dy):
-                    msg = "abs_reader: y and dy have different length"
-                    raise ValueError, msg
-                # If the data length is zero, consider this as
-                # though we were not able to read the file.
-                if len(x) == 0:
-                    raise ValueError, "ascii_reader: could not load file"
-                
-                output.x = x[x != 0]
-                output.y = y[x != 0]
-                output.dy = dy[x != 0]
-                output.dx = dx[x != 0]
-                if data_conv_q is not None:
-                    output.xaxis("\\rm{Q}", output.x_unit)
-                else:
-                    output.xaxis("\\rm{Q}", 'A^{-1}')
-                if data_conv_i is not None:
-                    output.yaxis("\\rm{Intensity}", output.y_unit)
-                else:
-                    output.yaxis("\\rm{Intensity}", "cm^{-1}")
-                    
-                # Store loading process information
-                output.meta_data['loader'] = self.type_name
-                return output
-        else:
-            raise RuntimeError, "%s is not a file" % path
-        return None
Index: c/sas/dataloader/readers/ascii_reader.py
===================================================================
--- src/sas/dataloader/readers/ascii_reader.py	(revision ef3445e276566d82d290fc50c6d95ab9611b9973)
+++ 	(revision )
@@ -1,358 +1,0 @@
-"""
-    ASCII reader
-"""
-############################################################################
-#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, University of Tennessee
-#############################################################################
-
-
-import numpy
-import os
-from sas.dataloader.data_info import Data1D
-
-# Check whether we have a converter available
-has_converter = True
-try:
-    from sas.data_util.nxsunit import Converter
-except:
-    has_converter = False
-_ZERO = 1e-16
-
-
-class Reader:
-    """
-    Class to load ascii files (2, 3 or 4 columns).
-    """
-    ## File type
-    type_name = "ASCII"
-    
-    ## Wildcards
-    type = ["ASCII files (*.txt)|*.txt",
-            "ASCII files (*.dat)|*.dat",
-            "ASCII files (*.abs)|*.abs",
-            "CSV files (*.csv)|*.csv"]
-    ## List of allowed extensions
-    ext = ['.txt', '.TXT', '.dat', '.DAT', '.abs', '.ABS', 'csv', 'CSV']
-    
-    ## Flag to bypass extension check
-    allow_all = True
-    
-    def read(self, path):
-        """
-        Load data file
-        
-        :param path: file path
-        
-        :return: Data1D object, or None
-        
-        :raise RuntimeError: when the file can't be opened
-        :raise ValueError: when the length of the data vectors are inconsistent
-        """
-        if os.path.isfile(path):
-            basename = os.path.basename(path)
-            _, extension = os.path.splitext(basename)
-            if self.allow_all or extension.lower() in self.ext:
-                try:
-                    # Read in binary mode since GRASP frequently has no-ascii
-                    # characters that brakes the open operation
-                    input_f = open(path,'rb')
-                except:
-                    raise  RuntimeError, "ascii_reader: cannot open %s" % path
-                buff = input_f.read()
-                lines = buff.splitlines()
-                 
-                x  = numpy.zeros(0)
-                y  = numpy.zeros(0)
-                dy = numpy.zeros(0)
-                dx = numpy.zeros(0)
-                
-                #temp. space to sort data
-                tx  = numpy.zeros(0)
-                ty  = numpy.zeros(0)
-                tdy = numpy.zeros(0)
-                tdx = numpy.zeros(0)
-                
-                output = Data1D(x, y, dy=dy, dx=dx)
-                self.filename = output.filename = basename
-           
-                data_conv_q = None
-                data_conv_i = None
-                
-                if has_converter == True and output.x_unit != '1/A':
-                    data_conv_q = Converter('1/A')
-                    # Test it
-                    data_conv_q(1.0, output.x_unit)
-                    
-                if has_converter == True and output.y_unit != '1/cm':
-                    data_conv_i = Converter('1/cm')
-                    # Test it
-                    data_conv_i(1.0, output.y_unit)
-           
-                # The first good line of data will define whether
-                # we have 2-column or 3-column ascii
-                has_error_dx = None
-                has_error_dy = None
-                
-                #Initialize counters for data lines and header lines.
-                is_data = False  # Has more than 5 lines
-                # More than "5" lines of data is considered as actual
-                # data unless that is the only data
-                mum_data_lines = 5
-                # To count # of current data candidate lines
-                i = -1
-                # To count total # of previous data candidate lines
-                i1 = -1
-                # To count # of header lines
-                j = -1
-                # Helps to count # of header lines
-                j1 = -1
-                #minimum required number of columns of data; ( <= 4).
-                lentoks = 2
-                for line in lines:
-                    # Initial try for CSV (split on ,)
-                    toks = line.split(',')
-                    # Now try SCSV (split on ;)
-                    if len(toks) < 2:
-                        toks = line.split(';')
-                    # Now go for whitespace                    
-                    if len(toks) < 2:
-                        toks = line.split()
-                    try:
-                        #Make sure that all columns are numbers.
-                        for colnum in range(len(toks)):
-                            float(toks[colnum])
-                            
-                        _x = float(toks[0])
-                        _y = float(toks[1])
-                        
-                        #Reset the header line counters
-                        if j == j1:
-                            j = 0
-                            j1 = 0
-                            
-                        if i > 1:
-                            is_data = True
-                        
-                        if data_conv_q is not None:
-                            _x = data_conv_q(_x, units=output.x_unit)
-                            
-                        if data_conv_i is not None:
-                            _y = data_conv_i(_y, units=output.y_unit)
-                        
-                        # If we have an extra token, check
-                        # whether it can be interpreted as a
-                        # third column.
-                        _dy = None
-                        if len(toks) > 2:
-                            try:
-                                _dy = float(toks[2])
-                                
-                                if data_conv_i is not None:
-                                    _dy = data_conv_i(_dy, units=output.y_unit)
-                                
-                            except:
-                                # The third column is not a float, skip it.
-                                pass
-                            
-                        # If we haven't set the 3rd column
-                        # flag, set it now.
-                        if has_error_dy == None:
-                            has_error_dy = False if _dy == None else True
-                            
-                        #Check for dx
-                        _dx = None
-                        if len(toks) > 3:
-                            try:
-                                _dx = float(toks[3])
-                                
-                                if data_conv_i is not None:
-                                    _dx = data_conv_i(_dx, units=output.x_unit)
-                                
-                            except:
-                                # The 4th column is not a float, skip it.
-                                pass
-                            
-                        # If we haven't set the 3rd column
-                        # flag, set it now.
-                        if has_error_dx == None:
-                            has_error_dx = False if _dx == None else True
-                        
-                        #After talked with PB, we decided to take care of only
-                        # 4 columns of data for now.
-                        #number of columns in the current line
-                        #To remember the # of columns in the current
-                        #line of data
-                        new_lentoks = len(toks)
-                        
-                        #If the previous columns not equal to the current,
-                        #mark the previous as non-data and reset the dependents.
-                        if lentoks != new_lentoks:
-                            if is_data == True:
-                                break
-                            else:
-                                i = -1
-                                i1 = 0
-                                j = -1
-                                j1 = -1
-                            
-                        #Delete the previously stored lines of data candidates
-                        # if is not data.
-                        if i < 0 and -1 < i1 < mum_data_lines and \
-                            is_data == False:
-                            try:
-                                x = numpy.zeros(0)
-                                y = numpy.zeros(0)
-                            except:
-                                pass
-                            
-                        x = numpy.append(x, _x)
-                        y = numpy.append(y, _y)
-                        
-                        if has_error_dy == True:
-                            #Delete the previously stored lines of
-                            # data candidates if is not data.
-                            if i < 0 and -1 < i1 < mum_data_lines and \
-                                is_data == False:
-                                try:
-                                    dy = numpy.zeros(0)
-                                except:
-                                    pass
-                            dy = numpy.append(dy, _dy)
-                            
-                        if has_error_dx == True:
-                            #Delete the previously stored lines of
-                            # data candidates if is not data.
-                            if i < 0 and -1 < i1 < mum_data_lines and \
-                                is_data == False:
-                                try:
-                                    dx = numpy.zeros(0)
-                                except:
-                                    pass
-                            dx = numpy.append(dx, _dx)
-                            
-                        #Same for temp.
-                        #Delete the previously stored lines of data candidates
-                        # if is not data.
-                        if i < 0 and -1 < i1 < mum_data_lines and\
-                            is_data == False:
-                            try:
-                                tx = numpy.zeros(0)
-                                ty = numpy.zeros(0)
-                            except:
-                                pass
-
-                        tx = numpy.append(tx, _x)
-                        ty = numpy.append(ty, _y)
-                        
-                        if has_error_dy == True:
-                            #Delete the previously stored lines of
-                            # data candidates if is not data.
-                            if i < 0 and -1 < i1 < mum_data_lines and \
-                                is_data == False:
-                                try:
-                                    tdy = numpy.zeros(0)
-                                except:
-                                    pass
-                            tdy = numpy.append(tdy, _dy)
-                        if has_error_dx == True:
-                            #Delete the previously stored lines of
-                            # data candidates if is not data.
-                            if i < 0 and -1 < i1 < mum_data_lines and \
-                                is_data == False:
-                                try:
-                                    tdx = numpy.zeros(0)
-                                except:
-                                    pass
-                            tdx = numpy.append(tdx, _dx)
-
-                        #reset i1 and flag lentoks for the next
-                        if lentoks < new_lentoks:
-                            if is_data == False:
-                                i1 = -1
-                        #To remember the # of columns on the current line
-                        # for the next line of data
-                        lentoks = len(toks)
-                        
-                        #Reset # of header lines and counts #
-                        # of data candidate lines
-                        if j == 0 and j1 == 0:
-                            i1 = i + 1
-                        i += 1
-                    except:
-                        # It is data and meet non - number, then stop reading
-                        if is_data == True:
-                            break
-                        lentoks = 2
-                        #Counting # of header lines
-                        j += 1
-                        if j == j1 + 1:
-                            j1 = j
-                        else:
-                            j = -1
-                        #Reset # of lines of data candidates
-                        i = -1
-                        
-                        # Couldn't parse this line, skip it
-                        pass
-                    
-                input_f.close()
-                # Sanity check
-                if has_error_dy == True and not len(y) == len(dy):
-                    msg = "ascii_reader: y and dy have different length"
-                    raise RuntimeError, msg
-                if has_error_dx == True and not len(x) == len(dx):
-                    msg = "ascii_reader: y and dy have different length"
-                    raise RuntimeError, msg
-                # If the data length is zero, consider this as
-                # though we were not able to read the file.
-                if len(x) == 0:
-                    raise RuntimeError, "ascii_reader: could not load file"
-                
-                #Let's re-order the data to make cal.
-                # curve look better some cases
-                ind = numpy.lexsort((ty, tx))
-                for i in ind:
-                    x[i] = tx[ind[i]]
-                    y[i] = ty[ind[i]]
-                    if has_error_dy == True:
-                        dy[i] = tdy[ind[i]]
-                    if has_error_dx == True:
-                        dx[i] = tdx[ind[i]]
-                # Zeros in dx, dy
-                if has_error_dx:
-                    dx[dx == 0] = _ZERO
-                if has_error_dy:
-                    dy[dy == 0] = _ZERO
-                #Data
-                output.x = x[x != 0]
-                output.y = y[x != 0]
-                output.dy = dy[x != 0] if has_error_dy == True\
-                    else numpy.zeros(len(output.y))
-                output.dx = dx[x != 0] if has_error_dx == True\
-                    else numpy.zeros(len(output.x))
-                                
-                if data_conv_q is not None:
-                    output.xaxis("\\rm{Q}", output.x_unit)
-                else:
-                    output.xaxis("\\rm{Q}", 'A^{-1}')
-                if data_conv_i is not None:
-                    output.yaxis("\\rm{Intensity}", output.y_unit)
-                else:
-                    output.yaxis("\\rm{Intensity}", "cm^{-1}")
-                    
-                # Store loading process information
-                output.meta_data['loader'] = self.type_name
-                if len(output.x) < 1:
-                    raise RuntimeError, "%s is empty" % path
-                return output
-            
-        else:
-            raise RuntimeError, "%s is not a file" % path
-        return None
Index: c/sas/dataloader/readers/associations.py
===================================================================
--- src/sas/dataloader/readers/associations.py	(revision 5e326a64f3587643c6a4cb468b4e95e5c900be9b)
+++ 	(revision )
@@ -1,105 +1,0 @@
-"""
-Module to associate default readers to file extensions.
-The module reads an xml file to get the readers for each file extension.
-The readers are tried in order they appear when reading a file.
-"""
-############################################################################
-#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 2009, University of Tennessee
-#############################################################################
-import os
-import sys
-import logging
-import json
-
-FILE_NAME = 'defaults.json'
-
-def read_associations(loader, settings=FILE_NAME):
-    """
-    Read the specified settings file to associate
-    default readers to file extension.
-    
-    :param loader: Loader object
-    :param settings: path to the json settings file [string]
-    """
-    reader_dir = os.path.dirname(__file__)
-    path = os.path.join(reader_dir, settings)
-    
-    # If we can't find the file in the installation
-    # directory, look into the execution directory.
-    if not os.path.isfile(path):
-        path = os.path.join(os.getcwd(), settings)
-    if not os.path.isfile(path):
-        path = os.path.join(sys.path[0], settings)
-    if not os.path.isfile(path):
-        path = settings
-    if not os.path.isfile(path):
-        path = "./%s" % settings
-    if os.path.isfile(path):
-        with open(path) as fh:
-            json_tree = json.load(fh)
-        
-        # Read in the file extension associations
-        entry_list = json_tree['SasLoader']['FileType']
-
-        # For each FileType entry, get the associated reader and extension
-        for entry in entry_list:
-            reader = entry['-reader']
-            ext = entry['-extension']
-            
-            if reader is not None and ext is not None:
-                # Associate the extension with a particular reader
-                # TODO: Modify the Register code to be case-insensitive
-                # and remove the extra line below.
-                try:
-                    exec "import %s" % reader
-                    exec "loader.associate_file_type('%s', %s)" % (ext.lower(),
-                                                                    reader)
-                    exec "loader.associate_file_type('%s', %s)" % (ext.upper(),
-                                                                    reader)
-                except:
-                    msg = "read_associations: skipping association"
-                    msg += " for %s\n  %s" % (ext.lower(), sys.exc_value)
-                    logging.error(msg)
-    else:
-        print "Could not find reader association settings\n  %s [%s]" % (__file__, os.getcwd())
-         
-         
-def register_readers(registry_function):
-    """
-    Function called by the registry/loader object to register
-    all default readers using a call back function.
-    
-    :WARNING: this method is now obsolete
-
-    :param registry_function: function to be called to register each reader
-    """
-    logging.info("register_readers is now obsolete: use read_associations()")
-    import abs_reader
-    import ascii_reader
-    import cansas_reader
-    import danse_reader
-    import hfir1d_reader
-    import IgorReader
-    import red2d_reader
-    #import tiff_reader
-    import nexus_reader
-    import sesans_reader
-    registry_function(sesans_reader)
-    registry_function(abs_reader)
-    registry_function(ascii_reader)
-    registry_function(cansas_reader)
-    registry_function(danse_reader)
-    registry_function(hfir1d_reader)
-    registry_function(IgorReader)
-    registry_function(red2d_reader)
-    #registry_function(tiff_reader)
-    registry_function(nexus_reader)
-    
-    return True
Index: c/sas/dataloader/readers/cansas_constants.py
===================================================================
--- src/sas/dataloader/readers/cansas_constants.py	(revision 682c432d9d62b6579bcdee8cedf8793d5d84f77b)
+++ 	(revision )
@@ -1,672 +1,0 @@
-"""
-Information relating to the CanSAS data format. These constants are used in
-the cansas_reader.py file to read in any version of the cansas format.
-"""
-class CansasConstants(object):
-    """
-    The base class to define where all of the data is to be saved by
-    cansas_reader.py.
-    """
-    names = ''
-    format = ''
-
-    def __init__(self):
-        self.names = self.CANSAS_NS
-        self.format = self.CANSAS_FORMAT
-
-    def iterate_namespace(self, namespace):
-        """
-        Method to iterate through a cansas constants tree based on a list of
-        names
-
-        :param namespace: A list of names that match the tree structure of
-            cansas_constants
-        """
-        # The current level to look through in cansas_constants.
-        return_me = CurrentLevel()
-        return_me.current_level = self.CANSAS_FORMAT.get("SASentry")
-        # Defaults for variable and datatype
-        return_me.ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\""
-        return_me.ns_datatype = "content"
-        return_me.ns_optional = True
-        for name in namespace:
-            try:
-                if name != "SASentry":
-                    return_me.current_level = \
-                        return_me.current_level.get("children").get(name, "")
-                    if return_me.current_level == "":
-                        return_me.current_level = \
-                                return_me.current_level.get("<any>", "")
-                    cl_variable = return_me.current_level.get("variable", "")
-                    cl_datatype = return_me.current_level.get("storeas", "")
-                    cl_units_optional = \
-                             return_me.current_level.get("units_required", "")
-                    # Where are how to store the variable for the given
-                    # namespace CANSAS_CONSTANTS tree is hierarchical, so
-                    # is no value, inherit
-                    return_me.ns_variable = cl_variable if cl_variable != "" \
-                        else return_me.ns_variable
-                    return_me.ns_datatype = cl_datatype if cl_datatype != "" \
-                        else return_me.ns_datatype
-                    return_me.ns_optional = cl_units_optional if \
-                        cl_units_optional != return_me.ns_optional \
-                                        else return_me.ns_optional
-            except AttributeError:
-                return_me.ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\""
-                return_me.ns_datatype = "content"
-                return_me.ns_optional = True
-        return return_me
-
-    def get_namespace_map(self):
-        """
-        Helper method to get the names namespace list
-        """
-        return self.names
-
-    # CANSAS_NS holds the base namespace and default schema file information
-    CANSAS_NS = {"1.0" : {"ns" : "cansas1d/1.0",
-                          "schema" : "cansas1d_v1_0.xsd"
-                         },
-                 "1.1" : {"ns" : "urn:cansas1d:1.1",
-                          "schema" : "cansas1d_v1_1.xsd"
-                         }
-                }
-
-    # The constants below hold information on where to store the CanSAS data
-    # when loaded in using sasview
-    META_DATA = "{0}.meta_data[\"{2}\"] = \"{1}\""
-    ANY = {"variable" : "{0}.meta_data[\"{2}\"] = \'{1}\'",
-           "storeas" : "content",
-          }
-    TITLE = {"variable" : "{0}.title = \"{1}\""}
-    SASNOTE = {"variable" : "{0}.notes.append(\'{1}\')"}
-    SASPROCESS_TERM = {"variable" : None,
-                       "attributes" : {"unit" : {"variable" : None},
-                                       "name" : {"variable" : None}
-                                      }
-                      }
-    SASPROCESS_SASPROCESSNOTE = {"variable" : None,
-                                 "children" : {"<any>" : ANY}
-                                }
-    SASPROCESS = {"variable" : None,
-                  "children" : {"name" : {"variable" : "{0}.name = \'{1}\'"},
-                                "date" : {"variable" : "{0}.date = \'{1}\'"},
-                                "description" :
-                                {"variable" : "{0}.description = \'{1}\'"},
-                                "term" : SASPROCESS_TERM,
-                                "SASprocessnote" : SASPROCESS_SASPROCESSNOTE,
-                                "<any>" : ANY
-                               },
-                 }
-    RUN = {"variable" : "{0}.run.append(\"{1}\")",
-           "attributes" : {"name" :
-                           {"variable" : "{0}.run_name[\"{3}\"] = \"{1}\""}}
-          }
-    SASDATA_IDATA_Q = {"variable" : "{0}.x = numpy.append({0}.x, {1})",
-                       "unit" : "x_unit",
-                       "attributes" : {"unit" :
-                                       {"variable" : "{0}._xunit = \"{1}\"",
-                                        "storeas" : "content"
-                                       }
-                                      },
-                      }
-    SASDATA_IDATA_I = {"variable" : "{0}.y = numpy.append({0}.y, {1})",
-                       "unit" : "y_unit",
-                       "attributes" : {"unit" :
-                                       {"variable" : "{0}._yunit = \"{1}\"",
-                                        "storeas" : "content"
-                                       }
-                                      },
-                      }
-    SASDATA_IDATA_IDEV = {"variable" : "{0}.dy = numpy.append({0}.dy, {1})",
-                          "unit" : "y_unit",
-                          "attributes" : {"unit" :
-                                          {"variable" : META_DATA,
-                                           "storeas" : "content"
-                                          }
-                                         },
-                         }
-    SASDATA_IDATA_QDEV = {"variable" : "{0}.dx = numpy.append({0}.dx, {1})",
-                          "unit" : "x_unit",
-                          "attributes" : {"unit" :
-                                          {"variable" : META_DATA,
-                                           "storeas" : "content"
-                                          }
-                                         },
-                         }
-    SASDATA_IDATA_DQL = {"variable" : "{0}.dxl = numpy.append({0}.dxl, {1})",
-                         "unit" : "x_unit",
-                         "attributes" : {"unit" :
-                                         {"variable" : META_DATA,
-                                          "storeas" : "content"
-                                         }
-                                        },
-                        }
-    SASDATA_IDATA_DQW = {"variable" : "{0}.dxw = numpy.append({0}.dxw, {1})",
-                         "unit" : "x_unit",
-                         "attributes" : {"unit" :
-                                         {"variable" : META_DATA,
-                                          "storeas" : "content"
-                                         }
-                                        },
-                        }
-    SASDATA_IDATA_QMEAN = {"storeas" : "content",
-                           "unit" : "x_unit",
-                           "variable" : META_DATA,
-                           "attributes" : {"unit" : {"variable" : META_DATA}},
-                          }
-    SASDATA_IDATA_SHADOWFACTOR = {"variable" : META_DATA,
-                                  "storeas" : "content",
-                                 }
-    SASDATA_IDATA = {"storeas" : "float",
-                     "units_optional" : False,
-                     "variable" : None,
-                     "attributes" : {"name" : {"variable" : META_DATA,
-                                               "storeas" : "content",
-                                              },
-                                     "timestamp" : {"variable" : META_DATA,
-                                                    "storeas" : "timestamp",
-                                                   }
-                                    },
-                     "children" : {"Q" : SASDATA_IDATA_Q,
-                                   "I" : SASDATA_IDATA_I,
-                                   "Idev" : SASDATA_IDATA_IDEV,
-                                   "Qdev" : SASDATA_IDATA_QDEV,
-                                   "dQw" : SASDATA_IDATA_DQW,
-                                   "dQl" : SASDATA_IDATA_DQL,
-                                   "Qmean" : SASDATA_IDATA_QMEAN,
-                                   "Shadowfactor" : SASDATA_IDATA_SHADOWFACTOR,
-                                   "<any>" : ANY
-                                  }
-                    }
-    SASDATA = {"attributes" : {"name" : {"variable" : META_DATA,}},
-               "variable" : None,
-               "children" : {"Idata" : SASDATA_IDATA,
-                             "<any>" : ANY
-                            }
-              }
-    SASTRANSSPEC_TDATA_LAMDBA = {"variable" : "{0}.wavelength.append({1})",
-                                 "unit" : "wavelength_unit",
-                                 "attributes" :
-                                 {"unit" :
-                                  {"variable" : "{0}.wavelength_unit = \"{1}\"",
-                                   "storeas" : "content"
-                                  }
-                                 }
-                                }
-    SASTRANSSPEC_TDATA_T = {"variable" : "{0}.transmission.append({1})",
-                            "unit" : "transmission_unit",
-                            "attributes" :
-                            {"unit" :
-                             {"variable" : "{0}.transmission_unit = \"{1}\"",
-                              "storeas" : "content"
-                             }
-                            }
-                           }
-    SASTRANSSPEC_TDATA_TDEV = {"variable" :
-                               "{0}.transmission_deviation.append({1})",
-                               "unit" : "transmission_deviation_unit",
-                               "attributes" :
-                               {"unit" :
-                                {"variable" :
-                                 "{0}.transmission_deviation_unit = \"{1}\"",
-                                 "storeas" : "content"
-                                }
-                               }
-                              }
-    SASTRANSSPEC_TDATA = {"storeas" : "float",
-                          "variable" : None,
-                          "children" : {"Lambda" : SASTRANSSPEC_TDATA_LAMDBA,
-                                        "T" : SASTRANSSPEC_TDATA_T,
-                                        "Tdev" : SASTRANSSPEC_TDATA_TDEV,
-                                        "<any>" : ANY,
-                                       }
-                         }
-    SASTRANSSPEC = {"variable" : None,
-                    "children" : {"Tdata" : SASTRANSSPEC_TDATA,
-                                  "<any>" : ANY,
-                                 },
-                    "attributes" :
-                    {"name" :
-                     {"variable" : "{0}.name = \"{1}\""},
-                     "timestamp" :
-                     {"variable" : "{0}.timestamp = \"{1}\""},
-                    }
-                   }
-    SASSAMPLE_THICK = {"variable" : "{0}.sample.thickness = {1}",
-                       "unit" : "sample.thickness_unit",
-                       "storeas" : "float",
-                       "attributes" :
-                       {"unit" :
-                        {"variable" : "{0}.sample.thickness_unit = \"{1}\"",
-                         "storeas" : "content"
-                        }
-                       },
-                      }
-    SASSAMPLE_TRANS = {"variable" : "{0}.sample.transmission = {1}",
-                       "storeas" : "float",
-                      }
-    SASSAMPLE_TEMP = {"variable" : "{0}.sample.temperature = {1}",
-                      "unit" : "sample.temperature_unit",
-                      "storeas" : "float",
-                      "attributes" :
-                      {"unit" :
-                       {"variable" : "{0}.sample.temperature_unit = \"{1}\"",
-                        "storeas" : "content"
-                       }
-                      },
-                     }
-    SASSAMPLE_POS_ATTR = {"unit" : {"variable" :
-                                    "{0}.sample.position_unit = \"{1}\"",
-                                    "storeas" : "content"
-                                   }
-                         }
-    SASSAMPLE_POS_X = {"variable" : "{0}.sample.position.x = {1}",
-                       "unit" : "sample.position_unit",
-                       "storeas" : "float",
-                       "attributes" : SASSAMPLE_POS_ATTR
-                      }
-    SASSAMPLE_POS_Y = {"variable" : "{0}.sample.position.y = {1}",
-                       "unit" : "sample.position_unit",
-                       "storeas" : "float",
-                       "attributes" : SASSAMPLE_POS_ATTR
-                      }
-    SASSAMPLE_POS_Z = {"variable" : "{0}.sample.position.z = {1}",
-                       "unit" : "sample.position_unit",
-                       "storeas" : "float",
-                       "attributes" : SASSAMPLE_POS_ATTR
-                      }
-    SASSAMPLE_POS = {"children" : {"variable" : None,
-                                   "x" : SASSAMPLE_POS_X,
-                                   "y" : SASSAMPLE_POS_Y,
-                                   "z" : SASSAMPLE_POS_Z,
-                                  },
-                    }
-    SASSAMPLE_ORIENT_ATTR = {"unit" :
-                             {"variable" :
-                              "{0}.sample.orientation_unit = \"{1}\"",
-                              "storeas" : "content"
-                             }
-                            }
-    SASSAMPLE_ORIENT_ROLL = {"variable" : "{0}.sample.orientation.x = {1}",
-                             "unit" : "sample.orientation_unit",
-                             "storeas" : "float",
-                             "attributes" : SASSAMPLE_ORIENT_ATTR
-                            }
-    SASSAMPLE_ORIENT_PITCH = {"variable" : "{0}.sample.orientation.y = {1}",
-                              "unit" : "sample.orientation_unit",
-                              "storeas" : "float",
-                              "attributes" : SASSAMPLE_ORIENT_ATTR
-                             }
-    SASSAMPLE_ORIENT_YAW = {"variable" : "{0}.sample.orientation.z = {1}",
-                            "unit" : "sample.orientation_unit",
-                            "storeas" : "float",
-                            "attributes" : SASSAMPLE_ORIENT_ATTR
-                           }
-    SASSAMPLE_ORIENT = {"variable" : None,
-                        "children" : {"roll" : SASSAMPLE_ORIENT_ROLL,
-                                      "pitch" : SASSAMPLE_ORIENT_PITCH,
-                                      "yaw" : SASSAMPLE_ORIENT_YAW,
-                                     },
-                       }
-    SASSAMPLE = {"attributes" :
-                 {"name" : {"variable" : "{0}.sample.name = \"{1}\""},},
-                 "variable" : None,
-                 "children" : {"ID" : {"variable" : "{0}.sample.ID = \"{1}\""},
-                               "thickness" : SASSAMPLE_THICK,
-                               "transmission" : SASSAMPLE_TRANS,
-                               "temperature" : SASSAMPLE_TEMP,
-                               "position" : SASSAMPLE_POS,
-                               "orientation" : SASSAMPLE_ORIENT,
-                               "details" :
-                               {"variable" :
-                                "{0}.sample.details.append(\"{1}\")"},
-                               "<any>" : ANY
-                              },
-                }
-    SASINSTR_SRC_BEAMSIZE_ATTR = {"unit" :
-                                  "{0}.source.beam_size_unit = \"{1}\"",
-                                  "storeas" : "content"
-                                 }
-    SASINSTR_SRC_BEAMSIZE_X = {"variable" : "{0}.source.beam_size.x = {1}",
-                               "unit" : "source.beam_size_unit",
-                               "storeas" : "float",
-                               "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
-                              }
-    SASINSTR_SRC_BEAMSIZE_Y = {"variable" : "{0}.source.beam_size.y = {1}",
-                               "unit" : "source.beam_size_unit",
-                               "storeas" : "float",
-                               "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
-                              }
-    SASINSTR_SRC_BEAMSIZE_Z = {"variable" : "{0}.source.beam_size.z = {1}",
-                               "unit" : "source.beam_size_unit",
-                               "storeas" : "float",
-                               "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
-                              }
-    SASINSTR_SRC_BEAMSIZE = {"attributes" :
-                             {"name" : {"variable" :
-                                        "{0}.source.beam_size_name = \"{1}\""}},
-                             "variable" : None,
-                             "children" : {"x" : SASINSTR_SRC_BEAMSIZE_X,
-                                           "y" : SASINSTR_SRC_BEAMSIZE_Y,
-                                           "z" : SASINSTR_SRC_BEAMSIZE_Z,
-                                          }
-                            }
-    SASINSTR_SRC_WL = {"variable" : "{0}.source.wavelength = {1}",
-                       "unit" : "source.wavelength_unit",
-                       "storeas" : "float",
-                       "attributes" :
-                       {"unit" :
-                        {"variable" : "{0}.source.wavelength_unit = \"{1}\"",
-                         "storeas" : "content"
-                        },
-                       }
-                      }
-    SASINSTR_SRC_WL_MIN = {"variable" : "{0}.source.wavelength_min = {1}",
-                           "unit" : "source.wavelength_min_unit",
-                           "storeas" : "float",
-                           "attributes" :
-                           {"unit" :
-                            {"variable" :
-                             "{0}.source.wavelength_min_unit = \"{1}\"",
-                             "storeas" : "content"
-                            },
-                           }
-                          }
-    SASINSTR_SRC_WL_MAX = {"variable" : "{0}.source.wavelength_max = {1}",
-                           "unit" : "source.wavelength_max_unit",
-                           "storeas" : "float",
-                           "attributes" :
-                           {"unit" :
-                            {"variable" :
-                             "{0}.source.wavelength_max_unit = \"{1}\"",
-                             "storeas" : "content"
-                            },
-                           }
-                          }
-    SASINSTR_SRC_WL_SPR = {"variable" : "{0}.source.wavelength_spread = {1}",
-                           "unit" : "source.wavelength_spread_unit",
-                           "storeas" : "float",
-                           "attributes" :
-                           {"unit" :
-                            {"variable" :
-                             "{0}.source.wavelength_spread_unit = \"{1}\"",
-                             "storeas" : "content"
-                            },
-                           }
-                          }
-    SASINSTR_SRC = {"attributes" : {"name" : {"variable" :
-                                              "{0}.source.name = \"{1}\""}},
-                    "variable" : None,
-                    "children" : {"radiation" :
-                                  {"variable" :
-                                   "{0}.source.radiation = \"{1}\""},
-                                  "beam_size" : SASINSTR_SRC_BEAMSIZE,
-                                  "beam_shape" :
-                                  {"variable" :
-                                   "{0}.source.beam_shape = \"{1}\""},
-                                  "wavelength" : SASINSTR_SRC_WL,
-                                  "wavelength_min" : SASINSTR_SRC_WL_MIN,
-                                  "wavelength_max" : SASINSTR_SRC_WL_MAX,
-                                  "wavelength_spread" : SASINSTR_SRC_WL_SPR,
-                                 },
-                   }
-    SASINSTR_COLL_APER_ATTR = {"unit" : {"variable" : "{0}.size_unit = \"{1}\"",
-                                         "storeas" : "content"
-                                        },
-                              }
-    SASINSTR_COLL_APER_X = {"variable" : "{0}.size.x = {1}",
-                            "unit" : "size_unit",
-                            "storeas" : "float",
-                            "attributes" : SASINSTR_COLL_APER_ATTR
-                           }
-    SASINSTR_COLL_APER_Y = {"variable" : "{0}.size.y = {1}",
-                            "unit" : "size_unit",
-                            "storeas" : "float",
-                            "attributes" : SASINSTR_COLL_APER_ATTR
-                           }
-    SASINSTR_COLL_APER_Z = {"variable" : "{0}.size.z = {1}",
-                            "unit" : "size_unit",
-                            "storeas" : "float",
-                            "attributes" : SASINSTR_COLL_APER_ATTR
-                           }
-    SASINSTR_COLL_APER_SIZE = {"attributes" :
-                               {"unit" : {"variable" :
-                                          "{0}.size_unit = \"{1}\""}},
-                               "children" : {"storeas" : "float",
-                                             "x" : SASINSTR_COLL_APER_X,
-                                             "y" : SASINSTR_COLL_APER_Y,
-                                             "z" : SASINSTR_COLL_APER_Z,
-                                            }
-                              }
-    SASINSTR_COLL_APER_DIST = {"storeas" : "float",
-                               "attributes" :
-                               {"storeas" : "content",
-                                "unit" : {"variable" :
-                                          "{0}.distance_unit = \"{1}\""}
-                               },
-                               "variable" : "{0}.distance = {1}",
-                               "unit" : "distance_unit",
-                              }
-    SASINSTR_COLL_APER = {"variable" : None,
-                          "attributes" : {"name" : {"variable" :
-                                                    "{0}.name = \"{1}\""},
-                                          "type" : {"variable" :
-                                                    "{0}.type = \"{1}\""},
-                                         },
-                          "children" : {"size" : SASINSTR_COLL_APER_SIZE,
-                                        "distance" : SASINSTR_COLL_APER_DIST
-                                       }
-                         }
-    SASINSTR_COLL = {"attributes" :
-                     {"name" : {"variable" : "{0}.name = \"{1}\""}},
-                     "variable" : None,
-                     "children" :
-                     {"length" :
-                      {"variable" : "{0}.length = {1}",
-                       "unit" : "length_unit",
-                       "storeas" : "float",
-                       "attributes" :
-                       {"storeas" : "content",
-                        "unit" : {"variable" : "{0}.length_unit = \"{1}\""}
-                       },
-                      },
-                      "aperture" : SASINSTR_COLL_APER,
-                     },
-                    }
-    SASINSTR_DET_SDD = {"variable" : "{0}.distance = {1}",
-                        "unit" : "distance_unit",
-                        "attributes" :
-                        {"unit" :
-                         {"variable" : "{0}.distance_unit = \"{1}\"",
-                          "storeas" : "content"
-                         }
-                        },
-                       }
-    SASINSTR_DET_OFF_ATTR = {"unit" : {"variable" : "{0}.offset_unit = \"{1}\"",
-                                       "storeas" : "content"
-                                      },
-                            }
-    SASINSTR_DET_OFF_X = {"variable" : "{0}.offset.x = {1}",
-                          "unit" : "offset_unit",
-                          "attributes" : SASINSTR_DET_OFF_ATTR
-                         }
-    SASINSTR_DET_OFF_Y = {"variable" : "{0}.offset.y = {1}",
-                          "unit" : "offset_unit",
-                          "attributes" : SASINSTR_DET_OFF_ATTR
-                         }
-    SASINSTR_DET_OFF_Z = {"variable" : "{0}.offset.z = {1}",
-                          "unit" : "offset_unit",
-                          "attributes" : SASINSTR_DET_OFF_ATTR
-                         }
-    SASINSTR_DET_OFF = {"variable" : None,
-                        "children" : {"x" : SASINSTR_DET_OFF_X,
-                                      "y" : SASINSTR_DET_OFF_Y,
-                                      "z" : SASINSTR_DET_OFF_Z,
-                                     }
-                       }
-    SASINSTR_DET_OR_ATTR = {"unit" : "{0}.orientation_unit = \"{1}\"",
-                            "storeas" : "content"
-                           }
-    SASINSTR_DET_OR_ROLL = {"variable" : "{0}.orientation.x = {1}",
-                            "unit" : "orientation_unit",
-                            "attributes" : SASINSTR_DET_OR_ATTR
-                           }
-    SASINSTR_DET_OR_PITCH = {"variable" : "{0}.orientation.y = {1}",
-                             "unit" : "orientation_unit",
-                             "attributes" : SASINSTR_DET_OR_ATTR
-                            }
-    SASINSTR_DET_OR_YAW = {"variable" : "{0}.orientation.z = {1}",
-                           "unit" : "orientation_unit",
-                           "attributes" : SASINSTR_DET_OR_ATTR
-                          }
-    SASINSTR_DET_OR = {"variable" : None,
-                       "children" : {"roll" : SASINSTR_DET_OR_ROLL,
-                                     "pitch" : SASINSTR_DET_OR_PITCH,
-                                     "yaw" : SASINSTR_DET_OR_YAW,
-                                    }
-                      }
-    SASINSTR_DET_BC_X = {"variable" : "{0}.beam_center.x = {1}",
-                         "unit" : "beam_center_unit",
-                         "attributes" :
-                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
-                          "storeas" : "content"
-                         }
-                        }
-    SASINSTR_DET_BC_Y = {"variable" : "{0}.beam_center.y = {1}",
-                         "unit" : "beam_center_unit",
-                         "attributes" :
-                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
-                          "storeas" : "content"
-                         }
-                        }
-    SASINSTR_DET_BC_Z = {"variable" : "{0}.beam_center.z = {1}",
-                         "unit" : "beam_center_unit",
-                         "attributes" :
-                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
-                          "storeas" : "content"
-                         }
-                        }
-    SASINSTR_DET_BC = {"variable" : None,
-                       "children" : {"x" : SASINSTR_DET_BC_X,
-                                     "y" : SASINSTR_DET_BC_Y,
-                                     "z" : SASINSTR_DET_BC_Z,
-                                    }
-                      }
-    SASINSTR_DET_PIXEL_X = {"variable" : "{0}.pixel_size.x = {1}",
-                            "unit" : "pixel_size_unit",
-                            "attributes" :
-                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
-                             "storeas" : "content"
-                            }
-                           }
-    SASINSTR_DET_PIXEL_Y = {"variable" : "{0}.pixel_size.y = {1}",
-                            "unit" : "pixel_size_unit",
-                            "attributes" :
-                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
-                             "storeas" : "content"
-                            }
-                           }
-    SASINSTR_DET_PIXEL_Z = {"variable" : "{0}.pixel_size.z = {1}",
-                            "unit" : "pixel_size_unit",
-                            "attributes" :
-                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
-                             "storeas" : "content"
-                            }
-                           }
-    SASINSTR_DET_PIXEL = {"variable" : None,
-                          "children" : {"x" : SASINSTR_DET_PIXEL_X,
-                                        "y" : SASINSTR_DET_PIXEL_Y,
-                                        "z" : SASINSTR_DET_PIXEL_Z,
-                                       }
-                         }
-    SASINSTR_DET_SLIT = {"variable" : "{0}.slit_length = {1}",
-                         "unit" : "slit_length_unit",
-                         "attributes" :
-                         {"unit" :
-                          {"variable" : "{0}.slit_length_unit = \"{1}\"",
-                           "storeas" : "content"
-                          }
-                         }
-                        }
-    SASINSTR_DET = {"storeas" : "float",
-                    "variable" : None,
-                    "attributes" : {"name" :
-                                    {"storeas" : "content",
-                                     "variable" : "{0}.name = \"{1}\"",
-                                    }
-                                   },
-                    "children" : {"name" : {"storeas" : "content",
-                                            "variable" : "{0}.name = \"{1}\"",
-                                           },
-                                  "SDD" : SASINSTR_DET_SDD,
-                                  "offset" : SASINSTR_DET_OFF,
-                                  "orientation" : SASINSTR_DET_OR,
-                                  "beam_center" : SASINSTR_DET_BC,
-                                  "pixel_size" : SASINSTR_DET_PIXEL,
-                                  "slit_length" : SASINSTR_DET_SLIT,
-                                 }
-                   }
-    SASINSTR = {"variable" : None,
-                "children" :
-                {"variable" : None,
-                 "name" : {"variable" : "{0}.instrument = \"{1}\""},
-                 "SASsource" : SASINSTR_SRC,
-                 "SAScollimation" : SASINSTR_COLL,
-                 "SASdetector" : SASINSTR_DET,
-                },
-               }
-    CANSAS_FORMAT = {"SASentry" :
-                     {"units_optional" : True,
-                      "variable" : None,
-                      "storeas" : "content",
-                      "attributes" : {"name" :
-                                      {"variable" :
-                                       "{0}.run_name[\"{3}\"] = \"{1}\""}},
-                      "children" : {"Title" : TITLE,
-                                    "Run" : RUN,
-                                    "SASdata" : SASDATA,
-                                    "SAStransmission_spectrum" : SASTRANSSPEC,
-                                    "SASsample" : SASSAMPLE,
-                                    "SASinstrument" : SASINSTR,
-                                    "SASprocess" : SASPROCESS,
-                                    "SASnote" : SASNOTE,
-                                    "<any>" : ANY,
-                                   }
-                     }
-                    }
-
-
-class CurrentLevel(object):
-    """
-    A helper class to hold information on where you are in the constants tree
-    """
-
-    current_level = ''
-    ns_variable = ''
-    ns_datatype = ''
-    ns_optional = True
-
-    def __init__(self):
-        self.current_level = {}
-        self.ns_variable = ''
-        self.ns_datatype = "content"
-        self.ns_optional = True
-
-    def get_current_level(self):
-        """
-        Helper method to get the current_level map
-        """
-        return self.current_level
-
-    def get_data_type(self):
-        """
-        Helper method to get the ns_datatype label
-        """
-        return self.ns_datatype
-
-    def get_variable(self):
-        """
-        Helper method to get the ns_variable label
-        """
-        return self.ns_variable
Index: c/sas/dataloader/readers/cansas_reader.py
===================================================================
--- src/sas/dataloader/readers/cansas_reader.py	(revision 5ce7f17e66dc0bd0cabff37dedf011bc6ec0a929)
+++ 	(revision )
@@ -1,1181 +1,0 @@
-"""
-    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
-import os
-import sys
-import datetime
-import inspect
-# For saving individual sections of data
-from sas.dataloader.data_info import Data1D
-from sas.dataloader.data_info import Collimation
-from sas.dataloader.data_info import TransmissionSpectrum
-from sas.dataloader.data_info import Detector
-from sas.dataloader.data_info import Process
-from sas.dataloader.data_info import Aperture
-import sas.dataloader.readers.xml_reader as xml_reader
-from sas.dataloader.readers.xml_reader import XMLreader
-from sas.dataloader.readers.cansas_constants import CansasConstants
-
-# The following 2 imports *ARE* used. Do not remove either.
-import xml.dom.minidom
-from xml.dom.minidom import parseString
-
-_ZERO = 1e-16
-PREPROCESS = "xmlpreprocess"
-ENCODING = "encoding"
-RUN_NAME_DEFAULT = "None"
-HAS_CONVERTER = True
-try:
-    from sas.data_util.nxsunit import Converter
-except ImportError:
-    HAS_CONVERTER = False
-
-CONSTANTS = CansasConstants()
-CANSAS_FORMAT = CONSTANTS.format
-CANSAS_NS = CONSTANTS.names
-ALLOW_ALL = True
-
-# DO NOT REMOVE Called by outside packages:
-#    sas.perspectives.invariant.invariant_state
-#    sas.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.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
-
-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}"
-    logging = None
-    errors = None
-    type_name = "canSAS"
-    ## 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 __init__(self):
-        ## List of errors
-        self.errors = set()
-        self.logging = []
-        self.encoding = None
-
-    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
-        return False
-
-    def load_file_and_schema(self, xml_file):
-        """
-        Loads the file and associates a schema, if a known schema exists
-
-        :param xml_file: The xml file path sent to Reader.read
-        """
-        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
-        cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0")
-        schema_path = "{0}/sas/dataloader/readers/schema/{1}".format\
-                (base, cansas_defaults.get("schema")).replace("\\", "/")
-
-        # Link a schema to the XML file.
-        self.set_schema(schema_path)
-        return cansas_defaults
-
-    def read(self, xml_file):
-        """
-        Validate and read in an xml_file file in the canSAS format.
-
-        :param xml_file: A canSAS file path in proper XML format
-        """
-        # output - Final list of Data1D objects
-        output = []
-        # ns - Namespace hierarchy for current xml_file object
-        ns_list = []
-
-        # Check that the file exists
-        if os.path.isfile(xml_file):
-            basename = os.path.basename(xml_file)
-            _, extension = os.path.splitext(basename)
-            # If the file type is not allowed, return nothing
-            if extension in self.ext or self.allow_all:
-                # Get the file location of
-                cansas_defaults = self.load_file_and_schema(xml_file)
-
-                # Try to load the file, but raise an error if unable to.
-                # Check the file matches the XML schema
-                try:
-                    if self.is_cansas(extension):
-                        # Get each SASentry from XML file and add it to a list.
-                        entry_list = self.xmlroot.xpath(
-                                '/ns:SASroot/ns:SASentry',
-                                namespaces={'ns': cansas_defaults.get("ns")})
-                        ns_list.append("SASentry")
-
-                        # If multiple files, modify the name for each is unique
-                        increment = 0
-                        # Parse each SASentry item
-                        for entry in entry_list:
-                            # Define a new Data1D object with zeroes for
-                            # x_vals and y_vals
-                            data1d = Data1D(numpy.empty(0), numpy.empty(0),
-                                            numpy.empty(0), numpy.empty(0))
-                            data1d.dxl = numpy.empty(0)
-                            data1d.dxw = numpy.empty(0)
-
-                            # If more than one SASentry, increment each in order
-                            name = basename
-                            if len(entry_list) - 1 > 0:
-                                name += "_{0}".format(increment)
-                                increment += 1
-
-                            # Set the Data1D name and then parse the entry.
-                            # The entry is appended to a list of entry values
-                            data1d.filename = name
-                            data1d.meta_data["loader"] = "CanSAS 1D"
-
-                            # Get all preprocessing events and encoding
-                            self.set_processing_instructions()
-                            data1d.meta_data[PREPROCESS] = \
-                                    self.processing_instructions
-
-                            # Parse the XML file
-                            return_value, extras = \
-                                self._parse_entry(entry, ns_list, data1d)
-                            del extras[:]
-
-                            return_value = self._final_cleanup(return_value)
-                            output.append(return_value)
-                    else:
-                        output.append("Invalid XML at: {0}".format(\
-                                                    self.find_invalid_xml()))
-                except:
-                    # If the file does not match the schema, raise this error
-                    raise RuntimeError, "%s cannot be read" % xml_file
-                return output
-        # Return a list of parsed entries that dataloader can manage
-        return None
-
-    def _final_cleanup(self, data1d):
-        """
-        Final cleanup of the Data1D object to be sure it has all the
-        appropriate information needed for perspectives
-
-        :param data1d: Data1D object that has been populated
-        """
-        # Final cleanup
-        # Remove empty nodes, verify array sizes are correct
-        for error in self.errors:
-            data1d.errors.append(error)
-        self.errors.clear()
-        numpy.trim_zeros(data1d.x)
-        numpy.trim_zeros(data1d.y)
-        numpy.trim_zeros(data1d.dy)
-        size_dx = data1d.dx.size
-        size_dxl = data1d.dxl.size
-        size_dxw = data1d.dxw.size
-        if data1d._xunit != data1d.x_unit:
-            data1d.x_unit = data1d._xunit
-        if data1d._yunit != data1d.y_unit:
-            data1d.y_unit = data1d._yunit
-        if size_dxl == 0 and size_dxw == 0:
-            data1d.dxl = None
-            data1d.dxw = None
-            numpy.trim_zeros(data1d.dx)
-        elif size_dx == 0:
-            data1d.dx = None
-            size_dx = size_dxl
-            numpy.trim_zeros(data1d.dxl)
-            numpy.trim_zeros(data1d.dxw)
-        return data1d
-
-    def _create_unique_key(self, dictionary, name, numb=0):
-        """
-        Create a unique key value for any dictionary to prevent overwriting
-        Recurses 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 _unit_conversion(self, node, new_current_level, data1d, \
-                                                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 new_current_level: cansas_constants level as returned by
-            iterate_namespace
-        :param attr: The attributes of the node
-        :param data1d: Where the values will be saved
-        :param node_value: The value of the current dom node
-        """
-        attr = node.attrib
-        value_unit = ''
-        err_msg = None
-        default_unit = None
-        if 'unit' in attr and new_current_level.get('unit') is not None:
-            try:
-                local_unit = attr['unit']
-                if isinstance(node_value, float) is False:
-                    exec("node_value = float({0})".format(node_value))
-                unitname = new_current_level.get("unit")
-                exec "default_unit = data1d.{0}".format(unitname)
-                if local_unit is not None and default_unit is not None 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)
-        node_value = "float({0})".format(node_value)
-        return node_value, value_unit
-
-    def _check_for_empty_data(self, data1d):
-        """
-        Creates an empty data set if no data is passed to the reader
-
-        :param data1d: presumably a Data1D object
-        """
-        if data1d == None:
-            self.errors = set()
-            x_vals = numpy.empty(0)
-            y_vals = numpy.empty(0)
-            dx_vals = numpy.empty(0)
-            dy_vals = numpy.empty(0)
-            dxl = numpy.empty(0)
-            dxw = numpy.empty(0)
-            data1d = Data1D(x_vals, y_vals, dx_vals, dy_vals)
-            data1d.dxl = dxl
-            data1d.dxw = dxw
-        return data1d
-
-    def _handle_special_cases(self, tagname, data1d, children):
-        """
-        Handle cases where the data type in Data1D is a dictionary or list
-
-        :param tagname: XML tagname in use
-        :param data1d: The original Data1D object
-        :param children: Child nodes of node
-        :param node: existing node with tag name 'tagname'
-        """
-        if tagname == "SASdetector":
-            data1d = Detector()
-        elif tagname == "SAScollimation":
-            data1d = Collimation()
-        elif tagname == "SAStransmission_spectrum":
-            data1d = TransmissionSpectrum()
-        elif tagname == "SASprocess":
-            data1d = Process()
-            for child in children:
-                if child.tag.replace(self.base_ns, "") == "term":
-                    term_attr = {}
-                    for attr in child.keys():
-                        term_attr[attr] = \
-                            ' '.join(child.get(attr).split())
-                    if child.text is not None:
-                        term_attr['value'] = \
-                            ' '.join(child.text.split())
-                    data1d.term.append(term_attr)
-        elif tagname == "aperture":
-            data1d = Aperture()
-        if tagname == "Idata" and children is not None:
-            data1d = self._check_for_empty_resolution(data1d, children)
-        return data1d
-
-    def _check_for_empty_resolution(self, data1d, children):
-        """
-        A method to check all resolution data sets are the same size as I and Q
-        """
-        dql_exists = False
-        dqw_exists = False
-        dq_exists = False
-        di_exists = False
-        for child in children:
-            tag = child.tag.replace(self.base_ns, "")
-            if tag == "dQl":
-                dql_exists = True
-            if tag == "dQw":
-                dqw_exists = True
-            if tag == "Qdev":
-                dq_exists = True
-            if tag == "Idev":
-                di_exists = True
-        if dqw_exists and dql_exists == False:
-            data1d.dxl = numpy.append(data1d.dxl, 0.0)
-        elif dql_exists and dqw_exists == False:
-            data1d.dxw = numpy.append(data1d.dxw, 0.0)
-        elif dql_exists == False and dqw_exists == False \
-                                            and dq_exists == False:
-            data1d.dx = numpy.append(data1d.dx, 0.0)
-        if di_exists == False:
-            data1d.dy = numpy.append(data1d.dy, 0.0)
-        return data1d
-
-    def _restore_original_case(self,
-                               tagname_original,
-                               tagname,
-                               save_data1d,
-                               data1d):
-        """
-        Save the special case data to the appropriate location and restore
-        the original Data1D object
-
-        :param tagname_original: Unmodified tagname for the node
-        :param tagname: modified tagname for the node
-        :param save_data1d: The original Data1D object
-        :param data1d: If a special case was handled, an object of that type
-        """
-        if tagname_original == "SASdetector":
-            save_data1d.detector.append(data1d)
-        elif tagname_original == "SAScollimation":
-            save_data1d.collimation.append(data1d)
-        elif tagname == "SAStransmission_spectrum":
-            save_data1d.trans_spectrum.append(data1d)
-        elif tagname_original == "SASprocess":
-            save_data1d.process.append(data1d)
-        elif tagname_original == "aperture":
-            save_data1d.aperture.append(data1d)
-        else:
-            save_data1d = data1d
-        return save_data1d
-
-    def _handle_attributes(self, node, data1d, cs_values, tagname):
-        """
-        Process all of the attributes for a node
-        """
-        attr = node.attrib
-        if attr is not None:
-            for key in node.keys():
-                try:
-                    node_value, unit = self._get_node_value(node, cs_values, \
-                                                   data1d, tagname)
-                    cansas_attrib = \
-                        cs_values.current_level.get("attributes").get(key)
-                    attrib_variable = cansas_attrib.get("variable")
-                    if key == 'unit' and unit != '':
-                        attrib_value = unit
-                    else:
-                        attrib_value = node.attrib[key]
-                    store_attr = attrib_variable.format("data1d",
-                                                        attrib_value,
-                                                        key,
-                                                        node_value)
-                    exec store_attr
-                except AttributeError:
-                    pass
-        return data1d
-
-    def _get_node_value(self, node, cs_values, data1d, tagname):
-        """
-        Get the value of a node and any applicable units
-
-        :param node: The XML node to get the value of
-        :param cs_values: A CansasConstants.CurrentLevel object
-        :param attr: The node attributes
-        :param dataid: The working object to be modified
-        :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 == "":
-            node_value = None
-        if node_value is not None:
-            node_value = ' '.join(node_value.split())
-
-        # If the value is a float, compile with units.
-        if cs_values.ns_datatype == "float":
-            # If an empty value is given, set as zero.
-            if node_value is None or node_value.isspace() \
-                                    or node_value.lower() == "nan":
-                node_value = "0.0"
-            #Convert the value to the base units
-            node_value, units = self._unit_conversion(node, \
-                        cs_values.current_level, data1d, tagname, node_value)
-
-        # If the value is a timestamp, convert to a datetime object
-        elif cs_values.ns_datatype == "timestamp":
-            if node_value is None or node_value.isspace():
-                pass
-            else:
-                try:
-                    node_value = \
-                        datetime.datetime.fromtimestamp(node_value)
-                except ValueError:
-                    node_value = None
-        return node_value, units
-
-    def _parse_entry(self, dom, names=None, data1d=None, extras=None):
-        """
-        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
-        :param names: A list of element names that lead up to the dom object
-        :param data1d: The data1d object that will be modified
-        :param extras: Any values that should go into meta_data when data1d
-            is not a Data1D object
-        """
-
-        if extras is None:
-            extras = []
-        if names is None or names == []:
-            names = ["SASentry"]
-
-        data1d = self._check_for_empty_data(data1d)
-
-        self.base_ns = "{0}{1}{2}".format("{", \
-                            CANSAS_NS.get(self.cansas_version).get("ns"), "}")
-        tagname = ''
-        tagname_original = ''
-
-        # Go through each child in the parent element
-        for node in dom:
-            try:
-                # Get the element name and set the current names level
-                tagname = node.tag.replace(self.base_ns, "")
-                tagname_original = tagname
-                if tagname == "fitting_plug_in" or tagname == "pr_inversion" or\
-                    tagname == "invariant":
-                    continue
-                names.append(tagname)
-                children = node.getchildren()
-                if len(children) == 0:
-                    children = None
-                save_data1d = data1d
-
-                # Look for special cases
-                data1d = self._handle_special_cases(tagname, data1d, children)
-
-                # Get where to store content
-                cs_values = CONSTANTS.iterate_namespace(names)
-                # If the element is a child element, recurse
-                if children is not None:
-                    # Returned value is new Data1D object with all previous and
-                    # new values in it.
-                    data1d, extras = self._parse_entry(node,
-                                                       names, data1d, extras)
-
-                #Get the information from the node
-                node_value, _ = self._get_node_value(node, cs_values, \
-                                                            data1d, tagname)
-
-                # If appending to a dictionary (meta_data | run_name)
-                # make sure the key is unique
-                if cs_values.ns_variable == "{0}.meta_data[\"{2}\"] = \"{1}\"":
-                    # If we are within a Process, Detector, Collimation or
-                    # Aperture instance, pull out old data1d
-                    tagname = self._create_unique_key(data1d.meta_data, \
-                                                      tagname, 0)
-                    if isinstance(data1d, Data1D) == False:
-                        store_me = cs_values.ns_variable.format("data1d", \
-                                                            node_value, tagname)
-                        extras.append(store_me)
-                        cs_values.ns_variable = None
-                if cs_values.ns_variable == "{0}.run_name[\"{2}\"] = \"{1}\"":
-                    tagname = self._create_unique_key(data1d.run_name, \
-                                                      tagname, 0)
-
-                # Check for Data1D object and any extra commands to save
-                if isinstance(data1d, Data1D):
-                    for item in extras:
-                        exec item
-                # Don't bother saving empty information unless it is a float
-                if cs_values.ns_variable is not None and \
-                            node_value is not None and \
-                            node_value.isspace() == False:
-                    # Format a string and then execute it.
-                    store_me = cs_values.ns_variable.format("data1d", \
-                                                            node_value, tagname)
-                    exec store_me
-                # Get attributes and process them
-                data1d = self._handle_attributes(node, data1d, cs_values, \
-                                                 tagname)
-
-            except TypeError:
-                pass
-            except Exception as excep:
-                exc_type, exc_obj, exc_tb = sys.exc_info()
-                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
-                print(excep, exc_type, fname, exc_tb.tb_lineno, \
-                      tagname, exc_obj)
-            finally:
-                # Save special cases in original data1d object
-                # then restore the data1d
-                save_data1d = self._restore_original_case(tagname_original, \
-                                                tagname, save_data1d, data1d)
-                if tagname_original == "fitting_plug_in" or \
-                    tagname_original == "invariant" or \
-                    tagname_original == "pr_inversion":
-                    pass
-                else:
-                    data1d = save_data1d
-                    # Remove tagname from names to restore original base
-                    names.remove(tagname_original)
-        return data1d, extras
-
-    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://svn.smallangles.net/svn/canSAS/1dwg/trunk/"
-        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 == 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 the 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.x_unit})
-            if len(datainfo.y) >= i:
-                self.write_node(point, "I", datainfo.y[i],
-                                {'unit': datainfo.y_unit})
-            if datainfo.dy != 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:
-                self.write_node(point, "Qdev", datainfo.dx[i],
-                                {'unit': datainfo.x_unit})
-            if datainfo.dxw != 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:
-                self.write_node(point, "dQl", datainfo.dxl[i],
-                                {'unit': datainfo.x_unit})
-
-    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 != 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 == 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 == 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 == 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})
-            written = written | self.write_node(det, "slit_length",
-                                                item.slit_length,
-                                                {"unit": item.slit_length_unit})
-            if written == True:
-                self.append(pix, det)
-
-    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:
-                value = term['value']
-                del term['value']
-                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_origin(self, entry_node, doc, frm):
-        """
-        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 frm:
-            frm = inspect.stack()[1]
-        mod_name = frm[1].replace("\\", "/").replace(".pyc", "")
-        mod_name = mod_name.replace(".py", "")
-        mod = mod_name.split("sas/")
-        mod_name = mod[1]
-        if mod_name != "dataloader/readers/cansas_reader":
-            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
-
-    def _to_xml_doc(self, datainfo):
-        """
-        Create an XML document to contain the content of a Data1D
-
-        :param datainfo: Data1D object
-        """
-        if not issubclass(datainfo.__class__, Data1D):
-            raise RuntimeError, "The cansas writer expects a Data1D instance"
-
-        # 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
-        self._write_data(datainfo, entry_node)
-        # Transmission Spectrum Info
-        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.
-        frm = inspect.stack()[1]
-        doc, entry_node = self._check_origin(entry_node, doc, frm)
-        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 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 == None:
-            self.encoding = "UTF-8"
-        doc.write(file_ref, encoding=self.encoding,
-                  pretty_print=True, xml_declaration=True)
-        file_ref.close()
-
-    # 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 != 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:
-                                logging.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:
-                            logging.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
Index: c/sas/dataloader/readers/danse_reader.py
===================================================================
--- src/sas/dataloader/readers/danse_reader.py	(revision fd5ac0db8089df5bd882587f157d4bf317cfcf67)
+++ 	(revision )
@@ -1,280 +1,0 @@
-"""
-    DANSE/SANS file reader
-"""
-############################################################################
-#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, University of Tennessee
-#############################################################################
-import math
-import os
-import sys
-import numpy
-import logging
-from sas.dataloader.data_info import Data2D, Detector
-from sas.dataloader.manipulations import reader2D_converter
-
-# Look for unit converter
-has_converter = True
-try:
-    from sas.data_util.nxsunit import Converter
-except:
-    has_converter = False
-
-
-class Reader:
-    """
-    Example data manipulation
-    """
-    ## File type
-    type_name = "DANSE"
-    ## Wildcards
-    type = ["DANSE files (*.sans)|*.sans"]
-    ## Extension
-    ext  = ['.sans', '.SANS']
-        
-    def read(self, filename=None):
-        """
-        Open and read the data in a file
-        @param file: path of the file
-        """
-        
-        read_it = False
-        for item in self.ext:
-            if filename.lower().find(item) >= 0:
-                read_it = True
-                
-        if read_it:
-            try:
-                datafile = open(filename, 'r')
-            except:
-                raise  RuntimeError,"danse_reader cannot open %s" % (filename)
-        
-            # defaults
-            # wavelength in Angstrom
-            wavelength = 10.0
-            # Distance in meter
-            distance   = 11.0
-            # Pixel number of center in x
-            center_x   = 65
-            # Pixel number of center in y
-            center_y   = 65
-            # Pixel size [mm]
-            pixel      = 5.0
-            # Size in x, in pixels
-            size_x     = 128
-            # Size in y, in pixels
-            size_y     = 128
-            # Format version
-            fversion   = 1.0
-            
-            output = Data2D()
-            output.filename = os.path.basename(filename)
-            detector = Detector()
-            output.detector.append(detector)
-            
-            output.data = numpy.zeros([size_x,size_y])
-            output.err_data = numpy.zeros([size_x, size_y])
-            
-            data_conv_q = None
-            data_conv_i = None
-            
-            if has_converter == True and output.Q_unit != '1/A':
-                data_conv_q = Converter('1/A')
-                # Test it
-                data_conv_q(1.0, output.Q_unit)
-                
-            if has_converter == True and output.I_unit != '1/cm':
-                data_conv_i = Converter('1/cm')
-                # Test it
-                data_conv_i(1.0, output.I_unit)
-        
-            read_on = True
-            while read_on:
-                line = datafile.readline()
-                if line.find("DATA:") >= 0:
-                    read_on = False
-                    break
-                toks = line.split(':')
-                if toks[0] == "FORMATVERSION":
-                    fversion = float(toks[1])
-                if toks[0] == "WAVELENGTH":
-                    wavelength = float(toks[1])
-                elif toks[0] == "DISTANCE":
-                    distance = float(toks[1])
-                elif toks[0] == "CENTER_X":
-                    center_x = float(toks[1])
-                elif toks[0] == "CENTER_Y":
-                    center_y = float(toks[1])
-                elif toks[0] == "PIXELSIZE":
-                    pixel = float(toks[1])
-                elif toks[0] == "SIZE_X":
-                    size_x = int(toks[1])
-                elif toks[0] == "SIZE_Y":
-                    size_y = int(toks[1])
-            
-            # Read the data
-            data = []
-            error = []
-            if fversion == 1.0:
-                data_str = datafile.readline()
-                data = data_str.split(' ')
-            else:
-                read_on = True
-                while read_on:
-                    data_str = datafile.readline()
-                    if len(data_str) == 0:
-                        read_on = False
-                    else:
-                        toks = data_str.split()
-                        try:
-                            val = float(toks[0])
-                            err = float(toks[1])
-                            if data_conv_i is not None:
-                                val = data_conv_i(val, units=output._yunit)
-                                err = data_conv_i(err, units=output._yunit)
-                            data.append(val)
-                            error.append(err)
-                        except:
-                            logging.info("Skipping line:%s,%s" %(data_str,
-                                                                sys.exc_value))
-            
-            # Initialize
-            x_vals = []
-            y_vals = []
-            ymin = None
-            ymax = None
-            xmin = None
-            xmax = None
-            
-            # Qx and Qy vectors
-            theta = pixel / distance / 100.0
-            stepq = 4.0 * math.pi / wavelength * math.sin(theta / 2.0)
-            for i_x in range(size_x):
-                theta = (i_x - center_x + 1) * pixel / distance / 100.0
-                qx = 4.0 * math.pi / wavelength * math.sin(theta / 2.0)
-                
-                if has_converter == True and output.Q_unit != '1/A':
-                    qx = data_conv_q(qx, units=output.Q_unit)
-                
-                x_vals.append(qx)
-                if xmin == None or qx < xmin:
-                    xmin = qx
-                if xmax == None or qx > xmax:
-                    xmax = qx
-            
-            ymin = None
-            ymax = None
-            for i_y in range(size_y):
-                theta = (i_y - center_y + 1) * pixel / distance / 100.0
-                qy = 4.0 * math.pi / wavelength * math.sin(theta/2.0)
-                
-                if has_converter == True and output.Q_unit != '1/A':
-                    qy = data_conv_q(qy, units=output.Q_unit)
-                
-                y_vals.append(qy)
-                if ymin == None or qy < ymin:
-                    ymin = qy
-                if ymax == None or qy > ymax:
-                    ymax = qy
-            
-            # Store the data in the 2D array
-            i_x = 0
-            i_y = -1
-            
-            for i_pt in range(len(data)):
-                try:
-                    value = float(data[i_pt])
-                except:
-                    # For version 1.0, the data were still
-                    # stored as strings at this point.
-                    msg = "Skipping entry (v1.0):%s,%s" % (str(data[i_pt]),
-                                                           sys.exc_value)
-                    logging.info(msg)
-                
-                # Get bin number
-                if math.fmod(i_pt, size_x) == 0:
-                    i_x = 0
-                    i_y += 1
-                else:
-                    i_x += 1
-                    
-                output.data[i_y][i_x] = value
-                if fversion>1.0:
-                    output.err_data[i_y][i_x] = error[i_pt]
-                
-            # Store all data
-            # Store wavelength
-            if has_converter == True and output.source.wavelength_unit != 'A':
-                conv = Converter('A')
-                wavelength = conv(wavelength,
-                                  units=output.source.wavelength_unit)
-            output.source.wavelength = wavelength
-                
-            # Store distance
-            if has_converter == True and detector.distance_unit != 'm':
-                conv = Converter('m')
-                distance = conv(distance, units=detector.distance_unit)
-            detector.distance = distance
-            
-            # Store pixel size
-            if has_converter == True and detector.pixel_size_unit != 'mm':
-                conv = Converter('mm')
-                pixel = conv(pixel, units=detector.pixel_size_unit)
-            detector.pixel_size.x = pixel
-            detector.pixel_size.y = pixel
-
-            # Store beam center in distance units
-            detector.beam_center.x = center_x * pixel
-            detector.beam_center.y = center_y * pixel
-            
-            # Store limits of the image (2D array)
-            xmin = xmin - stepq / 2.0
-            xmax = xmax + stepq / 2.0
-            ymin = ymin - stepq /2.0
-            ymax = ymax + stepq / 2.0
-            
-            if has_converter == True and output.Q_unit != '1/A':
-                xmin = data_conv_q(xmin, units=output.Q_unit)
-                xmax = data_conv_q(xmax, units=output.Q_unit)
-                ymin = data_conv_q(ymin, units=output.Q_unit)
-                ymax = data_conv_q(ymax, units=output.Q_unit)
-            output.xmin = xmin
-            output.xmax = xmax
-            output.ymin = ymin
-            output.ymax = ymax
-            
-            # Store x and y axis bin centers
-            output.x_bins = x_vals
-            output.y_bins = y_vals
-           
-            # Units
-            if data_conv_q is not None:
-                output.xaxis("\\rm{Q_{x}}", output.Q_unit)
-                output.yaxis("\\rm{Q_{y}}", output.Q_unit)
-            else:
-                output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
-                output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
-                
-            if data_conv_i is not None:
-                output.zaxis("\\rm{Intensity}", output.I_unit)
-            else:
-                output.zaxis("\\rm{Intensity}", "cm^{-1}")
-           
-            if not fversion >= 1.0:
-                msg = "Danse_reader can't read this file %s" % filename
-                raise ValueError, msg
-            else:
-                logging.info("Danse_reader Reading %s \n" % filename)
-            
-            # Store loading process information
-            output.meta_data['loader'] = self.type_name
-            output = reader2D_converter(output)
-            return output
-        
-        return None
Index: c/sas/dataloader/readers/defaults.json
===================================================================
--- src/sas/dataloader/readers/defaults.json	(revision 5e326a64f3587643c6a4cb468b4e95e5c900be9b)
+++ 	(revision )
@@ -1,42 +1,0 @@
-{
-   "SasLoader":{
-      "FileType":[
-         {
-            "-extension":".xml",
-            "-reader":"cansas_reader"
-         },
-         {
-            "-extension":".ses",
-            "-reader":"sesans_reader"
-         },
-         {
-            "-extension":".txt",
-            "-reader":"ascii_reader"
-         },
-         {
-            "-extension":".asc",
-            "-reader":"IgorReader"
-         },
-         {
-            "-extension":".dat",
-            "-reader":"red2d_reader"
-         },
-         {
-            "-extension":".abs",
-            "-reader":"abs_reader"
-         },
-         {
-            "-extension":".d1d",
-            "-reader":"hfir1d_reader"
-         },
-         {
-            "-extension":".sans",
-            "-reader":"danse_reader"
-         },
-         {
-            "-extension":".nxs",
-            "-reader":"nexus_reader"
-         }
-      ]
-   }
-}
Index: c/sas/dataloader/readers/hfir1d_reader.py
===================================================================
--- src/sas/dataloader/readers/hfir1d_reader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,129 +1,0 @@
-"""
-    HFIR 1D 4-column data reader
-"""
-#####################################################################
-#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.
-#See the license text in license.txt
-#copyright 2008, University of Tennessee
-######################################################################
-import numpy
-import os
-from sas.dataloader.data_info import Data1D
-
-# Check whether we have a converter available
-has_converter = True
-try:
-    from sas.data_util.nxsunit import Converter
-except:
-    has_converter = False
-
-class Reader(object):
-    """
-    Class to load HFIR 1D 4-column files
-    """
-    ## File type
-    type_name = "HFIR 1D"
-    ## Wildcards
-    type = ["HFIR 1D files (*.d1d)|*.d1d"]
-    ## List of allowed extensions
-    ext = ['.d1d']
-    
-    def read(self, path):
-        """ 
-        Load data file
-        
-        :param path: file path
-        
-        :return: Data1D object, or None
-        
-        :raise RuntimeError: when the file can't be opened
-        :raise ValueError: when the length of the data vectors are inconsistent
-        """
-        if os.path.isfile(path):
-            basename = os.path.basename(path)
-            root, extension = os.path.splitext(basename)
-            if extension.lower() in self.ext:
-                try:
-                    input_f = open(path,'r')
-                except:
-                    raise  RuntimeError, "hfir1d_reader: cannot open %s" % path
-                buff = input_f.read()
-                lines = buff.split('\n')
-                x = numpy.zeros(0)
-                y = numpy.zeros(0)
-                dx = numpy.zeros(0)
-                dy = numpy.zeros(0)
-                output = Data1D(x, y, dx=dx, dy=dy)
-                self.filename = output.filename = basename
-           
-                data_conv_q = None
-                data_conv_i = None
-                
-                if has_converter == True and output.x_unit != '1/A':
-                    data_conv_q = Converter('1/A')
-                    # Test it
-                    data_conv_q(1.0, output.x_unit)
-                    
-                if has_converter == True and output.y_unit != '1/cm':
-                    data_conv_i = Converter('1/cm')
-                    # Test it
-                    data_conv_i(1.0, output.y_unit)
-                           
-                for line in lines:
-                    toks = line.split()
-                    try:
-                        _x = float(toks[0])
-                        _y = float(toks[1])
-                        _dx = float(toks[3])
-                        _dy = float(toks[2])
-                        
-                        if data_conv_q is not None:
-                            _x = data_conv_q(_x, units=output.x_unit)
-                            _dx = data_conv_q(_dx, units=output.x_unit)
-                            
-                        if data_conv_i is not None:
-                            _y = data_conv_i(_y, units=output.y_unit)
-                            _dy = data_conv_i(_dy, units=output.y_unit)
-                                                    
-                        x = numpy.append(x, _x)
-                        y = numpy.append(y, _y)
-                        dx = numpy.append(dx, _dx)
-                        dy = numpy.append(dy, _dy)
-                    except:
-                        # Couldn't parse this line, skip it 
-                        pass
-                         
-                # Sanity check
-                if not len(y) == len(dy):
-                    msg = "hfir1d_reader: y and dy have different length"
-                    raise RuntimeError, msg
-                if not len(x) == len(dx):
-                    msg = "hfir1d_reader: x and dx have different length"
-                    raise RuntimeError, msg
-
-                # If the data length is zero, consider this as
-                # though we were not able to read the file.
-                if len(x) == 0:
-                    raise RuntimeError, "hfir1d_reader: could not load file"
-               
-                output.x = x
-                output.y = y
-                output.dy = dy
-                output.dx = dx
-                if data_conv_q is not None:
-                    output.xaxis("\\rm{Q}", output.x_unit)
-                else:
-                    output.xaxis("\\rm{Q}", 'A^{-1}')
-                if data_conv_i is not None:
-                    output.yaxis("\\rm{Intensity}", output.y_unit)
-                else:
-                    output.yaxis("\\rm{Intensity}", "cm^{-1}")
-                
-                # Store loading process information
-                output.meta_data['loader'] = self.type_name
-                return output
-        else:
-            raise RuntimeError, "%s is not a file" % path
-        return None
Index: c/sas/dataloader/readers/nexus_reader.py
===================================================================
--- src/sas/dataloader/readers/nexus_reader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,96 +1,0 @@
-#####################################################################
-#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.
-#See the license text in license.txt
-#copyright 2012, University of Tennessee
-######################################################################
-"""
-    Nexus reader for 2D data reduced by Mantid
-"""
-import os
-from sas.dataloader.data_info import Data2D
-
-
-class Reader:
-    """
-        Nexus reader for 2D data reduced by Mantid
-    """
-    ## File type
-    type_name = "NXS"
-    ## Wildcards
-    type = ["Nexus files (*.nxs)|*.nxs"]
-    ## Extension
-    ext = ['.nxs']
-        
-    def read(self, filename=None):
-        """
-            Open and read the data in a file
-        
-            :param filename: path of the file
-        """
-        try:
-            import nxs
-        except:
-            msg = "Error reading Nexus file: Nexus package is missing.\n"
-            msg += "  Get it from http://http://www.nexusformat.org/"
-            raise RuntimeError, msg
-        
-        # Instantiate data object
-        output = Data2D()
-        output.filename = os.path.basename(filename)
-
-        fd = nxs.open(filename, 'rw')
-        
-        # Read in the 2D data
-        fd.opengroup('mantid_workspace_1')
-        fd.opengroup('workspace')
-        fd.opendata('values')
-        output.data = fd.getdata().copy()
-        fd.closedata()
-        
-        # Read in the errors
-        fd.opendata('errors')
-        output.err_data = fd.getdata().copy()
-        fd.closedata()
-        
-        # Read in the values on each axis
-        fd.opendata('axis1')
-        output.x_bins = fd.getdata().copy()
-        fd.closedata()
-        
-        fd.opendata('axis2')
-        output.y_bins = fd.getdata().copy()
-        fd.closedata()
-        fd.closegroup()
-                
-        output.xmin = min(output.x_bins)
-        output.xmax = max(output.x_bins)
-        output.ymin = min(output.y_bins)
-        output.ymax = max(output.y_bins)
-        
-        output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
-        output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
-        output.zaxis("\\rm{Intensity}", "cm^{-1}")
-
-        # Meta data
-        fd.opendata('title')
-        output.title = fd.getdata()
-        fd.closedata()
-        
-        fd.opengroup('instrument')
-        fd.opendata('name')
-        output.instrument = fd.getdata()
-        fd.closedata()
-        fd.closegroup()
-
-        fd.opengroup('logs')
-        fd.opengroup('run_number')
-        fd.opendata('value')
-        output.run = fd.getdata()
-
-        fd.close()
-
-        # Store loading process information
-        output.meta_data['loader'] = self.type_name
-        return output
Index: c/sas/dataloader/readers/red2d_reader.py
===================================================================
--- src/sas/dataloader/readers/red2d_reader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,367 +1,0 @@
-"""
-    TXT/IGOR 2D Q Map file reader
-"""
-#####################################################################
-#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. 
-#See the license text in license.txt
-#copyright 2008, University of Tennessee
-######################################################################
-import os
-import numpy
-import math
-from sas.dataloader.data_info import Data2D, Detector
-
-# Look for unit converter
-has_converter = True
-try:
-    from sas.data_util.nxsunit import Converter
-except:
-    has_converter = False
-    
-    
-def check_point(x_point):
-    """
-    check point validity
-    """
-    # set zero for non_floats
-    try:
-        return float(x_point)
-    except:
-        return 0
-      
-        
-class Reader:
-    """ Simple data reader for Igor data files """
-    ## File type
-    type_name = "IGOR/DAT 2D Q_map"
-    ## Wildcards
-    type = ["IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"]
-    ## Extension
-    ext = ['.DAT', '.dat']
-    
-    def write(self, filename, data):
-        """
-        Write to .dat
-        
-        :param filename: file name to write
-        :param data: data2D
-        """
-        import time
-        # Write the file
-        fd = open(filename, 'w')
-        t = time.localtime()
-        time_str = time.strftime("%H:%M on %b %d %y", t)
-        
-        header_str = "Data columns are Qx - Qy - I(Qx,Qy)\n\nASCII data"
-        header_str += " created at %s \n\n" % time_str
-        # simple 2D header
-        fd.write(header_str)
-        # write qx qy I values
-        for i in range(len(data.data)):
-            fd.write("%g  %g  %g\n" % (data.qx_data[i],
-                                        data.qy_data[i],
-                                       data.data[i]))
-        # close
-        fd.close()
-
-    def read(self, filename=None):
-        """ Read file """
-        if not os.path.isfile(filename):
-            raise ValueError, \
-            "Specified file %s is not a regular file" % filename
-
-        # Read file
-        f = open(filename, 'r')
-        buf = f.read()
-        f.close()
-        # Instantiate data object
-        output = Data2D()
-        output.filename = os.path.basename(filename)
-        detector = Detector()
-        if len(output.detector) > 0:
-            print str(output.detector[0])
-        output.detector.append(detector)
-                
-        # Get content
-        dataStarted = False
-        
-        ## Defaults
-        lines = buf.split('\n')
-        x = []
-        y = []
-        
-        wavelength = None
-        distance = None
-        transmission = None
-        
-        pixel_x = None
-        pixel_y = None
-        
-        isInfo = False
-        isCenter = False
-
-        data_conv_q = None
-        data_conv_i = None
-        
-        # Set units: This is the unit assumed for Q and I in the data file.
-        if has_converter == True and output.Q_unit != '1/A':
-            data_conv_q = Converter('1/A')
-            # Test it
-            data_conv_q(1.0, output.Q_unit)
-            
-        if has_converter == True and output.I_unit != '1/cm':
-            data_conv_i = Converter('1/cm')
-            # Test it
-            data_conv_i(1.0, output.I_unit)
-        
-              
-        # Remove the last lines before the for loop if the lines are empty
-        # to calculate the exact number of data points
-        count = 0
-        while (len(lines[len(lines) - (count + 1)].lstrip().rstrip()) < 1):
-            del lines[len(lines) - (count + 1)]
-            count = count + 1
-
-        #Read Header and find the dimensions of 2D data
-        line_num = 0
-        # Old version NIST files: 0
-        ver = 0
-        for line in lines:
-            line_num += 1
-            ## Reading the header applies only to IGOR/NIST 2D q_map data files
-            # Find setup info line
-            if isInfo:
-                isInfo = False
-                line_toks = line.split()
-                # Wavelength in Angstrom
-                try:
-                    wavelength = float(line_toks[1])
-                    # Units
-                    if has_converter == True and \
-                    output.source.wavelength_unit != 'A':
-                        conv = Converter('A')
-                        wavelength = conv(wavelength,
-                                          units=output.source.wavelength_unit)
-                except:
-                    #Not required
-                    pass
-                # Distance in mm
-                try:
-                    distance = float(line_toks[3])
-                    # Units
-                    if has_converter == True and detector.distance_unit != 'm':
-                        conv = Converter('m')
-                        distance = conv(distance, units=detector.distance_unit)
-                except:
-                    #Not required
-                    pass
-                
-                # Distance in meters
-                try:
-                    transmission = float(line_toks[4])
-                except:
-                    #Not required
-                    pass
-                                            
-            if line.count("LAMBDA") > 0:
-                isInfo = True
-                
-            # Find center info line
-            if isCenter:
-                isCenter = False
-                line_toks = line.split()
-                # Center in bin number
-                center_x = float(line_toks[0])
-                center_y = float(line_toks[1])
-
-            if line.count("BCENT") > 0:
-                isCenter = True
-            # Check version
-            if line.count("Data columns") > 0:
-                if line.count("err(I)") > 0:
-                    ver = 1
-            # Find data start
-            if line.count("ASCII data") > 0:
-                dataStarted = True
-                continue
-
-            ## Read and get data.
-            if dataStarted == True:
-                line_toks = line.split()
-                if len(line_toks) == 0:
-                    #empty line
-                    continue
-                # the number of columns must be stayed same 
-                col_num = len(line_toks)
-                break
-        # Make numpy array to remove header lines using index
-        lines_array = numpy.array(lines)
-
-        # index for lines_array
-        lines_index = numpy.arange(len(lines))
-        
-        # get the data lines
-        data_lines = lines_array[lines_index >= (line_num - 1)]
-        # Now we get the total number of rows (i.e., # of data points)
-        row_num = len(data_lines)
-        # make it as list again to control the separators
-        data_list = " ".join(data_lines.tolist())
-        # split all data to one big list w/" "separator
-        data_list = data_list.split()
- 
-        # Check if the size is consistent with data, otherwise
-        #try the tab(\t) separator
-        # (this may be removed once get the confidence
-        #the former working all cases).
-        if len(data_list) != (len(data_lines)) * col_num:
-            data_list = "\t".join(data_lines.tolist())
-            data_list = data_list.split()
-
-        # Change it(string) into float
-        #data_list = map(float,data_list)
-        data_list1 = map(check_point, data_list)
-
-        # numpy array form
-        data_array = numpy.array(data_list1)
-        # Redimesion based on the row_num and col_num,
-        #otherwise raise an error.
-        try:
-            data_point = data_array.reshape(row_num, col_num).transpose()
-        except:
-            msg = "red2d_reader: Can't read this file: Not a proper file format"
-            raise ValueError, msg
-        ## Get the all data: Let's HARDcoding; Todo find better way
-        # Defaults
-        dqx_data = numpy.zeros(0)
-        dqy_data = numpy.zeros(0)
-        err_data = numpy.ones(row_num)
-        qz_data = numpy.zeros(row_num)
-        mask = numpy.ones(row_num, dtype=bool)
-        # Get from the array
-        qx_data = data_point[0]
-        qy_data = data_point[1]
-        data = data_point[2]
-        if ver == 1:
-            if col_num > (2 + ver):
-                err_data = data_point[(2 + ver)]
-        if col_num > (3 + ver):
-            qz_data = data_point[(3 + ver)]
-        if col_num > (4 + ver):
-            dqx_data = data_point[(4 + ver)]
-        if col_num > (5 + ver):
-            dqy_data = data_point[(5 + ver)]
-        #if col_num > (6 + ver): mask[data_point[(6 + ver)] < 1] = False
-        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data+qz_data*qz_data)
-           
-        # Extra protection(it is needed for some data files): 
-        # If all mask elements are False, put all True
-        if not mask.any():
-            mask[mask == False] = True
-  
-        # Store limits of the image in q space
-        xmin = numpy.min(qx_data)
-        xmax = numpy.max(qx_data)
-        ymin = numpy.min(qy_data)
-        ymax = numpy.max(qy_data)
-
-        # units
-        if has_converter == True and output.Q_unit != '1/A':
-            xmin = data_conv_q(xmin, units=output.Q_unit)
-            xmax = data_conv_q(xmax, units=output.Q_unit)
-            ymin = data_conv_q(ymin, units=output.Q_unit)
-            ymax = data_conv_q(ymax, units=output.Q_unit)
-            
-        ## calculate the range of the qx and qy_data
-        x_size = math.fabs(xmax - xmin)
-        y_size = math.fabs(ymax - ymin)
-        
-        # calculate the number of pixels in the each axes
-        npix_y = math.floor(math.sqrt(len(data)))
-        npix_x = math.floor(len(data) / npix_y)
-        
-        # calculate the size of bins
-        xstep = x_size / (npix_x - 1)
-        ystep = y_size / (npix_y - 1)
-        
-        # store x and y axis bin centers in q space
-        x_bins = numpy.arange(xmin, xmax + xstep, xstep)
-        y_bins = numpy.arange(ymin, ymax + ystep, ystep)
-       
-        # get the limits of q values
-        xmin = xmin - xstep / 2
-        xmax = xmax + xstep / 2
-        ymin = ymin - ystep / 2
-        ymax = ymax + ystep / 2
-        
-        #Store data in outputs
-        #TODO: Check the lengths
-        output.data = data
-        if (err_data == 1).all():
-            output.err_data = numpy.sqrt(numpy.abs(data))
-            output.err_data[output.err_data == 0.0] = 1.0
-        else:
-            output.err_data = err_data
-            
-        output.qx_data = qx_data
-        output.qy_data = qy_data
-        output.q_data = q_data
-        output.mask = mask
-        
-        output.x_bins = x_bins
-        output.y_bins = y_bins
-               
-        output.xmin = xmin
-        output.xmax = xmax
-        output.ymin = ymin
-        output.ymax = ymax
-        
-        output.source.wavelength = wavelength
-        
-        # Store pixel size in mm
-        detector.pixel_size.x = pixel_x
-        detector.pixel_size.y = pixel_y
-        
-        # Store the sample to detector distance
-        detector.distance = distance
-        
-        # optional data: if all of dq data == 0, do not pass to output
-        if len(dqx_data) == len(qx_data) and dqx_data.any() != 0:
-            # if no dqx_data, do not pass dqy_data.
-            #(1 axis dq is not supported yet).
-            if len(dqy_data) == len(qy_data) and dqy_data.any() != 0:
-                # Currently we do not support dq parr, perp.
-                # tranfer the comp. to cartesian coord. for newer version.
-                if ver != 1:
-                    diag = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
-                    cos_th = qx_data / diag
-                    sin_th = qy_data / diag
-                    output.dqx_data = numpy.sqrt((dqx_data * cos_th) * \
-                                                 (dqx_data * cos_th) \
-                                                 + (dqy_data * sin_th) * \
-                                                  (dqy_data * sin_th))
-                    output.dqy_data = numpy.sqrt((dqx_data * sin_th) * \
-                                                 (dqx_data * sin_th) \
-                                                 + (dqy_data * cos_th) * \
-                                                  (dqy_data * cos_th))
-                else:
-                    output.dqx_data = dqx_data
-                    output.dqy_data = dqy_data
-
-        # Units of axes
-        if data_conv_q is not None:
-            output.xaxis("\\rm{Q_{x}}", output.Q_unit)
-            output.yaxis("\\rm{Q_{y}}", output.Q_unit)
-        else:
-            output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
-            output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
-        if data_conv_i is not None:
-            output.zaxis("\\rm{Intensity}", output.I_unit)
-        else:
-            output.zaxis("\\rm{Intensity}", "cm^{-1}")
-    
-        # Store loading process information
-        output.meta_data['loader'] = self.type_name
-
-        return output
Index: c/sas/dataloader/readers/schema/cansas1d_v1_0.xsd
===================================================================
--- src/sas/dataloader/readers/schema/cansas1d_v1_0.xsd	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,238 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?xml-stylesheet type="text/xsl" href="xsd-documentation.xsl" ?>
-<schema xmlns="http://www.w3.org/2001/XMLSchema"
-	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
-	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"  jaxb:version="2.0"
-	targetNamespace="cansas1d/1.0" xmlns:tns="cansas1d/1.0"
-	elementFormDefault="qualified">
-
-<!-- JAXB: http://java.sun.com/developer/technicalArticles/WebServices/jaxb/ -->
-
-	<complexType name="floatUnitType">
-		<simpleContent>
-			<extension base="float">
-				<attribute name="unit" type="string" use="required" />
-			</extension>
-		</simpleContent>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<group name="positionGroup">
-		<sequence>
-			<element name="x" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="y" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="z" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-		</sequence>
-	</group>
-
-	<complexType name="positionType">
-		<group ref="tns:positionGroup" />
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<group name="orientationGroup">
-		<sequence>
-			<element name="roll" type="tns:floatUnitType" minOccurs="0"  maxOccurs="1" />
-			<element name="pitch" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="yaw" type="tns:floatUnitType" minOccurs="0"   maxOccurs="1" />
-		</sequence>
-	</group>
-
-	<complexType name="orientationType">
-		<group ref="tns:orientationGroup" />
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="IdataType">
-		<sequence>
-			<element name="Q" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
-			<element name="I" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
-			<element name="Idev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-			<xsd:choice>
-				<!-- [ [Qdev] | [[dQw] | [dQl]] ] -->
-				<element name="Qdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-				<xsd:sequence>
-					<element name="dQw" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-					<element name="dQl" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-				</xsd:sequence>
-			</xsd:choice>
-			<element name="Qmean" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-			<element name="Shadowfactor" minOccurs="0" maxOccurs="1" type="float" default="1.0" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-	</complexType>
-
-	<complexType name="SASdataType">
-		<sequence>
-			<element name="Idata" minOccurs="1" maxOccurs="unbounded" type="tns:IdataType" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASsampleType">
-		<sequence>
-			<element name="ID" minOccurs="1" maxOccurs="1" type="string" />
-			<element name="thickness" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="transmission" minOccurs="0" maxOccurs="1" type="float" />
-			<element name="temperature" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="position" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
-			<element name="details" minOccurs="0" maxOccurs="unbounded" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="termType">
-		<simpleContent>
-			<extension base="string">
-				<attribute name="name" type="string" use="optional" default="" />
-				<attribute name="unit" type="string" use="optional"/>
-			</extension>
-		</simpleContent>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASprocessType">
-		<annotation>
-			<appinfo>
-				<!-- JAXB: simplify class name -->
-				<jaxb:property name="SASprocess" />
-			</appinfo>
-		</annotation>
-		<sequence>
-			<element name="name" minOccurs="0" maxOccurs="1"
-				type="string" default="">
-				<annotation>
-					<appinfo>
-						<!-- Remap collision of Name when using JAXB -->
-						<jaxb:property name="SASprocessName" />
-					</appinfo>
-				</annotation>
-			</element>
-			<element name="date" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="description" minOccurs="0" maxOccurs="1" />
-			<element name="term" minOccurs="0" maxOccurs="unbounded" type="tns:termType" />
-			<element name="SASprocessnote" minOccurs="1" maxOccurs="unbounded" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="">
-			<annotation>
-				<appinfo>
-					<!-- Remap collision of Name when using JAXB -->
-					<jaxb:property name="SASprocessNameAttr" />
-				</appinfo>
-			</annotation>
-		</attribute>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASsourceType">
-		<sequence>
-			<element name="radiation" minOccurs="1" maxOccurs="1" type="string" />
-			<element name="beam_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="beam_shape" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="wavelength" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_min" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_max" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_spread" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<complexType name="SAScollimationType">
-		<sequence>
-			<element name="length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="aperture" minOccurs="0"
-				maxOccurs="unbounded">
-				<complexType>
-					<sequence>
-						<element name="size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-						<element name="distance" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-					</sequence>
-					<attribute name="name" type="string" use="optional" default="" />
-					<attribute name="type" type="string" use="optional" default="" />
-				</complexType>
-			</element>
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<complexType name="SASdetectorType">
-		<sequence>
-			<element name="name" minOccurs="1" maxOccurs="1" type="string" default=""/>
-			<element name="SDD" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="offset" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
-			<element name="beam_center" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="pixel_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="slit_length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-		</sequence>
-	</complexType>
-
-	<complexType name="SASinstrumentType">
-		<sequence>
-			<element name="name" type="string" minOccurs="1" maxOccurs="1" default="" />
-			<element name="SASsource" type="tns:SASsourceType" minOccurs="1" maxOccurs="1" />
-			<element name="SAScollimation" type="tns:SAScollimationType" minOccurs="1" maxOccurs="unbounded" />
-			<element name="SASdetector" type="tns:SASdetectorType" minOccurs="1" maxOccurs="unbounded" />
-		</sequence>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASentryType">
-		<sequence>
-			<element name="Title" minOccurs="1" maxOccurs="1" type="string" />
-			<element name="Run" minOccurs="1" maxOccurs="unbounded">
-				<xsd:complexType>
-					<simpleContent>
-						<extension base="string">
-							<attribute name="name" type="string" use="optional" default="" />
-						</extension>
-					</simpleContent>
-				</xsd:complexType>
-			</element>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-			<element name="SASdata" minOccurs="1" maxOccurs="unbounded" type="tns:SASdataType" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other">
-				<!-- Is a second *any* element needed here? -->
-				<annotation>
-					<appinfo>
-						<!-- Remap collision of Any when using JAXB --> 
-						<jaxb:property name="SASentryAny2" />
-					</appinfo>
-				</annotation>
-			</xsd:any>
-			<element name="SASsample" type="tns:SASsampleType" />
-			<element name="SASinstrument" type="tns:SASinstrumentType" minOccurs="1" maxOccurs="1" />
-			<element name="SASprocess" type="tns:SASprocessType" minOccurs="0" maxOccurs="unbounded" />
-			<element name="SASnote" minOccurs="1" maxOccurs="unbounded" />
-				<!-- SASnote is a free-form element.
-					Do not define any content for it or it will become restrictive.
-				-->
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASrootType">
-		<sequence>
-			<element name="SASentry" minOccurs="1" maxOccurs="unbounded" type="tns:SASentryType" />
-		</sequence>
-		<attribute name="version" type="string" fixed="1.0" use="required" />
-	</complexType>
-
-	<element name="SASroot" type="tns:SASrootType" />
-
-</schema>
Index: c/sas/dataloader/readers/schema/cansas1d_v1_1.xsd
===================================================================
--- src/sas/dataloader/readers/schema/cansas1d_v1_1.xsd	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,271 +1,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?xml-stylesheet type="text/xsl" href="xsd-documentation.xsl" ?>
-<schema xmlns="http://www.w3.org/2001/XMLSchema"
-	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
-	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"  jaxb:version="2.0"
-	targetNamespace="urn:cansas1d:1.1" xmlns:tns="urn:cansas1d:1.1"
-	elementFormDefault="qualified">
-
-<!-- 
-Copyright (c) 2013, UChicago Argonne, LLC
-This file is distributed subject to a Software License Agreement found
-in the file LICENSE that is included with this distribution. 
- -->
- 
-<!-- JAXB: http://java.sun.com/developer/technicalArticles/WebServices/jaxb/ -->
-
-	<complexType name="floatUnitType">
-		<simpleContent>
-			<extension base="float">
-				<attribute name="unit" type="string" use="required" />
-			</extension>
-		</simpleContent>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<group name="positionGroup">
-		<sequence>
-			<element name="x" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="y" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="z" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-		</sequence>
-	</group>
-
-	<complexType name="positionType">
-		<group ref="tns:positionGroup" />
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<group name="orientationGroup">
-		<sequence>
-			<element name="roll" type="tns:floatUnitType" minOccurs="0"  maxOccurs="1" />
-			<element name="pitch" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
-			<element name="yaw" type="tns:floatUnitType" minOccurs="0"   maxOccurs="1" />
-		</sequence>
-	</group>
-
-	<complexType name="orientationType">
-		<group ref="tns:orientationGroup" />
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-	
-	<complexType name="IdataType">
-		<sequence>
-			<element name="Q" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
-			<element name="I" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
-			<element name="Idev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-			<xsd:choice>
-				<!-- [ [Qdev] | [[dQw] | [dQl]] ] -->
-				<element name="Qdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-				<xsd:sequence>
-					<element name="dQw" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-					<element name="dQl" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-				</xsd:sequence>
-			</xsd:choice>
-			<element name="Qmean" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-			<element name="Shadowfactor" minOccurs="0" maxOccurs="1" type="float" default="1.0" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-	</complexType>
-	
-	<complexType name="SASdataType">
-		<sequence minOccurs="1" maxOccurs="1">  <!-- http://svn.cansas.org/trac/ticket/21 -->
-			<sequence>
-				<element name="Idata" minOccurs="1" maxOccurs="unbounded" type="tns:IdataType" />
-			</sequence>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-		<attribute name="timestamp" type="dateTime" use="optional" />
-	</complexType>
-	
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-	
-	<complexType name="TdataType">
-			<sequence>
-				<element name="Lambda" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
-				<element name="T" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
-				<element name="Tdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
-				<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-	</complexType>
-	
-	<complexType name="SAStransmission_spectrumType">
-		<sequence minOccurs="1" maxOccurs="1">
-			<sequence minOccurs="1" maxOccurs="1">
-				<element name="Tdata" minOccurs="1" maxOccurs="unbounded" type="tns:TdataType" />
-			</sequence>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-		<attribute name="timestamp" type="dateTime" use="optional" />
-	</complexType>
-	
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASsampleType">
-		<sequence>
-			<element name="ID" minOccurs="1" maxOccurs="1" type="string" />
-			<element name="thickness" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="transmission" minOccurs="0" maxOccurs="1" type="float" />
-			<element name="temperature" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="position" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
-			<element name="details" minOccurs="0" maxOccurs="unbounded" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="termType">
-		<simpleContent>
-			<extension base="string">
-				<attribute name="name" type="string" use="optional" default="" />
-				<attribute name="unit" type="string" use="optional"/>
-			</extension>
-		</simpleContent>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASprocessType">
-		<annotation>
-			<appinfo>
-				<!-- JAXB: simplify class name -->
-				<jaxb:property name="SASprocess" />
-			</appinfo>
-		</annotation>
-		<sequence>
-			<element name="name" minOccurs="0" maxOccurs="1"
-				type="string" default="">
-				<annotation>
-					<appinfo>
-						<!-- Remap collision of Name when using JAXB -->
-						<jaxb:property name="SASprocessName" />
-					</appinfo>
-				</annotation>
-			</element>
-			<element name="date" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="description" minOccurs="0" maxOccurs="1" />
-			<element name="term" minOccurs="0" maxOccurs="unbounded" type="tns:termType" />
-			<element name="SASprocessnote" minOccurs="1" maxOccurs="unbounded" />
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="">
-			<annotation>
-				<appinfo>
-					<!-- Remap collision of Name when using JAXB -->
-					<jaxb:property name="SASprocessNameAttr" />
-				</appinfo>
-			</annotation>
-		</attribute>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASsourceType">
-		<sequence>
-			<element name="radiation" minOccurs="1" maxOccurs="1" type="string" />
-			<element name="beam_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="beam_shape" minOccurs="0" maxOccurs="1" type="string" />
-			<element name="wavelength" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_min" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_max" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="wavelength_spread" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<complexType name="SAScollimationType">
-		<sequence>
-			<element name="length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="aperture" minOccurs="0"
-				maxOccurs="unbounded">
-				<complexType>
-					<sequence>
-						<element name="size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-						<element name="distance" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-					</sequence>
-					<attribute name="name" type="string" use="optional" default="" />
-					<attribute name="type" type="string" use="optional" default="" />
-				</complexType>
-			</element>
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<complexType name="SASdetectorType">
-		<sequence>
-			<element name="name" minOccurs="1" maxOccurs="1" type="string" default=""/>
-			<element name="SDD" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-			<element name="offset" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
-			<element name="beam_center" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="pixel_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
-			<element name="slit_length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
-		</sequence>
-	</complexType>
-
-	<complexType name="SASinstrumentType">
-		<sequence>
-			<element name="name" type="string" minOccurs="1" maxOccurs="1" default="" />
-			<element name="SASsource" type="tns:SASsourceType" minOccurs="1" maxOccurs="1" />
-			<element name="SAScollimation" type="tns:SAScollimationType" minOccurs="1" maxOccurs="unbounded" />
-			<element name="SASdetector" type="tns:SASdetectorType" minOccurs="1" maxOccurs="unbounded" />
-		</sequence>
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASentryType">
-		<sequence>
-			<element name="Title" minOccurs="1" maxOccurs="1" type="string" />
-			<element name="Run" minOccurs="1" maxOccurs="unbounded">
-				<xsd:complexType>
-					<simpleContent>
-						<extension base="string">
-							<attribute name="name" type="string" use="optional" default="" />
-						</extension>
-					</simpleContent>
-				</xsd:complexType>
-			</element>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
-			<element name="SASdata" minOccurs="1" maxOccurs="unbounded" type="tns:SASdataType" />
-			<element name="SAStransmission_spectrum" minOccurs="0" maxOccurs="unbounded" type="tns:SAStransmission_spectrumType"/>
-			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other">
-				<!-- Is a second *any* element needed here? -->
-				<annotation>
-					<appinfo>
-						<!-- Remap collision of Any when using JAXB --> 
-						<jaxb:property name="SASentryAny2" />
-					</appinfo>
-				</annotation>
-			</xsd:any>
-			<element name="SASsample" type="tns:SASsampleType" />
-			<element name="SASinstrument" type="tns:SASinstrumentType" minOccurs="1" maxOccurs="1" />
-			<element name="SASprocess" type="tns:SASprocessType" minOccurs="0" maxOccurs="unbounded" />
-			<element name="SASnote" minOccurs="1" maxOccurs="unbounded" />
-				<!-- SASnote is a free-form element.
-					Do not define any content for it or it will become restrictive.
-				-->
-		</sequence>
-		<attribute name="name" type="string" use="optional" default="" />
-	</complexType>
-
-	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
-
-	<complexType name="SASrootType">
-		<sequence>
-			<element name="SASentry" minOccurs="1" maxOccurs="unbounded" type="tns:SASentryType" />
-		</sequence>
-		<attribute name="version" type="string" fixed="1.1" use="required" />
-	</complexType>
-
-	<element name="SASroot" type="tns:SASrootType" />
-
-</schema>
Index: c/sas/dataloader/readers/sesans_reader.py
===================================================================
--- src/sas/dataloader/readers/sesans_reader.py	(revision edfc8acb1e2f889cc2b3d4fb76086808eb43fb35)
+++ 	(revision )
@@ -1,273 +1,0 @@
-"""
-    SESANS reader (based on ASCII reader)
-    
-    Reader for .ses or .sesans file format
-    
-    Jurrian Bakker 
-"""
-import numpy
-import os
-from sas.dataloader.data_info import SESANSData1D
-
-# Check whether we have a converter available
-has_converter = True
-try:
-    from sas.data_util.nxsunit import Converter
-except:
-    has_converter = False
-_ZERO = 1e-16
-
-class Reader:
-    """
-    Class to load sesans files (6 columns).
-    """
-    ## File type
-    type_name = "SESANS"
-    
-    ## Wildcards
-    type = ["SESANS files (*.ses)|*.ses",
-            "SESANS files (*..sesans)|*.sesans"]
-    ## List of allowed extensions
-    ext = ['.ses', '.SES', '.sesans', '.SESANS']
-    
-    ## Flag to bypass extension check
-    allow_all = True
-    
-    def read(self, path):
-        
-#        print "reader triggered"
-        
-        """
-        Load data file
-        
-        :param path: file path
-        
-        :return: SESANSData1D object, or None
-        
-        :raise RuntimeError: when the file can't be opened
-        :raise ValueError: when the length of the data vectors are inconsistent
-        """
-        if os.path.isfile(path):
-            basename = os.path.basename(path)
-            _, extension = os.path.splitext(basename)
-            if self.allow_all or extension.lower() in self.ext:
-                try:
-                    # Read in binary mode since GRASP frequently has no-ascii
-                    # characters that brakes the open operation
-                    input_f = open(path,'rb')
-                except:
-                    raise  RuntimeError, "sesans_reader: cannot open %s" % path
-                buff = input_f.read()
-#                print buff
-                lines = buff.splitlines()
-#                print lines
-                #Jae could not find python universal line spliter:
-                #keep the below for now
-                # some ascii data has \r line separator,
-                # try it when the data is on only one long line
-#                if len(lines) < 2 :
-#                    lines = buff.split('\r')
-                 
-                x  = numpy.zeros(0)
-                y  = numpy.zeros(0)
-                dy = numpy.zeros(0)
-                lam  = numpy.zeros(0)
-                dlam = numpy.zeros(0)
-                dx = numpy.zeros(0)
-                
-               #temp. space to sort data
-                tx  = numpy.zeros(0)
-                ty  = numpy.zeros(0)
-                tdy = numpy.zeros(0)
-                tlam  = numpy.zeros(0)
-                tdlam = numpy.zeros(0)
-                tdx = numpy.zeros(0)
-#                print "all good"
-                output = SESANSData1D(x=x, y=y, lam=lam, dy=dy, dx=dx, dlam=dlam)
-#                print output                
-                self.filename = output.filename = basename
-                
-                
-                data_conv_z = None
-                data_conv_P = None
-#                print "passing"
-                if has_converter == True and output.x_unit != 'A':
-                    data_conv_z = Converter('nm')
-                    # Test it
-                    data_conv_z(1.0, output.x_unit)
-#                    print data_conv_z
-#                    print data_conv_z(1.0, output.x_unit)
-                if has_converter == True and output.y_unit != ' ':
-                    data_conv_P = Converter('a.u.')
-                    # Test it
-                    data_conv_P(1.0, output.y_unit)
-#                    print data_conv_P
-#                    print data_conv_P(1.0, output.y_unit)
-                # The first good line of data will define whether
-                # we have 2-column or 3-column ascii
-#                print output.x_unit
-#                print output.y_unit
-                
-#                print "past output"
-                
-#                has_error_dx = None
-#                has_error_dy = None
-                
-#                #Initialize counters for data lines and header lines.
-#                is_data = False  # Has more than 5 lines
-#                # More than "5" lines of data is considered as actual
-#                # data unless that is the only data
-#                mum_data_lines = 5
-#                # To count # of current data candidate lines
-#                i = -1
-#                # To count total # of previous data candidate lines
-#                i1 = -1
-#                # To count # of header lines
-#                j = -1
-#                # Helps to count # of header lines
-#                j1 = -1
-#                #minimum required number of columns of data; ( <= 4).
-#                lentoks = 2
-                paramnames=[]
-                paramvals=[]
-                zvals=[]
-                dzvals=[]
-                lamvals=[]
-                dlamvals=[]
-                Pvals=[]
-                dPvals=[]
-#                print x
-#                print zvals
-                for line in lines:
-                    # Initial try for CSV (split on ,)
-#                    print line
-                    line=line.strip()
-#                    print line
-                    toks = line.split('\t')
-#                    print toks
-                    if len(toks)==2:
-                        paramnames.append(toks[0])
-#                        print paramnames
-                        paramvals.append(toks[1])
-#                        print paramvals
-                    if len(toks)>5:
-                        zvals.append(toks[0])
-                        dzvals.append(toks[1])
-                        lamvals.append(toks[2])
-                        dlamvals.append(toks[3])
-                        Pvals.append(toks[4])
-                        dPvals.append(toks[5])
-                    else:
-                        continue
-#                print varheaders
-#                print paramnames
-#                print paramvals
-#                print zvals  
-#                print len(zvals)
-                
-                x=[]
-                y=[]
-                lam=[]
-                dx=[]
-                dy=[]
-                dlam=[]
-                varheader=[zvals[0],dzvals[0],lamvals[0],dlamvals[0],Pvals[0],dPvals[0]]
-#                print varheader
-                valrange=range(len(zvals)-1)
-#                print valrange
-                for i in valrange:
-                    x.append(float(zvals[i+1]))
-                    y.append(float(Pvals[i+1]))
-                    lam.append(float(lamvals[i+1]))
-                    dy.append(float(dPvals[i+1]))
-                    dx.append(float(dzvals[i+1]))
-                    dlam.append(float(dlamvals[i+1]))
-                    
-                x,y,lam,dy,dx,dlam = [
-                   numpy.asarray(v, 'double')
-                   for v in (x,y,lam,dy,dx,dlam)
-                ]
-
-#                print x
-#                print y
-#                print dx
-#                print dy
-#                print len(x)
-#                print len(y)
-#                print len(dx)
-#                print len(dy)
-                
-                
-                input_f.close()
-                # Sanity check
-#                if has_error_dy == True and not len(y) == len(dy):
-#                    msg = "sesans_reader: y and dy have different length"
-#                    raise RuntimeError, msg
-#                if has_error_dx == True and not len(x) == len(dx):
-#                    msg = "sesans_reader: y and dy have different length"
-#                    raise RuntimeError, msg
-#                # If the data length is zero, consider this as
-#                # though we were not able to read the file.
-#                if len(x) == 0:
-#                    raise RuntimeError, "sesans_reader: could not load file"
-#                print "alive"
-                #Let's re-order the data to make cal.
-                # curve look better some cases
-#                ind = numpy.lexsort((ty, tx))
-#                for i in ind:
-#                    x[i] = tx[ind[i]]
-#                    y[i] = ty[ind[i]]
-#                    if has_error_dy == True:
-#                        dy[i] = tdy[ind[i]]
-#                    if has_error_dx == True:
-#                        dx[i] = tdx[ind[i]]
-                # Zeros in dx, dy
-#                if has_error_dx:
-#                    dx[dx == 0] = _ZERO
-#                if has_error_dy:
-#                    dy[dy == 0] = _ZERO
-                #Data
-                output.x = x #[x != 0]
-#                print output.x
-                output.y = y #[x != 0]
-#                print output.y
-#                output.dy = dy[x != 0] if has_error_dy == True\
-#                    else numpy.zeros(len(output.y))
-#                output.dx = dx[x != 0] if has_error_dx == True\
-#                    else numpy.zeros(len(output.x))
-                output.dy = dy
-                output.dx = dx
-                output.lam = lam
-                output.dlam = dlam
-
-
-#                print "still alive"                
-#                if data_conv_z is not None:
-#                    output.xaxis("\\rm{z}", output.x_unit)
-#                else:
-#                    output.xaxis("\\rm{z}", 'nm')
-#                if data_conv_P is not None:
-#                    output.yaxis("\\rm{P/P0}", output.y_unit)
-#                else:
-#                    output.yaxis("\\rm{P/P0}", "a.u.")
-                output.xaxis("\\rm{z}", 'nm')   
-                output.yaxis("\\rm{P/P0}", " ")
-                # Store loading process information
-                output.meta_data['loader'] = self.type_name
-                output.sample.thickness = float(paramvals[6])
-                output.sample.name = paramvals[1]
-                output.sample.ID = paramvals[0]
-                output.sample.zacceptance=float(paramvals[7])
-                output.vars=varheader
-
-#                print "sesans_reader end"
-                
-                if len(output.x) < 1:
-                    raise RuntimeError, "%s is empty" % path
-#                print output
-#                print output.lam
-                return output
-            
-        else:
-            raise RuntimeError, "%s is not a file" % path
-        return None
Index: c/sas/dataloader/readers/tiff_reader.py
===================================================================
--- src/sas/dataloader/readers/tiff_reader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,106 +1,0 @@
-#####################################################################
-#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. 
-#See the license text in license.txt
-#copyright 2008, University of Tennessee
-######################################################################
-"""
-    Image reader. Untested.
-"""
-#TODO: load and check data and orientation of the image (needs rendering)
-import math
-import logging
-import os
-import numpy
-from sas.dataloader.data_info import Data2D
-from sas.dataloader.manipulations import reader2D_converter
-    
-class Reader:
-    """
-    Example data manipulation
-    """
-    ## File type
-    type_name = "TIF"
-    ## Wildcards
-    type = ["TIF files (*.tif)|*.tif",
-            "TIFF files (*.tiff)|*.tiff",
-            ]
-    ## Extension
-    ext = ['.tif', '.tiff']
-        
-    def read(self, filename=None):
-        """
-        Open and read the data in a file
-        
-        :param file: path of the file
-        """
-        try:
-            import Image
-            import TiffImagePlugin
-            Image._initialized=2
-        except:
-            msg = "tiff_reader: could not load file. Missing Image module."
-            raise RuntimeError, msg
-        
-        # Instantiate data object
-        output = Data2D()
-        output.filename = os.path.basename(filename)
-            
-        # Read in the image
-        try:
-            im = Image.open(filename)
-        except:
-            raise  RuntimeError, "cannot open %s"%(filename)
-        data = im.getdata()
-
-        # Initiazed the output data object
-        output.data = numpy.zeros([im.size[0], im.size[1]])
-        output.err_data = numpy.zeros([im.size[0], im.size[1]])
-        output.mask = numpy.ones([im.size[0], im.size[1]], dtype=bool)
-        
-        # Initialize
-        x_vals = []
-        y_vals = []
-
-        # x and y vectors
-        for i_x in range(im.size[0]):
-            x_vals.append(i_x)
-            
-        itot = 0
-        for i_y in range(im.size[1]):
-            y_vals.append(i_y)
-
-        for val in data:
-            try:
-                value = float(val)
-            except:
-                logging.error("tiff_reader: had to skip a non-float point")
-                continue
-            
-            # Get bin number
-            if math.fmod(itot, im.size[0]) == 0:
-                i_x = 0
-                i_y += 1
-            else:
-                i_x += 1
-                
-            output.data[im.size[1] - 1 - i_y][i_x] = value
-            
-            itot += 1
-                
-        output.xbins = im.size[0]
-        output.ybins = im.size[1]
-        output.x_bins = x_vals
-        output.y_bins = y_vals
-        output.qx_data = numpy.array(x_vals)
-        output.qy_data = numpy.array(y_vals)
-        output.xmin = 0
-        output.xmax = im.size[0] - 1
-        output.ymin = 0
-        output.ymax = im.size[0] - 1
-        
-        # Store loading process information
-        output.meta_data['loader'] = self.type_name
-        output = reader2D_converter(output)
-        return output
Index: c/sas/dataloader/readers/xml_reader.py
===================================================================
--- src/sas/dataloader/readers/xml_reader.py	(revision 5ce7f17e66dc0bd0cabff37dedf011bc6ec0a929)
+++ 	(revision )
@@ -1,286 +1,0 @@
-"""
-    Generic XML read and write utility
-
-    Usage: Either extend xml_reader or add as a class variable.
-"""
-############################################################################
-#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
-from lxml import etree
-from lxml.builder import E
-
-PARSER = etree.ETCompatXMLParser(remove_comments=True, remove_pis=False)
-
-class XMLreader():
-    """
-    Generic XML read and write class. Mostly helper functions.
-    Makes reading/writing XML a bit easier than calling lxml libraries directly.
-
-    :Dependencies:
-        This class requires lxml 2.3 or higher.
-    """
-
-    xml = None
-    xmldoc = None
-    xmlroot = None
-    schema = None
-    schemadoc = None
-    encoding = None
-    processing_instructions = None
-
-    def __init__(self, xml=None, schema=None):
-        self.xml = xml
-        self.schema = schema
-        self.processing_instructions = {}
-        if xml is not None:
-            self.set_xml_file(xml)
-        else:
-            self.xmldoc = None
-            self.xmlroot = None
-        if schema is not None:
-            self.set_schema(schema)
-        else:
-            self.schemadoc = None
-
-    def reader(self):
-        """
-        Read in an XML file into memory and return an lxml dictionary
-        """
-        if self.validate_xml():
-            self.xmldoc = etree.parse(self.xml, parser=PARSER)
-        else:
-            raise etree.XMLSchemaValidateError(self, self.find_invalid_xml())
-        return self.xmldoc
-
-    def set_xml_file(self, xml):
-        """
-        Set the XML file and parse
-        """
-        try:
-            self.xml = xml
-            self.xmldoc = etree.parse(self.xml, parser=PARSER)
-            self.xmlroot = self.xmldoc.getroot()
-        except etree.XMLSyntaxError as xml_error:
-            logging.info(xml_error)
-        except Exception:
-            self.xml = None
-            self.xmldoc = None
-            self.xmlroot = None
-
-    def set_schema(self, schema):
-        """
-        Set the schema file and parse
-        """
-        try:
-            self.schema = schema
-            self.schemadoc = etree.parse(self.schema, parser=PARSER)
-        except etree.XMLSyntaxError as xml_error:
-            logging.info(xml_error)
-        except Exception:
-            self.schema = None
-            self.schemadoc = None
-
-    def validate_xml(self):
-        """
-        Checks to see if the XML file meets the schema
-        """
-        valid = True
-        if self.schema is not None:
-            self.parse_schema_and_doc()
-            schema_check = etree.XMLSchema(self.schemadoc)
-            valid = schema_check.validate(self.xmldoc)
-        return valid
-
-    def find_invalid_xml(self):
-        """
-        Finds the first offending element that should not be present in XML file
-        """
-        first_error = ""
-        self.parse_schema_and_doc()
-        schema = etree.XMLSchema(self.schemadoc)
-        try:
-            first_error = schema.assertValid(self.xmldoc)
-        except etree.DocumentInvalid as err:
-            first_error = str(err)
-        return first_error
-
-    def parse_schema_and_doc(self):
-        """
-        Creates a dictionary of the parsed schema and xml files.
-        """
-        self.set_xml_file(self.xml)
-        self.set_schema(self.schema)
-
-    def to_string(self, elem, pretty_print=False, encoding=None):
-        """
-        Converts an etree element into a string
-        """
-        return etree.tostring(elem, pretty_print=pretty_print, \
-                              encoding=encoding)
-
-    def break_processing_instructions(self, string, dic):
-        """
-        Method to break a processing instruction string apart and add to a dict
-
-        :param string: A processing instruction as a string
-        :param dic: The dictionary to save the PIs to
-        """
-        pi_string = string.replace("<?", "").replace("?>", "")
-        split = pi_string.split(" ", 1)
-        pi_name = split[0]
-        attr = split[1]
-        new_pi_name = self._create_unique_key(dic, pi_name)
-        dic[new_pi_name] = attr
-        return dic
-
-    def set_processing_instructions(self):
-        """
-        Take out all processing instructions and create a dictionary from them
-        If there is a default encoding, the value is also saved
-        """
-        dic = {}
-        proc_instr = self.xmlroot.getprevious()
-        while proc_instr is not None:
-            pi_string = self.to_string(proc_instr)
-            if "?>\n<?" in pi_string:
-                pi_string = pi_string.split("?>\n<?")
-            if isinstance(pi_string, str):
-                dic = self.break_processing_instructions(pi_string, dic)
-            elif isinstance(pi_string, list):
-                for item in pi_string:
-                    dic = self.break_processing_instructions(item, dic)
-            proc_instr = proc_instr.getprevious()
-        if 'xml' in dic:
-            self.set_encoding(dic['xml'])
-            del dic['xml']
-        self.processing_instructions = dic
-
-    def set_encoding(self, attr_str):
-        """
-        Find the encoding in the xml declaration and save it as a string
-
-        :param attr_str: All attributes as a string
-            e.g. "foo1="bar1" foo2="bar2" foo3="bar3" ... foo_n="bar_n""
-        """
-        attr_str = attr_str.replace(" = ", "=")
-        attr_list = attr_str.split()
-        for item in attr_list:
-            name_value = item.split("\"=")
-            name = name_value[0].lower()
-            value = name_value[1]
-            if name == "encoding":
-                self.encoding = value
-                return
-        self.encoding = None
-
-    def _create_unique_key(self, dictionary, name, numb=0):
-        """
-        Create a unique key value for any dictionary to prevent overwriting
-        Recurses 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 create_tree(self, root):
-        """
-        Create an element tree for processing from an etree element
-
-        :param root: etree Element(s) 
-        """
-        return etree.ElementTree(root)
-
-    def create_element_from_string(self, xml_string):
-        """
-        Create an element from an XML string
-
-        :param xml_string: A string of xml
-        """
-        return etree.fromstring(xml_string)
-
-    def create_element(self, name, attrib=None, nsmap=None):
-        """
-        Create an XML element for writing to file
-
-        :param name: The name of the element to be created
-        """
-        if attrib == None:
-            attrib = {}
-        return etree.Element(name, attrib, nsmap)
-
-    def write_text(self, elem, text):
-        """
-        Write text to an etree Element
-
-        :param elem: etree.Element object
-        :param text: text to write to the element
-        """
-        elem.text = text
-        return elem
-
-    def write_attribute(self, elem, attr_name, attr_value):
-        """
-        Write attributes to an Element
-
-        :param elem: etree.Element object
-        :param attr_name: attribute name to write
-        :param attr_value: attribute value to set
-        """
-        attr = elem.attrib
-        attr[attr_name] = attr_value
-
-    def return_processing_instructions(self):
-        """
-        Get all processing instructions saved when loading the document
-
-        :param tree: etree.ElementTree object to write PIs to
-        """
-        pi_list = []
-        if self.processing_instructions is not None:
-            for key in self.processing_instructions:
-                value = self.processing_instructions.get(key)
-                pi_item = etree.ProcessingInstruction(key, value)
-                pi_list.append(pi_item)
-        return pi_list
-
-    def append(self, element, tree):
-        """
-        Append an etree Element to an ElementTree.
-
-        :param element: etree Element to append
-        :param tree: ElementTree object to append to
-        """
-        tree = tree.append(element)
-        return tree
-
-    def ebuilder(self, parent, elementname, text=None, attrib=None):
-        """
-        Use lxml E builder class with arbitrary inputs.
-
-        :param parnet: The parent element to append a child to
-        :param elementname: The name of the child in string form
-        :param text: The element text
-        :param attrib: A dictionary of attribute names to attribute values
-        """
-        text = str(text)
-        if attrib == None:
-            attrib = {}
-        elem = E(elementname, attrib, text)
-        parent = parent.append(elem)
-        return parent
Index: c/sas/fit/AbstractFitEngine.py
===================================================================
--- src/sas/fit/AbstractFitEngine.py	(revision 9f7fbd95345068210f07aa5bc9994d51a0415eec)
+++ 	(revision )
@@ -1,632 +1,0 @@
-
-import  copy
-#import logging
-import sys
-import math
-import numpy
-
-from sas.dataloader.data_info import Data1D
-from sas.dataloader.data_info import Data2D
-_SMALLVALUE = 1.0e-10
-
-class FitHandler(object):
-    """
-    Abstract interface for fit thread handler.
-
-    The methods in this class are called by the optimizer as the fit
-    progresses.
-
-    Note that it is up to the optimizer to call the fit handler correctly,
-    reporting all status changes and maintaining the 'done' flag.
-    """
-    done = False
-    """True when the fit job is complete"""
-    result = None
-    """The current best result of the fit"""
-
-    def improvement(self):
-        """
-        Called when a result is observed which is better than previous
-        results from the fit.
-
-        result is a FitResult object, with parameters, #calls and fitness.
-        """
-    def error(self, msg):
-        """
-        Model had an error; print traceback
-        """
-    def progress(self, current, expected):
-        """
-        Called each cycle of the fit, reporting the current and the
-        expected amount of work.   The meaning of these values is
-        optimizer dependent, but they can be converted into a percent
-        complete using (100*current)//expected.
-
-        Progress is updated each iteration of the fit, whatever that
-        means for the particular optimization algorithm.  It is called
-        after any calls to improvement for the iteration so that the
-        update handler can control I/O bandwidth by suppressing
-        intermediate improvements until the fit is complete.
-        """
-    def finalize(self):
-        """
-        Fit is complete; best results are reported
-        """
-    def abort(self):
-        """
-        Fit was aborted.
-        """
-
-    # TODO: not sure how these are used, but they are needed for running the fit
-    def update_fit(self, last=False): pass
-    def set_result(self, result=None): self.result = result
-
-class Model:
-    """
-    Fit wrapper for SAS models.
-    """
-    def __init__(self, sas_model, sas_data=None, **kw):
-        """
-        :param sas_model: the sas model to wrap for fitting
-
-        """
-        self.model = sas_model
-        self.name = sas_model.name
-        self.data = sas_data
-
-    def get_params(self, fitparams):
-        """
-        return a list of value of paramter to fit
-
-        :param fitparams: list of paramaters name to fit
-
-        """
-        return [self.model.getParam(k) for k in fitparams]
-
-    def set_params(self, paramlist, params):
-        """
-        Set value for parameters to fit
-
-        :param params: list of value for parameters to fit
-
-        """
-        for k,v in zip(paramlist, params):
-            self.model.setParam(k,v)
-
-    def set(self, **kw):
-        self.set_params(*zip(*kw.items()))
-
-    def eval(self, x):
-        """
-            Override eval method of model.
-
-            :param x: the x value used to compute a function
-        """
-        try:
-            return self.model.evalDistribution(x)
-        except:
-            raise
-
-    def eval_derivs(self, x, pars=[]):
-        """
-        Evaluate the model and derivatives wrt pars at x.
-
-        pars is a list of the names of the parameters for which derivatives
-        are desired.
-
-        This method needs to be specialized in the model to evaluate the
-        model function.  Alternatively, the model can implement is own
-        version of residuals which calculates the residuals directly
-        instead of calling eval.
-        """
-        raise NotImplementedError('no derivatives available')
-
-    def __call__(self, x):
-        return self.eval(x)
-
-class FitData1D(Data1D):
-    """
-        Wrapper class  for SAS data
-        FitData1D inherits from DataLoader.data_info.Data1D. Implements
-        a way to get residuals from data.
-    """
-    def __init__(self, x, y, dx=None, dy=None, smearer=None, data=None):
-        """
-            :param smearer: is an object of class QSmearer or SlitSmearer
-               that will smear the theory data (slit smearing or resolution
-               smearing) when set.
-            
-            The proper way to set the smearing object would be to
-            do the following: ::
-            
-                from sas.models.qsmearing import smear_selection
-                smearer = smear_selection(some_data)
-                fitdata1d = FitData1D( x= [1,3,..,],
-                                        y= [3,4,..,8],
-                                        dx=None,
-                                        dy=[1,2...], smearer= smearer)
-           
-            :Note: that some_data _HAS_ to be of
-                class DataLoader.data_info.Data1D
-                Setting it back to None will turn smearing off.
-                
-        """
-        Data1D.__init__(self, x=x, y=y, dx=dx, dy=dy)
-        self.num_points = len(x)
-        self.sas_data = data
-        self.smearer = smearer
-        self._first_unsmeared_bin = None
-        self._last_unsmeared_bin = None
-        # Check error bar; if no error bar found, set it constant(=1)
-        # TODO: Should provide an option for users to set it like percent,
-        # constant, or dy data
-        if dy is None or dy == [] or dy.all() == 0:
-            self.dy = numpy.ones(len(y))
-        else:
-            self.dy = numpy.asarray(dy).copy()
-
-        ## Min Q-value
-        #Skip the Q=0 point, especially when y(q=0)=None at x[0].
-        if min(self.x) == 0.0 and self.x[0] == 0 and\
-                     not numpy.isfinite(self.y[0]):
-            self.qmin = min(self.x[self.x != 0])
-        else:
-            self.qmin = min(self.x)
-        ## Max Q-value
-        self.qmax = max(self.x)
-        
-        # Range used for input to smearing
-        self._qmin_unsmeared = self.qmin
-        self._qmax_unsmeared = self.qmax
-        # Identify the bin range for the unsmeared and smeared spaces
-        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
-        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
-                            & (self.x <= self._qmax_unsmeared)
-  
-    def set_fit_range(self, qmin=None, qmax=None):
-        """ to set the fit range"""
-        # Skip Q=0 point, (especially for y(q=0)=None at x[0]).
-        # ToDo: Find better way to do it.
-        if qmin == 0.0 and not numpy.isfinite(self.y[qmin]):
-            self.qmin = min(self.x[self.x != 0])
-        elif qmin != None:
-            self.qmin = qmin
-        if qmax != None:
-            self.qmax = qmax
-        # Determine the range needed in unsmeared-Q to cover
-        # the smeared Q range
-        self._qmin_unsmeared = self.qmin
-        self._qmax_unsmeared = self.qmax
-        
-        self._first_unsmeared_bin = 0
-        self._last_unsmeared_bin = len(self.x) - 1
-        
-        if self.smearer != None:
-            self._first_unsmeared_bin, self._last_unsmeared_bin = \
-                    self.smearer.get_bin_range(self.qmin, self.qmax)
-            self._qmin_unsmeared = self.x[self._first_unsmeared_bin]
-            self._qmax_unsmeared = self.x[self._last_unsmeared_bin]
-            
-        # Identify the bin range for the unsmeared and smeared spaces
-        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
-        ## zero error can not participate for fitting
-        self.idx = self.idx & (self.dy != 0)
-        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
-                            & (self.x <= self._qmax_unsmeared)
-
-    def get_fit_range(self):
-        """
-            Return the range of data.x to fit
-        """
-        return self.qmin, self.qmax
-
-    def size(self):
-        """
-        Number of measurement points in data set after masking, etc.
-        """
-        return len(self.x)
-
-    def residuals(self, fn):
-        """
-            Compute residuals.
-            
-            If self.smearer has been set, use if to smear
-            the data before computing chi squared.
-            
-            :param fn: function that return model value
-            
-            :return: residuals
-        """
-        # Compute theory data f(x)
-        fx = numpy.zeros(len(self.x))
-        fx[self.idx_unsmeared] = fn(self.x[self.idx_unsmeared])
-       
-        ## Smear theory data
-        if self.smearer is not None:
-            fx = self.smearer(fx, self._first_unsmeared_bin,
-                              self._last_unsmeared_bin)
-        ## Sanity check
-        if numpy.size(self.dy) != numpy.size(fx):
-            msg = "FitData1D: invalid error array "
-            msg += "%d <> %d" % (numpy.shape(self.dy), numpy.size(fx))
-            raise RuntimeError, msg
-        return (self.y[self.idx] - fx[self.idx]) / self.dy[self.idx], fx[self.idx]
-            
-    def residuals_deriv(self, model, pars=[]):
-        """
-            :return: residuals derivatives .
-            
-            :note: in this case just return empty array 
-        """
-        return []
-
-
-class FitData2D(Data2D):
-    """
-        Wrapper class  for SAS data
-    """
-    def __init__(self, sas_data2d, data=None, err_data=None):
-        Data2D.__init__(self, data=data, err_data=err_data)
-        # Data can be initialized with a sas plottable or with vectors.
-        self.res_err_image = []
-        self.num_points = 0 # will be set by set_data
-        self.idx = []
-        self.qmin = None
-        self.qmax = None
-        self.smearer = None
-        self.radius = 0
-        self.res_err_data = []
-        self.sas_data = sas_data2d
-        self.set_data(sas_data2d)
-
-    def set_data(self, sas_data2d, qmin=None, qmax=None):
-        """
-            Determine the correct qx_data and qy_data within range to fit
-        """
-        self.data = sas_data2d.data
-        self.err_data = sas_data2d.err_data
-        self.qx_data = sas_data2d.qx_data
-        self.qy_data = sas_data2d.qy_data
-        self.mask = sas_data2d.mask
-
-        x_max = max(math.fabs(sas_data2d.xmin), math.fabs(sas_data2d.xmax))
-        y_max = max(math.fabs(sas_data2d.ymin), math.fabs(sas_data2d.ymax))
-        
-        ## fitting range
-        if qmin == None:
-            self.qmin = 1e-16
-        if qmax == 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 == []:
-            self.res_err_data = numpy.ones(len(self.data))
-        else:
-            self.res_err_data = copy.deepcopy(self.err_data)
-        #self.res_err_data[self.res_err_data==0]=1
-        
-        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
-        
-        # Note: mask = True: for MASK while mask = False for NOT to mask
-        self.idx = ((self.qmin <= self.radius) &\
-                            (self.radius <= self.qmax))
-        self.idx = (self.idx) & (self.mask)
-        self.idx = (self.idx) & (numpy.isfinite(self.data))
-        self.num_points = numpy.sum(self.idx)
-
-    def set_smearer(self, smearer):
-        """
-            Set smearer
-        """
-        if smearer == None:
-            return
-        self.smearer = smearer
-        self.smearer.set_index(self.idx)
-        self.smearer.get_data()
-
-    def set_fit_range(self, qmin=None, qmax=None):
-        """
-            To set the fit range
-        """
-        if qmin == 0.0:
-            self.qmin = 1e-16
-        elif qmin != None:
-            self.qmin = qmin
-        if qmax != None:
-            self.qmax = qmax
-        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
-        self.idx = ((self.qmin <= self.radius) &\
-                            (self.radius <= self.qmax))
-        self.idx = (self.idx) & (self.mask)
-        self.idx = (self.idx) & (numpy.isfinite(self.data))
-        self.idx = (self.idx) & (self.res_err_data != 0)
-
-    def get_fit_range(self):
-        """
-        return the range of data.x to fit
-        """
-        return self.qmin, self.qmax
-
-    def size(self):
-        """
-        Number of measurement points in data set after masking, etc.
-        """
-        return numpy.sum(self.idx)
-
-    def residuals(self, fn):
-        """
-        return the residuals
-        """
-        if self.smearer != None:
-            fn.set_index(self.idx)
-            # Get necessary data from self.data and set the data for smearing
-            fn.get_data()
-
-            gn = fn.get_value()
-        else:
-            gn = fn([self.qx_data[self.idx],
-                     self.qy_data[self.idx]])
-        # use only the data point within ROI range
-        res = (self.data[self.idx] - gn) / self.res_err_data[self.idx]
-
-        return res, gn
-        
-    def residuals_deriv(self, model, pars=[]):
-        """
-        :return: residuals derivatives .
-        
-        :note: in this case just return empty array
-        
-        """
-        return []
-    
-    
-class FitAbort(Exception):
-    """
-    Exception raise to stop the fit
-    """
-    #pass
-    #print"Creating fit abort Exception"
-
-
-
-class FitEngine:
-    def __init__(self):
-        """
-        Base class for the fit engine
-        """
-        #Dictionnary of fitArrange element (fit problems)
-        self.fit_arrange_dict = {}
-        self.fitter_id = None
-        
-    def set_model(self, model, id, pars=[], constraints=[], data=None):
-        """
-        set a model on a given  in the fit engine.
-        
-        :param model: sas.models type 
-        :param id: is the key of the fitArrange dictionary where model is saved as a value
-        :param pars: the list of parameters to fit 
-        :param constraints: list of 
-            tuple (name of parameter, value of parameters)
-            the value of parameter must be a string to constraint 2 different
-            parameters.
-            Example:  
-            we want to fit 2 model M1 and M2 both have parameters A and B.
-            constraints can be ``constraints = [(M1.A, M2.B+2), (M1.B= M2.A *5),...,]``
-            
-             
-        :note: pars must contains only name of existing model's parameters
-        
-        """
-        if not pars:
-            raise ValueError("no fitting parameters")
-
-        if model is None:
-            raise ValueError("no model to fit")
-
-        if not issubclass(model.__class__, Model):
-            model = Model(model, data)
-
-        sasmodel = model.model
-        available_parameters = sasmodel.getParamList()
-        for p in pars:
-            if p not in available_parameters:
-                raise ValueError("parameter %s not available in model %s; use one of [%s] instead"
-                                 %(p, sasmodel.name, ", ".join(available_parameters)))
-
-        if id not in self.fit_arrange_dict:
-            self.fit_arrange_dict[id] = FitArrange()
-
-        self.fit_arrange_dict[id].set_model(model)
-        self.fit_arrange_dict[id].pars = pars
-        self.fit_arrange_dict[id].vals = [sasmodel.getParam(name) for name in pars]
-        self.fit_arrange_dict[id].constraints = constraints
-
-    def set_data(self, data, id, smearer=None, qmin=None, qmax=None):
-        """
-        Receives plottable, creates a list of data to fit,set data
-        in a FitArrange object and adds that object in a dictionary
-        with key id.
-        
-        :param data: data added
-        :param id: unique key corresponding to a fitArrange object with data
-        """
-        if data.__class__.__name__ == 'Data2D':
-            fitdata = FitData2D(sas_data2d=data, data=data.data,
-                                 err_data=data.err_data)
-        else:
-            fitdata = FitData1D(x=data.x, y=data.y,
-                                 dx=data.dx, dy=data.dy, smearer=smearer)
-        fitdata.sas_data = data
-       
-        fitdata.set_fit_range(qmin=qmin, qmax=qmax)
-        #A fitArrange is already created but contains model only at id
-        if id in self.fit_arrange_dict:
-            self.fit_arrange_dict[id].add_data(fitdata)
-        else:
-        #no fitArrange object has been create with this id
-            fitproblem = FitArrange()
-            fitproblem.add_data(fitdata)
-            self.fit_arrange_dict[id] = fitproblem
-   
-    def get_model(self, id):
-        """
-        :param id: id is key in the dictionary containing the model to return
-        
-        :return:  a model at this id or None if no FitArrange element was
-            created with this id
-        """
-        if id in self.fit_arrange_dict:
-            return self.fit_arrange_dict[id].get_model()
-        else:
-            return None
-    
-    def remove_fit_problem(self, id):
-        """remove   fitarrange in id"""
-        if id in self.fit_arrange_dict:
-            del self.fit_arrange_dict[id]
-            
-    def select_problem_for_fit(self, id, value):
-        """
-        select a couple of model and data at the id position in dictionary
-        and set in self.selected value to value
-        
-        :param value: the value to allow fitting.
-                can only have the value one or zero
-        """
-        if id in self.fit_arrange_dict:
-            self.fit_arrange_dict[id].set_to_fit(value)
-             
-    def get_problem_to_fit(self, id):
-        """
-        return the self.selected value of the fit problem of id
-        
-        :param id: the id of the problem
-        """
-        if id in self.fit_arrange_dict:
-            self.fit_arrange_dict[id].get_to_fit()
-    
-    
-class FitArrange:
-    def __init__(self):
-        """
-        Class FitArrange contains a set of data for a given model
-        to perform the Fit.FitArrange must contain exactly one model
-        and at least one data for the fit to be performed.
-        
-        model: the model selected by the user
-        Ldata: a list of data what the user wants to fit
-            
-        """
-        self.model = None
-        self.data_list = []
-        self.pars = []
-        self.vals = []
-        self.selected = 0
-
-    def set_model(self, model):
-        """
-        set_model save a copy of the model
-        
-        :param model: the model being set
-        """
-        self.model = model
-        
-    def add_data(self, data):
-        """
-        add_data fill a self.data_list with data to fit
-        
-        :param data: Data to add in the list
-        """
-        if not data in self.data_list:
-            self.data_list.append(data)
-            
-    def get_model(self):
-        """
-        :return: saved model
-        """
-        return self.model
-     
-    def get_data(self):
-        """
-        :return: list of data data_list
-        """
-        return self.data_list[0]
-      
-    def remove_data(self, data):
-        """
-        Remove one element from the list
-        
-        :param data: Data to remove from data_list
-        """
-        if data in self.data_list:
-            self.data_list.remove(data)
-            
-    def set_to_fit(self, value=0):
-        """
-        set self.selected to 0 or 1  for other values raise an exception
-        
-        :param value: integer between 0 or 1
-        """
-        self.selected = value
-        
-    def get_to_fit(self):
-        """
-        return self.selected value
-        """
-        return self.selected
-
-class FResult(object):
-    """
-    Storing fit result
-    """
-    def __init__(self, model=None, param_list=None, data=None):
-        self.calls = None
-        self.fitness = None
-        self.chisqr = None
-        self.pvec = []
-        self.cov = []
-        self.info = None
-        self.mesg = None
-        self.success = None
-        self.stderr = None
-        self.residuals = []
-        self.index = []
-        self.model = model
-        self.data = data
-        self.theory = []
-        self.param_list = param_list
-        self.iterations = 0
-        self.inputs = []
-        self.fitter_id = None
-        if self.model is not None and self.data is not None:
-            self.inputs = [(self.model, self.data)]
-     
-    def set_model(self, model):
-        """
-        """
-        self.model = model
-        
-    def set_fitness(self, fitness):
-        """
-        """
-        self.fitness = fitness
-        
-    def __str__(self):
-        """
-        """
-        if self.pvec == None and self.model is None and self.param_list is None:
-            return "No results"
-
-        sasmodel = self.model.model
-        pars = enumerate(sasmodel.getParamList())
-        msg1 = "[Iteration #: %s ]" % self.iterations
-        msg3 = "=== goodness of fit: %s ===" % (str(self.fitness))
-        msg2 = ["P%-3d  %s......|.....%s" % (i, v, sasmodel.getParam(v))
-                for i,v in pars if v in self.param_list]
-        msg = [msg1, msg3] + msg2
-        return "\n".join(msg)
-    
-    def print_summary(self):
-        """
-        """
-        print str(self)
Index: c/sas/fit/BumpsFitting.py
===================================================================
--- src/sas/fit/BumpsFitting.py	(revision 7945367d6e45b1f5abeedcae09a5284e3063d095)
+++ 	(revision )
@@ -1,365 +1,0 @@
-"""
-BumpsFitting module runs the bumps optimizer.
-"""
-import os
-from datetime import timedelta, datetime
-
-import numpy
-
-from bumps import fitters
-try:
-    from bumps.options import FIT_CONFIG
-    # Default bumps to use the Levenberg-Marquardt optimizer
-    FIT_CONFIG.selected_id = fitters.LevenbergMarquardtFit.id
-    def get_fitter():
-        return FIT_CONFIG.selected_fitter, FIT_CONFIG.selected_values
-except:
-    # CRUFT: Bumps changed its handling of fit options around 0.7.5.6
-    # Default bumps to use the Levenberg-Marquardt optimizer
-    fitters.FIT_DEFAULT = 'lm'
-    def get_fitter():
-        fitopts = fitters.FIT_OPTIONS[fitters.FIT_DEFAULT]
-        return fitopts.fitclass, fitopts.options.copy()
-
-
-from bumps.mapper import SerialMapper, MPMapper
-from bumps import parameter
-from bumps.fitproblem import FitProblem
-
-
-from sas.fit.AbstractFitEngine import FitEngine
-from sas.fit.AbstractFitEngine import FResult
-from sas.fit.expression import compile_constraints
-
-class Progress(object):
-    def __init__(self, history, max_step, pars, dof):
-        remaining_time = int(history.time[0]*(float(max_step)/history.step[0]-1))
-        # Depending on the time remaining, either display the expected
-        # time of completion, or the amount of time remaining.  Use precision
-        # appropriate for the duration.
-        if remaining_time >= 1800:
-            completion_time = datetime.now() + timedelta(seconds=remaining_time)
-            if remaining_time >= 36000:
-                time = completion_time.strftime('%Y-%m-%d %H:%M')
-            else:
-                time = completion_time.strftime('%H:%M')
-        else:
-            if remaining_time >= 3600:
-                time = '%dh %dm'%(remaining_time//3600, (remaining_time%3600)//60)
-            elif remaining_time >= 60:
-                time = '%dm %ds'%(remaining_time//60, remaining_time%60)
-            else:
-                time = '%ds'%remaining_time
-        chisq = "%.3g"%(2*history.value[0]/dof)
-        step = "%d of %d"%(history.step[0], max_step)
-        header = "=== Steps: %s  chisq: %s  ETA: %s\n"%(step, chisq, time)
-        parameters = ["%15s: %-10.3g%s"%(k,v,("\n" if i%3==2 else " | "))
-                      for i,(k,v) in enumerate(zip(pars,history.point[0]))]
-        self.msg = "".join([header]+parameters)
-
-    def __str__(self):
-        return self.msg
-
-
-class BumpsMonitor(object):
-    def __init__(self, handler, max_step, pars, dof):
-        self.handler = handler
-        self.max_step = max_step
-        self.pars = pars
-        self.dof = dof
-
-    def config_history(self, history):
-        history.requires(time=1, value=2, point=1, step=1)
-
-    def __call__(self, history):
-        if self.handler is None: return
-        self.handler.set_result(Progress(history, self.max_step, self.pars, self.dof))
-        self.handler.progress(history.step[0], self.max_step)
-        if len(history.step)>1 and history.step[1] > history.step[0]:
-            self.handler.improvement()
-        self.handler.update_fit()
-
-class ConvergenceMonitor(object):
-    """
-    ConvergenceMonitor contains population summary statistics to show progress
-    of the fit.  This is a list [ (best, 0%, 25%, 50%, 75%, 100%) ] or
-    just a list [ (best, ) ] if population size is 1.
-    """
-    def __init__(self):
-        self.convergence = []
-
-    def config_history(self, history):
-        history.requires(value=1, population_values=1)
-
-    def __call__(self, history):
-        best = history.value[0]
-        try:
-            p = history.population_values[0]
-            n,p = len(p), numpy.sort(p)
-            QI,Qmid, = int(0.2*n),int(0.5*n)
-            self.convergence.append((best, p[0],p[QI],p[Qmid],p[-1-QI],p[-1]))
-        except:
-            self.convergence.append((best, best,best,best,best,best))
-
-
-# Note: currently using bumps parameters for each parameter object so that
-# a SasFitness can be used directly in bumps with the usual semantics.
-# The disadvantage of this technique is that we need to copy every parameter
-# back into the model each time the function is evaluated.  We could instead
-# define reference parameters for each sas parameter, but then we would not
-# be able to express constraints using python expressions in the usual way
-# from bumps, and would instead need to use string expressions.
-class SasFitness(object):
-    """
-    Wrap SAS model as a bumps fitness object
-    """
-    def __init__(self, model, data, fitted=[], constraints={},
-                 initial_values=None, **kw):
-        self.name = model.name
-        self.model = model.model
-        self.data = data
-        if self.data.smearer is not None:
-            self.data.smearer.model = self.model
-        self._define_pars()
-        self._init_pars(kw)
-        if initial_values is not None:
-            self._reset_pars(fitted, initial_values)
-        self.constraints = dict(constraints)
-        self.set_fitted(fitted)
-        self.update()
-
-    def _reset_pars(self, names, values):
-        for k,v in zip(names, values):
-            self._pars[k].value = v
-
-    def _define_pars(self):
-        self._pars = {}
-        for k in self.model.getParamList():
-            name = ".".join((self.name,k))
-            value = self.model.getParam(k)
-            bounds = self.model.details.get(k,["",None,None])[1:3]
-            self._pars[k] = parameter.Parameter(value=value, bounds=bounds,
-                                                fixed=True, name=name)
-        #print parameter.summarize(self._pars.values())
-
-    def _init_pars(self, kw):
-        for k,v in kw.items():
-            # dispersion parameters initialized with _field instead of .field
-            if k.endswith('_width'): k = k[:-6]+'.width'
-            elif k.endswith('_npts'): k = k[:-5]+'.npts'
-            elif k.endswith('_nsigmas'): k = k[:-7]+'.nsigmas'
-            elif k.endswith('_type'): k = k[:-5]+'.type'
-            if k not in self._pars:
-                formatted_pars = ", ".join(sorted(self._pars.keys()))
-                raise KeyError("invalid parameter %r for %s--use one of: %s"
-                               %(k, self.model, formatted_pars))
-            if '.' in k and not k.endswith('.width'):
-                self.model.setParam(k, v)
-            elif isinstance(v, parameter.BaseParameter):
-                self._pars[k] = v
-            elif isinstance(v, (tuple,list)):
-                low, high = v
-                self._pars[k].value = (low+high)/2
-                self._pars[k].range(low,high)
-            else:
-                self._pars[k].value = v
-
-    def set_fitted(self, param_list):
-        """
-        Flag a set of parameters as fitted parameters.
-        """
-        for k,p in self._pars.items():
-            p.fixed = (k not in param_list or k in self.constraints)
-        self.fitted_par_names = [k for k in param_list if k not in self.constraints]
-        self.computed_par_names = [k for k in param_list if k in self.constraints]
-        self.fitted_pars = [self._pars[k] for k in self.fitted_par_names]
-        self.computed_pars = [self._pars[k] for k in self.computed_par_names]
-
-    # ===== Fitness interface ====
-    def parameters(self):
-        return self._pars
-
-    def update(self):
-        for k,v in self._pars.items():
-            #print "updating",k,v,v.value
-            self.model.setParam(k,v.value)
-        self._dirty = True
-
-    def _recalculate(self):
-        if self._dirty:
-            self._residuals, self._theory \
-                = self.data.residuals(self.model.evalDistribution)
-            self._dirty = False
-
-    def numpoints(self):
-        return numpy.sum(self.data.idx) # number of fitted points
-
-    def nllf(self):
-        return 0.5*numpy.sum(self.residuals()**2)
-
-    def theory(self):
-        self._recalculate()
-        return self._theory
-
-    def residuals(self):
-        self._recalculate()
-        return self._residuals
-
-    # Not implementing the data methods for now:
-    #
-    #     resynth_data/restore_data/save/plot
-
-class ParameterExpressions(object):
-    def __init__(self, models):
-        self.models = models
-        self._setup()
-
-    def _setup(self):
-        exprs = {}
-        for M in self.models:
-            exprs.update((".".join((M.name, k)), v) for k, v in M.constraints.items())
-        if exprs:
-            symtab = dict((".".join((M.name, k)), p)
-                          for M in self.models
-                          for k,p in M.parameters().items())
-            self.update = compile_constraints(symtab, exprs)
-        else:
-            self.update = lambda: 0
-
-    def __call__(self):
-        self.update()
-
-    def __getstate__(self):
-        return self.models
-
-    def __setstate__(self, state):
-        self.models = state
-        self._setup()
-
-class BumpsFit(FitEngine):
-    """
-    Fit a model using bumps.
-    """
-    def __init__(self):
-        """
-        Creates a dictionary (self.fit_arrange_dict={})of FitArrange elements
-        with Uid as keys
-        """
-        FitEngine.__init__(self)
-        self.curr_thread = None
-
-    def fit(self, msg_q=None,
-            q=None, handler=None, curr_thread=None,
-            ftol=1.49012e-8, reset_flag=False):
-        # Build collection of bumps fitness calculators
-        models = [SasFitness(model=M.get_model(),
-                             data=M.get_data(),
-                             constraints=M.constraints,
-                             fitted=M.pars,
-                             initial_values=M.vals if reset_flag else None)
-                  for M in self.fit_arrange_dict.values()
-                  if M.get_to_fit()]
-        if len(models) == 0:
-            raise RuntimeError("Nothing to fit")
-        problem = FitProblem(models)
-
-        # TODO: need better handling of parameter expressions and bounds constraints
-        # so that they are applied during polydispersity calculations.  This
-        # will remove the immediate need for the setp_hook in bumps, though
-        # bumps may still need something similar, such as a sane class structure
-        # which allows a subclass to override setp.
-        problem.setp_hook = ParameterExpressions(models)
-
-        # Run the fit
-        result = run_bumps(problem, handler, curr_thread)
-        if handler is not None:
-            handler.update_fit(last=True)
-
-        # TODO: shouldn't reference internal parameters of fit problem
-        varying = problem._parameters
-        # collect the results
-        all_results = []
-        for M in problem.models:
-            fitness = M.fitness
-            fitted_index = [varying.index(p) for p in fitness.fitted_pars]
-            param_list = fitness.fitted_par_names + fitness.computed_par_names
-            R = FResult(model=fitness.model, data=fitness.data,
-                        param_list=param_list)
-            R.theory = fitness.theory()
-            R.residuals = fitness.residuals()
-            R.index = fitness.data.idx
-            R.fitter_id = self.fitter_id
-            # TODO: should scale stderr by sqrt(chisq/DOF) if dy is unknown
-            R.success = result['success']
-            if R.success:
-                R.stderr = numpy.hstack((result['stderr'][fitted_index],
-                                         numpy.NaN*numpy.ones(len(fitness.computed_pars))))
-                R.pvec = numpy.hstack((result['value'][fitted_index],
-                                      [p.value for p in fitness.computed_pars]))
-                R.fitness = numpy.sum(R.residuals**2)/(fitness.numpoints() - len(fitted_index))
-            else:
-                R.stderr = numpy.NaN*numpy.ones(len(param_list))
-                R.pvec = numpy.asarray( [p.value for p in fitness.fitted_pars+fitness.computed_pars])
-                R.fitness = numpy.NaN
-            R.convergence = result['convergence']
-            if result['uncertainty'] is not None:
-                R.uncertainty_state = result['uncertainty']
-            all_results.append(R)
-
-        if q is not None:
-            q.put(all_results)
-            return q
-        else:
-            return all_results
-
-def run_bumps(problem, handler, curr_thread):
-    def abort_test():
-        if curr_thread is None: return False
-        try: curr_thread.isquit()
-        except KeyboardInterrupt:
-            if handler is not None:
-                handler.stop("Fitting: Terminated!!!")
-            return True
-        return False
-
-    fitclass, options = get_fitter()
-    steps = options.get('steps', 0)
-    if steps == 0:
-        pop = options.get('pop',0)*len(problem._parameters)
-        samples = options.get('samples', 0)
-        steps = (samples+pop-1)/pop if pop != 0 else samples
-    max_step = steps + options.get('burn', 0)
-    pars = [p.name for p in problem._parameters]
-    #x0 = numpy.asarray([p.value for p in problem._parameters])
-    options['monitors'] = [
-        BumpsMonitor(handler, max_step, pars, problem.dof),
-        ConvergenceMonitor(),
-        ]
-    fitdriver = fitters.FitDriver(fitclass, problem=problem,
-                                  abort_test=abort_test, **options)
-    omp_threads = int(os.environ.get('OMP_NUM_THREADS','0'))
-    mapper = MPMapper if omp_threads == 1 else SerialMapper
-    fitdriver.mapper = mapper.start_mapper(problem, None)
-    #import time; T0 = time.time()
-    try:
-        best, fbest = fitdriver.fit()
-    except:
-        import traceback; traceback.print_exc()
-        raise
-    finally:
-        mapper.stop_mapper(fitdriver.mapper)
-
-
-    convergence_list = options['monitors'][-1].convergence
-    convergence = (2*numpy.asarray(convergence_list)/problem.dof
-                   if convergence_list else numpy.empty((0,1),'d'))
-
-    success = best is not None
-    return {
-        'value': best if success else None,
-        'stderr': fitdriver.stderr() if success else None,
-        'success': success,
-        'convergence': convergence,
-        'uncertainty': getattr(fitdriver.fitter, 'state', None),
-        }
-
Index: c/sas/fit/Loader.py
===================================================================
--- src/sas/fit/Loader.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,86 +1,0 @@
-# class Loader  to load any king of file
-#import wx
-#import string
-import numpy
-
-class Load:
-    """
-    This class is loading values from given file or value giving by the user
-    """
-    def __init__(self, x=None, y=None, dx=None, dy=None):
-        raise NotImplementedError("a code search shows that this code is not active, and you are not seeing this message")
-        # variable to store loaded values
-        self.x = x
-        self.y = y
-        self.dx = dx
-        self.dy = dy
-        self.filename = None
-        
-    def set_filename(self, path=None):
-        """
-        Store path into a variable.If the user doesn't give 
-        a path as a parameter a pop-up
-        window appears to select the file.
-        
-        :param path: the path given by the user
-        
-        """
-        self.filename = path
-       
-    def get_filename(self):
-        """ return the file's path"""
-        return self.filename
-    
-    def set_values(self):
-        """ Store the values loaded from file in local variables"""
-        if not self.filename == None:
-            input_f =  open(self.filename, 'r')
-            buff = input_f.read()
-            lines = buff.split('\n')
-            self.x = []
-            self.y = []
-            self.dx = [] 
-            self.dy = []
-            for line in lines:
-                try:
-                    toks = line.split()
-                    x = float(toks[0])
-                    y = float(toks[1])
-                    dy = float(toks[2])
-                    
-                    self.x.append(x)
-                    self.y.append(y)
-                    self.dy.append(dy)
-                    self.dx = numpy.zeros(len(self.x))
-                except:
-                    print "READ ERROR", line
-            # Sanity check
-            if not len(self.x) == len(self.dx):
-                raise ValueError, "x and dx have different length"
-            if not len(self.y) == len(self.dy):
-                raise ValueError, "y and dy have different length"
-            
-            
-    def get_values(self):
-        """ Return x, y, dx, dy"""
-        return self.x, self.y, self.dx, self.dy
-    
-    def load_data(self, data):
-        """ Return plottable"""
-        #load data
-        data.x = self.x
-        data.y = self.y
-        data.dx = self.dx
-        data.dy = self.dy
-        #Load its View class
-        #plottable.reset_view()
-       
-    
-if __name__ == "__main__": 
-    load = Load()
-    load.set_filename("testdata_line.txt")
-    print load.get_filename() 
-    load.set_values()
-    print load.get_values()
-    
-            
Index: c/sas/fit/__init__.py
===================================================================
--- src/sas/fit/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,1 +1,0 @@
-from .AbstractFitEngine import FitHandler
Index: c/sas/fit/expression.py
===================================================================
--- src/sas/fit/expression.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,400 +1,0 @@
-# This program is public domain
-"""
-Parameter expression evaluator.
-
-For systems in which constraints are expressed as string expressions rather
-than python code, :func:`compile_constraints` can construct an expression
-evaluator that substitutes the computed values of the expressions into the
-parameters.
-
-The compiler requires a symbol table, an expression set and a context.
-The symbol table maps strings containing fully qualified names such as
-'M1.c[3].full_width' to parameter objects with a 'value' property that
-can be queried and set.  The expression set maps symbol names from the
-symbol table to string expressions.  The context provides additional symbols
-for the expressions in addition to the usual mathematical functions and
-constants.
-
-The expressions are compiled and interpreted by python, with only minimal
-effort to make sure that they don't contain bad code.  The resulting
-constraints function returns 0 so it can be used directly in a fit problem
-definition.
-
-Extracting the symbol table from the model depends on the structure of the
-model.  If fitness.parameters() is set correctly, then this should simply
-be a matter of walking the parameter data, remembering the path to each
-parameter in the symbol table.  For compactness, dictionary elements should
-be referenced by .name rather than ["name"].  Model name can be used as the
-top level.
-
-Getting the parameter expressions applied correctly is challenging.
-The following monkey patch works by overriding model_update in FitProblem
-so that after setp(p) is called and, the constraints expression can be
-applied before telling the underlying fitness function that the model
-is out of date::
-
-        # Override model update so that parameter constraints are applied
-        problem._model_update = problem.model_update
-        def model_update():
-            constraints()
-            problem._model_update()
-        problem.model_update = model_update
-
-Ideally, this interface will change
-"""
-import math
-import re
-
-# simple pattern which matches symbols.  Note that it will also match
-# invalid substrings such as a3...9, but given syntactically correct
-# input it will only match symbols.
-_symbol_pattern = re.compile('([a-zA-Z_][a-zA-Z_0-9.]*)')
-
-def _symbols(expr,symtab):
-    """
-    Given an expression string and a symbol table, return the set of symbols
-    used in the expression.  Symbols are only returned once even if they
-    occur multiple times.  The return value is a set with the elements in
-    no particular order.
-    
-    This is the first step in computing a dependency graph.
-    """
-    matches = [m.group(0) for m in _symbol_pattern.finditer(expr)]
-    return set([symtab[m] for m in matches if m in symtab])
-
-def _substitute(expr,mapping):
-    """
-    Replace all occurrences of symbol s with mapping[s] for s in mapping.
-    """
-    # Find the symbols and the mapping
-    matches = [(m.start(),m.end(),mapping[m.group(1)])
-               for m in _symbol_pattern.finditer(expr)
-               if m.group(1) in mapping]
-
-    # Split the expression in to pieces, with new symbols replacing old
-    pieces = []
-    offset = 0
-    for start,end,text in matches:
-        pieces += [expr[offset:start],text]
-        offset = end
-    pieces.append(expr[offset:])
-    
-    # Join the pieces and return them
-    return "".join(pieces)
-
-def _find_dependencies(symtab, exprs):
-    """
-    Returns a list of pair-wise dependencies from the parameter expressions.
-    
-    For example, if p3 = p1+p2, then find_dependencies([p1,p2,p3]) will
-    return [(p3,p1),(p3,p2)].  For base expressions without dependencies,
-    such as p4 = 2*pi, this should return [(p4, None)]
-    """
-    deps = [(target,source)
-            for target,expr in exprs.items()
-            for source in _symbols_or_none(expr,symtab)]
-    return deps
-
-# Hack to deal with expressions without dependencies --- return a fake
-# dependency of None.
-# The better solution is fix order_dependencies so that it takes a
-# dictionary of {symbol: dependency_list}, for which no dependencies
-# is simply []; fix in parameter_mapping as well
-def _symbols_or_none(expr,symtab):
-    syms = _symbols(expr,symtab)
-    return syms if len(syms) else [None]
-
-def _parameter_mapping(pairs):
-    """
-    Find the parameter substitution we need so that expressions can
-    be evaluated without having to traverse a chain of 
-    model.layer.parameter.value
-    """
-    left,right = zip(*pairs)
-    pars = list(sorted(p for p in set(left+right) if p is not None))
-    definition = dict( ('P%d'%i,p)  for i,p in enumerate(pars) )
-    # p is None when there is an expression with no dependencies
-    substitution = dict( (p,'P%d.value'%i)
-                    for i,p in enumerate(sorted(pars))
-                    if p is not None)
-    return definition, substitution
-
-def no_constraints(): 
-    """
-    This parameter set has no constraints between the parameters.
-    """
-    pass
-
-def compile_constraints(symtab, exprs, context={}):
-    """
-    Build and return a function to evaluate all parameter expressions in
-    the proper order.
-
-    Input:
-
-        *symtab* is the symbol table for the model: { 'name': parameter }
-
-        *exprs* is the set of computed symbols: { 'name': 'expression' }
-
-        *context* is any additional context needed to evaluate the expression
-
-    Return:
-
-        updater function which sets parameter.value for each expression
-
-    Raises:
-
-       AssertionError - model, parameter or function is missing
-
-       SyntaxError - improper expression syntax
-
-       ValueError - expressions have circular dependencies
-
-    This function is not terribly sophisticated, and it would be easy to
-    trick.  However it handles the common cases cleanly and generates
-    reasonable messages for the common errors.
-
-    This code has not been fully audited for security.  While we have
-    removed the builtins and the ability to import modules, there may
-    be other vectors for users to perform more than simple function
-    evaluations.  Unauthenticated users should not be running this code.
-
-    Parameter names are assumed to contain only _.a-zA-Z0-9#[]
-    
-    Both names are provided for inverse functions, e.g., acos and arccos.
-
-    Should try running the function to identify syntax errors before
-    running it in a fit.
-    
-    Use help(fn) to see the code generated for the returned function fn.
-    dis.dis(fn) will show the corresponding python vm instructions.
-    """
-
-    # Sort the parameters in the order they need to be evaluated
-    deps = _find_dependencies(symtab, exprs)
-    if deps == []: return no_constraints
-    order = order_dependencies(deps)
-
-
-    # Rather than using the full path to the parameters in the parameter
-    # expressions, instead use Pn, and substitute Pn.value for each occurrence
-    # of the parameter in the expression.
-    names = list(sorted(symtab.keys()))
-    parameters = dict(('P%d'%i, symtab[k]) for i,k in enumerate(names))
-    mapping = dict((k, 'P%d.value'%i) for i,k in enumerate(names))
-
-
-    # Initialize dictionary with available functions
-    globals = {}
-    globals.update(math.__dict__)
-    globals.update(dict(arcsin=math.asin,arccos=math.acos,
-                        arctan=math.atan,arctan2=math.atan2))
-    globals.update(context)
-    globals.update(parameters)
-    globals['id'] = id
-    locals = {}
-
-    # Define the constraints function
-    assignments = ["=".join((p,exprs[p])) for p in order]
-    code = [_substitute(s, mapping) for s in assignments]
-    functiondef = """
-def eval_expressions():
-    '''
-    %s
-    '''
-    %s
-    return 0
-"""%("\n    ".join(assignments),"\n    ".join(code))
-
-    #print("Function: "+functiondef)
-    exec functiondef in globals,locals
-    retfn = locals['eval_expressions']
-
-    # Remove garbage added to globals by exec
-    globals.pop('__doc__',None)
-    globals.pop('__name__',None)
-    globals.pop('__file__',None)
-    globals.pop('__builtins__')
-    #print globals.keys()
-
-    return retfn
-
-def order_dependencies(pairs):
-    """
-    Order elements from pairs so that b comes before a in the
-    ordered list for all pairs (a,b).
-    """
-    #print "order_dependencies",pairs
-    emptyset = set()
-    order = []
-
-    # Break pairs into left set and right set
-    left,right = [set(s) for s in zip(*pairs)] if pairs != [] else ([],[])
-    while pairs != []:
-        #print "within",pairs
-        # Find which items only occur on the right
-        independent = right - left
-        if independent == emptyset:
-            cycleset = ", ".join(str(s) for s in left)
-            raise ValueError,"Cyclic dependencies amongst %s"%cycleset
-
-        # The possibly resolvable items are those that depend on the independents
-        dependent = set([a for a,b in pairs if b in independent])
-        pairs = [(a,b) for a,b in pairs if b not in independent]
-        if pairs == []:
-            resolved = dependent
-        else:
-            left,right = [set(s) for s in zip(*pairs)]
-            resolved = dependent - left
-        #print "independent",independent,"dependent",dependent,"resolvable",resolved
-        order += resolved
-        #print "new order",order
-    order.reverse()
-    return order
-
-# ========= Test code ========
-def _check(msg,pairs):
-    """
-    Verify that the list n contains the given items, and that the list
-    satisfies the partial ordering given by the pairs in partial order.
-    """
-    left,right = zip(*pairs) if pairs != [] else ([],[])
-    items = set(left)
-    n = order_dependencies(pairs)
-    if set(n) != items or len(n) != len(items):
-        n.sort()
-        items = list(items); items.sort()
-        raise Exception,"%s expect %s to contain %s for %s"%(msg,n,items,pairs)
-    for lo,hi in pairs:
-        if lo in n and hi in n and n.index(lo) >= n.index(hi):
-            raise Exception,"%s expect %s before %s in %s for %s"%(msg,lo,hi,n,pairs)
-
-def test_deps():
-    import numpy
-
-    # Null case
-    _check("test empty",[])
-
-    # Some dependencies
-    _check("test1",[(2,7),(1,5),(1,4),(2,1),(3,1),(5,6)])
-    _check("test1 renumbered",[(6,1),(7,3),(7,4),(6,7),(5,7),(3,2)])
-    _check("test1 numpy",numpy.array([(2,7),(1,5),(1,4),(2,1),(3,1),(5,6)]))
-
-    # No dependencies
-    _check("test2",[(4,1),(3,2),(8,4)])
-
-    # Cycle test
-    pairs = [(1,4),(4,3),(4,5),(5,1)]
-    try: n = order_dependencies(pairs)
-    except ValueError: pass
-    else: raise Exception,"test3 expect ValueError exception for %s"%(pairs,)
-
-    # large test for gross speed check
-    A = numpy.random.randint(4000,size=(1000,2))
-    A[:,1] += 4000  # Avoid cycles
-    _check("test-large",A)
-
-    # depth tests
-    k = 200
-    A = numpy.array([range(0,k),range(1,k+1)]).T
-    _check("depth-1",A)
-
-    A = numpy.array([range(1,k+1),range(0,k)]).T
-    _check("depth-2",A)
-
-def test_expr():
-    import inspect, dis
-    import math
-    
-    symtab = {'a.b.x':1, 'a.c':2, 'a.b':3, 'b.x':4}
-    expr = 'a.b.x + sin(4*pi*a.c) + a.b.x/a.b'
-    
-    # Check symbol lookup
-    assert _symbols(expr, symtab) == set([1,2,3])
-
-    # Check symbol rename
-    assert _substitute(expr,{'a.b.x':'Q'}) == 'Q + sin(4*pi*a.c) + Q/a.b'
-    assert _substitute(expr,{'a.b':'Q'}) == 'a.b.x + sin(4*pi*a.c) + a.b.x/Q'
-
-
-    # Check dependency builder
-    # Fake parameter class
-    class Parameter:
-        def __init__(self, name, value=0, expression=''):
-            self.path = name
-            self.value = value
-            self.expression = expression
-        def iscomputed(self): return (self.expression != '')
-        def __repr__(self): return self.path
-    def world(*pars):
-        symtab = dict((p.path,p) for p in pars)
-        exprs = dict((p.path,p.expression) for p in pars if p.iscomputed())
-        return symtab, exprs
-    p1 = Parameter('G0.sigma',5)
-    p2 = Parameter('other',expression='2*pi*sin(G0.sigma/.1875) + M1.G1')
-    p3 = Parameter('M1.G1',6)
-    p4 = Parameter('constant',expression='2*pi*35')
-    # Simple chain
-    assert set(_find_dependencies(*world(p1,p2,p3))) == set([(p2.path,p1),(p2.path,p3)])
-    # Constant expression
-    assert set(_find_dependencies(*world(p1,p4))) == set([(p4.path,None)])
-    # No dependencies
-    assert set(_find_dependencies(*world(p1,p3))) == set([])
-
-    # Check function builder
-    fn = compile_constraints(*world(p1,p2,p3))
-
-    # Inspect the resulting function
-    if 0:
-        print(inspect.getdoc(fn))
-        print(dis.dis(fn))
-
-    # Evaluate the function and see if it updates the
-    # target value as expected
-    fn()
-    expected = 2*math.pi*math.sin(5/.1875) + 6
-    assert p2.value == expected,"Value was %s, not %s"%(p2.value,expected)
-    
-    # Check empty dependency set doesn't crash
-    fn = compile_constraints(*world(p1,p3))
-    fn()
-
-    # Check that constants are evaluated properly
-    fn = compile_constraints(*world(p4))
-    fn()
-    assert p4.value == 2*math.pi*35
-
-    # Check additional context example; this also tests multiple
-    # expressions
-    class Table:
-        Si = 2.09
-        values = {'Si': 2.07}
-    tbl = Table()
-    p5 = Parameter('lookup',expression="tbl.Si")
-    fn = compile_constraints(*world(p1,p2,p3,p5),context=dict(tbl=tbl))
-    fn()
-    assert p5.value == 2.09,"Value for %s was %s"%(p5.expression,p5.value)
-    p5.expression = "tbl.values['Si']"
-    fn = compile_constraints(*world(p1,p2,p3,p5),context=dict(tbl=tbl))
-    fn()
-    assert p5.value == 2.07,"Value for %s was %s"%(p5.expression,p5.value)
-    
-
-    # Verify that we capture invalid expressions
-    for expr in ['G4.cage', 'M0.cage', 'M1.G1 + *2', 
-                 'piddle',
-                 '5; import sys; print "p0wned"',
-                 '__import__("sys").argv']:
-        try:
-            p6 = Parameter('broken',expression=expr)
-            fn = compile_constraints(*world(p6))
-            fn()
-        except Exception as msg:
-            #print(msg)
-            pass
-        else:
-            raise "Failed to raise error for %s"%expr
-
-if __name__ == "__main__":
-    test_expr()
-    test_deps()
Index: src/sas/guiframe/CategoryInstaller.py
===================================================================
--- src/sas/guiframe/CategoryInstaller.py	(revision fd5ac0db8089df5bd882587f157d4bf317cfcf67)
+++ src/sas/guiframe/CategoryInstaller.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -33,6 +33,6 @@
         returns the dir where installed_models.txt should be
         """
-        import sas.dataloader.readers
-        return sas.dataloader.readers.get_data_path()
+        import sas.sascalc.dataloader.readers
+        return sas.sascalc.dataloader.readers.get_data_path()
 
     @staticmethod
Index: src/sas/guiframe/dataFitting.py
===================================================================
--- src/sas/guiframe/dataFitting.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/dataFitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -5,11 +5,11 @@
 import numpy
 import math
-from sas.data_util.uncertainty import Uncertainty
+from sas.sascalc.data_util.uncertainty import Uncertainty
 from sas.plottools.plottables import Data1D as PlotData1D
 from sas.plottools.plottables import Data2D as PlotData2D
 from sas.plottools.plottables import Theory1D as PlotTheory1D
 
-from sas.dataloader.data_info import Data1D as LoadData1D
-from sas.dataloader.data_info import Data2D as LoadData2D
+from sas.sascalc.dataloader.data_info import Data1D as LoadData1D
+from sas.sascalc.dataloader.data_info import Data2D as LoadData2D
 
 
Index: src/sas/guiframe/data_manager.py
===================================================================
--- src/sas/guiframe/data_manager.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/guiframe/data_manager.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -21,5 +21,5 @@
 from sas.guiframe.data_state import DataState
 from sas.guiframe.utils import parse_name
-import sas.dataloader.data_info as DataInfo
+import sas.sascalc.dataloader.data_info as DataInfo
 from sas.guiframe.dataFitting import Data1D
 from sas.guiframe.dataFitting import Data2D
Index: src/sas/guiframe/data_panel.py
===================================================================
--- src/sas/guiframe/data_panel.py	(revision 3a5f7c82274a6d5e083ee2f7c32b483e06ee818d)
+++ src/sas/guiframe/data_panel.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -34,5 +34,5 @@
 from sas.guiframe.gui_style import GUIFRAME
 from sas.guiframe.events import NewBatchEvent
-from sas.dataloader.loader import Loader
+from sas.sascalc.dataloader.loader import Loader
 #from sas.guiframe.local_perspectives.plotting.masking \
 #    import FloatPanel as QucikPlotDialog
Index: src/sas/guiframe/gui_manager.py
===================================================================
--- src/sas/guiframe/gui_manager.py	(revision 957af0dc596ff456a7b4745fe74937a9cea56f8f)
+++ src/sas/guiframe/gui_manager.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -45,5 +45,5 @@
 from sas.guiframe.events import EVT_NEW_BATCH
 from sas.guiframe.CategoryManager import CategoryManager
-from sas.dataloader.loader import Loader
+from sas.sascalc.dataloader.loader import Loader
 from sas.guiframe.proxy import Connection
 from matplotlib import _pylab_helpers
Index: src/sas/guiframe/local_perspectives/data_loader/data_loader.py
===================================================================
--- src/sas/guiframe/local_perspectives/data_loader/data_loader.py	(revision f21d4969d416d232125ec9af18b6bb0189f68ace)
+++ src/sas/guiframe/local_perspectives/data_loader/data_loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -8,5 +8,5 @@
 import logging
 
-from sas.dataloader.loader import Loader
+from sas.sascalc.dataloader.loader import Loader
 from sas.guiframe.plugin_base import PluginBase
 from sas.guiframe.events import StatusEvent
Index: src/sas/guiframe/local_perspectives/data_loader/load_thread.py
===================================================================
--- src/sas/guiframe/local_perspectives/data_loader/load_thread.py	(revision b9dbd6b5e3f5275da6eb798e3bc7343fddcb6d50)
+++ src/sas/guiframe/local_perspectives/data_loader/load_thread.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,5 +6,5 @@
 import os
 
-from sas.data_util.calcthread import CalcThread
+from sas.sascalc.data_util.calcthread import CalcThread
 
 
Index: src/sas/guiframe/local_perspectives/plotting/AnnulusSlicer.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/AnnulusSlicer.py	(revision 824e488a9a9f8e5e5eaf22c06f7c7d7a0c3af455)
+++ src/sas/guiframe/local_perspectives/plotting/AnnulusSlicer.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -125,5 +125,5 @@
             return
 
-        from sas.dataloader.manipulations import Ring
+        from sas.sascalc.dataloader.manipulations import Ring
         rmin = min(math.fabs(self.inner_circle.get_radius()),
                    math.fabs(self.outer_circle.get_radius()))
@@ -524,5 +524,5 @@
             return
         mask = data.mask
-        from sas.dataloader.manipulations import Ringcut
+        from sas.sascalc.dataloader.manipulations import Ringcut
 
         rmin = 0
Index: src/sas/guiframe/local_perspectives/plotting/AzimutSlicer.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/AzimutSlicer.py	(revision 824e488a9a9f8e5e5eaf22c06f7c7d7a0c3af455)
+++ src/sas/guiframe/local_perspectives/plotting/AzimutSlicer.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -150,5 +150,5 @@
         # print "phimin, phimax, rmin ,rmax",math.degrees(phimin),
         # math.degrees(phimax), rmin ,rmax
-        # from sas.dataloader.manipulations import SectorQ
+        # from sas.sascalc.dataloader.manipulations import SectorQ
 
         sect = new_sector(r_min=rmin, r_max=rmax,
@@ -261,5 +261,5 @@
         """
         """
-        from sas.dataloader.manipulations import SectorQ
+        from sas.sascalc.dataloader.manipulations import SectorQ
         self.post_data(SectorQ)
 
@@ -278,5 +278,5 @@
         """
         """
-        from sas.dataloader.manipulations import SectorPhi
+        from sas.sascalc.dataloader.manipulations import SectorPhi
         self.post_data(SectorPhi)
 
Index: src/sas/guiframe/local_perspectives/plotting/Plotter2D.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/Plotter2D.py	(revision 6f16e25ca8391464dfcea6ba0d48d6b3702d148e)
+++ src/sas/guiframe/local_perspectives/plotting/Plotter2D.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -571,5 +571,5 @@
         npt = math.sqrt(len(self.data2D.data[numpy.isfinite(self.data2D.data)]))
         npt = math.floor(npt)
-        from sas.dataloader.manipulations import CircularAverage
+        from sas.sascalc.dataloader.manipulations import CircularAverage
         ## compute the maximum radius of data2D
         self.qmax = max(math.fabs(self.data2D.xmax),
Index: src/sas/guiframe/local_perspectives/plotting/SectorSlicer.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/SectorSlicer.py	(revision b40ad402cdc9c4a7e69f4ce5a13e29b50b8242fc)
+++ src/sas/guiframe/local_perspectives/plotting/SectorSlicer.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -147,5 +147,5 @@
             return
         ## Averaging
-        from sas.dataloader.manipulations import SectorQ
+        from sas.sascalc.dataloader.manipulations import SectorQ
         radius = self.qmax
         phimin = -self.left_line.phi + self.main_line.theta
Index: src/sas/guiframe/local_perspectives/plotting/boxMask.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/boxMask.py	(revision b5de88e13816ca80c8329096bc22edbab9a36123)
+++ src/sas/guiframe/local_perspectives/plotting/boxMask.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -142,5 +142,5 @@
         contained in that region and the error on that sum
         """
-        from sas.dataloader.manipulations import Boxcut
+        from sas.sascalc.dataloader.manipulations import Boxcut
         # # Data 2D for which the pixel will be summed
         data = self.base.data
Index: src/sas/guiframe/local_perspectives/plotting/boxSlicer.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/boxSlicer.py	(revision b5de88e13816ca80c8329096bc22edbab9a36123)
+++ src/sas/guiframe/local_perspectives/plotting/boxSlicer.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -519,5 +519,5 @@
         Post data creating by averaging in Qx direction
         """
-        from sas.dataloader.manipulations import SlabX
+        from sas.sascalc.dataloader.manipulations import SlabX
         self.post_data(SlabX, direction="X")
 
@@ -536,5 +536,5 @@
         Post data creating by averaging in Qy direction
         """
-        from sas.dataloader.manipulations import SlabY
+        from sas.sascalc.dataloader.manipulations import SlabY
         self.post_data(SlabY, direction="Y")
 
Index: src/sas/guiframe/local_perspectives/plotting/boxSum.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/boxSum.py	(revision b5de88e13816ca80c8329096bc22edbab9a36123)
+++ src/sas/guiframe/local_perspectives/plotting/boxSum.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -180,9 +180,9 @@
         y_max = self.vertical_lines.y1
         # #computation of the sum and its error
-        from sas.dataloader.manipulations import Boxavg
+        from sas.sascalc.dataloader.manipulations import Boxavg
         box = Boxavg(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max)
         self.count, self.error = box(self.base.data2D)
         # Dig out number of points summed, SMK & PDB, 04/03/2013
-        from sas.dataloader.manipulations import Boxsum
+        from sas.sascalc.dataloader.manipulations import Boxsum
         boxtotal = Boxsum(x_min=x_min, x_max=x_max, y_min=y_min, y_max=y_max)
         self.total, self.totalerror, self.points = boxtotal(self.base.data2D)
Index: src/sas/guiframe/local_perspectives/plotting/masking.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/masking.py	(revision c039589ad31dec3058311cae229f7aa1dc56faf8)
+++ src/sas/guiframe/local_perspectives/plotting/masking.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -52,5 +52,5 @@
     FONT_VARIANT = 1
 
-from sas.data_util.calcthread import CalcThread
+from sas.sascalc.data_util.calcthread import CalcThread
 
 class CalcPlot(CalcThread):
Index: src/sas/guiframe/local_perspectives/plotting/sector_mask.py
===================================================================
--- src/sas/guiframe/local_perspectives/plotting/sector_mask.py	(revision c039589ad31dec3058311cae229f7aa1dc56faf8)
+++ src/sas/guiframe/local_perspectives/plotting/sector_mask.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -120,5 +120,5 @@
             return
         ## Averaging
-        from sas.dataloader.manipulations import Sectorcut
+        from sas.sascalc.dataloader.manipulations import Sectorcut
         phimin = -self.left_line.phi + self.main_line.theta
         phimax = self.left_line.phi + self.main_line.theta
Index: c/sas/invariant/invariant.py
===================================================================
--- src/sas/invariant/invariant.py	(revision c76bdc3c495542e5fb3e2e17aa25a700f7298a8a)
+++ 	(revision )
@@ -1,962 +1,0 @@
-# pylint: disable=invalid-name
-#####################################################################
-#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.
-#See the license text in license.txt
-#copyright 2010, University of Tennessee
-######################################################################
-
-"""
-This module implements invariant and its related computations.
-
-:author: Gervaise B. Alina/UTK
-:author: Mathieu Doucet/UTK
-:author: Jae Cho/UTK
-
-"""
-import math
-import numpy
-
-from sas.dataloader.data_info import Data1D as LoaderData1D
-
-# The minimum q-value to be used when extrapolating
-Q_MINIMUM = 1e-5
-
-# The maximum q-value to be used when extrapolating
-Q_MAXIMUM = 10
-
-# Number of steps in the extrapolation
-INTEGRATION_NSTEPS = 1000
-
-class Transform(object):
-    """
-    Define interface that need to compute a function or an inverse
-    function given some x, y
-    """
-
-    def linearize_data(self, data):
-        """
-        Linearize data so that a linear fit can be performed.
-        Filter out the data that can't be transformed.
-
-        :param data: LoadData1D instance
-
-        """
-        # Check that the vector lengths are equal
-        assert len(data.x) == len(data.y)
-        if data.dy is not None:
-            assert len(data.x) == len(data.dy)
-            dy = data.dy
-        else:
-            dy = numpy.ones(len(data.y))
-
-        # Transform the data
-        data_points = zip(data.x, data.y, dy)
-
-        output_points = [(self.linearize_q_value(p[0]),
-                          math.log(p[1]),
-                          p[2] / p[1]) for p in data_points if p[0] > 0 and \
-                          p[1] > 0 and p[2] > 0]
-
-        x_out, y_out, dy_out = zip(*output_points)
-
-        # Create Data1D object
-        x_out = numpy.asarray(x_out)
-        y_out = numpy.asarray(y_out)
-        dy_out = numpy.asarray(dy_out)
-        linear_data = LoaderData1D(x=x_out, y=y_out, dy=dy_out)
-
-        return linear_data
-
-    def get_allowed_bins(self, data):
-        """
-        Goes through the data points and returns a list of boolean values
-        to indicate whether each points is allowed by the model or not.
-
-        :param data: Data1D object
-        """
-        return [p[0] > 0 and p[1] > 0 and p[2] > 0 for p in zip(data.x, data.y,
-                                                                data.dy)]
-
-    def linearize_q_value(self, value):
-        """
-        Transform the input q-value for linearization
-        """
-        return NotImplemented
-
-    def extract_model_parameters(self, constant, slope, dconstant=0, dslope=0):
-        """
-        set private member
-        """
-        return NotImplemented
-
-    def evaluate_model(self, x):
-        """
-        Returns an array f(x) values where f is the Transform function.
-        """
-        return NotImplemented
-
-    def evaluate_model_errors(self, x):
-        """
-        Returns an array of I(q) errors
-        """
-        return NotImplemented
-
-class Guinier(Transform):
-    """
-    class of type Transform that performs operations related to guinier
-    function
-    """
-    def __init__(self, scale=1, radius=60):
-        Transform.__init__(self)
-        self.scale = scale
-        self.radius = radius
-        ## Uncertainty of scale parameter
-        self.dscale = 0
-        ## Unvertainty of radius parameter
-        self.dradius = 0
-
-    def linearize_q_value(self, value):
-        """
-        Transform the input q-value for linearization
-
-        :param value: q-value
-
-        :return: q*q
-        """
-        return value * value
-
-    def extract_model_parameters(self, constant, slope, dconstant=0, dslope=0):
-        """
-	    assign new value to the scale and the radius
-    	"""
-        self.scale = math.exp(constant)
-        if slope > 0:
-            slope = 0.0
-        self.radius = math.sqrt(-3 * slope)
-        # Errors
-        self.dscale = math.exp(constant) * dconstant
-        if slope == 0.0:
-            n_zero = -1.0e-24
-            self.dradius = -3.0 / 2.0 / math.sqrt(-3 * n_zero) * dslope
-        else:
-            self.dradius = -3.0 / 2.0 / math.sqrt(-3 * slope) * dslope
-
-        return [self.radius, self.scale], [self.dradius, self.dscale]
-
-    def evaluate_model(self, x):
-        """
-        return F(x)= scale* e-((radius*x)**2/3)
-        """
-        return self._guinier(x)
-
-    def evaluate_model_errors(self, x):
-        """
-        Returns the error on I(q) for the given array of q-values
-
-        :param x: array of q-values
-        """
-        p1 = numpy.array([self.dscale * math.exp(-((self.radius * q) ** 2 / 3)) \
-                          for q in x])
-        p2 = numpy.array([self.scale * math.exp(-((self.radius * q) ** 2 / 3))\
-                     * (-(q ** 2 / 3)) * 2 * self.radius * self.dradius for q in x])
-        diq2 = p1 * p1 + p2 * p2
-        return numpy.array([math.sqrt(err) for err in diq2])
-
-    def _guinier(self, x):
-        """
-        Retrieve the guinier function after apply an inverse guinier function
-        to x
-        Compute a F(x) = scale* e-((radius*x)**2/3).
-
-        :param x: a vector of q values
-        :param scale: the scale value
-        :param radius: the guinier radius value
-
-        :return: F(x)
-        """
-        # transform the radius of coming from the inverse guinier function to a
-        # a radius of a guinier function
-        if self.radius <= 0:
-            msg = "Rg expected positive value, but got %s" % self.radius
-            raise ValueError(msg)
-        value = numpy.array([math.exp(-((self.radius * i) ** 2 / 3)) for i in x])
-        return self.scale * value
-
-class PowerLaw(Transform):
-    """
-    class of type transform that perform operation related to power_law
-    function
-    """
-    def __init__(self, scale=1, power=4):
-        Transform.__init__(self)
-        self.scale = scale
-        self.power = power
-        self.dscale = 0.0
-        self.dpower = 0.0
-
-    def linearize_q_value(self, value):
-        """
-        Transform the input q-value for linearization
-
-        :param value: q-value
-
-        :return: log(q)
-        """
-        return math.log(value)
-
-    def extract_model_parameters(self, constant, slope, dconstant=0, dslope=0):
-        """
-        Assign new value to the scale and the power
-        """
-        self.power = -slope
-        self.scale = math.exp(constant)
-
-        # Errors
-        self.dscale = math.exp(constant) * dconstant
-        self.dpower = -dslope
-
-        return [self.power, self.scale], [self.dpower, self.dscale]
-
-    def evaluate_model(self, x):
-        """
-        given a scale and a radius transform x, y using a power_law
-        function
-        """
-        return self._power_law(x)
-
-    def evaluate_model_errors(self, x):
-        """
-        Returns the error on I(q) for the given array of q-values
-        :param x: array of q-values
-        """
-        p1 = numpy.array([self.dscale * math.pow(q, -self.power) for q in x])
-        p2 = numpy.array([self.scale * self.power * math.pow(q, -self.power - 1)\
-                           * self.dpower for q in x])
-        diq2 = p1 * p1 + p2 * p2
-        return numpy.array([math.sqrt(err) for err in diq2])
-
-    def _power_law(self, x):
-        """
-        F(x) = scale* (x)^(-power)
-            when power= 4. the model is porod
-            else power_law
-        The model has three parameters: ::
-            1. x: a vector of q values
-            2. power: power of the function
-            3. scale : scale factor value
-
-        :param x: array
-        :return: F(x)
-        """
-        if self.power <= 0:
-            msg = "Power_law function expected positive power,"
-            msg += " but got %s" % self.power
-            raise ValueError(msg)
-        if self.scale <= 0:
-            msg = "scale expected positive value, but got %s" % self.scale
-            raise ValueError(msg)
-
-        value = numpy.array([math.pow(i, -self.power) for i in x])
-        return self.scale * value
-
-class Extrapolator(object):
-    """
-    Extrapolate I(q) distribution using a given model
-    """
-    def __init__(self, data, model=None):
-        """
-        Determine a and b given a linear equation y = ax + b
-
-        If a model is given, it will be used to linearize the data before
-        the extrapolation is performed. If None,
-        a simple linear fit will be done.
-
-        :param data: data containing x and y  such as  y = ax + b
-        :param model: optional Transform object
-        """
-        self.data = data
-        self.model = model
-
-        # Set qmin as the lowest non-zero value
-        self.qmin = Q_MINIMUM
-        for q_value in self.data.x:
-            if q_value > 0:
-                self.qmin = q_value
-                break
-        self.qmax = max(self.data.x)
-
-    def fit(self, power=None, qmin=None, qmax=None):
-        """
-        Fit data for y = ax + b  return a and b
-
-        :param power: a fixed, otherwise None
-        :param qmin: Minimum Q-value
-        :param qmax: Maximum Q-value
-        """
-        if qmin is None:
-            qmin = self.qmin
-        if qmax is None:
-            qmax = self.qmax
-
-        # Identify the bin range for the fit
-        idx = (self.data.x >= qmin) & (self.data.x <= qmax)
-
-        fx = numpy.zeros(len(self.data.x))
-
-        # Uncertainty
-        if type(self.data.dy) == numpy.ndarray and \
-            len(self.data.dy) == len(self.data.x) and \
-            numpy.all(self.data.dy > 0):
-            sigma = self.data.dy
-        else:
-            sigma = numpy.ones(len(self.data.x))
-
-        # Compute theory data f(x)
-        fx[idx] = self.data.y[idx]
-
-        # Linearize the data
-        if self.model is not None:
-            linearized_data = self.model.linearize_data(\
-                                            LoaderData1D(self.data.x[idx],
-                                                         fx[idx],
-                                                         dy=sigma[idx]))
-        else:
-            linearized_data = LoaderData1D(self.data.x[idx],
-                                           fx[idx],
-                                           dy=sigma[idx])
-
-        ##power is given only for function = power_law
-        if power != None:
-            sigma2 = linearized_data.dy * linearized_data.dy
-            a = -(power)
-            b = (numpy.sum(linearized_data.y / sigma2) \
-                 - a * numpy.sum(linearized_data.x / sigma2)) / numpy.sum(1.0 / sigma2)
-
-
-            deltas = linearized_data.x * a + \
-                    numpy.ones(len(linearized_data.x)) * b - linearized_data.y
-            residuals = numpy.sum(deltas * deltas / sigma2)
-
-            err = math.fabs(residuals) / numpy.sum(1.0 / sigma2)
-            return [a, b], [0, math.sqrt(err)]
-        else:
-            A = numpy.vstack([linearized_data.x / linearized_data.dy, 1.0 / linearized_data.dy]).T
-            (p, residuals, _, _) = numpy.linalg.lstsq(A, linearized_data.y / linearized_data.dy)
-
-            # Get the covariance matrix, defined as inv_cov = a_transposed * a
-            err = numpy.zeros(2)
-            try:
-                inv_cov = numpy.dot(A.transpose(), A)
-                cov = numpy.linalg.pinv(inv_cov)
-                err_matrix = math.fabs(residuals) * cov
-                err = [math.sqrt(err_matrix[0][0]), math.sqrt(err_matrix[1][1])]
-            except:
-                err = [-1.0, -1.0]
-
-            return p, err
-
-
-class InvariantCalculator(object):
-    """
-    Compute invariant if data is given.
-    Can provide volume fraction and surface area if the user provides
-    Porod constant  and contrast values.
-
-    :precondition:  the user must send a data of type DataLoader.Data1D
-                    the user provide background and scale values.
-
-    :note: Some computations depends on each others.
-    """
-    def __init__(self, data, background=0, scale=1):
-        """
-        Initialize variables.
-
-        :param data: data must be of type DataLoader.Data1D
-        :param background: Background value. The data will be corrected
-            before processing
-        :param scale: Scaling factor for I(q). The data will be corrected
-            before processing
-        """
-        # Background and scale should be private data member if the only way to
-        # change them are by instantiating a new object.
-        self._background = background
-        self._scale = scale
-        # slit height for smeared data
-        self._smeared = None
-        # The data should be private
-        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:
-            # assumes constant dxl
-            self._smeared = self._data.dxl[0]
-
-        # Since there are multiple variants of Q*, you should force the
-        # user to use the get method and keep Q* a private data member
-        self._qstar = None
-
-        # You should keep the error on Q* so you can reuse it without
-        # recomputing the whole thing.
-        self._qstar_err = 0
-
-        # Extrapolation parameters
-        self._low_extrapolation_npts = 4
-        self._low_extrapolation_function = Guinier()
-        self._low_extrapolation_power = None
-        self._low_extrapolation_power_fitted = None
-
-        self._high_extrapolation_npts = 4
-        self._high_extrapolation_function = PowerLaw()
-        self._high_extrapolation_power = None
-        self._high_extrapolation_power_fitted = None
-
-        # Extrapolation range
-        self._low_q_limit = Q_MINIMUM
-
-    def _get_data(self, data):
-        """
-        :note: this function must be call before computing any type
-         of invariant
-
-        :return: new data = self._scale *data - self._background
-        """
-        if not issubclass(data.__class__, LoaderData1D):
-            #Process only data that inherited from DataLoader.Data_info.Data1D
-            raise ValueError, "Data must be of type DataLoader.Data1D"
-        #from copy import deepcopy
-        new_data = (self._scale * data) - self._background
-
-        # Check that the vector lengths are equal
-        assert len(new_data.x) == len(new_data.y)
-
-        # Verify that the errors are set correctly
-        if new_data.dy is None or len(new_data.x) != len(new_data.dy) or \
-            (min(new_data.dy) == 0 and max(new_data.dy) == 0):
-            new_data.dy = numpy.ones(len(new_data.x))
-        return  new_data
-
-    def _fit(self, model, qmin=Q_MINIMUM, qmax=Q_MAXIMUM, power=None):
-        """
-        fit data with function using
-        data = self._get_data()
-        fx = Functor(data , function)
-        y = data.y
-        slope, constant = linalg.lstsq(y,fx)
-
-        :param qmin: data first q value to consider during the fit
-        :param qmax: data last q value to consider during the fit
-        :param power : power value to consider for power-law
-        :param function: the function to use during the fit
-
-        :return a: the scale of the function
-        :return b: the other parameter of the function for guinier will be radius
-                for power_law will be the power value
-        """
-        extrapolator = Extrapolator(data=self._data, model=model)
-        p, dp = extrapolator.fit(power=power, qmin=qmin, qmax=qmax)
-
-        return model.extract_model_parameters(constant=p[1], slope=p[0],
-                                              dconstant=dp[1], dslope=dp[0])
-
-    def _get_qstar(self, data):
-        """
-        Compute invariant for pinhole data.
-        This invariant is given by: ::
-
-            q_star = x0**2 *y0 *dx0 +x1**2 *y1 *dx1
-                        + ..+ xn**2 *yn *dxn    for non smeared data
-
-            q_star = dxl0 *x0 *y0 *dx0 +dxl1 *x1 *y1 *dx1
-                        + ..+ dlxn *xn *yn *dxn    for smeared data
-
-            where n >= len(data.x)-1
-            dxl = slit height dQl
-            dxi = 1/2*(xi+1 - xi) + (xi - xi-1)
-            dx0 = (x1 - x0)/2
-            dxn = (xn - xn-1)/2
-
-        :param data: the data to use to compute invariant.
-
-        :return q_star: invariant value for pinhole data. q_star > 0
-        """
-        if len(data.x) <= 1 or len(data.y) <= 1 or len(data.x) != len(data.y):
-            msg = "Length x and y must be equal"
-            msg += " and greater than 1; got x=%s, y=%s" % (len(data.x), len(data.y))
-            raise ValueError, msg
-        else:
-            # Take care of smeared data
-            if self._smeared is None:
-                gx = data.x * data.x
-            # assumes that len(x) == len(dxl).
-            else:
-                gx = data.dxl * data.x
-
-            n = len(data.x) - 1
-            #compute the first delta q
-            dx0 = (data.x[1] - data.x[0]) / 2
-            #compute the last delta q
-            dxn = (data.x[n] - data.x[n - 1]) / 2
-            total = 0
-            total += gx[0] * data.y[0] * dx0
-            total += gx[n] * data.y[n] * dxn
-
-            if len(data.x) == 2:
-                return total
-            else:
-                #iterate between for element different
-                #from the first and the last
-                for i in xrange(1, n - 1):
-                    dxi = (data.x[i + 1] - data.x[i - 1]) / 2
-                    total += gx[i] * data.y[i] * dxi
-                return total
-
-    def _get_qstar_uncertainty(self, data):
-        """
-        Compute invariant uncertainty with with pinhole data.
-        This uncertainty is given as follow: ::
-
-           dq_star = math.sqrt[(x0**2*(dy0)*dx0)**2 +
-                (x1**2 *(dy1)*dx1)**2 + ..+ (xn**2 *(dyn)*dxn)**2 ]
-        where n >= len(data.x)-1
-        dxi = 1/2*(xi+1 - xi) + (xi - xi-1)
-        dx0 = (x1 - x0)/2
-        dxn = (xn - xn-1)/2
-        dyn: error on dy
-
-        :param data:
-        :note: if data doesn't contain dy assume dy= math.sqrt(data.y)
-        """
-        if len(data.x) <= 1 or len(data.y) <= 1 or \
-            len(data.x) != len(data.y) or \
-            (data.dy is not None and (len(data.dy) != len(data.y))):
-            msg = "Length of data.x and data.y must be equal"
-            msg += " and greater than 1; got x=%s, y=%s" % (len(data.x), len(data.y))
-            raise ValueError, msg
-        else:
-            #Create error for data without dy error
-            if data.dy is None:
-                dy = math.sqrt(data.y)
-            else:
-                dy = data.dy
-            # Take care of smeared data
-            if self._smeared is None:
-                gx = data.x * data.x
-            # assumes that len(x) == len(dxl).
-            else:
-                gx = data.dxl * data.x
-
-            n = len(data.x) - 1
-            #compute the first delta
-            dx0 = (data.x[1] - data.x[0]) / 2
-            #compute the last delta
-            dxn = (data.x[n] - data.x[n - 1]) / 2
-            total = 0
-            total += (gx[0] * dy[0] * dx0) ** 2
-            total += (gx[n] * dy[n] * dxn) ** 2
-            if len(data.x) == 2:
-                return math.sqrt(total)
-            else:
-                #iterate between for element different
-                #from the first and the last
-                for i in xrange(1, n - 1):
-                    dxi = (data.x[i + 1] - data.x[i - 1]) / 2
-                    total += (gx[i] * dy[i] * dxi) ** 2
-                return math.sqrt(total)
-
-    def _get_extrapolated_data(self, model, npts=INTEGRATION_NSTEPS,
-                               q_start=Q_MINIMUM, q_end=Q_MAXIMUM):
-        """
-        :return: extrapolate data create from data
-        """
-        #create new Data1D to compute the invariant
-        q = numpy.linspace(start=q_start,
-                           stop=q_end,
-                           num=npts,
-                           endpoint=True)
-        iq = model.evaluate_model(q)
-        diq = model.evaluate_model_errors(q)
-
-        result_data = LoaderData1D(x=q, y=iq, dy=diq)
-        if self._smeared != None:
-            result_data.dxl = self._smeared * numpy.ones(len(q))
-        return result_data
-
-    def get_data(self):
-        """
-        :return: self._data
-        """
-        return self._data
-
-    def get_extrapolation_power(self, range='high'):
-        """
-        :return: the fitted power for power law function for a given
-            extrapolation range
-        """
-        if range == 'low':
-            return self._low_extrapolation_power_fitted
-        return self._high_extrapolation_power_fitted
-
-    def get_qstar_low(self):
-        """
-        Compute the invariant for extrapolated data at low q range.
-
-        Implementation:
-            data = self._get_extra_data_low()
-            return self._get_qstar()
-
-        :return q_star: the invariant for data extrapolated at low q.
-        """
-        # Data boundaries for fitting
-        qmin = self._data.x[0]
-        qmax = self._data.x[self._low_extrapolation_npts - 1]
-
-        # Extrapolate the low-Q data
-        p, _ = self._fit(model=self._low_extrapolation_function,
-                         qmin=qmin,
-                         qmax=qmax,
-                         power=self._low_extrapolation_power)
-        self._low_extrapolation_power_fitted = p[0]
-
-        # Distribution starting point
-        self._low_q_limit = Q_MINIMUM
-        if Q_MINIMUM >= qmin:
-            self._low_q_limit = qmin / 10
-
-        data = self._get_extrapolated_data(\
-                                    model=self._low_extrapolation_function,
-                                    npts=INTEGRATION_NSTEPS,
-                                    q_start=self._low_q_limit, q_end=qmin)
-
-        # Systematic error
-        # If we have smearing, the shape of the I(q) distribution at low Q will
-        # may not be a Guinier or simple power law. The following is
-        # a conservative estimation for the systematic error.
-        err = qmin * qmin * math.fabs((qmin - self._low_q_limit) * \
-                                  (data.y[0] - data.y[INTEGRATION_NSTEPS - 1]))
-        return self._get_qstar(data), self._get_qstar_uncertainty(data) + err
-
-    def get_qstar_high(self):
-        """
-        Compute the invariant for extrapolated data at high q range.
-
-        Implementation:
-            data = self._get_extra_data_high()
-            return self._get_qstar()
-
-        :return q_star: the invariant for data extrapolated at high q.
-        """
-        # Data boundaries for fitting
-        x_len = len(self._data.x) - 1
-        qmin = self._data.x[x_len - (self._high_extrapolation_npts - 1)]
-        qmax = self._data.x[x_len]
-
-        # fit the data with a model to get the appropriate parameters
-        p, _ = self._fit(model=self._high_extrapolation_function,
-                         qmin=qmin,
-                         qmax=qmax,
-                         power=self._high_extrapolation_power)
-        self._high_extrapolation_power_fitted = p[0]
-
-        #create new Data1D to compute the invariant
-        data = self._get_extrapolated_data(\
-                                    model=self._high_extrapolation_function,
-                                    npts=INTEGRATION_NSTEPS,
-                                    q_start=qmax, q_end=Q_MAXIMUM)
-
-        return self._get_qstar(data), self._get_qstar_uncertainty(data)
-
-    def get_extra_data_low(self, npts_in=None, q_start=None, npts=20):
-        """
-        Returns the extrapolated data used for the loew-Q invariant calculation.
-        By default, the distribution will cover the data points used for the
-        extrapolation. The number of overlap points is a parameter (npts_in).
-        By default, the maximum q-value of the distribution will be
-        the minimum q-value used when extrapolating for the purpose of the
-        invariant calculation.
-
-        :param npts_in: number of data points for which
-            the extrapolated data overlap
-        :param q_start: is the minimum value to uses for extrapolated data
-        :param npts: the number of points in the extrapolated distribution
-
-        """
-        # Get extrapolation range
-        if q_start is None:
-            q_start = self._low_q_limit
-
-        if npts_in is None:
-            npts_in = self._low_extrapolation_npts
-        q_end = self._data.x[max(0, npts_in - 1)]
-
-        if q_start >= q_end:
-            return numpy.zeros(0), numpy.zeros(0)
-
-        return self._get_extrapolated_data(\
-                                    model=self._low_extrapolation_function,
-                                    npts=npts,
-                                    q_start=q_start, q_end=q_end)
-
-    def get_extra_data_high(self, npts_in=None, q_end=Q_MAXIMUM, npts=20):
-        """
-        Returns the extrapolated data used for the high-Q invariant calculation.
-        By default, the distribution will cover the data points used for the
-        extrapolation. The number of overlap points is a parameter (npts_in).
-        By default, the maximum q-value of the distribution will be Q_MAXIMUM,
-        the maximum q-value used when extrapolating for the purpose of the
-        invariant calculation.
-
-        :param npts_in: number of data points for which the
-            extrapolated data overlap
-        :param q_end: is the maximum value to uses for extrapolated data
-        :param npts: the number of points in the extrapolated distribution
-        """
-        # Get extrapolation range
-        if npts_in is None:
-            npts_in = self._high_extrapolation_npts
-        _npts = len(self._data.x)
-        q_start = self._data.x[min(_npts, _npts - npts_in)]
-
-        if q_start >= q_end:
-            return numpy.zeros(0), numpy.zeros(0)
-
-        return self._get_extrapolated_data(\
-                                model=self._high_extrapolation_function,
-                                npts=npts,
-                                q_start=q_start, q_end=q_end)
-
-    def set_extrapolation(self, range, npts=4, function=None, power=None):
-        """
-        Set the extrapolation parameters for the high or low Q-range.
-        Note that this does not turn extrapolation on or off.
-
-        :param range: a keyword set the type of extrapolation . type string
-        :param npts: the numbers of q points of data to consider
-            for extrapolation
-        :param function: a keyword to select the function to use
-            for extrapolation.
-            of type string.
-        :param power: an power to apply power_low function
-
-        """
-        range = range.lower()
-        if range not in ['high', 'low']:
-            raise ValueError, "Extrapolation range should be 'high' or 'low'"
-        function = function.lower()
-        if function not in ['power_law', 'guinier']:
-            msg = "Extrapolation function should be 'guinier' or 'power_law'"
-            raise ValueError, msg
-
-        if range == 'high':
-            if function != 'power_law':
-                msg = "Extrapolation only allows a power law at high Q"
-                raise ValueError, msg
-            self._high_extrapolation_npts = npts
-            self._high_extrapolation_power = power
-            self._high_extrapolation_power_fitted = power
-        else:
-            if function == 'power_law':
-                self._low_extrapolation_function = PowerLaw()
-            else:
-                self._low_extrapolation_function = Guinier()
-            self._low_extrapolation_npts = npts
-            self._low_extrapolation_power = power
-            self._low_extrapolation_power_fitted = power
-
-    def get_qstar(self, extrapolation=None):
-        """
-        Compute the invariant of the local copy of data.
-
-        :param extrapolation: string to apply optional extrapolation
-
-        :return q_star: invariant of the data within data's q range
-
-        :warning: When using setting data to Data1D ,
-            the user is responsible of
-            checking that the scale and the background are
-            properly apply to the data
-
-        """
-        self._qstar = self._get_qstar(self._data)
-        self._qstar_err = self._get_qstar_uncertainty(self._data)
-
-        if extrapolation is None:
-            return self._qstar
-
-        # Compute invariant plus invariant of extrapolated data
-        extrapolation = extrapolation.lower()
-        if extrapolation == "low":
-            qs_low, dqs_low = self.get_qstar_low()
-            qs_hi, dqs_hi = 0, 0
-
-        elif extrapolation == "high":
-            qs_low, dqs_low = 0, 0
-            qs_hi, dqs_hi = self.get_qstar_high()
-
-        elif extrapolation == "both":
-            qs_low, dqs_low = self.get_qstar_low()
-            qs_hi, dqs_hi = self.get_qstar_high()
-
-        self._qstar += qs_low + qs_hi
-        self._qstar_err = math.sqrt(self._qstar_err * self._qstar_err \
-                                    + dqs_low * dqs_low + dqs_hi * dqs_hi)
-
-        return self._qstar
-
-    def get_surface(self, contrast, porod_const, extrapolation=None):
-        """
-        Compute the specific surface from the data.
-
-        Implementation::
-
-          V =  self.get_volume_fraction(contrast, extrapolation)
-
-          Compute the surface given by:
-            surface = (2*pi *V(1- V)*porod_const)/ q_star
-
-        :param contrast: contrast value to compute the volume
-        :param porod_const: Porod constant to compute the surface
-        :param extrapolation: string to apply optional extrapolation
-
-        :return: specific surface
-        """
-        # Compute the volume
-        volume = self.get_volume_fraction(contrast, extrapolation)
-        return 2 * math.pi * volume * (1 - volume) * \
-            float(porod_const) / self._qstar
-
-    def get_volume_fraction(self, contrast, extrapolation=None):
-        """
-        Compute volume fraction is deduced as follow: ::
-
-            q_star = 2*(pi*contrast)**2* volume( 1- volume)
-            for k = 10^(-8)*q_star/(2*(pi*|contrast|)**2)
-            we get 2 values of volume:
-                 with   1 - 4 * k >= 0
-                 volume1 = (1- sqrt(1- 4*k))/2
-                 volume2 = (1+ sqrt(1- 4*k))/2
-
-            q_star: the invariant value included extrapolation is applied
-                         unit  1/A^(3)*1/cm
-                    q_star = self.get_qstar()
-
-            the result returned will be 0 <= volume <= 1
-
-        :param contrast: contrast value provides by the user of type float.
-                 contrast unit is 1/A^(2)= 10^(16)cm^(2)
-        :param extrapolation: string to apply optional extrapolation
-
-        :return: volume fraction
-
-        :note: volume fraction must have no unit
-        """
-        if contrast <= 0:
-            raise ValueError, "The contrast parameter must be greater than zero"
-
-        # Make sure Q star is up to date
-        self.get_qstar(extrapolation)
-
-        if self._qstar <= 0:
-            msg = "Invalid invariant: Invariant Q* must be greater than zero"
-            raise RuntimeError, msg
-
-        # Compute intermediate constant
-        k = 1.e-8 * self._qstar / (2 * (math.pi * math.fabs(float(contrast))) ** 2)
-        # Check discriminant value
-        discrim = 1 - 4 * k
-
-        # Compute volume fraction
-        if discrim < 0:
-            msg = "Could not compute the volume fraction: negative discriminant"
-            raise RuntimeError, msg
-        elif discrim == 0:
-            return 1 / 2
-        else:
-            volume1 = 0.5 * (1 - math.sqrt(discrim))
-            volume2 = 0.5 * (1 + math.sqrt(discrim))
-
-            if 0 <= volume1 and volume1 <= 1:
-                return volume1
-            elif 0 <= volume2 and volume2 <= 1:
-                return volume2
-            msg = "Could not compute the volume fraction: inconsistent results"
-            raise RuntimeError, msg
-
-    def get_qstar_with_error(self, extrapolation=None):
-        """
-        Compute the invariant uncertainty.
-        This uncertainty computation depends on whether or not the data is
-        smeared.
-
-        :param extrapolation: string to apply optional extrapolation
-
-        :return: invariant, the invariant uncertainty
-        """
-        self.get_qstar(extrapolation)
-        return self._qstar, self._qstar_err
-
-    def get_volume_fraction_with_error(self, contrast, extrapolation=None):
-        """
-        Compute uncertainty on volume value as well as the volume fraction
-        This uncertainty is given by the following equation: ::
-
-            dV = 0.5 * (4*k* dq_star) /(2* math.sqrt(1-k* q_star))
-
-            for k = 10^(-8)*q_star/(2*(pi*|contrast|)**2)
-
-            q_star: the invariant value including extrapolated value if existing
-            dq_star: the invariant uncertainty
-            dV: the volume uncertainty
-
-        The uncertainty will be set to -1 if it can't be computed.
-
-        :param contrast: contrast value
-        :param extrapolation: string to apply optional extrapolation
-
-        :return: V, dV = volume fraction, error on volume fraction
-        """
-        volume = self.get_volume_fraction(contrast, extrapolation)
-
-        # Compute error
-        k = 1.e-8 * self._qstar / (2 * (math.pi * math.fabs(float(contrast))) ** 2)
-        # Check value inside the sqrt function
-        value = 1 - k * self._qstar
-        if (value) <= 0:
-            uncertainty = -1
-        # Compute uncertainty
-        uncertainty = math.fabs((0.5 * 4 * k * \
-                        self._qstar_err) / (2 * math.sqrt(1 - k * self._qstar)))
-
-        return volume, uncertainty
-
-    def get_surface_with_error(self, contrast, porod_const, extrapolation=None):
-        """
-        Compute uncertainty of the surface value as well as the surface value.
-        The uncertainty is given as follow: ::
-
-            dS = porod_const *2*pi[( dV -2*V*dV)/q_star
-                 + dq_star(v-v**2)
-
-            q_star: the invariant value
-            dq_star: the invariant uncertainty
-            V: the volume fraction value
-            dV: the volume uncertainty
-
-        :param contrast: contrast value
-        :param porod_const: porod constant value
-        :param extrapolation: string to apply optional extrapolation
-
-        :return S, dS: the surface, with its uncertainty
-        """
-        # We get the volume fraction, with error
-        #   get_volume_fraction_with_error calls get_volume_fraction
-        #   get_volume_fraction calls get_qstar
-        #   which computes Qstar and dQstar
-        v, dv = self.get_volume_fraction_with_error(contrast, extrapolation)
-
-        s = self.get_surface(contrast=contrast, porod_const=porod_const,
-                             extrapolation=extrapolation)
-        ds = porod_const * 2 * math.pi * ((dv - 2 * v * dv) / self._qstar\
-                 + self._qstar_err * (v - v ** 2))
-
-        return s, ds
Index: c/sas/invariant/invariant_mapper.py
===================================================================
--- src/sas/invariant/invariant_mapper.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,48 +1,0 @@
-"""
-This module is a wrapper to a map function. It allows to loop through 
-different invariant objects to call the same function
-"""
-
-
-def get_qstar(inv, extrapolation=None):
-    """
-    Get invariant value (Q*)
-    """
-    return inv.get_qstar(extrapolation)
-
-def get_qstar_with_error(inv, extrapolation=None):
-    """
-    Get invariant value with uncertainty
-    """
-    return inv.get_qstar_with_error(extrapolation)
-
-def get_volume_fraction(inv, contrast, extrapolation=None):
-    """
-    Get volume fraction
-    """
-    return inv.get_volume_fraction(contrast, extrapolation)
-
-def get_volume_fraction_with_error(inv, contrast, extrapolation=None):
-    """
-    Get volume fraction with uncertainty
-    """
-    return inv.get_volume_fraction_with_error(contrast,
-                                                    extrapolation)
-
-def get_surface(inv, contrast, porod_const, extrapolation=None):
-    """
-    Get surface with uncertainty
-    """
-    return inv.get_surface(contrast=contrast,
-                                      porod_const=porod_const,
-                                      extrapolation=extrapolation)
-
-def get_surface_with_error(inv, contrast, 
-                           porod_const, extrapolation=None):
-    """
-    Get surface with uncertainty
-    """
-    return inv.get_surface_with_error(contrast=contrast,
-                                      porod_const=porod_const,
-                                      extrapolation=extrapolation)
-
Index: src/sas/models/smearing_2d.py
===================================================================
--- src/sas/models/smearing_2d.py	(revision fd5ac0db8089df5bd882587f157d4bf317cfcf67)
+++ src/sas/models/smearing_2d.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -240,5 +240,5 @@
     dz = numpy.sqrt(z)
     
-    from sas.dataloader import Data2D
+    from sas.sascalc.dataloader import Data2D
     #for i in range(10): print i, 0.001 + i*0.008/9.0 
     #for i in range(100): print i, int(math.floor( (i/ (100/9.0)) )) 
Index: src/sas/perspectives/calculator/collimation_editor.py
===================================================================
--- src/sas/perspectives/calculator/collimation_editor.py	(revision 49ab5d718dd7743a6a296e666dfb643183bc969d)
+++ src/sas/perspectives/calculator/collimation_editor.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -4,6 +4,6 @@
 import sys
 from copy import deepcopy
-from sas.dataloader.loader import Loader
-from sas.dataloader.data_info import Aperture, Collimation
+from sas.sascalc.dataloader.loader import Loader
+from sas.sascalc.dataloader.data_info import Aperture, Collimation
 from aperture_editor import ApertureDialog
 
Index: src/sas/perspectives/calculator/console.py
===================================================================
--- src/sas/perspectives/calculator/console.py	(revision 49ab5d718dd7743a6a296e666dfb643183bc969d)
+++ src/sas/perspectives/calculator/console.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -4,5 +4,5 @@
 import wx
 import sys
-from sas.dataloader.loader import Loader
+from sas.sascalc.dataloader.loader import Loader
 
 _BOX_WIDTH = 60
Index: src/sas/perspectives/calculator/data_editor.py
===================================================================
--- src/sas/perspectives/calculator/data_editor.py	(revision ac7be54ccf932e813cbe6e41e05b682a1630a03e)
+++ src/sas/perspectives/calculator/data_editor.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -5,6 +5,6 @@
 from copy import deepcopy
 
-from sas.dataloader.loader import Loader
-from sas.dataloader.data_info import Data2D
+from sas.sascalc.dataloader.loader import Loader
+from sas.sascalc.dataloader.data_info import Data2D
 from detector_editor import DetectorDialog
 from collimation_editor import CollimationDialog
Index: src/sas/perspectives/calculator/data_operator.py
===================================================================
--- src/sas/perspectives/calculator/data_operator.py	(revision 3db44fbfe87ababed011758181e38e2806c5aac2)
+++ src/sas/perspectives/calculator/data_operator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,5 +6,5 @@
 import time
 import numpy
-from sas.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.data_info import Data1D
 from sas.plottools.PlotPanel import PlotPanel
 from sas.plottools.plottables import Graph
Index: src/sas/perspectives/calculator/detector_editor.py
===================================================================
--- src/sas/perspectives/calculator/detector_editor.py	(revision 49ab5d718dd7743a6a296e666dfb643183bc969d)
+++ src/sas/perspectives/calculator/detector_editor.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -3,5 +3,5 @@
 import sys
 from copy import deepcopy
-from sas.dataloader.data_info import Detector
+from sas.sascalc.dataloader.data_info import Detector
 from sas.guiframe.utils import check_float
 
Index: src/sas/perspectives/calculator/gen_scatter_panel.py
===================================================================
--- src/sas/perspectives/calculator/gen_scatter_panel.py	(revision 3db44fbfe87ababed011758181e38e2806c5aac2)
+++ src/sas/perspectives/calculator/gen_scatter_panel.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -20,14 +20,14 @@
 
 #from sas.guiframe.gui_manager import MDIFrame
-from sas.data_util.calcthread import CalcThread
+from sas.sascalc.data_util.calcthread import CalcThread
 from sas.guiframe.local_perspectives.plotting.SimplePlot import PlotFrame
 from sas.guiframe.dataFitting import Data2D
 from sas.guiframe.dataFitting import Data1D
-from sas.dataloader.data_info import Detector
-from sas.dataloader.data_info import Source
+from sas.sascalc.dataloader.data_info import Detector
+from sas.sascalc.dataloader.data_info import Source
 from sas.guiframe.panel_base import PanelBase
 from sas.guiframe.utils import format_number
 from sas.guiframe.events import StatusEvent
-from sas.calculator import sas_gen
+from sas.sascalc.calculator import sas_gen
 from sas.perspectives.calculator.calculator_widgets import OutputTextCtrl
 from sas.perspectives.calculator.calculator_widgets import InputTextCtrl
Index: src/sas/perspectives/calculator/image_viewer.py
===================================================================
--- src/sas/perspectives/calculator/image_viewer.py	(revision 3db44fbfe87ababed011758181e38e2806c5aac2)
+++ src/sas/perspectives/calculator/image_viewer.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -13,7 +13,7 @@
 from sas.guiframe.events import StatusEvent
 from sas.perspectives.calculator.calculator_widgets import InputTextCtrl
-from sas.dataloader.data_info import Data2D
-from sas.dataloader.data_info import Detector
-from sas.dataloader.manipulations import reader2D_converter
+from sas.sascalc.dataloader.data_info import Data2D
+from sas.sascalc.dataloader.data_info import Detector
+from sas.sascalc.dataloader.manipulations import reader2D_converter
 from sas.guiframe.documentation_window import DocumentationWindow
 
Index: src/sas/perspectives/calculator/kiessig_calculator_panel.py
===================================================================
--- src/sas/perspectives/calculator/kiessig_calculator_panel.py	(revision 3db44fbfe87ababed011758181e38e2806c5aac2)
+++ src/sas/perspectives/calculator/kiessig_calculator_panel.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -13,5 +13,5 @@
 
 from sas.guiframe.panel_base import PanelBase
-from sas.calculator.kiessig_calculator import KiessigThicknessCalculator
+from sas.sascalc.calculator.kiessig_calculator import KiessigThicknessCalculator
 from calculator_widgets import OutputTextCtrl
 from calculator_widgets import InputTextCtrl
Index: src/sas/perspectives/calculator/load_thread.py
===================================================================
--- src/sas/perspectives/calculator/load_thread.py	(revision 49ab5d718dd7743a6a296e666dfb643183bc969d)
+++ src/sas/perspectives/calculator/load_thread.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -3,6 +3,6 @@
 """
 import time
-from sas.data_util.calcthread import CalcThread
-from sas.dataloader.loader import Loader
+from sas.sascalc.data_util.calcthread import CalcThread
+from sas.sascalc.dataloader.loader import Loader
 
 class DataReader(CalcThread):
Index: src/sas/perspectives/calculator/resolcal_thread.py
===================================================================
--- src/sas/perspectives/calculator/resolcal_thread.py	(revision 49ab5d718dd7743a6a296e666dfb643183bc969d)
+++ src/sas/perspectives/calculator/resolcal_thread.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -3,5 +3,5 @@
 """
 import time
-from sas.data_util.calcthread import CalcThread
+from sas.sascalc.data_util.calcthread import CalcThread
 
 class CalcRes(CalcThread):
Index: src/sas/perspectives/calculator/resolution_calculator_panel.py
===================================================================
--- src/sas/perspectives/calculator/resolution_calculator_panel.py	(revision 3db44fbfe87ababed011758181e38e2806c5aac2)
+++ src/sas/perspectives/calculator/resolution_calculator_panel.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -26,5 +26,5 @@
 
 #from sas.guicomm.events import StatusEvent
-from sas.calculator.resolution_calculator import ResolutionCalculator
+from sas.sascalc.calculator.resolution_calculator import ResolutionCalculator
 from sas.guiframe.events import StatusEvent
 from sas.perspectives.calculator.calculator_widgets import OutputTextCtrl
Index: src/sas/perspectives/calculator/sample_editor.py
===================================================================
--- src/sas/perspectives/calculator/sample_editor.py	(revision 49ab5d718dd7743a6a296e666dfb643183bc969d)
+++ src/sas/perspectives/calculator/sample_editor.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -546,5 +546,5 @@
 if __name__ == "__main__":
     app = wx.App()
-    from sas.dataloader.data_info import Sample
+    from sas.sascalc.dataloader.data_info import Sample
     sample = Sample()
     dlg = SampleDialog(sample=sample)
Index: src/sas/perspectives/calculator/slit_length_calculator_panel.py
===================================================================
--- src/sas/perspectives/calculator/slit_length_calculator_panel.py	(revision 3db44fbfe87ababed011758181e38e2806c5aac2)
+++ src/sas/perspectives/calculator/slit_length_calculator_panel.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -16,5 +16,5 @@
 
 from sas.guiframe.events import StatusEvent
-from sas.calculator.slit_length_calculator import SlitlengthCalculator
+from sas.sascalc.calculator.slit_length_calculator import SlitlengthCalculator
 from calculator_widgets import OutputTextCtrl
 from calculator_widgets import InterActiveOutputTextCtrl
Index: src/sas/perspectives/calculator/source_editor.py
===================================================================
--- src/sas/perspectives/calculator/source_editor.py	(revision 49ab5d718dd7743a6a296e666dfb643183bc969d)
+++ src/sas/perspectives/calculator/source_editor.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -534,5 +534,5 @@
 if __name__ == "__main__":
     app = wx.App()
-    from sas.dataloader.data_info import Source
+    from sas.sascalc.dataloader.data_info import Source
     source = Source()
     dlg = SourceDialog(source=source)
Index: src/sas/perspectives/fitting/basepage.py
===================================================================
--- src/sas/perspectives/fitting/basepage.py	(revision 1c2bf901c33eeec26176d8cf085ab77daf74075b)
+++ src/sas/perspectives/fitting/basepage.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -23,6 +23,6 @@
 from sas.guiframe.dataFitting import check_data_validity
 from sas.guiframe.gui_style import GUIFRAME_ID
-from sas.dataloader.data_info import Detector
-from sas.dataloader.data_info import Source
+from sas.sascalc.dataloader.data_info import Detector
+from sas.sascalc.dataloader.data_info import Source
 from sas.perspectives.fitting.pagestate import PageState
 from sas.guiframe.CategoryInstaller import CategoryInstaller
Index: src/sas/perspectives/fitting/console.py
===================================================================
--- src/sas/perspectives/fitting/console.py	(revision 2f4b43041eaa7b7f496750f223a299cb33cd268e)
+++ src/sas/perspectives/fitting/console.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -5,5 +5,5 @@
 import time
 import wx
-from sas.fit import FitHandler
+from sas.sascalc.fit import FitHandler
 
 class ConsoleUpdate(FitHandler):
Index: src/sas/perspectives/fitting/fit_thread.py
===================================================================
--- src/sas/perspectives/fitting/fit_thread.py	(revision 2f4b43041eaa7b7f496750f223a299cb33cd268e)
+++ src/sas/perspectives/fitting/fit_thread.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -2,5 +2,5 @@
 import sys
 import time
-from sas.data_util.calcthread import CalcThread
+from sas.sascalc.data_util.calcthread import CalcThread
 
 def map_getattr(classInstance, classFunc, *args):
Index: src/sas/perspectives/fitting/fitting.py
===================================================================
--- src/sas/perspectives/fitting/fitting.py	(revision 6f16e25ca8391464dfcea6ba0d48d6b3702d148e)
+++ src/sas/perspectives/fitting/fitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -21,5 +21,5 @@
 import models
 
-from sas.dataloader.loader import Loader
+from sas.sascalc.dataloader.loader import Loader
 from sas.guiframe.dataFitting import Data2D
 from sas.guiframe.dataFitting import Data1D
@@ -32,5 +32,5 @@
 from sas.guiframe.plugin_base import PluginBase
 from sas.guiframe.data_processor import BatchCell
-from sas.fit.BumpsFitting import BumpsFit as Fit
+from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
 from sas.perspectives.fitting.console import ConsoleUpdate
 from sas.perspectives.fitting.fitproblem import FitProblemDictionary
Index: src/sas/perspectives/fitting/model_thread.py
===================================================================
--- src/sas/perspectives/fitting/model_thread.py	(revision a3f125f0f3c4ff6ed01529da503fbdd5ccb23b14)
+++ src/sas/perspectives/fitting/model_thread.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,5 +6,5 @@
 import numpy
 import math
-from sas.data_util.calcthread import CalcThread
+from sas.sascalc.data_util.calcthread import CalcThread
 
 class Calc2D(CalcThread):
Index: src/sas/perspectives/fitting/pagestate.py
===================================================================
--- src/sas/perspectives/fitting/pagestate.py	(revision abcbe097d74401f06e04d41c9fe6612fd97f6dad)
+++ src/sas/perspectives/fitting/pagestate.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -25,12 +25,12 @@
 from lxml import etree
 
-import sas.dataloader
-from sas.dataloader.readers.cansas_reader import Reader as CansasReader
-from sas.dataloader.readers.cansas_reader import get_content, write_node
-from sas.dataloader.data_info import Data2D
-from sas.dataloader.data_info import Collimation
-from sas.dataloader.data_info import Detector
-from sas.dataloader.data_info import Process
-from sas.dataloader.data_info import Aperture
+import sas.sascalc.dataloader
+from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
+from sas.sascalc.dataloader.readers.cansas_reader import get_content, write_node
+from sas.sascalc.dataloader.data_info import Data2D
+from sas.sascalc.dataloader.data_info import Collimation
+from sas.sascalc.dataloader.data_info import Detector
+from sas.sascalc.dataloader.data_info import Process
+from sas.sascalc.dataloader.data_info import Aperture
 #Information to read/write state as xml
 FITTING_NODE_NAME = 'fitting_plug_in'
Index: src/sas/perspectives/invariant/__init__.py
===================================================================
--- src/sas/perspectives/invariant/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/invariant/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -1,6 +1,8 @@
 PLUGIN_ID = "Invariant plug-in 1.0"
 import os
+
+from distutils.filelist import findall
 from invariant import *
-from distutils.filelist import findall
+
 def get_data_path(media):
     """
Index: src/sas/perspectives/invariant/invariant.py
===================================================================
--- src/sas/perspectives/invariant/invariant.py	(revision f21d4969d416d232125ec9af18b6bb0189f68ace)
+++ src/sas/perspectives/invariant/invariant.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -23,5 +23,5 @@
 from sas.guiframe.gui_style import GUIFRAME_ID
 from sas.perspectives.invariant.invariant_state import Reader as reader
-from sas.dataloader.loader import Loader
+from sas.sascalc.dataloader.loader import Loader
 from sas.perspectives.invariant.invariant_panel import InvariantPanel
 from sas.guiframe.plugin_base import PluginBase
Index: src/sas/perspectives/invariant/invariant_panel.py
===================================================================
--- src/sas/perspectives/invariant/invariant_panel.py	(revision 0e33a8d85cb72949d5570ad7e0ae62153a71b845)
+++ src/sas/perspectives/invariant/invariant_panel.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -9,6 +9,8 @@
 import wx
 import logging
+
 from wx.lib.scrolledpanel import ScrolledPanel
-from sas.invariant import invariant
+from sas.sascalc.invariant import invariant
+
 from sas.guiframe.utils import format_number
 from sas.guiframe.utils import check_float
@@ -1922,5 +1924,5 @@
         wx.Frame.__init__(self, parent, id, title, size=(PANEL_WIDTH + 100,
                                                          PANEL_HEIGHT + 100))
-        from sas.dataloader.loader import  Loader
+        from sas.sascalc.dataloader.loader import  Loader
         self.loader = Loader()
         path = "C:/ECLPS/workspace/trunk/sasdataloader/test/ascii_test_3.txt"
Index: src/sas/perspectives/invariant/invariant_state.py
===================================================================
--- src/sas/perspectives/invariant/invariant_state.py	(revision 78a205ad60ae50caaa91008f0d11b7bb5353d84d)
+++ src/sas/perspectives/invariant/invariant_state.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -8,9 +8,9 @@
 import logging
 import copy
-import sas.dataloader
+import sas.sascalc.dataloader
 # from xml.dom.minidom import parse
 from lxml import etree
-from sas.dataloader.readers.cansas_reader import Reader as CansasReader
-from sas.dataloader.readers.cansas_reader import get_content
+from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
+from sas.sascalc.dataloader.readers.cansas_reader import get_content
 from sas.guiframe.utils import format_number
 from sas.guiframe.gui_style import GUIFRAME_ID
Index: src/sas/perspectives/pr/explore_dialog.py
===================================================================
--- src/sas/perspectives/pr/explore_dialog.py	(revision 3d250da3ae17ea6190c69794c598422f07134904)
+++ src/sas/perspectives/pr/explore_dialog.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -150,5 +150,5 @@
         Initialization. The parameters added to Dialog are:
 
-        :param pr_state: sas.pr.invertor.Invertor object
+        :param pr_state: sas.sascalc.pr.invertor.Invertor object
         :param nfunc: Number of terms in the expansion
 
Index: src/sas/perspectives/pr/inversion_panel.py
===================================================================
--- src/sas/perspectives/pr/inversion_panel.py	(revision f21d4969d416d232125ec9af18b6bb0189f68ace)
+++ src/sas/perspectives/pr/inversion_panel.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -966,5 +966,5 @@
     """
     def __init__(self, parent, id):
-        from sas.pr.invertor import help
+        from sas.sascalc.pr.invertor import help
         wx.Dialog.__init__(self, parent, id, size=(250, 120))
         self.SetTitle("P(r) distribution")
Index: src/sas/perspectives/pr/inversion_state.py
===================================================================
--- src/sas/perspectives/pr/inversion_state.py	(revision 8b21fa7e884f48449cda4fc02c4ab3b6567f4795)
+++ src/sas/perspectives/pr/inversion_state.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -19,6 +19,6 @@
 from lxml import etree
 from sas.guiframe.dataFitting import Data1D
-from sas.dataloader.readers.cansas_reader import Reader as CansasReader
-from sas.dataloader.readers.cansas_reader import get_content
+from sas.sascalc.dataloader.readers.cansas_reader import Reader as CansasReader
+from sas.sascalc.dataloader.readers.cansas_reader import get_content
 
 PRNODE_NAME = 'pr_inversion'
Index: src/sas/perspectives/pr/pr.py
===================================================================
--- src/sas/perspectives/pr/pr.py	(revision f21d4969d416d232125ec9af18b6bb0189f68ace)
+++ src/sas/perspectives/pr/pr.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -28,7 +28,7 @@
 from sas.guiframe.events import StatusEvent
 from sas.guiframe.gui_style import GUIFRAME_ID
-from sas.pr.invertor import Invertor
-from sas.dataloader.loader import Loader
-import sas.dataloader
+from sas.sascalc.pr.invertor import Invertor
+from sas.sascalc.dataloader.loader import Loader
+import sas.sascalc.dataloader
 
 from pr_widgets import load_error
Index: src/sas/perspectives/pr/pr_thread.py
===================================================================
--- src/sas/perspectives/pr/pr_thread.py	(revision 8b21fa7e884f48449cda4fc02c4ab3b6567f4795)
+++ src/sas/perspectives/pr/pr_thread.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -12,5 +12,5 @@
 import sys
 import time
-from sas.data_util.calcthread import CalcThread
+from sas.sascalc.data_util.calcthread import CalcThread
 
 class CalcPr(CalcThread):
Index: src/sas/perspectives/simulation/simulation.py
===================================================================
--- src/sas/perspectives/simulation/simulation.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ src/sas/perspectives/simulation/simulation.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -22,5 +22,5 @@
 import sas.realspace.VolumeCanvas as VolumeCanvas
 
-from sas.data_util.calcthread import CalcThread
+from sas.sascalc.data_util.calcthread import CalcThread
 from sas.guicomm.events import NewPlotEvent, StatusEvent    
 
Index: c/sas/pr/Doxyfile
===================================================================
--- src/sas/pr/Doxyfile	(revision b9a5f0eb8e8cbff8f8d934bbfa4ea18168141e13)
+++ 	(revision )
@@ -1,263 +1,0 @@
-# Doxyfile 1.5.1-p1
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
-PROJECT_NAME           = saspr
-PROJECT_NUMBER         = 
-OUTPUT_DIRECTORY       = doc
-CREATE_SUBDIRS         = NO
-OUTPUT_LANGUAGE        = English
-USE_WINDOWS_ENCODING   = NO
-BRIEF_MEMBER_DESC      = YES
-REPEAT_BRIEF           = YES
-ABBREVIATE_BRIEF       = "The $name class" \
-                         "The $name widget" \
-                         "The $name file" \
-                         is \
-                         provides \
-                         specifies \
-                         contains \
-                         represents \
-                         a \
-                         an \
-                         the
-ALWAYS_DETAILED_SEC    = NO
-INLINE_INHERITED_MEMB  = NO
-FULL_PATH_NAMES        = YES
-STRIP_FROM_PATH        = 
-STRIP_FROM_INC_PATH    = 
-SHORT_NAMES            = NO
-JAVADOC_AUTOBRIEF      = NO
-MULTILINE_CPP_IS_BRIEF = NO
-DETAILS_AT_TOP         = NO
-INHERIT_DOCS           = YES
-SEPARATE_MEMBER_PAGES  = NO
-TAB_SIZE               = 4
-ALIASES                = 
-OPTIMIZE_OUTPUT_FOR_C  = NO
-OPTIMIZE_OUTPUT_JAVA   = YES
-BUILTIN_STL_SUPPORT    = NO
-DISTRIBUTE_GROUP_DOC   = NO
-SUBGROUPING            = YES
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
-EXTRACT_ALL            = NO
-EXTRACT_PRIVATE        = NO
-EXTRACT_STATIC         = NO
-EXTRACT_LOCAL_CLASSES  = YES
-EXTRACT_LOCAL_METHODS  = NO
-HIDE_UNDOC_MEMBERS     = NO
-HIDE_UNDOC_CLASSES     = NO
-HIDE_FRIEND_COMPOUNDS  = NO
-HIDE_IN_BODY_DOCS      = NO
-INTERNAL_DOCS          = NO
-CASE_SENSE_NAMES       = YES
-HIDE_SCOPE_NAMES       = NO
-SHOW_INCLUDE_FILES     = YES
-INLINE_INFO            = YES
-SORT_MEMBER_DOCS       = YES
-SORT_BRIEF_DOCS        = NO
-SORT_BY_SCOPE_NAME     = NO
-GENERATE_TODOLIST      = YES
-GENERATE_TESTLIST      = YES
-GENERATE_BUGLIST       = YES
-GENERATE_DEPRECATEDLIST= YES
-ENABLED_SECTIONS       = 
-MAX_INITIALIZER_LINES  = 30
-SHOW_USED_FILES        = YES
-SHOW_DIRECTORIES       = YES
-FILE_VERSION_FILTER    = 
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
-QUIET                  = NO
-WARNINGS               = YES
-WARN_IF_UNDOCUMENTED   = YES
-WARN_IF_DOC_ERROR      = YES
-WARN_NO_PARAMDOC       = NO
-WARN_FORMAT            = "$file:$line: $text"
-WARN_LOGFILE           = 
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
-INPUT                  = .
-FILE_PATTERNS          = *.c \
-                         *.cc \
-                         *.cxx \
-                         *.cpp \
-                         *.c++ \
-                         *.d \
-                         *.java \
-                         *.ii \
-                         *.ixx \
-                         *.ipp \
-                         *.i++ \
-                         *.inl \
-                         *.h \
-                         *.hh \
-                         *.hxx \
-                         *.hpp \
-                         *.h++ \
-                         *.idl \
-                         *.odl \
-                         *.cs \
-                         *.php \
-                         *.php3 \
-                         *.inc \
-                         *.m \
-                         *.mm \
-                         *.dox \
-                         *.py
-RECURSIVE              = YES
-EXCLUDE                = build \
-                         dist \
-                         test
-EXCLUDE_SYMLINKS       = NO
-EXCLUDE_PATTERNS       = 
-EXAMPLE_PATH           = 
-EXAMPLE_PATTERNS       = *
-EXAMPLE_RECURSIVE      = NO
-IMAGE_PATH             = doc
-INPUT_FILTER           = 
-FILTER_PATTERNS        = 
-FILTER_SOURCE_FILES    = NO
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
-SOURCE_BROWSER         = NO
-INLINE_SOURCES         = NO
-STRIP_CODE_COMMENTS    = YES
-REFERENCED_BY_RELATION = NO
-REFERENCES_RELATION    = NO
-REFERENCES_LINK_SOURCE = YES
-USE_HTAGS              = NO
-VERBATIM_HEADERS       = YES
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
-ALPHABETICAL_INDEX     = NO
-COLS_IN_ALPHA_INDEX    = 5
-IGNORE_PREFIX          = 
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
-GENERATE_HTML          = YES
-HTML_OUTPUT            = html
-HTML_FILE_EXTENSION    = .html
-HTML_HEADER            = 
-HTML_FOOTER            = 
-HTML_STYLESHEET        = 
-HTML_ALIGN_MEMBERS     = YES
-GENERATE_HTMLHELP      = NO
-CHM_FILE               = 
-HHC_LOCATION           = 
-GENERATE_CHI           = NO
-BINARY_TOC             = NO
-TOC_EXPAND             = NO
-DISABLE_INDEX          = NO
-ENUM_VALUES_PER_LINE   = 4
-GENERATE_TREEVIEW      = NO
-TREEVIEW_WIDTH         = 250
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
-GENERATE_LATEX         = NO
-LATEX_OUTPUT           = latex
-LATEX_CMD_NAME         = latex
-MAKEINDEX_CMD_NAME     = makeindex
-COMPACT_LATEX          = NO
-PAPER_TYPE             = a4wide
-EXTRA_PACKAGES         = 
-LATEX_HEADER           = 
-PDF_HYPERLINKS         = NO
-USE_PDFLATEX           = YES
-LATEX_BATCHMODE        = NO
-LATEX_HIDE_INDICES     = NO
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
-GENERATE_RTF           = NO
-RTF_OUTPUT             = rtf
-COMPACT_RTF            = NO
-RTF_HYPERLINKS         = NO
-RTF_STYLESHEET_FILE    = 
-RTF_EXTENSIONS_FILE    = 
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
-GENERATE_MAN           = NO
-MAN_OUTPUT             = man
-MAN_EXTENSION          = .3
-MAN_LINKS              = NO
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
-GENERATE_XML           = NO
-XML_OUTPUT             = xml
-XML_SCHEMA             = 
-XML_DTD                = 
-XML_PROGRAMLISTING     = YES
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
-GENERATE_AUTOGEN_DEF   = NO
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
-GENERATE_PERLMOD       = NO
-PERLMOD_LATEX          = NO
-PERLMOD_PRETTY         = YES
-PERLMOD_MAKEVAR_PREFIX = 
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
-#---------------------------------------------------------------------------
-ENABLE_PREPROCESSING   = YES
-MACRO_EXPANSION        = NO
-EXPAND_ONLY_PREDEF     = NO
-SEARCH_INCLUDES        = YES
-INCLUDE_PATH           = 
-INCLUDE_FILE_PATTERNS  = 
-PREDEFINED             = 
-EXPAND_AS_DEFINED      = 
-SKIP_FUNCTION_MACROS   = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references   
-#---------------------------------------------------------------------------
-TAGFILES               = 
-GENERATE_TAGFILE       = 
-ALLEXTERNALS           = NO
-EXTERNAL_GROUPS        = YES
-PERL_PATH              = /usr/bin/perl
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
-#---------------------------------------------------------------------------
-CLASS_DIAGRAMS         = YES
-HIDE_UNDOC_RELATIONS   = YES
-HAVE_DOT               = YES
-CLASS_GRAPH            = YES
-COLLABORATION_GRAPH    = NO
-GROUP_GRAPHS           = YES
-UML_LOOK               = YES
-TEMPLATE_RELATIONS     = YES
-INCLUDE_GRAPH          = YES
-INCLUDED_BY_GRAPH      = YES
-CALL_GRAPH             = YES
-CALLER_GRAPH           = YES
-GRAPHICAL_HIERARCHY    = YES
-DIRECTORY_GRAPH        = YES
-DOT_IMAGE_FORMAT       = png
-DOT_PATH               = 
-DOTFILE_DIRS           = 
-MAX_DOT_GRAPH_WIDTH    = 1024
-MAX_DOT_GRAPH_HEIGHT   = 1024
-MAX_DOT_GRAPH_DEPTH    = 1000
-DOT_TRANSPARENT        = NO
-DOT_MULTI_TARGETS      = NO
-GENERATE_LEGEND        = YES
-DOT_CLEANUP            = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine   
-#---------------------------------------------------------------------------
-SEARCHENGINE           = NO
Index: c/sas/pr/__init__.py
===================================================================
--- src/sas/pr/__init__.py	(revision b9a5f0eb8e8cbff8f8d934bbfa4ea18168141e13)
+++ 	(revision )
@@ -1,106 +1,0 @@
-"""
-    P(r) inversion for SAS
-"""
-## \mainpage P(r) inversion for SAS
-#
-# \section intro_sec Introduction
-# This module provides calculations to transform scattering intensity data 
-# I(q) into distance distribution function P(r). A description of the 
-# technique can be found elsewhere [1-5]. The module is useable as a 
-# standalone application but its functionality is meant to be presented 
-# to end-users through the user interface developed as part of the SAS 
-# flagship application. 
-#
-# Procedure: We will follow the procedure of Moore [1].
-# 
-# [1] P.B. Moore, J.Appl. Cryst (1980) 13, 168-175.
-#
-# [2] O. Glatter, J.Appl. Cryst (1977) 10, 415-421.
-#
-# [3] D.I. Svergun, J.Appl. Cryst (1991) 24, 485-492.
-#
-# [4] D.I. Svergun, J.Appl. Cryst (1992) 25, 495-503.
-#
-# [5] S. Hansen and J. Skov Pedersen, J.Appl. Cryst (1991) 24, 541-548.
-#
-## \subsection class Class Diagram:
-# The following shows a partial class diagram with the main attributes
-# and methods of the invertor.
-#
-# \image html architecture.png
-#
-# \section install_sec Installation
-#
-# \subsection obtain Obtaining the Code
-#
-# The code is available here:
-# \verbatim
-#$ svn co svn://danse.us/sas/pr_inversion
-# \endverbatim
-#
-# \subsection depends External Dependencies
-# scipy, numpy
-#
-# \subsection build Building the code
-# The standard python package can be built with distutils.
-# \verbatim
-#$ python setup.py build
-#$ python setup.py install
-# \endverbatim
-#
-#
-# \subsection Tutorial
-# To create an inversion object:
-# \verbatim
-#from sas.pr.invertor import Invertor
-#    invertor = Invertor()
-# \endverbatim
-#
-# To set the maximum distance between any two points:
-# \verbatim
-#    invertor.d_max = 160.0
-# \endverbatim
-#
-# To set the regularization constant:
-# \verbatim
-#    invertor.alpha = 0.0007
-# \endverbatim
-#
-# To set the q, I(q) and error on I(q):
-# \verbatim
-#    invertor.x = q_vector
-#    invertor.y = Iq_vector
-#    invertor.err = dIq_vector
-# \endverbatim
-#
-# To perform the inversion. In this example, we choose
-# a P(r) expension wit 10 base functions.
-# \verbatim
-#    c_out, c_cov = invertor.invert(10)
-# \endverbatim
-# The c_out and c_cov are the set of coefficients and the covariance 
-# matrix for those coefficients, respectively.
-#
-# To get P(r):
-# \verbatim
-#    r = 10.0
-#    pr = invertor.pr(c_out, r)
-# \endverbatim
-# Alternatively, one can get P(r) with the error on P(r):
-# \verbatim
-#    r = 10.0
-#    pr, dpr = invertor.pr_err(c_out, c_cov, r)
-# \endverbatim
-#
-# To get the output I(q) from the set of coefficients found:
-# \verbatim
-#    q = 0.001
-#    iq = invertor.iq(c_out, q)
-# \endverbatim
-#
-# Examples are available as unit tests under sas.pr_inversion.test.
-#
-# \section help_sec Contact Info
-# Code and Documentation produced as part of the DANSE project.
-
-__author__ = 'University of Tennessee'
Index: c/sas/pr/c_extensions/Cinvertor.c
===================================================================
--- src/sas/pr/c_extensions/Cinvertor.c	(revision b9a5f0eb8e8cbff8f8d934bbfa4ea18168141e13)
+++ 	(revision )
@@ -1,1133 +1,0 @@
-/**
- * C implementation of the P(r) inversion
- * Cinvertor is the base class for the Invertor class
- * and provides the underlying computations.
- *
- */
-#include <Python.h>
-#include "structmember.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <time.h>
-
-#include "invertor.h"
-
-
-/// Error object for raised exceptions
-PyObject * CinvertorError;
-
-#define INVECTOR(obj,buf,len)										\
-    do { \
-        int err = PyObject_AsReadBuffer(obj, (const void **)(&buf), &len); \
-        if (err < 0) return NULL; \
-        len /= sizeof(*buf); \
-    } while (0)
-
-#define OUTVECTOR(obj,buf,len) \
-    do { \
-        int err = PyObject_AsWriteBuffer(obj, (void **)(&buf), &len); \
-        if (err < 0) return NULL; \
-        len /= sizeof(*buf); \
-    } while (0)
-
-
-// Class definition
-/**
- * C implementation of the P(r) inversion
- * Cinvertor is the base class for the Invertor class
- * and provides the underlying computations.
- *
- */
-typedef struct {
-    PyObject_HEAD
-    /// Internal data structure
-    Invertor_params params;
-} Cinvertor;
-
-
-static void
-Cinvertor_dealloc(Cinvertor* self)
-{
-    invertor_dealloc(&(self->params));
-
-    self->ob_type->tp_free((PyObject*)self);
-
-}
-
-static PyObject *
-Cinvertor_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
-    Cinvertor *self;
-
-    self = (Cinvertor *)type->tp_alloc(type, 0);
-
-    return (PyObject *)self;
-}
-
-static int
-Cinvertor_init(Cinvertor *self, PyObject *args, PyObject *kwds)
-{
-    if (self != NULL) {
-    	// Create parameters
-    	invertor_init(&(self->params));
-    }
-    return 0;
-}
-
-static PyMemberDef Cinvertor_members[] = {
-    //{"params", T_OBJECT, offsetof(Cinvertor, params), 0,
-    // "Parameters"},
-    {NULL}  /* Sentinel */
-};
-
-const char set_x_doc[] =
-	"Function to set the x data\n"
-	"Takes an array of doubles as input.\n"
-	" @return: number of entries found";
-
-/**
- * Function to set the x data
- * Takes an array of doubles as input
- * Returns the number of entries found
- */
-static PyObject * set_x(Cinvertor *self, PyObject *args) {
-	PyObject *data_obj;
-	Py_ssize_t ndata;
-	double *data;
-	int i;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj,data,ndata);
-
-	free(self->params.x);
-	self->params.x = (double*) malloc(ndata*sizeof(double));
-
-	if(self->params.x==NULL) {
-	    PyErr_SetString(CinvertorError,
-	    	"Cinvertor.set_x: problem allocating memory.");
-		return NULL;
-	}
-
-	for (i=0; i<ndata; i++) {
-		self->params.x[i] = data[i];
-	}
-
-	//self->params.x = data;
-	self->params.npoints = ndata;
-	return Py_BuildValue("i", self->params.npoints);
-}
-
-const char get_x_doc[] =
-	"Function to get the x data\n"
-	"Takes an array of doubles as input.\n"
-	" @return: number of entries found";
-
-static PyObject * get_x(Cinvertor *self, PyObject *args) {
-	PyObject *data_obj;
-	Py_ssize_t ndata;
-	double *data;
-    int i;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj, data, ndata);
-
-	// Check that the input array is large enough
-	if (ndata < self->params.npoints) {
-	    PyErr_SetString(CinvertorError,
-	    	"Cinvertor.get_x: input array too short for data.");
-		return NULL;
-	}
-
-	for(i=0; i<self->params.npoints; i++){
-		data[i] = self->params.x[i];
-	}
-
-	return Py_BuildValue("i", self->params.npoints);
-}
-
-const char set_y_doc[] =
-	"Function to set the y data\n"
-	"Takes an array of doubles as input.\n"
-	" @return: number of entries found";
-
-/**
- * Function to set the y data
- * Takes an array of doubles as input
- * Returns the number of entries found
- */
-static PyObject * set_y(Cinvertor *self, PyObject *args) {
-	PyObject *data_obj;
-	Py_ssize_t ndata;
-	double *data;
-	int i;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj,data,ndata);
-
-	free(self->params.y);
-	self->params.y = (double*) malloc(ndata*sizeof(double));
-
-	if(self->params.y==NULL) {
-	    PyErr_SetString(CinvertorError,
-	    	"Cinvertor.set_y: problem allocating memory.");
-		return NULL;
-	}
-
-	for (i=0; i<ndata; i++) {
-		self->params.y[i] = data[i];
-	}
-
-	//self->params.y = data;
-	self->params.ny = ndata;
-	return Py_BuildValue("i", self->params.ny);
-}
-
-const char get_y_doc[] =
-	"Function to get the y data\n"
-	"Takes an array of doubles as input.\n"
-	" @return: number of entries found";
-
-static PyObject * get_y(Cinvertor *self, PyObject *args) {
-	PyObject *data_obj;
-	Py_ssize_t ndata;
-	double *data;
-    int i;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj, data, ndata);
-
-	// Check that the input array is large enough
-	if (ndata < self->params.ny) {
-	    PyErr_SetString(CinvertorError,
-	    	"Cinvertor.get_y: input array too short for data.");
-		return NULL;
-	}
-
-	for(i=0; i<self->params.ny; i++){
-		data[i] = self->params.y[i];
-	}
-
-	return Py_BuildValue("i", self->params.npoints);
-}
-
-const char set_err_doc[] =
-	"Function to set the err data\n"
-	"Takes an array of doubles as input.\n"
-	" @return: number of entries found";
-
-/**
- * Function to set the x data
- * Takes an array of doubles as input
- * Returns the number of entries found
- */
-static PyObject * set_err(Cinvertor *self, PyObject *args) {
-	PyObject *data_obj;
-	Py_ssize_t ndata;
-	double *data;
-	int i;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj,data,ndata);
-
-	free(self->params.err);
-	self->params.err = (double*) malloc(ndata*sizeof(double));
-
-	if(self->params.err==NULL) {
-	    PyErr_SetString(CinvertorError,
-	    	"Cinvertor.set_err: problem allocating memory.");
-		return NULL;
-	}
-
-	for (i=0; i<ndata; i++) {
-		self->params.err[i] = data[i];
-	}
-
-	//self->params.err = data;
-	self->params.nerr = ndata;
-	return Py_BuildValue("i", self->params.nerr);
-}
-
-const char get_err_doc[] =
-	"Function to get the err data\n"
-	"Takes an array of doubles as input.\n"
-	" @return: number of entries found";
-
-static PyObject * get_err(Cinvertor *self, PyObject *args) {
-	PyObject *data_obj;
-	Py_ssize_t ndata;
-	double *data;
-    int i;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj, data, ndata);
-
-	// Check that the input array is large enough
-	if (ndata < self->params.nerr) {
-	    PyErr_SetString(CinvertorError,
-	    	"Cinvertor.get_err: input array too short for data.");
-		return NULL;
-	}
-
-	for(i=0; i<self->params.nerr; i++){
-		data[i] = self->params.err[i];
-	}
-
-	return Py_BuildValue("i", self->params.npoints);
-}
-
-const char is_valid_doc[] =
-	"Check the validity of the stored data\n"
-	" @return: Returns the number of points if it's all good, -1 otherwise";
-
-/**
- * Check the validity of the stored data
- * Returns the number of points if it's all good, -1 otherwise
- */
-static PyObject * is_valid(Cinvertor *self, PyObject *args) {
-	if(self->params.npoints==self->params.ny &&
-			self->params.npoints==self->params.nerr) {
-		return Py_BuildValue("i", self->params.npoints);
-	} else {
-		return Py_BuildValue("i", -1);
-	}
-}
-
-const char set_has_bck_doc[] =
-	"Sets background flag\n";
-
-/**
- * Sets the maximum distance
- */
-static PyObject * set_has_bck(Cinvertor *self, PyObject *args) {
-	int has_bck;
-
-	if (!PyArg_ParseTuple(args, "i", &has_bck)) return NULL;
-	self->params.has_bck = has_bck;
-	return Py_BuildValue("i", self->params.has_bck);
-}
-
-const char get_has_bck_doc[] =
-	"Gets background flag\n";
-
-/**
- * Gets the maximum distance
- */
-static PyObject * get_has_bck(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("i", self->params.has_bck);
-}
-
-const char set_dmax_doc[] =
-	"Sets the maximum distance\n";
-
-/**
- * Sets the maximum distance
- */
-static PyObject * set_dmax(Cinvertor *self, PyObject *args) {
-	double d_max;
-
-	if (!PyArg_ParseTuple(args, "d", &d_max)) return NULL;
-	self->params.d_max = d_max;
-	return Py_BuildValue("d", self->params.d_max);
-}
-
-const char get_dmax_doc[] =
-	"Gets the maximum distance\n";
-
-/**
- * Gets the maximum distance
- */
-static PyObject * get_dmax(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("d", self->params.d_max);
-}
-
-const char set_slit_height_doc[] =
-	"Sets the slit height in units of q [A-1]\n";
-
-/**
- * Sets the slit height
- */
-static PyObject * set_slit_height(Cinvertor *self, PyObject *args) {
-	double slit_height;
-
-	if (!PyArg_ParseTuple(args, "d", &slit_height)) return NULL;
-	self->params.slit_height = slit_height;
-	return Py_BuildValue("d", self->params.slit_height);
-}
-
-const char get_slit_height_doc[] =
-	"Gets the slit height\n";
-
-/**
- * Gets the slit height
- */
-static PyObject * get_slit_height(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("d", self->params.slit_height);
-}
-
-const char set_slit_width_doc[] =
-	"Sets the slit width in units of q [A-1]\n";
-
-/**
- * Sets the slit width
- */
-static PyObject * set_slit_width(Cinvertor *self, PyObject *args) {
-	double slit_width;
-
-	if (!PyArg_ParseTuple(args, "d", &slit_width)) return NULL;
-	self->params.slit_width = slit_width;
-	return Py_BuildValue("d", self->params.slit_width);
-}
-
-const char get_slit_width_doc[] =
-	"Gets the slit width\n";
-
-/**
- * Gets the slit width
- */
-static PyObject * get_slit_width(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("d", self->params.slit_width);
-}
-
-
-const char set_qmin_doc[] =
-	"Sets the minimum q\n";
-
-/**
- * Sets the minimum q
- */
-static PyObject * set_qmin(Cinvertor *self, PyObject *args) {
-	double q_min;
-
-	if (!PyArg_ParseTuple(args, "d", &q_min)) return NULL;
-	self->params.q_min = q_min;
-	return Py_BuildValue("d", self->params.q_min);
-}
-
-const char get_qmin_doc[] =
-	"Gets the minimum q\n";
-
-/**
- * Gets the minimum q
- */
-static PyObject * get_qmin(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("d", self->params.q_min);
-}
-
-const char set_qmax_doc[] =
-	"Sets the maximum q\n";
-
-/**
- * Sets the maximum q
- */
-static PyObject * set_qmax(Cinvertor *self, PyObject *args) {
-	double q_max;
-
-	if (!PyArg_ParseTuple(args, "d", &q_max)) return NULL;
-	self->params.q_max = q_max;
-	return Py_BuildValue("d", self->params.q_max);
-}
-
-const char get_qmax_doc[] =
-	"Gets the maximum q\n";
-
-/**
- * Gets the maximum q
- */
-static PyObject * get_qmax(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("d", self->params.q_max);
-}
-
-const char set_alpha_doc[] =
-	"Sets the alpha parameter\n";
-
-static PyObject * set_alpha(Cinvertor *self, PyObject *args) {
-	double alpha;
-
-	if (!PyArg_ParseTuple(args, "d", &alpha)) return NULL;
-	self->params.alpha = alpha;
-	return Py_BuildValue("d", self->params.alpha);
-}
-
-const char get_alpha_doc[] =
-	"Gets the alpha parameter\n";
-
-/**
- * Gets the maximum distance
- */
-static PyObject * get_alpha(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("d", self->params.alpha);
-}
-
-const char get_nx_doc[] =
-	"Gets the number of x points\n";
-
-/**
- * Gets the number of x points
- */
-static PyObject * get_nx(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("i", self->params.npoints);
-}
-
-const char get_ny_doc[] =
-	"Gets the number of y points\n";
-
-/**
- * Gets the number of y points
- */
-static PyObject * get_ny(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("i", self->params.ny);
-}
-
-const char get_nerr_doc[] =
-	"Gets the number of err points\n";
-
-/**
- * Gets the number of error points
- */
-static PyObject * get_nerr(Cinvertor *self, PyObject *args) {
-	return Py_BuildValue("i", self->params.nerr);
-}
-
-
-const char residuals_doc[] =
-	"Function to call to evaluate the residuals\n"
-	"for P(r) inversion\n"
-	" @param args: input parameters\n"
-	" @return: list of residuals";
-
-/**
- * Function to call to evaluate the residuals
- * @param args: input parameters
- * @return: list of residuals
- */
-static PyObject * residuals(Cinvertor *self, PyObject *args) {
-	double *pars;
-	PyObject* residuals;
-	int i;
-	double residual, diff;
-	// Regularization factor
-	double regterm = 0.0;
-	// Number of slices in regularization term estimate
-	int nslice = 25;
-
-	PyObject *data_obj;
-	Py_ssize_t npars;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-
-	OUTVECTOR(data_obj,pars,npars);
-
-    // PyList of residuals
-	// Should create this list only once and refill it
-    residuals = PyList_New(self->params.npoints);
-
-    regterm = reg_term(pars, self->params.d_max, npars, nslice);
-
-    for(i=0; i<self->params.npoints; i++) {
-    	diff = self->params.y[i] - iq(pars, self->params.d_max, npars, self->params.x[i]);
-    	residual = diff*diff / (self->params.err[i]*self->params.err[i]);
-
-    	// regularization term
-    	residual += self->params.alpha * regterm;
-
-    	if (PyList_SetItem(residuals, i, Py_BuildValue("d",residual) ) < 0){
-    	    PyErr_SetString(CinvertorError,
-    	    	"Cinvertor.residuals: error setting residual.");
-    		return NULL;
-    	};
-    }
-	return residuals;
-}
-
-const char pr_residuals_doc[] =
-	"Function to call to evaluate the residuals\n"
-	"for P(r) minimization (for testing purposes)\n"
-	" @param args: input parameters\n"
-	" @return: list of residuals";
-
-/**
- * Function to call to evaluate the residuals
- * for P(r) minimization (for testing purposes)
- * @param args: input parameters
- * @return: list of residuals
- */
-static PyObject * pr_residuals(Cinvertor *self, PyObject *args) {
-	double *pars;
-	PyObject* residuals;
-	int i;
-	double residual, diff;
-	// Regularization factor
-	double regterm = 0.0;
-	// Number of slices in regularization term estimate
-	int nslice = 25;
-
-	PyObject *data_obj;
-	Py_ssize_t npars;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-
-	OUTVECTOR(data_obj,pars,npars);
-
-	// Should create this list only once and refill it
-    residuals = PyList_New(self->params.npoints);
-
-    regterm = reg_term(pars, self->params.d_max, npars, nslice);
-
-
-    for(i=0; i<self->params.npoints; i++) {
-    	diff = self->params.y[i] - pr(pars, self->params.d_max, npars, self->params.x[i]);
-    	residual = diff*diff / (self->params.err[i]*self->params.err[i]);
-
-    	// regularization term
-    	residual += self->params.alpha * regterm;
-
-    	if (PyList_SetItem(residuals, i, Py_BuildValue("d",residual) ) < 0){
-    	    PyErr_SetString(CinvertorError,
-    	    	"Cinvertor.residuals: error setting residual.");
-    		return NULL;
-    	};
-    }
-	return residuals;
-}
-
-const char get_iq_doc[] =
-	"Function to call to evaluate the scattering intensity\n"
-	" @param args: c-parameters, and q\n"
-	" @return: I(q)";
-
-/**
- * Function to call to evaluate the scattering intensity
- * @param args: c-parameters, and q
- * @return: I(q)
- */
-static PyObject * get_iq(Cinvertor *self, PyObject *args) {
-	double *pars;
-	double q, iq_value;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-
-	if (!PyArg_ParseTuple(args, "Od", &data_obj, &q)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	iq_value = iq(pars, self->params.d_max, npars, q);
-	return Py_BuildValue("f", iq_value);
-}
-
-const char get_iq_smeared_doc[] =
-	"Function to call to evaluate the scattering intensity.\n"
-	"The scattering intensity is slit-smeared."
-	" @param args: c-parameters, and q\n"
-	" @return: I(q)";
-
-/**
- * Function to call to evaluate the scattering intensity
- * The scattering intensity is slit-smeared.
- * @param args: c-parameters, and q
- * @return: I(q)
- */
-static PyObject * get_iq_smeared(Cinvertor *self, PyObject *args) {
-	double *pars;
-	double q, iq_value;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-
-	if (!PyArg_ParseTuple(args, "Od", &data_obj, &q)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	iq_value = iq_smeared(pars, self->params.d_max, npars,
-							self->params.slit_height, self->params.slit_width,
-							q, 21);
-	return Py_BuildValue("f", iq_value);
-}
-
-const char get_pr_doc[] =
-	"Function to call to evaluate P(r)\n"
-	" @param args: c-parameters and r\n"
-	" @return: P(r)";
-
-/**
- * Function to call to evaluate P(r)
- * @param args: c-parameters and r
- * @return: P(r)
- */
-static PyObject * get_pr(Cinvertor *self, PyObject *args) {
-	double *pars;
-	double r, pr_value;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-
-	if (!PyArg_ParseTuple(args, "Od", &data_obj, &r)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	pr_value = pr(pars, self->params.d_max, npars, r);
-	return Py_BuildValue("f", pr_value);
-}
-
-const char get_pr_err_doc[] =
-	"Function to call to evaluate P(r) with errors\n"
-	" @param args: c-parameters and r\n"
-	" @return: (P(r),dP(r))";
-
-/**
- * Function to call to evaluate P(r) with errors
- * @param args: c-parameters and r
- * @return: P(r)
- */
-static PyObject * get_pr_err(Cinvertor *self, PyObject *args) {
-	double *pars;
-	double *pars_err;
-	double pr_err_value;
-	double r, pr_value;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-	PyObject *err_obj;
-	Py_ssize_t npars2;
-
-	if (!PyArg_ParseTuple(args, "OOd", &data_obj, &err_obj, &r)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	if (err_obj == Py_None) {
-		pr_value = pr(pars, self->params.d_max, npars, r);
-		pr_err_value = 0.0;
-	} else {
-		OUTVECTOR(err_obj,pars_err,npars2);
-		pr_err(pars, pars_err, self->params.d_max, npars, r, &pr_value, &pr_err_value);
-	}
-	return Py_BuildValue("ff", pr_value, pr_err_value);
-}
-
-const char basefunc_ft_doc[] =
-	"Returns the value of the nth Fourier transofrmed base function\n"
-	" @param args: c-parameters, n and q\n"
-	" @return: nth Fourier transformed base function, evaluated at q";
-
-static PyObject * basefunc_ft(Cinvertor *self, PyObject *args) {
-	double d_max, q;
-	int n;
-
-	if (!PyArg_ParseTuple(args, "did", &d_max, &n, &q)) return NULL;
-	return Py_BuildValue("f", ortho_transformed(d_max, n, q));
-
-}
-
-const char oscillations_doc[] =
-	"Returns the value of the oscillation figure of merit for\n"
-	"the given set of coefficients. For a sphere, the oscillation\n"
-	"figure of merit is 1.1.\n"
-	" @param args: c-parameters\n"
-	" @return: oscillation figure of merit";
-
-static PyObject * oscillations(Cinvertor *self, PyObject *args) {
-	double *pars;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-	double oscill, norm;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	oscill = reg_term(pars, self->params.d_max, npars, 100);
-	norm   = int_p2(pars, self->params.d_max, npars, 100);
-	return Py_BuildValue("f", sqrt(oscill/norm)/acos(-1.0)*self->params.d_max );
-
-}
-
-const char get_peaks_doc[] =
-	"Returns the number of peaks in the output P(r) distrubution\n"
-	"for the given set of coefficients.\n"
-	" @param args: c-parameters\n"
-	" @return: number of P(r) peaks";
-
-static PyObject * get_peaks(Cinvertor *self, PyObject *args) {
-	double *pars;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-	int count;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	count = npeaks(pars, self->params.d_max, npars, 100);
-
-	return Py_BuildValue("i", count );
-
-}
-
-const char get_positive_doc[] =
-	"Returns the fraction of P(r) that is positive over\n"
-	"the full range of r for the given set of coefficients.\n"
-	" @param args: c-parameters\n"
-	" @return: fraction of P(r) that is positive";
-
-static PyObject * get_positive(Cinvertor *self, PyObject *args) {
-	double *pars;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-	double fraction;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	fraction = positive_integral(pars, self->params.d_max, npars, 100);
-
-	return Py_BuildValue("f", fraction );
-
-}
-
-const char get_pos_err_doc[] =
-	"Returns the fraction of P(r) that is 1 standard deviation\n"
-	"above zero over the full range of r for the given set of coefficients.\n"
-	" @param args: c-parameters\n"
-	" @return: fraction of P(r) that is positive";
-
-static PyObject * get_pos_err(Cinvertor *self, PyObject *args) {
-	double *pars;
-	double *pars_err;
-	PyObject *data_obj;
-	PyObject *err_obj;
-	Py_ssize_t npars;
-	Py_ssize_t npars2;
-	double fraction;
-
-	if (!PyArg_ParseTuple(args, "OO", &data_obj, &err_obj)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-	OUTVECTOR(err_obj,pars_err,npars2);
-
-	fraction = positive_errors(pars, pars_err, self->params.d_max, npars, 51);
-
-	return Py_BuildValue("f", fraction );
-
-}
-
-const char get_rg_doc[] =
-	"Returns the value of the radius of gyration Rg.\n"
-	" @param args: c-parameters\n"
-	" @return: Rg";
-
-static PyObject * get_rg(Cinvertor *self, PyObject *args) {
-	double *pars;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-	double value;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	value = rg(pars, self->params.d_max, npars, 101);
-
-	return Py_BuildValue("f", value );
-
-}
-
-const char get_iq0_doc[] =
-	"Returns the value of I(q=0).\n"
-	" @param args: c-parameters\n"
-	" @return: I(q=0)";
-
-static PyObject * get_iq0(Cinvertor *self, PyObject *args) {
-	double *pars;
-	PyObject *data_obj;
-	Py_ssize_t npars;
-	double value;
-
-	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
-	OUTVECTOR(data_obj,pars,npars);
-
-	value = 4.0*acos(-1.0)*int_pr(pars, self->params.d_max, npars, 101);
-
-	return Py_BuildValue("f", value );
-
-}
-
-/**
- * Check whether a q-value is within acceptabel limits
- * Return 1 if accepted, 0 if rejected.
- */
-int accept_q(Cinvertor *self, double q) {
-    if (self->params.q_min>0 && q<self->params.q_min) return 0;
-    if (self->params.q_max>0 && q>self->params.q_max) return 0;
-    return 1;
-}
-
-const char get_matrix_doc[] =
-	"Returns A matrix and b vector for least square problem.\n"
-	" @param nfunc: number of base functions\n"
-	" @param nr: number of r-points used when evaluating reg term.\n"
-	" @param a: A array to fill\n"
-	" @param b: b vector to fill\n"
-	" @return: 0";
-
-static PyObject * get_matrix(Cinvertor *self, PyObject *args) {
-	double *a;
-	double *b;
-	PyObject *a_obj;
-	PyObject *b_obj;
-	Py_ssize_t n_a;
-	Py_ssize_t n_b;
-	// Number of bins for regularization term evaluation
-	int nr, nfunc;
-	int i, j, i_r;
-	double r, sqrt_alpha, pi;
-	double tmp;
-	int offset;
-
-	if (!PyArg_ParseTuple(args, "iiOO", &nfunc, &nr, &a_obj, &b_obj)) return NULL;
-	OUTVECTOR(a_obj,a,n_a);
-	OUTVECTOR(b_obj,b,n_b);
-
-	assert(n_b>=nfunc);
-	assert(n_a>=nfunc*(nr+self->params.npoints));
-
-	sqrt_alpha = sqrt(self->params.alpha);
-	pi = acos(-1.0);
-	offset = (self->params.has_bck==1) ? 0 : 1;
-
-    for (j=0; j<nfunc; j++) {
-        for (i=0; i<self->params.npoints; i++) {
-            if (self->params.err[i]==0.0) {
-              PyErr_SetString(CinvertorError,
-                "Cinvertor.get_matrix: Some I(Q) points have no error.");
-              return NULL;
-            }
-            if (accept_q(self, self->params.x[i])){
-                if (self->params.has_bck==1 && j==0) {
-                    a[i*nfunc+j] = 1.0/self->params.err[i];
-                } else {
-                	if (self->params.slit_width>0 || self->params.slit_height>0) {
-                		a[i*nfunc+j] = ortho_transformed_smeared(self->params.d_max,
-                				j+offset, self->params.slit_height, self->params.slit_width,
-                				self->params.x[i], 21)/self->params.err[i];
-                	} else {
-                		a[i*nfunc+j] = ortho_transformed(self->params.d_max, j+offset, self->params.x[i])/self->params.err[i];
-                	}
-            	}
-            }
-        }
-        for (i_r=0; i_r<nr; i_r++){
-            if (self->params.has_bck==1 && j==0) {
-                a[(i_r+self->params.npoints)*nfunc+j] = 0.0;
-            } else {
-	            r = self->params.d_max/nr*i_r;
-	            tmp = pi*(j+offset)/self->params.d_max;
-	            a[(i_r+self->params.npoints)*nfunc+j] = sqrt_alpha * 1.0/nr*self->params.d_max*2.0*
-	            (2.0*pi*(j+offset)/self->params.d_max*cos(pi*(j+offset)*r/self->params.d_max) +
-	            tmp*tmp*r * sin(pi*(j+offset)*r/self->params.d_max));
-	        }
-        }
-    }
-
-    for (i=0; i<self->params.npoints; i++) {
-        if (accept_q(self, self->params.x[i])){
-            b[i] = self->params.y[i]/self->params.err[i];
-         }
-	}
-
-	return Py_BuildValue("i", 0);
-
-}
-
-const char get_invcov_matrix_doc[] =
-	" Compute the inverse covariance matrix, defined as inv_cov = a_transposed x a.\n"
-	" @param nfunc: number of base functions\n"
-	" @param nr: number of r-points used when evaluating reg term.\n"
-	" @param a: A array to fill\n"
-	" @param inv_cov: inverse covariance array to be filled\n"
-	" @return: 0";
-
-static PyObject * get_invcov_matrix(Cinvertor *self, PyObject *args) {
-	double *a;
-	PyObject *a_obj;
-	Py_ssize_t n_a;
-	double *inv_cov;
-	PyObject *cov_obj;
-	Py_ssize_t n_cov;
-	int nr, nfunc;
-	int i, j, k;
-
-	if (!PyArg_ParseTuple(args, "iiOO", &nfunc, &nr, &a_obj, &cov_obj)) return NULL;
-	OUTVECTOR(a_obj,a,n_a);
-	OUTVECTOR(cov_obj,inv_cov,n_cov);
-
-	assert(n_cov>=nfunc*nfunc);
-	assert(n_a>=nfunc*(nr+self->params.npoints));
-
-	for (i=0; i<nfunc; i++) {
-		for (j=0; j<nfunc; j++) {
-			inv_cov[i*nfunc+j] = 0.0;
-			for (k=0; k<nr+self->params.npoints; k++) {
-				inv_cov[i*nfunc+j] += a[k*nfunc+i]*a[k*nfunc+j];
-			}
-		}
-	}
-	return Py_BuildValue("i", 0);
-}
-
-const char get_reg_size_doc[] =
-	" Compute the covariance matrix, defined as inv_cov = a_transposed x a.\n"
-	" @param nfunc: number of base functions\n"
-	" @param nr: number of r-points used when evaluating reg term.\n"
-	" @param a: A array to fill\n"
-	" @param inv_cov: inverse covariance array to be filled\n"
-	" @return: 0";
-
-static PyObject * get_reg_size(Cinvertor *self, PyObject *args) {
-	double *a;
-	PyObject *a_obj;
-	Py_ssize_t n_a;
-	int nr, nfunc;
-	int i, j;
-	double sum_sig, sum_reg;
-
-	if (!PyArg_ParseTuple(args, "iiO", &nfunc, &nr, &a_obj)) return NULL;
-	OUTVECTOR(a_obj,a,n_a);
-
-	assert(n_a>=nfunc*(nr+self->params.npoints));
-
-    sum_sig = 0.0;
-    sum_reg = 0.0;
-    for (j=0; j<nfunc; j++){
-        for (i=0; i<self->params.npoints; i++){
-            if (accept_q(self, self->params.x[i])==1)
-                sum_sig += (a[i*nfunc+j])*(a[i*nfunc+j]);
-        }
-        for (i=0; i<nr; i++){
-            sum_reg += (a[(i+self->params.npoints)*nfunc+j])*(a[(i+self->params.npoints)*nfunc+j]);
-        }
-    }
-    return Py_BuildValue("ff", sum_sig, sum_reg);
-}
-
-const char eeeget_qmin_doc[] = "\
-This is a multiline doc string.\n\
-\n\
-This is the second line.";
-const char eeeset_qmin_doc[] =
-	"This is a multiline doc string.\n"
-	"\n"
-	"This is the second line.";
-
-static PyMethodDef Cinvertor_methods[] = {
-		   {"residuals", (PyCFunction)residuals, METH_VARARGS, residuals_doc},
-		   {"pr_residuals", (PyCFunction)pr_residuals, METH_VARARGS, pr_residuals_doc},
-		   {"set_x", (PyCFunction)set_x, METH_VARARGS, set_x_doc},
-		   {"get_x", (PyCFunction)get_x, METH_VARARGS, get_x_doc},
-		   {"set_y", (PyCFunction)set_y, METH_VARARGS, set_y_doc},
-		   {"get_y", (PyCFunction)get_y, METH_VARARGS, get_y_doc},
-		   {"set_err", (PyCFunction)set_err, METH_VARARGS, set_err_doc},
-		   {"get_err", (PyCFunction)get_err, METH_VARARGS, get_err_doc},
-		   {"set_dmax", (PyCFunction)set_dmax, METH_VARARGS, set_dmax_doc},
-		   {"get_dmax", (PyCFunction)get_dmax, METH_VARARGS, get_dmax_doc},
-		   {"set_qmin", (PyCFunction)set_qmin, METH_VARARGS, set_qmin_doc},
-		   {"get_qmin", (PyCFunction)get_qmin, METH_VARARGS, get_qmin_doc},
-		   {"set_qmax", (PyCFunction)set_qmax, METH_VARARGS, set_qmax_doc},
-		   {"get_qmax", (PyCFunction)get_qmax, METH_VARARGS, get_qmax_doc},
-		   {"set_alpha", (PyCFunction)set_alpha, METH_VARARGS, set_alpha_doc},
-		   {"get_alpha", (PyCFunction)get_alpha, METH_VARARGS, get_alpha_doc},
-		   {"set_slit_width", (PyCFunction)set_slit_width, METH_VARARGS, set_slit_width_doc},
-		   {"get_slit_width", (PyCFunction)get_slit_width, METH_VARARGS, get_slit_width_doc},
-		   {"set_slit_height", (PyCFunction)set_slit_height, METH_VARARGS, set_slit_height_doc},
-		   {"get_slit_height", (PyCFunction)get_slit_height, METH_VARARGS, get_slit_height_doc},
-		   {"set_has_bck", (PyCFunction)set_has_bck, METH_VARARGS, set_has_bck_doc},
-		   {"get_has_bck", (PyCFunction)get_has_bck, METH_VARARGS, get_has_bck_doc},
-		   {"get_nx", (PyCFunction)get_nx, METH_VARARGS, get_nx_doc},
-		   {"get_ny", (PyCFunction)get_ny, METH_VARARGS, get_ny_doc},
-		   {"get_nerr", (PyCFunction)get_nerr, METH_VARARGS, get_nerr_doc},
-		   {"iq", (PyCFunction)get_iq, METH_VARARGS, get_iq_doc},
-		   {"iq_smeared", (PyCFunction)get_iq_smeared, METH_VARARGS, get_iq_smeared_doc},
-		   {"pr", (PyCFunction)get_pr, METH_VARARGS, get_pr_doc},
-		   {"get_pr_err", (PyCFunction)get_pr_err, METH_VARARGS, get_pr_err_doc},
-		   {"is_valid", (PyCFunction)is_valid, METH_VARARGS, is_valid_doc},
-		   {"basefunc_ft", (PyCFunction)basefunc_ft, METH_VARARGS, basefunc_ft_doc},
-		   {"oscillations", (PyCFunction)oscillations, METH_VARARGS, oscillations_doc},
-		   {"get_peaks", (PyCFunction)get_peaks, METH_VARARGS, get_peaks_doc},
-		   {"get_positive", (PyCFunction)get_positive, METH_VARARGS, get_positive_doc},
-		   {"get_pos_err", (PyCFunction)get_pos_err, METH_VARARGS, get_pos_err_doc},
-		   {"rg", (PyCFunction)get_rg, METH_VARARGS, get_rg_doc},
-		   {"iq0", (PyCFunction)get_iq0, METH_VARARGS, get_iq0_doc},
-		   {"_get_matrix", (PyCFunction)get_matrix, METH_VARARGS, get_matrix_doc},
-		   {"_get_invcov_matrix", (PyCFunction)get_invcov_matrix, METH_VARARGS, get_invcov_matrix_doc},
-		   {"_get_reg_size", (PyCFunction)get_reg_size, METH_VARARGS, get_reg_size_doc},
-
-   {NULL}
-};
-
-static PyTypeObject CinvertorType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                         /*ob_size*/
-    "Cinvertor",             /*tp_name*/
-    sizeof(Cinvertor),             /*tp_basicsize*/
-    0,                         /*tp_itemsize*/
-    (destructor)Cinvertor_dealloc, /*tp_dealloc*/
-    0,                         /*tp_print*/
-    0,                         /*tp_getattr*/
-    0,                         /*tp_setattr*/
-    0,                         /*tp_compare*/
-    0,                         /*tp_repr*/
-    0,                         /*tp_as_number*/
-    0,                         /*tp_as_sequence*/
-    0,                         /*tp_as_mapping*/
-    0,                         /*tp_hash */
-    0,                         /*tp_call*/
-    0,                         /*tp_str*/
-    0,                         /*tp_getattro*/
-    0,                         /*tp_setattro*/
-    0,                         /*tp_as_buffer*/
-    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
-    "Cinvertor objects",           /* tp_doc */
-    0,		               /* tp_traverse */
-    0,		               /* tp_clear */
-    0,		               /* tp_richcompare */
-    0,		               /* tp_weaklistoffset */
-    0,		               /* tp_iter */
-    0,		               /* tp_iternext */
-    Cinvertor_methods,             /* tp_methods */
-    Cinvertor_members,             /* tp_members */
-    0,                         /* tp_getset */
-    0,                         /* tp_base */
-    0,                         /* tp_dict */
-    0,                         /* tp_descr_get */
-    0,                         /* tp_descr_set */
-    0,                         /* tp_dictoffset */
-    (initproc)Cinvertor_init,      /* tp_init */
-    0,                         /* tp_alloc */
-    Cinvertor_new,                 /* tp_new */
-};
-
-
-static PyMethodDef module_methods[] = {
-    {NULL}
-};
-
-/**
- * Function used to add the model class to a module
- * @param module: module to add the class to
- */
-void addCinvertor(PyObject *module) {
-	PyObject *d;
-
-    if (PyType_Ready(&CinvertorType) < 0)
-        return;
-
-    Py_INCREF(&CinvertorType);
-    PyModule_AddObject(module, "Cinvertor", (PyObject *)&CinvertorType);
-
-    d = PyModule_GetDict(module);
-    CinvertorError = PyErr_NewException("sas.pr.invertor.Cinvertor.InvertorError", PyExc_RuntimeError, NULL);
-    PyDict_SetItemString(d, "CinvertorError", CinvertorError);
-}
-
-
-#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
-#define PyMODINIT_FUNC void
-#endif
-PyMODINIT_FUNC
-initpr_inversion(void)
-{
-    PyObject* m;
-
-    m = Py_InitModule3("pr_inversion", module_methods,
-                       "C extension module for inversion to P(r).");
-
-    addCinvertor(m);
-}
Index: c/sas/pr/c_extensions/__init__.py
===================================================================
--- src/sas/pr/c_extensions/__init__.py	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,3 +1,0 @@
-"""
-    C extensions to provide the P(r) inversion computations.
-"""
Index: c/sas/pr/c_extensions/invertor.c
===================================================================
--- src/sas/pr/c_extensions/invertor.c	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,315 +1,0 @@
-#include <math.h>
-#include "invertor.h"
-#include <memory.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-double pi = 3.1416;
-
-/**
- * Deallocate memory
- */
-void invertor_dealloc(Invertor_params *pars) {
-	free(pars->x);
-	free(pars->y);
-	free(pars->err);
-}
-
-void invertor_init(Invertor_params *pars) {
-	pars->d_max = 180;
-	pars->q_min = -1.0;
-	pars->q_max = -1.0;
-	pars->has_bck = 0;
-}
-
-
-/**
- * P(r) of a sphere, for test purposes
- *
- * @param R: radius of the sphere
- * @param r: distance, in the same units as the radius
- * @return: P(r)
- */
-double pr_sphere(double R, double r) {
-    if (r <= 2.0*R) {
-        return 12.0* pow(0.5*r/R, 2.0) * pow(1.0-0.5*r/R, 2.0) * ( 2.0 + 0.5*r/R );
-    } else {
-        return 0.0;
-    }
-}
-
-/**
- * Orthogonal functions:
- * B(r) = 2r sin(pi*nr/d)
- *
- */
-double ortho(double d_max, int n, double r) {
-	return 2.0*r*sin(pi*n*r/d_max);
-}
-
-/**
- * Fourier transform of the nth orthogonal function
- *
- */
-double ortho_transformed(double d_max, int n, double q) {
-	return 8.0*pow(pi, 2.0)/q * d_max * n * pow(-1.0, n+1)
-		*sin(q*d_max) / ( pow(pi*n, 2.0) - pow(q*d_max, 2.0) );
-}
-
-/**
- * Slit-smeared Fourier transform of the nth orthogonal function.
- * Smearing follows Lake, Acta Cryst. (1967) 23, 191.
- */
-double ortho_transformed_smeared(double d_max, int n, double height, double width, double q, int npts) {
-	double sum, y, z;
-	int i, j, n_height, n_width;
-	double count_w;
-	double fnpts;
-	sum = 0.0;
-	fnpts = (float)npts-1.0;
-
-	// Check for zero slit size
-	n_height = (height>0) ? npts : 1;
-	n_width  = (width>0)  ? npts : 1;
-
-	count_w = 0.0;
-
-	for(j=0; j<n_height; j++) {
-		if(height>0){
-			z = height/fnpts*(float)j;
-		} else {
-			z = 0.0;
-		}
-
-		for(i=0; i<n_width; i++) {
-			if(width>0){
-				y = -width/2.0+width/fnpts*(float)i;
-			} else {
-				y = 0.0;
-			}
-			if (((q-y)*(q-y)+z*z)<=0.0) continue;
-			count_w += 1.0;
-			sum += ortho_transformed(d_max, n, sqrt((q-y)*(q-y)+z*z));
-		}
-	}
-	return sum/count_w;
-}
-
-/**
- * First derivative in of the orthogonal function dB(r)/dr
- *
- */
-double ortho_derived(double d_max, int n, double r) {
-    return 2.0*sin(pi*n*r/d_max) + 2.0*r*cos(pi*n*r/d_max);
-}
-
-/**
- * Scattering intensity calculated from the expansion.
- */
-double iq(double *pars, double d_max, int n_c, double q) {
-    double sum = 0.0;
-	int i;
-    for (i=0; i<n_c; i++) {
-        sum += pars[i] * ortho_transformed(d_max, i+1, q);
-    }
-    return sum;
-}
-
-/**
- * Scattering intensity calculated from the expansion,
- * slit-smeared.
- */
-double iq_smeared(double *pars, double d_max, int n_c, double height, double width, double q, int npts) {
-    double sum = 0.0;
-	int i;
-    for (i=0; i<n_c; i++) {
-        sum += pars[i] * ortho_transformed_smeared(d_max, i+1, height, width, q, npts);
-    }
-    return sum;
-}
-
-/**
- * P(r) calculated from the expansion.
- */
-double pr(double *pars, double d_max, int n_c, double r) {
-    double sum = 0.0;
-	int i;
-    for (i=0; i<n_c; i++) {
-        sum += pars[i] * ortho(d_max, i+1, r);
-    }
-    return sum;
-}
-
-/**
- * P(r) calculated from the expansion, with errors
- */
-void pr_err(double *pars, double *err, double d_max, int n_c,
-		double r, double *pr_value, double *pr_value_err) {
-    double sum = 0.0;
-    double sum_err = 0.0;
-    double func_value;
-	int i;
-    for (i=0; i<n_c; i++) {
-    	func_value = ortho(d_max, i+1, r);
-        sum += pars[i] * func_value;
-        //sum_err += err[i]*err[i]*func_value*func_value;
-        sum_err += err[i*n_c+i]*func_value*func_value;
-    }
-    *pr_value = sum;
-    if (sum_err>0) {
-    	*pr_value_err = sqrt(sum_err);
-    } else {
-    	*pr_value_err = sum;
-    }
-}
-
-/**
- * dP(r)/dr calculated from the expansion.
- */
-double dprdr(double *pars, double d_max, int n_c, double r) {
-    double sum = 0.0;
-	int i;
-    for (i=0; i<n_c; i++) {
-        sum += pars[i] * 2.0*(sin(pi*(i+1)*r/d_max) + pi*(i+1)*r/d_max * cos(pi*(i+1)*r/d_max));
-    }
-    return sum;
-}
-
-/**
- * regularization term calculated from the expansion.
- */
-double reg_term(double *pars, double d_max, int n_c, int nslice) {
-    double sum = 0.0;
-    double r;
-    double deriv;
-	int i;
-    for (i=0; i<nslice; i++) {
-    	r = d_max/(1.0*nslice)*i;
-    	deriv = dprdr(pars, d_max, n_c, r);
-        sum += deriv*deriv;
-    }
-    return sum/(1.0*nslice)*d_max;
-}
-
-/**
- * regularization term calculated from the expansion.
- */
-double int_p2(double *pars, double d_max, int n_c, int nslice) {
-    double sum = 0.0;
-    double r;
-    double value;
-	int i;
-    for (i=0; i<nslice; i++) {
-    	r = d_max/(1.0*nslice)*i;
-    	value = pr(pars, d_max, n_c, r);
-        sum += value*value;
-    }
-    return sum/(1.0*nslice)*d_max;
-}
-
-/**
- * Integral of P(r)
- */
-double int_pr(double *pars, double d_max, int n_c, int nslice) {
-    double sum = 0.0;
-    double r;
-    double value;
-	int i;
-    for (i=0; i<nslice; i++) {
-    	r = d_max/(1.0*nslice)*i;
-    	value = pr(pars, d_max, n_c, r);
-        sum += value;
-    }
-    return sum/(1.0*nslice)*d_max;
-}
-
-/**
- * Get the number of P(r) peaks.
- */
-int npeaks(double *pars, double d_max, int n_c, int nslice) {
-    double r;
-    double value;
-	int i;
-	double previous = 0.0;
-	double slope    = 0.0;
-	int count = 0;
-    for (i=0; i<nslice; i++) {
-    	r = d_max/(1.0*nslice)*i;
-    	value = pr(pars, d_max, n_c, r);
-    	if (previous<=value){
-    		//if (slope<0) count += 1;
-    		slope = 1;
-    	} else {
-    		//printf("slope -1");
-    		if (slope>0) count += 1;
-    		slope = -1;
-    	}
-    	previous = value;
-    }
-    return count;
-}
-
-/**
- * Get the fraction of the integral of P(r) over the whole range
- * of r that is above zero.
- * A valid P(r) is define as being positive for all r.
- */
-double positive_integral(double *pars, double d_max, int n_c, int nslice) {
-    double r;
-    double value;
-	int i;
-	double sum_pos = 0.0;
-	double sum = 0.0;
-
-    for (i=0; i<nslice; i++) {
-    	r = d_max/(1.0*nslice)*i;
-    	value = pr(pars, d_max, n_c, r);
-    	if (value>0.0) sum_pos += value;
-    	sum += fabs(value);
-    }
-    return sum_pos/sum;
-}
-
-/**
- * Get the fraction of the integral of P(r) over the whole range
- * of r that is at least one sigma above zero.
- */
-double positive_errors(double *pars, double *err, double d_max, int n_c, int nslice) {
-    double r;
-	int i;
-	double sum_pos = 0.0;
-	double sum = 0.0;
-	double pr_val;
-	double pr_val_err;
-
-    for (i=0; i<nslice; i++) {
-    	r = d_max/(1.0*nslice)*i;
-    	pr_err(pars, err, d_max, n_c, r, &pr_val, &pr_val_err);
-    	if (pr_val>pr_val_err) sum_pos += pr_val;
-    	sum += fabs(pr_val);
-
-
-    }
-    return sum_pos/sum;
-}
-
-/**
- * R_g radius of gyration calculation
- *
- * R_g**2 = integral[r**2 * p(r) dr] /  (2.0 * integral[p(r) dr])
- */
-double rg(double *pars, double d_max, int n_c, int nslice) {
-    double sum_r2 = 0.0;
-    double sum    = 0.0;
-    double r;
-    double value;
-	int i;
-    for (i=0; i<nslice; i++) {
-    	r = d_max/(1.0*nslice)*i;
-    	value = pr(pars, d_max, n_c, r);
-        sum += value;
-        sum_r2 += r*r*value;
-    }
-    return sqrt(sum_r2/(2.0*sum));
-}
-
Index: c/sas/pr/c_extensions/invertor.h
===================================================================
--- src/sas/pr/c_extensions/invertor.h	(revision 794922220f2fb3b23dd08c648aabf87b4850596c)
+++ 	(revision )
@@ -1,59 +1,0 @@
-#if !defined(invertor_h)
-#define invertor_h
-
-/**
- * Internal data structure for P(r) inversion
- */
-typedef struct {
-	/// Maximum distance between any two points in the system
-    double d_max;
-    /// q data
-    double *x;
-    /// I(q) data
-    double *y;
-    /// dI(q) data
-    double *err;
-    /// Number of q points
-    int npoints;
-    /// Number of I(q) points
-    int ny;
-    /// Number of dI(q) points
-    int nerr;
-    /// Alpha value
-    double alpha;
-    /// Minimum q to include in inversion
-    double q_min;
-    /// Maximum q to include in inversion
-    double q_max;
-    /// Flag for whether or not to evalute a constant background while inverting
-    int has_bck;
-    /// Slit height in units of q [A-1]
-    double slit_height;
-    /// Slit width in units of q [A-1]
-    double slit_width;
-} Invertor_params;
-
-void invertor_dealloc(Invertor_params *pars);
-
-void invertor_init(Invertor_params *pars);
-
-double pr_sphere(double R, double r);
-double ortho(double d_max, int n, double r);
-double ortho_transformed(double d_max, int n, double q);
-double ortho_derived(double d_max, int n, double r);
-double iq(double *c, double d_max, int n_c, double q);
-double pr(double *c, double d_max, int n_c, double r);
-double dprdr(double *pars, double d_max, int n_c, double r);
-double reg_term(double *pars, double d_max, int n_c, int nslice);
-double int_p2(double *pars, double d_max, int n_c, int nslice);
-void pr_err(double *pars, double *err, double d_max, int n_c,
-		double r, double *pr_value, double *pr_value_err);
-int npeaks(double *pars, double d_max, int n_c, int nslice);
-double positive_integral(double *pars, double d_max, int n_c, int nslice);
-double positive_errors(double *pars, double *err, double d_max, int n_c, int nslice);
-double rg(double *pars, double d_max, int n_c, int nslice);
-double int_pr(double *pars, double d_max, int n_c, int nslice);
-double ortho_transformed_smeared(double d_max, int n, double heigth, double width, double q, int npts);
-double iq_smeared(double *pars, double d_max, int n_c, double height, double width, double q, int npts);
-
-#endif
Index: c/sas/pr/distance_explorer.py
===================================================================
--- src/sas/pr/distance_explorer.py	(revision 038c00cff7f85bea4546e70f60a2a59099d8c8df)
+++ 	(revision )
@@ -1,107 +1,0 @@
-
-################################################################################
-#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.
-#
-#See the license text in license.txt
-#
-#copyright 2009, University of Tennessee
-################################################################################
-
-"""
-Module to explore the P(r) inversion results for a range
-of D_max value. User picks a number of points and a range of
-distances, then get a series of outputs as a function of D_max
-over that range.
-"""
-import sys
-
-
-class Results(object):
-    """
-    Class to hold the inversion output parameters
-    as a function of D_max
-    """
-    def __init__(self):
-        """
-        Initialization. Create empty arrays
-        and dictionary of labels.
-        """
-        # Array of output for each inversion
-        self.chi2 = []
-        self.osc = []
-        self.pos = []
-        self.pos_err = []
-        self.rg = []
-        self.iq0 = []
-        self.bck = []
-        self.d_max = []
-        ## List of errors found during the last exploration
-        self.errors = []
-
-
-class DistExplorer(object):
-    """
-    The explorer class
-    """
-
-    def __init__(self, pr_state):
-        """
-        Initialization.
-
-        :param pr_state: sas.pr.invertor.Invertor object
-
-        """
-        self.pr_state = pr_state
-        self._default_min = 0.8 * self.pr_state.d_max
-        self._default_max = 1.2 * self.pr_state.d_max
-
-    def __call__(self, dmin=None, dmax=None, npts=10):
-        """
-        Compute the outputs as a function of D_max.
-
-        :param dmin: minimum value for D_max
-        :param dmax: maximum value for D_max
-        :param npts: number of points for D_max
-
-        """
-        # Take care of the defaults if needed
-        if dmin is None:
-            dmin = self._default_min
-
-        if dmax is None:
-            dmax = self._default_max
-
-        # Results object to store the computation outputs.
-        results = Results()
-
-        # Loop over d_max values
-        for i in range(npts):
-            d = dmin + i * (dmax - dmin) / (npts - 1.0)
-            self.pr_state.d_max = d
-            try:
-                out, cov = self.pr_state.invert(self.pr_state.nfunc)
-
-                # Store results
-                iq0 = self.pr_state.iq0(out)
-                rg = self.pr_state.rg(out)
-                pos = self.pr_state.get_positive(out)
-                pos_err = self.pr_state.get_pos_err(out, cov)
-                osc = self.pr_state.oscillations(out)
-
-                results.d_max.append(self.pr_state.d_max)
-                results.bck.append(self.pr_state.background)
-                results.chi2.append(self.pr_state.chi2)
-                results.iq0.append(iq0)
-                results.rg.append(rg)
-                results.pos.append(pos)
-                results.pos_err.append(pos_err)
-                results.osc.append(osc)
-            except:
-                # This inversion failed, skip this D_max value
-                msg = "ExploreDialog: inversion failed for "
-                msg += "D_max=%s\n %s" % (str(d), sys.exc_value)
-                results.errors.append(msg)
-
-        return results
Index: c/sas/pr/invertor.py
===================================================================
--- src/sas/pr/invertor.py	(revision 3d250da3ae17ea6190c69794c598422f07134904)
+++ 	(revision )
@@ -1,750 +1,0 @@
-# pylint: disable=invalid-name
-"""
-Module to perform P(r) inversion.
-The module contains the Invertor class.
-
-FIXME: The way the Invertor interacts with its C component should be cleaned up
-"""
-
-import numpy
-import sys
-import math
-import time
-import copy
-import os
-import re
-import logging
-from numpy.linalg import lstsq
-from scipy import optimize
-from sas.pr.core.pr_inversion import Cinvertor
-
-def help():
-    """
-    Provide general online help text
-    Future work: extend this function to allow topic selection
-    """
-    info_txt = "The inversion approach is based on Moore, J. Appl. Cryst. "
-    info_txt += "(1980) 13, 168-175.\n\n"
-    info_txt += "P(r) is set to be equal to an expansion of base functions "
-    info_txt += "of the type "
-    info_txt += "phi_n(r) = 2*r*sin(pi*n*r/D_max). The coefficient of each "
-    info_txt += "base functions "
-    info_txt += "in the expansion is found by performing a least square fit "
-    info_txt += "with the "
-    info_txt += "following fit function:\n\n"
-    info_txt += "chi**2 = sum_i[ I_meas(q_i) - I_th(q_i) ]**2/error**2 +"
-    info_txt += "Reg_term\n\n"
-    info_txt += "where I_meas(q) is the measured scattering intensity and "
-    info_txt += "I_th(q) is "
-    info_txt += "the prediction from the Fourier transform of the P(r) "
-    info_txt += "expansion. "
-    info_txt += "The Reg_term term is a regularization term set to the second"
-    info_txt += " derivative "
-    info_txt += "d**2P(r)/dr**2 integrated over r. It is used to produce "
-    info_txt += "a smooth P(r) output.\n\n"
-    info_txt += "The following are user inputs:\n\n"
-    info_txt += "   - Number of terms: the number of base functions in the P(r)"
-    info_txt += " expansion.\n\n"
-    info_txt += "   - Regularization constant: a multiplicative constant "
-    info_txt += "to set the size of "
-    info_txt += "the regularization term.\n\n"
-    info_txt += "   - Maximum distance: the maximum distance between any "
-    info_txt += "two points in the system.\n"
-
-    return info_txt
-
-
-class Invertor(Cinvertor):
-    """
-    Invertor class to perform P(r) inversion
-
-    The problem is solved by posing the problem as  Ax = b,
-    where x is the set of coefficients we are looking for.
-
-    Npts is the number of points.
-
-    In the following i refers to the ith base function coefficient.
-    The matrix has its entries j in its first Npts rows set to ::
-
-        A[j][i] = (Fourier transformed base function for point j)
-
-    We them choose a number of r-points, n_r, to evaluate the second
-    derivative of P(r) at. This is used as our regularization term.
-    For a vector r of length n_r, the following n_r rows are set to ::
-
-        A[j+Npts][i] = (2nd derivative of P(r), d**2(P(r))/d(r)**2,
-        evaluated at r[j])
-
-    The vector b has its first Npts entries set to ::
-
-        b[j] = (I(q) observed for point j)
-
-    The following n_r entries are set to zero.
-
-    The result is found by using scipy.linalg.basic.lstsq to invert
-    the matrix and find the coefficients x.
-
-    Methods inherited from Cinvertor:
-
-    * ``get_peaks(pars)``: returns the number of P(r) peaks
-    * ``oscillations(pars)``: returns the oscillation parameters for the output P(r)
-    * ``get_positive(pars)``: returns the fraction of P(r) that is above zero
-    * ``get_pos_err(pars)``: returns the fraction of P(r) that is 1-sigma above zero
-    """
-    ## Chisqr of the last computation
-    chi2 = 0
-    ## Time elapsed for last computation
-    elapsed = 0
-    ## Alpha to get the reg term the same size as the signal
-    suggested_alpha = 0
-    ## Last number of base functions used
-    nfunc = 10
-    ## Last output values
-    out = None
-    ## Last errors on output values
-    cov = None
-    ## Background value
-    background = 0
-    ## Information dictionary for application use
-    info = {}
-
-    def __init__(self):
-        Cinvertor.__init__(self)
-
-    def __setstate__(self, state):
-        """
-        restore the state of invertor for pickle
-        """
-        (self.__dict__, self.alpha, self.d_max,
-         self.q_min, self.q_max,
-         self.x, self.y,
-         self.err, self.has_bck,
-         self.slit_height, self.slit_width) = state
-
-    def __reduce_ex__(self, proto):
-        """
-        Overwrite the __reduce_ex__
-        """
-
-        state = (self.__dict__,
-                 self.alpha, self.d_max,
-                 self.q_min, self.q_max,
-                 self.x, self.y,
-                 self.err, self.has_bck,
-                 self.slit_height, self.slit_width,
-                )
-        return (Invertor, tuple(), state, None, None)
-
-    def __setattr__(self, name, value):
-        """
-        Set the value of an attribute.
-        Access the parent class methods for
-        x, y, err, d_max, q_min, q_max and alpha
-        """
-        if   name == 'x':
-            if 0.0 in value:
-                msg = "Invertor: one of your q-values is zero. "
-                msg += "Delete that entry before proceeding"
-                raise ValueError, msg
-            return self.set_x(value)
-        elif name == 'y':
-            return self.set_y(value)
-        elif name == 'err':
-            value2 = abs(value)
-            return self.set_err(value2)
-        elif name == 'd_max':
-            return self.set_dmax(value)
-        elif name == 'q_min':
-            if value == None:
-                return self.set_qmin(-1.0)
-            return self.set_qmin(value)
-        elif name == 'q_max':
-            if value == None:
-                return self.set_qmax(-1.0)
-            return self.set_qmax(value)
-        elif name == 'alpha':
-            return self.set_alpha(value)
-        elif name == 'slit_height':
-            return self.set_slit_height(value)
-        elif name == 'slit_width':
-            return self.set_slit_width(value)
-        elif name == 'has_bck':
-            if value == True:
-                return self.set_has_bck(1)
-            elif value == False:
-                return self.set_has_bck(0)
-            else:
-                raise ValueError, "Invertor: has_bck can only be True or False"
-
-        return Cinvertor.__setattr__(self, name, value)
-
-    def __getattr__(self, name):
-        """
-        Return the value of an attribute
-        """
-        #import numpy
-        if name == 'x':
-            out = numpy.ones(self.get_nx())
-            self.get_x(out)
-            return out
-        elif name == 'y':
-            out = numpy.ones(self.get_ny())
-            self.get_y(out)
-            return out
-        elif name == 'err':
-            out = numpy.ones(self.get_nerr())
-            self.get_err(out)
-            return out
-        elif name == 'd_max':
-            return self.get_dmax()
-        elif name == 'q_min':
-            qmin = self.get_qmin()
-            if qmin < 0:
-                return None
-            return qmin
-        elif name == 'q_max':
-            qmax = self.get_qmax()
-            if qmax < 0:
-                return None
-            return qmax
-        elif name == 'alpha':
-            return self.get_alpha()
-        elif name == 'slit_height':
-            return self.get_slit_height()
-        elif name == 'slit_width':
-            return self.get_slit_width()
-        elif name == 'has_bck':
-            value = self.get_has_bck()
-            if value == 1:
-                return True
-            else:
-                return False
-        elif name in self.__dict__:
-            return self.__dict__[name]
-        return None
-
-    def clone(self):
-        """
-        Return a clone of this instance
-        """
-        #import copy
-
-        invertor = Invertor()
-        invertor.chi2 = self.chi2
-        invertor.elapsed = self.elapsed
-        invertor.nfunc = self.nfunc
-        invertor.alpha = self.alpha
-        invertor.d_max = self.d_max
-        invertor.q_min = self.q_min
-        invertor.q_max = self.q_max
-
-        invertor.x = self.x
-        invertor.y = self.y
-        invertor.err = self.err
-        invertor.has_bck = self.has_bck
-        invertor.slit_height = self.slit_height
-        invertor.slit_width = self.slit_width
-
-        invertor.info = copy.deepcopy(self.info)
-
-        return invertor
-
-    def invert(self, nfunc=10, nr=20):
-        """
-        Perform inversion to P(r)
-
-        The problem is solved by posing the problem as  Ax = b,
-        where x is the set of coefficients we are looking for.
-
-        Npts is the number of points.
-
-        In the following i refers to the ith base function coefficient.
-        The matrix has its entries j in its first Npts rows set to ::
-
-            A[i][j] = (Fourier transformed base function for point j)
-
-        We them choose a number of r-points, n_r, to evaluate the second
-        derivative of P(r) at. This is used as our regularization term.
-        For a vector r of length n_r, the following n_r rows are set to ::
-
-            A[i+Npts][j] = (2nd derivative of P(r), d**2(P(r))/d(r)**2, evaluated at r[j])
-
-        The vector b has its first Npts entries set to ::
-
-            b[j] = (I(q) observed for point j)
-
-        The following n_r entries are set to zero.
-
-        The result is found by using scipy.linalg.basic.lstsq to invert
-        the matrix and find the coefficients x.
-
-        :param nfunc: number of base functions to use.
-        :param nr: number of r points to evaluate the 2nd derivative at for the reg. term.
-        :return: c_out, c_cov - the coefficients with covariance matrix
-        """
-        # Reset the background value before proceeding
-        self.background = 0.0
-        return self.lstsq(nfunc, nr=nr)
-
-    def iq(self, out, q):
-        """
-        Function to call to evaluate the scattering intensity
-
-        :param args: c-parameters, and q
-        :return: I(q)
-
-        """
-        return Cinvertor.iq(self, out, q) + self.background
-
-    def invert_optimize(self, nfunc=10, nr=20):
-        """
-        Slower version of the P(r) inversion that uses scipy.optimize.leastsq.
-
-        This probably produce more reliable results, but is much slower.
-        The minimization function is set to
-        sum_i[ (I_obs(q_i) - I_theo(q_i))/err**2 ] + alpha * reg_term,
-        where the reg_term is given by Svergun: it is the integral of
-        the square of the first derivative
-        of P(r), d(P(r))/dr, integrated over the full range of r.
-
-        :param nfunc: number of base functions to use.
-        :param nr: number of r points to evaluate the 2nd derivative at
-            for the reg. term.
-
-        :return: c_out, c_cov - the coefficients with covariance matrix
-
-        """
-        self.nfunc = nfunc
-        # First, check that the current data is valid
-        if self.is_valid() <= 0:
-            msg = "Invertor.invert: Data array are of different length"
-            raise RuntimeError, msg
-
-        p = numpy.ones(nfunc)
-        t_0 = time.time()
-        out, cov_x, _, _, _ = optimize.leastsq(self.residuals, p, full_output=1)
-
-        # Compute chi^2
-        res = self.residuals(out)
-        chisqr = 0
-        for i in range(len(res)):
-            chisqr += res[i]
-
-        self.chi2 = chisqr
-
-        # Store computation time
-        self.elapsed = time.time() - t_0
-
-        if cov_x is None:
-            cov_x = numpy.ones([nfunc, nfunc])
-            cov_x *= math.fabs(chisqr)
-        return out, cov_x
-
-    def pr_fit(self, nfunc=5):
-        """
-        This is a direct fit to a given P(r). It assumes that the y data
-        is set to some P(r) distribution that we are trying to reproduce
-        with a set of base functions.
-
-        This method is provided as a test.
-        """
-        # First, check that the current data is valid
-        if self.is_valid() <= 0:
-            msg = "Invertor.invert: Data arrays are of different length"
-            raise RuntimeError, msg
-
-        p = numpy.ones(nfunc)
-        t_0 = time.time()
-        out, cov_x, _, _, _ = optimize.leastsq(self.pr_residuals, p, full_output=1)
-
-        # Compute chi^2
-        res = self.pr_residuals(out)
-        chisqr = 0
-        for i in range(len(res)):
-            chisqr += res[i]
-
-        self.chisqr = chisqr
-
-        # Store computation time
-        self.elapsed = time.time() - t_0
-
-        return out, cov_x
-
-    def pr_err(self, c, c_cov, r):
-        """
-        Returns the value of P(r) for a given r, and base function
-        coefficients, with error.
-
-        :param c: base function coefficients
-        :param c_cov: covariance matrice of the base function coefficients
-        :param r: r-value to evaluate P(r) at
-
-        :return: P(r)
-
-        """
-        return self.get_pr_err(c, c_cov, r)
-
-    def _accept_q(self, q):
-        """
-        Check q-value against user-defined range
-        """
-        if not self.q_min == None and q < self.q_min:
-            return False
-        if not self.q_max == None and q > self.q_max:
-            return False
-        return True
-
-    def lstsq(self, nfunc=5, nr=20):
-        """
-        The problem is solved by posing the problem as  Ax = b,
-        where x is the set of coefficients we are looking for.
-
-        Npts is the number of points.
-
-        In the following i refers to the ith base function coefficient.
-        The matrix has its entries j in its first Npts rows set to ::
-
-            A[i][j] = (Fourier transformed base function for point j)
-
-        We them choose a number of r-points, n_r, to evaluate the second
-        derivative of P(r) at. This is used as our regularization term.
-        For a vector r of length n_r, the following n_r rows are set to ::
-
-            A[i+Npts][j] = (2nd derivative of P(r), d**2(P(r))/d(r)**2,
-            evaluated at r[j])
-
-        The vector b has its first Npts entries set to ::
-
-            b[j] = (I(q) observed for point j)
-
-        The following n_r entries are set to zero.
-
-        The result is found by using scipy.linalg.basic.lstsq to invert
-        the matrix and find the coefficients x.
-
-        :param nfunc: number of base functions to use.
-        :param nr: number of r points to evaluate the 2nd derivative at for the reg. term.
-
-        If the result does not allow us to compute the covariance matrix,
-        a matrix filled with zeros will be returned.
-
-        """
-        # Note: To make sure an array is contiguous:
-        # blah = numpy.ascontiguousarray(blah_original)
-        # ... before passing it to C
-
-        if self.is_valid() < 0:
-            msg = "Invertor: invalid data; incompatible data lengths."
-            raise RuntimeError, msg
-
-        self.nfunc = nfunc
-        # a -- An M x N matrix.
-        # b -- An M x nrhs matrix or M vector.
-        npts = len(self.x)
-        nq = nr
-        sqrt_alpha = math.sqrt(math.fabs(self.alpha))
-        if sqrt_alpha < 0.0:
-            nq = 0
-
-        # If we need to fit the background, add a term
-        if self.has_bck == True:
-            nfunc_0 = nfunc
-            nfunc += 1
-
-        a = numpy.zeros([npts + nq, nfunc])
-        b = numpy.zeros(npts + nq)
-        err = numpy.zeros([nfunc, nfunc])
-
-        # Construct the a matrix and b vector that represent the problem
-        t_0 = time.time()
-        try:
-            self._get_matrix(nfunc, nq, a, b)
-        except:
-            raise RuntimeError, "Invertor: could not invert I(Q)\n  %s" % sys.exc_value
-
-        # Perform the inversion (least square fit)
-        c, chi2, _, _ = lstsq(a, b)
-        # Sanity check
-        try:
-            float(chi2)
-        except:
-            chi2 = -1.0
-        self.chi2 = chi2
-
-        inv_cov = numpy.zeros([nfunc, nfunc])
-        # Get the covariance matrix, defined as inv_cov = a_transposed * a
-        self._get_invcov_matrix(nfunc, nr, a, inv_cov)
-
-        # Compute the reg term size for the output
-        sum_sig, sum_reg = self._get_reg_size(nfunc, nr, a)
-
-        if math.fabs(self.alpha) > 0:
-            new_alpha = sum_sig / (sum_reg / self.alpha)
-        else:
-            new_alpha = 0.0
-        self.suggested_alpha = new_alpha
-
-        try:
-            cov = numpy.linalg.pinv(inv_cov)
-            err = math.fabs(chi2 / float(npts - nfunc)) * cov
-        except:
-            # We were not able to estimate the errors
-            # Return an empty error matrix
-            logging.error(sys.exc_value)
-
-        # Keep a copy of the last output
-        if self.has_bck == False:
-            self.background = 0
-            self.out = c
-            self.cov = err
-        else:
-            self.background = c[0]
-
-            err_0 = numpy.zeros([nfunc, nfunc])
-            c_0 = numpy.zeros(nfunc)
-
-            for i in range(nfunc_0):
-                c_0[i] = c[i + 1]
-                for j in range(nfunc_0):
-                    err_0[i][j] = err[i + 1][j + 1]
-
-            self.out = c_0
-            self.cov = err_0
-
-        # Store computation time
-        self.elapsed = time.time() - t_0
-
-        return self.out, self.cov
-
-    def estimate_numterms(self, isquit_func=None):
-        """
-        Returns a reasonable guess for the
-        number of terms
-
-        :param isquit_func:
-          reference to thread function to call to check whether the computation needs to
-          be stopped.
-
-        :return: number of terms, alpha, message
-
-        """
-        from num_term import NTermEstimator
-        estimator = NTermEstimator(self.clone())
-        try:
-            return estimator.num_terms(isquit_func)
-        except:
-            # If we fail, estimate alpha and return the default
-            # number of terms
-            best_alpha, _, _ = self.estimate_alpha(self.nfunc)
-            logging.warning("Invertor.estimate_numterms: %s" % sys.exc_value)
-            return self.nfunc, best_alpha, "Could not estimate number of terms"
-
-    def estimate_alpha(self, nfunc):
-        """
-        Returns a reasonable guess for the
-        regularization constant alpha
-
-        :param nfunc: number of terms to use in the expansion.
-
-        :return: alpha, message, elapsed
-
-        where alpha is the estimate for alpha,
-        message is a message for the user,
-        elapsed is the computation time
-        """
-        #import time
-        try:
-            pr = self.clone()
-
-            # T_0 for computation time
-            starttime = time.time()
-            elapsed = 0
-
-            # If the current alpha is zero, try
-            # another value
-            if pr.alpha <= 0:
-                pr.alpha = 0.0001
-
-            # Perform inversion to find the largest alpha
-            out, _ = pr.invert(nfunc)
-            elapsed = time.time() - starttime
-            initial_alpha = pr.alpha
-            initial_peaks = pr.get_peaks(out)
-
-            # Try the inversion with the estimated alpha
-            pr.alpha = pr.suggested_alpha
-            out, _ = pr.invert(nfunc)
-
-            npeaks = pr.get_peaks(out)
-            # if more than one peak to start with
-            # just return the estimate
-            if npeaks > 1:
-                #message = "Your P(r) is not smooth,
-                #please check your inversion parameters"
-                message = None
-                return pr.suggested_alpha, message, elapsed
-            else:
-
-                # Look at smaller values
-                # We assume that for the suggested alpha, we have 1 peak
-                # if not, send a message to change parameters
-                alpha = pr.suggested_alpha
-                best_alpha = pr.suggested_alpha
-                found = False
-                for i in range(10):
-                    pr.alpha = (0.33) ** (i + 1) * alpha
-                    out, _ = pr.invert(nfunc)
-
-                    peaks = pr.get_peaks(out)
-                    if peaks > 1:
-                        found = True
-                        break
-                    best_alpha = pr.alpha
-
-                # If we didn't find a turning point for alpha and
-                # the initial alpha already had only one peak,
-                # just return that
-                if not found and initial_peaks == 1 and \
-                    initial_alpha < best_alpha:
-                    best_alpha = initial_alpha
-
-                # Check whether the size makes sense
-                message = ''
-
-                if not found:
-                    message = None
-                elif best_alpha >= 0.5 * pr.suggested_alpha:
-                    # best alpha is too big, return a
-                    # reasonable value
-                    message = "The estimated alpha for your system is too "
-                    message += "large. "
-                    message += "Try increasing your maximum distance."
-
-                return best_alpha, message, elapsed
-
-        except:
-            message = "Invertor.estimate_alpha: %s" % sys.exc_value
-            return 0, message, elapsed
-
-    def to_file(self, path, npts=100):
-        """
-        Save the state to a file that will be readable
-        by SliceView.
-
-        :param path: path of the file to write
-        :param npts: number of P(r) points to be written
-
-        """
-        file = open(path, 'w')
-        file.write("#d_max=%g\n" % self.d_max)
-        file.write("#nfunc=%g\n" % self.nfunc)
-        file.write("#alpha=%g\n" % self.alpha)
-        file.write("#chi2=%g\n" % self.chi2)
-        file.write("#elapsed=%g\n" % self.elapsed)
-        file.write("#qmin=%s\n" % str(self.q_min))
-        file.write("#qmax=%s\n" % str(self.q_max))
-        file.write("#slit_height=%g\n" % self.slit_height)
-        file.write("#slit_width=%g\n" % self.slit_width)
-        file.write("#background=%g\n" % self.background)
-        if self.has_bck == True:
-            file.write("#has_bck=1\n")
-        else:
-            file.write("#has_bck=0\n")
-        file.write("#alpha_estimate=%g\n" % self.suggested_alpha)
-        if not self.out == None:
-            if len(self.out) == len(self.cov):
-                for i in range(len(self.out)):
-                    file.write("#C_%i=%s+-%s\n" % (i, str(self.out[i]),
-                                                   str(self.cov[i][i])))
-        file.write("<r>  <Pr>  <dPr>\n")
-        r = numpy.arange(0.0, self.d_max, self.d_max / npts)
-
-        for r_i in r:
-            (value, err) = self.pr_err(self.out, self.cov, r_i)
-            file.write("%g  %g  %g\n" % (r_i, value, err))
-
-        file.close()
-
-    def from_file(self, path):
-        """
-        Load the state of the Invertor from a file,
-        to be able to generate P(r) from a set of
-        parameters.
-
-        :param path: path of the file to load
-
-        """
-        #import os
-        #import re
-        if os.path.isfile(path):
-            try:
-                fd = open(path, 'r')
-
-                buff = fd.read()
-                lines = buff.split('\n')
-                for line in lines:
-                    if line.startswith('#d_max='):
-                        toks = line.split('=')
-                        self.d_max = float(toks[1])
-                    elif line.startswith('#nfunc='):
-                        toks = line.split('=')
-                        self.nfunc = int(toks[1])
-                        self.out = numpy.zeros(self.nfunc)
-                        self.cov = numpy.zeros([self.nfunc, self.nfunc])
-                    elif line.startswith('#alpha='):
-                        toks = line.split('=')
-                        self.alpha = float(toks[1])
-                    elif line.startswith('#chi2='):
-                        toks = line.split('=')
-                        self.chi2 = float(toks[1])
-                    elif line.startswith('#elapsed='):
-                        toks = line.split('=')
-                        self.elapsed = float(toks[1])
-                    elif line.startswith('#alpha_estimate='):
-                        toks = line.split('=')
-                        self.suggested_alpha = float(toks[1])
-                    elif line.startswith('#qmin='):
-                        toks = line.split('=')
-                        try:
-                            self.q_min = float(toks[1])
-                        except:
-                            self.q_min = None
-                    elif line.startswith('#qmax='):
-                        toks = line.split('=')
-                        try:
-                            self.q_max = float(toks[1])
-                        except:
-                            self.q_max = None
-                    elif line.startswith('#slit_height='):
-                        toks = line.split('=')
-                        self.slit_height = float(toks[1])
-                    elif line.startswith('#slit_width='):
-                        toks = line.split('=')
-                        self.slit_width = float(toks[1])
-                    elif line.startswith('#background='):
-                        toks = line.split('=')
-                        self.background = float(toks[1])
-                    elif line.startswith('#has_bck='):
-                        toks = line.split('=')
-                        if int(toks[1]) == 1:
-                            self.has_bck = True
-                        else:
-                            self.has_bck = False
-
-                    # Now read in the parameters
-                    elif line.startswith('#C_'):
-                        toks = line.split('=')
-                        p = re.compile('#C_([0-9]+)')
-                        m = p.search(toks[0])
-                        toks2 = toks[1].split('+-')
-                        i = int(m.group(1))
-                        self.out[i] = float(toks2[0])
-
-                        self.cov[i][i] = float(toks2[1])
-
-            except:
-                msg = "Invertor.from_file: corrupted file\n%s" % sys.exc_value
-                raise RuntimeError, msg
-        else:
-            msg = "Invertor.from_file: '%s' is not a file" % str(path)
-            raise RuntimeError, msg
Index: c/sas/pr/num_term.py
===================================================================
--- src/sas/pr/num_term.py	(revision c1bffa54af1e22806462ad42a4f76dd9f73a53f5)
+++ 	(revision )
@@ -1,197 +1,0 @@
-import math
-import numpy
-import copy
-import sys
-import logging
-from sas.pr.invertor import Invertor
-
-class NTermEstimator(object):
-    """
-    """
-    def __init__(self, invertor):
-        """
-        """
-        self.invertor = invertor
-        self.nterm_min = 10
-        self.nterm_max = len(self.invertor.x)
-        if self.nterm_max > 50:
-            self.nterm_max = 50
-        self.isquit_func = None
-
-        self.osc_list = []
-        self.err_list = []
-        self.alpha_list = []
-        self.mess_list = []
-        self.dataset = []
-
-    def is_odd(self, n):
-        """
-        """
-        return bool(n % 2)
-
-    def sort_osc(self):
-        """
-        """
-        #import copy
-        osc = copy.deepcopy(self.dataset)
-        lis = []
-        for i in range(len(osc)):
-            osc.sort()
-            re = osc.pop(0)
-            lis.append(re)
-        return lis
-
-    def median_osc(self):
-        """
-        """
-        osc = self.sort_osc()
-        dv = len(osc)
-        med = float(dv) / 2.0
-        odd = self.is_odd(dv)
-        medi = 0
-        for i in range(dv):
-            if odd == True:
-                medi = osc[int(med)]
-            else:
-                medi = osc[int(med) - 1]
-        return medi
-
-    def get0_out(self):
-        """
-        """
-        inver = self.invertor
-        self.osc_list = []
-        self.err_list = []
-        self.alpha_list = []
-        for k in range(self.nterm_min, self.nterm_max, 1):
-            if self.isquit_func != None:
-                self.isquit_func()
-            best_alpha, message, _ = inver.estimate_alpha(k)
-            inver.alpha = best_alpha
-            inver.out, inver.cov = inver.lstsq(k)
-            osc = inver.oscillations(inver.out)
-            err = inver.get_pos_err(inver.out, inver.cov)
-            if osc > 10.0:
-                break
-            self.osc_list.append(osc)
-            self.err_list.append(err)
-            self.alpha_list.append(inver.alpha)
-            self.mess_list.append(message)
-
-        new_osc1 = []
-        new_osc2 = []
-        new_osc3 = []
-        flag9 = False
-        flag8 = False
-        for i in range(len(self.err_list)):
-            if self.err_list[i] <= 1.0 and self.err_list[i] >= 0.9:
-                new_osc1.append(self.osc_list[i])
-                flag9 = True
-            if self.err_list[i] < 0.9 and self.err_list[i] >= 0.8:
-                new_osc2.append(self.osc_list[i])
-                flag8 = True
-            if self.err_list[i] < 0.8 and self.err_list[i] >= 0.7:
-                new_osc3.append(self.osc_list[i])
-
-        if flag9 == True:
-            self.dataset = new_osc1
-        elif flag8 == True:
-            self.dataset = new_osc2
-        else:
-            self.dataset = new_osc3
-
-        return self.dataset
-
-    def ls_osc(self):
-        """
-        """
-        # Generate data
-        self.get0_out()
-        med = self.median_osc()
-
-        #TODO: check 1
-        ls_osc = self.dataset
-        ls = []
-        for i in range(len(ls_osc)):
-            if int(med) == int(ls_osc[i]):
-                ls.append(ls_osc[i])
-        return ls
-
-    def compare_err(self):
-        """
-        """
-        ls = self.ls_osc()
-        nt_ls = []
-        for i in range(len(ls)):
-            r = ls[i]
-            n = self.osc_list.index(r) + 10
-            nt_ls.append(n)
-        return nt_ls
-
-    def num_terms(self, isquit_func=None):
-        """
-        """
-        try:
-            self.isquit_func = isquit_func
-            nts = self.compare_err()
-            div = len(nts)
-            tem = float(div) / 2.0
-            odd = self.is_odd(div)
-            if odd == True:
-                nt = nts[int(tem)]
-            else:
-                nt = nts[int(tem) - 1]
-            return nt, self.alpha_list[nt - 10], self.mess_list[nt - 10]
-        except:
-            #TODO: check the logic above and make sure it doesn't 
-            # rely on the try-except.
-            return self.nterm_min, self.invertor.alpha, ''
-
-
-#For testing
-def load(path):
-    # Read the data from the data file
-    data_x = numpy.zeros(0)
-    data_y = numpy.zeros(0)
-    data_err = numpy.zeros(0)
-    scale = None
-    min_err = 0.0
-    if not path == None:
-        input_f = open(path, 'r')
-        buff = input_f.read()
-        lines = buff.split('\n')
-        for line in lines:
-            try:
-                toks = line.split()
-                test_x = float(toks[0])
-                test_y = float(toks[1])
-                if len(toks) > 2:
-                    err = float(toks[2])
-                else:
-                    if scale == None:
-                        scale = 0.05 * math.sqrt(test_y)
-                        #scale = 0.05/math.sqrt(y)
-                        min_err = 0.01 * y
-                    err = scale * math.sqrt(test_y) + min_err
-                    #err = 0
-
-                data_x = numpy.append(data_x, test_x)
-                data_y = numpy.append(data_y, test_y)
-                data_err = numpy.append(data_err, err)
-            except:
-                logging.error(sys.exc_value)
-
-    return data_x, data_y, data_err
-
-
-if __name__ == "__main__":
-    invert = Invertor()
-    x, y, erro = load("test/Cyl_A_D102.txt")
-    invert.d_max = 102.0
-    invert.nfunc = 10
-    invert.x = x
-    invert.y = y
-    invert.err = erro
-    # Testing estimator
-    est = NTermEstimator(invert)
-    print est.num_terms()
Index: c/sas/pr/release_notes.txt
===================================================================
--- src/sas/pr/release_notes.txt	(revision b9a5f0eb8e8cbff8f8d934bbfa4ea18168141e13)
+++ 	(revision )
@@ -1,64 +1,0 @@
-Release Notes
-=============
-
-Package name: pr_inversion 0.2
-
-1- 	Version 0.2.2
-	- Release date: 7/4/2009
-	- Minor fixes
-	  
- 	Version 0.2.1
-	- Release date: 7/8/2008
-	- Minor change to clone method
-	  
-	Version 0.2.0
-	
-	- Release date: 7/4/2008
-	- Number of term estimator
-	- Faster implementation
-
-	Version 0.1.0
-
-	- Release date: 5/28/2008
-	- Provide python module sas.pr
-
-2- Downloading and Installing
-
-	2.1- System Requirements:
-		- Python version >= 2.4 should be running on the system
-
-	2.2- Installing:
-		- Get the code from svn://danse.us/sas/releases/pr_inversion-0.1.0
-		- The following modules are required:
-			* matplotlib
-			* numpy
-			* scipy
-
-3- Known Issues
-
-	3.1- All systems:
-		- None
-
-	3.2- Windows:
-		- None
-
-	3.3- Linux:
-		- None
-
-4- Troubleshooting
-
-	- None
-
-5- Frequently Asked Questions
-
-	- None
-
-6- Other Resources
-
-	- See: http://danse.chem.utk.edu/prview.html
-
-	
-
-	
-
-
Index: src/sas/sascalc/calculator/instrument.py
===================================================================
--- src/sas/sascalc/calculator/instrument.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/calculator/instrument.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,390 @@
+"""
+This module is a small tool to allow user to
+control instrumental parameters
+"""
+import numpy
+
+# defaults in cgs unit
+_SAMPLE_A_SIZE = [1.27]
+_SOURCE_A_SIZE = [3.81]
+_SAMPLE_DISTANCE = [1627, 0]
+_SAMPLE_OFFSET = [0, 0]
+_SAMPLE_SIZE = [2.54]
+_SAMPLE_THICKNESS = 0.2
+_D_DISTANCE = [1000, 0]
+_D_SIZE = [128, 128]
+_D_PIX_SIZE = [0.5, 0.5]
+
+_MIN = 0.0
+_MAX = 50.0
+_INTENSITY = 368428
+_WAVE_LENGTH = 6.0
+_WAVE_SPREAD = 0.125
+_MASS = 1.67492729E-24  # [gr]
+_LAMBDA_ARRAY = [[0, 1e+16], [_INTENSITY, _INTENSITY]]
+
+
+class Aperture(object):
+    """
+    An object class that defines the aperture variables
+    """
+    def __init__(self):
+
+        # assumes that all aligned at the centers
+        # aperture_size [diameter] for pinhole, [dx, dy] for rectangular
+        self.sample_size = _SAMPLE_A_SIZE
+        self.source_size = _SOURCE_A_SIZE
+        self.sample_distance = _SAMPLE_DISTANCE
+
+    def set_source_size(self, size=[]):
+        """
+        Set the source aperture size
+        """
+        if len(size) == 0:
+            self.source_size = 0.0
+        else:
+            self.source_size = size
+            validate(size[0])
+
+    def set_sample_size(self, size=[]):
+        """
+        Set the sample aperture size
+        """
+        if len(size) == 0:
+            self.sample_size = 0.0
+        else:
+            self.sample_size = size
+            validate(size[0])
+
+    def set_sample_distance(self, distance=[]):
+        """
+        Set the sample aperture distance
+        """
+        if len(distance) == 0:
+            self.sample_distance = 0.0
+        else:
+            self.sample_distance = distance
+            validate(distance[0])
+
+
+class Sample(object):
+    """
+    An object class that defines the sample variables
+    """
+    def __init__(self):
+
+        # assumes that all aligned at the centers
+        # source2sample or sample2detector distance
+        self.distance = _SAMPLE_OFFSET
+        self.size = _SAMPLE_SIZE
+        self.thickness = _SAMPLE_THICKNESS
+
+    def set_size(self, size=[]):
+        """
+        Set the sample size
+        """
+        if len(size) == 0:
+            self.size = 0.0
+        else:
+            self.size = size
+            validate(size[0])
+
+    def set_thickness(self, thickness=0.0):
+        """
+        Set the sample thickness
+        """
+        self.thickness = thickness
+        validate(thickness)
+
+    def set_distance(self, distance=[]):
+        """
+        Set the sample distance
+        """
+        if len(distance) == 0:
+            self.distance = 0.0
+        else:
+            self.distance = distance
+            if distance[0] != 0.0:
+                validate(distance[0])
+
+
+class Detector(object):
+    """
+    An object class that defines the detector variables
+    """
+    def __init__(self):
+
+        # assumes that all aligned at the centers
+        # source2sample or sample2detector distance
+        self.distance = _D_DISTANCE
+        self.size = _D_SIZE
+        self.pix_size = _D_PIX_SIZE
+
+    def set_size(self, size=[]):
+        """
+        Set the detector  size
+        """
+        if len(size) == 0:
+            self.size = 0
+        else:
+            self.size = size
+            validate(size[0])
+
+    def set_pix_size(self, size=[]):
+        """
+        Set the detector pix_size
+        """
+        if len(size) == 0:
+            self.pix_size = 0
+        else:
+            self.pix_size = size
+            validate(size[0])
+
+    def set_distance(self, distance=[]):
+        """
+        Set the detector distance
+        """
+        if len(distance) == 0:
+            self.distance = 0
+        else:
+            self.distance = distance
+            validate(distance[0])
+
+
+class Neutron(object):
+    """
+    An object that defines the wavelength variables
+    """
+    def __init__(self):
+
+        # neutron mass in cgs unit
+        self.mass = _MASS
+
+        # wavelength
+        self.wavelength = _WAVE_LENGTH
+        # wavelength spread (FWHM)
+        self.wavelength_spread = _WAVE_SPREAD
+        # wavelength spectrum
+        self.spectrum = self.get_default_spectrum()
+        # intensity in counts/sec
+        self.intensity = numpy.interp(self.wavelength,
+                                      self.spectrum[0],
+                                      self.spectrum[1],
+                                      0.0,
+                                      0.0)
+        # min max range of the spectrum
+        self.min = min(self.spectrum[0])
+        self.max = max(self.spectrum[0])
+        # wavelength band
+        self.band = [self.min, self.max]
+
+        # default unit of the thickness
+        self.wavelength_unit = 'A'
+
+    def set_full_band(self):
+        """
+        set band to default value
+        """
+        self.band = self.spectrum
+
+    def set_spectrum(self, spectrum):
+        """
+        Set spectrum
+
+        :param spectrum: numpy array
+        """
+        self.spectrum = spectrum
+        self.setup_spectrum()
+
+    def setup_spectrum(self):
+        """
+        To set the wavelength spectrum, and intensity, assumes
+        wavelength is already within the spectrum
+        """
+        spectrum = self.spectrum
+        intensity = numpy.interp(self.wavelength,
+                                 spectrum[0],
+                                 spectrum[1],
+                                 0.0,
+                                 0.0)
+        self.set_intensity(intensity)
+        # min max range of the spectrum
+        self.min = min(self.spectrum[0])
+        self.max = max(self.spectrum[0])
+        # set default band
+        self.set_band([self.min, self.max])
+
+    def set_band(self, band=[]):
+        """
+        To set the wavelength band
+
+        :param band: array of [min, max]
+        """
+        # check if the wavelength is in range
+        if min(band) < self.min or\
+                max(band) > self.max:
+            raise
+        self.band = band
+
+    def set_intensity(self, intensity=368428):
+        """
+        Sets the intensity in counts/sec
+        """
+        self.intensity = intensity
+        validate(intensity)
+
+    def set_wavelength(self, wavelength=_WAVE_LENGTH):
+        """
+        Sets the wavelength
+        """
+        # check if the wavelength is in range
+        if wavelength < min(self.band) or\
+                wavelength > max(self.band):
+            raise
+        self.wavelength = wavelength
+        validate(wavelength)
+        self.intensity = numpy.interp(self.wavelength,
+                                      self.spectrum[0],
+                                      self.spectrum[1],
+                                      0.0,
+                                      0.0)
+
+    def set_mass(self, mass=_MASS):
+        """
+        Sets the wavelength
+        """
+        self.mass = mass
+        validate(mass)
+
+    def set_wavelength_spread(self, spread=_WAVE_SPREAD):
+        """
+        Sets the wavelength spread
+        """
+        self.wavelength_spread = spread
+        if spread != 0.0:
+            validate(spread)
+
+    def get_intensity(self):
+        """
+        To get the value of intensity
+        """
+        return self.intensity
+
+    def get_wavelength(self):
+        """
+        To get the value of wavelength
+        """
+        return self.wavelength
+
+    def get_mass(self):
+        """
+        To get the neutron mass
+        """
+        return self.mass
+
+    def get_wavelength_spread(self):
+        """
+        To get the value of wavelength spread
+        """
+        return self.wavelength_spread
+
+    def get_ramdom_value(self):
+        """
+        To get the value of wave length
+        """
+        return self.wavelength
+
+    def get_spectrum(self):
+        """
+        To get the wavelength spectrum
+        """
+        return self.spectrum
+
+    def get_default_spectrum(self):
+        """
+        get default spectrum
+        """
+        return numpy.array(_LAMBDA_ARRAY)
+
+    def get_band(self):
+        """
+        To get the wavelength band
+        """
+        return self.band
+
+    def plot_spectrum(self):
+        """
+        To plot the wavelength spactrum
+        : requirment: matplotlib.pyplot
+        """
+        try:
+            import matplotlib.pyplot as plt
+            plt.plot(self.spectrum[0], self.spectrum[1], linewidth=2, color='r')
+            plt.legend(['Spectrum'], loc='best')
+            plt.show()
+        except:
+            raise RuntimeError, "Can't import matplotlib required to plot..."
+
+
+class TOF(Neutron):
+    """
+    TOF: make list of wavelength and wave length spreads
+    """
+    def __init__(self):
+        """
+        Init
+        """
+        Neutron.__init__(self)
+        #self.switch = switch
+        self.wavelength_list = [self.wavelength]
+        self.wavelength_spread_list = [self.wavelength_spread]
+        self.intensity_list = self.get_intensity_list()
+
+    def get_intensity_list(self):
+        """
+        get list of the intensity wrt wavelength_list
+        """
+        out = numpy.interp(self.wavelength_list,
+                           self.spectrum[0],
+                           self.spectrum[1],
+                           0.0,
+                           0.0)
+        return out
+
+    def get_wave_list(self):
+        """
+        Get wavelength and wavelength_spread list
+        """
+        return self.wavelength_list, self.wavelength_spread_list
+
+    def set_wave_list(self, wavelength=[]):
+        """
+        Set wavelength list
+
+        :param wavelength: list of wavelengths
+        """
+        self.wavelength_list = wavelength
+
+    def set_wave_spread_list(self, wavelength_spread=[]):
+        """
+        Set wavelength_spread list
+
+        :param wavelength_spread: list of wavelength spreads
+        """
+        self.wavelength_spread_list = wavelength_spread
+
+
+def validate(value=None):
+    """
+    Check if the value is folat > 0.0
+
+    :return value: True / False
+    """
+    try:
+        val = float(value)
+        if val >= 0:
+            val = True
+        else:
+            val = False
+    except:
+        val = False
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 b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,64 @@
+"""
+This module is a small tool to allow user to quickly
+determine the size value in real space  from the
+fringe width in q space.
+"""
+from math import pi, fabs
+_DQ_DEFAULT = 0.05
+
+
+class KiessigThicknessCalculator(object):
+    """
+    compute thickness from the fringe width of data
+    """
+    def __init__(self):
+
+        # dq value
+        self.deltaq = _DQ_DEFAULT
+        # thickenss value
+        self.thickness = None
+        # unit of the thickness
+        self.thickness_unit = 'A'
+
+    def set_deltaq(self, dq=None):
+        """
+        Receive deltaQ value
+
+        :param dq: q fringe width in 1/A unit
+        """
+        # set dq
+        self.deltaq = dq
+
+    def get_deltaq(self):
+        """
+        return deltaQ value in 1/A unit
+        """
+        # return dq
+        return self.deltaq
+
+    def compute_thickness(self):
+        """
+        Calculate thickness.
+
+        :return: the thickness.
+        """
+        # check if it is float
+        try:
+            dq = float(self.deltaq)
+        except:
+            return None
+        # check if delta_q is zero
+        if dq == 0.0 or dq == None:
+            return None
+        else:
+            # calculate thickness
+            thickness = 2*pi/fabs(dq)
+            # return thickness value
+            return thickness
+
+    def get_thickness_unit(self):
+        """
+        :return: the thickness unit.
+        """
+        # unit of thickness
+        return self.thickness_unit
Index: src/sas/sascalc/calculator/resolution_calculator.py
===================================================================
--- src/sas/sascalc/calculator/resolution_calculator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/calculator/resolution_calculator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1167 @@
+"""
+This object is a small tool to allow user to quickly
+determine the variance in q  from the
+instrumental parameters.
+"""
+from instrument import Sample
+from instrument import Detector
+from instrument import TOF as Neutron
+from instrument import Aperture
+# import math stuffs
+from math import pi
+from math import sqrt
+import math
+import numpy
+import sys
+import logging
+
+#Plank's constant in cgs unit
+_PLANK_H = 6.62606896E-27
+#Gravitational acc. in cgs unit
+_GRAVITY = 981.0
+
+
+class ResolutionCalculator(object):
+    """
+    compute resolution in 2D
+    """
+    def __init__(self):
+
+        # wavelength
+        self.wave = Neutron()
+        # sample
+        self.sample = Sample()
+        # aperture
+        self.aperture = Aperture()
+        # detector
+        self.detector = Detector()
+        # 2d image of the resolution
+        self.image = []
+        self.image_lam = []
+        # resolutions
+        # lamda in r-direction
+        self.sigma_lamd = 0
+        # x-dir (no lamda)
+        self.sigma_1 = 0
+        #y-dir (no lamda)
+        self.sigma_2 = 0
+        # 1D total
+        self.sigma_1d = 0
+        self.gravity_phi = None
+        # q min and max
+        self.qx_min = -0.3
+        self.qx_max = 0.3
+        self.qy_min = -0.3
+        self.qy_max = 0.3
+        # q min and max of the detector
+        self.detector_qx_min = -0.3
+        self.detector_qx_max = 0.3
+        self.detector_qy_min = -0.3
+        self.detector_qy_max = 0.3
+        # possible max qrange
+        self.qxmin_limit = 0
+        self.qxmax_limit = 0
+        self.qymin_limit = 0
+        self.qymax_limit = 0
+
+        # plots
+        self.plot = None
+        # instrumental params defaults
+        self.mass = 0
+        self.intensity = 0
+        self.wavelength = 0
+        self.wavelength_spread = 0
+        self.source_aperture_size = []
+        self.source2sample_distance = []
+        self.sample2sample_distance = []
+        self.sample_aperture_size = []
+        self.sample2detector_distance = []
+        self.detector_pix_size = []
+        self.detector_size = []
+        self.get_all_instrument_params()
+        # max q range for all lambdas
+        self.qxrange = []
+        self.qyrange = []
+
+    def compute_and_plot(self, qx_value, qy_value, qx_min, qx_max,
+                         qy_min, qy_max, coord='cartesian'):
+        """
+        Compute the resolution
+        : qx_value: x component of q
+        : qy_value: y component of q
+        """
+        # make sure to update all the variables need.
+        # except lambda, dlambda, and intensity
+        self.get_all_instrument_params()
+        # wavelength etc.
+        lamda_list, dlamb_list = self.get_wave_list()
+        intens_list = []
+        sig1_list = []
+        sig2_list = []
+        sigr_list = []
+        sigma1d_list = []
+        num_lamda = len(lamda_list)
+        for num in range(num_lamda):
+            lam = lamda_list[num]
+            # wavelength spread
+            dlam = dlamb_list[num]
+            intens = self.setup_tof(lam, dlam)
+            intens_list.append(intens)
+            # cehck if tof
+            if num_lamda > 1:
+                tof = True
+            else:
+                tof = False
+            # compute 2d resolution
+            _, _, sigma_1, sigma_2, sigma_r, sigma1d = \
+                        self.compute(lam, dlam, qx_value, qy_value, coord, tof)
+            # make image
+            image = self.get_image(qx_value, qy_value, sigma_1, sigma_2,
+                                   sigma_r, qx_min, qx_max, qy_min, qy_max,
+                                   coord, False)
+            if qx_min > self.qx_min:
+                qx_min = self.qx_min
+            if qx_max < self.qx_max:
+                qx_max = self.qx_max
+            if qy_min > self.qy_min:
+                qy_min = self.qy_min
+            if qy_max < self.qy_max:
+                qy_max = self.qy_max
+
+            # set max qranges
+            self.qxrange = [qx_min, qx_max]
+            self.qyrange = [qy_min, qy_max]
+            sig1_list.append(sigma_1)
+            sig2_list.append(sigma_2)
+            sigr_list.append(sigma_r)
+            sigma1d_list.append(sigma1d)
+        # redraw image in global 2d q-space.
+        self.image_lam = []
+        total_intensity = 0
+        sigma_1 = 0
+        sigma_r = 0
+        sigma_2 = 0
+        sigma1d = 0
+        for ind in range(num_lamda):
+            lam = lamda_list[ind]
+            dlam = dlamb_list[ind]
+            intens = self.setup_tof(lam, dlam)
+            out = self.get_image(qx_value, qy_value, sig1_list[ind],
+                                 sig2_list[ind], sigr_list[ind],
+                                 qx_min, qx_max, qy_min, qy_max, coord)
+            # this is the case of q being outside the detector
+            #if numpy.all(out==0.0):
+            #    continue
+            image = out
+            # set variance as sigmas
+            sigma_1 += sig1_list[ind] * sig1_list[ind] * self.intensity
+            sigma_r += sigr_list[ind] * sigr_list[ind] * self.intensity
+            sigma_2 += sig2_list[ind] * sig2_list[ind] * self.intensity
+            sigma1d += sigma1d_list[ind] * sigma1d_list[ind] * self.intensity
+            total_intensity += self.intensity
+
+        if total_intensity != 0:
+            # average variance
+            image_out = image / total_intensity
+            sigma_1 = sigma_1 / total_intensity
+            sigma_r = sigma_r / total_intensity
+            sigma_2 = sigma_2 / total_intensity
+            sigma1d = sigma1d / total_intensity
+            # set sigmas
+            self.sigma_1 = sqrt(sigma_1)
+            self.sigma_lamd = sqrt(sigma_r)
+            self.sigma_2 = sqrt(sigma_2)
+            self.sigma_1d = sqrt(sigma1d)
+            # rescale
+            max_im_val = 1
+            if max_im_val > 0:
+                image_out /= max_im_val
+        else:
+            image_out = image * 0.0
+            # Don't calculate sigmas nor set self.sigmas!
+            sigma_1 = 0
+            sigma_r = 0
+            sigma_2 = 0
+            sigma1d = 0
+        if len(self.image) > 0:
+            self.image += image_out
+        else:
+            self.image = image_out
+
+        # plot image
+        return self.plot_image(self.image)
+
+    def setup_tof(self, wavelength, wavelength_spread):
+        """
+        Setup all parameters in instrument
+
+        : param ind: index of lambda, etc
+        """
+
+        # set wave.wavelength
+        self.set_wavelength(wavelength)
+        self.set_wavelength_spread(wavelength_spread)
+        self.intensity = self.wave.get_intensity()
+
+        if wavelength == 0:
+            msg = "Can't compute the resolution: the wavelength is zero..."
+            raise RuntimeError, msg
+        return self.intensity
+
+    def compute(self, wavelength, wavelength_spread, qx_value, qy_value,
+                coord='cartesian', tof=False):
+        """
+        Compute the Q resoltuion in || and + direction of 2D
+        : qx_value: x component of q
+        : qy_value: y component of q
+        """
+        coord = 'cartesian'
+        lamb = wavelength
+        lamb_spread = wavelength_spread
+        # the shape of wavelength distribution
+
+        if tof:
+            # rectangular
+            tof_factor = 2
+        else:
+            # triangular
+            tof_factor = 1
+        # Find polar values
+        qr_value, phi = self._get_polar_value(qx_value, qy_value)
+        # vacuum wave transfer
+        knot = 2*pi/lamb
+        # scattering angle theta; always true for plane detector
+        # aligned vertically to the ko direction
+        if qr_value > knot:
+            theta = pi/2
+        else:
+            theta = math.asin(qr_value/knot)
+        # source aperture size
+        rone = self.source_aperture_size
+        # sample aperture size
+        rtwo = self.sample_aperture_size
+        # detector pixel size
+        rthree = self.detector_pix_size
+        # source to sample(aperture) distance
+        l_ssa = self.source2sample_distance[0]
+        # sample(aperture) to detector distance
+        l_sad = self.sample2detector_distance[0]
+        # sample (aperture) to sample distance
+        l_sas = self.sample2sample_distance[0]
+        # source to sample distance
+        l_one = l_ssa + l_sas
+        # sample to detector distance
+        l_two = l_sad - l_sas
+
+        # Sample offset correction for l_one and Lp on variance calculation
+        l1_cor = (l_ssa * l_two) / (l_sas + l_two)
+        lp_cor = (l_ssa * l_two) / (l_one + l_two)
+        # the radial distance to the pixel from the center of the detector
+        radius = math.tan(theta) * l_two
+        #Lp = l_one*l_two/(l_one+l_two)
+        # default polar coordinate
+        comp1 = 'radial'
+        comp2 = 'phi'
+        # in the case of the cartesian coordinate
+        if coord == 'cartesian':
+            comp1 = 'x'
+            comp2 = 'y'
+
+        # sigma in the radial/x direction
+        # for source aperture
+        sigma_1 = self.get_variance(rone, l1_cor, phi, comp1)
+        # for sample apperture
+        sigma_1 += self.get_variance(rtwo, lp_cor, phi, comp1)
+        # for detector pix
+        sigma_1 += self.get_variance(rthree, l_two, phi, comp1)
+        # for gravity term for 1d
+        sigma_1grav1d = self.get_variance_gravity(l_ssa, l_sad, lamb,
+                                                  lamb_spread, phi, comp1, 'on') / tof_factor
+        # for wavelength spread
+        # reserve for 1d calculation
+        A_value = self._cal_A_value(lamb, l_ssa, l_sad)
+        sigma_wave_1, sigma_wave_1_1d = self.get_variance_wave(A_value,
+                                                               radius, l_two, lamb_spread,
+                                                               phi, 'radial', 'on')
+        sigma_wave_1 /= tof_factor
+        sigma_wave_1_1d /= tof_factor
+        # for 1d
+        variance_1d_1 = (sigma_1 + sigma_1grav1d) / 2 + sigma_wave_1_1d
+        # normalize
+        variance_1d_1 = knot * knot * variance_1d_1 / 12
+
+        # for 2d
+        #sigma_1 += sigma_wave_1
+        # normalize
+        sigma_1 = knot * sqrt(sigma_1 / 12)
+        sigma_r = knot * sqrt(sigma_wave_1 / (tof_factor *12))
+        # sigma in the phi/y direction
+        # for source apperture
+        sigma_2 = self.get_variance(rone, l1_cor, phi, comp2)
+
+        # for sample apperture
+        sigma_2 += self.get_variance(rtwo, lp_cor, phi, comp2)
+
+        # for detector pix
+        sigma_2 += self.get_variance(rthree, l_two, phi, comp2)
+
+        # for gravity term for 1d
+        sigma_2grav1d = self.get_variance_gravity(l_ssa, l_sad, lamb,
+                                                  lamb_spread, phi, comp2, 'on') / tof_factor
+
+        # for wavelength spread
+        # reserve for 1d calculation
+        sigma_wave_2, sigma_wave_2_1d = self.get_variance_wave(A_value,
+                                                               radius, l_two, lamb_spread,
+                                                               phi, 'phi', 'on')
+        sigma_wave_2 /= tof_factor
+        sigma_wave_2_1d /= tof_factor
+        # for 1d
+        variance_1d_2 = (sigma_2 + sigma_2grav1d) / 2 + sigma_wave_2_1d
+        # normalize
+        variance_1d_2 = knot * knot * variance_1d_2 / 12
+
+        # for 2d
+        #sigma_2 =  knot*sqrt(sigma_2/12)
+        #sigma_2 += sigma_wave_2
+        # normalize
+        sigma_2 = knot * sqrt(sigma_2 / 12)
+        sigma1d = sqrt(variance_1d_1 + variance_1d_2)
+        # set sigmas
+        self.sigma_1 = sigma_1
+        self.sigma_lamd = sigma_r
+        self.sigma_2 = sigma_2
+        self.sigma_1d = sigma1d
+        return qr_value, phi, sigma_1, sigma_2, sigma_r, sigma1d
+
+    def _within_detector_range(self, qx_value, qy_value):
+        """
+        check if qvalues are within detector range
+        """
+        # detector range
+        detector_qx_min = self.detector_qx_min
+        detector_qx_max = self.detector_qx_max
+        detector_qy_min = self.detector_qy_min
+        detector_qy_max = self.detector_qy_max
+        if self.qxmin_limit > detector_qx_min:
+            self.qxmin_limit = detector_qx_min
+        if self.qxmax_limit < detector_qx_max:
+            self.qxmax_limit = detector_qx_max
+        if self.qymin_limit > detector_qy_min:
+            self.qymin_limit = detector_qy_min
+        if self.qymax_limit < detector_qy_max:
+            self.qymax_limit = detector_qy_max
+        if qx_value < detector_qx_min or qx_value > detector_qx_max:
+            return False
+        if qy_value < detector_qy_min or qy_value > detector_qy_max:
+            return False
+        return True
+
+    def get_image(self, qx_value, qy_value, sigma_1, sigma_2, sigma_r,
+                  qx_min, qx_max, qy_min, qy_max,
+                  coord='cartesian', full_cal=True):
+        """
+        Get the resolution in polar coordinate ready to plot
+        : qx_value: qx_value value
+        : qy_value: qy_value value
+        : sigma_1: variance in r direction
+        : sigma_2: variance in phi direction
+        : coord: coordinate system of image, 'polar' or 'cartesian'
+        """
+        # Get  qx_max and qy_max...
+        self._get_detector_qxqy_pixels()
+
+        qr_value, phi = self._get_polar_value(qx_value, qy_value)
+
+        # Check whether the q value is within the detector range
+        if qx_min < self.qx_min:
+            self.qx_min = qx_min
+            #raise ValueError, msg
+        if qx_max > self.qx_max:
+            self.qx_max = qx_max
+            #raise ValueError, msg
+        if qy_min < self.qy_min:
+            self.qy_min = qy_min
+            #raise ValueError, msg
+        if qy_max > self.qy_max:
+            self.qy_max = qy_max
+            #raise ValueError, msg
+        if not full_cal:
+            return None
+
+        # Make an empty graph in the detector scale
+        dx_size = (self.qx_max - self.qx_min) / (1000 - 1)
+        dy_size = (self.qy_max - self.qy_min) / (1000 - 1)
+        x_val = numpy.arange(self.qx_min, self.qx_max, dx_size)
+        y_val = numpy.arange(self.qy_max, self.qy_min, -dy_size)
+        q_1, q_2 = numpy.meshgrid(x_val, y_val)
+        #q_phi = numpy.arctan(q_1,q_2)
+        # check whether polar or cartesian
+        if coord == 'polar':
+            # Find polar values
+            qr_value, phi = self._get_polar_value(qx_value, qy_value)
+            q_1, q_2 = self._rotate_z(q_1, q_2, phi)
+            qc_1 = qr_value
+            qc_2 = 0.0
+            # Calculate the 2D Gaussian distribution image
+            image = self._gaussian2d_polar(q_1, q_2, qc_1, qc_2,
+                                           sigma_1, sigma_2, sigma_r)
+        else:
+            # catesian coordinate
+            # qx_center
+            qc_1 = qx_value
+            # qy_center
+            qc_2 = qy_value
+
+            # Calculate the 2D Gaussian distribution image
+            image = self._gaussian2d(q_1, q_2, qc_1, qc_2,
+                                     sigma_1, sigma_2, sigma_r)
+        # out side of detector
+        if not self._within_detector_range(qx_value, qy_value):
+            image *= 0.0
+            self.intensity = 0.0
+            #return self.image
+
+        # Add it if there are more than one inputs.
+        if len(self.image_lam) > 0:
+            self.image_lam += image * self.intensity
+        else:
+            self.image_lam = image * self.intensity
+
+        return self.image_lam
+
+    def plot_image(self, image):
+        """
+        Plot image using pyplot
+        : image: 2d resolution image
+
+        : return plt: pylab object
+        """
+        import matplotlib.pyplot as plt
+
+        self.plot = plt
+        plt.xlabel('$\\rm{Q}_{x} [A^{-1}]$')
+        plt.ylabel('$\\rm{Q}_{y} [A^{-1}]$')
+        # Max value of the image
+        # max = numpy.max(image)
+        qx_min, qx_max, qy_min, qy_max = self.get_detector_qrange()
+
+        # Image
+        im = plt.imshow(image,
+                        extent=[qx_min, qx_max, qy_min, qy_max])
+
+        # bilinear interpolation to make it smoother
+        im.set_interpolation('bilinear')
+
+        return plt
+
+    def reset_image(self):
+        """
+        Reset image to default (=[])
+        """
+        self.image = []
+
+    def get_variance(self, size=[], distance=0, phi=0, comp='radial'):
+        """
+        Get the variance when the slit/pinhole size is given
+        : size: list that can be one(diameter for circular) or two components(lengths for rectangular)
+        : distance: [z, x] where z along the incident beam, x // qx_value
+        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
+
+        : return variance: sigma^2
+        """
+        # check the length of size (list)
+        len_size = len(size)
+
+        # define sigma component direction
+        if comp == 'radial':
+            phi_x = math.cos(phi)
+            phi_y = math.sin(phi)
+        elif comp == 'phi':
+            phi_x = math.sin(phi)
+            phi_y = math.cos(phi)
+        elif comp == 'x':
+            phi_x = 1
+            phi_y = 0
+        elif comp == 'y':
+            phi_x = 0
+            phi_y = 1
+        else:
+            phi_x = 0
+            phi_y = 0
+        # calculate each component
+        # for pinhole w/ radius = size[0]/2
+        if len_size == 1:
+            x_comp = (0.5 * size[0]) * sqrt(3)
+            y_comp = 0
+        # for rectangular slit
+        elif len_size == 2:
+            x_comp = size[0] * phi_x
+            y_comp = size[1] * phi_y
+        # otherwise
+        else:
+            raise ValueError, " Improper input..."
+        # get them squared
+        sigma = x_comp * x_comp
+        sigma += y_comp * y_comp
+        # normalize by distance
+        sigma /= (distance * distance)
+
+        return sigma
+
+    def get_variance_wave(self, A_value, radius, distance, spread, phi,
+                          comp='radial', switch='on'):
+        """
+        Get the variance when the wavelength spread is given
+
+        : radius: the radial distance from the beam center to the pix of q
+        : distance: sample to detector distance
+        : spread: wavelength spread (ratio)
+        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
+
+        : return variance: sigma^2 for 2d, sigma^2 for 1d [tuple]
+        """
+        if switch.lower() == 'off':
+            return 0, 0
+        # check the singular point
+        if distance == 0 or comp == 'phi':
+            return 0, 0
+        else:
+            # calculate sigma^2 for 1d
+            sigma1d = 2 * math.pow(radius/distance*spread, 2)
+            if comp == 'x':
+                sigma1d *= (math.cos(phi)*math.cos(phi))
+            elif comp == 'y':
+                sigma1d *= (math.sin(phi)*math.sin(phi))
+            else:
+                sigma1d *= 1
+            # sigma^2 for 2d
+            # shift the coordinate due to the gravitational shift
+            rad_x = radius * math.cos(phi)
+            rad_y = A_value - radius * math.sin(phi)
+            radius = math.sqrt(rad_x * rad_x + rad_y * rad_y)
+            # new phi
+            phi = math.atan2(-rad_y, rad_x)
+            self.gravity_phi = phi
+            # calculate sigma^2
+            sigma = 2 * math.pow(radius/distance*spread, 2)
+            if comp == 'x':
+                sigma *= (math.cos(phi)*math.cos(phi))
+            elif comp == 'y':
+                sigma *= (math.sin(phi)*math.sin(phi))
+            else:
+                sigma *= 1
+
+            return sigma, sigma1d
+
+    def get_variance_gravity(self, s_distance, d_distance, wavelength, spread,
+                             phi, comp='radial', switch='on'):
+        """
+        Get the variance from gravity when the wavelength spread is given
+
+        : s_distance: source to sample distance
+        : d_distance: sample to detector distance
+        : wavelength: wavelength
+        : spread: wavelength spread (ratio)
+        : comp: direction of the sigma; can be 'phi', 'y', 'x', and 'radial'
+
+        : return variance: sigma^2
+        """
+        if switch.lower() == 'off':
+            return 0
+        if self.mass == 0.0:
+            return 0
+        # check the singular point
+        if d_distance == 0 or comp == 'x':
+            return 0
+        else:
+            a_value = self._cal_A_value(None, s_distance, d_distance)
+            # calculate sigma^2
+            sigma = math.pow(a_value / d_distance, 2)
+            sigma *= math.pow(wavelength, 4)
+            sigma *= math.pow(spread, 2)
+            sigma *= 8
+            return sigma
+
+    def _cal_A_value(self, lamda, s_distance, d_distance):
+        """
+        Calculate A value for gravity
+
+        : s_distance: source to sample distance
+        : d_distance: sample to detector distance
+        """
+        # neutron mass in cgs unit
+        self.mass = self.get_neutron_mass()
+        # plank constant in cgs unit
+        h_constant = _PLANK_H
+        # gravity in cgs unit
+        gravy = _GRAVITY
+        # m/h
+        m_over_h = self.mass / h_constant
+        # A value
+        a_value = d_distance * (s_distance + d_distance)
+        a_value *= math.pow(m_over_h / 2, 2)
+        a_value *= gravy
+        # unit correction (1/cm to 1/A) for A and d_distance below
+        a_value *= 1.0E-16
+        # if lamda is give (broad meanning of A)  return 2* lamda^2 * A
+        if lamda != None:
+            a_value *= (4 * lamda * lamda)
+        return a_value
+
+    def get_intensity(self):
+        """
+        Get intensity
+        """
+        return self.wave.intensity
+
+    def get_wavelength(self):
+        """
+        Get wavelength
+        """
+        return self.wave.wavelength
+
+    def get_default_spectrum(self):
+        """
+        Get default_spectrum
+        """
+        return self.wave.get_default_spectrum()
+
+    def get_spectrum(self):
+        """
+        Get _spectrum
+        """
+        return self.wave.get_spectrum()
+
+    def get_wavelength_spread(self):
+        """
+        Get wavelength spread
+        """
+        return self.wave.wavelength_spread
+
+    def get_neutron_mass(self):
+        """
+        Get Neutron mass
+        """
+        return self.wave.mass
+
+    def get_source_aperture_size(self):
+        """
+        Get source aperture size
+        """
+        return self.aperture.source_size
+
+    def get_sample_aperture_size(self):
+        """
+        Get sample aperture size
+        """
+        return self.aperture.sample_size
+
+    def get_detector_pix_size(self):
+        """
+        Get detector pixel size
+        """
+        return self.detector.pix_size
+
+    def get_detector_size(self):
+        """
+        Get detector size
+        """
+        return self.detector.size
+
+    def get_source2sample_distance(self):
+        """
+        Get detector source2sample_distance
+        """
+        return self.aperture.sample_distance
+
+    def get_sample2sample_distance(self):
+        """
+        Get detector sampleslitsample_distance
+        """
+        return self.sample.distance
+
+    def get_sample2detector_distance(self):
+        """
+        Get detector sample2detector_distance
+        """
+        return self.detector.distance
+
+    def set_intensity(self, intensity):
+        """
+        Set intensity
+        """
+        self.wave.set_intensity(intensity)
+
+    def set_wave(self, wavelength):
+        """
+        Set wavelength list or wavelength
+        """
+        if wavelength.__class__.__name__ == 'list':
+            self.wave.set_wave_list(wavelength)
+        elif wavelength.__class__.__name__ == 'float':
+            self.wave.set_wave_list([wavelength])
+            #self.set_wavelength(wavelength)
+        else:
+            raise
+
+    def set_wave_spread(self, wavelength_spread):
+        """
+        Set wavelength spread  or wavelength spread
+        """
+        if wavelength_spread.__class__.__name__ == 'list':
+            self.wave.set_wave_spread_list(wavelength_spread)
+        elif wavelength_spread.__class__.__name__ == 'float':
+            self.wave.set_wave_spread_list([wavelength_spread])
+        else:
+            raise
+
+    def set_wavelength(self, wavelength):
+        """
+        Set wavelength
+        """
+        self.wavelength = wavelength
+        self.wave.set_wavelength(wavelength)
+
+    def set_spectrum(self, spectrum):
+        """
+        Set spectrum
+        """
+        self.spectrum = spectrum
+        self.wave.set_spectrum(spectrum)
+
+    def set_wavelength_spread(self, wavelength_spread):
+        """
+        Set wavelength spread
+        """
+        self.wavelength_spread = wavelength_spread
+        self.wave.set_wavelength_spread(wavelength_spread)
+
+    def set_wave_list(self, wavelength_list, wavelengthspread_list):
+        """
+        Set wavelength and its spread list
+        """
+        self.wave.set_wave_list(wavelength_list)
+        self.wave.set_wave_spread_list(wavelengthspread_list)
+
+    def get_wave_list(self):
+        """
+        Set wavelength spread
+        """
+        return self.wave.get_wave_list()
+
+    def get_intensity_list(self):
+        """
+        Set wavelength spread
+        """
+        return self.wave.get_intensity_list()
+
+    def set_source_aperture_size(self, size):
+        """
+        Set source aperture size
+
+        : param size: [dia_value] or [x_value, y_value]
+        """
+        if len(size) < 1 or len(size) > 2:
+            raise RuntimeError, "The length of the size must be one or two."
+        self.aperture.set_source_size(size)
+
+    def set_neutron_mass(self, mass):
+        """
+        Set Neutron mass
+        """
+        self.wave.set_mass(mass)
+        self.mass = mass
+
+    def set_sample_aperture_size(self, size):
+        """
+        Set sample aperture size
+
+        : param size: [dia_value] or [xheight_value, yheight_value]
+        """
+        if len(size) < 1 or len(size) > 2:
+            raise RuntimeError, "The length of the size must be one or two."
+        self.aperture.set_sample_size(size)
+
+    def set_detector_pix_size(self, size):
+        """
+        Set detector pixel size
+        """
+        self.detector.set_pix_size(size)
+
+    def set_detector_size(self, size):
+        """
+        Set detector size in number of pixels
+        : param size: [pixel_nums] or [x_pix_num, yx_pix_num]
+        """
+        self.detector.set_size(size)
+
+    def set_source2sample_distance(self, distance):
+        """
+        Set detector source2sample_distance
+
+        : param distance: [distance, x_offset]
+        """
+        if len(distance) < 1 or len(distance) > 2:
+            raise RuntimeError, "The length of the size must be one or two."
+        self.aperture.set_sample_distance(distance)
+
+    def set_sample2sample_distance(self, distance):
+        """
+        Set detector sample_slit2sample_distance
+
+        : param distance: [distance, x_offset]
+        """
+        if len(distance) < 1 or len(distance) > 2:
+            raise RuntimeError, "The length of the size must be one or two."
+        self.sample.set_distance(distance)
+
+    def set_sample2detector_distance(self, distance):
+        """
+        Set detector sample2detector_distance
+
+        : param distance: [distance, x_offset]
+        """
+        if len(distance) < 1 or len(distance) > 2:
+            raise RuntimeError, "The length of the size must be one or two."
+        self.detector.set_distance(distance)
+
+    def get_all_instrument_params(self):
+        """
+        Get all instrumental parameters
+        """
+        self.mass = self.get_neutron_mass()
+        self.spectrum = self.get_spectrum()
+        self.source_aperture_size = self.get_source_aperture_size()
+        self.sample_aperture_size = self.get_sample_aperture_size()
+        self.detector_pix_size = self.get_detector_pix_size()
+        self.detector_size = self.get_detector_size()
+        self.source2sample_distance = self.get_source2sample_distance()
+        self.sample2sample_distance = self.get_sample2sample_distance()
+        self.sample2detector_distance = self.get_sample2detector_distance()
+
+    def get_detector_qrange(self):
+        """
+        get max detector q ranges
+
+        : return: qx_min, qx_max, qy_min, qy_max tuple
+        """
+        if len(self.qxrange) != 2 or len(self.qyrange) != 2:
+            return None
+        qx_min = self.qxrange[0]
+        qx_max = self.qxrange[1]
+        qy_min = self.qyrange[0]
+        qy_max = self.qyrange[1]
+
+        return qx_min, qx_max, qy_min, qy_max
+
+    def _rotate_z(self, x_value, y_value, theta=0.0):
+        """
+        Rotate x-y cordinate around z-axis by theta
+        : x_value: numpy array of x values
+        : y_value: numpy array of y values
+        : theta: angle to rotate by in rad
+
+        :return: x_prime, y-prime
+        """
+        # rotate by theta
+        x_prime = x_value * math.cos(theta) + y_value * math.sin(theta)
+        y_prime = -x_value * math.sin(theta) + y_value * math.cos(theta)
+
+        return x_prime, y_prime
+
+    def _gaussian2d(self, x_val, y_val, x0_val, y0_val,
+                    sigma_x, sigma_y, sigma_r):
+        """
+        Calculate 2D Gaussian distribution
+        : x_val: x value
+        : y_val: y value
+        : x0_val: mean value in x-axis
+        : y0_val: mean value in y-axis
+        : sigma_x: variance in x-direction
+        : sigma_y: variance in y-direction
+
+        : return: gaussian (value)
+        """
+        # phi values at each points (not at the center)
+        x_value = x_val - x0_val
+        y_value = y_val - y0_val
+        phi_i = numpy.arctan2(y_val, x_val)
+
+        # phi correction due to the gravity shift (in phi)
+        phi_0 = math.atan2(y0_val, x0_val)
+        phi_i = phi_i - phi_0 + self.gravity_phi
+
+        sin_phi = numpy.sin(self.gravity_phi)
+        cos_phi = numpy.cos(self.gravity_phi)
+
+        x_p = x_value * cos_phi + y_value * sin_phi
+        y_p = -x_value * sin_phi + y_value * cos_phi
+
+        new_sig_x = sqrt(sigma_r * sigma_r / (sigma_x * sigma_x) + 1)
+        new_sig_y = sqrt(sigma_r * sigma_r / (sigma_y * sigma_y) + 1)
+        new_x = x_p * cos_phi / new_sig_x - y_p * sin_phi
+        new_x /= sigma_x
+        new_y = x_p * sin_phi / new_sig_y + y_p * cos_phi
+        new_y /= sigma_y
+
+        nu_value = -0.5 * (new_x * new_x + new_y * new_y)
+
+        gaussian = numpy.exp(nu_value)
+        # normalizing factor correction
+        gaussian /= gaussian.sum()
+
+        return gaussian
+
+    def _gaussian2d_polar(self, x_val, y_val, x0_val, y0_val,
+                          sigma_x, sigma_y, sigma_r):
+        """
+        Calculate 2D Gaussian distribution for polar coodinate
+        : x_val: x value
+        : y_val: y value
+        : x0_val: mean value in x-axis
+        : y0_val: mean value in y-axis
+        : sigma_x: variance in r-direction
+        : sigma_y: variance in phi-direction
+        : sigma_r: wavelength variance in r-direction
+
+        : return: gaussian (value)
+        """
+        sigma_x = sqrt(sigma_x * sigma_x + sigma_r * sigma_r)
+        # call gaussian1d
+        gaussian = self._gaussian1d(x_val, x0_val, sigma_x)
+        gaussian *= self._gaussian1d(y_val, y0_val, sigma_y)
+
+        # normalizing factor correction
+        if sigma_x != 0 and sigma_y != 0:
+            gaussian *= sqrt(2 * pi)
+        return gaussian
+
+    def _gaussian1d(self, value, mean, sigma):
+        """
+        Calculate 1D Gaussian distribution
+        : value: value
+        : mean: mean value
+        : sigma: variance
+
+        : return: gaussian (value)
+        """
+        # default
+        gaussian = 1.0
+        if sigma != 0:
+            # get exponent
+            nu_value = (value - mean) / sigma
+            nu_value *= nu_value
+            nu_value *= -0.5
+            gaussian *= numpy.exp(nu_value)
+            gaussian /= sigma
+            # normalize
+            gaussian /= sqrt(2 * pi)
+
+        return gaussian
+
+    def _atan_phi(self, qy_value, qx_value):
+        """
+        Find the angle phi of q on the detector plane for qx_value, qy_value given
+        : qx_value: x component of q
+        : qy_value: y component of q
+
+        : return phi: the azimuthal angle of q on x-y plane
+        """
+        phi = math.atan2(qy_value, qx_value)
+        return phi
+
+    def _get_detector_qxqy_pixels(self):
+        """
+        Get the pixel positions of the detector in the qx_value-qy_value space
+        """
+
+        # update all param values
+        self.get_all_instrument_params()
+
+        # wavelength
+        wavelength = self.wave.wavelength
+        # Gavity correction
+        delta_y = self._get_beamcenter_drop()  # in cm
+
+        # detector_pix size
+        detector_pix_size = self.detector_pix_size
+        # Square or circular pixel
+        if len(detector_pix_size) == 1:
+            pix_x_size = detector_pix_size[0]
+            pix_y_size = detector_pix_size[0]
+        # rectangular pixel pixel
+        elif len(detector_pix_size) == 2:
+            pix_x_size = detector_pix_size[0]
+            pix_y_size = detector_pix_size[1]
+        else:
+            raise ValueError, " Input value format error..."
+        # Sample to detector distance = sample slit to detector
+        # minus sample offset
+        sample2detector_distance = self.sample2detector_distance[0] - \
+                                    self.sample2sample_distance[0]
+        # detector offset in x-direction
+        detector_offset = 0
+        try:
+            detector_offset = self.sample2detector_distance[1]
+        except:
+            logging.error(sys.exc_value)
+
+        # detector size in [no of pix_x,no of pix_y]
+        detector_pix_nums_x = self.detector_size[0]
+
+        # get pix_y if it exists, otherwse take it from [0]
+        try:
+            detector_pix_nums_y = self.detector_size[1]
+        except:
+            detector_pix_nums_y = self.detector_size[0]
+
+        # detector offset in pix number
+        offset_x = detector_offset / pix_x_size
+        offset_y = delta_y / pix_y_size
+
+        # beam center position in pix number (start from 0)
+        center_x, center_y = self._get_beamcenter_position(detector_pix_nums_x,
+                                                           detector_pix_nums_y,
+                                                           offset_x, offset_y)
+        # distance [cm] from the beam center on detector plane
+        detector_ind_x = numpy.arange(detector_pix_nums_x)
+        detector_ind_y = numpy.arange(detector_pix_nums_y)
+
+        # shif 0.5 pixel so that pix position is at the center of the pixel
+        detector_ind_x = detector_ind_x + 0.5
+        detector_ind_y = detector_ind_y + 0.5
+
+        # the relative postion from the beam center
+        detector_ind_x = detector_ind_x - center_x
+        detector_ind_y = detector_ind_y - center_y
+
+        # unit correction in cm
+        detector_ind_x = detector_ind_x * pix_x_size
+        detector_ind_y = detector_ind_y * pix_y_size
+
+        qx_value = numpy.zeros(len(detector_ind_x))
+        qy_value = numpy.zeros(len(detector_ind_y))
+        i = 0
+
+        for indx in detector_ind_x:
+            qx_value[i] = self._get_qx(indx, sample2detector_distance, wavelength)
+            i += 1
+        i = 0
+        for indy in detector_ind_y:
+            qy_value[i] = self._get_qx(indy, sample2detector_distance, wavelength)
+            i += 1
+
+        # qx_value and qy_value values in array
+        qx_value = qx_value.repeat(detector_pix_nums_y)
+        qx_value = qx_value.reshape(detector_pix_nums_x, detector_pix_nums_y)
+        qy_value = qy_value.repeat(detector_pix_nums_x)
+        qy_value = qy_value.reshape(detector_pix_nums_y, detector_pix_nums_x)
+        qy_value = qy_value.transpose()
+
+        # p min and max values among the center of pixels
+        self.qx_min = numpy.min(qx_value)
+        self.qx_max = numpy.max(qx_value)
+        self.qy_min = numpy.min(qy_value)
+        self.qy_max = numpy.max(qy_value)
+
+        # Appr. min and max values of the detector display limits
+        # i.e., edges of the last pixels.
+        self.qy_min += self._get_qx(-0.5 * pix_y_size,
+                                    sample2detector_distance, wavelength)
+        self.qy_max += self._get_qx(0.5 * pix_y_size,
+                                    sample2detector_distance, wavelength)
+        #if self.qx_min == self.qx_max:
+        self.qx_min += self._get_qx(-0.5 * pix_x_size,
+                                    sample2detector_distance, wavelength)
+        self.qx_max += self._get_qx(0.5 * pix_x_size,
+                                    sample2detector_distance, wavelength)
+
+        # min and max values of detecter
+        self.detector_qx_min = self.qx_min
+        self.detector_qx_max = self.qx_max
+        self.detector_qy_min = self.qy_min
+        self.detector_qy_max = self.qy_max
+
+        # try to set it as a Data2D otherwise pass (not required for now)
+        try:
+            from sas.sascalc.dataloader.data_info import Data2D
+            output = Data2D()
+            inten = numpy.zeros_like(qx_value)
+            output.data = inten
+            output.qx_data = qx_value
+            output.qy_data = qy_value
+        except:
+            logging.error(sys.exc_value)
+
+        return output
+
+    def _get_qx(self, dx_size, det_dist, wavelength):
+        """
+        :param dx_size: x-distance from beam center [cm]
+        :param det_dist: sample to detector distance [cm]
+
+        :return: q-value at the given position
+        """
+        # Distance from beam center in the plane of detector
+        plane_dist = dx_size
+        # full scattering angle on the x-axis
+        theta = numpy.arctan(plane_dist / det_dist)
+        qx_value = (2.0 * pi / wavelength) * numpy.sin(theta)
+        return qx_value
+
+    def _get_polar_value(self, qx_value, qy_value):
+        """
+        Find qr_value and phi from qx_value and qy_value values
+
+        : return qr_value, phi
+        """
+        # find |q| on detector plane
+        qr_value = sqrt(qx_value*qx_value + qy_value*qy_value)
+        # find angle phi
+        phi = self._atan_phi(qy_value, qx_value)
+
+        return qr_value, phi
+
+    def _get_beamcenter_position(self, num_x, num_y, offset_x, offset_y):
+        """
+        :param num_x: number of pixel in x-direction
+        :param num_y: number of pixel in y-direction
+        :param offset: detector offset in x-direction in pix number
+
+        :return: pix number; pos_x, pos_y in pix index
+        """
+        # beam center position
+        pos_x = num_x / 2
+        pos_y = num_y / 2
+
+        # correction for offset
+        pos_x += offset_x
+        # correction for gravity that is always negative
+        pos_y -= offset_y
+
+        return pos_x, pos_y
+
+    def _get_beamcenter_drop(self):
+        """
+        Get the beam center drop (delta y) in y diection due to gravity
+
+        :return delta y: the beam center drop in cm
+        """
+        # Check if mass == 0 (X-ray).
+        if self.mass == 0:
+            return 0
+        # Covert unit from A to cm
+        unit_cm = 1e-08
+        # Velocity of neutron in horizontal direction (~ actual velocity)
+        velocity = _PLANK_H / (self.mass * self.wave.wavelength * unit_cm)
+        # Compute delta y
+        delta_y = 0.5
+        delta_y *= _GRAVITY
+        sampletodetector = self.sample2detector_distance[0] - \
+                                    self.sample2sample_distance[0]
+        delta_y *= sampletodetector
+        delta_y *= (self.source2sample_distance[0] + self.sample2detector_distance[0])
+        delta_y /= (velocity * velocity)
+
+        return delta_y
Index: src/sas/sascalc/calculator/sas_gen.py
===================================================================
--- src/sas/sascalc/calculator/sas_gen.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/calculator/sas_gen.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1116 @@
+# pylint: disable=invalid-name
+"""
+SAS generic computation and sld file readers
+"""
+from sas.models.BaseComponent import BaseComponent
+import sas.models.sas_extension.sld2i as mod
+from periodictable import formula
+from periodictable import nsf
+import numpy
+import os
+import copy
+import sys
+import logging
+
+MFACTOR_AM = 2.853E-12
+MFACTOR_MT = 2.3164E-9
+METER2ANG = 1.0E+10
+#Avogadro constant [1/mol]
+NA = 6.02214129e+23
+
+def mag2sld(mag, v_unit=None):
+    """
+    Convert magnetization to magnatic SLD
+    sldm = Dm * mag where Dm = gamma * classical elec. radius/(2*Bohr magneton)
+    Dm ~ 2.853E-12 [A^(-2)] ==> Shouldn't be 2.90636E-12 [A^(-2)]???
+    """
+    if v_unit == "A/m":
+        factor = MFACTOR_AM
+    elif v_unit == "mT":
+        factor = MFACTOR_MT
+    else:
+        raise ValueError, "Invalid valueunit"
+    sld_m = factor * mag
+    return sld_m
+
+def transform_center(pos_x, pos_y, pos_z):
+    """
+    re-center
+    :return: posx, posy, posz   [arrays]
+    """
+    posx = pos_x - (min(pos_x) + max(pos_x)) / 2.0
+    posy = pos_y - (min(pos_y) + max(pos_y)) / 2.0
+    posz = pos_z - (min(pos_z) + max(pos_z)) / 2.0
+    return posx, posy, posz
+
+class GenSAS(BaseComponent):
+    """
+    Generic SAS computation Model based on sld (n & m) arrays
+    """
+    def __init__(self):
+        """
+        Init
+        :Params sld_data: MagSLD object
+        """
+        # Initialize BaseComponent
+        BaseComponent.__init__(self)
+        self.sld_data = None
+        self.data_pos_unit = None
+        self.data_x = None
+        self.data_y = None
+        self.data_z = None
+        self.data_sldn = None
+        self.data_mx = None
+        self.data_my = None
+        self.data_mz = None
+        self.data_vol = None #[A^3]
+        self.is_avg = False
+        ## Name of the model
+        self.name = "GenSAS"
+        ## Define parameters
+        self.params = {}
+        self.params['scale'] = 1.0
+        self.params['background'] = 0.0
+        self.params['solvent_SLD'] = 0.0
+        self.params['total_volume'] = 1.0
+        self.params['Up_frac_in'] = 1.0
+        self.params['Up_frac_out'] = 1.0
+        self.params['Up_theta'] = 0.0
+        self.description = 'GenSAS'
+        ## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['background'] = ['[1/cm]', None, None]
+        self.details['solvent_SLD'] = ['1/A^(2)', None, None]
+        self.details['total_volume'] = ['A^(3)', None, None]
+        self.details['Up_frac_in'] = ['[u/(u+d)]', None, None]
+        self.details['Up_frac_out'] = ['[u/(u+d)]', None, None]
+        self.details['Up_theta'] = ['[deg]', None, None]
+        # fixed parameters
+        self.fixed = []
+
+    def set_pixel_volumes(self, volume):
+        """
+        Set the volume of a pixel in (A^3) unit
+        :Param volume: pixel volume [float]
+        """
+        if self.data_vol == None:
+            raise
+        self.data_vol = volume
+
+    def set_is_avg(self, is_avg=False):
+        """
+        Sets is_avg: [bool]
+        """
+        self.is_avg = is_avg
+
+    def _gen(self, x, y, i):
+        """
+        Evaluate the function
+        :Param x: array of x-values
+        :Param y: array of y-values
+        :Param i: array of initial i-value
+        :return: function value
+        """
+        pos_x = self.data_x
+        pos_y = self.data_y
+        pos_z = self.data_z
+        len_x = len(pos_x)
+        if self.is_avg == None:
+            len_x *= -1
+            pos_x, pos_y, pos_z = transform_center(pos_x, pos_y, pos_z)
+        len_q = len(x)
+        sldn = copy.deepcopy(self.data_sldn)
+        sldn -= self.params['solvent_SLD']
+        model = mod.new_GenI(len_x, pos_x, pos_y, pos_z,
+                             sldn, self.data_mx, self.data_my,
+                             self.data_mz, self.data_vol,
+                             self.params['Up_frac_in'],
+                             self.params['Up_frac_out'],
+                             self.params['Up_theta'])
+        if y == []:
+            mod.genicom(model, len_q, x, i)
+        else:
+            mod.genicomXY(model, len_q, x, y, i)
+        vol_correction = self.data_total_volume / self.params['total_volume']
+        return  self.params['scale'] * vol_correction * i + \
+                        self.params['background']
+
+    def set_sld_data(self, sld_data=None):
+        """
+        Sets sld_data
+        """
+        self.sld_data = sld_data
+        self.data_pos_unit = sld_data.pos_unit
+        self.data_x = sld_data.pos_x
+        self.data_y = sld_data.pos_y
+        self.data_z = sld_data.pos_z
+        self.data_sldn = sld_data.sld_n
+        self.data_mx = sld_data.sld_mx
+        self.data_my = sld_data.sld_my
+        self.data_mz = sld_data.sld_mz
+        self.data_vol = sld_data.vol_pix
+        self.data_total_volume = sum(sld_data.vol_pix)
+        self.params['total_volume'] = sum(sld_data.vol_pix)
+
+    def getProfile(self):
+        """
+        Get SLD profile
+        : return: sld_data
+        """
+        return self.sld_data
+
+    def run(self, x=0.0):
+        """
+        Evaluate the model
+        :param x: simple value
+        :return: (I value)
+        """
+        if x.__class__.__name__ == 'list':
+            if len(x[1]) > 0:
+                msg = "Not a 1D."
+                raise ValueError, msg
+            i_out = numpy.zeros_like(x[0])
+            # 1D I is found at y =0 in the 2D pattern
+            out = self._gen(x[0], [], i_out)
+            return out
+        else:
+            msg = "Q must be given as list of qx's and qy's"
+            raise ValueError, msg
+
+    def runXY(self, x=0.0):
+        """
+        Evaluate the model
+        :param x: simple value
+        :return: I value
+        :Use this runXY() for the computation
+        """
+        if x.__class__.__name__ == 'list':
+            i_out = numpy.zeros_like(x[0])
+            out = self._gen(x[0], x[1], i_out)
+            return out
+        else:
+            msg = "Q must be given as list of qx's and qy's"
+            raise ValueError, msg
+
+    def evalDistribution(self, qdist):
+        """
+        Evaluate a distribution of q-values.
+
+        :param qdist: ndarray of scalar q-values (for 1D) or list [qx,qy]
+                      where qx,qy are 1D ndarrays (for 2D).
+        """
+        if qdist.__class__.__name__ == 'list':
+            if len(qdist[1]) < 1:
+                out = self.run(qdist)
+            else:
+                out = self.runXY(qdist)
+            return out
+        else:
+            mesg = "evalDistribution is expecting an ndarray of "
+            mesg += "a list [qx,qy] where qx,qy are arrays."
+            raise RuntimeError, mesg
+
+class OMF2SLD(object):
+    """
+    Convert OMFData to MAgData
+    """
+    def __init__(self):
+        """
+        Init
+        """
+        self.pos_x = None
+        self.pos_y = None
+        self.pos_z = None
+        self.mx = None
+        self.my = None
+        self.mz = None
+        self.sld_n = None
+        self.vol_pix = None
+        self.output = None
+        self.omfdata = None
+
+    def set_data(self, omfdata, shape='rectangular'):
+        """
+        Set all data
+        """
+        self.omfdata = omfdata
+        length = int(omfdata.xnodes * omfdata.ynodes * omfdata.znodes)
+        pos_x = numpy.arange(omfdata.xmin,
+                             omfdata.xnodes*omfdata.xstepsize + omfdata.xmin,
+                             omfdata.xstepsize)
+        pos_y = numpy.arange(omfdata.ymin,
+                             omfdata.ynodes*omfdata.ystepsize + omfdata.ymin,
+                             omfdata.ystepsize)
+        pos_z = numpy.arange(omfdata.zmin,
+                             omfdata.znodes*omfdata.zstepsize + omfdata.zmin,
+                             omfdata.zstepsize)
+        self.pos_x = numpy.tile(pos_x, int(omfdata.ynodes * omfdata.znodes))
+        self.pos_y = pos_y.repeat(int(omfdata.xnodes))
+        self.pos_y = numpy.tile(self.pos_y, int(omfdata.znodes))
+        self.pos_z = pos_z.repeat(int(omfdata.xnodes * omfdata.ynodes))
+        self.mx = omfdata.mx
+        self.my = omfdata.my
+        self.mz = omfdata.mz
+        self.sld_n = numpy.zeros(length)
+
+        if omfdata.mx == None:
+            self.mx = numpy.zeros(length)
+        if omfdata.my == None:
+            self.my = numpy.zeros(length)
+        if omfdata.mz == None:
+            self.mz = numpy.zeros(length)
+
+        self._check_data_length(length)
+        self.remove_null_points(False, False)
+        mask = numpy.ones(len(self.sld_n), dtype=bool)
+        if shape.lower() == 'ellipsoid':
+            try:
+                # Pixel (step) size included
+                x_c = max(self.pos_x) + min(self.pos_x)
+                y_c = max(self.pos_y) + min(self.pos_y)
+                z_c = max(self.pos_z) + min(self.pos_z)
+                x_d = max(self.pos_x) - min(self.pos_x)
+                y_d = max(self.pos_y) - min(self.pos_y)
+                z_d = max(self.pos_z) - min(self.pos_z)
+                x_r = (x_d + omfdata.xstepsize) / 2.0
+                y_r = (y_d + omfdata.ystepsize) / 2.0
+                z_r = (z_d + omfdata.zstepsize) / 2.0
+                x_dir2 = ((self.pos_x - x_c / 2.0) / x_r)
+                x_dir2 *= x_dir2
+                y_dir2 = ((self.pos_y - y_c / 2.0) / y_r)
+                y_dir2 *= y_dir2
+                z_dir2 = ((self.pos_z - z_c / 2.0) / z_r)
+                z_dir2 *= z_dir2
+                mask = (x_dir2 + y_dir2 + z_dir2) <= 1.0
+            except:
+                logging.error(sys.exc_value)
+        self.output = MagSLD(self.pos_x[mask], self.pos_y[mask],
+                             self.pos_z[mask], self.sld_n[mask],
+                             self.mx[mask], self.my[mask], self.mz[mask])
+        self.output.set_pix_type('pixel')
+        self.output.set_pixel_symbols('pixel')
+
+    def get_omfdata(self):
+        """
+        Return all data
+        """
+        return self.omfdata
+
+    def get_output(self):
+        """
+        Return output
+        """
+        return self.output
+
+    def _check_data_length(self, length):
+        """
+        Check if the data lengths are consistent
+        :Params length: data length
+        """
+        msg = "Error: Inconsistent data length."
+        if len(self.pos_x) != length:
+            raise ValueError, msg
+        if len(self.pos_y) != length:
+            raise ValueError, msg
+        if len(self.pos_z) != length:
+            raise ValueError, msg
+        if len(self.mx) != length:
+            raise ValueError, msg
+        if len(self.my) != length:
+            raise ValueError, msg
+        if len(self.mz) != length:
+            raise ValueError, msg
+
+    def remove_null_points(self, remove=False, recenter=False):
+        """
+        Removes any mx, my, and mz = 0 points
+        """
+        if remove:
+            is_nonzero = (numpy.fabs(self.mx) + numpy.fabs(self.my) +
+                          numpy.fabs(self.mz)).nonzero()
+            if len(is_nonzero[0]) > 0:
+                self.pos_x = self.pos_x[is_nonzero]
+                self.pos_y = self.pos_y[is_nonzero]
+                self.pos_z = self.pos_z[is_nonzero]
+                self.sld_n = self.sld_n[is_nonzero]
+                self.mx = self.mx[is_nonzero]
+                self.my = self.my[is_nonzero]
+                self.mz = self.mz[is_nonzero]
+        if recenter:
+            self.pos_x -= (min(self.pos_x) + max(self.pos_x)) / 2.0
+            self.pos_y -= (min(self.pos_y) + max(self.pos_y)) / 2.0
+            self.pos_z -= (min(self.pos_z) + max(self.pos_z)) / 2.0
+
+    def get_magsld(self):
+        """
+        return MagSLD
+        """
+        return self.output
+
+
+class OMFReader(object):
+    """
+    Class to load omf/ascii files (3 columns w/header).
+    """
+    ## File type
+    type_name = "OMF ASCII"
+
+    ## Wildcards
+    type = ["OMF files (*.OMF, *.omf)|*.omf"]
+    ## List of allowed extensions
+    ext = ['.omf', '.OMF']
+
+    def read(self, path):
+        """
+        Load data file
+        :param path: file path
+        :return: x, y, z, sld_n, sld_mx, sld_my, sld_mz
+        """
+        desc = ""
+        mx = numpy.zeros(0)
+        my = numpy.zeros(0)
+        mz = numpy.zeros(0)
+        try:
+            input_f = open(path, 'rb')
+            buff = input_f.read()
+            lines = buff.split('\n')
+            input_f.close()
+            output = OMFData()
+            valueunit = None
+            for line in lines:
+                toks = line.split()
+                # Read data
+                try:
+                    _mx = float(toks[0])
+                    _my = float(toks[1])
+                    _mz = float(toks[2])
+                    _mx = mag2sld(_mx, valueunit)
+                    _my = mag2sld(_my, valueunit)
+                    _mz = mag2sld(_mz, valueunit)
+                    mx = numpy.append(mx, _mx)
+                    my = numpy.append(my, _my)
+                    mz = numpy.append(mz, _mz)
+                except:
+                    # Skip non-data lines
+                    logging.error(sys.exc_value)
+                #Reading Header; Segment count ignored
+                s_line = line.split(":", 1)
+                if s_line[0].lower().count("oommf") > 0:
+                    oommf = s_line[1].lstrip()
+                if s_line[0].lower().count("title") > 0:
+                    title = s_line[1].lstrip()
+                if s_line[0].lower().count("desc") > 0:
+                    desc += s_line[1].lstrip()
+                    desc += '\n'
+                if s_line[0].lower().count("meshtype") > 0:
+                    meshtype = s_line[1].lstrip()
+                if s_line[0].lower().count("meshunit") > 0:
+                    meshunit = s_line[1].lstrip()
+                    if meshunit.count("m") < 1:
+                        msg = "Error: \n"
+                        msg += "We accept only m as meshunit"
+                        raise ValueError, msg
+                if s_line[0].lower().count("xbase") > 0:
+                    xbase = s_line[1].lstrip()
+                if s_line[0].lower().count("ybase") > 0:
+                    ybase = s_line[1].lstrip()
+                if s_line[0].lower().count("zbase") > 0:
+                    zbase = s_line[1].lstrip()
+                if s_line[0].lower().count("xstepsize") > 0:
+                    xstepsize = s_line[1].lstrip()
+                if s_line[0].lower().count("ystepsize") > 0:
+                    ystepsize = s_line[1].lstrip()
+                if s_line[0].lower().count("zstepsize") > 0:
+                    zstepsize = s_line[1].lstrip()
+                if s_line[0].lower().count("xnodes") > 0:
+                    xnodes = s_line[1].lstrip()
+                if s_line[0].lower().count("ynodes") > 0:
+                    ynodes = s_line[1].lstrip()
+                if s_line[0].lower().count("znodes") > 0:
+                    znodes = s_line[1].lstrip()
+                if s_line[0].lower().count("xmin") > 0:
+                    xmin = s_line[1].lstrip()
+                if s_line[0].lower().count("ymin") > 0:
+                    ymin = s_line[1].lstrip()
+                if s_line[0].lower().count("zmin") > 0:
+                    zmin = s_line[1].lstrip()
+                if s_line[0].lower().count("xmax") > 0:
+                    xmax = s_line[1].lstrip()
+                if s_line[0].lower().count("ymax") > 0:
+                    ymax = s_line[1].lstrip()
+                if s_line[0].lower().count("zmax") > 0:
+                    zmax = s_line[1].lstrip()
+                if s_line[0].lower().count("valueunit") > 0:
+                    valueunit = s_line[1].lstrip().rstrip()
+                if s_line[0].lower().count("valuemultiplier") > 0:
+                    valuemultiplier = s_line[1].lstrip()
+                if s_line[0].lower().count("valuerangeminmag") > 0:
+                    valuerangeminmag = s_line[1].lstrip()
+                if s_line[0].lower().count("valuerangemaxmag") > 0:
+                    valuerangemaxmag = s_line[1].lstrip()
+                if s_line[0].lower().count("end") > 0:
+                    output.filename = os.path.basename(path)
+                    output.oommf = oommf
+                    output.title = title
+                    output.desc = desc
+                    output.meshtype = meshtype
+                    output.xbase = float(xbase) * METER2ANG
+                    output.ybase = float(ybase) * METER2ANG
+                    output.zbase = float(zbase) * METER2ANG
+                    output.xstepsize = float(xstepsize) * METER2ANG
+                    output.ystepsize = float(ystepsize) * METER2ANG
+                    output.zstepsize = float(zstepsize) * METER2ANG
+                    output.xnodes = float(xnodes)
+                    output.ynodes = float(ynodes)
+                    output.znodes = float(znodes)
+                    output.xmin = float(xmin) * METER2ANG
+                    output.ymin = float(ymin) * METER2ANG
+                    output.zmin = float(zmin) * METER2ANG
+                    output.xmax = float(xmax) * METER2ANG
+                    output.ymax = float(ymax) * METER2ANG
+                    output.zmax = float(zmax) * METER2ANG
+                    output.valuemultiplier = valuemultiplier
+                    output.valuerangeminmag = mag2sld(float(valuerangeminmag), \
+                                                      valueunit)
+                    output.valuerangemaxmag = mag2sld(float(valuerangemaxmag), \
+                                                      valueunit)
+            output.set_m(mx, my, mz)
+            return output
+        except:
+            msg = "%s is not supported: \n" % path
+            msg += "We accept only Text format OMF file."
+            raise RuntimeError, msg
+
+class PDBReader(object):
+    """
+    PDB reader class: limited for reading the lines starting with 'ATOM'
+    """
+    type_name = "PDB"
+    ## Wildcards
+    type = ["pdb files (*.PDB, *.pdb)|*.pdb"]
+    ## List of allowed extensions
+    ext = ['.pdb', '.PDB']
+
+    def read(self, path):
+        """
+        Load data file
+
+        :param path: file path
+        :return: MagSLD
+        :raise RuntimeError: when the file can't be opened
+        """
+        pos_x = numpy.zeros(0)
+        pos_y = numpy.zeros(0)
+        pos_z = numpy.zeros(0)
+        sld_n = numpy.zeros(0)
+        sld_mx = numpy.zeros(0)
+        sld_my = numpy.zeros(0)
+        sld_mz = numpy.zeros(0)
+        vol_pix = numpy.zeros(0)
+        pix_symbol = numpy.zeros(0)
+        x_line = []
+        y_line = []
+        z_line = []
+        x_lines = []
+        y_lines = []
+        z_lines = []
+        try:
+            input_f = open(path, 'rb')
+            buff = input_f.read()
+            lines = buff.split('\n')
+            input_f.close()
+            num = 0
+            for line in lines:
+                try:
+                    # check if line starts with "ATOM"
+                    if line[0:6].strip().count('ATM') > 0 or \
+                                line[0:6].strip() == 'ATOM':
+                        # define fields of interest
+                        atom_name = line[12:16].strip()
+                        try:
+                            float(line[12])
+                            atom_name = atom_name[1].upper()
+                        except:
+                            if len(atom_name) == 4:
+                                atom_name = atom_name[0].upper()
+                            elif line[12] != ' ':
+                                atom_name = atom_name[0].upper() + \
+                                        atom_name[1].lower()
+                            else:
+                                atom_name = atom_name[0].upper()
+                        _pos_x = float(line[30:38].strip())
+                        _pos_y = float(line[38:46].strip())
+                        _pos_z = float(line[46:54].strip())
+                        pos_x = numpy.append(pos_x, _pos_x)
+                        pos_y = numpy.append(pos_y, _pos_y)
+                        pos_z = numpy.append(pos_z, _pos_z)
+                        try:
+                            val = nsf.neutron_sld(atom_name)[0]
+                            # sld in Ang^-2 unit
+                            val *= 1.0e-6
+                            sld_n = numpy.append(sld_n, val)
+                            atom = formula(atom_name)
+                            # cm to A units
+                            vol = 1.0e+24 * atom.mass / atom.density / NA
+                            vol_pix = numpy.append(vol_pix, vol)
+                        except:
+                            print "Error: set the sld of %s to zero"% atom_name
+                            sld_n = numpy.append(sld_n, 0.0)
+                        sld_mx = numpy.append(sld_mx, 0)
+                        sld_my = numpy.append(sld_my, 0)
+                        sld_mz = numpy.append(sld_mz, 0)
+                        pix_symbol = numpy.append(pix_symbol, atom_name)
+                    elif line[0:6].strip().count('CONECT') > 0:
+                        toks = line.split()
+                        num = int(toks[1]) - 1
+                        val_list = []
+                        for val in toks[2:]:
+                            try:
+                                int_val = int(val)
+                            except:
+                                break
+                            if int_val == 0:
+                                break
+                            val_list.append(int_val)
+                        #need val_list ordered
+                        for val in val_list:
+                            index = val - 1
+                            if (pos_x[index], pos_x[num]) in x_line and \
+                               (pos_y[index], pos_y[num]) in y_line and \
+                               (pos_z[index], pos_z[num]) in z_line:
+                                continue
+                            x_line.append((pos_x[num], pos_x[index]))
+                            y_line.append((pos_y[num], pos_y[index]))
+                            z_line.append((pos_z[num], pos_z[index]))
+                    if len(x_line) > 0:
+                        x_lines.append(x_line)
+                        y_lines.append(y_line)
+                        z_lines.append(z_line)
+                except:
+                    logging.error(sys.exc_value)
+
+            output = MagSLD(pos_x, pos_y, pos_z, sld_n, sld_mx, sld_my, sld_mz)
+            output.set_conect_lines(x_line, y_line, z_line)
+            output.filename = os.path.basename(path)
+            output.set_pix_type('atom')
+            output.set_pixel_symbols(pix_symbol)
+            output.set_nodes()
+            output.set_pixel_volumes(vol_pix)
+            output.sld_unit = '1/A^(2)'
+            return output
+        except:
+            raise RuntimeError, "%s is not a sld file" % path
+
+    def write(self, path, data):
+        """
+        Write
+        """
+        print "Not implemented... "
+
+class SLDReader(object):
+    """
+    Class to load ascii files (7 columns).
+    """
+    ## File type
+    type_name = "SLD ASCII"
+    ## Wildcards
+    type = ["sld files (*.SLD, *.sld)|*.sld",
+            "txt files (*.TXT, *.txt)|*.txt",
+            "all files (*.*)|*.*"]
+    ## List of allowed extensions
+    ext = ['.sld', '.SLD', '.txt', '.TXT', '.*']
+    def read(self, path):
+        """
+        Load data file
+        :param path: file path
+        :return MagSLD: x, y, z, sld_n, sld_mx, sld_my, sld_mz
+        :raise RuntimeError: when the file can't be opened
+        :raise ValueError: when the length of the data vectors are inconsistent
+        """
+        try:
+            pos_x = numpy.zeros(0)
+            pos_y = numpy.zeros(0)
+            pos_z = numpy.zeros(0)
+            sld_n = numpy.zeros(0)
+            sld_mx = numpy.zeros(0)
+            sld_my = numpy.zeros(0)
+            sld_mz = numpy.zeros(0)
+            try:
+                # Use numpy to speed up loading
+                input_f = numpy.loadtxt(path, dtype='float', skiprows=1,
+                                        ndmin=1, unpack=True)
+                pos_x = numpy.array(input_f[0])
+                pos_y = numpy.array(input_f[1])
+                pos_z = numpy.array(input_f[2])
+                sld_n = numpy.array(input_f[3])
+                sld_mx = numpy.array(input_f[4])
+                sld_my = numpy.array(input_f[5])
+                sld_mz = numpy.array(input_f[6])
+                ncols = len(input_f)
+                if ncols == 8:
+                    vol_pix = numpy.array(input_f[7])
+                elif ncols == 7:
+                    vol_pix = None
+            except:
+                # For older version of numpy
+                input_f = open(path, 'rb')
+                buff = input_f.read()
+                lines = buff.split('\n')
+                input_f.close()
+                for line in lines:
+                    toks = line.split()
+                    try:
+                        _pos_x = float(toks[0])
+                        _pos_y = float(toks[1])
+                        _pos_z = float(toks[2])
+                        _sld_n = float(toks[3])
+                        _sld_mx = float(toks[4])
+                        _sld_my = float(toks[5])
+                        _sld_mz = float(toks[6])
+                        pos_x = numpy.append(pos_x, _pos_x)
+                        pos_y = numpy.append(pos_y, _pos_y)
+                        pos_z = numpy.append(pos_z, _pos_z)
+                        sld_n = numpy.append(sld_n, _sld_n)
+                        sld_mx = numpy.append(sld_mx, _sld_mx)
+                        sld_my = numpy.append(sld_my, _sld_my)
+                        sld_mz = numpy.append(sld_mz, _sld_mz)
+                        try:
+                            _vol_pix = float(toks[7])
+                            vol_pix = numpy.append(vol_pix, _vol_pix)
+                        except:
+                            vol_pix = None
+                    except:
+                        # Skip non-data lines
+                        logging.error(sys.exc_value)
+            output = MagSLD(pos_x, pos_y, pos_z, sld_n,
+                            sld_mx, sld_my, sld_mz)
+            output.filename = os.path.basename(path)
+            output.set_pix_type('pixel')
+            output.set_pixel_symbols('pixel')
+            if vol_pix != None:
+                output.set_pixel_volumes(vol_pix)
+            return output
+        except:
+            raise RuntimeError, "%s is not a sld file" % path
+
+    def write(self, path, data):
+        """
+        Write sld file
+        :Param path: file path
+        :Param data: MagSLD data object
+        """
+        if path == None:
+            raise ValueError, "Missing the file path."
+        if data == None:
+            raise ValueError, "Missing the data to save."
+        x_val = data.pos_x
+        y_val = data.pos_y
+        z_val = data.pos_z
+        vol_pix = data.vol_pix
+        length = len(x_val)
+        sld_n = data.sld_n
+        if sld_n == None:
+            sld_n = numpy.zeros(length)
+        sld_mx = data.sld_mx
+        if sld_mx == None:
+            sld_mx = numpy.zeros(length)
+            sld_my = numpy.zeros(length)
+            sld_mz = numpy.zeros(length)
+        else:
+            sld_my = data.sld_my
+            sld_mz = data.sld_mz
+        out = open(path, 'w')
+        # First Line: Column names
+        out.write("X  Y  Z  SLDN SLDMx  SLDMy  SLDMz VOLUMEpix")
+        for ind in range(length):
+            out.write("\n%g  %g  %g  %g  %g  %g  %g %g" % \
+                      (x_val[ind], y_val[ind], z_val[ind], sld_n[ind],
+                       sld_mx[ind], sld_my[ind], sld_mz[ind], vol_pix[ind]))
+        out.close()
+
+
+class OMFData(object):
+    """
+    OMF Data.
+    """
+    _meshunit = "A"
+    _valueunit = "A^(-2)"
+    def __init__(self):
+        """
+        Init for mag SLD
+        """
+        self.filename = 'default'
+        self.oommf = ''
+        self.title = ''
+        self.desc = ''
+        self.meshtype = ''
+        self.meshunit = self._meshunit
+        self.valueunit = self._valueunit
+        self.xbase = 0.0
+        self.ybase = 0.0
+        self.zbase = 0.0
+        self.xstepsize = 6.0
+        self.ystepsize = 6.0
+        self.zstepsize = 6.0
+        self.xnodes = 10.0
+        self.ynodes = 10.0
+        self.znodes = 10.0
+        self.xmin = 0.0
+        self.ymin = 0.0
+        self.zmin = 0.0
+        self.xmax = 60.0
+        self.ymax = 60.0
+        self.zmax = 60.0
+        self.mx = None
+        self.my = None
+        self.mz = None
+        self.valuemultiplier = 1.
+        self.valuerangeminmag = 0
+        self.valuerangemaxmag = 0
+
+    def __str__(self):
+        """
+        doc strings
+        """
+        _str = "Type:            %s\n" % self.__class__.__name__
+        _str += "File:            %s\n" % self.filename
+        _str += "OOMMF:           %s\n" % self.oommf
+        _str += "Title:           %s\n" % self.title
+        _str += "Desc:            %s\n" % self.desc
+        _str += "meshtype:        %s\n" % self.meshtype
+        _str += "meshunit:        %s\n" % str(self.meshunit)
+        _str += "xbase:           %s [%s]\n" % (str(self.xbase), self.meshunit)
+        _str += "ybase:           %s [%s]\n" % (str(self.ybase), self.meshunit)
+        _str += "zbase:           %s [%s]\n" % (str(self.zbase), self.meshunit)
+        _str += "xstepsize:       %s [%s]\n" % (str(self.xstepsize),
+                                                self.meshunit)
+        _str += "ystepsize:       %s [%s]\n" % (str(self.ystepsize),
+                                                self.meshunit)
+        _str += "zstepsize:       %s [%s]\n" % (str(self.zstepsize),
+                                                self.meshunit)
+        _str += "xnodes:          %s\n" % str(self.xnodes)
+        _str += "ynodes:          %s\n" % str(self.ynodes)
+        _str += "znodes:          %s\n" % str(self.znodes)
+        _str += "xmin:            %s [%s]\n" % (str(self.xmin), self.meshunit)
+        _str += "ymin:            %s [%s]\n" % (str(self.ymin), self.meshunit)
+        _str += "zmin:            %s [%s]\n" % (str(self.zmin), self.meshunit)
+        _str += "xmax:            %s [%s]\n" % (str(self.xmax), self.meshunit)
+        _str += "ymax:            %s [%s]\n" % (str(self.ymax), self.meshunit)
+        _str += "zmax:            %s [%s]\n" % (str(self.zmax), self.meshunit)
+        _str += "valueunit:       %s\n" % self.valueunit
+        _str += "valuemultiplier: %s\n" % str(self.valuemultiplier)
+        _str += "ValueRangeMinMag:%s [%s]\n" % (str(self.valuerangeminmag),
+                                                self.valueunit)
+        _str += "ValueRangeMaxMag:%s [%s]\n" % (str(self.valuerangemaxmag),
+                                                self.valueunit)
+        return _str
+
+    def set_m(self, mx, my, mz):
+        """
+        Set the Mx, My, Mz values
+        """
+        self.mx = mx
+        self.my = my
+        self.mz = mz
+
+class MagSLD(object):
+    """
+    Magnetic SLD.
+    """
+    pos_x = None
+    pos_y = None
+    pos_z = None
+    sld_n = None
+    sld_mx = None
+    sld_my = None
+    sld_mz = None
+    # Units
+    _pos_unit = 'A'
+    _sld_unit = '1/A^(2)'
+    _pix_type = 'pixel'
+
+    def __init__(self, pos_x, pos_y, pos_z, sld_n=None,
+                 sld_mx=None, sld_my=None, sld_mz=None, vol_pix=None):
+        """
+        Init for mag SLD
+        :params : All should be numpy 1D array
+        """
+        self.is_data = True
+        self.filename = ''
+        self.xstepsize = 6.0
+        self.ystepsize = 6.0
+        self.zstepsize = 6.0
+        self.xnodes = 10.0
+        self.ynodes = 10.0
+        self.znodes = 10.0
+        self.has_stepsize = False
+        self.has_conect = False
+        self.pos_unit = self._pos_unit
+        self.sld_unit = self._sld_unit
+        self.pix_type = 'pixel'
+        self.pos_x = pos_x
+        self.pos_y = pos_y
+        self.pos_z = pos_z
+        self.sld_n = sld_n
+        self.line_x = None
+        self.line_y = None
+        self.line_z = None
+        self.sld_mx = sld_mx
+        self.sld_my = sld_my
+        self.sld_mz = sld_mz
+        self.vol_pix = vol_pix
+        self.sld_m = None
+        self.sld_phi = None
+        self.sld_theta = None
+        self.pix_symbol = None
+        if sld_mx != None and sld_my != None and sld_mz != None:
+            self.set_sldms(sld_mx, sld_my, sld_mz)
+        self.set_nodes()
+
+    def __str__(self):
+        """
+        doc strings
+        """
+        _str = "Type:       %s\n" % self.__class__.__name__
+        _str += "File:       %s\n" % self.filename
+        _str += "Axis_unit:  %s\n" % self.pos_unit
+        _str += "SLD_unit:   %s\n" % self.sld_unit
+        return _str
+
+    def set_pix_type(self, pix_type):
+        """
+        Set pixel type
+        :Param pix_type: string, 'pixel' or 'atom'
+        """
+        self.pix_type = pix_type
+
+    def set_sldn(self, sld_n):
+        """
+        Sets neutron SLD
+        """
+        if sld_n.__class__.__name__ == 'float':
+            if self.is_data:
+                # For data, put the value to only the pixels w non-zero M
+                is_nonzero = (numpy.fabs(self.sld_mx) +
+                              numpy.fabs(self.sld_my) +
+                              numpy.fabs(self.sld_mz)).nonzero()
+                self.sld_n = numpy.zeros(len(self.pos_x))
+                if len(self.sld_n[is_nonzero]) > 0:
+                    self.sld_n[is_nonzero] = sld_n
+                else:
+                    self.sld_n.fill(sld_n)
+            else:
+                # For non-data, put the value to all the pixels
+                self.sld_n = numpy.ones(len(self.pos_x)) * sld_n
+        else:
+            self.sld_n = sld_n
+
+    def set_sldms(self, sld_mx, sld_my, sld_mz):
+        r"""
+        Sets (\|m\|, m_theta, m_phi)
+        """
+        if sld_mx.__class__.__name__ == 'float':
+            self.sld_mx = numpy.ones(len(self.pos_x)) * sld_mx
+        else:
+            self.sld_mx = sld_mx
+        if sld_my.__class__.__name__ == 'float':
+            self.sld_my = numpy.ones(len(self.pos_x)) * sld_my
+        else:
+            self.sld_my = sld_my
+        if sld_mz.__class__.__name__ == 'float':
+            self.sld_mz = numpy.ones(len(self.pos_x)) * sld_mz
+        else:
+            self.sld_mz = sld_mz
+
+        sld_m = numpy.sqrt(sld_mx * sld_mx + sld_my * sld_my + \
+                                sld_mz * sld_mz)
+        self.sld_m = sld_m
+
+    def set_pixel_symbols(self, symbol='pixel'):
+        """
+        Set pixel
+        :Params pixel: str; pixel or atomic symbol, or array of strings
+        """
+        if self.sld_n == None:
+            return
+        if symbol.__class__.__name__ == 'str':
+            self.pix_symbol = numpy.repeat(symbol, len(self.sld_n))
+        else:
+            self.pix_symbol = symbol
+
+    def set_pixel_volumes(self, vol):
+        """
+        Set pixel volumes
+        :Params pixel: str; pixel or atomic symbol, or array of strings
+        """
+        if self.sld_n == None:
+            return
+        if vol.__class__.__name__ == 'ndarray':
+            self.vol_pix = vol
+        elif vol.__class__.__name__.count('float') > 0:
+            self.vol_pix = numpy.repeat(vol, len(self.sld_n))
+        else:
+            self.vol_pix = None
+
+    def get_sldn(self):
+        """
+        Returns nuclear sld
+        """
+        return self.sld_n
+
+    def set_nodes(self):
+        """
+        Set xnodes, ynodes, and znodes
+        """
+        self.set_stepsize()
+        if self.pix_type == 'pixel':
+            try:
+                xdist = (max(self.pos_x) - min(self.pos_x)) / self.xstepsize
+                ydist = (max(self.pos_y) - min(self.pos_y)) / self.ystepsize
+                zdist = (max(self.pos_z) - min(self.pos_z)) / self.zstepsize
+                self.xnodes = int(xdist) + 1
+                self.ynodes = int(ydist) + 1
+                self.znodes = int(zdist) + 1
+            except:
+                self.xnodes = None
+                self.ynodes = None
+                self.znodes = None
+        else:
+            self.xnodes = None
+            self.ynodes = None
+            self.znodes = None
+
+    def set_stepsize(self):
+        """
+        Set xtepsize, ystepsize, and zstepsize
+        """
+        if self.pix_type == 'pixel':
+            try:
+                xpos_pre = self.pos_x[0]
+                ypos_pre = self.pos_y[0]
+                zpos_pre = self.pos_z[0]
+                for x_pos in self.pos_x:
+                    if xpos_pre != x_pos:
+                        self.xstepsize = numpy.fabs(x_pos - xpos_pre)
+                        break
+                for y_pos in self.pos_y:
+                    if ypos_pre != y_pos:
+                        self.ystepsize = numpy.fabs(y_pos - ypos_pre)
+                        break
+                for z_pos in self.pos_z:
+                    if zpos_pre != z_pos:
+                        self.zstepsize = numpy.fabs(z_pos - zpos_pre)
+                        break
+                #default pix volume
+                self.vol_pix = numpy.ones(len(self.pos_x))
+                vol = self.xstepsize * self.ystepsize * self.zstepsize
+                self.set_pixel_volumes(vol)
+                self.has_stepsize = True
+            except:
+                self.xstepsize = None
+                self.ystepsize = None
+                self.zstepsize = None
+                self.vol_pix = None
+                self.has_stepsize = False
+        else:
+            self.xstepsize = None
+            self.ystepsize = None
+            self.zstepsize = None
+            self.has_stepsize = True
+        return self.xstepsize, self.ystepsize, self.zstepsize
+
+    def set_conect_lines(self, line_x, line_y, line_z):
+        """
+        Set bonding line data if taken from pdb
+        """
+        if line_x.__class__.__name__ != 'list' or len(line_x) < 1:
+            return
+        if line_y.__class__.__name__ != 'list' or len(line_y) < 1:
+            return
+        if line_z.__class__.__name__ != 'list' or len(line_z) < 1:
+            return
+        self.has_conect = True
+        self.line_x = line_x
+        self.line_y = line_y
+        self.line_z = line_z
+
+def test_load():
+    """
+        Test code
+    """
+    from sas.plottools.arrow3d import Arrow3D
+    current_dir = os.path.abspath(os.path.curdir)
+    print current_dir
+    for i in range(6):
+        current_dir, _ = os.path.split(current_dir)
+        tfile = os.path.join(current_dir, "test", "CoreXY_ShellZ.txt")
+        ofile = os.path.join(current_dir, "test", "A_Raw_Example-1.omf")
+        if os.path.isfile(tfile):
+            tfpath = tfile
+            ofpath = ofile
+            break
+    reader = SLDReader()
+    oreader = OMFReader()
+    output = reader.read(tfpath)
+    ooutput = oreader.read(ofpath)
+    foutput = OMF2SLD()
+    foutput.set_data(ooutput)
+
+    import matplotlib.pyplot as plt
+    fig = plt.figure()
+    ax = fig.gca(projection='3d')
+    ax.plot(output.pos_x, output.pos_y, output.pos_z, '.', c="g",
+            alpha=0.7, markeredgecolor='gray', rasterized=True)
+    gap = 7
+    max_mx = max(output.sld_mx)
+    max_my = max(output.sld_my)
+    max_mz = max(output.sld_mz)
+    max_m = max(max_mx, max_my, max_mz)
+    x2 = output.pos_x+output.sld_mx/max_m * gap
+    y2 = output.pos_y+output.sld_my/max_m * gap
+    z2 = output.pos_z+output.sld_mz/max_m * gap
+    x_arrow = numpy.column_stack((output.pos_x, x2))
+    y_arrow = numpy.column_stack((output.pos_y, y2))
+    z_arrow = numpy.column_stack((output.pos_z, z2))
+    unit_x2 = output.sld_mx / max_m
+    unit_y2 = output.sld_my / max_m
+    unit_z2 = output.sld_mz / max_m
+    color_x = numpy.fabs(unit_x2 * 0.8)
+    color_y = numpy.fabs(unit_y2 * 0.8)
+    color_z = numpy.fabs(unit_z2 * 0.8)
+    colors = numpy.column_stack((color_x, color_y, color_z))
+    a = Arrow3D(None, x_arrow, y_arrow, z_arrow, colors,
+                mutation_scale=10, lw=1, arrowstyle="->",
+                color="y", alpha=0.5)
+    ax.add_artist(a)
+    plt.show()
+
+def test():
+    """
+        Test code
+    """
+    current_dir = os.path.abspath(os.path.curdir)
+    for i in range(3):
+        current_dir, _ = os.path.split(current_dir)
+        ofile = os.path.join(current_dir, "test", "A_Raw_Example-1.omf")
+        if os.path.isfile(ofile):
+            ofpath = ofile
+            break
+    oreader = OMFReader()
+    ooutput = oreader.read(ofpath)
+    foutput = OMF2SLD()
+    foutput.set_data(ooutput)
+    writer = SLDReader()
+    writer.write(os.path.join(os.path.dirname(ofpath), "out.txt"),
+                 foutput.output)
+    model = GenSAS()
+    model.set_sld_data(foutput.output)
+    x = numpy.arange(1000)/10000. + 1e-5
+    y = numpy.arange(1000)/10000. + 1e-5
+    i = numpy.zeros(1000)
+    model.runXY([x, y, i])
+
+if __name__ == "__main__":
+    test()
+    test_load()
Index: src/sas/sascalc/calculator/slit_length_calculator.py
===================================================================
--- src/sas/sascalc/calculator/slit_length_calculator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/calculator/slit_length_calculator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,108 @@
+"""
+This module is a small tool to allow user to quickly
+determine the slit length value of data.
+"""
+
+
+class SlitlengthCalculator(object):
+    """
+    compute slit length from SAXSess beam profile (1st col. Q , 2nd col. I ,
+    and 3rd col. dI.: don't need the 3rd)
+    """
+    def __init__(self):
+
+        # x data
+        self.x = None
+        # y data
+        self.y = None
+        #default slit length
+        self.slit_length = 0.0
+
+        # The unit is unknown from SAXSess profile:
+        # It seems 1/nm but it could be not fixed,
+        # so users should be notified to determine the unit by themselves.
+        self.slit_length_unit = "unknown"
+
+    def set_data(self, x=None, y=None):
+        """
+         Receive two vector x, y and prepare the slit calculator for
+         computation.
+
+        :param x: array
+        :param y: array
+        """
+        self.x = x
+        self.y = y
+
+    def calculate_slit_length(self):
+        """
+        Calculate slit length.
+
+        :return: the slit length calculated value.
+        """
+        # None data do nothing
+        if self.y == None or self.x == None:
+            return
+        # set local variable
+        y = self.y
+        x = self.x
+
+        # find max y
+        max_y = y.max()
+
+        # initial values
+        y_sum = 0.0
+        y_max = 0.0
+        ind = 0.0
+
+        # sum 10 or more y values until getting max_y,
+        while True:
+            if ind >= 10 and y_max == max_y:
+                break
+            y_sum = y_sum + y[ind]
+            if y[ind] > y_max:
+                y_max = y[ind]
+            ind += 1
+
+        # find the average value/2 of the top values
+        y_half = y_sum/(2.0*ind)
+
+        # defaults
+        y_half_d = 0.0
+        ind = 0.0
+        # find indices where it crosses y = y_half.
+        while True:
+            # no need to check when ind == 0
+            ind += 1
+            # y value and ind just after passed the spot of the half height
+            y_half_d = y[ind]
+            if y[ind] < y_half:
+                break
+
+        # y value and ind just before passed the spot of the half height
+        y_half_u = y[ind-1]
+
+        # get corresponding x values
+        x_half_d = x[ind]
+        x_half_u = x[ind-1]
+
+        # calculate x at y = y_half using linear interpolation
+        if y_half_u == y_half_d:
+            x_half = (x_half_d + x_half_u)/2.0
+        else:
+            x_half = (x_half_u * (y_half - y_half_d)  \
+                       + x_half_d * (y_half_u - y_half)) \
+                        / (y_half_u - y_half_d)
+
+        # Our slit length is half width, so just give half beam value
+        slit_length = x_half
+
+        # set slit_length
+        self.slit_length = slit_length
+        return self.slit_length
+
+    def get_slit_length_unit(self):
+        """
+        :return: the slit length unit.
+        """
+        return self.slit_length_unit
Index: src/sas/sascalc/data_util/calcthread.py
===================================================================
--- src/sas/sascalc/data_util/calcthread.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/calcthread.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,305 @@
+# This program is public domain
+
+## \file
+#  \brief Abstract class for defining calculation threads.
+#
+
+import thread
+import traceback
+import sys
+
+if sys.platform.count("darwin") > 0:
+    import time
+    stime = time.time()
+    
+    def clock():
+        return time.time() - stime
+    
+    def sleep(t):
+        return time.sleep(t)
+else:
+    from time import clock
+    from time import sleep
+
+
+class CalcThread:
+    """Threaded calculation class.  Inherit from here and specialize
+    the compute() method to perform the appropriate operations for the
+    class.
+
+    If you specialize the __init__ method be sure to call
+    CalcThread.__init__, passing it the keyword arguments for
+    yieldtime, worktime, update and complete.
+    
+    When defining the compute() method you need to include code which
+    allows the GUI to run.  They are as follows: ::
+
+        self.isquit()          # call frequently to check for interrupts
+        self.update(kw=...)    # call when the GUI could be updated
+        self.complete(kw=...)  # call before exiting compute()
+
+    The update() and complete() calls accept field=value keyword
+    arguments which are passed to the called function.  complete()
+    should be called before exiting the GUI function.  A KeyboardInterrupt
+    event is triggered if the GUI signals that the computation should
+    be halted.
+
+    The following documentation should be included in the description
+    of the derived class.
+
+    The user of this class will call the following: ::
+
+        thread = Work(...,kw=...)  # prepare the work thread.
+        thread.queue(...,kw=...)   # queue a work unit
+        thread.requeue(...,kw=...) # replace work unit on the end of queue
+        thread.reset(...,kw=...)   # reset the queue to the given work unit
+        thread.stop()              # clear the queue and halt
+        thread.interrupt()         # halt the current work unit but continue
+        thread.ready(delay=0.)     # request an update signal after delay
+        thread.isrunning()         # returns true if compute() is running
+
+    Use queue() when all work must be done.  Use requeue() when intermediate
+    work items don't need to be done (e.g., in response to a mouse move
+    event).  Use reset() when the current item doesn't need to be completed
+    before the new event (e.g., in response to a mouse release event).  Use
+    stop() to halt the current and pending computations (e.g., in response to
+    a stop button).
+
+    The methods queue(), requeue() and reset() are proxies for the compute()
+    method in the subclass.  Look there for a description of the arguments.
+    The compute() method can be called directly to run the computation in
+    the main thread, but it should not be called if isrunning() returns true.
+
+    The constructor accepts additional keywords yieldtime=0.01 and
+    worktime=0.01 which determine the cooperative multitasking
+    behaviour.  Yield time is the duration of the sleep period
+    required to give other processes a chance to run.  Work time
+    is the duration between sleep periods.
+
+    Notifying the GUI thread of work in progress and work complete
+    is done with updatefn=updatefn and completefn=completefn arguments
+    to the constructor.  Details of the parameters to the functions
+    depend on the particular calculation class, but they will all
+    be passed as keyword arguments.  Details of how the functions
+    should be implemented vary from framework to framework.
+
+    For wx, something like the following is needed::
+
+        import wx, wx.lib.newevent
+        (CalcCompleteEvent, EVT_CALC_COMPLETE) = wx.lib.newevent.NewEvent()
+
+        # methods in the main window class of your application
+        def __init__():
+            ...
+            # Prepare the calculation in the GUI thread.
+            self.work = Work(completefn=self.CalcComplete)
+            self.Bind(EVT_CALC_COMPLETE, self.OnCalcComplete)
+            ...
+            # Bind work queue to a menu event.
+            self.Bind(wx.EVT_MENU, self.OnCalcStart, id=idCALCSTART)
+            ...
+
+        def OnCalcStart(self,event):
+            # Start the work thread from the GUI thread.
+            self.work.queue(...work unit parameters...)
+
+        def CalcComplete(self,**kwargs):
+            # Generate CalcComplete event in the calculation thread.
+            # kwargs contains field1, field2, etc. as defined by
+            # the Work thread class.
+            event = CalcCompleteEvent(**kwargs)
+            wx.PostEvent(self, event)
+
+        def OnCalcComplete(self,event):
+            # Process CalcComplete event in GUI thread.
+            # Use values from event.field1, event.field2 etc. as
+            # defined by the Work thread class to show the results.
+            ...
+    """
+
+    def __init__(self, completefn=None, updatefn=None,
+                 yieldtime=0.01, worktime=0.01):
+        """Prepare the calculator"""
+        self.yieldtime     = yieldtime
+        self.worktime      = worktime
+        self.completefn    = completefn
+        self.updatefn      = updatefn
+        self._interrupting = False
+        self._running      = False
+        self._queue        = []
+        self._lock         = thread.allocate_lock()
+        self._delay        = 1e6
+
+    def queue(self,*args,**kwargs):
+        """Add a work unit to the end of the queue.  See the compute()
+        method for details of the arguments to the work unit."""
+        self._lock.acquire()
+        self._queue.append((args, kwargs))
+        # Cannot do start_new_thread call within the lock
+        self._lock.release()
+        if not self._running:
+            self._time_for_update = clock() + 1e6
+            thread.start_new_thread(self._run, ())
+
+    def requeue(self, *args, **kwargs):
+        """Replace the work unit on the end of the queue.  See the compute()
+        method for details of the arguments to the work unit."""
+        self._lock.acquire()
+        self._queue = self._queue[:-1]
+        self._lock.release()
+        self.queue(*args, **kwargs)
+
+    def reset(self, *args, **kwargs):
+        """Clear the queue and start a new work unit.  See the compute()
+        method for details of the arguments to the work unit."""
+        self.stop()
+        self.queue(*args, **kwargs)
+
+    def stop(self):
+        """Clear the queue and stop the thread.  New items may be
+        queued after stop.  To stop just the current work item, and
+        continue the rest of the queue call the interrupt method"""
+        self._lock.acquire()
+        self._interrupting = True
+        self._queue = []
+        self._lock.release()
+
+    def interrupt(self):
+        """Stop the current work item.  To clear the work queue as
+        well call the stop() method."""
+        self._lock.acquire()
+        self._interrupting = True
+        self._lock.release()
+
+    def isrunning(self):
+        return self._running
+
+    def ready(self, delay=0.):
+        """Ready for another update after delay=t seconds.  Call
+        this for threads which can show intermediate results from
+        long calculations."""
+        self._delay = delay
+        self._lock.acquire()
+        self._time_for_update = clock() + delay
+        # print "setting _time_for_update to ",self._time_for_update
+        self._lock.release()
+
+    def isquit(self):
+        """Check for interrupts.  Should be called frequently to
+        provide user responsiveness.  Also yields to other running
+        threads, which is required for good performance on OS X."""
+
+        # Only called from within the running thread so no need to lock
+        if self._running and self.yieldtime > 0 \
+            and clock() > self._time_for_nap:
+            sleep(self.yieldtime)
+            self._time_for_nap = clock() + self.worktime
+        if self._interrupting:
+            raise KeyboardInterrupt
+
+    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:
+            self._lock.acquire()
+            self._time_for_update = clock() + self._delay
+            self._lock.release()
+            self._time_for_update += 1e6  # No more updates
+            
+            self.updatefn(**kwargs)
+            sleep(self.yieldtime)
+            if self._interrupting:
+                raise KeyboardInterrupt
+        else:
+            self.isquit()
+        return
+
+    def complete(self, **kwargs):
+        """Update the GUI with the completed results from a work unit."""
+        if self.completefn != None:
+            self.completefn(**kwargs)
+            sleep(self.yieldtime)
+        return
+
+    def compute(self, *args, **kwargs):
+        """Perform a work unit.  The subclass will provide details of
+        the arguments."""
+        raise NotImplemented, "Calculation thread needs compute method"
+
+    def _run(self):
+        """Internal function to manage the thread."""
+        # The code for condition wait in the threading package is
+        # implemented using polling.  I'll accept for now that the
+        # authors of this code are clever enough that polling is
+        # difficult to avoid.  Rather than polling, I will exit the
+        # thread when the queue is empty and start a new thread when
+        # there is more work to be done.
+        while 1:
+            self._lock.acquire()
+            self._time_for_nap = clock() + self.worktime
+            self._running = True
+            if self._queue == []:
+                break
+            self._interrupting = False
+            args, kwargs = self._queue[0]
+            self._queue = self._queue[1:]
+            self._lock.release()
+            try:
+                self.compute(*args, **kwargs)
+            except KeyboardInterrupt:
+                pass
+            except:
+                traceback.print_exc()
+                #print 'CalcThread exception',
+        self._running = False
+
+
+# ======================================================================
+# Demonstration of calcthread in action
+class CalcDemo(CalcThread):
+    """Example of a calculation thread."""
+    def compute(self, n):
+        total = 0.
+        for i in range(n):
+            self.update(i=i)
+            for j in range(n):
+                self.isquit()
+                total += j
+        self.complete(total=total)
+
+
+class CalcCommandline:
+    """
+        Test method
+    """
+    def __init__(self, n=20000):
+        print thread.get_ident()
+        self.starttime = clock()
+        self.done = False
+        self.work = CalcDemo(completefn=self.complete,
+                             updatefn=self.update, yieldtime=0.001)
+        self.work2 = CalcDemo(completefn=self.complete,
+                             updatefn=self.update)
+        self.work3 = CalcDemo(completefn=self.complete,
+                             updatefn=self.update)
+        self.work.queue(n)
+        self.work2.queue(n)
+        self.work3.queue(n)
+        print "Expect updates from Main every second and from thread every 2.5 seconds"
+        print ""
+        self.work.ready(.5)
+        while not self.done:
+            sleep(1)
+            print "Main thread %d at %.2f" % (thread.get_ident(),
+                                              clock() - self.starttime)
+
+    def update(self, i=0):
+        print "Update i=%d from thread %d at %.2f" % (i, thread.get_ident(),
+                                                      clock() - self.starttime)
+        self.work.ready(2.5)
+
+    def complete(self, total=0.0):
+        print "Complete total=%g from thread %d at %.2f" % (total,
+                                                    thread.get_ident(),
+                                                    clock() - self.starttime)
+        self.done = True
Index: src/sas/sascalc/data_util/err1d.py
===================================================================
--- src/sas/sascalc/data_util/err1d.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/err1d.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,155 @@
+# This program is public domain
+"""
+Error propogation algorithms for simple arithmetic
+
+Warning: like the underlying numpy library, the inplace operations
+may return values of the wrong type if some of the arguments are
+integers, so be sure to create them with floating point inputs.
+"""
+from __future__ import division  # Get true division
+import numpy
+
+
+def div(X, varX, Y, varY):
+    """Division with error propagation"""
+    # Direct algorithm:
+    #   Z = X/Y
+    #   varZ = (varX/X**2 + varY/Y**2) * Z**2
+    #        = (varX + varY * Z**2) / Y**2
+    # Indirect algorithm to minimize intermediates
+    Z = X/Y      # truediv => Z is a float
+    varZ = Z**2  # Z is a float => varZ is a float
+    varZ *= varY
+    varZ += varX
+    T = Y**2     # Doesn't matter if T is float or int
+    varZ /= T
+    return Z, varZ
+
+
+def mul(X, varX, Y, varY):
+    """Multiplication with error propagation"""
+    # Direct algorithm:
+    Z = X * Y
+    varZ = Y**2 * varX + X**2 * varY
+    # Indirect algorithm won't ensure floating point results
+    #   varZ = Y**2
+    #   varZ *= varX
+    #   Z = X**2   # Using Z to hold the temporary
+    #   Z *= varY
+    #   varZ += Z
+    #   Z[:] = X
+    #   Z *= Y
+    return Z, varZ
+
+
+def sub(X, varX, Y, varY):
+    """Subtraction with error propagation"""
+    Z = X - Y
+    varZ = varX + varY
+    return Z, varZ
+
+
+def add(X, varX, Y, varY):
+    """Addition with error propagation"""
+    Z = X + Y
+    varZ = varX + varY
+    return Z, varZ
+
+
+def exp(X, varX):
+    """Exponentiation with error propagation"""
+    Z = numpy.exp(X)
+    varZ = varX * Z**2
+    return Z, varZ
+
+
+def log(X, varX):
+    """Logarithm with error propagation"""
+    Z = numpy.log(X)
+    varZ = varX / X**2
+    return Z, varZ
+
+# Confirm this formula before using it
+# def pow(X,varX, Y,varY):
+#    Z = X**Y
+#    varZ = (Y**2 * varX/X**2 + varY * numpy.log(X)**2) * Z**2
+#    return Z,varZ
+#
+
+
+def pow(X, varX, n):
+    """X**n with error propagation"""
+    # Direct algorithm
+    #   Z = X**n
+    #   varZ = n*n * varX/X**2 * Z**2
+    # Indirect algorithm to minimize intermediates
+    Z = X**n
+    varZ = varX / X
+    varZ /= X
+    varZ *= Z
+    varZ *= Z
+    varZ *= n**2
+    return Z, varZ
+
+
+def div_inplace(X, varX, Y, varY):
+    """In-place division with error propagation"""
+    # Z = X/Y
+    # varZ = (varX + varY * (X/Y)**2) / Y**2 = (varX + varY * Z**2) / Y**2
+    X /= Y     # X now has Z = X/Y
+    T = X**2   # create T with Z**2
+    T *= varY  # T now has varY * Z**2
+    varX += T  # varX now has varX + varY*Z**2
+    del T   # may want to use T[:] = Y for vectors
+    T = Y   # reuse T for Y
+    T **= 2     # T now has Y**2
+    varX /= T  # varX now has varZ
+    return X, varX
+
+
+def mul_inplace(X, varX, Y, varY):
+    """In-place multiplication with error propagation"""
+    # Z = X * Y
+    # varZ = Y**2 * varX + X**2 * varY
+    T = Y**2   # create T with Y**2
+    varX *= T  # varX now has Y**2 * varX
+    del T   # may want to use T[:] = X for vectors
+    T = X   # reuse T for X**2 * varY
+    T **=2     # T now has X**2
+    T *= varY  # T now has X**2 * varY
+    varX += T  # varX now has varZ
+    X *= Y     # X now has Z
+    return X, varX
+
+
+def sub_inplace(X, varX, Y, varY):
+    """In-place subtraction with error propagation"""
+    # Z = X - Y
+    # varZ = varX + varY
+    X -= Y
+    varX += varY
+    return X, varX
+
+
+def add_inplace(X, varX, Y, varY):
+    """In-place addition with error propagation"""
+    # Z = X + Y
+    # varZ = varX + varY
+    X += Y
+    varX += varY
+    return X, varX
+
+
+def pow_inplace(X, varX, n):
+    """In-place X**n with error propagation"""
+    # Direct algorithm
+    #   Z = X**n
+    #   varZ = abs(n) * varX/X**2 * Z**2
+    # Indirect algorithm to minimize intermediates
+    varX /= X
+    varX /= X     # varX now has varX/X**2
+    X **= n       # X now has Z = X**n
+    varX *= X
+    varX *= X     # varX now has varX/X**2 * Z**2
+    varX *= n**2  # varX now has varZ
+    return X, varX
Index: src/sas/sascalc/data_util/formatnum.py
===================================================================
--- src/sas/sascalc/data_util/formatnum.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/formatnum.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,441 @@
+# This program is public domain
+# Author: Paul Kienzle
+"""
+Format values and uncertainties nicely for printing.
+
+:func:`format_uncertainty_pm` produces the expanded format v +/- err.
+
+:func:`format_uncertainty_compact` produces the compact format v(##),
+where the number in parenthesis is the uncertainty in the last two digits of v.
+
+:func:`format_uncertainty` uses the compact format by default, but this
+can be changed to use the expanded +/- format by setting 
+format_uncertainty.compact to False.
+    
+The formatted string uses only the number of digits warranted by
+the uncertainty in the measurement.
+
+If the uncertainty is 0 or not otherwise provided, the simple
+%g floating point format option is used.
+
+Infinite and indefinite numbers are represented as inf and NaN.
+
+Example::
+
+    >>> v,dv = 757.2356,0.01032
+    >>> print format_uncertainty_pm(v,dv)
+    757.236 +/- 0.010
+    >>> print format_uncertainty_compact(v,dv)
+    757.236(10)
+    >>> print format_uncertainty(v,dv)
+    757.236(10)
+    >>> format_uncertainty.compact = False
+    >>> print format_uncertainty(v,dv)
+    757.236 +/- 0.010
+
+UncertaintyFormatter() returns a private formatter with its own
+formatter.compact flag.
+"""
+from __future__ import division
+
+import math
+import numpy
+__all__ = ['format_uncertainty', 'format_uncertainty_pm',
+           'format_uncertainty_compact']
+
+# Coordinating scales across a set of numbers is not supported.  For easy
+# comparison a set of numbers should be shown in the same scale.  One could
+# force this from the outside by adding scale parameter (either 10**n, n, or
+# a string representing the desired SI prefix) and having a separate routine
+# which computes the scale given a set of values.
+
+# Coordinating scales with units offers its own problems.  Again, the user
+# may want to force particular units.  This can be done by outside of the
+# formatting routines by scaling the numbers to the appropriate units then
+# forcing them to print with scale 10**0.  If this is a common operation,
+# however, it may want to happen inside.
+
+# The value e<n> is currently formatted into the number.  Alternatively this
+# scale factor could be returned so that the user can choose the appropriate
+# SI prefix when printing the units.  This gets tricky when talking about
+# composite units such as 2.3e-3 m**2 -> 2300 mm**2, and with volumes
+# such as 1 g/cm**3 -> 1 kg/L.
+
+
+def format_uncertainty_pm(value, uncertainty):
+    """
+    Given *value* v and *uncertainty* dv, return a string v +/- dv.
+    """
+    return _format_uncertainty(value, uncertainty, compact=False)
+
+
+def format_uncertainty_compact(value, uncertainty):
+    """
+    Given *value* v and *uncertainty* dv, return the compact
+    representation v(##), where ## are the first two digits of
+    the uncertainty.
+    """
+    return _format_uncertainty(value, uncertainty, compact=True)
+
+
+class UncertaintyFormatter:
+    """
+    Value and uncertainty formatter.
+
+    The *formatter* instance will use either the expanded v +/- dv form
+    or the compact v(##) form depending on whether *formatter.compact* is
+    True or False.  The default is True.
+    """
+    compact = True
+    
+    def __call__(self, value, uncertainty):
+        """
+        Given *value* and *uncertainty*, return a string representation.
+        """
+        return _format_uncertainty(value, uncertainty, self.compact)
+format_uncertainty = UncertaintyFormatter()
+
+
+def _format_uncertainty(value, uncertainty, compact):
+    """
+    Implementation of both the compact and the +/- formats.
+    """
+    # Handle indefinite value
+    if numpy.isinf(value):
+        return "inf" if value > 0 else "-inf"
+    if numpy.isnan(value):
+        return "NaN"
+
+    # Handle indefinite uncertainty
+    if uncertainty is None or uncertainty <= 0 or numpy.isnan(uncertainty):
+        return "%g" % value
+    if numpy.isinf(uncertainty):
+        if compact:
+            return "%.2g(inf)" % value
+        else:
+            return "%.2g +/- inf" % value
+
+    # Handle zero and negative values
+    sign = "-" if value < 0 else ""
+    value = abs(value)
+
+    # Determine scale of value and error
+    err_place = int(math.floor(math.log10(uncertainty)))
+    if value == 0:
+        val_place = err_place - 1
+    else:
+        val_place = int(math.floor(math.log10(value)))
+
+    if err_place > val_place:
+        # Degenerate case: error bigger than value
+        # The mantissa is 0.#(##)e#, 0.0#(##)e# or 0.00#(##)e#
+        val_place = err_place + 2
+    elif err_place == val_place:
+        # Degenerate case: error and value the same order of magnitude
+        # The value is ##(##)e#, #.#(##)e# or 0.##(##)e#
+        val_place = err_place + 1
+    elif err_place <= 1 and val_place >= -3:
+        # Normal case: nice numbers and errors
+        # The value is ###.###(##)
+        val_place = 0
+    else:
+        # Extreme cases: zeros before value or after error
+        # The value is ###.###(##)e#, ##.####(##)e# or #.#####(##)e#
+        pass
+    
+    # Force engineering notation, with exponent a multiple of 3
+    val_place = int(math.floor(val_place / 3.)) * 3
+
+    # Format the result
+    digits_after_decimal = abs(val_place - err_place + 1)
+    val_str = "%.*f" % (digits_after_decimal, value / 10.**val_place)
+    exp_str = "e%d" % val_place if val_place != 0 else ""
+    if compact:
+        err_str = "(%2d)" % int(uncertainty / 10.**(err_place - 1) + 0.5)
+        result = "".join((sign, val_str, err_str, exp_str))
+    else:
+        err_str = "%.*f" % (digits_after_decimal, uncertainty / 10.**val_place)
+        result = "".join((sign, val_str, exp_str + " +/- ", err_str, exp_str))
+    return result
+
+
+def test_compact():
+    # Oops... renamed function after writing tests
+    value_str = format_uncertainty_compact
+
+    # val_place > err_place
+    assert value_str(1235670,766000) == "1.24(77)e6"
+    assert value_str(123567.,76600) == "124(77)e3"
+    assert value_str(12356.7,7660) == "12.4(77)e3"
+    assert value_str(1235.67,766) == "1.24(77)e3"
+    assert value_str(123.567,76.6) == "124(77)"
+    assert value_str(12.3567,7.66) == "12.4(77)"
+    assert value_str(1.23567,.766) == "1.24(77)"
+    assert value_str(.123567,.0766) == "0.124(77)"
+    assert value_str(.0123567,.00766) == "0.0124(77)"
+    assert value_str(.00123567,.000766) == "0.00124(77)"
+    assert value_str(.000123567,.0000766) == "124(77)e-6"
+    assert value_str(.0000123567,.00000766) == "12.4(77)e-6"
+    assert value_str(.00000123567,.000000766) == "1.24(77)e-6"
+    assert value_str(.000000123567,.0000000766) == "124(77)e-9"
+    assert value_str(.00000123567,.0000000766) == "1.236(77)e-6"
+    assert value_str(.0000123567,.0000000766) == "12.357(77)e-6"
+    assert value_str(.000123567,.0000000766) == "123.567(77)e-6"
+    assert value_str(.00123567,.000000766) == "0.00123567(77)"
+    assert value_str(.0123567,.00000766) == "0.0123567(77)"
+    assert value_str(.123567,.0000766) == "0.123567(77)"
+    assert value_str(1.23567,.000766) == "1.23567(77)"
+    assert value_str(12.3567,.00766) == "12.3567(77)"
+    assert value_str(123.567,.0764) == "123.567(76)"
+    assert value_str(1235.67,.764) == "1235.67(76)"
+    assert value_str(12356.7,7.64) == "12356.7(76)"
+    assert value_str(123567,76.4) == "123567(76)"
+    assert value_str(1235670,764) == "1.23567(76)e6"
+    assert value_str(12356700,764) == "12.35670(76)e6"
+    assert value_str(123567000,764) == "123.56700(76)e6"
+    assert value_str(123567000,7640) == "123.5670(76)e6"
+    assert value_str(1235670000,76400) == "1.235670(76)e9"
+
+    # val_place == err_place
+    assert value_str(123567,764000) == "0.12(76)e6"
+    assert value_str(12356.7,76400) == "12(76)e3"
+    assert value_str(1235.67,7640) == "1.2(76)e3"
+    assert value_str(123.567,764) == "0.12(76)e3"
+    assert value_str(12.3567,76.4) == "12(76)"
+    assert value_str(1.23567,7.64) == "1.2(76)"
+    assert value_str(.123567,.764) == "0.12(76)"
+    assert value_str(.0123567,.0764) == "12(76)e-3"
+    assert value_str(.00123567,.00764) == "1.2(76)e-3"
+    assert value_str(.000123567,.000764) == "0.12(76)e-3"
+
+    # val_place == err_place-1
+    assert value_str(123567,7640000) == "0.1(76)e6"
+    assert value_str(12356.7,764000) == "0.01(76)e6"
+    assert value_str(1235.67,76400) == "0.001(76)e6"
+    assert value_str(123.567,7640) == "0.1(76)e3"
+    assert value_str(12.3567,764) == "0.01(76)e3"
+    assert value_str(1.23567,76.4) == "0.001(76)e3"
+    assert value_str(.123567,7.64) == "0.1(76)"
+    assert value_str(.0123567,.764) == "0.01(76)"
+    assert value_str(.00123567,.0764) == "0.001(76)"
+    assert value_str(.000123567,.00764) == "0.1(76)e-3"
+
+    # val_place == err_place-2
+    assert value_str(12356700,7640000000) == "0.0(76)e9"
+    assert value_str(1235670,764000000) == "0.00(76)e9"
+    assert value_str(123567,76400000) == "0.000(76)e9"
+    assert value_str(12356,7640000) == "0.0(76)e6"
+    assert value_str(1235,764000) == "0.00(76)e6"
+    assert value_str(123,76400) == "0.000(76)e6"
+    assert value_str(12,7640) == "0.0(76)e3"
+    assert value_str(1,764) == "0.00(76)e3"
+    assert value_str(0.1,76.4) == "0.000(76)e3"
+    assert value_str(0.01,7.64) == "0.0(76)"
+    assert value_str(0.001,0.764) == "0.00(76)"
+    assert value_str(0.0001,0.0764) == "0.000(76)"
+    assert value_str(0.00001,0.00764) == "0.0(76)e-3"
+
+    # val_place == err_place-3
+    assert value_str(12356700,76400000000) == "0.000(76)e12"
+    assert value_str(1235670,7640000000) == "0.0(76)e9"
+    assert value_str(123567,764000000) == "0.00(76)e9"
+    assert value_str(12356,76400000) == "0.000(76)e9"
+    assert value_str(1235,7640000) == "0.0(76)e6"
+    assert value_str(123,764000) == "0.00(76)e6"
+    assert value_str(12,76400) == "0.000(76)e6"
+    assert value_str(1,7640) == "0.0(76)e3"
+    assert value_str(0.1,764) == "0.00(76)e3"
+    assert value_str(0.01,76.4) == "0.000(76)e3"
+    assert value_str(0.001,7.64) == "0.0(76)"
+    assert value_str(0.0001,0.764) == "0.00(76)"
+    assert value_str(0.00001,0.0764) == "0.000(76)"
+    assert value_str(0.000001,0.00764) == "0.0(76)e-3"
+
+    # Zero values
+    assert value_str(0,7640000) == "0.0(76)e6"
+    assert value_str(0, 764000) == "0.00(76)e6"
+    assert value_str(0,  76400) == "0.000(76)e6"
+    assert value_str(0,   7640) == "0.0(76)e3"
+    assert value_str(0,    764) == "0.00(76)e3"
+    assert value_str(0,     76.4) == "0.000(76)e3"
+    assert value_str(0,      7.64) == "0.0(76)"
+    assert value_str(0,      0.764) == "0.00(76)"
+    assert value_str(0,      0.0764) == "0.000(76)"
+    assert value_str(0,      0.00764) == "0.0(76)e-3"
+    assert value_str(0,      0.000764) == "0.00(76)e-3"
+    assert value_str(0,      0.0000764) == "0.000(76)e-3"
+
+    # negative values
+    assert value_str(-1235670,765000) == "-1.24(77)e6"
+    assert value_str(-1.23567,.766) == "-1.24(77)"
+    assert value_str(-.00000123567,.0000000766) == "-1.236(77)e-6"
+    assert value_str(-12356.7,7.64) == "-12356.7(76)"
+    assert value_str(-123.567,764) == "-0.12(76)e3"
+    assert value_str(-1235.67,76400) == "-0.001(76)e6"
+    assert value_str(-.000123567,.00764) == "-0.1(76)e-3"
+    assert value_str(-12356,7640000) == "-0.0(76)e6"
+    assert value_str(-12,76400) == "-0.000(76)e6"
+    assert value_str(-0.0001,0.764) == "-0.00(76)"
+
+    # non-finite values
+    assert value_str(-numpy.inf,None) == "-inf"
+    assert value_str(numpy.inf,None) == "inf"
+    assert value_str(numpy.NaN,None) == "NaN"
+    
+    # bad or missing uncertainty
+    assert value_str(-1.23567,numpy.NaN) == "-1.23567"
+    assert value_str(-1.23567,-numpy.inf) == "-1.23567"
+    assert value_str(-1.23567,-0.1) == "-1.23567"
+    assert value_str(-1.23567,0) == "-1.23567"
+    assert value_str(-1.23567,None) == "-1.23567"
+    assert value_str(-1.23567,numpy.inf) == "-1.2(inf)"
+
+def test_pm():
+    # Oops... renamed function after writing tests
+    value_str = format_uncertainty_pm
+
+    # val_place > err_place
+    assert value_str(1235670,766000) == "1.24e6 +/- 0.77e6"
+    assert value_str(123567., 76600) == "124e3 +/- 77e3"
+    assert value_str(12356.7,  7660) == "12.4e3 +/- 7.7e3"
+    assert value_str(1235.67,   766) == "1.24e3 +/- 0.77e3"
+    assert value_str(123.567,    76.6) == "124 +/- 77"
+    assert value_str(12.3567,     7.66) == "12.4 +/- 7.7"
+    assert value_str(1.23567,      .766) == "1.24 +/- 0.77"
+    assert value_str(.123567,      .0766) == "0.124 +/- 0.077"
+    assert value_str(.0123567,     .00766) == "0.0124 +/- 0.0077"
+    assert value_str(.00123567,    .000766) == "0.00124 +/- 0.00077"
+    assert value_str(.000123567,   .0000766) == "124e-6 +/- 77e-6"
+    assert value_str(.0000123567,  .00000766) == "12.4e-6 +/- 7.7e-6"
+    assert value_str(.00000123567, .000000766) == "1.24e-6 +/- 0.77e-6"
+    assert value_str(.000000123567,.0000000766) == "124e-9 +/- 77e-9"
+    assert value_str(.00000123567, .0000000766) == "1.236e-6 +/- 0.077e-6"
+    assert value_str(.0000123567,  .0000000766) == "12.357e-6 +/- 0.077e-6"
+    assert value_str(.000123567,   .0000000766) == "123.567e-6 +/- 0.077e-6"
+    assert value_str(.00123567,    .000000766) == "0.00123567 +/- 0.00000077"
+    assert value_str(.0123567,     .00000766) == "0.0123567 +/- 0.0000077"
+    assert value_str(.123567,      .0000766) == "0.123567 +/- 0.000077"
+    assert value_str(1.23567,      .000766) == "1.23567 +/- 0.00077"
+    assert value_str(12.3567,      .00766) == "12.3567 +/- 0.0077"
+    assert value_str(123.567,      .0764) == "123.567 +/- 0.076"
+    assert value_str(1235.67,      .764) == "1235.67 +/- 0.76"
+    assert value_str(12356.7,     7.64) == "12356.7 +/- 7.6"
+    assert value_str(123567,     76.4) == "123567 +/- 76"
+    assert value_str(1235670,   764) == "1.23567e6 +/- 0.00076e6"
+    assert value_str(12356700,  764) == "12.35670e6 +/- 0.00076e6"
+    assert value_str(123567000, 764) == "123.56700e6 +/- 0.00076e6"
+    assert value_str(123567000,7640) == "123.5670e6 +/- 0.0076e6"
+    assert value_str(1235670000,76400) == "1.235670e9 +/- 0.000076e9"
+
+    # val_place == err_place
+    assert value_str(123567,764000) == "0.12e6 +/- 0.76e6"
+    assert value_str(12356.7,76400) == "12e3 +/- 76e3"
+    assert value_str(1235.67,7640) == "1.2e3 +/- 7.6e3"
+    assert value_str(123.567,764) == "0.12e3 +/- 0.76e3"
+    assert value_str(12.3567,76.4) == "12 +/- 76"
+    assert value_str(1.23567,7.64) == "1.2 +/- 7.6"
+    assert value_str(.123567,.764) == "0.12 +/- 0.76"
+    assert value_str(.0123567,.0764) == "12e-3 +/- 76e-3"
+    assert value_str(.00123567,.00764) == "1.2e-3 +/- 7.6e-3"
+    assert value_str(.000123567,.000764) == "0.12e-3 +/- 0.76e-3"
+
+    # val_place == err_place-1
+    assert value_str(123567,7640000) == "0.1e6 +/- 7.6e6"
+    assert value_str(12356.7,764000) == "0.01e6 +/- 0.76e6"
+    assert value_str(1235.67,76400) == "0.001e6 +/- 0.076e6"
+    assert value_str(123.567,7640) == "0.1e3 +/- 7.6e3"
+    assert value_str(12.3567,764) == "0.01e3 +/- 0.76e3"
+    assert value_str(1.23567,76.4) == "0.001e3 +/- 0.076e3"
+    assert value_str(.123567,7.64) == "0.1 +/- 7.6"
+    assert value_str(.0123567,.764) == "0.01 +/- 0.76"
+    assert value_str(.00123567,.0764) == "0.001 +/- 0.076"
+    assert value_str(.000123567,.00764) == "0.1e-3 +/- 7.6e-3"
+
+    # val_place == err_place-2
+    assert value_str(12356700,7640000000) == "0.0e9 +/- 7.6e9"
+    assert value_str(1235670,764000000) == "0.00e9 +/- 0.76e9"
+    assert value_str(123567,76400000) == "0.000e9 +/- 0.076e9"
+    assert value_str(12356,7640000) == "0.0e6 +/- 7.6e6"
+    assert value_str(1235,764000) == "0.00e6 +/- 0.76e6"
+    assert value_str(123,76400) == "0.000e6 +/- 0.076e6"
+    assert value_str(12,7640) == "0.0e3 +/- 7.6e3"
+    assert value_str(1,764) == "0.00e3 +/- 0.76e3"
+    assert value_str(0.1,76.4) == "0.000e3 +/- 0.076e3"
+    assert value_str(0.01,7.64) == "0.0 +/- 7.6"
+    assert value_str(0.001,0.764) == "0.00 +/- 0.76"
+    assert value_str(0.0001,0.0764) == "0.000 +/- 0.076"
+    assert value_str(0.00001,0.00764) == "0.0e-3 +/- 7.6e-3"
+
+    # val_place == err_place-3
+    assert value_str(12356700,76400000000) == "0.000e12 +/- 0.076e12"
+    assert value_str(1235670,7640000000) == "0.0e9 +/- 7.6e9"
+    assert value_str(123567,764000000) == "0.00e9 +/- 0.76e9"
+    assert value_str(12356,76400000) == "0.000e9 +/- 0.076e9"
+    assert value_str(1235,7640000) == "0.0e6 +/- 7.6e6"
+    assert value_str(123,764000) == "0.00e6 +/- 0.76e6"
+    assert value_str(12,76400) == "0.000e6 +/- 0.076e6"
+    assert value_str(1,7640) == "0.0e3 +/- 7.6e3"
+    assert value_str(0.1,764) == "0.00e3 +/- 0.76e3"
+    assert value_str(0.01,76.4) == "0.000e3 +/- 0.076e3"
+    assert value_str(0.001,7.64) == "0.0 +/- 7.6"
+    assert value_str(0.0001,0.764) == "0.00 +/- 0.76"
+    assert value_str(0.00001,0.0764) == "0.000 +/- 0.076"
+    assert value_str(0.000001,0.00764) == "0.0e-3 +/- 7.6e-3"
+
+    # Zero values
+    assert value_str(0,7640000) == "0.0e6 +/- 7.6e6"
+    assert value_str(0, 764000) == "0.00e6 +/- 0.76e6"
+    assert value_str(0,  76400) == "0.000e6 +/- 0.076e6"
+    assert value_str(0,   7640) == "0.0e3 +/- 7.6e3"
+    assert value_str(0,    764) == "0.00e3 +/- 0.76e3"
+    assert value_str(0,     76.4) == "0.000e3 +/- 0.076e3"
+    assert value_str(0,      7.64) == "0.0 +/- 7.6"
+    assert value_str(0,      0.764) == "0.00 +/- 0.76"
+    assert value_str(0,      0.0764) == "0.000 +/- 0.076"
+    assert value_str(0,      0.00764) == "0.0e-3 +/- 7.6e-3"
+    assert value_str(0,      0.000764) == "0.00e-3 +/- 0.76e-3"
+    assert value_str(0,      0.0000764) == "0.000e-3 +/- 0.076e-3"
+
+    # negative values
+    assert value_str(-1235670,766000) == "-1.24e6 +/- 0.77e6"
+    assert value_str(-1.23567,.766) == "-1.24 +/- 0.77"
+    assert value_str(-.00000123567,.0000000766) == "-1.236e-6 +/- 0.077e-6"
+    assert value_str(-12356.7,7.64) == "-12356.7 +/- 7.6"
+    assert value_str(-123.567,764) == "-0.12e3 +/- 0.76e3"
+    assert value_str(-1235.67,76400) == "-0.001e6 +/- 0.076e6"
+    assert value_str(-.000123567,.00764) == "-0.1e-3 +/- 7.6e-3"
+    assert value_str(-12356,7640000) == "-0.0e6 +/- 7.6e6"
+    assert value_str(-12,76400) == "-0.000e6 +/- 0.076e6"
+    assert value_str(-0.0001,0.764) == "-0.00 +/- 0.76"
+
+    # non-finite values
+    assert value_str(-numpy.inf,None) == "-inf"
+    assert value_str(numpy.inf,None) == "inf"
+    assert value_str(numpy.NaN,None) == "NaN"
+    
+    # bad or missing uncertainty
+    assert value_str(-1.23567,numpy.NaN) == "-1.23567"
+    assert value_str(-1.23567,-numpy.inf) == "-1.23567"
+    assert value_str(-1.23567,-0.1) == "-1.23567"
+    assert value_str(-1.23567,0) == "-1.23567"
+    assert value_str(-1.23567,None) == "-1.23567"
+    assert value_str(-1.23567,numpy.inf) == "-1.2 +/- inf"
+
+def test_default():
+    # Check that the default is the compact format
+    assert format_uncertainty(-1.23567,0.766) == "-1.24(77)"
+
+def main():
+    """
+    Run all tests.
+
+    This is equivalent to "nosetests --with-doctest"
+    """
+    test_compact()
+    test_pm()
+    test_default()
+    
+    import doctest
+    doctest.testmod()
+
+if __name__ == "__main__": main()
Index: src/sas/sascalc/data_util/nxsunit.py
===================================================================
--- src/sas/sascalc/data_util/nxsunit.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/nxsunit.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,210 @@
+# This program is public domain
+# Author: Paul Kienzle
+"""
+Define unit conversion support for NeXus style units.
+
+The unit format is somewhat complicated.  There are variant spellings
+and incorrect capitalization to worry about, as well as forms such as
+"mili*metre" and "1e-7 seconds".
+
+This is a minimal implementation of units including only what I happen to
+need now.  It does not support the complete dimensional analysis provided
+by the package udunits on which NeXus is based, or even the units used
+in the NeXus definition files.
+
+Unlike other units packages, this package does not carry the units along with 
+the value but merely provides a conversion function for transforming values.
+
+Usage example::
+
+    import nxsunit
+    u = nxsunit.Converter('mili*metre')  # Units stored in mm
+    v = u(3000,'m')  # Convert the value 3000 mm into meters
+
+NeXus example::
+
+    # Load sample orientation in radians regardless of how it is stored.
+    # 1. Open the path
+    file.openpath('/entry1/sample/sample_orientation')
+    # 2. scan the attributes, retrieving 'units'
+    units = [for attr,value in file.attrs() if attr == 'units']
+    # 3. set up the converter (assumes that units actually exists)
+    u = nxsunit.Converter(units[0])
+    # 4. read the data and convert to the correct units
+    v = u(file.read(),'radians')
+
+This is a standalone module, not relying on either DANSE or NeXus, and
+can be used for other unit conversion tasks.
+
+Note: minutes are used for angle and seconds are used for time.  We
+cannot tell what the correct interpretation is without knowing something
+about the fields themselves.  If this becomes an issue, we will need to
+allow the application to set the dimension for the unit rather than
+inferring the dimension from an example unit.
+"""
+
+# TODO: Add udunits to NAPI rather than reimplementing it in python
+# TODO: Alternatively, parse the udunits database directly
+# UDUnits:
+#  http://www.unidata.ucar.edu/software/udunits/udunits-1/udunits.txt
+
+from __future__ import division
+import math
+
+__all__ = ['Converter']
+
+# Limited form of units for returning objects of a specific type.
+# Maybe want to do full units handling with e.g., pyre's
+# unit class. For now lets keep it simple.  Note that
+def _build_metric_units(unit,abbr):
+    """
+    Construct standard SI names for the given unit.
+    Builds e.g.,
+        s, ns
+        second, nanosecond, nano*second
+        seconds, nanoseconds
+    Includes prefixes for femto through peta.
+
+    Ack! Allows, e.g., Coulomb and coulomb even though Coulomb is not
+    a unit because some NeXus files store it that way!
+    
+    Returns a dictionary of names and scales.
+    """
+    prefix = dict(peta=1e15,tera=1e12,giga=1e9,mega=1e6,kilo=1e3,
+                  deci=1e-1,centi=1e-2,milli=1e-3,mili=1e-3,micro=1e-6,
+                  nano=1e-9,pico=1e-12,femto=1e-15)
+    short_prefix = dict(P=1e15,T=1e12,G=1e9,M=1e6,k=1e3,
+                        d=1e-1,c=1e-2,m=1e-3,u=1e-6,
+                        n=1e-9,p=1e-12,f=1e-15)
+    map = {abbr:1}
+    map.update([(P+abbr,scale) for (P,scale) in short_prefix.iteritems()])
+    for name in [unit,unit.capitalize()]:
+        map.update({name:1,name+'s':1})
+        map.update([(P+name,scale) for (P,scale) in prefix.iteritems()])
+        map.update([(P+'*'+name,scale) for (P,scale) in prefix.iteritems()])
+        map.update([(P+name+'s',scale) for (P,scale) in prefix.iteritems()])
+    return map
+
+def _build_plural_units(**kw):
+    """
+    Construct names for the given units.  Builds singular and plural form.
+    """
+    map = {}
+    map.update([(name,scale) for name,scale in kw.iteritems()])
+    map.update([(name+'s',scale) for name,scale in kw.iteritems()])
+    return map
+
+def _caret_optional(s):
+    """
+    Strip '^' from unit names.
+
+    * WARNING * this will incorrect transform 10^3 to 103.
+    """
+    s.update((k.replace('^',''),v) 
+             for k,v in s.items()
+             if '^' in k)
+
+def _build_all_units():
+    distance = _build_metric_units('meter','m')
+    distance.update(_build_metric_units('metre','m'))
+    distance.update(_build_plural_units(micron=1e-6, Angstrom=1e-10))
+    distance.update({'A':1e-10, 'Ang':1e-10})
+
+    # Note: minutes are used for angle
+    time = _build_metric_units('second','s')
+    time.update(_build_plural_units(hour=3600,day=24*3600,week=7*24*3600))
+
+    # Note: seconds are used for time
+    angle = _build_plural_units(degree=1, minute=1/60.,
+                  arcminute=1/60., arcsecond=1/3600., radian=180/math.pi)
+    angle.update(deg=1, arcmin=1/60., arcsec=1/3600., rad=180/math.pi)
+
+    frequency = _build_metric_units('hertz','Hz')
+    frequency.update(_build_metric_units('Hertz','Hz'))
+    frequency.update(_build_plural_units(rpm=1/60.))
+
+    # Note: degrees are used for angle
+    # Note: temperature needs an offset as well as a scale
+    temperature = _build_metric_units('kelvin','K')
+    temperature.update(_build_metric_units('Kelvin','K'))
+    temperature.update(_build_metric_units('Celcius', 'C'))
+    temperature.update(_build_metric_units('celcius', 'C'))
+    
+    charge = _build_metric_units('coulomb','C')
+    charge.update({'microAmp*hour':0.0036})
+
+    sld = { '10^-6 Angstrom^-2': 1e-6, 'Angstrom^-2': 1 }
+    Q = { 'invA': 1, 'invAng': 1, 'invAngstroms': 1, '1/A': 1, 
+          '10^-3 Angstrom^-1': 1e-3, '1/cm': 1e-8,
+          'nm^-1': 0.1, '1/nm': 0.1, 'n_m^-1': 0.1 }
+
+    _caret_optional(sld)
+    _caret_optional(Q)
+
+    dims = [distance, time, angle, frequency, temperature, charge, sld, Q]
+    return dims
+
+class Converter(object):
+    """
+    Unit converter for NeXus style units.
+    """
+    # Define the units, using both American and European spelling.
+    scalemap = None
+    scalebase = 1
+    dims = _build_all_units()
+
+    # Note: a.u. stands for arbitrary units, which should return the default
+    # units for that particular dimension.
+    # Note: don't have support for dimensionless units.
+    unknown = {None:1, '???':1, '': 1, 'a.u.': 1}
+
+    def __init__(self, name):
+        self.base = name
+        for map in self.dims:
+            if name in map:
+                self.scalemap = map
+                self.scalebase = self.scalemap[name]
+                return
+        if name in self.unknown:
+            return # default scalemap and scalebase correspond to unknown
+        else:
+            raise KeyError("Unknown unit %s"%name)
+
+    def scale(self, units=""):
+        if units == "" or self.scalemap is None: return 1
+        return self.scalebase/self.scalemap[units]
+
+    def __call__(self, value, units=""):
+        # Note: calculating a*1 rather than simply returning a would produce
+        # an unnecessary copy of the array, which in the case of the raw
+        # counts array would be bad.  Sometimes copying and other times
+        # not copying is also bad, but copy on modify semantics isn't
+        # supported.
+        if units == "" or self.scalemap is None: return value
+        try:
+            return value * (self.scalebase/self.scalemap[units])
+        except KeyError:
+            possible_units = ", ".join(str(k) for k in self.scalemap.keys())
+            raise KeyError("%s not in %s"%(units,possible_units))
+
+def _check(expect,get):
+    if expect != get: raise ValueError, "Expected %s but got %s"%(expect,get)
+     #print expect,"==",get
+
+def test():
+    _check(1,Converter('n_m^-1')(10,'invA')) # 10 nm^-1 = 1 inv Angstroms
+    _check(2,Converter('mm')(2000,'m')) # 2000 mm -> 2 m
+    _check(0.003,Converter('microseconds')(3,units='ms')) # 3 us -> 0.003 ms
+    _check(45,Converter('nanokelvin')(45))  # 45 nK -> 45 nK
+    _check(0.5,Converter('seconds')(1800,units='hours')) # 1800 s -> 0.5 hr
+    _check(123,Converter('a.u.')(123,units='mm')) # arbitrary units always returns the same value
+    _check(123,Converter('a.u.')(123,units='s')) # arbitrary units always returns the same value
+    _check(123,Converter('a.u.')(123,units='')) # arbitrary units always returns the same value
+    try: Converter('help')
+    except KeyError: pass
+    else: raise Exception("unknown unit did not raise an error")
+
+    # TODO: more tests
+
+if __name__ == "__main__":
+    test()
Index: src/sas/sascalc/data_util/odict.py
===================================================================
--- src/sas/sascalc/data_util/odict.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/odict.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1399 @@
+# odict.py
+# An Ordered Dictionary object
+# Copyright (C) 2005 Nicola Larosa, Michael Foord
+# E-mail: nico AT tekNico DOT net, fuzzyman AT voidspace DOT org DOT uk
+
+# This software is licensed under the terms of the BSD license.
+# http://www.voidspace.org.uk/python/license.shtml
+# Basically you're free to copy, modify, distribute and relicense it,
+# So long as you keep a copy of the license with it.
+
+# Documentation at http://www.voidspace.org.uk/python/odict.html
+# For information about bugfixes, updates and support, please join the
+# Pythonutils mailing list:
+# http://groups.google.com/group/pythonutils/
+# Comments, suggestions and bug reports welcome.
+
+"""A dict that keeps keys in insertion order"""
+from __future__ import generators
+
+__author__ = ('Nicola Larosa <nico-NoSp@m-tekNico.net>,'
+    'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>')
+
+__docformat__ = "restructuredtext en"
+
+__revision__ = '$Id: odict.py 58 2008-09-02 14:09:54Z farrowch $'
+
+__version__ = '0.2.2'
+
+__all__ = ['OrderedDict', 'SequenceOrderedDict']
+
+import sys
+INTP_VER = sys.version_info[:2]
+if INTP_VER < (2, 2):
+    raise RuntimeError("Python v.2.2 or later required")
+
+import types, warnings
+
+class OrderedDict(dict):
+    """
+    A class of dictionary that keeps the insertion order of keys.
+    
+    All appropriate methods return keys, items, or values in an ordered way.
+    
+    All normal dictionary methods are available. Update and comparison is
+    restricted to other OrderedDict objects.
+    
+    Various sequence methods are available, including the ability to explicitly
+    mutate the key ordering.
+    
+    __contains__ tests:
+    
+    >>> d = OrderedDict(((1, 3),))
+    >>> 1 in d
+    1
+    >>> 4 in d
+    0
+    
+    __getitem__ tests:
+    
+    >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[2]
+    1
+    >>> OrderedDict(((1, 3), (3, 2), (2, 1)))[4]
+    Traceback (most recent call last):
+    KeyError: 4
+    
+    __len__ tests:
+    
+    >>> len(OrderedDict())
+    0
+    >>> len(OrderedDict(((1, 3), (3, 2), (2, 1))))
+    3
+    
+    get tests:
+    
+    >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+    >>> d.get(1)
+    3
+    >>> d.get(4) is None
+    1
+    >>> d.get(4, 5)
+    5
+    >>> d
+    OrderedDict([(1, 3), (3, 2), (2, 1)])
+    
+    has_key tests:
+    
+    >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+    >>> d.has_key(1)
+    1
+    >>> d.has_key(4)
+    0
+    """
+
+    def __init__(self, init_val=(), strict=False):
+        """
+        Create a new ordered dictionary. Cannot init from a normal dict,
+        nor from kwargs, since items order is undefined in those cases.
+        
+        If the ``strict`` keyword argument is ``True`` (``False`` is the
+        default) then when doing slice assignment - the ``OrderedDict`` you are
+        assigning from *must not* contain any keys in the remaining dict.
+        
+        >>> OrderedDict()
+        OrderedDict([])
+        >>> OrderedDict({1: 1})
+        Traceback (most recent call last):
+        TypeError: undefined order, cannot get items from dict
+        >>> OrderedDict({1: 1}.items())
+        OrderedDict([(1, 1)])
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d
+        OrderedDict([(1, 3), (3, 2), (2, 1)])
+        >>> OrderedDict(d)
+        OrderedDict([(1, 3), (3, 2), (2, 1)])
+        """
+        self.strict = strict
+        dict.__init__(self)
+        if isinstance(init_val, OrderedDict):
+            self._sequence = init_val.keys()
+            dict.update(self, init_val)
+        elif isinstance(init_val, dict):
+            # we lose compatibility with other ordered dict types this way
+            raise TypeError('undefined order, cannot get items from dict')
+        else:
+            self._sequence = []
+            self.update(init_val)
+
+### Special methods ###
+
+    def __delitem__(self, key):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> del d[3]
+        >>> d
+        OrderedDict([(1, 3), (2, 1)])
+        >>> del d[3]
+        Traceback (most recent call last):
+        KeyError: 3
+        >>> d[3] = 2
+        >>> d
+        OrderedDict([(1, 3), (2, 1), (3, 2)])
+        >>> del d[0:1]
+        >>> d
+        OrderedDict([(2, 1), (3, 2)])
+        """
+        if isinstance(key, types.SliceType):
+            # FIXME: efficiency?
+            keys = self._sequence[key]
+            for entry in keys:
+                dict.__delitem__(self, entry)
+            del self._sequence[key]
+        else:
+            # do the dict.__delitem__ *first* as it raises
+            # the more appropriate error
+            dict.__delitem__(self, key)
+            self._sequence.remove(key)
+
+    def __eq__(self, other):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d == OrderedDict(d)
+        True
+        >>> d == OrderedDict(((1, 3), (2, 1), (3, 2)))
+        False
+        >>> d == OrderedDict(((1, 0), (3, 2), (2, 1)))
+        False
+        >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
+        False
+        >>> d == dict(d)
+        False
+        >>> d == False
+        False
+        """
+        if isinstance(other, OrderedDict):
+            # FIXME: efficiency?
+            #   Generate both item lists for each compare
+            return (self.items() == other.items())
+        else:
+            return False
+
+    def __lt__(self, other):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
+        >>> c < d
+        True
+        >>> d < c
+        False
+        >>> d < dict(c)
+        Traceback (most recent call last):
+        TypeError: Can only compare with other OrderedDicts
+        """
+        if not isinstance(other, OrderedDict):
+            raise TypeError('Can only compare with other OrderedDicts')
+        # FIXME: efficiency?
+        #   Generate both item lists for each compare
+        return (self.items() < other.items())
+
+    def __le__(self, other):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
+        >>> e = OrderedDict(d)
+        >>> c <= d
+        True
+        >>> d <= c
+        False
+        >>> d <= dict(c)
+        Traceback (most recent call last):
+        TypeError: Can only compare with other OrderedDicts
+        >>> d <= e
+        True
+        """
+        if not isinstance(other, OrderedDict):
+            raise TypeError('Can only compare with other OrderedDicts')
+        # FIXME: efficiency?
+        #   Generate both item lists for each compare
+        return (self.items() <= other.items())
+
+    def __ne__(self, other):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d != OrderedDict(d)
+        False
+        >>> d != OrderedDict(((1, 3), (2, 1), (3, 2)))
+        True
+        >>> d != OrderedDict(((1, 0), (3, 2), (2, 1)))
+        True
+        >>> d == OrderedDict(((0, 3), (3, 2), (2, 1)))
+        False
+        >>> d != dict(d)
+        True
+        >>> d != False
+        True
+        """
+        if isinstance(other, OrderedDict):
+            # FIXME: efficiency?
+            #   Generate both item lists for each compare
+            return not (self.items() == other.items())
+        else:
+            return True
+
+    def __gt__(self, other):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
+        >>> d > c
+        True
+        >>> c > d
+        False
+        >>> d > dict(c)
+        Traceback (most recent call last):
+        TypeError: Can only compare with other OrderedDicts
+        """
+        if not isinstance(other, OrderedDict):
+            raise TypeError('Can only compare with other OrderedDicts')
+        # FIXME: efficiency?
+        #   Generate both item lists for each compare
+        return (self.items() > other.items())
+
+    def __ge__(self, other):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> c = OrderedDict(((0, 3), (3, 2), (2, 1)))
+        >>> e = OrderedDict(d)
+        >>> c >= d
+        False
+        >>> d >= c
+        True
+        >>> d >= dict(c)
+        Traceback (most recent call last):
+        TypeError: Can only compare with other OrderedDicts
+        >>> e >= d
+        True
+        """
+        if not isinstance(other, OrderedDict):
+            raise TypeError('Can only compare with other OrderedDicts')
+        # FIXME: efficiency?
+        #   Generate both item lists for each compare
+        return (self.items() >= other.items())
+
+    def __repr__(self):
+        """
+        Used for __repr__ and __str__
+        
+        >>> r1 = repr(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
+        >>> r1
+        "OrderedDict([('a', 'b'), ('c', 'd'), ('e', 'f')])"
+        >>> r2 = repr(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
+        >>> r2
+        "OrderedDict([('a', 'b'), ('e', 'f'), ('c', 'd')])"
+        >>> r1 == str(OrderedDict((('a', 'b'), ('c', 'd'), ('e', 'f'))))
+        True
+        >>> r2 == str(OrderedDict((('a', 'b'), ('e', 'f'), ('c', 'd'))))
+        True
+        """
+        return '%s([%s])' % (self.__class__.__name__, ', '.join(
+            ['(%r, %r)' % (key, self[key]) for key in self._sequence]))
+
+    def __setitem__(self, key, val):
+        """
+        Allows slice assignment, so long as the slice is an OrderedDict
+        >>> d = OrderedDict()
+        >>> d['a'] = 'b'
+        >>> d['b'] = 'a'
+        >>> d[3] = 12
+        >>> d
+        OrderedDict([('a', 'b'), ('b', 'a'), (3, 12)])
+        >>> d[:] = OrderedDict(((1, 2), (2, 3), (3, 4)))
+        >>> d
+        OrderedDict([(1, 2), (2, 3), (3, 4)])
+        >>> d[::2] = OrderedDict(((7, 8), (9, 10)))
+        >>> d
+        OrderedDict([(7, 8), (2, 3), (9, 10)])
+        >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)))
+        >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
+        >>> d
+        OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
+        >>> d = OrderedDict(((0, 1), (1, 2), (2, 3), (3, 4)), strict=True)
+        >>> d[1:3] = OrderedDict(((1, 2), (5, 6), (7, 8)))
+        >>> d
+        OrderedDict([(0, 1), (1, 2), (5, 6), (7, 8), (3, 4)])
+        
+        >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)), strict=True)
+        >>> a[3] = 4
+        >>> a
+        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a
+        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)])
+        Traceback (most recent call last):
+        ValueError: slice assignment must be from unique keys
+        >>> a = OrderedDict(((0, 1), (1, 2), (2, 3)))
+        >>> a[3] = 4
+        >>> a
+        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a[::1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a
+        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a[:2] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a
+        OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a[::-1] = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> a
+        OrderedDict([(3, 4), (2, 3), (1, 2), (0, 1)])
+        
+        >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> d[:1] = 3
+        Traceback (most recent call last):
+        TypeError: slice assignment requires an OrderedDict
+        
+        >>> d = OrderedDict([(0, 1), (1, 2), (2, 3), (3, 4)])
+        >>> d[:1] = OrderedDict([(9, 8)])
+        >>> d
+        OrderedDict([(9, 8), (1, 2), (2, 3), (3, 4)])
+        """
+        if isinstance(key, types.SliceType):
+            if not isinstance(val, OrderedDict):
+                # FIXME: allow a list of tuples?
+                raise TypeError('slice assignment requires an OrderedDict')
+            keys = self._sequence[key]
+            # NOTE: Could use ``range(*key.indices(len(self._sequence)))``
+            indexes = range(len(self._sequence))[key]
+            if key.step is None:
+                # NOTE: new slice may not be the same size as the one being
+                #   overwritten !
+                # NOTE: What is the algorithm for an impossible slice?
+                #   e.g. d[5:3]
+                pos = key.start or 0
+                del self[key]
+                newkeys = val.keys()
+                for k in newkeys:
+                    if k in self:
+                        if self.strict:
+                            raise ValueError('slice assignment must be from '
+                                'unique keys')
+                        else:
+                            # NOTE: This removes duplicate keys *first*
+                            #   so start position might have changed?
+                            del self[k]
+                self._sequence = (self._sequence[:pos] + newkeys +
+                    self._sequence[pos:])
+                dict.update(self, val)
+            else:
+                # extended slice - length of new slice must be the same
+                # as the one being replaced
+                if len(keys) != len(val):
+                    raise ValueError('attempt to assign sequence of size %s '
+                        'to extended slice of size %s' % (len(val), len(keys)))
+                # FIXME: efficiency?
+                del self[key]
+                item_list = zip(indexes, val.items())
+                # smallest indexes first - higher indexes not guaranteed to
+                # exist
+                item_list.sort()
+                for pos, (newkey, newval) in item_list:
+                    if self.strict and newkey in self:
+                        raise ValueError('slice assignment must be from unique'
+                            ' keys')
+                    self.insert(pos, newkey, newval)
+        else:
+            if key not in self:
+                self._sequence.append(key)
+            dict.__setitem__(self, key, val)
+
+    def __getitem__(self, key):
+        """
+        Allows slicing. Returns an OrderedDict if you slice.
+        >>> b = OrderedDict([(7, 0), (6, 1), (5, 2), (4, 3), (3, 4), (2, 5), (1, 6)])
+        >>> b[::-1]
+        OrderedDict([(1, 6), (2, 5), (3, 4), (4, 3), (5, 2), (6, 1), (7, 0)])
+        >>> b[2:5]
+        OrderedDict([(5, 2), (4, 3), (3, 4)])
+        >>> type(b[2:4])
+        <class '__main__.OrderedDict'>
+        """
+        if isinstance(key, types.SliceType):
+            # FIXME: does this raise the error we want?
+            keys = self._sequence[key]
+            # FIXME: efficiency?
+            return OrderedDict([(entry, self[entry]) for entry in keys])
+        else:
+            return dict.__getitem__(self, key)
+
+    __str__ = __repr__
+
+    def __setattr__(self, name, value):
+        """
+        Implemented so that accesses to ``sequence`` raise a warning and are
+        diverted to the new ``setkeys`` method.
+        """
+        if name == 'sequence':
+            warnings.warn('Use of the sequence attribute is deprecated.'
+                ' Use the keys method instead.', DeprecationWarning)
+            # NOTE: doesn't return anything
+            self.setkeys(value)
+        else:
+            # FIXME: do we want to allow arbitrary setting of attributes?
+            #   Or do we want to manage it?
+            object.__setattr__(self, name, value)
+
+    def __getattr__(self, name):
+        """
+        Implemented so that access to ``sequence`` raises a warning.
+        
+        >>> d = OrderedDict()
+        >>> d.sequence
+        []
+        """
+        if name == 'sequence':
+            warnings.warn('Use of the sequence attribute is deprecated.'
+                ' Use the keys method instead.', DeprecationWarning)
+            # NOTE: Still (currently) returns a direct reference. Need to
+            #   because code that uses sequence will expect to be able to
+            #   mutate it in place.
+            return self._sequence
+        else:
+            # raise the appropriate error
+            raise AttributeError("OrderedDict has no '%s' attribute" % name)
+
+    def __deepcopy__(self, memo):
+        """
+        To allow deepcopy to work with OrderedDict.
+        
+        >>> from copy import deepcopy
+        >>> a = OrderedDict([(1, 1), (2, 2), (3, 3)])
+        >>> a['test'] = {}
+        >>> b = deepcopy(a)
+        >>> b == a
+        True
+        >>> b is a
+        False
+        >>> a['test'] is b['test']
+        False
+        """
+        from copy import deepcopy
+        return self.__class__(deepcopy(self.items(), memo), self.strict)
+
+
+### Read-only methods ###
+
+    def copy(self):
+        """
+        >>> OrderedDict(((1, 3), (3, 2), (2, 1))).copy()
+        OrderedDict([(1, 3), (3, 2), (2, 1)])
+        """
+        return OrderedDict(self)
+
+    def items(self):
+        """
+        ``items`` returns a list of tuples representing all the 
+        ``(key, value)`` pairs in the dictionary.
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.items()
+        [(1, 3), (3, 2), (2, 1)]
+        >>> d.clear()
+        >>> d.items()
+        []
+        """
+        return zip(self._sequence, self.values())
+
+    def keys(self):
+        """
+        Return a list of keys in the ``OrderedDict``.
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.keys()
+        [1, 3, 2]
+        """
+        return self._sequence[:]
+
+    def values(self, values=None):
+        """
+        Return a list of all the values in the OrderedDict.
+        
+        Optionally you can pass in a list of values, which will replace the
+        current list. The value list must be the same len as the OrderedDict.
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.values()
+        [3, 2, 1]
+        """
+        return [self[key] for key in self._sequence]
+
+    def iteritems(self):
+        """
+        >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iteritems()
+        >>> ii.next()
+        (1, 3)
+        >>> ii.next()
+        (3, 2)
+        >>> ii.next()
+        (2, 1)
+        >>> ii.next()
+        Traceback (most recent call last):
+        StopIteration
+        """
+        def make_iter(self=self):
+            keys = self.iterkeys()
+            while True:
+                key = keys.next()
+                yield (key, self[key])
+        return make_iter()
+
+    def iterkeys(self):
+        """
+        >>> ii = OrderedDict(((1, 3), (3, 2), (2, 1))).iterkeys()
+        >>> ii.next()
+        1
+        >>> ii.next()
+        3
+        >>> ii.next()
+        2
+        >>> ii.next()
+        Traceback (most recent call last):
+        StopIteration
+        """
+        return iter(self._sequence)
+
+    __iter__ = iterkeys
+
+    def itervalues(self):
+        """
+        >>> iv = OrderedDict(((1, 3), (3, 2), (2, 1))).itervalues()
+        >>> iv.next()
+        3
+        >>> iv.next()
+        2
+        >>> iv.next()
+        1
+        >>> iv.next()
+        Traceback (most recent call last):
+        StopIteration
+        """
+        def make_iter(self=self):
+            keys = self.iterkeys()
+            while True:
+                yield self[keys.next()]
+        return make_iter()
+
+### Read-write methods ###
+
+    def clear(self):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.clear()
+        >>> d
+        OrderedDict([])
+        """
+        dict.clear(self)
+        self._sequence = []
+
+    def pop(self, key, *args):
+        """
+        No dict.pop in Python 2.2, gotta reimplement it
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.pop(3)
+        2
+        >>> d
+        OrderedDict([(1, 3), (2, 1)])
+        >>> d.pop(4)
+        Traceback (most recent call last):
+        KeyError: 4
+        >>> d.pop(4, 0)
+        0
+        >>> d.pop(4, 0, 1)
+        Traceback (most recent call last):
+        TypeError: pop expected at most 2 arguments, got 3
+        """
+        if len(args) > 1:
+            raise TypeError, ('pop expected at most 2 arguments, got %s' %
+                (len(args) + 1))
+        if key in self:
+            val = self[key]
+            del self[key]
+        else:
+            try:
+                val = args[0]
+            except IndexError:
+                raise KeyError(key)
+        return val
+
+    def popitem(self, i=-1):
+        """
+        Delete and return an item specified by index, not a random one as in
+        dict. The index is -1 by default (the last item).
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.popitem()
+        (2, 1)
+        >>> d
+        OrderedDict([(1, 3), (3, 2)])
+        >>> d.popitem(0)
+        (1, 3)
+        >>> OrderedDict().popitem()
+        Traceback (most recent call last):
+        KeyError: 'popitem(): dictionary is empty'
+        >>> d.popitem(2)
+        Traceback (most recent call last):
+        IndexError: popitem(): index 2 not valid
+        """
+        if not self._sequence:
+            raise KeyError('popitem(): dictionary is empty')
+        try:
+            key = self._sequence[i]
+        except IndexError:
+            raise IndexError('popitem(): index %s not valid' % i)
+        return (key, self.pop(key))
+
+    def setdefault(self, key, defval = None):
+        """
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.setdefault(1)
+        3
+        >>> d.setdefault(4) is None
+        True
+        >>> d
+        OrderedDict([(1, 3), (3, 2), (2, 1), (4, None)])
+        >>> d.setdefault(5, 0)
+        0
+        >>> d
+        OrderedDict([(1, 3), (3, 2), (2, 1), (4, None), (5, 0)])
+        """
+        if key in self:
+            return self[key]
+        else:
+            self[key] = defval
+            return defval
+
+    def update(self, from_od):
+        """
+        Update from another OrderedDict or sequence of (key, value) pairs
+        
+        >>> d = OrderedDict(((1, 0), (0, 1)))
+        >>> d.update(OrderedDict(((1, 3), (3, 2), (2, 1))))
+        >>> d
+        OrderedDict([(1, 3), (0, 1), (3, 2), (2, 1)])
+        >>> d.update({4: 4})
+        Traceback (most recent call last):
+        TypeError: undefined order, cannot get items from dict
+        >>> d.update((4, 4))
+        Traceback (most recent call last):
+        TypeError: cannot convert dictionary update sequence element "4" to a 2-item sequence
+        """
+        if isinstance(from_od, OrderedDict):
+            for key, val in from_od.items():
+                self[key] = val
+        elif isinstance(from_od, dict):
+            # we lose compatibility with other ordered dict types this way
+            raise TypeError('undefined order, cannot get items from dict')
+        else:
+            # FIXME: efficiency?
+            # sequence of 2-item sequences, or error
+            for item in from_od:
+                try:
+                    key, val = item
+                except TypeError:
+                    raise TypeError('cannot convert dictionary update'
+                        ' sequence element "%s" to a 2-item sequence' % item)
+                self[key] = val
+
+    def rename(self, old_key, new_key):
+        """
+        Rename the key for a given value, without modifying sequence order.
+        
+        For the case where new_key already exists this raise an exception,
+        since if new_key exists, it is ambiguous as to what happens to the
+        associated values, and the position of new_key in the sequence.
+        
+        >>> od = OrderedDict()
+        >>> od['a'] = 1
+        >>> od['b'] = 2
+        >>> od.items()
+        [('a', 1), ('b', 2)]
+        >>> od.rename('b', 'c')
+        >>> od.items()
+        [('a', 1), ('c', 2)]
+        >>> od.rename('c', 'a')
+        Traceback (most recent call last):
+        ValueError: New key already exists: 'a'
+        >>> od.rename('d', 'b')
+        Traceback (most recent call last):
+        KeyError: 'd'
+        """
+        if new_key == old_key:
+            # no-op
+            return
+        if new_key in self:
+            raise ValueError("New key already exists: %r" % new_key)
+        # rename sequence entry
+        value = self[old_key] 
+        old_idx = self._sequence.index(old_key)
+        self._sequence[old_idx] = new_key
+        # rename internal dict entry
+        dict.__delitem__(self, old_key)
+        dict.__setitem__(self, new_key, value)
+
+    def setitems(self, items):
+        """
+        This method allows you to set the items in the dict.
+        
+        It takes a list of tuples - of the same sort returned by the ``items``
+        method.
+        
+        >>> d = OrderedDict()
+        >>> d.setitems(((3, 1), (2, 3), (1, 2)))
+        >>> d
+        OrderedDict([(3, 1), (2, 3), (1, 2)])
+        """
+        self.clear()
+        # FIXME: this allows you to pass in an OrderedDict as well :-)
+        self.update(items)
+
+    def setkeys(self, keys):
+        """
+        ``setkeys`` all ows you to pass in a new list of keys which will
+        replace the current set. This must contain the same set of keys, but
+        need not be in the same order.
+        
+        If you pass in new keys that don't match, a ``KeyError`` will be
+        raised.
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.keys()
+        [1, 3, 2]
+        >>> d.setkeys((1, 2, 3))
+        >>> d
+        OrderedDict([(1, 3), (2, 1), (3, 2)])
+        >>> d.setkeys(['a', 'b', 'c'])
+        Traceback (most recent call last):
+        KeyError: 'Keylist is not the same as current keylist.'
+        """
+        # FIXME: Efficiency? (use set for Python 2.4 :-)
+        # NOTE: list(keys) rather than keys[:] because keys[:] returns
+        #   a tuple, if keys is a tuple.
+        kcopy = list(keys)
+        kcopy.sort()
+        self._sequence.sort()
+        if kcopy != self._sequence:
+            raise KeyError('Keylist is not the same as current keylist.')
+        # NOTE: This makes the _sequence attribute a new object, instead
+        #       of changing it in place.
+        # FIXME: efficiency?
+        self._sequence = list(keys)
+
+    def setvalues(self, values):
+        """
+        You can pass in a list of values, which will replace the
+        current list. The value list must be the same len as the OrderedDict.
+        
+        (Or a ``ValueError`` is raised.)
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.setvalues((1, 2, 3))
+        >>> d
+        OrderedDict([(1, 1), (3, 2), (2, 3)])
+        >>> d.setvalues([6])
+        Traceback (most recent call last):
+        ValueError: Value list is not the same length as the OrderedDict.
+        """
+        if len(values) != len(self):
+            # FIXME: correct error to raise?
+            raise ValueError('Value list is not the same length as the '
+                'OrderedDict.')
+        self.update(zip(self, values))
+
+### Sequence Methods ###
+
+    def index(self, key):
+        """
+        Return the position of the specified key in the OrderedDict.
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.index(3)
+        1
+        >>> d.index(4)
+        Traceback (most recent call last):
+        ValueError: list.index(x): x not in list
+        """
+        return self._sequence.index(key)
+
+    def insert(self, index, key, value):
+        """
+        Takes ``index``, ``key``, and ``value`` as arguments.
+        
+        Sets ``key`` to ``value``, so that ``key`` is at position ``index`` in
+        the OrderedDict.
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.insert(0, 4, 0)
+        >>> d
+        OrderedDict([(4, 0), (1, 3), (3, 2), (2, 1)])
+        >>> d.insert(0, 2, 1)
+        >>> d
+        OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2)])
+        >>> d.insert(8, 8, 1)
+        >>> d
+        OrderedDict([(2, 1), (4, 0), (1, 3), (3, 2), (8, 1)])
+        """
+        if key in self:
+            # FIXME: efficiency?
+            del self[key]
+        self._sequence.insert(index, key)
+        dict.__setitem__(self, key, value)
+
+    def reverse(self):
+        """
+        Reverse the order of the OrderedDict.
+        
+        >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
+        >>> d.reverse()
+        >>> d
+        OrderedDict([(2, 1), (3, 2), (1, 3)])
+        """
+        self._sequence.reverse()
+
+    def sort(self, *args, **kwargs):
+        """
+        Sort the key order in the OrderedDict.
+        
+        This method takes the same arguments as the ``list.sort`` method on
+        your version of Python.
+        
+        >>> d = OrderedDict(((4, 1), (2, 2), (3, 3), (1, 4)))
+        >>> d.sort()
+        >>> d
+        OrderedDict([(1, 4), (2, 2), (3, 3), (4, 1)])
+        """
+        self._sequence.sort(*args, **kwargs)
+
+class Keys(object):
+    # FIXME: should this object be a subclass of list?
+    """
+    Custom object for accessing the keys of an OrderedDict.
+    
+    Can be called like the normal ``OrderedDict.keys`` method, but also
+    supports indexing and sequence methods.
+    """
+
+    def __init__(self, main):
+        self._main = main
+
+    def __call__(self):
+        """Pretend to be the keys method."""
+        return self._main._keys()
+
+    def __getitem__(self, index):
+        """Fetch the key at position i."""
+        # NOTE: this automatically supports slicing :-)
+        return self._main._sequence[index]
+
+    def __setitem__(self, index, name):
+        """
+        You cannot assign to keys, but you can do slice assignment to re-order
+        them.
+        
+        You can only do slice assignment if the new set of keys is a reordering
+        of the original set.
+        """
+        if isinstance(index, types.SliceType):
+            # FIXME: efficiency?
+            # check length is the same
+            indexes = range(len(self._main._sequence))[index]
+            if len(indexes) != len(name):
+                raise ValueError('attempt to assign sequence of size %s '
+                    'to slice of size %s' % (len(name), len(indexes)))
+            # check they are the same keys
+            # FIXME: Use set
+            old_keys = self._main._sequence[index]
+            new_keys = list(name)
+            old_keys.sort()
+            new_keys.sort()
+            if old_keys != new_keys:
+                raise KeyError('Keylist is not the same as current keylist.')
+            orig_vals = [self._main[k] for k in name]
+            del self._main[index]
+            vals = zip(indexes, name, orig_vals)
+            vals.sort()
+            for i, k, v in vals:
+                if self._main.strict and k in self._main:
+                    raise ValueError('slice assignment must be from '
+                        'unique keys')
+                self._main.insert(i, k, v)
+        else:
+            raise ValueError('Cannot assign to keys')
+
+    ### following methods pinched from UserList and adapted ###
+    def __repr__(self): return repr(self._main._sequence)
+
+    # FIXME: do we need to check if we are comparing with another ``Keys``
+    #   object? (like the __cast method of UserList)
+    def __lt__(self, other): return self._main._sequence <  other
+    def __le__(self, other): return self._main._sequence <= other
+    def __eq__(self, other): return self._main._sequence == other
+    def __ne__(self, other): return self._main._sequence != other
+    def __gt__(self, other): return self._main._sequence >  other
+    def __ge__(self, other): return self._main._sequence >= other
+    # FIXME: do we need __cmp__ as well as rich comparisons?
+    def __cmp__(self, other): return cmp(self._main._sequence, other)
+
+    def __contains__(self, item): return item in self._main._sequence
+    def __len__(self): return len(self._main._sequence)
+    def __iter__(self): return self._main.iterkeys()
+    def count(self, item): return self._main._sequence.count(item)
+    def index(self, item, *args): return self._main._sequence.index(item, *args)
+    def reverse(self): self._main._sequence.reverse()
+    def sort(self, *args, **kwds): self._main._sequence.sort(*args, **kwds)
+    def __mul__(self, n): return self._main._sequence*n
+    __rmul__ = __mul__
+    def __add__(self, other): return self._main._sequence + other
+    def __radd__(self, other): return other + self._main._sequence
+
+    ## following methods not implemented for keys ##
+    def __delitem__(self, i): raise TypeError('Can\'t delete items from keys')
+    def __iadd__(self, other): raise TypeError('Can\'t add in place to keys')
+    def __imul__(self, n): raise TypeError('Can\'t multiply keys in place')
+    def append(self, item): raise TypeError('Can\'t append items to keys')
+    def insert(self, i, item): raise TypeError('Can\'t insert items into keys')
+    def pop(self, i=-1): raise TypeError('Can\'t pop items from keys')
+    def remove(self, item): raise TypeError('Can\'t remove items from keys')
+    def extend(self, other): raise TypeError('Can\'t extend keys')
+
+class Items(object):
+    """
+    Custom object for accessing the items of an OrderedDict.
+    
+    Can be called like the normal ``OrderedDict.items`` method, but also
+    supports indexing and sequence methods.
+    """
+
+    def __init__(self, main):
+        self._main = main
+
+    def __call__(self):
+        """Pretend to be the items method."""
+        return self._main._items()
+
+    def __getitem__(self, index):
+        """Fetch the item at position i."""
+        if isinstance(index, types.SliceType):
+            # fetching a slice returns an OrderedDict
+            return self._main[index].items()
+        key = self._main._sequence[index]
+        return (key, self._main[key])
+
+    def __setitem__(self, index, item):
+        """Set item at position i to item."""
+        if isinstance(index, types.SliceType):
+            # NOTE: item must be an iterable (list of tuples)
+            self._main[index] = OrderedDict(item)
+        else:
+            # FIXME: Does this raise a sensible error?
+            orig = self._main.keys[index]
+            key, value = item
+            if self._main.strict and key in self and (key != orig):
+                raise ValueError('slice assignment must be from '
+                        'unique keys')
+            # delete the current one
+            del self._main[self._main._sequence[index]]
+            self._main.insert(index, key, value)
+
+    def __delitem__(self, i):
+        """Delete the item at position i."""
+        key = self._main._sequence[i]
+        if isinstance(i, types.SliceType):
+            for k in key:
+                # FIXME: efficiency?
+                del self._main[k]
+        else:
+            del self._main[key]
+
+    ### following methods pinched from UserList and adapted ###
+    def __repr__(self): return repr(self._main.items())
+
+    # FIXME: do we need to check if we are comparing with another ``Items``
+    #   object? (like the __cast method of UserList)
+    def __lt__(self, other): return self._main.items() <  other
+    def __le__(self, other): return self._main.items() <= other
+    def __eq__(self, other): return self._main.items() == other
+    def __ne__(self, other): return self._main.items() != other
+    def __gt__(self, other): return self._main.items() >  other
+    def __ge__(self, other): return self._main.items() >= other
+    def __cmp__(self, other): return cmp(self._main.items(), other)
+
+    def __contains__(self, item): return item in self._main.items()
+    def __len__(self): return len(self._main._sequence) # easier :-)
+    def __iter__(self): return self._main.iteritems()
+    def count(self, item): return self._main.items().count(item)
+    def index(self, item, *args): return self._main.items().index(item, *args)
+    def reverse(self): self._main.reverse()
+    def sort(self, *args, **kwds): self._main.sort(*args, **kwds)
+    def __mul__(self, n): return self._main.items()*n
+    __rmul__ = __mul__
+    def __add__(self, other): return self._main.items() + other
+    def __radd__(self, other): return other + self._main.items()
+
+    def append(self, item):
+        """Add an item to the end."""
+        # FIXME: this is only append if the key isn't already present
+        key, value = item
+        self._main[key] = value
+
+    def insert(self, i, item):
+        key, value = item
+        self._main.insert(i, key, value)
+
+    def pop(self, i=-1):
+        key = self._main._sequence[i]
+        return (key, self._main.pop(key))
+
+    def remove(self, item):
+        key, value = item
+        try:
+            assert value == self._main[key]
+        except (KeyError, AssertionError):
+            raise ValueError('ValueError: list.remove(x): x not in list')
+        else:
+            del self._main[key]
+
+    def extend(self, other):
+        # FIXME: is only a true extend if none of the keys already present
+        for item in other:
+            key, value = item
+            self._main[key] = value
+
+    def __iadd__(self, other):
+        self.extend(other)
+
+    ## following methods not implemented for items ##
+
+    def __imul__(self, n): raise TypeError('Can\'t multiply items in place')
+
+class Values(object):
+    """
+    Custom object for accessing the values of an OrderedDict.
+    
+    Can be called like the normal ``OrderedDict.values`` method, but also
+    supports indexing and sequence methods.
+    """
+
+    def __init__(self, main):
+        self._main = main
+
+    def __call__(self):
+        """Pretend to be the values method."""
+        return self._main._values()
+
+    def __getitem__(self, index):
+        """Fetch the value at position i."""
+        if isinstance(index, types.SliceType):
+            return [self._main[key] for key in self._main._sequence[index]]
+        else:
+            return self._main[self._main._sequence[index]]
+
+    def __setitem__(self, index, value):
+        """
+        Set the value at position i to value.
+        
+        You can only do slice assignment to values if you supply a sequence of
+        equal length to the slice you are replacing.
+        """
+        if isinstance(index, types.SliceType):
+            keys = self._main._sequence[index]
+            if len(keys) != len(value):
+                raise ValueError('attempt to assign sequence of size %s '
+                    'to slice of size %s' % (len(name), len(keys)))
+            # FIXME: efficiency?  Would be better to calculate the indexes
+            #   directly from the slice object
+            # NOTE: the new keys can collide with existing keys (or even
+            #   contain duplicates) - these will overwrite
+            for key, val in zip(keys, value):
+                self._main[key] = val
+        else:
+            self._main[self._main._sequence[index]] = value
+
+    ### following methods pinched from UserList and adapted ###
+    def __repr__(self): return repr(self._main.values())
+
+    # FIXME: do we need to check if we are comparing with another ``Values``
+    #   object? (like the __cast method of UserList)
+    def __lt__(self, other): return self._main.values() <  other
+    def __le__(self, other): return self._main.values() <= other
+    def __eq__(self, other): return self._main.values() == other
+    def __ne__(self, other): return self._main.values() != other
+    def __gt__(self, other): return self._main.values() >  other
+    def __ge__(self, other): return self._main.values() >= other
+    def __cmp__(self, other): return cmp(self._main.values(), other)
+
+    def __contains__(self, item): return item in self._main.values()
+    def __len__(self): return len(self._main._sequence) # easier :-)
+    def __iter__(self): return self._main.itervalues()
+    def count(self, item): return self._main.values().count(item)
+    def index(self, item, *args): return self._main.values().index(item, *args)
+
+    def reverse(self):
+        """Reverse the values"""
+        vals = self._main.values()
+        vals.reverse()
+        # FIXME: efficiency
+        self[:] = vals
+
+    def sort(self, *args, **kwds):
+        """Sort the values."""
+        vals = self._main.values()
+        vals.sort(*args, **kwds)
+        self[:] = vals
+
+    def __mul__(self, n): return self._main.values()*n
+    __rmul__ = __mul__
+    def __add__(self, other): return self._main.values() + other
+    def __radd__(self, other): return other + self._main.values()
+
+    ## following methods not implemented for values ##
+    def __delitem__(self, i): raise TypeError('Can\'t delete items from values')
+    def __iadd__(self, other): raise TypeError('Can\'t add in place to values')
+    def __imul__(self, n): raise TypeError('Can\'t multiply values in place')
+    def append(self, item): raise TypeError('Can\'t append items to values')
+    def insert(self, i, item): raise TypeError('Can\'t insert items into values')
+    def pop(self, i=-1): raise TypeError('Can\'t pop items from values')
+    def remove(self, item): raise TypeError('Can\'t remove items from values')
+    def extend(self, other): raise TypeError('Can\'t extend values')
+
+class SequenceOrderedDict(OrderedDict):
+    """
+    Experimental version of OrderedDict that has a custom object for ``keys``,
+    ``values``, and ``items``.
+    
+    These are callable sequence objects that work as methods, or can be
+    manipulated directly as sequences.
+    
+    Test for ``keys``, ``items`` and ``values``.
+    
+    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
+    >>> d
+    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+    >>> d.keys
+    [1, 2, 3]
+    >>> d.keys()
+    [1, 2, 3]
+    >>> d.setkeys((3, 2, 1))
+    >>> d
+    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
+    >>> d.setkeys((1, 2, 3))
+    >>> d.keys[0]
+    1
+    >>> d.keys[:]
+    [1, 2, 3]
+    >>> d.keys[-1]
+    3
+    >>> d.keys[-2]
+    2
+    >>> d.keys[0:2] = [2, 1]
+    >>> d
+    SequenceOrderedDict([(2, 3), (1, 2), (3, 4)])
+    >>> d.keys.reverse()
+    >>> d.keys
+    [3, 1, 2]
+    >>> d.keys = [1, 2, 3]
+    >>> d
+    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+    >>> d.keys = [3, 1, 2]
+    >>> d
+    SequenceOrderedDict([(3, 4), (1, 2), (2, 3)])
+    >>> a = SequenceOrderedDict()
+    >>> b = SequenceOrderedDict()
+    >>> a.keys == b.keys
+    1
+    >>> a['a'] = 3
+    >>> a.keys == b.keys
+    0
+    >>> b['a'] = 3
+    >>> a.keys == b.keys
+    1
+    >>> b['b'] = 3
+    >>> a.keys == b.keys
+    0
+    >>> a.keys > b.keys
+    0
+    >>> a.keys < b.keys
+    1
+    >>> 'a' in a.keys
+    1
+    >>> len(b.keys)
+    2
+    >>> 'c' in d.keys
+    0
+    >>> 1 in d.keys
+    1
+    >>> [v for v in d.keys]
+    [3, 1, 2]
+    >>> d.keys.sort()
+    >>> d.keys
+    [1, 2, 3]
+    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)), strict=True)
+    >>> d.keys[::-1] = [1, 2, 3]
+    >>> d
+    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
+    >>> d.keys[:2]
+    [3, 2]
+    >>> d.keys[:2] = [1, 3]
+    Traceback (most recent call last):
+    KeyError: 'Keylist is not the same as current keylist.'
+
+    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
+    >>> d
+    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+    >>> d.values
+    [2, 3, 4]
+    >>> d.values()
+    [2, 3, 4]
+    >>> d.setvalues((4, 3, 2))
+    >>> d
+    SequenceOrderedDict([(1, 4), (2, 3), (3, 2)])
+    >>> d.values[::-1]
+    [2, 3, 4]
+    >>> d.values[0]
+    4
+    >>> d.values[-2]
+    3
+    >>> del d.values[0]
+    Traceback (most recent call last):
+    TypeError: Can't delete items from values
+    >>> d.values[::2] = [2, 4]
+    >>> d
+    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+    >>> 7 in d.values
+    0
+    >>> len(d.values)
+    3
+    >>> [val for val in d.values]
+    [2, 3, 4]
+    >>> d.values[-1] = 2
+    >>> d.values.count(2)
+    2
+    >>> d.values.index(2)
+    0
+    >>> d.values[-1] = 7
+    >>> d.values
+    [2, 3, 7]
+    >>> d.values.reverse()
+    >>> d.values
+    [7, 3, 2]
+    >>> d.values.sort()
+    >>> d.values
+    [2, 3, 7]
+    >>> d.values.append('anything')
+    Traceback (most recent call last):
+    TypeError: Can't append items to values
+    >>> d.values = (1, 2, 3)
+    >>> d
+    SequenceOrderedDict([(1, 1), (2, 2), (3, 3)])
+    
+    >>> d = SequenceOrderedDict(((1, 2), (2, 3), (3, 4)))
+    >>> d
+    SequenceOrderedDict([(1, 2), (2, 3), (3, 4)])
+    >>> d.items()
+    [(1, 2), (2, 3), (3, 4)]
+    >>> d.setitems([(3, 4), (2 ,3), (1, 2)])
+    >>> d
+    SequenceOrderedDict([(3, 4), (2, 3), (1, 2)])
+    >>> d.items[0]
+    (3, 4)
+    >>> d.items[:-1]
+    [(3, 4), (2, 3)]
+    >>> d.items[1] = (6, 3)
+    >>> d.items
+    [(3, 4), (6, 3), (1, 2)]
+    >>> d.items[1:2] = [(9, 9)]
+    >>> d
+    SequenceOrderedDict([(3, 4), (9, 9), (1, 2)])
+    >>> del d.items[1:2]
+    >>> d
+    SequenceOrderedDict([(3, 4), (1, 2)])
+    >>> (3, 4) in d.items
+    1
+    >>> (4, 3) in d.items
+    0
+    >>> len(d.items)
+    2
+    >>> [v for v in d.items]
+    [(3, 4), (1, 2)]
+    >>> d.items.count((3, 4))
+    1
+    >>> d.items.index((1, 2))
+    1
+    >>> d.items.index((2, 1))
+    Traceback (most recent call last):
+    ValueError: list.index(x): x not in list
+    >>> d.items.reverse()
+    >>> d.items
+    [(1, 2), (3, 4)]
+    >>> d.items.reverse()
+    >>> d.items.sort()
+    >>> d.items
+    [(1, 2), (3, 4)]
+    >>> d.items.append((5, 6))
+    >>> d.items
+    [(1, 2), (3, 4), (5, 6)]
+    >>> d.items.insert(0, (0, 0))
+    >>> d.items
+    [(0, 0), (1, 2), (3, 4), (5, 6)]
+    >>> d.items.insert(-1, (7, 8))
+    >>> d.items
+    [(0, 0), (1, 2), (3, 4), (7, 8), (5, 6)]
+    >>> d.items.pop()
+    (5, 6)
+    >>> d.items
+    [(0, 0), (1, 2), (3, 4), (7, 8)]
+    >>> d.items.remove((1, 2))
+    >>> d.items
+    [(0, 0), (3, 4), (7, 8)]
+    >>> d.items.extend([(1, 2), (5, 6)])
+    >>> d.items
+    [(0, 0), (3, 4), (7, 8), (1, 2), (5, 6)]
+    """
+
+    def __init__(self, init_val=(), strict=True):
+        OrderedDict.__init__(self, init_val, strict=strict)
+        self._keys = self.keys
+        self._values = self.values
+        self._items = self.items
+        self.keys = Keys(self)
+        self.values = Values(self)
+        self.items = Items(self)
+        self._att_dict = {
+            'keys': self.setkeys,
+            'items': self.setitems,
+            'values': self.setvalues,
+        }
+
+    def __setattr__(self, name, value):
+        """Protect keys, items, and values."""
+        if not '_att_dict' in self.__dict__:
+            object.__setattr__(self, name, value)
+        else:
+            try:
+                fun = self._att_dict[name]
+            except KeyError:
+                OrderedDict.__setattr__(self, name, value)
+            else:
+                fun(value)
+
+if __name__ == '__main__':
+    if INTP_VER < (2, 3):
+        raise RuntimeError("Tests require Python v.2.3 or later")
+    # turn off warnings for tests
+    warnings.filterwarnings('ignore')
+    # run the code tests in doctest format
+    import doctest
+    m = sys.modules.get('__main__')
+    globs = m.__dict__.copy()
+    globs.update({
+        'INTP_VER': INTP_VER,
+    })
+    doctest.testmod(m, globs=globs)
+
Index: src/sas/sascalc/data_util/ordereddict.py
===================================================================
--- src/sas/sascalc/data_util/ordereddict.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/ordereddict.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+"""Backport from python2.7 to python <= 2.6."""
+
+from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
+
+try:
+    from itertools import izip_longest as _zip_longest
+except ImportError:
+
+    from itertools import izip
+
+    def _zip_longest(*args, **kwds):
+        # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
+        fillvalue = kwds.get('fillvalue')
+        def sentinel(counter = ([fillvalue]*(len(args)-1)).pop):
+            yield counter()         # yields the fillvalue, or raises IndexError
+        fillers = _repeat(fillvalue)
+        iters = [_chain(it, sentinel(), fillers) for it in args]
+        try:
+            for tup in izip(*iters):
+                yield tup
+        except IndexError:
+            pass
+
+class OrderedDict(dict):
+
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at most 1 arguments, got %d' % len(args))
+        if not hasattr(self, '_keys'):
+            self._keys = []
+        self.update(*args, **kwds)
+
+    def clear(self):
+        del self._keys[:]
+        dict.clear(self)
+
+    def __setitem__(self, key, value):
+        if key not in self:
+            self._keys.append(key)
+        dict.__setitem__(self, key, value)
+
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        self._keys.remove(key)
+
+    def __iter__(self):
+        return iter(self._keys)
+
+    def __reversed__(self):
+        return reversed(self._keys)
+
+    def popitem(self):
+        if not self:
+            raise KeyError('dictionary is empty')
+        key = self._keys.pop()
+        value = dict.pop(self, key)
+        return key, value
+
+    def __reduce__(self):
+        items = [[k, self[k]] for k in self]
+        inst_dict = vars(self).copy()
+        inst_dict.pop('_keys', None)
+        return (self.__class__, (items,), inst_dict)
+
+    def setdefault(self, key, default=None):
+        try:
+            return self[key]
+        except KeyError:
+            self[key] = default
+        return default
+
+    def update(self, other=(), **kwds):
+        if hasattr(other, "keys"):
+            for key in other.keys():
+                self[key] = other[key]
+        else:
+            for key, value in other:
+                self[key] = value
+        for key, value in kwds.items():
+            self[key] = value
+
+    __marker = object()
+
+    def pop(self, key, default=__marker):
+        try:
+            value = self[key]
+        except KeyError:
+            if default is self.__marker:
+                raise
+            return default
+        else:
+            del self[key]
+            return value
+
+    def keys(self):
+        return list(self)
+
+    def values(self):
+        return [self[key] for key in self]
+
+    def items(self):
+        return [(key, self[key]) for key in self]
+
+    def __repr__(self):
+        if not self:
+            return '%s()' % (self.__class__.__name__,)
+        return '%s(%r)' % (self.__class__.__name__, list(self.items()))
+
+    def copy(self):
+        return self.__class__(self)
+
+    @classmethod
+    def fromkeys(cls, iterable, value=None):
+        d = cls()
+        for key in iterable:
+            d[key] = value
+        return d
+
+    def __eq__(self, other):
+        if isinstance(other, OrderedDict):
+            return all(p==q for p, q in  _zip_longest(self.items(), other.items()))
+        return dict.__eq__(self, other)
+
+
+
+# End class OrderedDict
+
Index: src/sas/sascalc/data_util/ordereddicttest.py
===================================================================
--- src/sas/sascalc/data_util/ordereddicttest.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/ordereddicttest.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+
+from random import shuffle
+import copy
+import inspect
+import pickle
+import unittest
+
+
+from ordereddict import OrderedDict
+
+
+class TestOrderedDict(unittest.TestCase):
+
+    def test_init(self):
+        self.assertRaises(TypeError, OrderedDict, ([('a', 1), ('b', 2)], None))
+                # too many args
+        pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
+        self.assertEqual(sorted(OrderedDict(dict(pairs)).items()), pairs)           # dict input
+        self.assertEqual(sorted(OrderedDict(**dict(pairs)).items()), pairs)         # kwds input
+        self.assertEqual(list(OrderedDict(pairs).items()), pairs)                   # pairs input
+        self.assertEqual(list(OrderedDict([('a', 1), ('b', 2), ('c', 9), ('d', 4)],
+                                          c=3, e=5).items()), pairs)                # mixed input
+
+        # make sure no positional args conflict with possible kwdargs
+        self.assertEqual(inspect.getargspec(OrderedDict.__dict__['__init__'])[0],
+                         ['self'])
+
+        # Make sure that direct calls to __init__ do not clear previous contents
+        d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
+        d.__init__([('e', 5), ('f', 6)], g=7, d=4)
+        self.assertEqual(list(d.items()),
+            [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
+
+    def test_update(self):
+        self.assertRaises(TypeError, OrderedDict().update, [('a', 1), ('b',
+            2)], None)                        # too many args
+        pairs = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5)]
+        od = OrderedDict()
+        od.update(dict(pairs))
+        self.assertEqual(sorted(od.items()), pairs)                                 # dict input
+        od = OrderedDict()
+        od.update(**dict(pairs))
+        self.assertEqual(sorted(od.items()), pairs)                                 # kwds input
+        od = OrderedDict()
+        od.update(pairs)
+        self.assertEqual(list(od.items()), pairs)                                   # pairs input
+        od = OrderedDict()
+        od.update([('a', 1), ('b', 2), ('c', 9), ('d', 4)], c=3, e=5)
+        self.assertEqual(list(od.items()), pairs)                                   # mixed input
+
+        # Make sure that direct calls to update do not clear previous contents
+        # add that updates items are not moved to the end
+        d = OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 44), ('e', 55)])
+        d.update([('e', 5), ('f', 6)], g=7, d=4)
+        self.assertEqual(list(d.items()),
+            [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6), ('g', 7)])
+
+    def test_clear(self):
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        shuffle(pairs)
+        od = OrderedDict(pairs)
+        self.assertEqual(len(od), len(pairs))
+        od.clear()
+        self.assertEqual(len(od), 0)
+
+    def test_delitem(self):
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        od = OrderedDict(pairs)
+        del od['a']
+        self.assert_('a' not in od)
+        self.assertRaises(KeyError, od.__delitem__, 'a')
+        self.assertEqual(list(od.items()), pairs[:2] + pairs[3:])
+
+    def test_setitem(self):
+        od = OrderedDict([('d', 1), ('b', 2), ('c', 3), ('a', 4), ('e', 5)])
+        od['c'] = 10           # existing element
+        od['f'] = 20           # new element
+        self.assertEqual(list(od.items()),
+                         [('d', 1), ('b', 2), ('c', 10), ('a', 4), ('e', 5), ('f', 20)])
+
+    def test_iterators(self):
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        shuffle(pairs)
+        od = OrderedDict(pairs)
+        self.assertEqual(list(od), [t[0] for t in pairs])
+        self.assertEqual(list(od.keys()), [t[0] for t in pairs])
+        self.assertEqual(list(od.values()), [t[1] for t in pairs])
+        self.assertEqual(list(od.items()), pairs)
+        self.assertEqual(list(reversed(od)),
+                         [t[0] for t in reversed(pairs)])
+
+    def test_popitem(self):
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        shuffle(pairs)
+        od = OrderedDict(pairs)
+        while pairs:
+            self.assertEqual(od.popitem(), pairs.pop())
+        self.assertRaises(KeyError, od.popitem)
+        self.assertEqual(len(od), 0)
+
+    def test_pop(self):
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        shuffle(pairs)
+        od = OrderedDict(pairs)
+        shuffle(pairs)
+        while pairs:
+            k, v = pairs.pop()
+            self.assertEqual(od.pop(k), v)
+        self.assertRaises(KeyError, od.pop, 'xyz')
+        self.assertEqual(len(od), 0)
+        self.assertEqual(od.pop(k, 12345), 12345)
+
+    def test_equality(self):
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        shuffle(pairs)
+        od1 = OrderedDict(pairs)
+        od2 = OrderedDict(pairs)
+        self.assertEqual(od1, od2)          # same order implies equality
+        pairs = pairs[2:] + pairs[:2]
+        od2 = OrderedDict(pairs)
+        self.assertNotEqual(od1, od2)       # different order implies inequality
+        # comparison to regular dict is not order sensitive
+        self.assertEqual(od1, dict(od2))
+        self.assertEqual(dict(od2), od1)
+        # different length implied inequality
+        self.assertNotEqual(od1, OrderedDict(pairs[:-1]))
+
+    def test_copying(self):
+        # Check that ordered dicts are copyable, deepcopyable, picklable,
+        # and have a repr/eval round-trip
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        od = OrderedDict(pairs)
+        update_test = OrderedDict()
+        update_test.update(od)
+        for i, dup in enumerate([
+                    od.copy(),
+                    copy.copy(od),
+                    copy.deepcopy(od),
+                    pickle.loads(pickle.dumps(od, 0)),
+                    pickle.loads(pickle.dumps(od, 1)),
+                    pickle.loads(pickle.dumps(od, 2)),
+                    pickle.loads(pickle.dumps(od, -1)),
+                    eval(repr(od)),
+                    update_test,
+                    OrderedDict(od),
+                    ]):
+            self.assert_(dup is not od)
+            self.assertEquals(dup, od)
+            self.assertEquals(list(dup.items()), list(od.items()))
+            self.assertEquals(len(dup), len(od))
+            self.assertEquals(type(dup), type(od))
+
+    def test_repr(self):
+        od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])
+        self.assertEqual(repr(od),
+            "OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
+        self.assertEqual(eval(repr(od)), od)
+        self.assertEqual(repr(OrderedDict()), "OrderedDict()")
+
+    def test_setdefault(self):
+        pairs = [('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)]
+        shuffle(pairs)
+        od = OrderedDict(pairs)
+        pair_order = list(od.items())
+        self.assertEqual(od.setdefault('a', 10), 3)
+        # make sure order didn't change
+        self.assertEqual(list(od.items()), pair_order)
+        self.assertEqual(od.setdefault('x', 10), 10)
+        # make sure 'x' is added to the end
+        self.assertEqual(list(od.items())[-1], ('x', 10))
+
+    def test_reinsert(self):
+        # Given insert a, insert b, delete a, re-insert a,
+        # verify that a is now later than b.
+        od = OrderedDict()
+        od['a'] = 1
+        od['b'] = 2
+        del od['a']
+        od['a'] = 1
+        self.assertEqual(list(od.items()), [('b', 2), ('a', 1)])
+
+if __name__ == "__main__":
+
+    unittest.main()
+
Index: src/sas/sascalc/data_util/pathutils.py
===================================================================
--- src/sas/sascalc/data_util/pathutils.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/pathutils.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+"""
+Utilities for path manipulation. Not to be confused with the pathutils module
+from the pythonutils package (http://groups.google.com/group/pythonutils).
+"""
+
+# NOTE: If enough of _that_ pathutils functionality is required, we can switch
+# this module for that one.
+
+# TODO: Make algorithm more robust and complete; consider using abspath.
+
+__all__ = ['relpath']
+
+from os.path import join
+from os.path import sep
+
+def relpath(p1, p2):
+    """Compute the relative path of p1 with respect to p2."""
+
+    def commonpath(L1, L2, common=[]):
+        if len(L1) < 1: return (common, L1, L2)
+        if len(L2) < 1: return (common, L1, L2)
+        if L1[0] != L2[0]: return (common, L1, L2)
+        return commonpath(L1[1:], L2[1:], common=common+[L1[0]])
+
+    # if the strings are equal, then return "."
+    if p1 == p2: return "."
+    (common,L1,L2) = commonpath(p2.split(sep), p1.split(sep))
+    # if there is nothing in common, then return an empty string
+    if not common: return ""
+    # otherwise, replace the common pieces with "../" (or "..\")
+    p = [(".."+sep) * len(L1)] + L2
+    return join(*p)
+
+def test():
+    p1 = sep.join(["a","b","c","d"])
+    p2 = sep.join(["a","b","c1","d1"])
+    p3 = sep.join(["a","b","c","d","e"])
+    p4 = sep.join(["a","b","c","d1","e"])
+    p5 = sep.join(["w","x","y","z"])
+
+    assert relpath(p1, p1) == "."
+    assert relpath(p2, p1) ==  sep.join(["..", "..", "c1", "d1"])
+    assert relpath(p3, p1) == "e"
+    assert relpath(p4, p1) ==  sep.join(["..", "d1", "e"])
+    assert relpath(p5, p1) == ""
+
+if __name__ == '__main__':
+    test()
Index: src/sas/sascalc/data_util/registry.py
===================================================================
--- src/sas/sascalc/data_util/registry.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/registry.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,174 @@
+# This program is public domain
+"""
+File extension registry.
+
+This provides routines for opening files based on extension,
+and registers the built-in file extensions.
+"""
+
+import os.path
+
+class ExtensionRegistry(object):
+    """
+    Associate a file loader with an extension.
+
+    Note that there may be multiple loaders for the same extension.
+
+    Example: ::
+
+        registry = ExtensionRegistry()
+
+        # Add an association by setting an element
+        registry['.zip'] = unzip
+        
+        # Multiple extensions for one loader
+        registry['.tgz'] = untar
+        registry['.tar.gz'] = untar
+
+        # Generic extensions to use after trying more specific extensions; 
+        # these will be checked after the more specific extensions fail.
+        registry['.gz'] = gunzip
+
+        # Multiple loaders for one extension
+        registry['.cx'] = cx1
+        registry['.cx'] = cx2
+        registry['.cx'] = cx3
+
+        # Show registered extensions
+        print registry.extensions()
+        
+        # Can also register a format name for explicit control from caller
+        registry['cx3'] = cx3
+        print registry.formats()
+
+        # Retrieve loaders for a file name
+        registry.lookup('hello.cx') -> [cx3,cx2,cx1]
+
+        # Run loader on a filename
+        registry.load('hello.cx') ->
+            try:
+                return cx3('hello.cx')
+            except:
+                try:
+                    return cx2('hello.cx')
+                except:
+                    return cx1('hello.cx')
+
+        # Load in a specific format ignoring extension
+        registry.load('hello.cx',format='cx3') ->
+            return cx3('hello.cx')
+    """
+    def __init__(self, **kw):
+        self.loaders = {}
+    def __setitem__(self, ext, loader):
+        if ext not in self.loaders:
+            self.loaders[ext] = []
+        self.loaders[ext].insert(0,loader)
+    def __getitem__(self, ext):
+        return self.loaders[ext]
+    def __contains__(self, ext):
+        return ext in self.loaders
+    def formats(self):
+        """
+        Return a sorted list of the registered formats.
+        """
+        names = [a for a in self.loaders.keys() if not a.startswith('.')]
+        names.sort()
+        return names
+    def extensions(self):
+        """
+        Return a sorted list of registered extensions.
+        """
+        exts = [a for a in self.loaders.keys() if a.startswith('.')]
+        exts.sort()
+        return exts
+    def lookup(self, path):
+        """
+        Return the loader associated with the file type of path.
+        
+        Raises ValueError if file type is not known.
+        """        
+        # Find matching extensions
+        extlist = [ext for ext in self.extensions() if path.endswith(ext)]
+        # Sort matching extensions by decreasing order of length
+        extlist.sort(lambda a,b: len(a)<len(b))
+        # Combine loaders for matching extensions into one big list
+        loaders = []
+        for L in [self.loaders[ext] for ext in extlist]:
+            loaders.extend(L)
+        # Remove duplicates if they exist
+        if len(loaders) != len(set(loaders)):
+            result = []
+            for L in loaders:
+                if L not in result: result.append(L)
+            loaders = L
+        # Raise an error if there are no matching extensions
+        if len(loaders) == 0:
+            raise ValueError, "Unknown file type for "+path
+        # All done
+        return loaders
+    def load(self, path, format=None):
+        """
+        Call the loader for the file type of path.
+
+        Raises ValueError if no loader is available.
+        Raises KeyError if format is not available.
+        May raise a loader-defined exception if loader fails.        
+        """
+        if format is None:
+            loaders = self.lookup(path)
+        else:
+            loaders = self.loaders[format]
+        for fn in loaders:
+            try:
+                return fn(path)
+            except:
+                pass # give other loaders a chance to succeed
+        # If we get here it is because all loaders failed
+        raise # reraises last exception
+
+def test():
+    reg = ExtensionRegistry()
+    class CxError(Exception): pass
+    def cx(file): return 'cx'
+    def new_cx(file): return 'new_cx'
+    def fail_cx(file): raise CxError
+    def cat(file): return 'cat'
+    def gunzip(file): return 'gunzip'
+    reg['.cx'] = cx
+    reg['.cx1'] = cx
+    reg['.cx'] = new_cx
+    reg['.gz'] = gunzip
+    reg['.cx.gz'] = new_cx
+    reg['.cx1.gz'] = fail_cx
+    reg['.cx1'] = fail_cx
+    reg['.cx2'] = fail_cx
+    reg['new_cx'] = new_cx
+
+    # Two loaders associated with .cx
+    assert reg.lookup('hello.cx') == [new_cx,cx]
+    # Make sure the last loader applies first
+    assert reg.load('hello.cx') == 'new_cx'
+    # Make sure the next loader applies if the first fails
+    assert reg.load('hello.cx1') == 'cx'
+    # Make sure the format override works
+    assert reg.load('hello.cx1',format='.cx.gz') == 'new_cx'
+    # Make sure the format override works
+    assert reg.load('hello.cx1',format='new_cx') == 'new_cx'
+    # Make sure the case of all loaders failing is correct
+    try:  reg.load('hello.cx2')
+    except CxError: pass # correct failure
+    else: raise AssertError,"Incorrect error on load failure"
+    # Make sure the case of no loaders fails correctly
+    try: reg.load('hello.missing')
+    except ValueError,msg:
+        assert str(msg)=="Unknown file type for hello.missing",'Message: <%s>'%(msg)
+    else: raise AssertError,"No error raised for missing extension"
+    assert reg.formats() == ['new_cx']
+    assert reg.extensions() == ['.cx','.cx.gz','.cx1','.cx1.gz','.cx2','.gz']
+    # make sure that it supports multiple '.' in filename
+    assert reg.load('hello.extra.cx1') == 'cx'
+    assert reg.load('hello.gz') == 'gunzip'
+    assert reg.load('hello.cx1.gz') == 'gunzip' # Since .cx1.gz fails
+
+if __name__ == "__main__": test()
Index: src/sas/sascalc/data_util/release_notes.txt
===================================================================
--- src/sas/sascalc/data_util/release_notes.txt	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/release_notes.txt	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,60 @@
+Release Notes
+=============
+
+Package name: data_util 0.1.5
+
+1-	Version 0.1.5
+	- Release date: 28/4/2010
+	- Added OrderedDict (backport from 2.7)
+	- Improved NeXus unit support
+	- Number/uncertainty formats fixes
+ 
+	Version 0.1.2
+	- Release date: 7/4/2009
+	- Added calcthread
+	
+ 	Version 0.1.1
+	- Release date: 4/21/2009
+	
+	Version 0.1
+	- Release date: 8/1/2008
+	- Contains useful data handling utilities from reflectometry group. 
+
+2- Downloading and Installing
+
+	2.1- System Requirements:
+		- Python version >= 2.4 should be running on the system
+
+	2.2- Installing:
+		- Get the code from svn://danse.us/common/releases/data_util-0.1.1
+		- The following modules are required:
+			* numpy
+
+3- Known Issues
+
+	3.1- All systems:
+		- None
+
+	3.2- Windows:
+		- None
+
+	3.3- Linux:
+		- None
+
+4- Troubleshooting
+
+	- None
+
+5- Frequently Asked Questions
+
+	- None
+
+6- Other Resources
+
+	- None
+
+	
+
+	
+
+
Index: src/sas/sascalc/data_util/uncertainty.py
===================================================================
--- src/sas/sascalc/data_util/uncertainty.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/uncertainty.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,316 @@
+r"""
+Uncertainty propagation class for arithmetic, log and exp.
+
+Based on scalars or numpy vectors, this class allows you to store and 
+manipulate values+uncertainties, with propagation of gaussian error for
+addition, subtraction, multiplication, division, power, exp and log.
+
+Storage properties are determined by the numbers used to set the value
+and uncertainty.  Be sure to use floating point uncertainty vectors 
+for inplace operations since numpy does not do automatic type conversion.
+Normal operations can use mixed integer and floating point.  In place
+operations such as *a \*= b* create at most one extra copy for each operation.
+By contrast, *c = a\*b* uses four intermediate vectors, so shouldn't be used
+for huge arrays.
+"""
+
+from __future__ import division
+
+import numpy
+import err1d
+from formatnum import format_uncertainty
+
+__all__ = ['Uncertainty']
+
+# TODO: rename to Measurement and add support for units?
+# TODO: C implementation of *,/,**?
+class Uncertainty(object):
+    # Make standard deviation available
+    def _getdx(self): return numpy.sqrt(self.variance)
+    def _setdx(self,dx): 
+        # Direct operation
+        #    variance = dx**2
+        # Indirect operation to avoid temporaries
+        self.variance[:] = dx
+        self.variance **= 2
+    dx = property(_getdx,_setdx,doc="standard deviation")
+
+    # Constructor
+    def __init__(self, x, variance=None):
+        self.x, self.variance = x, variance    
+ 
+    # Numpy array slicing operations
+    def __len__(self): 
+        return len(self.x)
+    def __getitem__(self,key): 
+        return Uncertainty(self.x[key],self.variance[key])
+    def __setitem__(self,key,value):
+        self.x[key] = value.x
+        self.variance[key] = value.variance
+    def __delitem__(self, key):
+        del self.x[key]
+        del self.variance[key]
+    #def __iter__(self): pass # Not sure we need iter
+
+    # Normal operations: may be of mixed type
+    def __add__(self, other):
+        if isinstance(other,Uncertainty):
+            return Uncertainty(*err1d.add(self.x,self.variance,other.x,other.variance))
+        else:
+            return Uncertainty(self.x+other, self.variance+0) # Force copy
+    def __sub__(self, other):
+        if isinstance(other,Uncertainty):
+            return Uncertainty(*err1d.sub(self.x,self.variance,other.x,other.variance))
+        else:
+            return Uncertainty(self.x-other, self.variance+0) # Force copy
+    def __mul__(self, other):
+        if isinstance(other,Uncertainty):
+            return Uncertainty(*err1d.mul(self.x,self.variance,other.x,other.variance))
+        else:
+            return Uncertainty(self.x*other, self.variance*other**2)
+    def __truediv__(self, other):
+        if isinstance(other,Uncertainty):
+            return Uncertainty(*err1d.div(self.x,self.variance,other.x,other.variance))
+        else:
+            return Uncertainty(self.x/other, self.variance/other**2)
+    def __pow__(self, other):
+        if isinstance(other,Uncertainty):
+            # Haven't calcuated variance in (a+/-da) ** (b+/-db)
+            return NotImplemented
+        else:
+            return Uncertainty(*err1d.pow(self.x,self.variance,other))
+
+    # Reverse operations
+    def __radd__(self, other):
+        return Uncertainty(self.x+other, self.variance+0) # Force copy
+    def __rsub__(self, other):
+        return Uncertainty(other-self.x, self.variance+0)
+    def __rmul__(self, other):
+        return Uncertainty(self.x*other, self.variance*other**2)
+    def __rtruediv__(self, other):
+        x,variance = err1d.pow(self.x,self.variance,-1)
+        return Uncertainty(x*other,variance*other**2)
+    def __rpow__(self, other): return NotImplemented
+
+    # In-place operations: may be of mixed type
+    def __iadd__(self, other):
+        if isinstance(other,Uncertainty):
+            self.x,self.variance \
+                = err1d.add_inplace(self.x,self.variance,other.x,other.variance)
+        else:
+            self.x+=other
+        return self
+    def __isub__(self, other):
+        if isinstance(other,Uncertainty):
+            self.x,self.variance \
+                = err1d.sub_inplace(self.x,self.variance,other.x,other.variance)
+        else:
+            self.x-=other
+        return self
+    def __imul__(self, other):
+        if isinstance(other,Uncertainty):
+            self.x, self.variance \
+                = err1d.mul_inplace(self.x,self.variance,other.x,other.variance)
+        else:
+            self.x *= other
+            self.variance *= other**2
+        return self
+    def __itruediv__(self, other):
+        if isinstance(other,Uncertainty):
+            self.x,self.variance \
+                = err1d.div_inplace(self.x,self.variance,other.x,other.variance)
+        else:
+            self.x /= other
+            self.variance /= other**2
+        return self
+    def __ipow__(self, other):
+        if isinstance(other,Uncertainty):
+            # Haven't calcuated variance in (a+/-da) ** (b+/-db)
+            return NotImplemented
+        else:
+            self.x,self.variance = err1d.pow_inplace(self.x, self.variance, other)
+        return self
+
+    # Use true division instead of integer division
+    def __div__(self, other): return self.__truediv__(other)
+    def __rdiv__(self, other): return self.__rtruediv__(other)
+    def __idiv__(self, other): return self.__itruediv__(other)
+
+        
+    # Unary ops
+    def __neg__(self):
+        return Uncertainty(-self.x,self.variance)
+    def __pos__(self):
+        return self
+    def __abs__(self):
+        return Uncertainty(numpy.abs(self.x),self.variance)
+
+    def __str__(self):
+        #return str(self.x)+" +/- "+str(numpy.sqrt(self.variance))
+        if numpy.isscalar(self.x):
+            return format_uncertainty(self.x,numpy.sqrt(self.variance))
+        else:
+            return [format_uncertainty(v,dv) 
+                    for v,dv in zip(self.x,numpy.sqrt(self.variance))]
+    def __repr__(self):
+        return "Uncertainty(%s,%s)"%(str(self.x),str(self.variance))
+
+    # Not implemented
+    def __floordiv__(self, other): return NotImplemented
+    def __mod__(self, other): return NotImplemented
+    def __divmod__(self, other): return NotImplemented
+    def __mod__(self, other): return NotImplemented
+    def __lshift__(self, other): return NotImplemented
+    def __rshift__(self, other): return NotImplemented
+    def __and__(self, other): return NotImplemented
+    def __xor__(self, other): return NotImplemented
+    def __or__(self, other): return NotImplemented
+
+    def __rfloordiv__(self, other): return NotImplemented
+    def __rmod__(self, other): return NotImplemented
+    def __rdivmod__(self, other): return NotImplemented
+    def __rmod__(self, other): return NotImplemented
+    def __rlshift__(self, other): return NotImplemented
+    def __rrshift__(self, other): return NotImplemented
+    def __rand__(self, other): return NotImplemented
+    def __rxor__(self, other): return NotImplemented
+    def __ror__(self, other): return NotImplemented
+
+    def __ifloordiv__(self, other): return NotImplemented
+    def __imod__(self, other): return NotImplemented
+    def __idivmod__(self, other): return NotImplemented
+    def __imod__(self, other): return NotImplemented
+    def __ilshift__(self, other): return NotImplemented
+    def __irshift__(self, other): return NotImplemented
+    def __iand__(self, other): return NotImplemented
+    def __ixor__(self, other): return NotImplemented
+    def __ior__(self, other): return NotImplemented
+
+    def __invert__(self): return NotImplmented  # For ~x
+    def __complex__(self): return NotImplmented
+    def __int__(self): return NotImplmented
+    def __long__(self): return NotImplmented
+    def __float__(self): return NotImplmented
+    def __oct__(self): return NotImplmented
+    def __hex__(self): return NotImplmented
+    def __index__(self): return NotImplmented
+    def __coerce__(self): return NotImplmented
+
+    def log(self):
+        return Uncertainty(*err1d.log(self.x,self.variance))
+
+    def exp(self):
+        return Uncertainty(*err1d.exp(self.x,self.variance))
+
+def log(val): return self.log()
+def exp(val): return self.exp()
+
+def test():
+    a = Uncertainty(5,3)
+    b = Uncertainty(4,2)
+
+    # Scalar operations
+    z = a+4
+    assert z.x == 5+4 and z.variance == 3
+    z = a-4
+    assert z.x == 5-4 and z.variance == 3
+    z = a*4
+    assert z.x == 5*4 and z.variance == 3*4**2
+    z = a/4
+    assert z.x == 5./4 and z.variance == 3./4**2
+    
+    # Reverse scalar operations
+    z = 4+a
+    assert z.x == 4+5 and z.variance == 3
+    z = 4-a
+    assert z.x == 4-5 and z.variance == 3
+    z = 4*a
+    assert z.x == 4*5 and z.variance == 3*4**2
+    z = 4/a
+    assert z.x == 4./5 and abs(z.variance - 3./5**4 * 4**2) < 1e-15
+    
+    # Power operations
+    z = a**2
+    assert z.x == 5**2 and z.variance == 4*3*5**2
+    z = a**1
+    assert z.x == 5**1 and z.variance == 3
+    z = a**0
+    assert z.x == 5**0 and z.variance == 0
+    z = a**-1
+    assert z.x == 5**-1 and abs(z.variance - 3./5**4) < 1e-15
+
+    # Binary operations
+    z = a+b
+    assert z.x == 5+4 and z.variance == 3+2
+    z = a-b
+    assert z.x == 5-4 and z.variance == 3+2
+    z = a*b
+    assert z.x == 5*4 and z.variance == (5**2*2 + 4**2*3)
+    z = a/b
+    assert z.x == 5./4 and abs(z.variance - (3./5**2 + 2./4**2)*(5./4)**2) < 1e-15
+
+    # ===== Inplace operations =====    
+    # Scalar operations
+    y = a+0; y += 4
+    z = a+4
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+    y = a+0; y -= 4
+    z = a-4
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+    y = a+0; y *= 4
+    z = a*4
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+    y = a+0; y /= 4
+    z = a/4
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+
+    # Power operations
+    y = a+0; y **= 4
+    z = a**4
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+
+    # Binary operations
+    y = a+0; y += b
+    z = a+b
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+    y = a+0; y -= b
+    z = a-b
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+    y = a+0; y *= b
+    z = a*b
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+    y = a+0; y /= b
+    z = a/b
+    assert y.x == z.x and abs(y.variance-z.variance) < 1e-15
+
+
+    # =============== vector operations ================
+    # Slicing
+    z = Uncertainty(numpy.array([1,2,3,4,5]),numpy.array([2,1,2,3,2]))
+    assert z[2].x == 3 and z[2].variance == 2
+    assert (z[2:4].x == [3,4]).all()
+    assert (z[2:4].variance == [2,3]).all()
+    z[2:4] = Uncertainty(numpy.array([8,7]),numpy.array([4,5]))
+    assert z[2].x == 8 and z[2].variance == 4
+    A = Uncertainty(numpy.array([a.x]*2),numpy.array([a.variance]*2))
+    B = Uncertainty(numpy.array([b.x]*2),numpy.array([b.variance]*2))
+
+    # TODO complete tests of copy and inplace operations for vectors and slices.
+
+    # Binary operations
+    z = A+B
+    assert (z.x == 5+4).all() and (z.variance == 3+2).all()
+    z = A-B
+    assert (z.x == 5-4).all() and (z.variance == 3+2).all()
+    z = A*B
+    assert (z.x == 5*4).all() and (z.variance == (5**2*2 + 4**2*3)).all()
+    z = A/B
+    assert (z.x == 5./4).all()
+    assert (abs(z.variance - (3./5**2 + 2./4**2)*(5./4)**2) < 1e-15).all()
+    
+    # printing; note that sqrt(3) ~ 1.7
+    assert str(Uncertainty(5,3)) == "5.0(17)"
+    assert str(Uncertainty(15,3)) == "15.0(17)"
+    assert str(Uncertainty(151.23356,0.324185**2)) == "151.23(32)"
+
+if __name__ == "__main__": test()
Index: src/sas/sascalc/data_util/uniquelist.py
===================================================================
--- src/sas/sascalc/data_util/uniquelist.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/data_util/uniquelist.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,40 @@
+#
+
+def uniquelist(inputlist, hash=None): 
+    '''remove redunduant elements from the give list
+    and return a list of unique elements.
+
+    inputlist: input list
+    hash: use this function to make the items in the list hashable.
+
+    Implementation details:
+        This function is order-preserving.
+    '''
+    if hash is None:
+        hash = __builtins__.hash
+    seen = {}
+    result = []
+    for item in inputlist:
+        marker = hash(item)
+        if marker in seen: continue
+        seen[marker] = 1
+        result.append(item)
+    return result
+
+
+def test():
+    assert uniquelist([1,2,2,3])==[1,2,3]
+    return
+
+
+def main():
+    test()
+    return
+
+if __name__ == '__main__': main()
+
+
+# version
+__id__ = "$Id$"
+
+# End of file 
Index: src/sas/sascalc/dataloader/__init__.py
===================================================================
--- src/sas/sascalc/dataloader/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,3 @@
+from data_info import *
+from manipulations import *
+from readers import *
Index: src/sas/sascalc/dataloader/data_info.py
===================================================================
--- src/sas/sascalc/dataloader/data_info.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/data_info.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1227 @@
+"""
+    Module that contains classes to hold information read from
+    reduced data files.
+
+    A good description of the data members can be found in
+    the CanSAS 1D XML data format:
+
+    http://www.smallangles.net/wgwiki/index.php/cansas1d_documentation
+"""
+#####################################################################
+#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.
+#See the license text in license.txt
+#copyright 2008, University of Tennessee
+######################################################################
+
+
+#TODO: Keep track of data manipulation in the 'process' data structure.
+#TODO: This module should be independent of plottables. We should write
+#        an adapter class for plottables when needed.
+
+#from sas.guitools.plottables import Data1D as plottable_1D
+from sas.sascalc.data_util.uncertainty import Uncertainty
+import numpy
+import math
+
+class plottable_sesans1D(object):
+    """
+    SESANS is a place holder for 1D SESANS plottables.
+
+    #TODO: This was directly copied from the plottables_1D. Modified Somewhat.
+    #Class has been updated.
+    """
+    # The presence of these should be mutually
+    # exclusive with the presence of Qdev (dx)
+    x = None
+    y = None
+    lam = None
+    dx = None
+    dy = None
+    dlam = None
+    ## Slit smearing length
+    dxl = None
+    ## Slit smearing width
+    dxw = None
+
+    # Units
+    _xaxis = ''
+    _xunit = ''
+    _yaxis = ''
+    _yunit = ''
+
+    def __init__(self, x, y, lam, dx=None, dy=None, dlam=None):
+#        print "SESANS plottable working"
+        self.x = numpy.asarray(x)
+        self.y = numpy.asarray(y)
+        self.lam = numpy.asarray(lam)
+        if dx is not None:
+            self.dx = numpy.asarray(dx)
+        if dy is not None:
+            self.dy = numpy.asarray(dy)
+        if dlam is not None:
+            self.dlam = numpy.asarray(dlam)
+
+    def xaxis(self, label, unit):
+        """
+        set the x axis label and unit
+        """
+        self._xaxis = label
+        self._xunit = unit
+
+    def yaxis(self, label, unit):
+        """
+        set the y axis label and unit
+        """
+        self._yaxis = label
+        self._yunit = unit
+
+
+class plottable_1D(object):
+    """
+    Data1D is a place holder for 1D plottables.
+    """
+    # The presence of these should be mutually
+    # exclusive with the presence of Qdev (dx)
+    x = None
+    y = None
+    dx = None
+    dy = None
+    ## Slit smearing length
+    dxl = None
+    ## Slit smearing width
+    dxw = None
+
+    # Units
+    _xaxis = ''
+    _xunit = ''
+    _yaxis = ''
+    _yunit = ''
+
+    def __init__(self, x, y, dx=None, dy=None, dxl=None, dxw=None):
+        self.x = numpy.asarray(x)
+        self.y = numpy.asarray(y)
+        if dx is not None:
+            self.dx = numpy.asarray(dx)
+        if dy is not None:
+            self.dy = numpy.asarray(dy)
+        if dxl is not None:
+            self.dxl = numpy.asarray(dxl)
+        if dxw is not None:
+            self.dxw = numpy.asarray(dxw)
+
+    def xaxis(self, label, unit):
+        """
+        set the x axis label and unit
+        """
+        self._xaxis = label
+        self._xunit = unit
+
+    def yaxis(self, label, unit):
+        """
+        set the y axis label and unit
+        """
+        self._yaxis = label
+        self._yunit = unit
+
+
+class plottable_2D(object):
+    """
+    Data2D is a place holder for 2D plottables.
+    """
+    xmin = None
+    xmax = None
+    ymin = None
+    ymax = None
+    data = None
+    qx_data = None
+    qy_data = None
+    q_data = None
+    err_data = None
+    dqx_data = None
+    dqy_data = None
+    mask = None
+
+    # Units
+    _xaxis = ''
+    _xunit = ''
+    _yaxis = ''
+    _yunit = ''
+    _zaxis = ''
+    _zunit = ''
+
+    def __init__(self, data=None, err_data=None, qx_data=None,
+                 qy_data=None, q_data=None, mask=None,
+                 dqx_data=None, dqy_data=None):
+        self.data = numpy.asarray(data)
+        self.qx_data = numpy.asarray(qx_data)
+        self.qy_data = numpy.asarray(qy_data)
+        self.q_data = numpy.asarray(q_data)
+        self.mask = numpy.asarray(mask)
+        self.err_data = numpy.asarray(err_data)
+        if dqx_data is not None:
+            self.dqx_data = numpy.asarray(dqx_data)
+        if dqy_data is not None:
+            self.dqy_data = numpy.asarray(dqy_data)
+
+    def xaxis(self, label, unit):
+        """
+        set the x axis label and unit
+        """
+        self._xaxis = label
+        self._xunit = unit
+
+    def yaxis(self, label, unit):
+        """
+        set the y axis label and unit
+        """
+        self._yaxis = label
+        self._yunit = unit
+
+    def zaxis(self, label, unit):
+        """
+        set the z axis label and unit
+        """
+        self._zaxis = label
+        self._zunit = unit
+
+
+class Vector(object):
+    """
+    Vector class to hold multi-dimensional objects
+    """
+    ## x component
+    x = None
+    ## y component
+    y = None
+    ## z component
+    z = None
+
+    def __init__(self, x=None, y=None, z=None):
+        """
+        Initialization. Components that are not
+        set a set to None by default.
+
+        :param x: x component
+        :param y: y component
+        :param z: z component
+        """
+        self.x = x
+        self.y = y
+        self.z = z
+
+    def __str__(self):
+        msg = "x = %s\ty = %s\tz = %s" % (str(self.x), str(self.y), str(self.z))
+        return msg
+
+
+class Detector(object):
+    """
+    Class to hold detector information
+    """
+    ## Name of the instrument [string]
+    name = None
+    ## Sample to detector distance [float] [mm]
+    distance = None
+    distance_unit = 'mm'
+    ## Offset of this detector position in X, Y,
+    #(and Z if necessary) [Vector] [mm]
+    offset = None
+    offset_unit = 'm'
+    ## Orientation (rotation) of this detector in roll,
+    # pitch, and yaw [Vector] [degrees]
+    orientation = None
+    orientation_unit = 'degree'
+    ## Center of the beam on the detector in X and Y
+    #(and Z if necessary) [Vector] [mm]
+    beam_center = None
+    beam_center_unit = 'mm'
+    ## Pixel size in X, Y, (and Z if necessary) [Vector] [mm]
+    pixel_size = None
+    pixel_size_unit = 'mm'
+    ## Slit length of the instrument for this detector.[float] [mm]
+    slit_length = None
+    slit_length_unit = 'mm'
+
+    def __init__(self):
+        """
+        Initialize class attribute that are objects...
+        """
+        self.offset = Vector()
+        self.orientation = Vector()
+        self.beam_center = Vector()
+        self.pixel_size = Vector()
+
+    def __str__(self):
+        _str = "Detector:\n"
+        _str += "   Name:         %s\n" % self.name
+        _str += "   Distance:     %s [%s]\n" % \
+            (str(self.distance), str(self.distance_unit))
+        _str += "   Offset:       %s [%s]\n" % \
+            (str(self.offset), str(self.offset_unit))
+        _str += "   Orientation:  %s [%s]\n" % \
+            (str(self.orientation), str(self.orientation_unit))
+        _str += "   Beam center:  %s [%s]\n" % \
+            (str(self.beam_center), str(self.beam_center_unit))
+        _str += "   Pixel size:   %s [%s]\n" % \
+            (str(self.pixel_size), str(self.pixel_size_unit))
+        _str += "   Slit length:  %s [%s]\n" % \
+            (str(self.slit_length), str(self.slit_length_unit))
+        return _str
+
+
+class Aperture(object):
+    ## Name
+    name = None
+    ## Type
+    type = None
+    ## Size name
+    size_name = None
+    ## Aperture size [Vector]
+    size = None
+    size_unit = 'mm'
+    ## Aperture distance [float]
+    distance = None
+    distance_unit = 'mm'
+
+    def __init__(self):
+        self.size = Vector()
+
+
+class Collimation(object):
+    """
+    Class to hold collimation information
+    """
+    ## Name
+    name = None
+    ## Length [float] [mm]
+    length = None
+    length_unit = 'mm'
+    ## Aperture
+    aperture = None
+
+    def __init__(self):
+        self.aperture = []
+
+    def __str__(self):
+        _str = "Collimation:\n"
+        _str += "   Length:       %s [%s]\n" % \
+            (str(self.length), str(self.length_unit))
+        for item in self.aperture:
+            _str += "   Aperture size:%s [%s]\n" % \
+                (str(item.size), str(item.size_unit))
+            _str += "   Aperture_dist:%s [%s]\n" % \
+                (str(item.distance), str(item.distance_unit))
+        return _str
+
+
+class Source(object):
+    """
+    Class to hold source information
+    """
+    ## Name
+    name = None
+    ## Radiation type [string]
+    radiation = None
+    ## Beam size name
+    beam_size_name = None
+    ## Beam size [Vector] [mm]
+    beam_size = None
+    beam_size_unit = 'mm'
+    ## Beam shape [string]
+    beam_shape = None
+    ## Wavelength [float] [Angstrom]
+    wavelength = None
+    wavelength_unit = 'A'
+    ## Minimum wavelength [float] [Angstrom]
+    wavelength_min = None
+    wavelength_min_unit = 'nm'
+    ## Maximum wavelength [float] [Angstrom]
+    wavelength_max = None
+    wavelength_max_unit = 'nm'
+    ## Wavelength spread [float] [Angstrom]
+    wavelength_spread = None
+    wavelength_spread_unit = 'percent'
+
+    def __init__(self):
+        self.beam_size = Vector()
+
+    def __str__(self):
+        _str = "Source:\n"
+        _str += "   Radiation:    %s\n" % str(self.radiation)
+        _str += "   Shape:        %s\n" % str(self.beam_shape)
+        _str += "   Wavelength:   %s [%s]\n" % \
+            (str(self.wavelength), str(self.wavelength_unit))
+        _str += "   Waveln_min:   %s [%s]\n" % \
+            (str(self.wavelength_min), str(self.wavelength_min_unit))
+        _str += "   Waveln_max:   %s [%s]\n" % \
+            (str(self.wavelength_max), str(self.wavelength_max_unit))
+        _str += "   Waveln_spread:%s [%s]\n" % \
+            (str(self.wavelength_spread), str(self.wavelength_spread_unit))
+        _str += "   Beam_size:    %s [%s]\n" % \
+            (str(self.beam_size), str(self.beam_size_unit))
+        return _str
+
+
+"""
+Definitions of radiation types
+"""
+NEUTRON = 'neutron'
+XRAY = 'x-ray'
+MUON = 'muon'
+ELECTRON = 'electron'
+
+
+class Sample(object):
+    """
+    Class to hold the sample description
+    """
+    ## Short name for sample
+    name = ''
+    ## ID
+    ID = ''
+    ## Thickness [float] [mm]
+    thickness = None
+    thickness_unit = 'mm'
+    ## Transmission [float] [fraction]
+    transmission = None
+    ## Temperature [float] [No Default]
+    temperature = None
+    temperature_unit = None
+    ## Position [Vector] [mm]
+    position = None
+    position_unit = 'mm'
+    ## Orientation [Vector] [degrees]
+    orientation = None
+    orientation_unit = 'degree'
+    ## Details
+    details = None
+
+    def __init__(self):
+        self.position = Vector()
+        self.orientation = Vector()
+        self.details = []
+
+    def __str__(self):
+        _str = "Sample:\n"
+        _str += "   ID:           %s\n" % str(self.ID)
+        _str += "   Transmission: %s\n" % str(self.transmission)
+        _str += "   Thickness:    %s [%s]\n" % \
+            (str(self.thickness), str(self.thickness_unit))
+        _str += "   Temperature:  %s [%s]\n" % \
+            (str(self.temperature), str(self.temperature_unit))
+        _str += "   Position:     %s [%s]\n" % \
+            (str(self.position), str(self.position_unit))
+        _str += "   Orientation:  %s [%s]\n" % \
+            (str(self.orientation), str(self.orientation_unit))
+
+        _str += "   Details:\n"
+        for item in self.details:
+            _str += "      %s\n" % item
+
+        return _str
+
+
+class Process(object):
+    """
+    Class that holds information about the processes
+    performed on the data.
+    """
+    name = ''
+    date = ''
+    description = ''
+    term = None
+    notes = None
+
+    def __init__(self):
+        self.term = []
+        self.notes = []
+
+    def is_empty(self):
+        """
+            Return True if the object is empty
+        """
+        return len(self.name) == 0 and len(self.date) == 0 and len(self.description) == 0 \
+            and len(self.term) == 0 and len(self.notes) == 0
+            
+    def single_line_desc(self):
+        """
+            Return a single line string representing the process
+        """
+        return "%s %s %s" % (self.name, self.date, self.description)
+     
+    def __str__(self):
+        _str = "Process:\n"
+        _str += "   Name:         %s\n" % self.name
+        _str += "   Date:         %s\n" % self.date
+        _str += "   Description:  %s\n" % self.description
+        for item in self.term:
+            _str += "   Term:         %s\n" % item
+        for item in self.notes:
+            _str += "   Note:         %s\n" % item
+        return _str
+
+
+class TransmissionSpectrum(object):
+    """
+    Class that holds information about transmission spectrum
+    for white beams and spallation sources.
+    """
+    name = ''
+    timestamp = ''
+    ## Wavelength (float) [A]
+    wavelength = None
+    wavelength_unit = 'A'
+    ## Transmission (float) [unit less]
+    transmission = None
+    transmission_unit = ''
+    ## Transmission Deviation (float) [unit less]
+    transmission_deviation = None
+    transmission_deviation_unit = ''
+
+    def __init__(self):
+        self.wavelength = []
+        self.transmission = []
+        self.transmission_deviation = []
+
+    def __str__(self):
+        _str = "Transmission Spectrum:\n"
+        _str += "   Name:             \t{0}\n".format(self.name)
+        _str += "   Timestamp:        \t{0}\n".format(self.timestamp)
+        _str += "   Wavelength unit:  \t{0}\n".format(self.wavelength_unit)
+        _str += "   Transmission unit:\t{0}\n".format(self.transmission_unit)
+        _str += "   Trans. Dev. unit:  \t{0}\n".format(\
+                                            self.transmission_deviation_unit)
+        length_list = [len(self.wavelength), len(self.transmission), \
+                len(self.transmission_deviation)]
+        _str += "   Number of Pts:    \t{0}\n".format(max(length_list))
+        return _str
+
+
+class DataInfo(object):
+    """
+    Class to hold the data read from a file.
+    It includes four blocks of data for the
+    instrument description, the sample description,
+    the data itself and any other meta data.
+    """
+    ## Title
+    title = ''
+    ## Run number
+    run = None
+    ## Run name
+    run_name = None
+    ## File name
+    filename = ''
+    ## Notes
+    notes = None
+    ## Processes (Action on the data)
+    process = None
+    ## Instrument name
+    instrument = ''
+    ## Detector information
+    detector = None
+    ## Sample information
+    sample = None
+    ## Source information
+    source = None
+    ## Collimation information
+    collimation = None
+    ## Transmission Spectrum INfo
+    trans_spectrum = None
+    ## Additional meta-data
+    meta_data = None
+    ## Loading errors
+    errors = None
+
+    def __init__(self):
+        """
+        Initialization
+        """
+        ## Title
+        self.title = ''
+        ## Run number
+        self.run = []
+        self.run_name = {}
+        ## File name
+        self.filename = ''
+        ## Notes
+        self.notes = []
+        ## Processes (Action on the data)
+        self.process = []
+        ## Instrument name
+        self.instrument = ''
+        ## Detector information
+        self.detector = []
+        ## Sample information
+        self.sample = Sample()
+        ## Source information
+        self.source = Source()
+        ## Collimation information
+        self.collimation = []
+        ## Transmission Spectrum
+        self.trans_spectrum = []
+        ## Additional meta-data
+        self.meta_data = {}
+        ## Loading errors
+        self.errors = []
+
+    def append_empty_process(self):
+        """
+        """
+        self.process.append(Process())
+
+    def add_notes(self, message=""):
+        """
+        Add notes to datainfo
+        """
+        self.notes.append(message)
+
+    def __str__(self):
+        """
+        Nice printout
+        """
+        _str = "File:            %s\n" % self.filename
+        _str += "Title:           %s\n" % self.title
+        _str += "Run:             %s\n" % str(self.run)
+        _str += "Instrument:      %s\n" % str(self.instrument)
+        _str += "%s\n" % str(self.sample)
+        _str += "%s\n" % str(self.source)
+        for item in self.detector:
+            _str += "%s\n" % str(item)
+        for item in self.collimation:
+            _str += "%s\n" % str(item)
+        for item in self.process:
+            _str += "%s\n" % str(item)
+        for item in self.notes:
+            _str += "%s\n" % str(item)
+        for item in self.trans_spectrum:
+            _str += "%s\n" % str(item)
+        return _str
+
+    # Private method to perform operation. Not implemented for DataInfo,
+    # but should be implemented for each data class inherited from DataInfo
+    # that holds actual data (ex.: Data1D)
+    def _perform_operation(self, other, operation):
+        """
+        Private method to perform operation. Not implemented for DataInfo,
+        but should be implemented for each data class inherited from DataInfo
+        that holds actual data (ex.: Data1D)
+        """
+        return NotImplemented
+
+    def _perform_union(self, other):
+        """
+        Private method to perform union operation. Not implemented for DataInfo,
+        but should be implemented for each data class inherited from DataInfo
+        that holds actual data (ex.: Data1D)
+        """
+        return NotImplemented
+
+    def __add__(self, other):
+        """
+        Add two data sets
+
+        :param other: data set to add to the current one
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b):
+            return a + b
+        return self._perform_operation(other, operation)
+
+    def __radd__(self, other):
+        """
+        Add two data sets
+
+        :param other: data set to add to the current one
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b):
+            return b + a
+        return self._perform_operation(other, operation)
+
+    def __sub__(self, other):
+        """
+        Subtract two data sets
+
+        :param other: data set to subtract from the current one
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b):
+            return a - b
+        return self._perform_operation(other, operation)
+
+    def __rsub__(self, other):
+        """
+        Subtract two data sets
+
+        :param other: data set to subtract from the current one
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b):
+            return b - a
+        return self._perform_operation(other, operation)
+
+    def __mul__(self, other):
+        """
+        Multiply two data sets
+
+        :param other: data set to subtract from the current one
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b):
+            return a * b
+        return self._perform_operation(other, operation)
+
+    def __rmul__(self, other):
+        """
+        Multiply two data sets
+
+        :param other: data set to subtract from the current one
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b):
+            return b * a
+        return self._perform_operation(other, operation)
+
+    def __div__(self, other):
+        """
+        Divided a data set by another
+
+        :param other: data set that the current one is divided by
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b):
+            return a/b
+        return self._perform_operation(other, operation)
+
+    def __rdiv__(self, other):
+        """
+        Divided a data set by another
+
+        :param other: data set that the current one is divided by
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        def operation(a, b):
+            return b/a
+        return self._perform_operation(other, operation)
+
+    def __or__(self, other):
+        """
+        Union a data set with another
+
+        :param other: data set to be unified
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        return self._perform_union(other)
+
+    def __ror__(self, other):
+        """
+        Union a data set with another
+
+        :param other: data set to be unified
+        :return: new data set
+        :raise ValueError: raised when two data sets are incompatible
+        """
+        return self._perform_union(other)
+
+class SESANSData1D(plottable_sesans1D, DataInfo):
+    """
+    SESANS 1D data class
+    """
+    x_unit = 'nm'
+    y_unit = 'pol'
+
+    def __init__(self, x=None, y=None, lam=None, dx=None, dy=None, dlam=None):
+        DataInfo.__init__(self)
+        plottable_sesans1D.__init__(self, x, y, lam, dx, dy, dlam)
+
+    def __str__(self):
+        """
+        Nice printout
+        """
+        _str = "%s\n" % DataInfo.__str__(self)
+        _str += "Data:\n"
+        _str += "   Type:         %s\n" % self.__class__.__name__
+        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit)
+        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit)
+        _str += "   Length:       %g\n" % len(self.x)
+        return _str
+
+    def clone_without_data(self, length=0, clone=None):
+        """
+        Clone the current object, without copying the data (which
+        will be filled out by a subsequent operation).
+        The data arrays will be initialized to zero.
+
+        :param length: length of the data array to be initialized
+        :param clone: if provided, the data will be copied to clone
+        """
+        from copy import deepcopy
+        if clone is None or not issubclass(clone.__class__, Data1D):
+            x = numpy.zeros(length)
+            dx = numpy.zeros(length)
+            y = numpy.zeros(length)
+            dy = numpy.zeros(length)
+            clone = Data1D(x, y, dx=dx, dy=dy)
+
+        clone.title = self.title
+        clone.run = self.run
+        clone.filename = self.filename
+        clone.instrument = self.instrument
+        clone.notes = deepcopy(self.notes)
+        clone.process = deepcopy(self.process)
+        clone.detector = deepcopy(self.detector)
+        clone.sample = deepcopy(self.sample)
+        clone.source = deepcopy(self.source)
+        clone.collimation = deepcopy(self.collimation)
+        clone.trans_spectrum = deepcopy(self.trans_spectrum)
+        clone.meta_data = deepcopy(self.meta_data)
+        clone.errors = deepcopy(self.errors)
+
+        return clone
+
+class Data1D(plottable_1D, DataInfo):
+    """
+    1D data class
+    """
+    x_unit = '1/A'
+    y_unit = '1/cm'
+
+    def __init__(self, x, y, dx=None, dy=None):
+        DataInfo.__init__(self)
+        plottable_1D.__init__(self, x, y, dx, dy)
+
+    def __str__(self):
+        """
+        Nice printout
+        """
+        _str = "%s\n" % DataInfo.__str__(self)
+        _str += "Data:\n"
+        _str += "   Type:         %s\n" % self.__class__.__name__
+        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit)
+        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit)
+        _str += "   Length:       %g\n" % len(self.x)
+        return _str
+
+    def is_slit_smeared(self):
+        """
+        Check whether the data has slit smearing information
+        :return: True is slit smearing info is present, False otherwise
+        """
+        def _check(v):
+            if (v.__class__ == list or v.__class__ == numpy.ndarray) \
+                and len(v) > 0 and min(v) > 0:
+                return True
+            return False
+        return _check(self.dxl) or _check(self.dxw)
+
+    def clone_without_data(self, length=0, clone=None):
+        """
+        Clone the current object, without copying the data (which
+        will be filled out by a subsequent operation).
+        The data arrays will be initialized to zero.
+
+        :param length: length of the data array to be initialized
+        :param clone: if provided, the data will be copied to clone
+        """
+        from copy import deepcopy
+
+        if clone is None or not issubclass(clone.__class__, Data1D):
+            x = numpy.zeros(length)
+            dx = numpy.zeros(length)
+            y = numpy.zeros(length)
+            dy = numpy.zeros(length)
+            clone = Data1D(x, y, dx=dx, dy=dy)
+
+        clone.title = self.title
+        clone.run = self.run
+        clone.filename = self.filename
+        clone.instrument = self.instrument
+        clone.notes = deepcopy(self.notes)
+        clone.process = deepcopy(self.process)
+        clone.detector = deepcopy(self.detector)
+        clone.sample = deepcopy(self.sample)
+        clone.source = deepcopy(self.source)
+        clone.collimation = deepcopy(self.collimation)
+        clone.trans_spectrum = deepcopy(self.trans_spectrum)
+        clone.meta_data = deepcopy(self.meta_data)
+        clone.errors = deepcopy(self.errors)
+
+        return clone
+
+    def _validity_check(self, other):
+        """
+        Checks that the data lengths are compatible.
+        Checks that the x vectors are compatible.
+        Returns errors vectors equal to original
+        errors vectors if they were present or vectors
+        of zeros when none was found.
+
+        :param other: other data set for operation
+        :return: dy for self, dy for other [numpy arrays]
+        :raise ValueError: when lengths are not compatible
+        """
+        dy_other = None
+        if isinstance(other, Data1D):
+            # Check that data lengths are the same
+            if len(self.x) != len(other.x) or \
+                len(self.y) != len(other.y):
+                msg = "Unable to perform operation: data length are not equal"
+                raise ValueError, msg
+            # Here we could also extrapolate between data points
+            ZERO = 1.0e-12
+            for i in range(len(self.x)):
+                if math.fabs(self.x[i] - other.x[i]) > ZERO:
+                    msg = "Incompatible data sets: x-values do not match"
+                    raise ValueError, msg
+
+            # Check that the other data set has errors, otherwise
+            # create zero vector
+            dy_other = other.dy
+            if other.dy == None or (len(other.dy) != len(other.y)):
+                dy_other = numpy.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)):
+            dy = numpy.zeros(len(self.y))
+
+        return dy, dy_other
+
+    def _perform_operation(self, other, operation):
+        """
+        """
+        # First, check the data compatibility
+        dy, dy_other = self._validity_check(other)
+        result = self.clone_without_data(len(self.x))
+        if self.dxw == None:
+            result.dxw = None
+        else:
+            result.dxw = numpy.zeros(len(self.x))
+        if self.dxl == None:
+            result.dxl = None
+        else:
+            result.dxl = numpy.zeros(len(self.x))
+
+        for i in range(len(self.x)):
+            result.x[i] = self.x[i]
+            if self.dx is not None and len(self.x) == len(self.dx):
+                result.dx[i] = self.dx[i]
+            if self.dxw is not None and len(self.x) == len(self.dxw):
+                result.dxw[i] = self.dxw[i]
+            if self.dxl is not None and len(self.x) == len(self.dxl):
+                result.dxl[i] = self.dxl[i]
+
+            a = Uncertainty(self.y[i], dy[i]**2)
+            if isinstance(other, Data1D):
+                b = Uncertainty(other.y[i], dy_other[i]**2)
+                if other.dx is not None:
+                    result.dx[i] *= self.dx[i]
+                    result.dx[i] += (other.dx[i]**2)
+                    result.dx[i] /= 2
+                    result.dx[i] = math.sqrt(result.dx[i])
+                if result.dxl is not None and other.dxl is not None:
+                    result.dxl[i] *= self.dxl[i]
+                    result.dxl[i] += (other.dxl[i]**2)
+                    result.dxl[i] /= 2
+                    result.dxl[i] = math.sqrt(result.dxl[i])
+            else:
+                b = other
+
+            output = operation(a, b)
+            result.y[i] = output.x
+            result.dy[i] = math.sqrt(math.fabs(output.variance))
+        return result
+
+    def _validity_check_union(self, other):
+        """
+        Checks that the data lengths are compatible.
+        Checks that the x vectors are compatible.
+        Returns errors vectors equal to original
+        errors vectors if they were present or vectors
+        of zeros when none was found.
+
+        :param other: other data set for operation
+        :return: bool
+        :raise ValueError: when data types are not compatible
+        """
+        if not isinstance(other, Data1D):
+            msg = "Unable to perform operation: different types of data set"
+            raise ValueError, msg
+        return True
+
+    def _perform_union(self, other):
+        """
+        """
+        # First, check the data compatibility
+        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:
+            result.dy = None
+        else:
+            result.dy = numpy.zeros(len(self.x) + len(other.x))
+        if self.dx == None or other.dx is None:
+            result.dx = None
+        else:
+            result.dx = numpy.zeros(len(self.x) + len(other.x))
+        if self.dxw == None or other.dxw is None:
+            result.dxw = None
+        else:
+            result.dxw = numpy.zeros(len(self.x) + len(other.x))
+        if self.dxl == None or other.dxl is None:
+            result.dxl = None
+        else:
+            result.dxl = numpy.zeros(len(self.x) + len(other.x))
+
+        result.x = numpy.append(self.x, other.x)
+        #argsorting
+        ind = numpy.argsort(result.x)
+        result.x = result.x[ind]
+        result.y = numpy.append(self.y, other.y)
+        result.y = result.y[ind]
+        if result.dy != None:
+            result.dy = numpy.append(self.dy, other.dy)
+            result.dy = result.dy[ind]
+        if result.dx is not None:
+            result.dx = numpy.append(self.dx, other.dx)
+            result.dx = result.dx[ind]
+        if result.dxw is not None:
+            result.dxw = numpy.append(self.dxw, other.dxw)
+            result.dxw = result.dxw[ind]
+        if result.dxl is not None:
+            result.dxl = numpy.append(self.dxl, other.dxl)
+            result.dxl = result.dxl[ind]
+        return result
+
+
+class Data2D(plottable_2D, DataInfo):
+    """
+    2D data class
+    """
+    ## Units for Q-values
+    Q_unit = '1/A'
+    ## Units for I(Q) values
+    I_unit = '1/cm'
+    ## Vector of Q-values at the center of each bin in x
+    x_bins = None
+    ## Vector of Q-values at the center of each bin in y
+    y_bins = None
+
+    def __init__(self, data=None, err_data=None, qx_data=None,
+                 qy_data=None, q_data=None, mask=None,
+                 dqx_data=None, dqy_data=None):
+        self.y_bins = []
+        self.x_bins = []
+        DataInfo.__init__(self)
+        plottable_2D.__init__(self, data, err_data, qx_data,
+                              qy_data, q_data, mask, dqx_data, dqy_data)
+        if len(self.detector) > 0:
+            raise RuntimeError, "Data2D: Detector bank already filled at init"
+
+    def __str__(self):
+        _str = "%s\n" % DataInfo.__str__(self)
+        _str += "Data:\n"
+        _str += "   Type:         %s\n" % self.__class__.__name__
+        _str += "   X- & Y-axis:  %s\t[%s]\n" % (self._yaxis, self._yunit)
+        _str += "   Z-axis:       %s\t[%s]\n" % (self._zaxis, self._zunit)
+        _str += "   Length:       %g \n" % (len(self.data))
+        return _str
+
+    def clone_without_data(self, length=0, clone=None):
+        """
+        Clone the current object, without copying the data (which
+        will be filled out by a subsequent operation).
+        The data arrays will be initialized to zero.
+
+        :param length: length of the data array to be initialized
+        :param clone: if provided, the data will be copied to clone
+        """
+        from copy import deepcopy
+
+        if clone is None or not issubclass(clone.__class__, Data2D):
+            data = numpy.zeros(length)
+            err_data = numpy.zeros(length)
+            qx_data = numpy.zeros(length)
+            qy_data = numpy.zeros(length)
+            q_data = numpy.zeros(length)
+            mask = numpy.zeros(length)
+            dqx_data = None
+            dqy_data = None
+            clone = Data2D(data=data, err_data=err_data,
+                           qx_data=qx_data, qy_data=qy_data,
+                           q_data=q_data, mask=mask)
+
+        clone.title = self.title
+        clone.run = self.run
+        clone.filename = self.filename
+        clone.instrument = self.instrument
+        clone.notes = deepcopy(self.notes)
+        clone.process = deepcopy(self.process)
+        clone.detector = deepcopy(self.detector)
+        clone.sample = deepcopy(self.sample)
+        clone.source = deepcopy(self.source)
+        clone.collimation = deepcopy(self.collimation)
+        clone.meta_data = deepcopy(self.meta_data)
+        clone.errors = deepcopy(self.errors)
+
+        return clone
+
+    def _validity_check(self, other):
+        """
+        Checks that the data lengths are compatible.
+        Checks that the x vectors are compatible.
+        Returns errors vectors equal to original
+        errors vectors if they were present or vectors
+        of zeros when none was found.
+
+        :param other: other data set for operation
+        :return: dy for self, dy for other [numpy arrays]
+        :raise ValueError: when lengths are not compatible
+        """
+        err_other = None
+        if isinstance(other, Data2D):
+            # Check that data lengths are the same
+            if len(self.data) != len(other.data) or \
+                len(self.qx_data) != len(other.qx_data) or \
+                len(self.qy_data) != len(other.qy_data):
+                msg = "Unable to perform operation: data length are not equal"
+                raise ValueError, msg
+            for ind in range(len(self.data)):
+                if self.qx_data[ind] != other.qx_data[ind]:
+                    msg = "Incompatible data sets: qx-values do not match"
+                    raise ValueError, msg
+                if self.qy_data[ind] != other.qy_data[ind]:
+                    msg = "Incompatible data sets: qy-values do not match"
+                    raise ValueError, msg
+
+            # Check that the scales match
+            err_other = other.err_data
+            if other.err_data == None or \
+                (len(other.err_data) != len(other.data)):
+                err_other = numpy.zeros(len(other.data))
+
+        # Check that we have errors, otherwise create zero vector
+        err = self.err_data
+        if self.err_data == None or \
+            (len(self.err_data) != len(self.data)):
+            err = numpy.zeros(len(other.data))
+        return err, err_other
+
+    def _perform_operation(self, other, operation):
+        """
+        Perform 2D operations between data sets
+
+        :param other: other data set
+        :param operation: function defining the operation
+        """
+        # First, check the data compatibility
+        dy, dy_other = self._validity_check(other)
+        result = self.clone_without_data(numpy.size(self.data))
+        if self.dqx_data == None or self.dqy_data == None:
+            result.dqx_data = None
+            result.dqy_data = None
+        else:
+            result.dqx_data = numpy.zeros(len(self.data))
+            result.dqy_data = numpy.zeros(len(self.data))
+        for i in range(numpy.size(self.data)):
+            result.data[i] = self.data[i]
+            if self.err_data is not None and \
+                numpy.size(self.data) == numpy.size(self.err_data):
+                result.err_data[i] = self.err_data[i]
+            if self.dqx_data is not None:
+                result.dqx_data[i] = self.dqx_data[i]
+            if self.dqy_data is not None:
+                result.dqy_data[i] = self.dqy_data[i]
+            result.qx_data[i] = self.qx_data[i]
+            result.qy_data[i] = self.qy_data[i]
+            result.q_data[i] = self.q_data[i]
+            result.mask[i] = self.mask[i]
+
+            a = Uncertainty(self.data[i], dy[i]**2)
+            if isinstance(other, Data2D):
+                b = Uncertainty(other.data[i], dy_other[i]**2)
+                if other.dqx_data is not None and \
+                        result.dqx_data is not None:
+                    result.dqx_data[i] *= self.dqx_data[i]
+                    result.dqx_data[i] += (other.dqx_data[i]**2)
+                    result.dqx_data[i] /= 2
+                    result.dqx_data[i] = math.sqrt(result.dqx_data[i])
+                if other.dqy_data is not None and \
+                        result.dqy_data is not None:
+                    result.dqy_data[i] *= self.dqy_data[i]
+                    result.dqy_data[i] += (other.dqy_data[i]**2)
+                    result.dqy_data[i] /= 2
+                    result.dqy_data[i] = math.sqrt(result.dqy_data[i])
+            else:
+                b = other
+            output = operation(a, b)
+            result.data[i] = output.x
+            result.err_data[i] = math.sqrt(math.fabs(output.variance))
+        return result
+
+    def _validity_check_union(self, other):
+        """
+        Checks that the data lengths are compatible.
+        Checks that the x vectors are compatible.
+        Returns errors vectors equal to original
+        errors vectors if they were present or vectors
+        of zeros when none was found.
+
+        :param other: other data set for operation
+        :return: bool
+        :raise ValueError: when data types are not compatible
+        """
+        if not isinstance(other, Data2D):
+            msg = "Unable to perform operation: different types of data set"
+            raise ValueError, msg
+        return True
+
+    def _perform_union(self, other):
+        """
+        Perform 2D operations between data sets
+
+        :param other: other data set
+        :param operation: function defining the operation
+        """
+        # First, check the data compatibility
+        self._validity_check_union(other)
+        result = self.clone_without_data(numpy.size(self.data) + \
+                                         numpy.size(other.data))
+        result.xmin = self.xmin
+        result.xmax = self.xmax
+        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:
+            result.dqx_data = None
+            result.dqy_data = None
+        else:
+            result.dqx_data = numpy.zeros(len(self.data) + \
+                                         numpy.size(other.data))
+            result.dqy_data = numpy.zeros(len(self.data) + \
+                                         numpy.size(other.data))
+
+        result.data = numpy.append(self.data, other.data)
+        result.qx_data = numpy.append(self.qx_data, other.qx_data)
+        result.qy_data = numpy.append(self.qy_data, other.qy_data)
+        result.q_data = numpy.append(self.q_data, other.q_data)
+        result.mask = numpy.append(self.mask, other.mask)
+        if result.err_data is not None:
+            result.err_data = numpy.append(self.err_data, other.err_data) 
+        if self.dqx_data is not None:
+            result.dqx_data = numpy.append(self.dqx_data, other.dqx_data)
+        if self.dqy_data is not None:
+            result.dqy_data = numpy.append(self.dqy_data, other.dqy_data)
+
+        return result
Index: src/sas/sascalc/dataloader/loader.py
===================================================================
--- src/sas/sascalc/dataloader/loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,385 @@
+"""
+    File handler to support different file extensions.
+    Uses reflectometry's registry utility.
+
+    The default readers are found in the 'readers' sub-module
+    and registered by default at initialization time.
+
+    To add a new default reader, one must register it in
+    the register_readers method found in readers/__init__.py.
+
+    A utility method (find_plugins) is available to inspect
+    a directory (for instance, a user plug-in directory) and
+    look for new readers/writers.
+"""
+#####################################################################
+#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.
+#See the license text in license.txt
+#copyright 2008, University of Tennessee
+######################################################################
+
+import os
+import sys
+import logging
+import time
+from zipfile import ZipFile
+from sas.sascalc.data_util.registry import ExtensionRegistry
+# Default readers are defined in the readers sub-module
+import readers
+from readers import ascii_reader
+from readers import cansas_reader
+
+class Registry(ExtensionRegistry):
+    """
+    Registry class for file format extensions.
+    Readers and writers are supported.
+    """
+
+    def __init__(self):
+        super(Registry, self).__init__()
+
+        ## Writers
+        self.writers = {}
+
+        ## List of wildcards
+        self.wildcards = ['All (*.*)|*.*']
+
+        ## Creation time, for testing
+        self._created = time.time()
+
+        # Register default readers
+        readers.read_associations(self)
+
+    def load(self, path, format=None):
+        """
+        Call the loader for the file type of path.
+
+        :param path: file path
+        :param format: explicit extension, to force the use
+            of a particular reader
+
+        Defaults to the ascii (multi-column) reader
+        if no reader was registered for the file's
+        extension.
+        """
+        try:
+            return super(Registry, self).load(path, format=format)
+        except:
+            try:
+                # No reader was found. Default to the ascii reader.
+                ascii_loader = ascii_reader.Reader()
+                return ascii_loader.read(path)
+            except:
+                cansas_loader = cansas_reader.Reader()
+                return cansas_loader.read(path)
+
+    def find_plugins(self, dir):
+        """
+        Find readers in a given directory. This method
+        can be used to inspect user plug-in directories to
+        find new readers/writers.
+
+        :param dir: directory to search into
+        :return: number of readers found
+        """
+        readers_found = 0
+        temp_path = os.path.abspath(dir)
+        if not os.path.isdir(temp_path):
+            temp_path = os.path.join(os.getcwd(), dir)
+        if not os.path.isdir(temp_path):
+            temp_path = os.path.join(os.path.dirname(__file__), dir)
+        if not os.path.isdir(temp_path):
+            temp_path = os.path.join(os.path.dirname(sys.path[0]), dir)
+
+        dir = temp_path
+        # Check whether the directory exists
+        if not os.path.isdir(dir):
+            msg = "DataLoader couldn't locate DataLoader plugin folder."
+            msg += """ "%s" does not exist""" % dir
+            logging.warning(msg)
+            return readers_found
+
+        for item in os.listdir(dir):
+            full_path = os.path.join(dir, item)
+            if os.path.isfile(full_path):
+
+                # Process python files
+                if item.endswith('.py'):
+                    toks = os.path.splitext(os.path.basename(item))
+                    try:
+                        sys.path.insert(0, os.path.abspath(dir))
+                        module = __import__(toks[0], globals(), locals())
+                        if self._identify_plugin(module):
+                            readers_found += 1
+                    except:
+                        msg = "Loader: Error importing "
+                        msg += "%s\n  %s" % (item, sys.exc_value)
+                        logging.error(msg)
+
+                # Process zip files
+                elif item.endswith('.zip'):
+                    try:
+                        # Find the modules in the zip file
+                        zfile = ZipFile(item)
+                        nlist = zfile.namelist()
+
+                        sys.path.insert(0, item)
+                        for mfile in nlist:
+                            try:
+                                # Change OS path to python path
+                                fullname = mfile.replace('/', '.')
+                                fullname = os.path.splitext(fullname)[0]
+                                module = __import__(fullname, globals(),
+                                                    locals(), [""])
+                                if self._identify_plugin(module):
+                                    readers_found += 1
+                            except:
+                                msg = "Loader: Error importing"
+                                msg += " %s\n  %s" % (mfile, sys.exc_value)
+                                logging.error(msg)
+
+                    except:
+                        msg = "Loader: Error importing "
+                        msg += " %s\n  %s" % (item, sys.exc_value)
+                        logging.error(msg)
+
+        return readers_found
+
+    def associate_file_type(self, ext, module):
+        """
+        Look into a module to find whether it contains a
+        Reader class. If so, APPEND it to readers and (potentially)
+        to the list of writers for the given extension
+
+        :param ext: file extension [string]
+        :param module: module object
+        """
+        reader_found = False
+
+        if hasattr(module, "Reader"):
+            try:
+                # Find supported extensions
+                loader = module.Reader()
+                if ext not in self.loaders:
+                    self.loaders[ext] = []
+                # Append the new reader to the list
+                self.loaders[ext].append(loader.read)
+
+                reader_found = True
+
+                # Keep track of wildcards
+                type_name = module.__name__
+                if hasattr(loader, 'type_name'):
+                    type_name = loader.type_name
+
+                wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
+                                                ext.lower())
+                if wcard not in self.wildcards:
+                    self.wildcards.append(wcard)
+
+                # Check whether writing is supported
+                if hasattr(loader, 'write'):
+                    if ext not in self.writers:
+                        self.writers[ext] = []
+                    # Append the new writer to the list
+                    self.writers[ext].append(loader.write)
+
+            except:
+                msg = "Loader: Error accessing"
+                msg += " Reader in %s\n  %s" % (module.__name__, sys.exc_value)
+                logging.error(msg)
+        return reader_found
+
+    def associate_file_reader(self, ext, loader):
+        """
+        Append a reader object to readers
+
+        :param ext: file extension [string]
+        :param module: reader object
+        """
+        reader_found = False
+
+        try:
+            # Find supported extensions
+            if ext not in self.loaders:
+                self.loaders[ext] = []
+            # Append the new reader to the list
+            self.loaders[ext].append(loader.read)
+
+            reader_found = True
+
+            # Keep track of wildcards
+            if hasattr(loader, 'type_name'):
+                type_name = loader.type_name
+
+                wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
+                                                ext.lower())
+                if wcard not in self.wildcards:
+                    self.wildcards.append(wcard)
+
+        except:
+            msg = "Loader: Error accessing Reader "
+            msg += "in %s\n  %s" % (loader.__name__, sys.exc_value)
+            logging.error(msg)
+        return reader_found
+
+    def _identify_plugin(self, module):
+        """
+        Look into a module to find whether it contains a
+        Reader class. If so, add it to readers and (potentially)
+        to the list of writers.
+        :param module: module object
+
+        """
+        reader_found = False
+
+        if hasattr(module, "Reader"):
+            try:
+                # Find supported extensions
+                loader = module.Reader()
+                for ext in loader.ext:
+                    if ext not in self.loaders:
+                        self.loaders[ext] = []
+                    # When finding a reader at run time,
+                    # treat this reader as the new default
+                    self.loaders[ext].insert(0, loader.read)
+
+                    reader_found = True
+
+                    # Keep track of wildcards
+                    type_name = module.__name__
+                    if hasattr(loader, 'type_name'):
+                        type_name = loader.type_name
+                    wcard = "%s files (*%s)|*%s" % (type_name, ext.lower(),
+                                                    ext.lower())
+                    if wcard not in self.wildcards:
+                        self.wildcards.append(wcard)
+
+                # Check whether writing is supported
+                if hasattr(loader, 'write'):
+                    for ext in loader.ext:
+                        if ext not in self.writers:
+                            self.writers[ext] = []
+                        self.writers[ext].insert(0, loader.write)
+
+            except:
+                msg = "Loader: Error accessing Reader"
+                msg += " in %s\n  %s" % (module.__name__, sys.exc_value)
+                logging.error(msg)
+        return reader_found
+
+    def lookup_writers(self, path):
+        """
+        :return: the loader associated with the file type of path.
+        :Raises ValueError: if file type is not known.
+        """
+        # Find matching extensions
+        extlist = [ext for ext in self.extensions() if path.endswith(ext)]
+        # Sort matching extensions by decreasing order of length
+        extlist.sort(lambda a, b: len(a) < len(b))
+        # Combine loaders for matching extensions into one big list
+        writers = []
+        for L in [self.writers[ext] for ext in extlist]:
+            writers.extend(L)
+        # Remove duplicates if they exist
+        if len(writers) != len(set(writers)):
+            result = []
+            for L in writers:
+                if L not in result:
+                    result.append(L)
+            writers = L
+        # Raise an error if there are no matching extensions
+        if len(writers) == 0:
+            raise ValueError, "Unknown file type for " + path
+        # All done
+        return writers
+
+    def save(self, path, data, format=None):
+        """
+        Call the writer for the file type of path.
+
+        Raises ValueError if no writer is available.
+        Raises KeyError if format is not available.
+        May raise a writer-defined exception if writer fails.
+        """
+        if format is None:
+            writers = self.lookup_writers(path)
+        else:
+            writers = self.writers[format]
+        for fn in writers:
+            try:
+                return fn(path, data)
+            except:
+                pass  # give other loaders a chance to succeed
+        # If we get here it is because all loaders failed
+        raise  # reraises last exception
+
+
+class Loader(object):
+    """
+    Utility class to use the Registry as a singleton.
+    """
+    ## Registry instance
+    __registry = Registry()
+
+    def associate_file_type(self, ext, module):
+        """
+        Look into a module to find whether it contains a
+        Reader class. If so, append it to readers and (potentially)
+        to the list of writers for the given extension
+
+        :param ext: file extension [string]
+        :param module: module object
+        """
+        return self.__registry.associate_file_type(ext, module)
+
+    def associate_file_reader(self, ext, loader):
+        """
+        Append a reader object to readers
+
+        :param ext: file extension [string]
+        :param module: reader object
+        """
+        return self.__registry.associate_file_reader(ext, loader)
+
+    def load(self, file, format=None):
+        """
+        Load a file
+
+        :param file: file name (path)
+        :param format: specified format to use (optional)
+        :return: DataInfo object
+        """
+        return self.__registry.load(file, format)
+
+    def save(self, file, data, format):
+        """
+        Save a DataInfo object to file
+        :param file: file name (path)
+        :param data: DataInfo object
+        :param format: format to write the data in
+        """
+        return self.__registry.save(file, data, format)
+
+    def _get_registry_creation_time(self):
+        """
+        Internal method used to test the uniqueness
+        of the registry object
+        """
+        return self.__registry._created
+
+    def find_plugins(self, directory):
+        """
+        Find plugins in a given directory
+
+        :param dir: directory to look into to find new readers/writers
+        """
+        return self.__registry.find_plugins(directory)
+
+    def get_wildcards(self):
+        """
+        Return the list of wildcards
+        """
+        return self.__registry.wildcards
Index: src/sas/sascalc/dataloader/manipulations.py
===================================================================
--- src/sas/sascalc/dataloader/manipulations.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/manipulations.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1140 @@
+"""
+Data manipulations for 2D data sets.
+Using the meta data information, various types of averaging
+are performed in Q-space
+"""
+#####################################################################
+#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.
+#See the license text in license.txt
+#copyright 2008, University of Tennessee
+######################################################################
+
+#TODO: copy the meta data from the 2D object to the resulting 1D object
+import math
+import numpy
+
+#from data_info import plottable_2D
+from data_info import Data1D
+
+
+def get_q(dx, dy, det_dist, wavelength):
+    """
+    :param dx: x-distance from beam center [mm]
+    :param dy: y-distance from beam center [mm]
+    :return: q-value at the given position
+    """
+    # Distance from beam center in the plane of detector
+    plane_dist = math.sqrt(dx * dx + dy * dy)
+    # Half of the scattering angle
+    theta = 0.5 * math.atan(plane_dist / det_dist)
+    return (4.0 * math.pi / wavelength) * math.sin(theta)
+
+
+def get_q_compo(dx, dy, det_dist, wavelength, compo=None):
+    """
+    This reduces tiny error at very large q.
+    Implementation of this func is not started yet.<--ToDo
+    """
+    if dy == 0:
+        if dx >= 0:
+            angle_xy = 0
+        else:
+            angle_xy = math.pi
+    else:
+        angle_xy = math.atan(dx / dy)
+
+    if compo == "x":
+        out = get_q(dx, dy, det_dist, wavelength) * math.cos(angle_xy)
+    elif compo == "y":
+        out = get_q(dx, dy, det_dist, wavelength) * math.sin(angle_xy)
+    else:
+        out = get_q(dx, dy, det_dist, wavelength)
+    return out
+
+
+def flip_phi(phi):
+    """
+    Correct phi to within the 0 <= to <= 2pi range
+
+    :return: phi in >=0 and <=2Pi
+    """
+    Pi = math.pi
+    if phi < 0:
+        phi_out = phi + (2 * Pi)
+    elif phi > (2 * Pi):
+        phi_out = phi - (2 * Pi)
+    else:
+        phi_out = phi
+    return phi_out
+
+
+def reader2D_converter(data2d=None):
+    """
+    convert old 2d format opened by IhorReader or danse_reader
+    to new Data2D format
+
+    :param data2d: 2d array of Data2D object
+    :return: 1d arrays of Data2D object
+
+    """
+    if data2d.data == None or data2d.x_bins == None or data2d.y_bins == None:
+        raise ValueError, "Can't convert this data: data=None..."
+    new_x = numpy.tile(data2d.x_bins, (len(data2d.y_bins), 1))
+    new_y = numpy.tile(data2d.y_bins, (len(data2d.x_bins), 1))
+    new_y = new_y.swapaxes(0, 1)
+
+    new_data = data2d.data.flatten()
+    qx_data = new_x.flatten()
+    qy_data = new_y.flatten()
+    q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
+    if data2d.err_data == None or numpy.any(data2d.err_data <= 0):
+        new_err_data = numpy.sqrt(numpy.abs(new_data))
+    else:
+        new_err_data = data2d.err_data.flatten()
+    mask = numpy.ones(len(new_data), dtype=bool)
+
+    #TODO: make sense of the following two lines...
+    #from sas.sascalc.dataloader.data_info import Data2D
+    #output = Data2D()
+    output = data2d
+    output.data = new_data
+    output.err_data = new_err_data
+    output.qx_data = qx_data
+    output.qy_data = qy_data
+    output.q_data = q_data
+    output.mask = mask
+
+    return output
+
+
+class _Slab(object):
+    """
+    Compute average I(Q) for a region of interest
+    """
+    def __init__(self, x_min=0.0, x_max=0.0, y_min=0.0,
+                 y_max=0.0, bin_width=0.001):
+        # Minimum Qx value [A-1]
+        self.x_min = x_min
+        # Maximum Qx value [A-1]
+        self.x_max = x_max
+        # Minimum Qy value [A-1]
+        self.y_min = y_min
+        # Maximum Qy value [A-1]
+        self.y_max = y_max
+        # Bin width (step size) [A-1]
+        self.bin_width = bin_width
+        # If True, I(|Q|) will be return, otherwise,
+        # negative q-values are allowed
+        self.fold = False
+
+    def __call__(self, data2D):
+        return NotImplemented
+
+    def _avg(self, data2D, maj):
+        """
+        Compute average I(Q_maj) for a region of interest.
+        The major axis is defined as the axis of Q_maj.
+        The minor axis is the axis that we average over.
+
+        :param data2D: Data2D object
+        :param maj_min: min value on the major axis
+        :return: Data1D object
+        """
+        if len(data2D.detector) != 1:
+            msg = "_Slab._avg: invalid number of "
+            msg += " detectors: %g" % len(data2D.detector)
+            raise RuntimeError, msg
+
+        # Get data
+        data = data2D.data[numpy.isfinite(data2D.data)]
+        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
+        qx_data = data2D.qx_data[numpy.isfinite(data2D.data)]
+        qy_data = data2D.qy_data[numpy.isfinite(data2D.data)]
+
+        # Build array of Q intervals
+        if maj == 'x':
+            if self.fold:
+                x_min = 0
+            else:
+                x_min = self.x_min
+            nbins = int(math.ceil((self.x_max - x_min) / self.bin_width))
+        elif maj == 'y':
+            if self.fold:
+                y_min = 0
+            else:
+                y_min = self.y_min
+            nbins = int(math.ceil((self.y_max - y_min) / self.bin_width))
+        else:
+            raise RuntimeError, "_Slab._avg: unrecognized axis %s" % str(maj)
+
+        x = numpy.zeros(nbins)
+        y = numpy.zeros(nbins)
+        err_y = numpy.zeros(nbins)
+        y_counts = numpy.zeros(nbins)
+
+        # Average pixelsize in q space
+        for npts in range(len(data)):
+            # default frac
+            frac_x = 0
+            frac_y = 0
+            # get ROI
+            if self.x_min <= qx_data[npts] and self.x_max > qx_data[npts]:
+                frac_x = 1
+            if self.y_min <= qy_data[npts] and self.y_max > qy_data[npts]:
+                frac_y = 1
+            frac = frac_x * frac_y
+
+            if frac == 0:
+                continue
+            # binning: find axis of q
+            if maj == 'x':
+                q_value = qx_data[npts]
+                min_value = x_min
+            if maj == 'y':
+                q_value = qy_data[npts]
+                min_value = y_min
+            if self.fold and q_value < 0:
+                q_value = -q_value
+            # bin
+            i_q = int(math.ceil((q_value - min_value) / self.bin_width)) - 1
+
+            # skip outside of max bins
+            if i_q < 0 or i_q >= nbins:
+                continue
+
+            #TODO: find better definition of x[i_q] based on q_data
+            # min_value + (i_q + 1) * self.bin_width / 2.0
+            x[i_q] += frac * q_value
+            y[i_q] += frac * data[npts]
+
+            if err_data == None or err_data[npts] == 0.0:
+                if data[npts] < 0:
+                    data[npts] = -data[npts]
+                err_y[i_q] += frac * frac * data[npts]
+            else:
+                err_y[i_q] += frac * frac * err_data[npts] * err_data[npts]
+            y_counts[i_q] += frac
+
+        # Average the sums
+        for n in range(nbins):
+            err_y[n] = math.sqrt(err_y[n])
+
+        err_y = err_y / y_counts
+        y = y / y_counts
+        x = x / y_counts
+        idx = (numpy.isfinite(y) & numpy.isfinite(x))
+
+        if not idx.any():
+            msg = "Average Error: No points inside ROI to average..."
+            raise ValueError, msg
+        return Data1D(x=x[idx], y=y[idx], dy=err_y[idx])
+
+
+class SlabY(_Slab):
+    """
+    Compute average I(Qy) for a region of interest
+    """
+    def __call__(self, data2D):
+        """
+        Compute average I(Qy) for a region of interest
+
+        :param data2D: Data2D object
+        :return: Data1D object
+        """
+        return self._avg(data2D, 'y')
+
+
+class SlabX(_Slab):
+    """
+    Compute average I(Qx) for a region of interest
+    """
+    def __call__(self, data2D):
+        """
+        Compute average I(Qx) for a region of interest
+        :param data2D: Data2D object
+        :return: Data1D object
+        """
+        return self._avg(data2D, 'x')
+
+
+class Boxsum(object):
+    """
+    Perform the sum of counts in a 2D region of interest.
+    """
+    def __init__(self, x_min=0.0, x_max=0.0, y_min=0.0, y_max=0.0):
+        # Minimum Qx value [A-1]
+        self.x_min = x_min
+        # Maximum Qx value [A-1]
+        self.x_max = x_max
+        # Minimum Qy value [A-1]
+        self.y_min = y_min
+        # Maximum Qy value [A-1]
+        self.y_max = y_max
+
+    def __call__(self, data2D):
+        """
+        Perform the sum in the region of interest
+
+        :param data2D: Data2D object
+        :return: number of counts, error on number of counts,
+            number of points summed
+        """
+        y, err_y, y_counts = self._sum(data2D)
+
+        # Average the sums
+        counts = 0 if y_counts == 0 else y
+        error = 0 if y_counts == 0 else math.sqrt(err_y)
+
+        # Added y_counts to return, SMK & PDB, 04/03/2013
+        return counts, error, y_counts
+
+    def _sum(self, data2D):
+        """
+        Perform the sum in the region of interest
+
+        :param data2D: Data2D object
+        :return: number of counts,
+            error on number of counts, number of entries summed
+        """
+        if len(data2D.detector) != 1:
+            msg = "Circular averaging: invalid number "
+            msg += "of detectors: %g" % len(data2D.detector)
+            raise RuntimeError, msg
+        # Get data
+        data = data2D.data[numpy.isfinite(data2D.data)]
+        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
+        qx_data = data2D.qx_data[numpy.isfinite(data2D.data)]
+        qy_data = data2D.qy_data[numpy.isfinite(data2D.data)]
+
+        y = 0.0
+        err_y = 0.0
+        y_counts = 0.0
+
+        # Average pixelsize in q space
+        for npts in range(len(data)):
+            # default frac
+            frac_x = 0
+            frac_y = 0
+
+            # get min and max at each points
+            qx = qx_data[npts]
+            qy = qy_data[npts]
+
+            # get the ROI
+            if self.x_min <= qx and self.x_max > qx:
+                frac_x = 1
+            if self.y_min <= qy and self.y_max > qy:
+                frac_y = 1
+            #Find the fraction along each directions
+            frac = frac_x * frac_y
+            if frac == 0:
+                continue
+            y += frac * data[npts]
+            if err_data == None or err_data[npts] == 0.0:
+                if data[npts] < 0:
+                    data[npts] = -data[npts]
+                err_y += frac * frac * data[npts]
+            else:
+                err_y += frac * frac * err_data[npts] * err_data[npts]
+            y_counts += frac
+        return y, err_y, y_counts
+
+
+class Boxavg(Boxsum):
+    """
+    Perform the average of counts in a 2D region of interest.
+    """
+    def __init__(self, x_min=0.0, x_max=0.0, y_min=0.0, y_max=0.0):
+        super(Boxavg, self).__init__(x_min=x_min, x_max=x_max,
+                                     y_min=y_min, y_max=y_max)
+
+    def __call__(self, data2D):
+        """
+        Perform the sum in the region of interest
+
+        :param data2D: Data2D object
+        :return: average counts, error on average counts
+
+        """
+        y, err_y, y_counts = self._sum(data2D)
+
+        # Average the sums
+        counts = 0 if y_counts == 0 else y / y_counts
+        error = 0 if y_counts == 0 else math.sqrt(err_y) / y_counts
+
+        return counts, error
+
+
+def get_pixel_fraction_square(x, xmin, xmax):
+    """
+    Return the fraction of the length
+    from xmin to x.::
+
+           A            B
+       +-----------+---------+
+       xmin        x         xmax
+
+    :param x: x-value
+    :param xmin: minimum x for the length considered
+    :param xmax: minimum x for the length considered
+    :return: (x-xmin)/(xmax-xmin) when xmin < x < xmax
+
+    """
+    if x <= xmin:
+        return 0.0
+    if x > xmin and x < xmax:
+        return (x - xmin) / (xmax - xmin)
+    else:
+        return 1.0
+
+
+class CircularAverage(object):
+    """
+    Perform circular averaging on 2D data
+
+    The data returned is the distribution of counts
+    as a function of Q
+    """
+    def __init__(self, r_min=0.0, r_max=0.0, bin_width=0.0005):
+        # Minimum radius included in the average [A-1]
+        self.r_min = r_min
+        # Maximum radius included in the average [A-1]
+        self.r_max = r_max
+        # Bin width (step size) [A-1]
+        self.bin_width = bin_width
+
+    def __call__(self, data2D, ismask=False):
+        """
+        Perform circular averaging on the data
+
+        :param data2D: Data2D object
+        :return: Data1D object
+        """
+        # Get data W/ finite values
+        data = data2D.data[numpy.isfinite(data2D.data)]
+        q_data = data2D.q_data[numpy.isfinite(data2D.data)]
+        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
+        mask_data = data2D.mask[numpy.isfinite(data2D.data)]
+
+        dq_data = None
+
+        # Get the dq for resolution averaging
+        if data2D.dqx_data != None and data2D.dqy_data != None:
+            # The pinholes and det. pix contribution present
+            # in both direction of the 2D which must be subtracted when
+            # converting to 1D: dq_overlap should calculated ideally at
+            # q = 0. Note This method works on only pinhole geometry.
+            # Extrapolate dqx(r) and dqy(phi) at q = 0, and take an average.
+            z_max = max(data2D.q_data)
+            z_min = min(data2D.q_data)
+            x_max = data2D.dqx_data[data2D.q_data[z_max]]
+            x_min = data2D.dqx_data[data2D.q_data[z_min]]
+            y_max = data2D.dqy_data[data2D.q_data[z_max]]
+            y_min = data2D.dqy_data[data2D.q_data[z_min]]
+            # Find qdx at q = 0
+            dq_overlap_x = (x_min * z_max - x_max * z_min) / (z_max - z_min)
+            # when extrapolation goes wrong
+            if dq_overlap_x > min(data2D.dqx_data):
+                dq_overlap_x = min(data2D.dqx_data)
+            dq_overlap_x *= dq_overlap_x
+            # Find qdx at q = 0
+            dq_overlap_y = (y_min * z_max - y_max * z_min) / (z_max - z_min)
+            # when extrapolation goes wrong
+            if dq_overlap_y > min(data2D.dqy_data):
+                dq_overlap_y = min(data2D.dqy_data)
+            # get dq at q=0.
+            dq_overlap_y *= dq_overlap_y
+
+            dq_overlap = numpy.sqrt((dq_overlap_x + dq_overlap_y) / 2.0)
+            # Final protection of dq
+            if dq_overlap < 0:
+                dq_overlap = y_min
+            dqx_data = data2D.dqx_data[numpy.isfinite(data2D.data)]
+            dqy_data = data2D.dqy_data[numpy.isfinite(data2D.data)] - dq_overlap
+            # def; dqx_data = dq_r dqy_data = dq_phi
+            # Convert dq 2D to 1D here
+            dqx = dqx_data * dqx_data
+            dqy = dqy_data * dqy_data
+            dq_data = numpy.add(dqx, dqy)
+            dq_data = numpy.sqrt(dq_data)
+
+        #q_data_max = numpy.max(q_data)
+        if len(data2D.q_data) == None:
+            msg = "Circular averaging: invalid q_data: %g" % data2D.q_data
+            raise RuntimeError, msg
+
+        # Build array of Q intervals
+        nbins = int(math.ceil((self.r_max - self.r_min) / self.bin_width))
+
+        x = numpy.zeros(nbins)
+        y = numpy.zeros(nbins)
+        err_y = numpy.zeros(nbins)
+        err_x = numpy.zeros(nbins)
+        y_counts = numpy.zeros(nbins)
+
+        for npt in range(len(data)):
+
+            if ismask and not mask_data[npt]:
+                continue
+
+            frac = 0
+
+            # q-value at the pixel (j,i)
+            q_value = q_data[npt]
+            data_n = data[npt]
+
+            ## No need to calculate the frac when all data are within range
+            if self.r_min >= self.r_max:
+                raise ValueError, "Limit Error: min > max"
+
+            if self.r_min <= q_value and q_value <= self.r_max:
+                frac = 1
+            if frac == 0:
+                continue
+            i_q = int(math.floor((q_value - self.r_min) / self.bin_width))
+
+            # Take care of the edge case at phi = 2pi.
+            if i_q == nbins:
+                i_q = nbins - 1
+            y[i_q] += frac * data_n
+            # 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 data_n < 0:
+                    data_n = -data_n
+                err_y[i_q] += frac * frac * data_n
+            else:
+                err_y[i_q] += frac * frac * err_data[npt] * err_data[npt]
+            if dq_data != None:
+                # To be consistent with dq calculation in 1d reduction,
+                # we need just the averages (not quadratures) because
+                # it should not depend on the number of the q points
+                # in the qr bins.
+                err_x[i_q] += frac * dq_data[npt]
+            else:
+                err_x = None
+            y_counts[i_q] += frac
+
+        # Average the sums
+        for n in range(nbins):
+            if err_y[n] < 0:
+                err_y[n] = -err_y[n]
+            err_y[n] = math.sqrt(err_y[n])
+            #if err_x != None:
+            #    err_x[n] = math.sqrt(err_x[n])
+
+        err_y = err_y / y_counts
+        err_y[err_y == 0] = numpy.average(err_y)
+        y = y / y_counts
+        x = x / y_counts
+        idx = (numpy.isfinite(y)) & (numpy.isfinite(x))
+
+        if err_x != None:
+            d_x = err_x[idx] / y_counts[idx]
+        else:
+            d_x = None
+
+        if not idx.any():
+            msg = "Average Error: No points inside ROI to average..."
+            raise ValueError, msg
+
+        return Data1D(x=x[idx], y=y[idx], dy=err_y[idx], dx=d_x)
+
+
+class Ring(object):
+    """
+    Defines a ring on a 2D data set.
+    The ring is defined by r_min, r_max, and
+    the position of the center of the ring.
+
+    The data returned is the distribution of counts
+    around the ring as a function of phi.
+
+    Phi_min and phi_max should be defined between 0 and 2*pi
+    in anti-clockwise starting from the x- axis on the left-hand side
+    """
+    #Todo: remove center.
+    def __init__(self, r_min=0, r_max=0, center_x=0, center_y=0, nbins=36):
+        # Minimum radius
+        self.r_min = r_min
+        # Maximum radius
+        self.r_max = r_max
+        # Center of the ring in x
+        self.center_x = center_x
+        # Center of the ring in y
+        self.center_y = center_y
+        # Number of angular bins
+        self.nbins_phi = nbins
+
+
+    def __call__(self, data2D):
+        """
+        Apply the ring to the data set.
+        Returns the angular distribution for a given q range
+
+        :param data2D: Data2D object
+
+        :return: Data1D object
+        """
+        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
+            raise RuntimeError, "Ring averaging only take plottable_2D objects"
+
+        Pi = math.pi
+
+        # Get data
+        data = data2D.data[numpy.isfinite(data2D.data)]
+        q_data = data2D.q_data[numpy.isfinite(data2D.data)]
+        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
+        qx_data = data2D.qx_data[numpy.isfinite(data2D.data)]
+        qy_data = data2D.qy_data[numpy.isfinite(data2D.data)]
+
+        # Set space for 1d outputs
+        phi_bins = numpy.zeros(self.nbins_phi)
+        phi_counts = numpy.zeros(self.nbins_phi)
+        phi_values = numpy.zeros(self.nbins_phi)
+        phi_err = numpy.zeros(self.nbins_phi)
+
+        # Shift to apply to calculated phi values in order
+        # to center first bin at zero
+        phi_shift = Pi / self.nbins_phi
+
+        for npt in range(len(data)):
+            frac = 0
+            # q-value at the point (npt)
+            q_value = q_data[npt]
+            data_n = data[npt]
+
+            # phi-value at the point (npt)
+            phi_value = math.atan2(qy_data[npt], qx_data[npt]) + Pi
+
+            if self.r_min <= q_value and q_value <= self.r_max:
+                frac = 1
+            if frac == 0:
+                continue
+            # binning
+            i_phi = int(math.floor((self.nbins_phi) * \
+                                   (phi_value + phi_shift) / (2 * Pi)))
+
+            # Take care of the edge case at phi = 2pi.
+            if i_phi >= self.nbins_phi:
+                i_phi = 0
+            phi_bins[i_phi] += frac * data[npt]
+
+            if err_data == None or err_data[npt] == 0.0:
+                if data_n < 0:
+                    data_n = -data_n
+                phi_err[i_phi] += frac * frac * math.fabs(data_n)
+            else:
+                phi_err[i_phi] += frac * frac * err_data[npt] * err_data[npt]
+            phi_counts[i_phi] += frac
+
+        for i in range(self.nbins_phi):
+            phi_bins[i] = phi_bins[i] / phi_counts[i]
+            phi_err[i] = math.sqrt(phi_err[i]) / phi_counts[i]
+            phi_values[i] = 2.0 * math.pi / self.nbins_phi * (1.0 * i)
+
+        idx = (numpy.isfinite(phi_bins))
+
+        if not idx.any():
+            msg = "Average Error: No points inside ROI to average..."
+            raise ValueError, msg
+        #elif len(phi_bins[idx])!= self.nbins_phi:
+        #    print "resulted",self.nbins_phi- len(phi_bins[idx])
+        #,"empty bin(s) due to tight binning..."
+        return Data1D(x=phi_values[idx], y=phi_bins[idx], dy=phi_err[idx])
+
+
+def get_pixel_fraction(qmax, q_00, q_01, q_10, q_11):
+    """
+    Returns the fraction of the pixel defined by
+    the four corners (q_00, q_01, q_10, q_11) that
+    has q < qmax.::
+
+                q_01                q_11
+        y=1         +--------------+
+                    |              |
+                    |              |
+                    |              |
+        y=0         +--------------+
+                q_00                q_10
+
+                    x=0            x=1
+
+    """
+    # y side for x = minx
+    x_0 = get_intercept(qmax, q_00, q_01)
+    # y side for x = maxx
+    x_1 = get_intercept(qmax, q_10, q_11)
+
+    # x side for y = miny
+    y_0 = get_intercept(qmax, q_00, q_10)
+    # x side for y = maxy
+    y_1 = get_intercept(qmax, q_01, q_11)
+
+    # surface fraction for a 1x1 pixel
+    frac_max = 0
+
+    if x_0 and x_1:
+        frac_max = (x_0 + x_1) / 2.0
+    elif y_0 and y_1:
+        frac_max = (y_0 + y_1) / 2.0
+    elif x_0 and y_0:
+        if q_00 < q_10:
+            frac_max = x_0 * y_0 / 2.0
+        else:
+            frac_max = 1.0 - x_0 * y_0 / 2.0
+    elif x_0 and y_1:
+        if q_00 < q_10:
+            frac_max = x_0 * y_1 / 2.0
+        else:
+            frac_max = 1.0 - x_0 * y_1 / 2.0
+    elif x_1 and y_0:
+        if q_00 > q_10:
+            frac_max = x_1 * y_0 / 2.0
+        else:
+            frac_max = 1.0 - x_1 * y_0 / 2.0
+    elif x_1 and y_1:
+        if q_00 < q_10:
+            frac_max = 1.0 - (1.0 - x_1) * (1.0 - y_1) / 2.0
+        else:
+            frac_max = (1.0 - x_1) * (1.0 - y_1) / 2.0
+
+    # If we make it here, there is no intercept between
+    # this pixel and the constant-q ring. We only need
+    # to know if we have to include it or exclude it.
+    elif (q_00 + q_01 + q_10 + q_11) / 4.0 < qmax:
+        frac_max = 1.0
+
+    return frac_max
+
+
+def get_intercept(q, q_0, q_1):
+    """
+    Returns the fraction of the side at which the
+    q-value intercept the pixel, None otherwise.
+    The values returned is the fraction ON THE SIDE
+    OF THE LOWEST Q. ::
+
+            A           B
+        +-----------+--------+    <--- pixel size
+        0                    1
+        Q_0 -------- Q ----- Q_1   <--- equivalent Q range
+        if Q_1 > Q_0, A is returned
+        if Q_1 < Q_0, B is returned
+        if Q is outside the range of [Q_0, Q_1], None is returned
+
+    """
+    if q_1 > q_0:
+        if q > q_0 and q <= q_1:
+            return (q - q_0) / (q_1 - q_0)
+    else:
+        if q > q_1 and q <= q_0:
+            return (q - q_1) / (q_0 - q_1)
+    return None
+
+
+class _Sector(object):
+    """
+    Defines a sector region on a 2D data set.
+    The sector is defined by r_min, r_max, phi_min, phi_max,
+    and the position of the center of the ring
+    where phi_min and phi_max are defined by the right
+    and left lines wrt central line
+    and phi_max could be less than phi_min.
+
+    Phi is defined between 0 and 2*pi in anti-clockwise
+    starting from the x- axis on the left-hand side
+    """
+    def __init__(self, r_min, r_max, phi_min=0, phi_max=2 * math.pi, nbins=20):
+        self.r_min = r_min
+        self.r_max = r_max
+        self.phi_min = phi_min
+        self.phi_max = phi_max
+        self.nbins = nbins
+
+    def _agv(self, data2D, run='phi'):
+        """
+        Perform sector averaging.
+
+        :param data2D: Data2D object
+        :param run:  define the varying parameter ('phi' , 'q' , or 'q2')
+
+        :return: Data1D object
+        """
+        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
+            raise RuntimeError, "Ring averaging only take plottable_2D objects"
+        Pi = math.pi
+
+        # Get the all data & info
+        data = data2D.data[numpy.isfinite(data2D.data)]
+        q_data = data2D.q_data[numpy.isfinite(data2D.data)]
+        err_data = data2D.err_data[numpy.isfinite(data2D.data)]
+        qx_data = data2D.qx_data[numpy.isfinite(data2D.data)]
+        qy_data = data2D.qy_data[numpy.isfinite(data2D.data)]
+        dq_data = None
+
+        # Get the dq for resolution averaging
+        if data2D.dqx_data != None and data2D.dqy_data != None:
+            # The pinholes and det. pix contribution present
+            # in both direction of the 2D which must be subtracted when
+            # converting to 1D: dq_overlap should calculated ideally at
+            # q = 0.
+            # Extrapolate dqy(perp) at q = 0
+            z_max = max(data2D.q_data)
+            z_min = min(data2D.q_data)
+            x_max = data2D.dqx_data[data2D.q_data[z_max]]
+            x_min = data2D.dqx_data[data2D.q_data[z_min]]
+            y_max = data2D.dqy_data[data2D.q_data[z_max]]
+            y_min = data2D.dqy_data[data2D.q_data[z_min]]
+            # Find qdx at q = 0
+            dq_overlap_x = (x_min * z_max - x_max * z_min) / (z_max - z_min)
+            # when extrapolation goes wrong
+            if dq_overlap_x > min(data2D.dqx_data):
+                dq_overlap_x = min(data2D.dqx_data)
+            dq_overlap_x *= dq_overlap_x
+            # Find qdx at q = 0
+            dq_overlap_y = (y_min * z_max - y_max * z_min) / (z_max - z_min)
+            # when extrapolation goes wrong
+            if dq_overlap_y > min(data2D.dqy_data):
+                dq_overlap_y = min(data2D.dqy_data)
+            # get dq at q=0.
+            dq_overlap_y *= dq_overlap_y
+
+            dq_overlap = numpy.sqrt((dq_overlap_x + dq_overlap_y) / 2.0)
+            if dq_overlap < 0:
+                dq_overlap = y_min
+            dqx_data = data2D.dqx_data[numpy.isfinite(data2D.data)]
+            dqy_data = data2D.dqy_data[numpy.isfinite(data2D.data)] - dq_overlap
+            # def; dqx_data = dq_r dqy_data = dq_phi
+            # Convert dq 2D to 1D here
+            dqx = dqx_data * dqx_data
+            dqy = dqy_data * dqy_data
+            dq_data = numpy.add(dqx, dqy)
+            dq_data = numpy.sqrt(dq_data)
+
+        #set space for 1d outputs
+        x = numpy.zeros(self.nbins)
+        y = numpy.zeros(self.nbins)
+        y_err = numpy.zeros(self.nbins)
+        x_err = numpy.zeros(self.nbins)
+        y_counts = numpy.zeros(self.nbins)
+
+        # Get the min and max into the region: 0 <= phi < 2Pi
+        phi_min = flip_phi(self.phi_min)
+        phi_max = flip_phi(self.phi_max)
+
+        for n in range(len(data)):
+            frac = 0
+
+            # q-value at the pixel (j,i)
+            q_value = q_data[n]
+            data_n = data[n]
+
+            # Is pixel within range?
+            is_in = False
+
+            # phi-value of the pixel (j,i)
+            phi_value = math.atan2(qy_data[n], qx_data[n]) + Pi
+
+            ## No need to calculate the frac when all data are within range
+            if self.r_min <= q_value and q_value <= self.r_max:
+                frac = 1
+            if frac == 0:
+                continue
+            #In case of two ROIs (symmetric major and minor regions)(for 'q2')
+            if run.lower() == 'q2':
+                ## For minor sector wing
+                # Calculate the minor wing phis
+                phi_min_minor = flip_phi(phi_min - Pi)
+                phi_max_minor = flip_phi(phi_max - Pi)
+                # Check if phis of the minor ring is within 0 to 2pi
+                if phi_min_minor > phi_max_minor:
+                    is_in = (phi_value > phi_min_minor or \
+                              phi_value < phi_max_minor)
+                else:
+                    is_in = (phi_value > phi_min_minor and \
+                              phi_value < phi_max_minor)
+
+            #For all cases(i.e.,for 'q', 'q2', and 'phi')
+            #Find pixels within ROI
+            if phi_min > phi_max:
+                is_in = is_in or (phi_value > phi_min or \
+                                   phi_value < phi_max)
+            else:
+                is_in = is_in or (phi_value >= phi_min  and \
+                                    phi_value < phi_max)
+
+            if not is_in:
+                frac = 0
+            if frac == 0:
+                continue
+            # Check which type of averaging we need
+            if run.lower() == 'phi':
+                temp_x = (self.nbins) * (phi_value - self.phi_min)
+                temp_y = (self.phi_max - self.phi_min)
+                i_bin = int(math.floor(temp_x / temp_y))
+            else:
+                temp_x = (self.nbins) * (q_value - self.r_min)
+                temp_y = (self.r_max - self.r_min)
+                i_bin = int(math.floor(temp_x / temp_y))
+
+            # Take care of the edge case at phi = 2pi.
+            if i_bin == self.nbins:
+                i_bin = self.nbins - 1
+
+            ## Get the total y
+            y[i_bin] += frac * data_n
+            x[i_bin] += frac * q_value
+            if err_data[n] == None or err_data[n] == 0.0:
+                if data_n < 0:
+                    data_n = -data_n
+                y_err[i_bin] += frac * frac * data_n
+            else:
+                y_err[i_bin] += frac * frac * err_data[n] * err_data[n]
+
+            if dq_data != None:
+                # To be consistent with dq calculation in 1d reduction,
+                # we need just the averages (not quadratures) because
+                # it should not depend on the number of the q points
+                # in the qr bins.
+                x_err[i_bin] += frac * dq_data[n]
+            else:
+                x_err = None
+            y_counts[i_bin] += frac
+
+        # Organize the results
+        for i in range(self.nbins):
+            y[i] = y[i] / y_counts[i]
+            y_err[i] = math.sqrt(y_err[i]) / y_counts[i]
+
+            # The type of averaging: phi,q2, or q
+            # Calculate x[i]should be at the center of the bin
+            if run.lower() == 'phi':
+                x[i] = (self.phi_max - self.phi_min) / self.nbins * \
+                    (1.0 * i + 0.5) + self.phi_min
+            else:
+                # We take the center of ring area, not radius.
+                # This is more accurate than taking the radial center of ring.
+                #delta_r = (self.r_max - self.r_min) / self.nbins
+                #r_inner = self.r_min + delta_r * i
+                #r_outer = r_inner + delta_r
+                #x[i] = math.sqrt((r_inner * r_inner + r_outer * r_outer) / 2)
+                x[i] = x[i] / y_counts[i]
+        y_err[y_err == 0] = numpy.average(y_err)
+        idx = (numpy.isfinite(y) & numpy.isfinite(y_err))
+        if x_err != None:
+            d_x = x_err[idx] / y_counts[idx]
+        else:
+            d_x = None
+        if not idx.any():
+            msg = "Average Error: No points inside sector of ROI to average..."
+            raise ValueError, msg
+        #elif len(y[idx])!= self.nbins:
+        #    print "resulted",self.nbins- len(y[idx]),
+        #"empty bin(s) due to tight binning..."
+        return Data1D(x=x[idx], y=y[idx], dy=y_err[idx], dx=d_x)
+
+
+class SectorPhi(_Sector):
+    """
+    Sector average as a function of phi.
+    I(phi) is return and the data is averaged over Q.
+
+    A sector is defined by r_min, r_max, phi_min, phi_max.
+    The number of bin in phi also has to be defined.
+    """
+    def __call__(self, data2D):
+        """
+        Perform sector average and return I(phi).
+
+        :param data2D: Data2D object
+        :return: Data1D object
+        """
+        return self._agv(data2D, 'phi')
+
+
+class SectorQ(_Sector):
+    """
+    Sector average as a function of Q for both symatric wings.
+    I(Q) is return and the data is averaged over phi.
+
+    A sector is defined by r_min, r_max, phi_min, phi_max.
+    r_min, r_max, phi_min, phi_max >0.
+    The number of bin in Q also has to be defined.
+    """
+    def __call__(self, data2D):
+        """
+        Perform sector average and return I(Q).
+
+        :param data2D: Data2D object
+
+        :return: Data1D object
+        """
+        return self._agv(data2D, 'q2')
+
+
+class Ringcut(object):
+    """
+    Defines a ring on a 2D data set.
+    The ring is defined by r_min, r_max, and
+    the position of the center of the ring.
+
+    The data returned is the region inside the ring
+
+    Phi_min and phi_max should be defined between 0 and 2*pi
+    in anti-clockwise starting from the x- axis on the left-hand side
+    """
+    def __init__(self, r_min=0, r_max=0, center_x=0, center_y=0):
+        # Minimum radius
+        self.r_min = r_min
+        # Maximum radius
+        self.r_max = r_max
+        # Center of the ring in x
+        self.center_x = center_x
+        # Center of the ring in y
+        self.center_y = center_y
+
+    def __call__(self, data2D):
+        """
+        Apply the ring to the data set.
+        Returns the angular distribution for a given q range
+
+        :param data2D: Data2D object
+
+        :return: index array in the range
+        """
+        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
+            raise RuntimeError, "Ring cut only take plottable_2D objects"
+
+        # Get data
+        qx_data = data2D.qx_data
+        qy_data = data2D.qy_data
+        q_data = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
+
+        # check whether or not the data point is inside ROI
+        out = (self.r_min <= q_data) & (self.r_max >= q_data)
+        return out
+
+
+class Boxcut(object):
+    """
+    Find a rectangular 2D region of interest.
+    """
+    def __init__(self, x_min=0.0, x_max=0.0, y_min=0.0, y_max=0.0):
+        # Minimum Qx value [A-1]
+        self.x_min = x_min
+        # Maximum Qx value [A-1]
+        self.x_max = x_max
+        # Minimum Qy value [A-1]
+        self.y_min = y_min
+        # Maximum Qy value [A-1]
+        self.y_max = y_max
+
+    def __call__(self, data2D):
+        """
+       Find a rectangular 2D region of interest.
+
+       :param data2D: Data2D object
+       :return: mask, 1d array (len = len(data))
+           with Trues where the data points are inside ROI, otherwise False
+        """
+        mask = self._find(data2D)
+
+        return mask
+
+    def _find(self, data2D):
+        """
+        Find a rectangular 2D region of interest.
+
+        :param data2D: Data2D object
+
+        :return: out, 1d array (length = len(data))
+           with Trues where the data points are inside ROI, otherwise Falses
+        """
+        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
+            raise RuntimeError, "Boxcut take only plottable_2D objects"
+        # Get qx_ and qy_data
+        qx_data = data2D.qx_data
+        qy_data = data2D.qy_data
+
+        # check whether or not the data point is inside ROI
+        outx = (self.x_min <= qx_data) & (self.x_max > qx_data)
+        outy = (self.y_min <= qy_data) & (self.y_max > qy_data)
+
+        return outx & outy
+
+
+class Sectorcut(object):
+    """
+    Defines a sector (major + minor) region on a 2D data set.
+    The sector is defined by phi_min, phi_max,
+    where phi_min and phi_max are defined by the right
+    and left lines wrt central line.
+
+    Phi_min and phi_max are given in units of radian
+    and (phi_max-phi_min) should not be larger than pi
+    """
+    def __init__(self, phi_min=0, phi_max=math.pi):
+        self.phi_min = phi_min
+        self.phi_max = phi_max
+
+    def __call__(self, data2D):
+        """
+        Find a rectangular 2D region of interest.
+
+        :param data2D: Data2D object
+
+        :return: mask, 1d array (len = len(data))
+
+        with Trues where the data points are inside ROI, otherwise False
+        """
+        mask = self._find(data2D)
+
+        return mask
+
+    def _find(self, data2D):
+        """
+        Find a rectangular 2D region of interest.
+
+        :param data2D: Data2D object
+
+        :return: out, 1d array (length = len(data))
+
+        with Trues where the data points are inside ROI, otherwise Falses
+        """
+        if data2D.__class__.__name__ not in ["Data2D", "plottable_2D"]:
+            raise RuntimeError, "Sectorcut take only plottable_2D objects"
+        Pi = math.pi
+        # Get data
+        qx_data = data2D.qx_data
+        qy_data = data2D.qy_data
+
+        # get phi from data
+        phi_data = numpy.arctan2(qy_data, qx_data)
+
+        # Get the min and max into the region: -pi <= phi < Pi
+        phi_min_major = flip_phi(self.phi_min + Pi) - Pi
+        phi_max_major = flip_phi(self.phi_max + Pi) - Pi
+        # check for major sector
+        if phi_min_major > phi_max_major:
+            out_major = (phi_min_major <= phi_data) + (phi_max_major > phi_data)
+        else:
+            out_major = (phi_min_major <= phi_data) & (phi_max_major > phi_data)
+
+        # minor sector
+        # Get the min and max into the region: -pi <= phi < Pi
+        phi_min_minor = flip_phi(self.phi_min) - Pi
+        phi_max_minor = flip_phi(self.phi_max) - Pi
+
+        # check for minor sector
+        if phi_min_minor > phi_max_minor:
+            out_minor = (phi_min_minor <= phi_data) + \
+                            (phi_max_minor >= phi_data)
+        else:
+            out_minor = (phi_min_minor <= phi_data) & \
+                            (phi_max_minor >= phi_data)
+        out = out_major + out_minor
+
+        return out
Index: src/sas/sascalc/dataloader/readers/IgorReader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/IgorReader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/IgorReader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,304 @@
+"""
+    IGOR 2D reduced file reader
+"""
+############################################################################
+#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, University of Tennessee
+#############################################################################
+import os
+import numpy
+import math
+#import logging
+from sas.sascalc.dataloader.data_info import Data2D
+from sas.sascalc.dataloader.data_info import Detector
+from sas.sascalc.dataloader.manipulations import reader2D_converter
+
+# Look for unit converter
+has_converter = True
+try:
+    from sas.sascalc.data_util.nxsunit import Converter
+except:
+    has_converter = False
+
+
+class Reader:
+    """ Simple data reader for Igor data files """
+    ## File type
+    type_name = "IGOR 2D"
+    ## Wildcards
+    type = ["IGOR 2D files (*.ASC)|*.ASC"]
+    ## Extension
+    ext=['.ASC', '.asc']
+
+    def read(self, filename=None):
+        """ Read file """
+        if not os.path.isfile(filename):
+            raise ValueError, \
+            "Specified file %s is not a regular file" % filename
+        
+        # Read file
+        f = open(filename, 'r')
+        buf = f.read()
+        
+        # Instantiate data object
+        output = Data2D()
+        output.filename = os.path.basename(filename)
+        detector = Detector()
+        if len(output.detector) > 0:
+            print str(output.detector[0])
+        output.detector.append(detector)
+                
+        # Get content
+        dataStarted = False
+        
+        lines = buf.split('\n')
+        itot = 0
+        x = []
+        y = []
+        
+        ncounts = 0
+        
+        xmin = None
+        xmax = None
+        ymin = None
+        ymax = None
+        
+        i_x = 0
+        i_y = -1
+        i_tot_row = 0
+        
+        isInfo = False
+        isCenter = False
+       
+        data_conv_q = None
+        data_conv_i = None
+        
+        if has_converter == True and output.Q_unit != '1/A':
+            data_conv_q = Converter('1/A')
+            # Test it
+            data_conv_q(1.0, output.Q_unit)
+            
+        if has_converter == True and output.I_unit != '1/cm':
+            data_conv_i = Converter('1/cm')
+            # Test it
+            data_conv_i(1.0, output.I_unit)
+         
+        for line in lines:
+            
+            # Find setup info line
+            if isInfo:
+                isInfo = False
+                line_toks = line.split()
+                # Wavelength in Angstrom
+                try:
+                    wavelength = float(line_toks[1])
+                except:
+                    msg = "IgorReader: can't read this file, missing wavelength"
+                    raise ValueError, msg
+                
+            #Find # of bins in a row assuming the detector is square.
+            if dataStarted == True:
+                try:
+                    value = float(line)
+                except:
+                    # Found a non-float entry, skip it
+                    continue
+                
+                # Get total bin number
+                
+            i_tot_row += 1
+        i_tot_row = math.ceil(math.sqrt(i_tot_row)) - 1
+        #print "i_tot", i_tot_row
+        size_x = i_tot_row  # 192#128
+        size_y = i_tot_row  # 192#128
+        output.data = numpy.zeros([size_x, size_y])
+        output.err_data = numpy.zeros([size_x, size_y])
+     
+        #Read Header and 2D data
+        for line in lines:
+            # Find setup info line
+            if isInfo:
+                isInfo = False
+                line_toks = line.split()
+                # Wavelength in Angstrom
+                try:
+                    wavelength = float(line_toks[1])
+                except:
+                    msg = "IgorReader: can't read this file, missing wavelength"
+                    raise ValueError, msg
+                # Distance in meters
+                try:
+                    distance = float(line_toks[3])
+                except:
+                    msg = "IgorReader: can't read this file, missing distance"
+                    raise ValueError, msg
+                
+                # Distance in meters
+                try:
+                    transmission = float(line_toks[4])
+                except:
+                    msg = "IgorReader: can't read this file, "
+                    msg += "missing transmission"
+                    raise ValueError, msg
+                                            
+            if line.count("LAMBDA") > 0:
+                isInfo = True
+                
+            # Find center info line
+            if isCenter:
+                isCenter = False
+                line_toks = line.split()
+                
+                # Center in bin number: Must substrate 1 because
+                #the index starts from 1
+                center_x = float(line_toks[0]) - 1
+                center_y = float(line_toks[1]) - 1
+
+            if line.count("BCENT") > 0:
+                isCenter = True
+                
+            # Find data start
+            if line.count("***")>0:
+                dataStarted = True
+                
+                # Check that we have all the info
+                if wavelength == None \
+                    or distance == None \
+                    or center_x == None \
+                    or center_y == None:
+                    msg = "IgorReader:Missing information in data file"
+                    raise ValueError, msg
+                
+            if dataStarted == True:
+                try:
+                    value = float(line)
+                except:
+                    # Found a non-float entry, skip it
+                    continue
+                
+                # Get bin number
+                if math.fmod(itot, i_tot_row) == 0:
+                    i_x = 0
+                    i_y += 1
+                else:
+                    i_x += 1
+                    
+                output.data[i_y][i_x] = value
+                ncounts += 1
+                
+                # Det 640 x 640 mm
+                # Q = 4pi/lambda sin(theta/2)
+                # Bin size is 0.5 cm 
+                #REmoved +1 from theta = (i_x-center_x+1)*0.5 / distance
+                # / 100.0 and 
+                #REmoved +1 from theta = (i_y-center_y+1)*0.5 /
+                # distance / 100.0
+                #ToDo: Need  complete check if the following
+                # covert process is consistent with fitting.py.
+                theta = (i_x - center_x) * 0.5 / distance / 100.0
+                qx = 4.0 * math.pi / wavelength * math.sin(theta/2.0)
+
+                if has_converter == True and output.Q_unit != '1/A':
+                    qx = data_conv_q(qx, units=output.Q_unit)
+
+                if xmin == None or qx < xmin:
+                    xmin = qx
+                if xmax == None or qx > xmax:
+                    xmax = qx
+                
+                theta = (i_y - center_y) * 0.5 / distance / 100.0
+                qy = 4.0 * math.pi / wavelength * math.sin(theta / 2.0)
+
+                if has_converter == True and output.Q_unit != '1/A':
+                    qy = data_conv_q(qy, units=output.Q_unit)
+                
+                if ymin == None or qy < ymin:
+                    ymin = qy
+                if ymax == None or qy > ymax:
+                    ymax = qy
+                
+                if not qx in x:
+                    x.append(qx)
+                if not qy in y:
+                    y.append(qy)
+                
+                itot += 1
+                  
+                  
+        theta = 0.25 / distance / 100.0
+        xstep = 4.0 * math.pi / wavelength * math.sin(theta / 2.0)
+        
+        theta = 0.25 / distance / 100.0
+        ystep = 4.0 * math.pi/ wavelength * math.sin(theta / 2.0)
+        
+        # Store all data ######################################
+        # Store wavelength
+        if has_converter == True and output.source.wavelength_unit != 'A':
+            conv = Converter('A')
+            wavelength = conv(wavelength, units=output.source.wavelength_unit)
+        output.source.wavelength = wavelength
+
+        # Store distance
+        if has_converter == True and detector.distance_unit != 'm':
+            conv = Converter('m')
+            distance = conv(distance, units=detector.distance_unit)
+        detector.distance = distance
+  
+        # Store transmission
+        output.sample.transmission = transmission
+        
+        # Store pixel size
+        pixel = 5.0
+        if has_converter == True and detector.pixel_size_unit != 'mm':
+            conv = Converter('mm')
+            pixel = conv(pixel, units=detector.pixel_size_unit)
+        detector.pixel_size.x = pixel
+        detector.pixel_size.y = pixel
+  
+        # Store beam center in distance units
+        detector.beam_center.x = center_x * pixel
+        detector.beam_center.y = center_y * pixel
+        
+        # Store limits of the image (2D array)
+        xmin = xmin - xstep / 2.0
+        xmax = xmax + xstep / 2.0
+        ymin = ymin - ystep / 2.0
+        ymax = ymax + ystep / 2.0
+        if has_converter == True and output.Q_unit != '1/A':
+            xmin = data_conv_q(xmin, units=output.Q_unit)
+            xmax = data_conv_q(xmax, units=output.Q_unit)
+            ymin = data_conv_q(ymin, units=output.Q_unit)
+            ymax = data_conv_q(ymax, units=output.Q_unit)
+        output.xmin = xmin
+        output.xmax = xmax
+        output.ymin = ymin
+        output.ymax = ymax
+        
+        # Store x and y axis bin centers
+        output.x_bins = x
+        output.y_bins = y
+        
+        # Units
+        if data_conv_q is not None:
+            output.xaxis("\\rm{Q_{x}}", output.Q_unit)
+            output.yaxis("\\rm{Q_{y}}", output.Q_unit)
+        else:
+            output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
+            output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
+            
+        if data_conv_i is not None:
+            output.zaxis("\\rm{Intensity}", output.I_unit)
+        else:
+            output.zaxis("\\rm{Intensity}", "cm^{-1}")
+    
+        # Store loading process information
+        output.meta_data['loader'] = self.type_name
+        output = reader2D_converter(output)
+
+        return output
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 b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,13 @@
+# Backward compatibility with the previous implementation of the default readers
+from associations import register_readers
+
+# Method to associate extensions to default readers 
+from associations import read_associations
+
+# Method to return the location of the XML settings file
+def get_data_path():
+    """
+        Return the location of the settings file for the data readers.
+    """
+    import os
+    return os.path.dirname(__file__)
Index: src/sas/sascalc/dataloader/readers/abs_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/abs_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/abs_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,249 @@
+"""
+"""
+#####################################################################
+#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.
+#See the license text in license.txt
+#copyright 2008, University of Tennessee
+######################################################################
+
+import numpy
+import os
+from sas.sascalc.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.data_info import Detector
+
+has_converter = True
+try:
+    from sas.sascalc.data_util.nxsunit import Converter
+except:
+    has_converter = False
+    
+    
+class Reader:
+    """
+    Class to load IGOR reduced .ABS files
+    """
+    ## File type
+    type_name = "IGOR 1D"
+    ## Wildcards
+    type = ["IGOR 1D files (*.abs)|*.abs"]
+    ## List of allowed extensions
+    ext = ['.abs', '.ABS']
+    
+    def read(self, path):
+        """ 
+        Load data file.
+        
+        :param path: file path
+        
+        :return: Data1D object, or None
+        
+        :raise RuntimeError: when the file can't be opened
+        :raise ValueError: when the length of the data vectors are inconsistent
+        """
+        if os.path.isfile(path):
+            basename = os.path.basename(path)
+            root, extension = os.path.splitext(basename)
+            if extension.lower() in self.ext:
+                try:
+                    input_f = open(path,'r')
+                except:
+                    raise  RuntimeError, "abs_reader: cannot open %s" % path
+                buff = input_f.read()
+                lines = buff.split('\n')
+                x  = numpy.zeros(0)
+                y  = numpy.zeros(0)
+                dy = numpy.zeros(0)
+                dx = numpy.zeros(0)
+                output = Data1D(x, y, dy=dy, dx=dx)
+                detector = Detector()
+                output.detector.append(detector)
+                output.filename = basename
+                
+                is_info = False
+                is_center = False
+                is_data_started = False
+                
+                data_conv_q = None
+                data_conv_i = None
+                
+                if has_converter == True and output.x_unit != '1/A':
+                    data_conv_q = Converter('1/A')
+                    # Test it
+                    data_conv_q(1.0, output.x_unit)
+                    
+                if has_converter == True and output.y_unit != '1/cm':
+                    data_conv_i = Converter('1/cm')
+                    # Test it
+                    data_conv_i(1.0, output.y_unit)
+                
+                for line in lines:
+                    
+                    # Information line 1
+                    if is_info == True:
+                        is_info = False
+                        line_toks = line.split()
+                        
+                        # Wavelength in Angstrom
+                        try:
+                            value = float(line_toks[1])
+                            if has_converter == True and \
+                                output.source.wavelength_unit != 'A':
+                                conv = Converter('A')
+                                output.source.wavelength = conv(value,
+                                        units=output.source.wavelength_unit)
+                            else:
+                                output.source.wavelength = value
+                        except:
+                            #goes to ASC reader
+                            msg = "abs_reader: cannot open %s" % path
+                            raise  RuntimeError, msg
+                        
+                        # Distance in meters
+                        try:
+                            value = float(line_toks[3])
+                            if has_converter == True and \
+                                detector.distance_unit != 'm':
+                                conv = Converter('m')
+                                detector.distance = conv(value,
+                                                units=detector.distance_unit)
+                            else:
+                                detector.distance = value
+                        except:
+                            #goes to ASC reader
+                            msg = "abs_reader: cannot open %s" % path
+                            raise  RuntimeError, msg
+                        # Transmission
+                        try:
+                            output.sample.transmission = float(line_toks[4])
+                        except:
+                            # Transmission is not a mandatory entry
+                            pass
+                    
+                        # Thickness in mm
+                        try:
+                            value = float(line_toks[5])
+                            if has_converter == True and \
+                                output.sample.thickness_unit != 'cm':
+                                conv = Converter('cm')
+                                output.sample.thickness = conv(value,
+                                            units=output.sample.thickness_unit)
+                            else:
+                                output.sample.thickness = value
+                        except:
+                            # Thickness is not a mandatory entry
+                            pass
+                    
+                    #MON CNT   LAMBDA   DET ANG   DET DIST   TRANS   THICK 
+                    #  AVE   STEP
+                    if line.count("LAMBDA") > 0:
+                        is_info = True
+                        
+                    # Find center info line
+                    if is_center == True:
+                        is_center = False
+                        line_toks = line.split()
+                        # Center in bin number
+                        center_x = float(line_toks[0])
+                        center_y = float(line_toks[1])
+                        
+                        # Bin size
+                        if has_converter == True and \
+                            detector.pixel_size_unit != 'mm':
+                            conv = Converter('mm')
+                            detector.pixel_size.x = conv(5.0,
+                                                units=detector.pixel_size_unit)
+                            detector.pixel_size.y = conv(5.0,
+                                                units=detector.pixel_size_unit)
+                        else:
+                            detector.pixel_size.x = 5.0
+                            detector.pixel_size.y = 5.0
+                        
+                        # Store beam center in distance units
+                        # Det 640 x 640 mm
+                        if has_converter == True and \
+                            detector.beam_center_unit != 'mm':
+                            conv = Converter('mm')
+                            detector.beam_center.x = conv(center_x * 5.0,
+                                             units=detector.beam_center_unit)
+                            detector.beam_center.y = conv(center_y * 5.0,
+                                            units=detector.beam_center_unit)
+                        else:
+                            detector.beam_center.x = center_x * 5.0
+                            detector.beam_center.y = center_y * 5.0
+                        
+                        # Detector type
+                        try:
+                            detector.name = line_toks[7]
+                        except:
+                            # Detector name is not a mandatory entry
+                            pass
+                    
+                    #BCENT(X,Y)   A1(mm)   A2(mm)   A1A2DIST(m)   DL/L
+                    #  BSTOP(mm)   DET_TYP
+                    if line.count("BCENT") > 0:
+                        is_center = True
+                        
+                    # Parse the data
+                    if is_data_started == True:
+                        toks = line.split()
+
+                        try:
+                            _x  = float(toks[0])
+                            _y  = float(toks[1])
+                            _dy = float(toks[2])
+                            _dx = float(toks[3])
+                            
+                            if data_conv_q is not None:
+                                _x = data_conv_q(_x, units=output.x_unit)
+                                _dx = data_conv_i(_dx, units=output.x_unit)
+                                
+                            if data_conv_i is not None:
+                                _y = data_conv_i(_y, units=output.y_unit)
+                                _dy = data_conv_i(_dy, units=output.y_unit)
+                           
+                            x = numpy.append(x, _x)
+                            y = numpy.append(y, _y)
+                            dy = numpy.append(dy, _dy)
+                            dx = numpy.append(dx, _dx)
+                            
+                        except:
+                            # Could not read this data line. If we are here
+                            # it is because we are in the data section. Just
+                            # skip it.
+                            pass
+                            
+                    #The 6 columns are | Q (1/A) | I(Q) (1/cm) | std. dev.
+                    # I(Q) (1/cm) | sigmaQ | meanQ | ShadowFactor|
+                    if line.count("The 6 columns") > 0:
+                        is_data_started = True
+            
+                # Sanity check
+                if not len(y) == len(dy):
+                    msg = "abs_reader: y and dy have different length"
+                    raise ValueError, msg
+                # If the data length is zero, consider this as
+                # though we were not able to read the file.
+                if len(x) == 0:
+                    raise ValueError, "ascii_reader: could not load file"
+                
+                output.x = x[x != 0]
+                output.y = y[x != 0]
+                output.dy = dy[x != 0]
+                output.dx = dx[x != 0]
+                if data_conv_q is not None:
+                    output.xaxis("\\rm{Q}", output.x_unit)
+                else:
+                    output.xaxis("\\rm{Q}", 'A^{-1}')
+                if data_conv_i is not None:
+                    output.yaxis("\\rm{Intensity}", output.y_unit)
+                else:
+                    output.yaxis("\\rm{Intensity}", "cm^{-1}")
+                    
+                # Store loading process information
+                output.meta_data['loader'] = self.type_name
+                return output
+        else:
+            raise RuntimeError, "%s is not a file" % path
+        return None
Index: src/sas/sascalc/dataloader/readers/ascii_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/ascii_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/ascii_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,358 @@
+"""
+    ASCII reader
+"""
+############################################################################
+#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, University of Tennessee
+#############################################################################
+
+
+import numpy
+import os
+from sas.sascalc.dataloader.data_info import Data1D
+
+# Check whether we have a converter available
+has_converter = True
+try:
+    from sas.sascalc.data_util.nxsunit import Converter
+except:
+    has_converter = False
+_ZERO = 1e-16
+
+
+class Reader:
+    """
+    Class to load ascii files (2, 3 or 4 columns).
+    """
+    ## File type
+    type_name = "ASCII"
+    
+    ## Wildcards
+    type = ["ASCII files (*.txt)|*.txt",
+            "ASCII files (*.dat)|*.dat",
+            "ASCII files (*.abs)|*.abs",
+            "CSV files (*.csv)|*.csv"]
+    ## List of allowed extensions
+    ext = ['.txt', '.TXT', '.dat', '.DAT', '.abs', '.ABS', 'csv', 'CSV']
+    
+    ## Flag to bypass extension check
+    allow_all = True
+    
+    def read(self, path):
+        """
+        Load data file
+        
+        :param path: file path
+        
+        :return: Data1D object, or None
+        
+        :raise RuntimeError: when the file can't be opened
+        :raise ValueError: when the length of the data vectors are inconsistent
+        """
+        if os.path.isfile(path):
+            basename = os.path.basename(path)
+            _, extension = os.path.splitext(basename)
+            if self.allow_all or extension.lower() in self.ext:
+                try:
+                    # Read in binary mode since GRASP frequently has no-ascii
+                    # characters that brakes the open operation
+                    input_f = open(path,'rb')
+                except:
+                    raise  RuntimeError, "ascii_reader: cannot open %s" % path
+                buff = input_f.read()
+                lines = buff.splitlines()
+                 
+                x  = numpy.zeros(0)
+                y  = numpy.zeros(0)
+                dy = numpy.zeros(0)
+                dx = numpy.zeros(0)
+                
+                #temp. space to sort data
+                tx  = numpy.zeros(0)
+                ty  = numpy.zeros(0)
+                tdy = numpy.zeros(0)
+                tdx = numpy.zeros(0)
+                
+                output = Data1D(x, y, dy=dy, dx=dx)
+                self.filename = output.filename = basename
+           
+                data_conv_q = None
+                data_conv_i = None
+                
+                if has_converter == True and output.x_unit != '1/A':
+                    data_conv_q = Converter('1/A')
+                    # Test it
+                    data_conv_q(1.0, output.x_unit)
+                    
+                if has_converter == True and output.y_unit != '1/cm':
+                    data_conv_i = Converter('1/cm')
+                    # Test it
+                    data_conv_i(1.0, output.y_unit)
+           
+                # The first good line of data will define whether
+                # we have 2-column or 3-column ascii
+                has_error_dx = None
+                has_error_dy = None
+                
+                #Initialize counters for data lines and header lines.
+                is_data = False  # Has more than 5 lines
+                # More than "5" lines of data is considered as actual
+                # data unless that is the only data
+                mum_data_lines = 5
+                # To count # of current data candidate lines
+                i = -1
+                # To count total # of previous data candidate lines
+                i1 = -1
+                # To count # of header lines
+                j = -1
+                # Helps to count # of header lines
+                j1 = -1
+                #minimum required number of columns of data; ( <= 4).
+                lentoks = 2
+                for line in lines:
+                    # Initial try for CSV (split on ,)
+                    toks = line.split(',')
+                    # Now try SCSV (split on ;)
+                    if len(toks) < 2:
+                        toks = line.split(';')
+                    # Now go for whitespace                    
+                    if len(toks) < 2:
+                        toks = line.split()
+                    try:
+                        #Make sure that all columns are numbers.
+                        for colnum in range(len(toks)):
+                            float(toks[colnum])
+                            
+                        _x = float(toks[0])
+                        _y = float(toks[1])
+                        
+                        #Reset the header line counters
+                        if j == j1:
+                            j = 0
+                            j1 = 0
+                            
+                        if i > 1:
+                            is_data = True
+                        
+                        if data_conv_q is not None:
+                            _x = data_conv_q(_x, units=output.x_unit)
+                            
+                        if data_conv_i is not None:
+                            _y = data_conv_i(_y, units=output.y_unit)
+                        
+                        # If we have an extra token, check
+                        # whether it can be interpreted as a
+                        # third column.
+                        _dy = None
+                        if len(toks) > 2:
+                            try:
+                                _dy = float(toks[2])
+                                
+                                if data_conv_i is not None:
+                                    _dy = data_conv_i(_dy, units=output.y_unit)
+                                
+                            except:
+                                # The third column is not a float, skip it.
+                                pass
+                            
+                        # If we haven't set the 3rd column
+                        # flag, set it now.
+                        if has_error_dy == None:
+                            has_error_dy = False if _dy == None else True
+                            
+                        #Check for dx
+                        _dx = None
+                        if len(toks) > 3:
+                            try:
+                                _dx = float(toks[3])
+                                
+                                if data_conv_i is not None:
+                                    _dx = data_conv_i(_dx, units=output.x_unit)
+                                
+                            except:
+                                # The 4th column is not a float, skip it.
+                                pass
+                            
+                        # If we haven't set the 3rd column
+                        # flag, set it now.
+                        if has_error_dx == None:
+                            has_error_dx = False if _dx == None else True
+                        
+                        #After talked with PB, we decided to take care of only
+                        # 4 columns of data for now.
+                        #number of columns in the current line
+                        #To remember the # of columns in the current
+                        #line of data
+                        new_lentoks = len(toks)
+                        
+                        #If the previous columns not equal to the current,
+                        #mark the previous as non-data and reset the dependents.
+                        if lentoks != new_lentoks:
+                            if is_data == True:
+                                break
+                            else:
+                                i = -1
+                                i1 = 0
+                                j = -1
+                                j1 = -1
+                            
+                        #Delete the previously stored lines of data candidates
+                        # if is not data.
+                        if i < 0 and -1 < i1 < mum_data_lines and \
+                            is_data == False:
+                            try:
+                                x = numpy.zeros(0)
+                                y = numpy.zeros(0)
+                            except:
+                                pass
+                            
+                        x = numpy.append(x, _x)
+                        y = numpy.append(y, _y)
+                        
+                        if has_error_dy == True:
+                            #Delete the previously stored lines of
+                            # data candidates if is not data.
+                            if i < 0 and -1 < i1 < mum_data_lines and \
+                                is_data == False:
+                                try:
+                                    dy = numpy.zeros(0)
+                                except:
+                                    pass
+                            dy = numpy.append(dy, _dy)
+                            
+                        if has_error_dx == True:
+                            #Delete the previously stored lines of
+                            # data candidates if is not data.
+                            if i < 0 and -1 < i1 < mum_data_lines and \
+                                is_data == False:
+                                try:
+                                    dx = numpy.zeros(0)
+                                except:
+                                    pass
+                            dx = numpy.append(dx, _dx)
+                            
+                        #Same for temp.
+                        #Delete the previously stored lines of data candidates
+                        # if is not data.
+                        if i < 0 and -1 < i1 < mum_data_lines and\
+                            is_data == False:
+                            try:
+                                tx = numpy.zeros(0)
+                                ty = numpy.zeros(0)
+                            except:
+                                pass
+
+                        tx = numpy.append(tx, _x)
+                        ty = numpy.append(ty, _y)
+                        
+                        if has_error_dy == True:
+                            #Delete the previously stored lines of
+                            # data candidates if is not data.
+                            if i < 0 and -1 < i1 < mum_data_lines and \
+                                is_data == False:
+                                try:
+                                    tdy = numpy.zeros(0)
+                                except:
+                                    pass
+                            tdy = numpy.append(tdy, _dy)
+                        if has_error_dx == True:
+                            #Delete the previously stored lines of
+                            # data candidates if is not data.
+                            if i < 0 and -1 < i1 < mum_data_lines and \
+                                is_data == False:
+                                try:
+                                    tdx = numpy.zeros(0)
+                                except:
+                                    pass
+                            tdx = numpy.append(tdx, _dx)
+
+                        #reset i1 and flag lentoks for the next
+                        if lentoks < new_lentoks:
+                            if is_data == False:
+                                i1 = -1
+                        #To remember the # of columns on the current line
+                        # for the next line of data
+                        lentoks = len(toks)
+                        
+                        #Reset # of header lines and counts #
+                        # of data candidate lines
+                        if j == 0 and j1 == 0:
+                            i1 = i + 1
+                        i += 1
+                    except:
+                        # It is data and meet non - number, then stop reading
+                        if is_data == True:
+                            break
+                        lentoks = 2
+                        #Counting # of header lines
+                        j += 1
+                        if j == j1 + 1:
+                            j1 = j
+                        else:
+                            j = -1
+                        #Reset # of lines of data candidates
+                        i = -1
+                        
+                        # Couldn't parse this line, skip it
+                        pass
+                    
+                input_f.close()
+                # Sanity check
+                if has_error_dy == True and not len(y) == len(dy):
+                    msg = "ascii_reader: y and dy have different length"
+                    raise RuntimeError, msg
+                if has_error_dx == True and not len(x) == len(dx):
+                    msg = "ascii_reader: y and dy have different length"
+                    raise RuntimeError, msg
+                # If the data length is zero, consider this as
+                # though we were not able to read the file.
+                if len(x) == 0:
+                    raise RuntimeError, "ascii_reader: could not load file"
+                
+                #Let's re-order the data to make cal.
+                # curve look better some cases
+                ind = numpy.lexsort((ty, tx))
+                for i in ind:
+                    x[i] = tx[ind[i]]
+                    y[i] = ty[ind[i]]
+                    if has_error_dy == True:
+                        dy[i] = tdy[ind[i]]
+                    if has_error_dx == True:
+                        dx[i] = tdx[ind[i]]
+                # Zeros in dx, dy
+                if has_error_dx:
+                    dx[dx == 0] = _ZERO
+                if has_error_dy:
+                    dy[dy == 0] = _ZERO
+                #Data
+                output.x = x[x != 0]
+                output.y = y[x != 0]
+                output.dy = dy[x != 0] if has_error_dy == True\
+                    else numpy.zeros(len(output.y))
+                output.dx = dx[x != 0] if has_error_dx == True\
+                    else numpy.zeros(len(output.x))
+                                
+                if data_conv_q is not None:
+                    output.xaxis("\\rm{Q}", output.x_unit)
+                else:
+                    output.xaxis("\\rm{Q}", 'A^{-1}')
+                if data_conv_i is not None:
+                    output.yaxis("\\rm{Intensity}", output.y_unit)
+                else:
+                    output.yaxis("\\rm{Intensity}", "cm^{-1}")
+                    
+                # Store loading process information
+                output.meta_data['loader'] = self.type_name
+                if len(output.x) < 1:
+                    raise RuntimeError, "%s is empty" % path
+                return output
+            
+        else:
+            raise RuntimeError, "%s is not a file" % path
+        return None
Index: src/sas/sascalc/dataloader/readers/associations.py
===================================================================
--- src/sas/sascalc/dataloader/readers/associations.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/associations.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,105 @@
+"""
+Module to associate default readers to file extensions.
+The module reads an xml file to get the readers for each file extension.
+The readers are tried in order they appear when reading a file.
+"""
+############################################################################
+#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 2009, University of Tennessee
+#############################################################################
+import os
+import sys
+import logging
+import json
+
+FILE_NAME = 'defaults.json'
+
+def read_associations(loader, settings=FILE_NAME):
+    """
+    Read the specified settings file to associate
+    default readers to file extension.
+    
+    :param loader: Loader object
+    :param settings: path to the json settings file [string]
+    """
+    reader_dir = os.path.dirname(__file__)
+    path = os.path.join(reader_dir, settings)
+    
+    # If we can't find the file in the installation
+    # directory, look into the execution directory.
+    if not os.path.isfile(path):
+        path = os.path.join(os.getcwd(), settings)
+    if not os.path.isfile(path):
+        path = os.path.join(sys.path[0], settings)
+    if not os.path.isfile(path):
+        path = settings
+    if not os.path.isfile(path):
+        path = "./%s" % settings
+    if os.path.isfile(path):
+        with open(path) as fh:
+            json_tree = json.load(fh)
+        
+        # Read in the file extension associations
+        entry_list = json_tree['SasLoader']['FileType']
+
+        # For each FileType entry, get the associated reader and extension
+        for entry in entry_list:
+            reader = entry['-reader']
+            ext = entry['-extension']
+            
+            if reader is not None and ext is not None:
+                # Associate the extension with a particular reader
+                # TODO: Modify the Register code to be case-insensitive
+                # and remove the extra line below.
+                try:
+                    exec "import %s" % reader
+                    exec "loader.associate_file_type('%s', %s)" % (ext.lower(),
+                                                                    reader)
+                    exec "loader.associate_file_type('%s', %s)" % (ext.upper(),
+                                                                    reader)
+                except:
+                    msg = "read_associations: skipping association"
+                    msg += " for %s\n  %s" % (ext.lower(), sys.exc_value)
+                    logging.error(msg)
+    else:
+        print "Could not find reader association settings\n  %s [%s]" % (__file__, os.getcwd())
+         
+         
+def register_readers(registry_function):
+    """
+    Function called by the registry/loader object to register
+    all default readers using a call back function.
+    
+    :WARNING: this method is now obsolete
+
+    :param registry_function: function to be called to register each reader
+    """
+    logging.info("register_readers is now obsolete: use read_associations()")
+    import abs_reader
+    import ascii_reader
+    import cansas_reader
+    import danse_reader
+    import hfir1d_reader
+    import IgorReader
+    import red2d_reader
+    #import tiff_reader
+    import nexus_reader
+    import sesans_reader
+    registry_function(sesans_reader)
+    registry_function(abs_reader)
+    registry_function(ascii_reader)
+    registry_function(cansas_reader)
+    registry_function(danse_reader)
+    registry_function(hfir1d_reader)
+    registry_function(IgorReader)
+    registry_function(red2d_reader)
+    #registry_function(tiff_reader)
+    registry_function(nexus_reader)
+    
+    return True
Index: src/sas/sascalc/dataloader/readers/cansas_constants.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_constants.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/cansas_constants.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,672 @@
+"""
+Information relating to the CanSAS data format. These constants are used in
+the cansas_reader.py file to read in any version of the cansas format.
+"""
+class CansasConstants(object):
+    """
+    The base class to define where all of the data is to be saved by
+    cansas_reader.py.
+    """
+    names = ''
+    format = ''
+
+    def __init__(self):
+        self.names = self.CANSAS_NS
+        self.format = self.CANSAS_FORMAT
+
+    def iterate_namespace(self, namespace):
+        """
+        Method to iterate through a cansas constants tree based on a list of
+        names
+
+        :param namespace: A list of names that match the tree structure of
+            cansas_constants
+        """
+        # The current level to look through in cansas_constants.
+        return_me = CurrentLevel()
+        return_me.current_level = self.CANSAS_FORMAT.get("SASentry")
+        # Defaults for variable and datatype
+        return_me.ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\""
+        return_me.ns_datatype = "content"
+        return_me.ns_optional = True
+        for name in namespace:
+            try:
+                if name != "SASentry":
+                    return_me.current_level = \
+                        return_me.current_level.get("children").get(name, "")
+                    if return_me.current_level == "":
+                        return_me.current_level = \
+                                return_me.current_level.get("<any>", "")
+                    cl_variable = return_me.current_level.get("variable", "")
+                    cl_datatype = return_me.current_level.get("storeas", "")
+                    cl_units_optional = \
+                             return_me.current_level.get("units_required", "")
+                    # Where are how to store the variable for the given
+                    # namespace CANSAS_CONSTANTS tree is hierarchical, so
+                    # is no value, inherit
+                    return_me.ns_variable = cl_variable if cl_variable != "" \
+                        else return_me.ns_variable
+                    return_me.ns_datatype = cl_datatype if cl_datatype != "" \
+                        else return_me.ns_datatype
+                    return_me.ns_optional = cl_units_optional if \
+                        cl_units_optional != return_me.ns_optional \
+                                        else return_me.ns_optional
+            except AttributeError:
+                return_me.ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\""
+                return_me.ns_datatype = "content"
+                return_me.ns_optional = True
+        return return_me
+
+    def get_namespace_map(self):
+        """
+        Helper method to get the names namespace list
+        """
+        return self.names
+
+    # CANSAS_NS holds the base namespace and default schema file information
+    CANSAS_NS = {"1.0" : {"ns" : "cansas1d/1.0",
+                          "schema" : "cansas1d_v1_0.xsd"
+                         },
+                 "1.1" : {"ns" : "urn:cansas1d:1.1",
+                          "schema" : "cansas1d_v1_1.xsd"
+                         }
+                }
+
+    # The constants below hold information on where to store the CanSAS data
+    # when loaded in using sasview
+    META_DATA = "{0}.meta_data[\"{2}\"] = \"{1}\""
+    ANY = {"variable" : "{0}.meta_data[\"{2}\"] = \'{1}\'",
+           "storeas" : "content",
+          }
+    TITLE = {"variable" : "{0}.title = \"{1}\""}
+    SASNOTE = {"variable" : "{0}.notes.append(\'{1}\')"}
+    SASPROCESS_TERM = {"variable" : None,
+                       "attributes" : {"unit" : {"variable" : None},
+                                       "name" : {"variable" : None}
+                                      }
+                      }
+    SASPROCESS_SASPROCESSNOTE = {"variable" : None,
+                                 "children" : {"<any>" : ANY}
+                                }
+    SASPROCESS = {"variable" : None,
+                  "children" : {"name" : {"variable" : "{0}.name = \'{1}\'"},
+                                "date" : {"variable" : "{0}.date = \'{1}\'"},
+                                "description" :
+                                {"variable" : "{0}.description = \'{1}\'"},
+                                "term" : SASPROCESS_TERM,
+                                "SASprocessnote" : SASPROCESS_SASPROCESSNOTE,
+                                "<any>" : ANY
+                               },
+                 }
+    RUN = {"variable" : "{0}.run.append(\"{1}\")",
+           "attributes" : {"name" :
+                           {"variable" : "{0}.run_name[\"{3}\"] = \"{1}\""}}
+          }
+    SASDATA_IDATA_Q = {"variable" : "{0}.x = numpy.append({0}.x, {1})",
+                       "unit" : "x_unit",
+                       "attributes" : {"unit" :
+                                       {"variable" : "{0}._xunit = \"{1}\"",
+                                        "storeas" : "content"
+                                       }
+                                      },
+                      }
+    SASDATA_IDATA_I = {"variable" : "{0}.y = numpy.append({0}.y, {1})",
+                       "unit" : "y_unit",
+                       "attributes" : {"unit" :
+                                       {"variable" : "{0}._yunit = \"{1}\"",
+                                        "storeas" : "content"
+                                       }
+                                      },
+                      }
+    SASDATA_IDATA_IDEV = {"variable" : "{0}.dy = numpy.append({0}.dy, {1})",
+                          "unit" : "y_unit",
+                          "attributes" : {"unit" :
+                                          {"variable" : META_DATA,
+                                           "storeas" : "content"
+                                          }
+                                         },
+                         }
+    SASDATA_IDATA_QDEV = {"variable" : "{0}.dx = numpy.append({0}.dx, {1})",
+                          "unit" : "x_unit",
+                          "attributes" : {"unit" :
+                                          {"variable" : META_DATA,
+                                           "storeas" : "content"
+                                          }
+                                         },
+                         }
+    SASDATA_IDATA_DQL = {"variable" : "{0}.dxl = numpy.append({0}.dxl, {1})",
+                         "unit" : "x_unit",
+                         "attributes" : {"unit" :
+                                         {"variable" : META_DATA,
+                                          "storeas" : "content"
+                                         }
+                                        },
+                        }
+    SASDATA_IDATA_DQW = {"variable" : "{0}.dxw = numpy.append({0}.dxw, {1})",
+                         "unit" : "x_unit",
+                         "attributes" : {"unit" :
+                                         {"variable" : META_DATA,
+                                          "storeas" : "content"
+                                         }
+                                        },
+                        }
+    SASDATA_IDATA_QMEAN = {"storeas" : "content",
+                           "unit" : "x_unit",
+                           "variable" : META_DATA,
+                           "attributes" : {"unit" : {"variable" : META_DATA}},
+                          }
+    SASDATA_IDATA_SHADOWFACTOR = {"variable" : META_DATA,
+                                  "storeas" : "content",
+                                 }
+    SASDATA_IDATA = {"storeas" : "float",
+                     "units_optional" : False,
+                     "variable" : None,
+                     "attributes" : {"name" : {"variable" : META_DATA,
+                                               "storeas" : "content",
+                                              },
+                                     "timestamp" : {"variable" : META_DATA,
+                                                    "storeas" : "timestamp",
+                                                   }
+                                    },
+                     "children" : {"Q" : SASDATA_IDATA_Q,
+                                   "I" : SASDATA_IDATA_I,
+                                   "Idev" : SASDATA_IDATA_IDEV,
+                                   "Qdev" : SASDATA_IDATA_QDEV,
+                                   "dQw" : SASDATA_IDATA_DQW,
+                                   "dQl" : SASDATA_IDATA_DQL,
+                                   "Qmean" : SASDATA_IDATA_QMEAN,
+                                   "Shadowfactor" : SASDATA_IDATA_SHADOWFACTOR,
+                                   "<any>" : ANY
+                                  }
+                    }
+    SASDATA = {"attributes" : {"name" : {"variable" : META_DATA,}},
+               "variable" : None,
+               "children" : {"Idata" : SASDATA_IDATA,
+                             "<any>" : ANY
+                            }
+              }
+    SASTRANSSPEC_TDATA_LAMDBA = {"variable" : "{0}.wavelength.append({1})",
+                                 "unit" : "wavelength_unit",
+                                 "attributes" :
+                                 {"unit" :
+                                  {"variable" : "{0}.wavelength_unit = \"{1}\"",
+                                   "storeas" : "content"
+                                  }
+                                 }
+                                }
+    SASTRANSSPEC_TDATA_T = {"variable" : "{0}.transmission.append({1})",
+                            "unit" : "transmission_unit",
+                            "attributes" :
+                            {"unit" :
+                             {"variable" : "{0}.transmission_unit = \"{1}\"",
+                              "storeas" : "content"
+                             }
+                            }
+                           }
+    SASTRANSSPEC_TDATA_TDEV = {"variable" :
+                               "{0}.transmission_deviation.append({1})",
+                               "unit" : "transmission_deviation_unit",
+                               "attributes" :
+                               {"unit" :
+                                {"variable" :
+                                 "{0}.transmission_deviation_unit = \"{1}\"",
+                                 "storeas" : "content"
+                                }
+                               }
+                              }
+    SASTRANSSPEC_TDATA = {"storeas" : "float",
+                          "variable" : None,
+                          "children" : {"Lambda" : SASTRANSSPEC_TDATA_LAMDBA,
+                                        "T" : SASTRANSSPEC_TDATA_T,
+                                        "Tdev" : SASTRANSSPEC_TDATA_TDEV,
+                                        "<any>" : ANY,
+                                       }
+                         }
+    SASTRANSSPEC = {"variable" : None,
+                    "children" : {"Tdata" : SASTRANSSPEC_TDATA,
+                                  "<any>" : ANY,
+                                 },
+                    "attributes" :
+                    {"name" :
+                     {"variable" : "{0}.name = \"{1}\""},
+                     "timestamp" :
+                     {"variable" : "{0}.timestamp = \"{1}\""},
+                    }
+                   }
+    SASSAMPLE_THICK = {"variable" : "{0}.sample.thickness = {1}",
+                       "unit" : "sample.thickness_unit",
+                       "storeas" : "float",
+                       "attributes" :
+                       {"unit" :
+                        {"variable" : "{0}.sample.thickness_unit = \"{1}\"",
+                         "storeas" : "content"
+                        }
+                       },
+                      }
+    SASSAMPLE_TRANS = {"variable" : "{0}.sample.transmission = {1}",
+                       "storeas" : "float",
+                      }
+    SASSAMPLE_TEMP = {"variable" : "{0}.sample.temperature = {1}",
+                      "unit" : "sample.temperature_unit",
+                      "storeas" : "float",
+                      "attributes" :
+                      {"unit" :
+                       {"variable" : "{0}.sample.temperature_unit = \"{1}\"",
+                        "storeas" : "content"
+                       }
+                      },
+                     }
+    SASSAMPLE_POS_ATTR = {"unit" : {"variable" :
+                                    "{0}.sample.position_unit = \"{1}\"",
+                                    "storeas" : "content"
+                                   }
+                         }
+    SASSAMPLE_POS_X = {"variable" : "{0}.sample.position.x = {1}",
+                       "unit" : "sample.position_unit",
+                       "storeas" : "float",
+                       "attributes" : SASSAMPLE_POS_ATTR
+                      }
+    SASSAMPLE_POS_Y = {"variable" : "{0}.sample.position.y = {1}",
+                       "unit" : "sample.position_unit",
+                       "storeas" : "float",
+                       "attributes" : SASSAMPLE_POS_ATTR
+                      }
+    SASSAMPLE_POS_Z = {"variable" : "{0}.sample.position.z = {1}",
+                       "unit" : "sample.position_unit",
+                       "storeas" : "float",
+                       "attributes" : SASSAMPLE_POS_ATTR
+                      }
+    SASSAMPLE_POS = {"children" : {"variable" : None,
+                                   "x" : SASSAMPLE_POS_X,
+                                   "y" : SASSAMPLE_POS_Y,
+                                   "z" : SASSAMPLE_POS_Z,
+                                  },
+                    }
+    SASSAMPLE_ORIENT_ATTR = {"unit" :
+                             {"variable" :
+                              "{0}.sample.orientation_unit = \"{1}\"",
+                              "storeas" : "content"
+                             }
+                            }
+    SASSAMPLE_ORIENT_ROLL = {"variable" : "{0}.sample.orientation.x = {1}",
+                             "unit" : "sample.orientation_unit",
+                             "storeas" : "float",
+                             "attributes" : SASSAMPLE_ORIENT_ATTR
+                            }
+    SASSAMPLE_ORIENT_PITCH = {"variable" : "{0}.sample.orientation.y = {1}",
+                              "unit" : "sample.orientation_unit",
+                              "storeas" : "float",
+                              "attributes" : SASSAMPLE_ORIENT_ATTR
+                             }
+    SASSAMPLE_ORIENT_YAW = {"variable" : "{0}.sample.orientation.z = {1}",
+                            "unit" : "sample.orientation_unit",
+                            "storeas" : "float",
+                            "attributes" : SASSAMPLE_ORIENT_ATTR
+                           }
+    SASSAMPLE_ORIENT = {"variable" : None,
+                        "children" : {"roll" : SASSAMPLE_ORIENT_ROLL,
+                                      "pitch" : SASSAMPLE_ORIENT_PITCH,
+                                      "yaw" : SASSAMPLE_ORIENT_YAW,
+                                     },
+                       }
+    SASSAMPLE = {"attributes" :
+                 {"name" : {"variable" : "{0}.sample.name = \"{1}\""},},
+                 "variable" : None,
+                 "children" : {"ID" : {"variable" : "{0}.sample.ID = \"{1}\""},
+                               "thickness" : SASSAMPLE_THICK,
+                               "transmission" : SASSAMPLE_TRANS,
+                               "temperature" : SASSAMPLE_TEMP,
+                               "position" : SASSAMPLE_POS,
+                               "orientation" : SASSAMPLE_ORIENT,
+                               "details" :
+                               {"variable" :
+                                "{0}.sample.details.append(\"{1}\")"},
+                               "<any>" : ANY
+                              },
+                }
+    SASINSTR_SRC_BEAMSIZE_ATTR = {"unit" :
+                                  "{0}.source.beam_size_unit = \"{1}\"",
+                                  "storeas" : "content"
+                                 }
+    SASINSTR_SRC_BEAMSIZE_X = {"variable" : "{0}.source.beam_size.x = {1}",
+                               "unit" : "source.beam_size_unit",
+                               "storeas" : "float",
+                               "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
+                              }
+    SASINSTR_SRC_BEAMSIZE_Y = {"variable" : "{0}.source.beam_size.y = {1}",
+                               "unit" : "source.beam_size_unit",
+                               "storeas" : "float",
+                               "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
+                              }
+    SASINSTR_SRC_BEAMSIZE_Z = {"variable" : "{0}.source.beam_size.z = {1}",
+                               "unit" : "source.beam_size_unit",
+                               "storeas" : "float",
+                               "attributes" : SASINSTR_SRC_BEAMSIZE_ATTR
+                              }
+    SASINSTR_SRC_BEAMSIZE = {"attributes" :
+                             {"name" : {"variable" :
+                                        "{0}.source.beam_size_name = \"{1}\""}},
+                             "variable" : None,
+                             "children" : {"x" : SASINSTR_SRC_BEAMSIZE_X,
+                                           "y" : SASINSTR_SRC_BEAMSIZE_Y,
+                                           "z" : SASINSTR_SRC_BEAMSIZE_Z,
+                                          }
+                            }
+    SASINSTR_SRC_WL = {"variable" : "{0}.source.wavelength = {1}",
+                       "unit" : "source.wavelength_unit",
+                       "storeas" : "float",
+                       "attributes" :
+                       {"unit" :
+                        {"variable" : "{0}.source.wavelength_unit = \"{1}\"",
+                         "storeas" : "content"
+                        },
+                       }
+                      }
+    SASINSTR_SRC_WL_MIN = {"variable" : "{0}.source.wavelength_min = {1}",
+                           "unit" : "source.wavelength_min_unit",
+                           "storeas" : "float",
+                           "attributes" :
+                           {"unit" :
+                            {"variable" :
+                             "{0}.source.wavelength_min_unit = \"{1}\"",
+                             "storeas" : "content"
+                            },
+                           }
+                          }
+    SASINSTR_SRC_WL_MAX = {"variable" : "{0}.source.wavelength_max = {1}",
+                           "unit" : "source.wavelength_max_unit",
+                           "storeas" : "float",
+                           "attributes" :
+                           {"unit" :
+                            {"variable" :
+                             "{0}.source.wavelength_max_unit = \"{1}\"",
+                             "storeas" : "content"
+                            },
+                           }
+                          }
+    SASINSTR_SRC_WL_SPR = {"variable" : "{0}.source.wavelength_spread = {1}",
+                           "unit" : "source.wavelength_spread_unit",
+                           "storeas" : "float",
+                           "attributes" :
+                           {"unit" :
+                            {"variable" :
+                             "{0}.source.wavelength_spread_unit = \"{1}\"",
+                             "storeas" : "content"
+                            },
+                           }
+                          }
+    SASINSTR_SRC = {"attributes" : {"name" : {"variable" :
+                                              "{0}.source.name = \"{1}\""}},
+                    "variable" : None,
+                    "children" : {"radiation" :
+                                  {"variable" :
+                                   "{0}.source.radiation = \"{1}\""},
+                                  "beam_size" : SASINSTR_SRC_BEAMSIZE,
+                                  "beam_shape" :
+                                  {"variable" :
+                                   "{0}.source.beam_shape = \"{1}\""},
+                                  "wavelength" : SASINSTR_SRC_WL,
+                                  "wavelength_min" : SASINSTR_SRC_WL_MIN,
+                                  "wavelength_max" : SASINSTR_SRC_WL_MAX,
+                                  "wavelength_spread" : SASINSTR_SRC_WL_SPR,
+                                 },
+                   }
+    SASINSTR_COLL_APER_ATTR = {"unit" : {"variable" : "{0}.size_unit = \"{1}\"",
+                                         "storeas" : "content"
+                                        },
+                              }
+    SASINSTR_COLL_APER_X = {"variable" : "{0}.size.x = {1}",
+                            "unit" : "size_unit",
+                            "storeas" : "float",
+                            "attributes" : SASINSTR_COLL_APER_ATTR
+                           }
+    SASINSTR_COLL_APER_Y = {"variable" : "{0}.size.y = {1}",
+                            "unit" : "size_unit",
+                            "storeas" : "float",
+                            "attributes" : SASINSTR_COLL_APER_ATTR
+                           }
+    SASINSTR_COLL_APER_Z = {"variable" : "{0}.size.z = {1}",
+                            "unit" : "size_unit",
+                            "storeas" : "float",
+                            "attributes" : SASINSTR_COLL_APER_ATTR
+                           }
+    SASINSTR_COLL_APER_SIZE = {"attributes" :
+                               {"unit" : {"variable" :
+                                          "{0}.size_unit = \"{1}\""}},
+                               "children" : {"storeas" : "float",
+                                             "x" : SASINSTR_COLL_APER_X,
+                                             "y" : SASINSTR_COLL_APER_Y,
+                                             "z" : SASINSTR_COLL_APER_Z,
+                                            }
+                              }
+    SASINSTR_COLL_APER_DIST = {"storeas" : "float",
+                               "attributes" :
+                               {"storeas" : "content",
+                                "unit" : {"variable" :
+                                          "{0}.distance_unit = \"{1}\""}
+                               },
+                               "variable" : "{0}.distance = {1}",
+                               "unit" : "distance_unit",
+                              }
+    SASINSTR_COLL_APER = {"variable" : None,
+                          "attributes" : {"name" : {"variable" :
+                                                    "{0}.name = \"{1}\""},
+                                          "type" : {"variable" :
+                                                    "{0}.type = \"{1}\""},
+                                         },
+                          "children" : {"size" : SASINSTR_COLL_APER_SIZE,
+                                        "distance" : SASINSTR_COLL_APER_DIST
+                                       }
+                         }
+    SASINSTR_COLL = {"attributes" :
+                     {"name" : {"variable" : "{0}.name = \"{1}\""}},
+                     "variable" : None,
+                     "children" :
+                     {"length" :
+                      {"variable" : "{0}.length = {1}",
+                       "unit" : "length_unit",
+                       "storeas" : "float",
+                       "attributes" :
+                       {"storeas" : "content",
+                        "unit" : {"variable" : "{0}.length_unit = \"{1}\""}
+                       },
+                      },
+                      "aperture" : SASINSTR_COLL_APER,
+                     },
+                    }
+    SASINSTR_DET_SDD = {"variable" : "{0}.distance = {1}",
+                        "unit" : "distance_unit",
+                        "attributes" :
+                        {"unit" :
+                         {"variable" : "{0}.distance_unit = \"{1}\"",
+                          "storeas" : "content"
+                         }
+                        },
+                       }
+    SASINSTR_DET_OFF_ATTR = {"unit" : {"variable" : "{0}.offset_unit = \"{1}\"",
+                                       "storeas" : "content"
+                                      },
+                            }
+    SASINSTR_DET_OFF_X = {"variable" : "{0}.offset.x = {1}",
+                          "unit" : "offset_unit",
+                          "attributes" : SASINSTR_DET_OFF_ATTR
+                         }
+    SASINSTR_DET_OFF_Y = {"variable" : "{0}.offset.y = {1}",
+                          "unit" : "offset_unit",
+                          "attributes" : SASINSTR_DET_OFF_ATTR
+                         }
+    SASINSTR_DET_OFF_Z = {"variable" : "{0}.offset.z = {1}",
+                          "unit" : "offset_unit",
+                          "attributes" : SASINSTR_DET_OFF_ATTR
+                         }
+    SASINSTR_DET_OFF = {"variable" : None,
+                        "children" : {"x" : SASINSTR_DET_OFF_X,
+                                      "y" : SASINSTR_DET_OFF_Y,
+                                      "z" : SASINSTR_DET_OFF_Z,
+                                     }
+                       }
+    SASINSTR_DET_OR_ATTR = {"unit" : "{0}.orientation_unit = \"{1}\"",
+                            "storeas" : "content"
+                           }
+    SASINSTR_DET_OR_ROLL = {"variable" : "{0}.orientation.x = {1}",
+                            "unit" : "orientation_unit",
+                            "attributes" : SASINSTR_DET_OR_ATTR
+                           }
+    SASINSTR_DET_OR_PITCH = {"variable" : "{0}.orientation.y = {1}",
+                             "unit" : "orientation_unit",
+                             "attributes" : SASINSTR_DET_OR_ATTR
+                            }
+    SASINSTR_DET_OR_YAW = {"variable" : "{0}.orientation.z = {1}",
+                           "unit" : "orientation_unit",
+                           "attributes" : SASINSTR_DET_OR_ATTR
+                          }
+    SASINSTR_DET_OR = {"variable" : None,
+                       "children" : {"roll" : SASINSTR_DET_OR_ROLL,
+                                     "pitch" : SASINSTR_DET_OR_PITCH,
+                                     "yaw" : SASINSTR_DET_OR_YAW,
+                                    }
+                      }
+    SASINSTR_DET_BC_X = {"variable" : "{0}.beam_center.x = {1}",
+                         "unit" : "beam_center_unit",
+                         "attributes" :
+                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
+                          "storeas" : "content"
+                         }
+                        }
+    SASINSTR_DET_BC_Y = {"variable" : "{0}.beam_center.y = {1}",
+                         "unit" : "beam_center_unit",
+                         "attributes" :
+                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
+                          "storeas" : "content"
+                         }
+                        }
+    SASINSTR_DET_BC_Z = {"variable" : "{0}.beam_center.z = {1}",
+                         "unit" : "beam_center_unit",
+                         "attributes" :
+                         {"unit" : "{0}.beam_center_unit = \"{1}\"",
+                          "storeas" : "content"
+                         }
+                        }
+    SASINSTR_DET_BC = {"variable" : None,
+                       "children" : {"x" : SASINSTR_DET_BC_X,
+                                     "y" : SASINSTR_DET_BC_Y,
+                                     "z" : SASINSTR_DET_BC_Z,
+                                    }
+                      }
+    SASINSTR_DET_PIXEL_X = {"variable" : "{0}.pixel_size.x = {1}",
+                            "unit" : "pixel_size_unit",
+                            "attributes" :
+                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
+                             "storeas" : "content"
+                            }
+                           }
+    SASINSTR_DET_PIXEL_Y = {"variable" : "{0}.pixel_size.y = {1}",
+                            "unit" : "pixel_size_unit",
+                            "attributes" :
+                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
+                             "storeas" : "content"
+                            }
+                           }
+    SASINSTR_DET_PIXEL_Z = {"variable" : "{0}.pixel_size.z = {1}",
+                            "unit" : "pixel_size_unit",
+                            "attributes" :
+                            {"unit" : "{0}.pixel_size_unit = \"{1}\"",
+                             "storeas" : "content"
+                            }
+                           }
+    SASINSTR_DET_PIXEL = {"variable" : None,
+                          "children" : {"x" : SASINSTR_DET_PIXEL_X,
+                                        "y" : SASINSTR_DET_PIXEL_Y,
+                                        "z" : SASINSTR_DET_PIXEL_Z,
+                                       }
+                         }
+    SASINSTR_DET_SLIT = {"variable" : "{0}.slit_length = {1}",
+                         "unit" : "slit_length_unit",
+                         "attributes" :
+                         {"unit" :
+                          {"variable" : "{0}.slit_length_unit = \"{1}\"",
+                           "storeas" : "content"
+                          }
+                         }
+                        }
+    SASINSTR_DET = {"storeas" : "float",
+                    "variable" : None,
+                    "attributes" : {"name" :
+                                    {"storeas" : "content",
+                                     "variable" : "{0}.name = \"{1}\"",
+                                    }
+                                   },
+                    "children" : {"name" : {"storeas" : "content",
+                                            "variable" : "{0}.name = \"{1}\"",
+                                           },
+                                  "SDD" : SASINSTR_DET_SDD,
+                                  "offset" : SASINSTR_DET_OFF,
+                                  "orientation" : SASINSTR_DET_OR,
+                                  "beam_center" : SASINSTR_DET_BC,
+                                  "pixel_size" : SASINSTR_DET_PIXEL,
+                                  "slit_length" : SASINSTR_DET_SLIT,
+                                 }
+                   }
+    SASINSTR = {"variable" : None,
+                "children" :
+                {"variable" : None,
+                 "name" : {"variable" : "{0}.instrument = \"{1}\""},
+                 "SASsource" : SASINSTR_SRC,
+                 "SAScollimation" : SASINSTR_COLL,
+                 "SASdetector" : SASINSTR_DET,
+                },
+               }
+    CANSAS_FORMAT = {"SASentry" :
+                     {"units_optional" : True,
+                      "variable" : None,
+                      "storeas" : "content",
+                      "attributes" : {"name" :
+                                      {"variable" :
+                                       "{0}.run_name[\"{3}\"] = \"{1}\""}},
+                      "children" : {"Title" : TITLE,
+                                    "Run" : RUN,
+                                    "SASdata" : SASDATA,
+                                    "SAStransmission_spectrum" : SASTRANSSPEC,
+                                    "SASsample" : SASSAMPLE,
+                                    "SASinstrument" : SASINSTR,
+                                    "SASprocess" : SASPROCESS,
+                                    "SASnote" : SASNOTE,
+                                    "<any>" : ANY,
+                                   }
+                     }
+                    }
+
+
+class CurrentLevel(object):
+    """
+    A helper class to hold information on where you are in the constants tree
+    """
+
+    current_level = ''
+    ns_variable = ''
+    ns_datatype = ''
+    ns_optional = True
+
+    def __init__(self):
+        self.current_level = {}
+        self.ns_variable = ''
+        self.ns_datatype = "content"
+        self.ns_optional = True
+
+    def get_current_level(self):
+        """
+        Helper method to get the current_level map
+        """
+        return self.current_level
+
+    def get_data_type(self):
+        """
+        Helper method to get the ns_datatype label
+        """
+        return self.ns_datatype
+
+    def get_variable(self):
+        """
+        Helper method to get the ns_variable label
+        """
+        return self.ns_variable
Index: src/sas/sascalc/dataloader/readers/cansas_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/cansas_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1181 @@
+"""
+    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
+import os
+import sys
+import datetime
+import inspect
+# For saving individual sections of data
+from sas.sascalc.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.data_info import Collimation
+from sas.sascalc.dataloader.data_info import TransmissionSpectrum
+from sas.sascalc.dataloader.data_info import Detector
+from sas.sascalc.dataloader.data_info import Process
+from sas.sascalc.dataloader.data_info import Aperture
+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
+
+# The following 2 imports *ARE* used. Do not remove either.
+import xml.dom.minidom
+from xml.dom.minidom import parseString
+
+_ZERO = 1e-16
+PREPROCESS = "xmlpreprocess"
+ENCODING = "encoding"
+RUN_NAME_DEFAULT = "None"
+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
+
+# DO NOT REMOVE Called by outside packages:
+#    sas.perspectives.invariant.invariant_state
+#    sas.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.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
+
+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}"
+    logging = None
+    errors = None
+    type_name = "canSAS"
+    ## 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 __init__(self):
+        ## List of errors
+        self.errors = set()
+        self.logging = []
+        self.encoding = None
+
+    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
+        return False
+
+    def load_file_and_schema(self, xml_file):
+        """
+        Loads the file and associates a schema, if a known schema exists
+
+        :param xml_file: The xml file path sent to Reader.read
+        """
+        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
+        cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0")
+        schema_path = "{0}/sas/dataloader/readers/schema/{1}".format\
+                (base, cansas_defaults.get("schema")).replace("\\", "/")
+
+        # Link a schema to the XML file.
+        self.set_schema(schema_path)
+        return cansas_defaults
+
+    def read(self, xml_file):
+        """
+        Validate and read in an xml_file file in the canSAS format.
+
+        :param xml_file: A canSAS file path in proper XML format
+        """
+        # output - Final list of Data1D objects
+        output = []
+        # ns - Namespace hierarchy for current xml_file object
+        ns_list = []
+
+        # Check that the file exists
+        if os.path.isfile(xml_file):
+            basename = os.path.basename(xml_file)
+            _, extension = os.path.splitext(basename)
+            # If the file type is not allowed, return nothing
+            if extension in self.ext or self.allow_all:
+                # Get the file location of
+                cansas_defaults = self.load_file_and_schema(xml_file)
+
+                # Try to load the file, but raise an error if unable to.
+                # Check the file matches the XML schema
+                try:
+                    if self.is_cansas(extension):
+                        # Get each SASentry from XML file and add it to a list.
+                        entry_list = self.xmlroot.xpath(
+                                '/ns:SASroot/ns:SASentry',
+                                namespaces={'ns': cansas_defaults.get("ns")})
+                        ns_list.append("SASentry")
+
+                        # If multiple files, modify the name for each is unique
+                        increment = 0
+                        # Parse each SASentry item
+                        for entry in entry_list:
+                            # Define a new Data1D object with zeroes for
+                            # x_vals and y_vals
+                            data1d = Data1D(numpy.empty(0), numpy.empty(0),
+                                            numpy.empty(0), numpy.empty(0))
+                            data1d.dxl = numpy.empty(0)
+                            data1d.dxw = numpy.empty(0)
+
+                            # If more than one SASentry, increment each in order
+                            name = basename
+                            if len(entry_list) - 1 > 0:
+                                name += "_{0}".format(increment)
+                                increment += 1
+
+                            # Set the Data1D name and then parse the entry.
+                            # The entry is appended to a list of entry values
+                            data1d.filename = name
+                            data1d.meta_data["loader"] = "CanSAS 1D"
+
+                            # Get all preprocessing events and encoding
+                            self.set_processing_instructions()
+                            data1d.meta_data[PREPROCESS] = \
+                                    self.processing_instructions
+
+                            # Parse the XML file
+                            return_value, extras = \
+                                self._parse_entry(entry, ns_list, data1d)
+                            del extras[:]
+
+                            return_value = self._final_cleanup(return_value)
+                            output.append(return_value)
+                    else:
+                        output.append("Invalid XML at: {0}".format(\
+                                                    self.find_invalid_xml()))
+                except:
+                    # If the file does not match the schema, raise this error
+                    raise RuntimeError, "%s cannot be read" % xml_file
+                return output
+        # Return a list of parsed entries that dataloader can manage
+        return None
+
+    def _final_cleanup(self, data1d):
+        """
+        Final cleanup of the Data1D object to be sure it has all the
+        appropriate information needed for perspectives
+
+        :param data1d: Data1D object that has been populated
+        """
+        # Final cleanup
+        # Remove empty nodes, verify array sizes are correct
+        for error in self.errors:
+            data1d.errors.append(error)
+        self.errors.clear()
+        numpy.trim_zeros(data1d.x)
+        numpy.trim_zeros(data1d.y)
+        numpy.trim_zeros(data1d.dy)
+        size_dx = data1d.dx.size
+        size_dxl = data1d.dxl.size
+        size_dxw = data1d.dxw.size
+        if data1d._xunit != data1d.x_unit:
+            data1d.x_unit = data1d._xunit
+        if data1d._yunit != data1d.y_unit:
+            data1d.y_unit = data1d._yunit
+        if size_dxl == 0 and size_dxw == 0:
+            data1d.dxl = None
+            data1d.dxw = None
+            numpy.trim_zeros(data1d.dx)
+        elif size_dx == 0:
+            data1d.dx = None
+            size_dx = size_dxl
+            numpy.trim_zeros(data1d.dxl)
+            numpy.trim_zeros(data1d.dxw)
+        return data1d
+
+    def _create_unique_key(self, dictionary, name, numb=0):
+        """
+        Create a unique key value for any dictionary to prevent overwriting
+        Recurses 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 _unit_conversion(self, node, new_current_level, data1d, \
+                                                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 new_current_level: cansas_constants level as returned by
+            iterate_namespace
+        :param attr: The attributes of the node
+        :param data1d: Where the values will be saved
+        :param node_value: The value of the current dom node
+        """
+        attr = node.attrib
+        value_unit = ''
+        err_msg = None
+        default_unit = None
+        if 'unit' in attr and new_current_level.get('unit') is not None:
+            try:
+                local_unit = attr['unit']
+                if isinstance(node_value, float) is False:
+                    exec("node_value = float({0})".format(node_value))
+                unitname = new_current_level.get("unit")
+                exec "default_unit = data1d.{0}".format(unitname)
+                if local_unit is not None and default_unit is not None 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)
+        node_value = "float({0})".format(node_value)
+        return node_value, value_unit
+
+    def _check_for_empty_data(self, data1d):
+        """
+        Creates an empty data set if no data is passed to the reader
+
+        :param data1d: presumably a Data1D object
+        """
+        if data1d == None:
+            self.errors = set()
+            x_vals = numpy.empty(0)
+            y_vals = numpy.empty(0)
+            dx_vals = numpy.empty(0)
+            dy_vals = numpy.empty(0)
+            dxl = numpy.empty(0)
+            dxw = numpy.empty(0)
+            data1d = Data1D(x_vals, y_vals, dx_vals, dy_vals)
+            data1d.dxl = dxl
+            data1d.dxw = dxw
+        return data1d
+
+    def _handle_special_cases(self, tagname, data1d, children):
+        """
+        Handle cases where the data type in Data1D is a dictionary or list
+
+        :param tagname: XML tagname in use
+        :param data1d: The original Data1D object
+        :param children: Child nodes of node
+        :param node: existing node with tag name 'tagname'
+        """
+        if tagname == "SASdetector":
+            data1d = Detector()
+        elif tagname == "SAScollimation":
+            data1d = Collimation()
+        elif tagname == "SAStransmission_spectrum":
+            data1d = TransmissionSpectrum()
+        elif tagname == "SASprocess":
+            data1d = Process()
+            for child in children:
+                if child.tag.replace(self.base_ns, "") == "term":
+                    term_attr = {}
+                    for attr in child.keys():
+                        term_attr[attr] = \
+                            ' '.join(child.get(attr).split())
+                    if child.text is not None:
+                        term_attr['value'] = \
+                            ' '.join(child.text.split())
+                    data1d.term.append(term_attr)
+        elif tagname == "aperture":
+            data1d = Aperture()
+        if tagname == "Idata" and children is not None:
+            data1d = self._check_for_empty_resolution(data1d, children)
+        return data1d
+
+    def _check_for_empty_resolution(self, data1d, children):
+        """
+        A method to check all resolution data sets are the same size as I and Q
+        """
+        dql_exists = False
+        dqw_exists = False
+        dq_exists = False
+        di_exists = False
+        for child in children:
+            tag = child.tag.replace(self.base_ns, "")
+            if tag == "dQl":
+                dql_exists = True
+            if tag == "dQw":
+                dqw_exists = True
+            if tag == "Qdev":
+                dq_exists = True
+            if tag == "Idev":
+                di_exists = True
+        if dqw_exists and dql_exists == False:
+            data1d.dxl = numpy.append(data1d.dxl, 0.0)
+        elif dql_exists and dqw_exists == False:
+            data1d.dxw = numpy.append(data1d.dxw, 0.0)
+        elif dql_exists == False and dqw_exists == False \
+                                            and dq_exists == False:
+            data1d.dx = numpy.append(data1d.dx, 0.0)
+        if di_exists == False:
+            data1d.dy = numpy.append(data1d.dy, 0.0)
+        return data1d
+
+    def _restore_original_case(self,
+                               tagname_original,
+                               tagname,
+                               save_data1d,
+                               data1d):
+        """
+        Save the special case data to the appropriate location and restore
+        the original Data1D object
+
+        :param tagname_original: Unmodified tagname for the node
+        :param tagname: modified tagname for the node
+        :param save_data1d: The original Data1D object
+        :param data1d: If a special case was handled, an object of that type
+        """
+        if tagname_original == "SASdetector":
+            save_data1d.detector.append(data1d)
+        elif tagname_original == "SAScollimation":
+            save_data1d.collimation.append(data1d)
+        elif tagname == "SAStransmission_spectrum":
+            save_data1d.trans_spectrum.append(data1d)
+        elif tagname_original == "SASprocess":
+            save_data1d.process.append(data1d)
+        elif tagname_original == "aperture":
+            save_data1d.aperture.append(data1d)
+        else:
+            save_data1d = data1d
+        return save_data1d
+
+    def _handle_attributes(self, node, data1d, cs_values, tagname):
+        """
+        Process all of the attributes for a node
+        """
+        attr = node.attrib
+        if attr is not None:
+            for key in node.keys():
+                try:
+                    node_value, unit = self._get_node_value(node, cs_values, \
+                                                   data1d, tagname)
+                    cansas_attrib = \
+                        cs_values.current_level.get("attributes").get(key)
+                    attrib_variable = cansas_attrib.get("variable")
+                    if key == 'unit' and unit != '':
+                        attrib_value = unit
+                    else:
+                        attrib_value = node.attrib[key]
+                    store_attr = attrib_variable.format("data1d",
+                                                        attrib_value,
+                                                        key,
+                                                        node_value)
+                    exec store_attr
+                except AttributeError:
+                    pass
+        return data1d
+
+    def _get_node_value(self, node, cs_values, data1d, tagname):
+        """
+        Get the value of a node and any applicable units
+
+        :param node: The XML node to get the value of
+        :param cs_values: A CansasConstants.CurrentLevel object
+        :param attr: The node attributes
+        :param dataid: The working object to be modified
+        :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 == "":
+            node_value = None
+        if node_value is not None:
+            node_value = ' '.join(node_value.split())
+
+        # If the value is a float, compile with units.
+        if cs_values.ns_datatype == "float":
+            # If an empty value is given, set as zero.
+            if node_value is None or node_value.isspace() \
+                                    or node_value.lower() == "nan":
+                node_value = "0.0"
+            #Convert the value to the base units
+            node_value, units = self._unit_conversion(node, \
+                        cs_values.current_level, data1d, tagname, node_value)
+
+        # If the value is a timestamp, convert to a datetime object
+        elif cs_values.ns_datatype == "timestamp":
+            if node_value is None or node_value.isspace():
+                pass
+            else:
+                try:
+                    node_value = \
+                        datetime.datetime.fromtimestamp(node_value)
+                except ValueError:
+                    node_value = None
+        return node_value, units
+
+    def _parse_entry(self, dom, names=None, data1d=None, extras=None):
+        """
+        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
+        :param names: A list of element names that lead up to the dom object
+        :param data1d: The data1d object that will be modified
+        :param extras: Any values that should go into meta_data when data1d
+            is not a Data1D object
+        """
+
+        if extras is None:
+            extras = []
+        if names is None or names == []:
+            names = ["SASentry"]
+
+        data1d = self._check_for_empty_data(data1d)
+
+        self.base_ns = "{0}{1}{2}".format("{", \
+                            CANSAS_NS.get(self.cansas_version).get("ns"), "}")
+        tagname = ''
+        tagname_original = ''
+
+        # Go through each child in the parent element
+        for node in dom:
+            try:
+                # Get the element name and set the current names level
+                tagname = node.tag.replace(self.base_ns, "")
+                tagname_original = tagname
+                if tagname == "fitting_plug_in" or tagname == "pr_inversion" or\
+                    tagname == "invariant":
+                    continue
+                names.append(tagname)
+                children = node.getchildren()
+                if len(children) == 0:
+                    children = None
+                save_data1d = data1d
+
+                # Look for special cases
+                data1d = self._handle_special_cases(tagname, data1d, children)
+
+                # Get where to store content
+                cs_values = CONSTANTS.iterate_namespace(names)
+                # If the element is a child element, recurse
+                if children is not None:
+                    # Returned value is new Data1D object with all previous and
+                    # new values in it.
+                    data1d, extras = self._parse_entry(node,
+                                                       names, data1d, extras)
+
+                #Get the information from the node
+                node_value, _ = self._get_node_value(node, cs_values, \
+                                                            data1d, tagname)
+
+                # If appending to a dictionary (meta_data | run_name)
+                # make sure the key is unique
+                if cs_values.ns_variable == "{0}.meta_data[\"{2}\"] = \"{1}\"":
+                    # If we are within a Process, Detector, Collimation or
+                    # Aperture instance, pull out old data1d
+                    tagname = self._create_unique_key(data1d.meta_data, \
+                                                      tagname, 0)
+                    if isinstance(data1d, Data1D) == False:
+                        store_me = cs_values.ns_variable.format("data1d", \
+                                                            node_value, tagname)
+                        extras.append(store_me)
+                        cs_values.ns_variable = None
+                if cs_values.ns_variable == "{0}.run_name[\"{2}\"] = \"{1}\"":
+                    tagname = self._create_unique_key(data1d.run_name, \
+                                                      tagname, 0)
+
+                # Check for Data1D object and any extra commands to save
+                if isinstance(data1d, Data1D):
+                    for item in extras:
+                        exec item
+                # Don't bother saving empty information unless it is a float
+                if cs_values.ns_variable is not None and \
+                            node_value is not None and \
+                            node_value.isspace() == False:
+                    # Format a string and then execute it.
+                    store_me = cs_values.ns_variable.format("data1d", \
+                                                            node_value, tagname)
+                    exec store_me
+                # Get attributes and process them
+                data1d = self._handle_attributes(node, data1d, cs_values, \
+                                                 tagname)
+
+            except TypeError:
+                pass
+            except Exception as excep:
+                exc_type, exc_obj, exc_tb = sys.exc_info()
+                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
+                print(excep, exc_type, fname, exc_tb.tb_lineno, \
+                      tagname, exc_obj)
+            finally:
+                # Save special cases in original data1d object
+                # then restore the data1d
+                save_data1d = self._restore_original_case(tagname_original, \
+                                                tagname, save_data1d, data1d)
+                if tagname_original == "fitting_plug_in" or \
+                    tagname_original == "invariant" or \
+                    tagname_original == "pr_inversion":
+                    pass
+                else:
+                    data1d = save_data1d
+                    # Remove tagname from names to restore original base
+                    names.remove(tagname_original)
+        return data1d, extras
+
+    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://svn.smallangles.net/svn/canSAS/1dwg/trunk/"
+        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 == 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 the 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.x_unit})
+            if len(datainfo.y) >= i:
+                self.write_node(point, "I", datainfo.y[i],
+                                {'unit': datainfo.y_unit})
+            if datainfo.dy != 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:
+                self.write_node(point, "Qdev", datainfo.dx[i],
+                                {'unit': datainfo.x_unit})
+            if datainfo.dxw != 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:
+                self.write_node(point, "dQl", datainfo.dxl[i],
+                                {'unit': datainfo.x_unit})
+
+    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 != 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 == 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 == 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 == 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})
+            written = written | self.write_node(det, "slit_length",
+                                                item.slit_length,
+                                                {"unit": item.slit_length_unit})
+            if written == True:
+                self.append(pix, det)
+
+    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:
+                value = term['value']
+                del term['value']
+                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_origin(self, entry_node, doc, frm):
+        """
+        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 frm:
+            frm = inspect.stack()[1]
+        mod_name = frm[1].replace("\\", "/").replace(".pyc", "")
+        mod_name = mod_name.replace(".py", "")
+        mod = mod_name.split("sas/")
+        mod_name = mod[1]
+        if mod_name != "dataloader/readers/cansas_reader":
+            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
+
+    def _to_xml_doc(self, datainfo):
+        """
+        Create an XML document to contain the content of a Data1D
+
+        :param datainfo: Data1D object
+        """
+        if not issubclass(datainfo.__class__, Data1D):
+            raise RuntimeError, "The cansas writer expects a Data1D instance"
+
+        # 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
+        self._write_data(datainfo, entry_node)
+        # Transmission Spectrum Info
+        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.
+        frm = inspect.stack()[1]
+        doc, entry_node = self._check_origin(entry_node, doc, frm)
+        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 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 == None:
+            self.encoding = "UTF-8"
+        doc.write(file_ref, encoding=self.encoding,
+                  pretty_print=True, xml_declaration=True)
+        file_ref.close()
+
+    # 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 != 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:
+                                logging.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:
+                            logging.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
Index: src/sas/sascalc/dataloader/readers/danse_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/danse_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/danse_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,280 @@
+"""
+    DANSE/SANS file reader
+"""
+############################################################################
+#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, University of Tennessee
+#############################################################################
+import math
+import os
+import sys
+import numpy
+import logging
+from sas.sascalc.dataloader.data_info import Data2D, Detector
+from sas.sascalc.dataloader.manipulations import reader2D_converter
+
+# Look for unit converter
+has_converter = True
+try:
+    from sas.sascalc.data_util.nxsunit import Converter
+except:
+    has_converter = False
+
+
+class Reader:
+    """
+    Example data manipulation
+    """
+    ## File type
+    type_name = "DANSE"
+    ## Wildcards
+    type = ["DANSE files (*.sans)|*.sans"]
+    ## Extension
+    ext  = ['.sans', '.SANS']
+        
+    def read(self, filename=None):
+        """
+        Open and read the data in a file
+        @param file: path of the file
+        """
+        
+        read_it = False
+        for item in self.ext:
+            if filename.lower().find(item) >= 0:
+                read_it = True
+                
+        if read_it:
+            try:
+                datafile = open(filename, 'r')
+            except:
+                raise  RuntimeError,"danse_reader cannot open %s" % (filename)
+        
+            # defaults
+            # wavelength in Angstrom
+            wavelength = 10.0
+            # Distance in meter
+            distance   = 11.0
+            # Pixel number of center in x
+            center_x   = 65
+            # Pixel number of center in y
+            center_y   = 65
+            # Pixel size [mm]
+            pixel      = 5.0
+            # Size in x, in pixels
+            size_x     = 128
+            # Size in y, in pixels
+            size_y     = 128
+            # Format version
+            fversion   = 1.0
+            
+            output = Data2D()
+            output.filename = os.path.basename(filename)
+            detector = Detector()
+            output.detector.append(detector)
+            
+            output.data = numpy.zeros([size_x,size_y])
+            output.err_data = numpy.zeros([size_x, size_y])
+            
+            data_conv_q = None
+            data_conv_i = None
+            
+            if has_converter == True and output.Q_unit != '1/A':
+                data_conv_q = Converter('1/A')
+                # Test it
+                data_conv_q(1.0, output.Q_unit)
+                
+            if has_converter == True and output.I_unit != '1/cm':
+                data_conv_i = Converter('1/cm')
+                # Test it
+                data_conv_i(1.0, output.I_unit)
+        
+            read_on = True
+            while read_on:
+                line = datafile.readline()
+                if line.find("DATA:") >= 0:
+                    read_on = False
+                    break
+                toks = line.split(':')
+                if toks[0] == "FORMATVERSION":
+                    fversion = float(toks[1])
+                if toks[0] == "WAVELENGTH":
+                    wavelength = float(toks[1])
+                elif toks[0] == "DISTANCE":
+                    distance = float(toks[1])
+                elif toks[0] == "CENTER_X":
+                    center_x = float(toks[1])
+                elif toks[0] == "CENTER_Y":
+                    center_y = float(toks[1])
+                elif toks[0] == "PIXELSIZE":
+                    pixel = float(toks[1])
+                elif toks[0] == "SIZE_X":
+                    size_x = int(toks[1])
+                elif toks[0] == "SIZE_Y":
+                    size_y = int(toks[1])
+            
+            # Read the data
+            data = []
+            error = []
+            if fversion == 1.0:
+                data_str = datafile.readline()
+                data = data_str.split(' ')
+            else:
+                read_on = True
+                while read_on:
+                    data_str = datafile.readline()
+                    if len(data_str) == 0:
+                        read_on = False
+                    else:
+                        toks = data_str.split()
+                        try:
+                            val = float(toks[0])
+                            err = float(toks[1])
+                            if data_conv_i is not None:
+                                val = data_conv_i(val, units=output._yunit)
+                                err = data_conv_i(err, units=output._yunit)
+                            data.append(val)
+                            error.append(err)
+                        except:
+                            logging.info("Skipping line:%s,%s" %(data_str,
+                                                                sys.exc_value))
+            
+            # Initialize
+            x_vals = []
+            y_vals = []
+            ymin = None
+            ymax = None
+            xmin = None
+            xmax = None
+            
+            # Qx and Qy vectors
+            theta = pixel / distance / 100.0
+            stepq = 4.0 * math.pi / wavelength * math.sin(theta / 2.0)
+            for i_x in range(size_x):
+                theta = (i_x - center_x + 1) * pixel / distance / 100.0
+                qx = 4.0 * math.pi / wavelength * math.sin(theta / 2.0)
+                
+                if has_converter == True and output.Q_unit != '1/A':
+                    qx = data_conv_q(qx, units=output.Q_unit)
+                
+                x_vals.append(qx)
+                if xmin == None or qx < xmin:
+                    xmin = qx
+                if xmax == None or qx > xmax:
+                    xmax = qx
+            
+            ymin = None
+            ymax = None
+            for i_y in range(size_y):
+                theta = (i_y - center_y + 1) * pixel / distance / 100.0
+                qy = 4.0 * math.pi / wavelength * math.sin(theta/2.0)
+                
+                if has_converter == True and output.Q_unit != '1/A':
+                    qy = data_conv_q(qy, units=output.Q_unit)
+                
+                y_vals.append(qy)
+                if ymin == None or qy < ymin:
+                    ymin = qy
+                if ymax == None or qy > ymax:
+                    ymax = qy
+            
+            # Store the data in the 2D array
+            i_x = 0
+            i_y = -1
+            
+            for i_pt in range(len(data)):
+                try:
+                    value = float(data[i_pt])
+                except:
+                    # For version 1.0, the data were still
+                    # stored as strings at this point.
+                    msg = "Skipping entry (v1.0):%s,%s" % (str(data[i_pt]),
+                                                           sys.exc_value)
+                    logging.info(msg)
+                
+                # Get bin number
+                if math.fmod(i_pt, size_x) == 0:
+                    i_x = 0
+                    i_y += 1
+                else:
+                    i_x += 1
+                    
+                output.data[i_y][i_x] = value
+                if fversion>1.0:
+                    output.err_data[i_y][i_x] = error[i_pt]
+                
+            # Store all data
+            # Store wavelength
+            if has_converter == True and output.source.wavelength_unit != 'A':
+                conv = Converter('A')
+                wavelength = conv(wavelength,
+                                  units=output.source.wavelength_unit)
+            output.source.wavelength = wavelength
+                
+            # Store distance
+            if has_converter == True and detector.distance_unit != 'm':
+                conv = Converter('m')
+                distance = conv(distance, units=detector.distance_unit)
+            detector.distance = distance
+            
+            # Store pixel size
+            if has_converter == True and detector.pixel_size_unit != 'mm':
+                conv = Converter('mm')
+                pixel = conv(pixel, units=detector.pixel_size_unit)
+            detector.pixel_size.x = pixel
+            detector.pixel_size.y = pixel
+
+            # Store beam center in distance units
+            detector.beam_center.x = center_x * pixel
+            detector.beam_center.y = center_y * pixel
+            
+            # Store limits of the image (2D array)
+            xmin = xmin - stepq / 2.0
+            xmax = xmax + stepq / 2.0
+            ymin = ymin - stepq /2.0
+            ymax = ymax + stepq / 2.0
+            
+            if has_converter == True and output.Q_unit != '1/A':
+                xmin = data_conv_q(xmin, units=output.Q_unit)
+                xmax = data_conv_q(xmax, units=output.Q_unit)
+                ymin = data_conv_q(ymin, units=output.Q_unit)
+                ymax = data_conv_q(ymax, units=output.Q_unit)
+            output.xmin = xmin
+            output.xmax = xmax
+            output.ymin = ymin
+            output.ymax = ymax
+            
+            # Store x and y axis bin centers
+            output.x_bins = x_vals
+            output.y_bins = y_vals
+           
+            # Units
+            if data_conv_q is not None:
+                output.xaxis("\\rm{Q_{x}}", output.Q_unit)
+                output.yaxis("\\rm{Q_{y}}", output.Q_unit)
+            else:
+                output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
+                output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
+                
+            if data_conv_i is not None:
+                output.zaxis("\\rm{Intensity}", output.I_unit)
+            else:
+                output.zaxis("\\rm{Intensity}", "cm^{-1}")
+           
+            if not fversion >= 1.0:
+                msg = "Danse_reader can't read this file %s" % filename
+                raise ValueError, msg
+            else:
+                logging.info("Danse_reader Reading %s \n" % filename)
+            
+            # Store loading process information
+            output.meta_data['loader'] = self.type_name
+            output = reader2D_converter(output)
+            return output
+        
+        return None
Index: src/sas/sascalc/dataloader/readers/defaults.json
===================================================================
--- src/sas/sascalc/dataloader/readers/defaults.json	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/defaults.json	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,42 @@
+{
+   "SasLoader":{
+      "FileType":[
+         {
+            "-extension":".xml",
+            "-reader":"cansas_reader"
+         },
+         {
+            "-extension":".ses",
+            "-reader":"sesans_reader"
+         },
+         {
+            "-extension":".txt",
+            "-reader":"ascii_reader"
+         },
+         {
+            "-extension":".asc",
+            "-reader":"IgorReader"
+         },
+         {
+            "-extension":".dat",
+            "-reader":"red2d_reader"
+         },
+         {
+            "-extension":".abs",
+            "-reader":"abs_reader"
+         },
+         {
+            "-extension":".d1d",
+            "-reader":"hfir1d_reader"
+         },
+         {
+            "-extension":".sans",
+            "-reader":"danse_reader"
+         },
+         {
+            "-extension":".nxs",
+            "-reader":"nexus_reader"
+         }
+      ]
+   }
+}
Index: src/sas/sascalc/dataloader/readers/hfir1d_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/hfir1d_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/hfir1d_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,129 @@
+"""
+    HFIR 1D 4-column data reader
+"""
+#####################################################################
+#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.
+#See the license text in license.txt
+#copyright 2008, University of Tennessee
+######################################################################
+import numpy
+import os
+from sas.sascalc.dataloader.data_info import Data1D
+
+# Check whether we have a converter available
+has_converter = True
+try:
+    from sas.sascalc.data_util.nxsunit import Converter
+except:
+    has_converter = False
+
+class Reader(object):
+    """
+    Class to load HFIR 1D 4-column files
+    """
+    ## File type
+    type_name = "HFIR 1D"
+    ## Wildcards
+    type = ["HFIR 1D files (*.d1d)|*.d1d"]
+    ## List of allowed extensions
+    ext = ['.d1d']
+    
+    def read(self, path):
+        """ 
+        Load data file
+        
+        :param path: file path
+        
+        :return: Data1D object, or None
+        
+        :raise RuntimeError: when the file can't be opened
+        :raise ValueError: when the length of the data vectors are inconsistent
+        """
+        if os.path.isfile(path):
+            basename = os.path.basename(path)
+            root, extension = os.path.splitext(basename)
+            if extension.lower() in self.ext:
+                try:
+                    input_f = open(path,'r')
+                except:
+                    raise  RuntimeError, "hfir1d_reader: cannot open %s" % path
+                buff = input_f.read()
+                lines = buff.split('\n')
+                x = numpy.zeros(0)
+                y = numpy.zeros(0)
+                dx = numpy.zeros(0)
+                dy = numpy.zeros(0)
+                output = Data1D(x, y, dx=dx, dy=dy)
+                self.filename = output.filename = basename
+           
+                data_conv_q = None
+                data_conv_i = None
+                
+                if has_converter == True and output.x_unit != '1/A':
+                    data_conv_q = Converter('1/A')
+                    # Test it
+                    data_conv_q(1.0, output.x_unit)
+                    
+                if has_converter == True and output.y_unit != '1/cm':
+                    data_conv_i = Converter('1/cm')
+                    # Test it
+                    data_conv_i(1.0, output.y_unit)
+                           
+                for line in lines:
+                    toks = line.split()
+                    try:
+                        _x = float(toks[0])
+                        _y = float(toks[1])
+                        _dx = float(toks[3])
+                        _dy = float(toks[2])
+                        
+                        if data_conv_q is not None:
+                            _x = data_conv_q(_x, units=output.x_unit)
+                            _dx = data_conv_q(_dx, units=output.x_unit)
+                            
+                        if data_conv_i is not None:
+                            _y = data_conv_i(_y, units=output.y_unit)
+                            _dy = data_conv_i(_dy, units=output.y_unit)
+                                                    
+                        x = numpy.append(x, _x)
+                        y = numpy.append(y, _y)
+                        dx = numpy.append(dx, _dx)
+                        dy = numpy.append(dy, _dy)
+                    except:
+                        # Couldn't parse this line, skip it 
+                        pass
+                         
+                # Sanity check
+                if not len(y) == len(dy):
+                    msg = "hfir1d_reader: y and dy have different length"
+                    raise RuntimeError, msg
+                if not len(x) == len(dx):
+                    msg = "hfir1d_reader: x and dx have different length"
+                    raise RuntimeError, msg
+
+                # If the data length is zero, consider this as
+                # though we were not able to read the file.
+                if len(x) == 0:
+                    raise RuntimeError, "hfir1d_reader: could not load file"
+               
+                output.x = x
+                output.y = y
+                output.dy = dy
+                output.dx = dx
+                if data_conv_q is not None:
+                    output.xaxis("\\rm{Q}", output.x_unit)
+                else:
+                    output.xaxis("\\rm{Q}", 'A^{-1}')
+                if data_conv_i is not None:
+                    output.yaxis("\\rm{Intensity}", output.y_unit)
+                else:
+                    output.yaxis("\\rm{Intensity}", "cm^{-1}")
+                
+                # Store loading process information
+                output.meta_data['loader'] = self.type_name
+                return output
+        else:
+            raise RuntimeError, "%s is not a file" % path
+        return None
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 b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,96 @@
+#####################################################################
+#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.
+#See the license text in license.txt
+#copyright 2012, University of Tennessee
+######################################################################
+"""
+    Nexus reader for 2D data reduced by Mantid
+"""
+import os
+from sas.sascalc.dataloader.data_info import Data2D
+
+
+class Reader:
+    """
+        Nexus reader for 2D data reduced by Mantid
+    """
+    ## File type
+    type_name = "NXS"
+    ## Wildcards
+    type = ["Nexus files (*.nxs)|*.nxs"]
+    ## Extension
+    ext = ['.nxs']
+        
+    def read(self, filename=None):
+        """
+            Open and read the data in a file
+        
+            :param filename: path of the file
+        """
+        try:
+            import nxs
+        except:
+            msg = "Error reading Nexus file: Nexus package is missing.\n"
+            msg += "  Get it from http://http://www.nexusformat.org/"
+            raise RuntimeError, msg
+        
+        # Instantiate data object
+        output = Data2D()
+        output.filename = os.path.basename(filename)
+
+        fd = nxs.open(filename, 'rw')
+        
+        # Read in the 2D data
+        fd.opengroup('mantid_workspace_1')
+        fd.opengroup('workspace')
+        fd.opendata('values')
+        output.data = fd.getdata().copy()
+        fd.closedata()
+        
+        # Read in the errors
+        fd.opendata('errors')
+        output.err_data = fd.getdata().copy()
+        fd.closedata()
+        
+        # Read in the values on each axis
+        fd.opendata('axis1')
+        output.x_bins = fd.getdata().copy()
+        fd.closedata()
+        
+        fd.opendata('axis2')
+        output.y_bins = fd.getdata().copy()
+        fd.closedata()
+        fd.closegroup()
+                
+        output.xmin = min(output.x_bins)
+        output.xmax = max(output.x_bins)
+        output.ymin = min(output.y_bins)
+        output.ymax = max(output.y_bins)
+        
+        output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
+        output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
+        output.zaxis("\\rm{Intensity}", "cm^{-1}")
+
+        # Meta data
+        fd.opendata('title')
+        output.title = fd.getdata()
+        fd.closedata()
+        
+        fd.opengroup('instrument')
+        fd.opendata('name')
+        output.instrument = fd.getdata()
+        fd.closedata()
+        fd.closegroup()
+
+        fd.opengroup('logs')
+        fd.opengroup('run_number')
+        fd.opendata('value')
+        output.run = fd.getdata()
+
+        fd.close()
+
+        # Store loading process information
+        output.meta_data['loader'] = self.type_name
+        return output
Index: src/sas/sascalc/dataloader/readers/red2d_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/red2d_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/red2d_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,367 @@
+"""
+    TXT/IGOR 2D Q Map file reader
+"""
+#####################################################################
+#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. 
+#See the license text in license.txt
+#copyright 2008, University of Tennessee
+######################################################################
+import os
+import numpy
+import math
+from sas.sascalc.dataloader.data_info import Data2D, Detector
+
+# Look for unit converter
+has_converter = True
+try:
+    from sas.sascalc.data_util.nxsunit import Converter
+except:
+    has_converter = False
+    
+    
+def check_point(x_point):
+    """
+    check point validity
+    """
+    # set zero for non_floats
+    try:
+        return float(x_point)
+    except:
+        return 0
+      
+        
+class Reader:
+    """ Simple data reader for Igor data files """
+    ## File type
+    type_name = "IGOR/DAT 2D Q_map"
+    ## Wildcards
+    type = ["IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"]
+    ## Extension
+    ext = ['.DAT', '.dat']
+    
+    def write(self, filename, data):
+        """
+        Write to .dat
+        
+        :param filename: file name to write
+        :param data: data2D
+        """
+        import time
+        # Write the file
+        fd = open(filename, 'w')
+        t = time.localtime()
+        time_str = time.strftime("%H:%M on %b %d %y", t)
+        
+        header_str = "Data columns are Qx - Qy - I(Qx,Qy)\n\nASCII data"
+        header_str += " created at %s \n\n" % time_str
+        # simple 2D header
+        fd.write(header_str)
+        # write qx qy I values
+        for i in range(len(data.data)):
+            fd.write("%g  %g  %g\n" % (data.qx_data[i],
+                                        data.qy_data[i],
+                                       data.data[i]))
+        # close
+        fd.close()
+
+    def read(self, filename=None):
+        """ Read file """
+        if not os.path.isfile(filename):
+            raise ValueError, \
+            "Specified file %s is not a regular file" % filename
+
+        # Read file
+        f = open(filename, 'r')
+        buf = f.read()
+        f.close()
+        # Instantiate data object
+        output = Data2D()
+        output.filename = os.path.basename(filename)
+        detector = Detector()
+        if len(output.detector) > 0:
+            print str(output.detector[0])
+        output.detector.append(detector)
+                
+        # Get content
+        dataStarted = False
+        
+        ## Defaults
+        lines = buf.split('\n')
+        x = []
+        y = []
+        
+        wavelength = None
+        distance = None
+        transmission = None
+        
+        pixel_x = None
+        pixel_y = None
+        
+        isInfo = False
+        isCenter = False
+
+        data_conv_q = None
+        data_conv_i = None
+        
+        # Set units: This is the unit assumed for Q and I in the data file.
+        if has_converter == True and output.Q_unit != '1/A':
+            data_conv_q = Converter('1/A')
+            # Test it
+            data_conv_q(1.0, output.Q_unit)
+            
+        if has_converter == True and output.I_unit != '1/cm':
+            data_conv_i = Converter('1/cm')
+            # Test it
+            data_conv_i(1.0, output.I_unit)
+        
+              
+        # Remove the last lines before the for loop if the lines are empty
+        # to calculate the exact number of data points
+        count = 0
+        while (len(lines[len(lines) - (count + 1)].lstrip().rstrip()) < 1):
+            del lines[len(lines) - (count + 1)]
+            count = count + 1
+
+        #Read Header and find the dimensions of 2D data
+        line_num = 0
+        # Old version NIST files: 0
+        ver = 0
+        for line in lines:
+            line_num += 1
+            ## Reading the header applies only to IGOR/NIST 2D q_map data files
+            # Find setup info line
+            if isInfo:
+                isInfo = False
+                line_toks = line.split()
+                # Wavelength in Angstrom
+                try:
+                    wavelength = float(line_toks[1])
+                    # Units
+                    if has_converter == True and \
+                    output.source.wavelength_unit != 'A':
+                        conv = Converter('A')
+                        wavelength = conv(wavelength,
+                                          units=output.source.wavelength_unit)
+                except:
+                    #Not required
+                    pass
+                # Distance in mm
+                try:
+                    distance = float(line_toks[3])
+                    # Units
+                    if has_converter == True and detector.distance_unit != 'm':
+                        conv = Converter('m')
+                        distance = conv(distance, units=detector.distance_unit)
+                except:
+                    #Not required
+                    pass
+                
+                # Distance in meters
+                try:
+                    transmission = float(line_toks[4])
+                except:
+                    #Not required
+                    pass
+                                            
+            if line.count("LAMBDA") > 0:
+                isInfo = True
+                
+            # Find center info line
+            if isCenter:
+                isCenter = False
+                line_toks = line.split()
+                # Center in bin number
+                center_x = float(line_toks[0])
+                center_y = float(line_toks[1])
+
+            if line.count("BCENT") > 0:
+                isCenter = True
+            # Check version
+            if line.count("Data columns") > 0:
+                if line.count("err(I)") > 0:
+                    ver = 1
+            # Find data start
+            if line.count("ASCII data") > 0:
+                dataStarted = True
+                continue
+
+            ## Read and get data.
+            if dataStarted == True:
+                line_toks = line.split()
+                if len(line_toks) == 0:
+                    #empty line
+                    continue
+                # the number of columns must be stayed same 
+                col_num = len(line_toks)
+                break
+        # Make numpy array to remove header lines using index
+        lines_array = numpy.array(lines)
+
+        # index for lines_array
+        lines_index = numpy.arange(len(lines))
+        
+        # get the data lines
+        data_lines = lines_array[lines_index >= (line_num - 1)]
+        # Now we get the total number of rows (i.e., # of data points)
+        row_num = len(data_lines)
+        # make it as list again to control the separators
+        data_list = " ".join(data_lines.tolist())
+        # split all data to one big list w/" "separator
+        data_list = data_list.split()
+ 
+        # Check if the size is consistent with data, otherwise
+        #try the tab(\t) separator
+        # (this may be removed once get the confidence
+        #the former working all cases).
+        if len(data_list) != (len(data_lines)) * col_num:
+            data_list = "\t".join(data_lines.tolist())
+            data_list = data_list.split()
+
+        # Change it(string) into float
+        #data_list = map(float,data_list)
+        data_list1 = map(check_point, data_list)
+
+        # numpy array form
+        data_array = numpy.array(data_list1)
+        # Redimesion based on the row_num and col_num,
+        #otherwise raise an error.
+        try:
+            data_point = data_array.reshape(row_num, col_num).transpose()
+        except:
+            msg = "red2d_reader: Can't read this file: Not a proper file format"
+            raise ValueError, msg
+        ## Get the all data: Let's HARDcoding; Todo find better way
+        # Defaults
+        dqx_data = numpy.zeros(0)
+        dqy_data = numpy.zeros(0)
+        err_data = numpy.ones(row_num)
+        qz_data = numpy.zeros(row_num)
+        mask = numpy.ones(row_num, dtype=bool)
+        # Get from the array
+        qx_data = data_point[0]
+        qy_data = data_point[1]
+        data = data_point[2]
+        if ver == 1:
+            if col_num > (2 + ver):
+                err_data = data_point[(2 + ver)]
+        if col_num > (3 + ver):
+            qz_data = data_point[(3 + ver)]
+        if col_num > (4 + ver):
+            dqx_data = data_point[(4 + ver)]
+        if col_num > (5 + ver):
+            dqy_data = data_point[(5 + ver)]
+        #if col_num > (6 + ver): mask[data_point[(6 + ver)] < 1] = False
+        q_data = numpy.sqrt(qx_data*qx_data+qy_data*qy_data+qz_data*qz_data)
+           
+        # Extra protection(it is needed for some data files): 
+        # If all mask elements are False, put all True
+        if not mask.any():
+            mask[mask == False] = True
+  
+        # Store limits of the image in q space
+        xmin = numpy.min(qx_data)
+        xmax = numpy.max(qx_data)
+        ymin = numpy.min(qy_data)
+        ymax = numpy.max(qy_data)
+
+        # units
+        if has_converter == True and output.Q_unit != '1/A':
+            xmin = data_conv_q(xmin, units=output.Q_unit)
+            xmax = data_conv_q(xmax, units=output.Q_unit)
+            ymin = data_conv_q(ymin, units=output.Q_unit)
+            ymax = data_conv_q(ymax, units=output.Q_unit)
+            
+        ## calculate the range of the qx and qy_data
+        x_size = math.fabs(xmax - xmin)
+        y_size = math.fabs(ymax - ymin)
+        
+        # calculate the number of pixels in the each axes
+        npix_y = math.floor(math.sqrt(len(data)))
+        npix_x = math.floor(len(data) / npix_y)
+        
+        # calculate the size of bins
+        xstep = x_size / (npix_x - 1)
+        ystep = y_size / (npix_y - 1)
+        
+        # store x and y axis bin centers in q space
+        x_bins = numpy.arange(xmin, xmax + xstep, xstep)
+        y_bins = numpy.arange(ymin, ymax + ystep, ystep)
+       
+        # get the limits of q values
+        xmin = xmin - xstep / 2
+        xmax = xmax + xstep / 2
+        ymin = ymin - ystep / 2
+        ymax = ymax + ystep / 2
+        
+        #Store data in outputs
+        #TODO: Check the lengths
+        output.data = data
+        if (err_data == 1).all():
+            output.err_data = numpy.sqrt(numpy.abs(data))
+            output.err_data[output.err_data == 0.0] = 1.0
+        else:
+            output.err_data = err_data
+            
+        output.qx_data = qx_data
+        output.qy_data = qy_data
+        output.q_data = q_data
+        output.mask = mask
+        
+        output.x_bins = x_bins
+        output.y_bins = y_bins
+               
+        output.xmin = xmin
+        output.xmax = xmax
+        output.ymin = ymin
+        output.ymax = ymax
+        
+        output.source.wavelength = wavelength
+        
+        # Store pixel size in mm
+        detector.pixel_size.x = pixel_x
+        detector.pixel_size.y = pixel_y
+        
+        # Store the sample to detector distance
+        detector.distance = distance
+        
+        # optional data: if all of dq data == 0, do not pass to output
+        if len(dqx_data) == len(qx_data) and dqx_data.any() != 0:
+            # if no dqx_data, do not pass dqy_data.
+            #(1 axis dq is not supported yet).
+            if len(dqy_data) == len(qy_data) and dqy_data.any() != 0:
+                # Currently we do not support dq parr, perp.
+                # tranfer the comp. to cartesian coord. for newer version.
+                if ver != 1:
+                    diag = numpy.sqrt(qx_data * qx_data + qy_data * qy_data)
+                    cos_th = qx_data / diag
+                    sin_th = qy_data / diag
+                    output.dqx_data = numpy.sqrt((dqx_data * cos_th) * \
+                                                 (dqx_data * cos_th) \
+                                                 + (dqy_data * sin_th) * \
+                                                  (dqy_data * sin_th))
+                    output.dqy_data = numpy.sqrt((dqx_data * sin_th) * \
+                                                 (dqx_data * sin_th) \
+                                                 + (dqy_data * cos_th) * \
+                                                  (dqy_data * cos_th))
+                else:
+                    output.dqx_data = dqx_data
+                    output.dqy_data = dqy_data
+
+        # Units of axes
+        if data_conv_q is not None:
+            output.xaxis("\\rm{Q_{x}}", output.Q_unit)
+            output.yaxis("\\rm{Q_{y}}", output.Q_unit)
+        else:
+            output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
+            output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
+        if data_conv_i is not None:
+            output.zaxis("\\rm{Intensity}", output.I_unit)
+        else:
+            output.zaxis("\\rm{Intensity}", "cm^{-1}")
+    
+        # Store loading process information
+        output.meta_data['loader'] = self.type_name
+
+        return output
Index: src/sas/sascalc/dataloader/readers/schema/cansas1d_v1_0.xsd
===================================================================
--- src/sas/sascalc/dataloader/readers/schema/cansas1d_v1_0.xsd	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/schema/cansas1d_v1_0.xsd	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="xsd-documentation.xsl" ?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"  jaxb:version="2.0"
+	targetNamespace="cansas1d/1.0" xmlns:tns="cansas1d/1.0"
+	elementFormDefault="qualified">
+
+<!-- JAXB: http://java.sun.com/developer/technicalArticles/WebServices/jaxb/ -->
+
+	<complexType name="floatUnitType">
+		<simpleContent>
+			<extension base="float">
+				<attribute name="unit" type="string" use="required" />
+			</extension>
+		</simpleContent>
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<group name="positionGroup">
+		<sequence>
+			<element name="x" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
+			<element name="y" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
+			<element name="z" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
+		</sequence>
+	</group>
+
+	<complexType name="positionType">
+		<group ref="tns:positionGroup" />
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<group name="orientationGroup">
+		<sequence>
+			<element name="roll" type="tns:floatUnitType" minOccurs="0"  maxOccurs="1" />
+			<element name="pitch" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
+			<element name="yaw" type="tns:floatUnitType" minOccurs="0"   maxOccurs="1" />
+		</sequence>
+	</group>
+
+	<complexType name="orientationType">
+		<group ref="tns:orientationGroup" />
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="IdataType">
+		<sequence>
+			<element name="Q" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+			<element name="I" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+			<element name="Idev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+			<xsd:choice>
+				<!-- [ [Qdev] | [[dQw] | [dQl]] ] -->
+				<element name="Qdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				<xsd:sequence>
+					<element name="dQw" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+					<element name="dQl" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				</xsd:sequence>
+			</xsd:choice>
+			<element name="Qmean" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+			<element name="Shadowfactor" minOccurs="0" maxOccurs="1" type="float" default="1.0" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+	</complexType>
+
+	<complexType name="SASdataType">
+		<sequence>
+			<element name="Idata" minOccurs="1" maxOccurs="unbounded" type="tns:IdataType" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASsampleType">
+		<sequence>
+			<element name="ID" minOccurs="1" maxOccurs="1" type="string" />
+			<element name="thickness" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="transmission" minOccurs="0" maxOccurs="1" type="float" />
+			<element name="temperature" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="position" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
+			<element name="details" minOccurs="0" maxOccurs="unbounded" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="termType">
+		<simpleContent>
+			<extension base="string">
+				<attribute name="name" type="string" use="optional" default="" />
+				<attribute name="unit" type="string" use="optional"/>
+			</extension>
+		</simpleContent>
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASprocessType">
+		<annotation>
+			<appinfo>
+				<!-- JAXB: simplify class name -->
+				<jaxb:property name="SASprocess" />
+			</appinfo>
+		</annotation>
+		<sequence>
+			<element name="name" minOccurs="0" maxOccurs="1"
+				type="string" default="">
+				<annotation>
+					<appinfo>
+						<!-- Remap collision of Name when using JAXB -->
+						<jaxb:property name="SASprocessName" />
+					</appinfo>
+				</annotation>
+			</element>
+			<element name="date" minOccurs="0" maxOccurs="1" type="string" />
+			<element name="description" minOccurs="0" maxOccurs="1" />
+			<element name="term" minOccurs="0" maxOccurs="unbounded" type="tns:termType" />
+			<element name="SASprocessnote" minOccurs="1" maxOccurs="unbounded" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="">
+			<annotation>
+				<appinfo>
+					<!-- Remap collision of Name when using JAXB -->
+					<jaxb:property name="SASprocessNameAttr" />
+				</appinfo>
+			</annotation>
+		</attribute>
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASsourceType">
+		<sequence>
+			<element name="radiation" minOccurs="1" maxOccurs="1" type="string" />
+			<element name="beam_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="beam_shape" minOccurs="0" maxOccurs="1" type="string" />
+			<element name="wavelength" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="wavelength_min" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="wavelength_max" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="wavelength_spread" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<complexType name="SAScollimationType">
+		<sequence>
+			<element name="length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="aperture" minOccurs="0"
+				maxOccurs="unbounded">
+				<complexType>
+					<sequence>
+						<element name="size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+						<element name="distance" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+					</sequence>
+					<attribute name="name" type="string" use="optional" default="" />
+					<attribute name="type" type="string" use="optional" default="" />
+				</complexType>
+			</element>
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<complexType name="SASdetectorType">
+		<sequence>
+			<element name="name" minOccurs="1" maxOccurs="1" type="string" default=""/>
+			<element name="SDD" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="offset" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
+			<element name="beam_center" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="pixel_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="slit_length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+		</sequence>
+	</complexType>
+
+	<complexType name="SASinstrumentType">
+		<sequence>
+			<element name="name" type="string" minOccurs="1" maxOccurs="1" default="" />
+			<element name="SASsource" type="tns:SASsourceType" minOccurs="1" maxOccurs="1" />
+			<element name="SAScollimation" type="tns:SAScollimationType" minOccurs="1" maxOccurs="unbounded" />
+			<element name="SASdetector" type="tns:SASdetectorType" minOccurs="1" maxOccurs="unbounded" />
+		</sequence>
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASentryType">
+		<sequence>
+			<element name="Title" minOccurs="1" maxOccurs="1" type="string" />
+			<element name="Run" minOccurs="1" maxOccurs="unbounded">
+				<xsd:complexType>
+					<simpleContent>
+						<extension base="string">
+							<attribute name="name" type="string" use="optional" default="" />
+						</extension>
+					</simpleContent>
+				</xsd:complexType>
+			</element>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+			<element name="SASdata" minOccurs="1" maxOccurs="unbounded" type="tns:SASdataType" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other">
+				<!-- Is a second *any* element needed here? -->
+				<annotation>
+					<appinfo>
+						<!-- Remap collision of Any when using JAXB --> 
+						<jaxb:property name="SASentryAny2" />
+					</appinfo>
+				</annotation>
+			</xsd:any>
+			<element name="SASsample" type="tns:SASsampleType" />
+			<element name="SASinstrument" type="tns:SASinstrumentType" minOccurs="1" maxOccurs="1" />
+			<element name="SASprocess" type="tns:SASprocessType" minOccurs="0" maxOccurs="unbounded" />
+			<element name="SASnote" minOccurs="1" maxOccurs="unbounded" />
+				<!-- SASnote is a free-form element.
+					Do not define any content for it or it will become restrictive.
+				-->
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASrootType">
+		<sequence>
+			<element name="SASentry" minOccurs="1" maxOccurs="unbounded" type="tns:SASentryType" />
+		</sequence>
+		<attribute name="version" type="string" fixed="1.0" use="required" />
+	</complexType>
+
+	<element name="SASroot" type="tns:SASrootType" />
+
+</schema>
Index: src/sas/sascalc/dataloader/readers/schema/cansas1d_v1_1.xsd
===================================================================
--- src/sas/sascalc/dataloader/readers/schema/cansas1d_v1_1.xsd	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/schema/cansas1d_v1_1.xsd	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="xsd-documentation.xsl" ?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+	xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"  jaxb:version="2.0"
+	targetNamespace="urn:cansas1d:1.1" xmlns:tns="urn:cansas1d:1.1"
+	elementFormDefault="qualified">
+
+<!-- 
+Copyright (c) 2013, UChicago Argonne, LLC
+This file is distributed subject to a Software License Agreement found
+in the file LICENSE that is included with this distribution. 
+ -->
+ 
+<!-- JAXB: http://java.sun.com/developer/technicalArticles/WebServices/jaxb/ -->
+
+	<complexType name="floatUnitType">
+		<simpleContent>
+			<extension base="float">
+				<attribute name="unit" type="string" use="required" />
+			</extension>
+		</simpleContent>
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<group name="positionGroup">
+		<sequence>
+			<element name="x" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
+			<element name="y" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
+			<element name="z" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
+		</sequence>
+	</group>
+
+	<complexType name="positionType">
+		<group ref="tns:positionGroup" />
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<group name="orientationGroup">
+		<sequence>
+			<element name="roll" type="tns:floatUnitType" minOccurs="0"  maxOccurs="1" />
+			<element name="pitch" type="tns:floatUnitType" minOccurs="0" maxOccurs="1" />
+			<element name="yaw" type="tns:floatUnitType" minOccurs="0"   maxOccurs="1" />
+		</sequence>
+	</group>
+
+	<complexType name="orientationType">
+		<group ref="tns:orientationGroup" />
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+	
+	<complexType name="IdataType">
+		<sequence>
+			<element name="Q" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+			<element name="I" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+			<element name="Idev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+			<xsd:choice>
+				<!-- [ [Qdev] | [[dQw] | [dQl]] ] -->
+				<element name="Qdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				<xsd:sequence>
+					<element name="dQw" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+					<element name="dQl" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				</xsd:sequence>
+			</xsd:choice>
+			<element name="Qmean" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+			<element name="Shadowfactor" minOccurs="0" maxOccurs="1" type="float" default="1.0" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+	</complexType>
+	
+	<complexType name="SASdataType">
+		<sequence minOccurs="1" maxOccurs="1">  <!-- http://svn.cansas.org/trac/ticket/21 -->
+			<sequence>
+				<element name="Idata" minOccurs="1" maxOccurs="unbounded" type="tns:IdataType" />
+			</sequence>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+		<attribute name="timestamp" type="dateTime" use="optional" />
+	</complexType>
+	
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+	
+	<complexType name="TdataType">
+			<sequence>
+				<element name="Lambda" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+				<element name="T" minOccurs="1" maxOccurs="1"    type="tns:floatUnitType" />
+				<element name="Tdev" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" default="0" />
+				<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+	</complexType>
+	
+	<complexType name="SAStransmission_spectrumType">
+		<sequence minOccurs="1" maxOccurs="1">
+			<sequence minOccurs="1" maxOccurs="1">
+				<element name="Tdata" minOccurs="1" maxOccurs="unbounded" type="tns:TdataType" />
+			</sequence>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+		<attribute name="timestamp" type="dateTime" use="optional" />
+	</complexType>
+	
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASsampleType">
+		<sequence>
+			<element name="ID" minOccurs="1" maxOccurs="1" type="string" />
+			<element name="thickness" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="transmission" minOccurs="0" maxOccurs="1" type="float" />
+			<element name="temperature" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="position" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
+			<element name="details" minOccurs="0" maxOccurs="unbounded" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="termType">
+		<simpleContent>
+			<extension base="string">
+				<attribute name="name" type="string" use="optional" default="" />
+				<attribute name="unit" type="string" use="optional"/>
+			</extension>
+		</simpleContent>
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASprocessType">
+		<annotation>
+			<appinfo>
+				<!-- JAXB: simplify class name -->
+				<jaxb:property name="SASprocess" />
+			</appinfo>
+		</annotation>
+		<sequence>
+			<element name="name" minOccurs="0" maxOccurs="1"
+				type="string" default="">
+				<annotation>
+					<appinfo>
+						<!-- Remap collision of Name when using JAXB -->
+						<jaxb:property name="SASprocessName" />
+					</appinfo>
+				</annotation>
+			</element>
+			<element name="date" minOccurs="0" maxOccurs="1" type="string" />
+			<element name="description" minOccurs="0" maxOccurs="1" />
+			<element name="term" minOccurs="0" maxOccurs="unbounded" type="tns:termType" />
+			<element name="SASprocessnote" minOccurs="1" maxOccurs="unbounded" />
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="">
+			<annotation>
+				<appinfo>
+					<!-- Remap collision of Name when using JAXB -->
+					<jaxb:property name="SASprocessNameAttr" />
+				</appinfo>
+			</annotation>
+		</attribute>
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASsourceType">
+		<sequence>
+			<element name="radiation" minOccurs="1" maxOccurs="1" type="string" />
+			<element name="beam_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="beam_shape" minOccurs="0" maxOccurs="1" type="string" />
+			<element name="wavelength" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="wavelength_min" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="wavelength_max" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="wavelength_spread" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<complexType name="SAScollimationType">
+		<sequence>
+			<element name="length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="aperture" minOccurs="0"
+				maxOccurs="unbounded">
+				<complexType>
+					<sequence>
+						<element name="size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+						<element name="distance" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+					</sequence>
+					<attribute name="name" type="string" use="optional" default="" />
+					<attribute name="type" type="string" use="optional" default="" />
+				</complexType>
+			</element>
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<complexType name="SASdetectorType">
+		<sequence>
+			<element name="name" minOccurs="1" maxOccurs="1" type="string" default=""/>
+			<element name="SDD" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+			<element name="offset" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="orientation" minOccurs="0" maxOccurs="1" type="tns:orientationType" />
+			<element name="beam_center" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="pixel_size" minOccurs="0" maxOccurs="1" type="tns:positionType" />
+			<element name="slit_length" minOccurs="0" maxOccurs="1" type="tns:floatUnitType" />
+		</sequence>
+	</complexType>
+
+	<complexType name="SASinstrumentType">
+		<sequence>
+			<element name="name" type="string" minOccurs="1" maxOccurs="1" default="" />
+			<element name="SASsource" type="tns:SASsourceType" minOccurs="1" maxOccurs="1" />
+			<element name="SAScollimation" type="tns:SAScollimationType" minOccurs="1" maxOccurs="unbounded" />
+			<element name="SASdetector" type="tns:SASdetectorType" minOccurs="1" maxOccurs="unbounded" />
+		</sequence>
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASentryType">
+		<sequence>
+			<element name="Title" minOccurs="1" maxOccurs="1" type="string" />
+			<element name="Run" minOccurs="1" maxOccurs="unbounded">
+				<xsd:complexType>
+					<simpleContent>
+						<extension base="string">
+							<attribute name="name" type="string" use="optional" default="" />
+						</extension>
+					</simpleContent>
+				</xsd:complexType>
+			</element>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other" />
+			<element name="SASdata" minOccurs="1" maxOccurs="unbounded" type="tns:SASdataType" />
+			<element name="SAStransmission_spectrum" minOccurs="0" maxOccurs="unbounded" type="tns:SAStransmission_spectrumType"/>
+			<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="skip" namespace="##other">
+				<!-- Is a second *any* element needed here? -->
+				<annotation>
+					<appinfo>
+						<!-- Remap collision of Any when using JAXB --> 
+						<jaxb:property name="SASentryAny2" />
+					</appinfo>
+				</annotation>
+			</xsd:any>
+			<element name="SASsample" type="tns:SASsampleType" />
+			<element name="SASinstrument" type="tns:SASinstrumentType" minOccurs="1" maxOccurs="1" />
+			<element name="SASprocess" type="tns:SASprocessType" minOccurs="0" maxOccurs="unbounded" />
+			<element name="SASnote" minOccurs="1" maxOccurs="unbounded" />
+				<!-- SASnote is a free-form element.
+					Do not define any content for it or it will become restrictive.
+				-->
+		</sequence>
+		<attribute name="name" type="string" use="optional" default="" />
+	</complexType>
+
+	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
+
+	<complexType name="SASrootType">
+		<sequence>
+			<element name="SASentry" minOccurs="1" maxOccurs="unbounded" type="tns:SASentryType" />
+		</sequence>
+		<attribute name="version" type="string" fixed="1.1" use="required" />
+	</complexType>
+
+	<element name="SASroot" type="tns:SASrootType" />
+
+</schema>
Index: src/sas/sascalc/dataloader/readers/sesans_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/sesans_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/sesans_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,273 @@
+"""
+    SESANS reader (based on ASCII reader)
+    
+    Reader for .ses or .sesans file format
+    
+    Jurrian Bakker 
+"""
+import numpy
+import os
+from sas.sascalc.dataloader.data_info import SESANSData1D
+
+# Check whether we have a converter available
+has_converter = True
+try:
+    from sas.sascalc.data_util.nxsunit import Converter
+except:
+    has_converter = False
+_ZERO = 1e-16
+
+class Reader:
+    """
+    Class to load sesans files (6 columns).
+    """
+    ## File type
+    type_name = "SESANS"
+    
+    ## Wildcards
+    type = ["SESANS files (*.ses)|*.ses",
+            "SESANS files (*..sesans)|*.sesans"]
+    ## List of allowed extensions
+    ext = ['.ses', '.SES', '.sesans', '.SESANS']
+    
+    ## Flag to bypass extension check
+    allow_all = True
+    
+    def read(self, path):
+        
+#        print "reader triggered"
+        
+        """
+        Load data file
+        
+        :param path: file path
+        
+        :return: SESANSData1D object, or None
+        
+        :raise RuntimeError: when the file can't be opened
+        :raise ValueError: when the length of the data vectors are inconsistent
+        """
+        if os.path.isfile(path):
+            basename = os.path.basename(path)
+            _, extension = os.path.splitext(basename)
+            if self.allow_all or extension.lower() in self.ext:
+                try:
+                    # Read in binary mode since GRASP frequently has no-ascii
+                    # characters that brakes the open operation
+                    input_f = open(path,'rb')
+                except:
+                    raise  RuntimeError, "sesans_reader: cannot open %s" % path
+                buff = input_f.read()
+#                print buff
+                lines = buff.splitlines()
+#                print lines
+                #Jae could not find python universal line spliter:
+                #keep the below for now
+                # some ascii data has \r line separator,
+                # try it when the data is on only one long line
+#                if len(lines) < 2 :
+#                    lines = buff.split('\r')
+                 
+                x  = numpy.zeros(0)
+                y  = numpy.zeros(0)
+                dy = numpy.zeros(0)
+                lam  = numpy.zeros(0)
+                dlam = numpy.zeros(0)
+                dx = numpy.zeros(0)
+                
+               #temp. space to sort data
+                tx  = numpy.zeros(0)
+                ty  = numpy.zeros(0)
+                tdy = numpy.zeros(0)
+                tlam  = numpy.zeros(0)
+                tdlam = numpy.zeros(0)
+                tdx = numpy.zeros(0)
+#                print "all good"
+                output = SESANSData1D(x=x, y=y, lam=lam, dy=dy, dx=dx, dlam=dlam)
+#                print output                
+                self.filename = output.filename = basename
+                
+                
+                data_conv_z = None
+                data_conv_P = None
+#                print "passing"
+                if has_converter == True and output.x_unit != 'A':
+                    data_conv_z = Converter('nm')
+                    # Test it
+                    data_conv_z(1.0, output.x_unit)
+#                    print data_conv_z
+#                    print data_conv_z(1.0, output.x_unit)
+                if has_converter == True and output.y_unit != ' ':
+                    data_conv_P = Converter('a.u.')
+                    # Test it
+                    data_conv_P(1.0, output.y_unit)
+#                    print data_conv_P
+#                    print data_conv_P(1.0, output.y_unit)
+                # The first good line of data will define whether
+                # we have 2-column or 3-column ascii
+#                print output.x_unit
+#                print output.y_unit
+                
+#                print "past output"
+                
+#                has_error_dx = None
+#                has_error_dy = None
+                
+#                #Initialize counters for data lines and header lines.
+#                is_data = False  # Has more than 5 lines
+#                # More than "5" lines of data is considered as actual
+#                # data unless that is the only data
+#                mum_data_lines = 5
+#                # To count # of current data candidate lines
+#                i = -1
+#                # To count total # of previous data candidate lines
+#                i1 = -1
+#                # To count # of header lines
+#                j = -1
+#                # Helps to count # of header lines
+#                j1 = -1
+#                #minimum required number of columns of data; ( <= 4).
+#                lentoks = 2
+                paramnames=[]
+                paramvals=[]
+                zvals=[]
+                dzvals=[]
+                lamvals=[]
+                dlamvals=[]
+                Pvals=[]
+                dPvals=[]
+#                print x
+#                print zvals
+                for line in lines:
+                    # Initial try for CSV (split on ,)
+#                    print line
+                    line=line.strip()
+#                    print line
+                    toks = line.split('\t')
+#                    print toks
+                    if len(toks)==2:
+                        paramnames.append(toks[0])
+#                        print paramnames
+                        paramvals.append(toks[1])
+#                        print paramvals
+                    if len(toks)>5:
+                        zvals.append(toks[0])
+                        dzvals.append(toks[1])
+                        lamvals.append(toks[2])
+                        dlamvals.append(toks[3])
+                        Pvals.append(toks[4])
+                        dPvals.append(toks[5])
+                    else:
+                        continue
+#                print varheaders
+#                print paramnames
+#                print paramvals
+#                print zvals  
+#                print len(zvals)
+                
+                x=[]
+                y=[]
+                lam=[]
+                dx=[]
+                dy=[]
+                dlam=[]
+                varheader=[zvals[0],dzvals[0],lamvals[0],dlamvals[0],Pvals[0],dPvals[0]]
+#                print varheader
+                valrange=range(len(zvals)-1)
+#                print valrange
+                for i in valrange:
+                    x.append(float(zvals[i+1]))
+                    y.append(float(Pvals[i+1]))
+                    lam.append(float(lamvals[i+1]))
+                    dy.append(float(dPvals[i+1]))
+                    dx.append(float(dzvals[i+1]))
+                    dlam.append(float(dlamvals[i+1]))
+                    
+                x,y,lam,dy,dx,dlam = [
+                   numpy.asarray(v, 'double')
+                   for v in (x,y,lam,dy,dx,dlam)
+                ]
+
+#                print x
+#                print y
+#                print dx
+#                print dy
+#                print len(x)
+#                print len(y)
+#                print len(dx)
+#                print len(dy)
+                
+                
+                input_f.close()
+                # Sanity check
+#                if has_error_dy == True and not len(y) == len(dy):
+#                    msg = "sesans_reader: y and dy have different length"
+#                    raise RuntimeError, msg
+#                if has_error_dx == True and not len(x) == len(dx):
+#                    msg = "sesans_reader: y and dy have different length"
+#                    raise RuntimeError, msg
+#                # If the data length is zero, consider this as
+#                # though we were not able to read the file.
+#                if len(x) == 0:
+#                    raise RuntimeError, "sesans_reader: could not load file"
+#                print "alive"
+                #Let's re-order the data to make cal.
+                # curve look better some cases
+#                ind = numpy.lexsort((ty, tx))
+#                for i in ind:
+#                    x[i] = tx[ind[i]]
+#                    y[i] = ty[ind[i]]
+#                    if has_error_dy == True:
+#                        dy[i] = tdy[ind[i]]
+#                    if has_error_dx == True:
+#                        dx[i] = tdx[ind[i]]
+                # Zeros in dx, dy
+#                if has_error_dx:
+#                    dx[dx == 0] = _ZERO
+#                if has_error_dy:
+#                    dy[dy == 0] = _ZERO
+                #Data
+                output.x = x #[x != 0]
+#                print output.x
+                output.y = y #[x != 0]
+#                print output.y
+#                output.dy = dy[x != 0] if has_error_dy == True\
+#                    else numpy.zeros(len(output.y))
+#                output.dx = dx[x != 0] if has_error_dx == True\
+#                    else numpy.zeros(len(output.x))
+                output.dy = dy
+                output.dx = dx
+                output.lam = lam
+                output.dlam = dlam
+
+
+#                print "still alive"                
+#                if data_conv_z is not None:
+#                    output.xaxis("\\rm{z}", output.x_unit)
+#                else:
+#                    output.xaxis("\\rm{z}", 'nm')
+#                if data_conv_P is not None:
+#                    output.yaxis("\\rm{P/P0}", output.y_unit)
+#                else:
+#                    output.yaxis("\\rm{P/P0}", "a.u.")
+                output.xaxis("\\rm{z}", 'nm')   
+                output.yaxis("\\rm{P/P0}", " ")
+                # Store loading process information
+                output.meta_data['loader'] = self.type_name
+                output.sample.thickness = float(paramvals[6])
+                output.sample.name = paramvals[1]
+                output.sample.ID = paramvals[0]
+                output.sample.zacceptance=float(paramvals[7])
+                output.vars=varheader
+
+#                print "sesans_reader end"
+                
+                if len(output.x) < 1:
+                    raise RuntimeError, "%s is empty" % path
+#                print output
+#                print output.lam
+                return output
+            
+        else:
+            raise RuntimeError, "%s is not a file" % path
+        return None
Index: src/sas/sascalc/dataloader/readers/tiff_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/tiff_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/tiff_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,106 @@
+#####################################################################
+#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. 
+#See the license text in license.txt
+#copyright 2008, University of Tennessee
+######################################################################
+"""
+    Image reader. Untested.
+"""
+#TODO: load and check data and orientation of the image (needs rendering)
+import math
+import logging
+import os
+import numpy
+from sas.sascalc.dataloader.data_info import Data2D
+from sas.sascalc.dataloader.manipulations import reader2D_converter
+    
+class Reader:
+    """
+    Example data manipulation
+    """
+    ## File type
+    type_name = "TIF"
+    ## Wildcards
+    type = ["TIF files (*.tif)|*.tif",
+            "TIFF files (*.tiff)|*.tiff",
+            ]
+    ## Extension
+    ext = ['.tif', '.tiff']
+        
+    def read(self, filename=None):
+        """
+        Open and read the data in a file
+        
+        :param file: path of the file
+        """
+        try:
+            import Image
+            import TiffImagePlugin
+            Image._initialized=2
+        except:
+            msg = "tiff_reader: could not load file. Missing Image module."
+            raise RuntimeError, msg
+        
+        # Instantiate data object
+        output = Data2D()
+        output.filename = os.path.basename(filename)
+            
+        # Read in the image
+        try:
+            im = Image.open(filename)
+        except:
+            raise  RuntimeError, "cannot open %s"%(filename)
+        data = im.getdata()
+
+        # Initiazed the output data object
+        output.data = numpy.zeros([im.size[0], im.size[1]])
+        output.err_data = numpy.zeros([im.size[0], im.size[1]])
+        output.mask = numpy.ones([im.size[0], im.size[1]], dtype=bool)
+        
+        # Initialize
+        x_vals = []
+        y_vals = []
+
+        # x and y vectors
+        for i_x in range(im.size[0]):
+            x_vals.append(i_x)
+            
+        itot = 0
+        for i_y in range(im.size[1]):
+            y_vals.append(i_y)
+
+        for val in data:
+            try:
+                value = float(val)
+            except:
+                logging.error("tiff_reader: had to skip a non-float point")
+                continue
+            
+            # Get bin number
+            if math.fmod(itot, im.size[0]) == 0:
+                i_x = 0
+                i_y += 1
+            else:
+                i_x += 1
+                
+            output.data[im.size[1] - 1 - i_y][i_x] = value
+            
+            itot += 1
+                
+        output.xbins = im.size[0]
+        output.ybins = im.size[1]
+        output.x_bins = x_vals
+        output.y_bins = y_vals
+        output.qx_data = numpy.array(x_vals)
+        output.qy_data = numpy.array(y_vals)
+        output.xmin = 0
+        output.xmax = im.size[0] - 1
+        output.ymin = 0
+        output.ymax = im.size[0] - 1
+        
+        # Store loading process information
+        output.meta_data['loader'] = self.type_name
+        output = reader2D_converter(output)
+        return output
Index: src/sas/sascalc/dataloader/readers/xml_reader.py
===================================================================
--- src/sas/sascalc/dataloader/readers/xml_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/dataloader/readers/xml_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,286 @@
+"""
+    Generic XML read and write utility
+
+    Usage: Either extend xml_reader or add as a class variable.
+"""
+############################################################################
+#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
+from lxml import etree
+from lxml.builder import E
+
+PARSER = etree.ETCompatXMLParser(remove_comments=True, remove_pis=False)
+
+class XMLreader():
+    """
+    Generic XML read and write class. Mostly helper functions.
+    Makes reading/writing XML a bit easier than calling lxml libraries directly.
+
+    :Dependencies:
+        This class requires lxml 2.3 or higher.
+    """
+
+    xml = None
+    xmldoc = None
+    xmlroot = None
+    schema = None
+    schemadoc = None
+    encoding = None
+    processing_instructions = None
+
+    def __init__(self, xml=None, schema=None):
+        self.xml = xml
+        self.schema = schema
+        self.processing_instructions = {}
+        if xml is not None:
+            self.set_xml_file(xml)
+        else:
+            self.xmldoc = None
+            self.xmlroot = None
+        if schema is not None:
+            self.set_schema(schema)
+        else:
+            self.schemadoc = None
+
+    def reader(self):
+        """
+        Read in an XML file into memory and return an lxml dictionary
+        """
+        if self.validate_xml():
+            self.xmldoc = etree.parse(self.xml, parser=PARSER)
+        else:
+            raise etree.XMLSchemaValidateError(self, self.find_invalid_xml())
+        return self.xmldoc
+
+    def set_xml_file(self, xml):
+        """
+        Set the XML file and parse
+        """
+        try:
+            self.xml = xml
+            self.xmldoc = etree.parse(self.xml, parser=PARSER)
+            self.xmlroot = self.xmldoc.getroot()
+        except etree.XMLSyntaxError as xml_error:
+            logging.info(xml_error)
+        except Exception:
+            self.xml = None
+            self.xmldoc = None
+            self.xmlroot = None
+
+    def set_schema(self, schema):
+        """
+        Set the schema file and parse
+        """
+        try:
+            self.schema = schema
+            self.schemadoc = etree.parse(self.schema, parser=PARSER)
+        except etree.XMLSyntaxError as xml_error:
+            logging.info(xml_error)
+        except Exception:
+            self.schema = None
+            self.schemadoc = None
+
+    def validate_xml(self):
+        """
+        Checks to see if the XML file meets the schema
+        """
+        valid = True
+        if self.schema is not None:
+            self.parse_schema_and_doc()
+            schema_check = etree.XMLSchema(self.schemadoc)
+            valid = schema_check.validate(self.xmldoc)
+        return valid
+
+    def find_invalid_xml(self):
+        """
+        Finds the first offending element that should not be present in XML file
+        """
+        first_error = ""
+        self.parse_schema_and_doc()
+        schema = etree.XMLSchema(self.schemadoc)
+        try:
+            first_error = schema.assertValid(self.xmldoc)
+        except etree.DocumentInvalid as err:
+            first_error = str(err)
+        return first_error
+
+    def parse_schema_and_doc(self):
+        """
+        Creates a dictionary of the parsed schema and xml files.
+        """
+        self.set_xml_file(self.xml)
+        self.set_schema(self.schema)
+
+    def to_string(self, elem, pretty_print=False, encoding=None):
+        """
+        Converts an etree element into a string
+        """
+        return etree.tostring(elem, pretty_print=pretty_print, \
+                              encoding=encoding)
+
+    def break_processing_instructions(self, string, dic):
+        """
+        Method to break a processing instruction string apart and add to a dict
+
+        :param string: A processing instruction as a string
+        :param dic: The dictionary to save the PIs to
+        """
+        pi_string = string.replace("<?", "").replace("?>", "")
+        split = pi_string.split(" ", 1)
+        pi_name = split[0]
+        attr = split[1]
+        new_pi_name = self._create_unique_key(dic, pi_name)
+        dic[new_pi_name] = attr
+        return dic
+
+    def set_processing_instructions(self):
+        """
+        Take out all processing instructions and create a dictionary from them
+        If there is a default encoding, the value is also saved
+        """
+        dic = {}
+        proc_instr = self.xmlroot.getprevious()
+        while proc_instr is not None:
+            pi_string = self.to_string(proc_instr)
+            if "?>\n<?" in pi_string:
+                pi_string = pi_string.split("?>\n<?")
+            if isinstance(pi_string, str):
+                dic = self.break_processing_instructions(pi_string, dic)
+            elif isinstance(pi_string, list):
+                for item in pi_string:
+                    dic = self.break_processing_instructions(item, dic)
+            proc_instr = proc_instr.getprevious()
+        if 'xml' in dic:
+            self.set_encoding(dic['xml'])
+            del dic['xml']
+        self.processing_instructions = dic
+
+    def set_encoding(self, attr_str):
+        """
+        Find the encoding in the xml declaration and save it as a string
+
+        :param attr_str: All attributes as a string
+            e.g. "foo1="bar1" foo2="bar2" foo3="bar3" ... foo_n="bar_n""
+        """
+        attr_str = attr_str.replace(" = ", "=")
+        attr_list = attr_str.split()
+        for item in attr_list:
+            name_value = item.split("\"=")
+            name = name_value[0].lower()
+            value = name_value[1]
+            if name == "encoding":
+                self.encoding = value
+                return
+        self.encoding = None
+
+    def _create_unique_key(self, dictionary, name, numb=0):
+        """
+        Create a unique key value for any dictionary to prevent overwriting
+        Recurses 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 create_tree(self, root):
+        """
+        Create an element tree for processing from an etree element
+
+        :param root: etree Element(s) 
+        """
+        return etree.ElementTree(root)
+
+    def create_element_from_string(self, xml_string):
+        """
+        Create an element from an XML string
+
+        :param xml_string: A string of xml
+        """
+        return etree.fromstring(xml_string)
+
+    def create_element(self, name, attrib=None, nsmap=None):
+        """
+        Create an XML element for writing to file
+
+        :param name: The name of the element to be created
+        """
+        if attrib == None:
+            attrib = {}
+        return etree.Element(name, attrib, nsmap)
+
+    def write_text(self, elem, text):
+        """
+        Write text to an etree Element
+
+        :param elem: etree.Element object
+        :param text: text to write to the element
+        """
+        elem.text = text
+        return elem
+
+    def write_attribute(self, elem, attr_name, attr_value):
+        """
+        Write attributes to an Element
+
+        :param elem: etree.Element object
+        :param attr_name: attribute name to write
+        :param attr_value: attribute value to set
+        """
+        attr = elem.attrib
+        attr[attr_name] = attr_value
+
+    def return_processing_instructions(self):
+        """
+        Get all processing instructions saved when loading the document
+
+        :param tree: etree.ElementTree object to write PIs to
+        """
+        pi_list = []
+        if self.processing_instructions is not None:
+            for key in self.processing_instructions:
+                value = self.processing_instructions.get(key)
+                pi_item = etree.ProcessingInstruction(key, value)
+                pi_list.append(pi_item)
+        return pi_list
+
+    def append(self, element, tree):
+        """
+        Append an etree Element to an ElementTree.
+
+        :param element: etree Element to append
+        :param tree: ElementTree object to append to
+        """
+        tree = tree.append(element)
+        return tree
+
+    def ebuilder(self, parent, elementname, text=None, attrib=None):
+        """
+        Use lxml E builder class with arbitrary inputs.
+
+        :param parnet: The parent element to append a child to
+        :param elementname: The name of the child in string form
+        :param text: The element text
+        :param attrib: A dictionary of attribute names to attribute values
+        """
+        text = str(text)
+        if attrib == None:
+            attrib = {}
+        elem = E(elementname, attrib, text)
+        parent = parent.append(elem)
+        return parent
Index: src/sas/sascalc/fit/AbstractFitEngine.py
===================================================================
--- src/sas/sascalc/fit/AbstractFitEngine.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/AbstractFitEngine.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,632 @@
+
+import  copy
+#import logging
+import sys
+import math
+import numpy
+
+from sas.sascalc.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.data_info import Data2D
+_SMALLVALUE = 1.0e-10
+
+class FitHandler(object):
+    """
+    Abstract interface for fit thread handler.
+
+    The methods in this class are called by the optimizer as the fit
+    progresses.
+
+    Note that it is up to the optimizer to call the fit handler correctly,
+    reporting all status changes and maintaining the 'done' flag.
+    """
+    done = False
+    """True when the fit job is complete"""
+    result = None
+    """The current best result of the fit"""
+
+    def improvement(self):
+        """
+        Called when a result is observed which is better than previous
+        results from the fit.
+
+        result is a FitResult object, with parameters, #calls and fitness.
+        """
+    def error(self, msg):
+        """
+        Model had an error; print traceback
+        """
+    def progress(self, current, expected):
+        """
+        Called each cycle of the fit, reporting the current and the
+        expected amount of work.   The meaning of these values is
+        optimizer dependent, but they can be converted into a percent
+        complete using (100*current)//expected.
+
+        Progress is updated each iteration of the fit, whatever that
+        means for the particular optimization algorithm.  It is called
+        after any calls to improvement for the iteration so that the
+        update handler can control I/O bandwidth by suppressing
+        intermediate improvements until the fit is complete.
+        """
+    def finalize(self):
+        """
+        Fit is complete; best results are reported
+        """
+    def abort(self):
+        """
+        Fit was aborted.
+        """
+
+    # TODO: not sure how these are used, but they are needed for running the fit
+    def update_fit(self, last=False): pass
+    def set_result(self, result=None): self.result = result
+
+class Model:
+    """
+    Fit wrapper for SAS models.
+    """
+    def __init__(self, sas_model, sas_data=None, **kw):
+        """
+        :param sas_model: the sas model to wrap for fitting
+
+        """
+        self.model = sas_model
+        self.name = sas_model.name
+        self.data = sas_data
+
+    def get_params(self, fitparams):
+        """
+        return a list of value of paramter to fit
+
+        :param fitparams: list of paramaters name to fit
+
+        """
+        return [self.model.getParam(k) for k in fitparams]
+
+    def set_params(self, paramlist, params):
+        """
+        Set value for parameters to fit
+
+        :param params: list of value for parameters to fit
+
+        """
+        for k,v in zip(paramlist, params):
+            self.model.setParam(k,v)
+
+    def set(self, **kw):
+        self.set_params(*zip(*kw.items()))
+
+    def eval(self, x):
+        """
+            Override eval method of model.
+
+            :param x: the x value used to compute a function
+        """
+        try:
+            return self.model.evalDistribution(x)
+        except:
+            raise
+
+    def eval_derivs(self, x, pars=[]):
+        """
+        Evaluate the model and derivatives wrt pars at x.
+
+        pars is a list of the names of the parameters for which derivatives
+        are desired.
+
+        This method needs to be specialized in the model to evaluate the
+        model function.  Alternatively, the model can implement is own
+        version of residuals which calculates the residuals directly
+        instead of calling eval.
+        """
+        raise NotImplementedError('no derivatives available')
+
+    def __call__(self, x):
+        return self.eval(x)
+
+class FitData1D(Data1D):
+    """
+        Wrapper class  for SAS data
+        FitData1D inherits from DataLoader.data_info.Data1D. Implements
+        a way to get residuals from data.
+    """
+    def __init__(self, x, y, dx=None, dy=None, smearer=None, data=None):
+        """
+            :param smearer: is an object of class QSmearer or SlitSmearer
+               that will smear the theory data (slit smearing or resolution
+               smearing) when set.
+            
+            The proper way to set the smearing object would be to
+            do the following: ::
+            
+                from sas.models.qsmearing import smear_selection
+                smearer = smear_selection(some_data)
+                fitdata1d = FitData1D( x= [1,3,..,],
+                                        y= [3,4,..,8],
+                                        dx=None,
+                                        dy=[1,2...], smearer= smearer)
+           
+            :Note: that some_data _HAS_ to be of
+                class DataLoader.data_info.Data1D
+                Setting it back to None will turn smearing off.
+                
+        """
+        Data1D.__init__(self, x=x, y=y, dx=dx, dy=dy)
+        self.num_points = len(x)
+        self.sas_data = data
+        self.smearer = smearer
+        self._first_unsmeared_bin = None
+        self._last_unsmeared_bin = None
+        # Check error bar; if no error bar found, set it constant(=1)
+        # TODO: Should provide an option for users to set it like percent,
+        # constant, or dy data
+        if dy is None or dy == [] or dy.all() == 0:
+            self.dy = numpy.ones(len(y))
+        else:
+            self.dy = numpy.asarray(dy).copy()
+
+        ## Min Q-value
+        #Skip the Q=0 point, especially when y(q=0)=None at x[0].
+        if min(self.x) == 0.0 and self.x[0] == 0 and\
+                     not numpy.isfinite(self.y[0]):
+            self.qmin = min(self.x[self.x != 0])
+        else:
+            self.qmin = min(self.x)
+        ## Max Q-value
+        self.qmax = max(self.x)
+        
+        # Range used for input to smearing
+        self._qmin_unsmeared = self.qmin
+        self._qmax_unsmeared = self.qmax
+        # Identify the bin range for the unsmeared and smeared spaces
+        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
+        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
+                            & (self.x <= self._qmax_unsmeared)
+  
+    def set_fit_range(self, qmin=None, qmax=None):
+        """ to set the fit range"""
+        # Skip Q=0 point, (especially for y(q=0)=None at x[0]).
+        # ToDo: Find better way to do it.
+        if qmin == 0.0 and not numpy.isfinite(self.y[qmin]):
+            self.qmin = min(self.x[self.x != 0])
+        elif qmin != None:
+            self.qmin = qmin
+        if qmax != None:
+            self.qmax = qmax
+        # Determine the range needed in unsmeared-Q to cover
+        # the smeared Q range
+        self._qmin_unsmeared = self.qmin
+        self._qmax_unsmeared = self.qmax
+        
+        self._first_unsmeared_bin = 0
+        self._last_unsmeared_bin = len(self.x) - 1
+        
+        if self.smearer != None:
+            self._first_unsmeared_bin, self._last_unsmeared_bin = \
+                    self.smearer.get_bin_range(self.qmin, self.qmax)
+            self._qmin_unsmeared = self.x[self._first_unsmeared_bin]
+            self._qmax_unsmeared = self.x[self._last_unsmeared_bin]
+            
+        # Identify the bin range for the unsmeared and smeared spaces
+        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
+        ## zero error can not participate for fitting
+        self.idx = self.idx & (self.dy != 0)
+        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
+                            & (self.x <= self._qmax_unsmeared)
+
+    def get_fit_range(self):
+        """
+            Return the range of data.x to fit
+        """
+        return self.qmin, self.qmax
+
+    def size(self):
+        """
+        Number of measurement points in data set after masking, etc.
+        """
+        return len(self.x)
+
+    def residuals(self, fn):
+        """
+            Compute residuals.
+            
+            If self.smearer has been set, use if to smear
+            the data before computing chi squared.
+            
+            :param fn: function that return model value
+            
+            :return: residuals
+        """
+        # Compute theory data f(x)
+        fx = numpy.zeros(len(self.x))
+        fx[self.idx_unsmeared] = fn(self.x[self.idx_unsmeared])
+       
+        ## Smear theory data
+        if self.smearer is not None:
+            fx = self.smearer(fx, self._first_unsmeared_bin,
+                              self._last_unsmeared_bin)
+        ## Sanity check
+        if numpy.size(self.dy) != numpy.size(fx):
+            msg = "FitData1D: invalid error array "
+            msg += "%d <> %d" % (numpy.shape(self.dy), numpy.size(fx))
+            raise RuntimeError, msg
+        return (self.y[self.idx] - fx[self.idx]) / self.dy[self.idx], fx[self.idx]
+            
+    def residuals_deriv(self, model, pars=[]):
+        """
+            :return: residuals derivatives .
+            
+            :note: in this case just return empty array 
+        """
+        return []
+
+
+class FitData2D(Data2D):
+    """
+        Wrapper class  for SAS data
+    """
+    def __init__(self, sas_data2d, data=None, err_data=None):
+        Data2D.__init__(self, data=data, err_data=err_data)
+        # Data can be initialized with a sas plottable or with vectors.
+        self.res_err_image = []
+        self.num_points = 0 # will be set by set_data
+        self.idx = []
+        self.qmin = None
+        self.qmax = None
+        self.smearer = None
+        self.radius = 0
+        self.res_err_data = []
+        self.sas_data = sas_data2d
+        self.set_data(sas_data2d)
+
+    def set_data(self, sas_data2d, qmin=None, qmax=None):
+        """
+            Determine the correct qx_data and qy_data within range to fit
+        """
+        self.data = sas_data2d.data
+        self.err_data = sas_data2d.err_data
+        self.qx_data = sas_data2d.qx_data
+        self.qy_data = sas_data2d.qy_data
+        self.mask = sas_data2d.mask
+
+        x_max = max(math.fabs(sas_data2d.xmin), math.fabs(sas_data2d.xmax))
+        y_max = max(math.fabs(sas_data2d.ymin), math.fabs(sas_data2d.ymax))
+        
+        ## fitting range
+        if qmin == None:
+            self.qmin = 1e-16
+        if qmax == 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 == []:
+            self.res_err_data = numpy.ones(len(self.data))
+        else:
+            self.res_err_data = copy.deepcopy(self.err_data)
+        #self.res_err_data[self.res_err_data==0]=1
+        
+        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
+        
+        # Note: mask = True: for MASK while mask = False for NOT to mask
+        self.idx = ((self.qmin <= self.radius) &\
+                            (self.radius <= self.qmax))
+        self.idx = (self.idx) & (self.mask)
+        self.idx = (self.idx) & (numpy.isfinite(self.data))
+        self.num_points = numpy.sum(self.idx)
+
+    def set_smearer(self, smearer):
+        """
+            Set smearer
+        """
+        if smearer == None:
+            return
+        self.smearer = smearer
+        self.smearer.set_index(self.idx)
+        self.smearer.get_data()
+
+    def set_fit_range(self, qmin=None, qmax=None):
+        """
+            To set the fit range
+        """
+        if qmin == 0.0:
+            self.qmin = 1e-16
+        elif qmin != None:
+            self.qmin = qmin
+        if qmax != None:
+            self.qmax = qmax
+        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
+        self.idx = ((self.qmin <= self.radius) &\
+                            (self.radius <= self.qmax))
+        self.idx = (self.idx) & (self.mask)
+        self.idx = (self.idx) & (numpy.isfinite(self.data))
+        self.idx = (self.idx) & (self.res_err_data != 0)
+
+    def get_fit_range(self):
+        """
+        return the range of data.x to fit
+        """
+        return self.qmin, self.qmax
+
+    def size(self):
+        """
+        Number of measurement points in data set after masking, etc.
+        """
+        return numpy.sum(self.idx)
+
+    def residuals(self, fn):
+        """
+        return the residuals
+        """
+        if self.smearer != None:
+            fn.set_index(self.idx)
+            # Get necessary data from self.data and set the data for smearing
+            fn.get_data()
+
+            gn = fn.get_value()
+        else:
+            gn = fn([self.qx_data[self.idx],
+                     self.qy_data[self.idx]])
+        # use only the data point within ROI range
+        res = (self.data[self.idx] - gn) / self.res_err_data[self.idx]
+
+        return res, gn
+        
+    def residuals_deriv(self, model, pars=[]):
+        """
+        :return: residuals derivatives .
+        
+        :note: in this case just return empty array
+        
+        """
+        return []
+    
+    
+class FitAbort(Exception):
+    """
+    Exception raise to stop the fit
+    """
+    #pass
+    #print"Creating fit abort Exception"
+
+
+
+class FitEngine:
+    def __init__(self):
+        """
+        Base class for the fit engine
+        """
+        #Dictionnary of fitArrange element (fit problems)
+        self.fit_arrange_dict = {}
+        self.fitter_id = None
+        
+    def set_model(self, model, id, pars=[], constraints=[], data=None):
+        """
+        set a model on a given  in the fit engine.
+        
+        :param model: sas.models type 
+        :param id: is the key of the fitArrange dictionary where model is saved as a value
+        :param pars: the list of parameters to fit 
+        :param constraints: list of 
+            tuple (name of parameter, value of parameters)
+            the value of parameter must be a string to constraint 2 different
+            parameters.
+            Example:  
+            we want to fit 2 model M1 and M2 both have parameters A and B.
+            constraints can be ``constraints = [(M1.A, M2.B+2), (M1.B= M2.A *5),...,]``
+            
+             
+        :note: pars must contains only name of existing model's parameters
+        
+        """
+        if not pars:
+            raise ValueError("no fitting parameters")
+
+        if model is None:
+            raise ValueError("no model to fit")
+
+        if not issubclass(model.__class__, Model):
+            model = Model(model, data)
+
+        sasmodel = model.model
+        available_parameters = sasmodel.getParamList()
+        for p in pars:
+            if p not in available_parameters:
+                raise ValueError("parameter %s not available in model %s; use one of [%s] instead"
+                                 %(p, sasmodel.name, ", ".join(available_parameters)))
+
+        if id not in self.fit_arrange_dict:
+            self.fit_arrange_dict[id] = FitArrange()
+
+        self.fit_arrange_dict[id].set_model(model)
+        self.fit_arrange_dict[id].pars = pars
+        self.fit_arrange_dict[id].vals = [sasmodel.getParam(name) for name in pars]
+        self.fit_arrange_dict[id].constraints = constraints
+
+    def set_data(self, data, id, smearer=None, qmin=None, qmax=None):
+        """
+        Receives plottable, creates a list of data to fit,set data
+        in a FitArrange object and adds that object in a dictionary
+        with key id.
+        
+        :param data: data added
+        :param id: unique key corresponding to a fitArrange object with data
+        """
+        if data.__class__.__name__ == 'Data2D':
+            fitdata = FitData2D(sas_data2d=data, data=data.data,
+                                 err_data=data.err_data)
+        else:
+            fitdata = FitData1D(x=data.x, y=data.y,
+                                 dx=data.dx, dy=data.dy, smearer=smearer)
+        fitdata.sas_data = data
+       
+        fitdata.set_fit_range(qmin=qmin, qmax=qmax)
+        #A fitArrange is already created but contains model only at id
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].add_data(fitdata)
+        else:
+        #no fitArrange object has been create with this id
+            fitproblem = FitArrange()
+            fitproblem.add_data(fitdata)
+            self.fit_arrange_dict[id] = fitproblem
+   
+    def get_model(self, id):
+        """
+        :param id: id is key in the dictionary containing the model to return
+        
+        :return:  a model at this id or None if no FitArrange element was
+            created with this id
+        """
+        if id in self.fit_arrange_dict:
+            return self.fit_arrange_dict[id].get_model()
+        else:
+            return None
+    
+    def remove_fit_problem(self, id):
+        """remove   fitarrange in id"""
+        if id in self.fit_arrange_dict:
+            del self.fit_arrange_dict[id]
+            
+    def select_problem_for_fit(self, id, value):
+        """
+        select a couple of model and data at the id position in dictionary
+        and set in self.selected value to value
+        
+        :param value: the value to allow fitting.
+                can only have the value one or zero
+        """
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].set_to_fit(value)
+             
+    def get_problem_to_fit(self, id):
+        """
+        return the self.selected value of the fit problem of id
+        
+        :param id: the id of the problem
+        """
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].get_to_fit()
+    
+    
+class FitArrange:
+    def __init__(self):
+        """
+        Class FitArrange contains a set of data for a given model
+        to perform the Fit.FitArrange must contain exactly one model
+        and at least one data for the fit to be performed.
+        
+        model: the model selected by the user
+        Ldata: a list of data what the user wants to fit
+            
+        """
+        self.model = None
+        self.data_list = []
+        self.pars = []
+        self.vals = []
+        self.selected = 0
+
+    def set_model(self, model):
+        """
+        set_model save a copy of the model
+        
+        :param model: the model being set
+        """
+        self.model = model
+        
+    def add_data(self, data):
+        """
+        add_data fill a self.data_list with data to fit
+        
+        :param data: Data to add in the list
+        """
+        if not data in self.data_list:
+            self.data_list.append(data)
+            
+    def get_model(self):
+        """
+        :return: saved model
+        """
+        return self.model
+     
+    def get_data(self):
+        """
+        :return: list of data data_list
+        """
+        return self.data_list[0]
+      
+    def remove_data(self, data):
+        """
+        Remove one element from the list
+        
+        :param data: Data to remove from data_list
+        """
+        if data in self.data_list:
+            self.data_list.remove(data)
+            
+    def set_to_fit(self, value=0):
+        """
+        set self.selected to 0 or 1  for other values raise an exception
+        
+        :param value: integer between 0 or 1
+        """
+        self.selected = value
+        
+    def get_to_fit(self):
+        """
+        return self.selected value
+        """
+        return self.selected
+
+class FResult(object):
+    """
+    Storing fit result
+    """
+    def __init__(self, model=None, param_list=None, data=None):
+        self.calls = None
+        self.fitness = None
+        self.chisqr = None
+        self.pvec = []
+        self.cov = []
+        self.info = None
+        self.mesg = None
+        self.success = None
+        self.stderr = None
+        self.residuals = []
+        self.index = []
+        self.model = model
+        self.data = data
+        self.theory = []
+        self.param_list = param_list
+        self.iterations = 0
+        self.inputs = []
+        self.fitter_id = None
+        if self.model is not None and self.data is not None:
+            self.inputs = [(self.model, self.data)]
+     
+    def set_model(self, model):
+        """
+        """
+        self.model = model
+        
+    def set_fitness(self, fitness):
+        """
+        """
+        self.fitness = fitness
+        
+    def __str__(self):
+        """
+        """
+        if self.pvec == None and self.model is None and self.param_list is None:
+            return "No results"
+
+        sasmodel = self.model.model
+        pars = enumerate(sasmodel.getParamList())
+        msg1 = "[Iteration #: %s ]" % self.iterations
+        msg3 = "=== goodness of fit: %s ===" % (str(self.fitness))
+        msg2 = ["P%-3d  %s......|.....%s" % (i, v, sasmodel.getParam(v))
+                for i,v in pars if v in self.param_list]
+        msg = [msg1, msg3] + msg2
+        return "\n".join(msg)
+    
+    def print_summary(self):
+        """
+        """
+        print str(self)
Index: src/sas/sascalc/fit/BumpsFitting.py
===================================================================
--- src/sas/sascalc/fit/BumpsFitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/BumpsFitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,365 @@
+"""
+BumpsFitting module runs the bumps optimizer.
+"""
+import os
+from datetime import timedelta, datetime
+
+import numpy
+
+from bumps import fitters
+try:
+    from bumps.options import FIT_CONFIG
+    # Default bumps to use the Levenberg-Marquardt optimizer
+    FIT_CONFIG.selected_id = fitters.LevenbergMarquardtFit.id
+    def get_fitter():
+        return FIT_CONFIG.selected_fitter, FIT_CONFIG.selected_values
+except:
+    # CRUFT: Bumps changed its handling of fit options around 0.7.5.6
+    # Default bumps to use the Levenberg-Marquardt optimizer
+    fitters.FIT_DEFAULT = 'lm'
+    def get_fitter():
+        fitopts = fitters.FIT_OPTIONS[fitters.FIT_DEFAULT]
+        return fitopts.fitclass, fitopts.options.copy()
+
+
+from bumps.mapper import SerialMapper, MPMapper
+from bumps import parameter
+from bumps.fitproblem import FitProblem
+
+
+from sas.sascalc.fit.AbstractFitEngine import FitEngine
+from sas.sascalc.fit.AbstractFitEngine import FResult
+from sas.sascalc.fit.expression import compile_constraints
+
+class Progress(object):
+    def __init__(self, history, max_step, pars, dof):
+        remaining_time = int(history.time[0]*(float(max_step)/history.step[0]-1))
+        # Depending on the time remaining, either display the expected
+        # time of completion, or the amount of time remaining.  Use precision
+        # appropriate for the duration.
+        if remaining_time >= 1800:
+            completion_time = datetime.now() + timedelta(seconds=remaining_time)
+            if remaining_time >= 36000:
+                time = completion_time.strftime('%Y-%m-%d %H:%M')
+            else:
+                time = completion_time.strftime('%H:%M')
+        else:
+            if remaining_time >= 3600:
+                time = '%dh %dm'%(remaining_time//3600, (remaining_time%3600)//60)
+            elif remaining_time >= 60:
+                time = '%dm %ds'%(remaining_time//60, remaining_time%60)
+            else:
+                time = '%ds'%remaining_time
+        chisq = "%.3g"%(2*history.value[0]/dof)
+        step = "%d of %d"%(history.step[0], max_step)
+        header = "=== Steps: %s  chisq: %s  ETA: %s\n"%(step, chisq, time)
+        parameters = ["%15s: %-10.3g%s"%(k,v,("\n" if i%3==2 else " | "))
+                      for i,(k,v) in enumerate(zip(pars,history.point[0]))]
+        self.msg = "".join([header]+parameters)
+
+    def __str__(self):
+        return self.msg
+
+
+class BumpsMonitor(object):
+    def __init__(self, handler, max_step, pars, dof):
+        self.handler = handler
+        self.max_step = max_step
+        self.pars = pars
+        self.dof = dof
+
+    def config_history(self, history):
+        history.requires(time=1, value=2, point=1, step=1)
+
+    def __call__(self, history):
+        if self.handler is None: return
+        self.handler.set_result(Progress(history, self.max_step, self.pars, self.dof))
+        self.handler.progress(history.step[0], self.max_step)
+        if len(history.step)>1 and history.step[1] > history.step[0]:
+            self.handler.improvement()
+        self.handler.update_fit()
+
+class ConvergenceMonitor(object):
+    """
+    ConvergenceMonitor contains population summary statistics to show progress
+    of the fit.  This is a list [ (best, 0%, 25%, 50%, 75%, 100%) ] or
+    just a list [ (best, ) ] if population size is 1.
+    """
+    def __init__(self):
+        self.convergence = []
+
+    def config_history(self, history):
+        history.requires(value=1, population_values=1)
+
+    def __call__(self, history):
+        best = history.value[0]
+        try:
+            p = history.population_values[0]
+            n,p = len(p), numpy.sort(p)
+            QI,Qmid, = int(0.2*n),int(0.5*n)
+            self.convergence.append((best, p[0],p[QI],p[Qmid],p[-1-QI],p[-1]))
+        except:
+            self.convergence.append((best, best,best,best,best,best))
+
+
+# Note: currently using bumps parameters for each parameter object so that
+# a SasFitness can be used directly in bumps with the usual semantics.
+# The disadvantage of this technique is that we need to copy every parameter
+# back into the model each time the function is evaluated.  We could instead
+# define reference parameters for each sas parameter, but then we would not
+# be able to express constraints using python expressions in the usual way
+# from bumps, and would instead need to use string expressions.
+class SasFitness(object):
+    """
+    Wrap SAS model as a bumps fitness object
+    """
+    def __init__(self, model, data, fitted=[], constraints={},
+                 initial_values=None, **kw):
+        self.name = model.name
+        self.model = model.model
+        self.data = data
+        if self.data.smearer is not None:
+            self.data.smearer.model = self.model
+        self._define_pars()
+        self._init_pars(kw)
+        if initial_values is not None:
+            self._reset_pars(fitted, initial_values)
+        self.constraints = dict(constraints)
+        self.set_fitted(fitted)
+        self.update()
+
+    def _reset_pars(self, names, values):
+        for k,v in zip(names, values):
+            self._pars[k].value = v
+
+    def _define_pars(self):
+        self._pars = {}
+        for k in self.model.getParamList():
+            name = ".".join((self.name,k))
+            value = self.model.getParam(k)
+            bounds = self.model.details.get(k,["",None,None])[1:3]
+            self._pars[k] = parameter.Parameter(value=value, bounds=bounds,
+                                                fixed=True, name=name)
+        #print parameter.summarize(self._pars.values())
+
+    def _init_pars(self, kw):
+        for k,v in kw.items():
+            # dispersion parameters initialized with _field instead of .field
+            if k.endswith('_width'): k = k[:-6]+'.width'
+            elif k.endswith('_npts'): k = k[:-5]+'.npts'
+            elif k.endswith('_nsigmas'): k = k[:-7]+'.nsigmas'
+            elif k.endswith('_type'): k = k[:-5]+'.type'
+            if k not in self._pars:
+                formatted_pars = ", ".join(sorted(self._pars.keys()))
+                raise KeyError("invalid parameter %r for %s--use one of: %s"
+                               %(k, self.model, formatted_pars))
+            if '.' in k and not k.endswith('.width'):
+                self.model.setParam(k, v)
+            elif isinstance(v, parameter.BaseParameter):
+                self._pars[k] = v
+            elif isinstance(v, (tuple,list)):
+                low, high = v
+                self._pars[k].value = (low+high)/2
+                self._pars[k].range(low,high)
+            else:
+                self._pars[k].value = v
+
+    def set_fitted(self, param_list):
+        """
+        Flag a set of parameters as fitted parameters.
+        """
+        for k,p in self._pars.items():
+            p.fixed = (k not in param_list or k in self.constraints)
+        self.fitted_par_names = [k for k in param_list if k not in self.constraints]
+        self.computed_par_names = [k for k in param_list if k in self.constraints]
+        self.fitted_pars = [self._pars[k] for k in self.fitted_par_names]
+        self.computed_pars = [self._pars[k] for k in self.computed_par_names]
+
+    # ===== Fitness interface ====
+    def parameters(self):
+        return self._pars
+
+    def update(self):
+        for k,v in self._pars.items():
+            #print "updating",k,v,v.value
+            self.model.setParam(k,v.value)
+        self._dirty = True
+
+    def _recalculate(self):
+        if self._dirty:
+            self._residuals, self._theory \
+                = self.data.residuals(self.model.evalDistribution)
+            self._dirty = False
+
+    def numpoints(self):
+        return numpy.sum(self.data.idx) # number of fitted points
+
+    def nllf(self):
+        return 0.5*numpy.sum(self.residuals()**2)
+
+    def theory(self):
+        self._recalculate()
+        return self._theory
+
+    def residuals(self):
+        self._recalculate()
+        return self._residuals
+
+    # Not implementing the data methods for now:
+    #
+    #     resynth_data/restore_data/save/plot
+
+class ParameterExpressions(object):
+    def __init__(self, models):
+        self.models = models
+        self._setup()
+
+    def _setup(self):
+        exprs = {}
+        for M in self.models:
+            exprs.update((".".join((M.name, k)), v) for k, v in M.constraints.items())
+        if exprs:
+            symtab = dict((".".join((M.name, k)), p)
+                          for M in self.models
+                          for k,p in M.parameters().items())
+            self.update = compile_constraints(symtab, exprs)
+        else:
+            self.update = lambda: 0
+
+    def __call__(self):
+        self.update()
+
+    def __getstate__(self):
+        return self.models
+
+    def __setstate__(self, state):
+        self.models = state
+        self._setup()
+
+class BumpsFit(FitEngine):
+    """
+    Fit a model using bumps.
+    """
+    def __init__(self):
+        """
+        Creates a dictionary (self.fit_arrange_dict={})of FitArrange elements
+        with Uid as keys
+        """
+        FitEngine.__init__(self)
+        self.curr_thread = None
+
+    def fit(self, msg_q=None,
+            q=None, handler=None, curr_thread=None,
+            ftol=1.49012e-8, reset_flag=False):
+        # Build collection of bumps fitness calculators
+        models = [SasFitness(model=M.get_model(),
+                             data=M.get_data(),
+                             constraints=M.constraints,
+                             fitted=M.pars,
+                             initial_values=M.vals if reset_flag else None)
+                  for M in self.fit_arrange_dict.values()
+                  if M.get_to_fit()]
+        if len(models) == 0:
+            raise RuntimeError("Nothing to fit")
+        problem = FitProblem(models)
+
+        # TODO: need better handling of parameter expressions and bounds constraints
+        # so that they are applied during polydispersity calculations.  This
+        # will remove the immediate need for the setp_hook in bumps, though
+        # bumps may still need something similar, such as a sane class structure
+        # which allows a subclass to override setp.
+        problem.setp_hook = ParameterExpressions(models)
+
+        # Run the fit
+        result = run_bumps(problem, handler, curr_thread)
+        if handler is not None:
+            handler.update_fit(last=True)
+
+        # TODO: shouldn't reference internal parameters of fit problem
+        varying = problem._parameters
+        # collect the results
+        all_results = []
+        for M in problem.models:
+            fitness = M.fitness
+            fitted_index = [varying.index(p) for p in fitness.fitted_pars]
+            param_list = fitness.fitted_par_names + fitness.computed_par_names
+            R = FResult(model=fitness.model, data=fitness.data,
+                        param_list=param_list)
+            R.theory = fitness.theory()
+            R.residuals = fitness.residuals()
+            R.index = fitness.data.idx
+            R.fitter_id = self.fitter_id
+            # TODO: should scale stderr by sqrt(chisq/DOF) if dy is unknown
+            R.success = result['success']
+            if R.success:
+                R.stderr = numpy.hstack((result['stderr'][fitted_index],
+                                         numpy.NaN*numpy.ones(len(fitness.computed_pars))))
+                R.pvec = numpy.hstack((result['value'][fitted_index],
+                                      [p.value for p in fitness.computed_pars]))
+                R.fitness = numpy.sum(R.residuals**2)/(fitness.numpoints() - len(fitted_index))
+            else:
+                R.stderr = numpy.NaN*numpy.ones(len(param_list))
+                R.pvec = numpy.asarray( [p.value for p in fitness.fitted_pars+fitness.computed_pars])
+                R.fitness = numpy.NaN
+            R.convergence = result['convergence']
+            if result['uncertainty'] is not None:
+                R.uncertainty_state = result['uncertainty']
+            all_results.append(R)
+
+        if q is not None:
+            q.put(all_results)
+            return q
+        else:
+            return all_results
+
+def run_bumps(problem, handler, curr_thread):
+    def abort_test():
+        if curr_thread is None: return False
+        try: curr_thread.isquit()
+        except KeyboardInterrupt:
+            if handler is not None:
+                handler.stop("Fitting: Terminated!!!")
+            return True
+        return False
+
+    fitclass, options = get_fitter()
+    steps = options.get('steps', 0)
+    if steps == 0:
+        pop = options.get('pop',0)*len(problem._parameters)
+        samples = options.get('samples', 0)
+        steps = (samples+pop-1)/pop if pop != 0 else samples
+    max_step = steps + options.get('burn', 0)
+    pars = [p.name for p in problem._parameters]
+    #x0 = numpy.asarray([p.value for p in problem._parameters])
+    options['monitors'] = [
+        BumpsMonitor(handler, max_step, pars, problem.dof),
+        ConvergenceMonitor(),
+        ]
+    fitdriver = fitters.FitDriver(fitclass, problem=problem,
+                                  abort_test=abort_test, **options)
+    omp_threads = int(os.environ.get('OMP_NUM_THREADS','0'))
+    mapper = MPMapper if omp_threads == 1 else SerialMapper
+    fitdriver.mapper = mapper.start_mapper(problem, None)
+    #import time; T0 = time.time()
+    try:
+        best, fbest = fitdriver.fit()
+    except:
+        import traceback; traceback.print_exc()
+        raise
+    finally:
+        mapper.stop_mapper(fitdriver.mapper)
+
+
+    convergence_list = options['monitors'][-1].convergence
+    convergence = (2*numpy.asarray(convergence_list)/problem.dof
+                   if convergence_list else numpy.empty((0,1),'d'))
+
+    success = best is not None
+    return {
+        'value': best if success else None,
+        'stderr': fitdriver.stderr() if success else None,
+        'success': success,
+        'convergence': convergence,
+        'uncertainty': getattr(fitdriver.fitter, 'state', None),
+        }
+
Index: src/sas/sascalc/fit/Loader.py
===================================================================
--- src/sas/sascalc/fit/Loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/Loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,86 @@
+# class Loader  to load any king of file
+#import wx
+#import string
+import numpy
+
+class Load:
+    """
+    This class is loading values from given file or value giving by the user
+    """
+    def __init__(self, x=None, y=None, dx=None, dy=None):
+        raise NotImplementedError("a code search shows that this code is not active, and you are not seeing this message")
+        # variable to store loaded values
+        self.x = x
+        self.y = y
+        self.dx = dx
+        self.dy = dy
+        self.filename = None
+        
+    def set_filename(self, path=None):
+        """
+        Store path into a variable.If the user doesn't give 
+        a path as a parameter a pop-up
+        window appears to select the file.
+        
+        :param path: the path given by the user
+        
+        """
+        self.filename = path
+       
+    def get_filename(self):
+        """ return the file's path"""
+        return self.filename
+    
+    def set_values(self):
+        """ Store the values loaded from file in local variables"""
+        if not self.filename == None:
+            input_f =  open(self.filename, 'r')
+            buff = input_f.read()
+            lines = buff.split('\n')
+            self.x = []
+            self.y = []
+            self.dx = [] 
+            self.dy = []
+            for line in lines:
+                try:
+                    toks = line.split()
+                    x = float(toks[0])
+                    y = float(toks[1])
+                    dy = float(toks[2])
+                    
+                    self.x.append(x)
+                    self.y.append(y)
+                    self.dy.append(dy)
+                    self.dx = numpy.zeros(len(self.x))
+                except:
+                    print "READ ERROR", line
+            # Sanity check
+            if not len(self.x) == len(self.dx):
+                raise ValueError, "x and dx have different length"
+            if not len(self.y) == len(self.dy):
+                raise ValueError, "y and dy have different length"
+            
+            
+    def get_values(self):
+        """ Return x, y, dx, dy"""
+        return self.x, self.y, self.dx, self.dy
+    
+    def load_data(self, data):
+        """ Return plottable"""
+        #load data
+        data.x = self.x
+        data.y = self.y
+        data.dx = self.dx
+        data.dy = self.dy
+        #Load its View class
+        #plottable.reset_view()
+       
+    
+if __name__ == "__main__": 
+    load = Load()
+    load.set_filename("testdata_line.txt")
+    print load.get_filename() 
+    load.set_values()
+    print load.get_values()
+    
+            
Index: src/sas/sascalc/fit/__init__.py
===================================================================
--- src/sas/sascalc/fit/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1 @@
+from .AbstractFitEngine import FitHandler
Index: src/sas/sascalc/fit/expression.py
===================================================================
--- src/sas/sascalc/fit/expression.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/fit/expression.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,400 @@
+# This program is public domain
+"""
+Parameter expression evaluator.
+
+For systems in which constraints are expressed as string expressions rather
+than python code, :func:`compile_constraints` can construct an expression
+evaluator that substitutes the computed values of the expressions into the
+parameters.
+
+The compiler requires a symbol table, an expression set and a context.
+The symbol table maps strings containing fully qualified names such as
+'M1.c[3].full_width' to parameter objects with a 'value' property that
+can be queried and set.  The expression set maps symbol names from the
+symbol table to string expressions.  The context provides additional symbols
+for the expressions in addition to the usual mathematical functions and
+constants.
+
+The expressions are compiled and interpreted by python, with only minimal
+effort to make sure that they don't contain bad code.  The resulting
+constraints function returns 0 so it can be used directly in a fit problem
+definition.
+
+Extracting the symbol table from the model depends on the structure of the
+model.  If fitness.parameters() is set correctly, then this should simply
+be a matter of walking the parameter data, remembering the path to each
+parameter in the symbol table.  For compactness, dictionary elements should
+be referenced by .name rather than ["name"].  Model name can be used as the
+top level.
+
+Getting the parameter expressions applied correctly is challenging.
+The following monkey patch works by overriding model_update in FitProblem
+so that after setp(p) is called and, the constraints expression can be
+applied before telling the underlying fitness function that the model
+is out of date::
+
+        # Override model update so that parameter constraints are applied
+        problem._model_update = problem.model_update
+        def model_update():
+            constraints()
+            problem._model_update()
+        problem.model_update = model_update
+
+Ideally, this interface will change
+"""
+import math
+import re
+
+# simple pattern which matches symbols.  Note that it will also match
+# invalid substrings such as a3...9, but given syntactically correct
+# input it will only match symbols.
+_symbol_pattern = re.compile('([a-zA-Z_][a-zA-Z_0-9.]*)')
+
+def _symbols(expr,symtab):
+    """
+    Given an expression string and a symbol table, return the set of symbols
+    used in the expression.  Symbols are only returned once even if they
+    occur multiple times.  The return value is a set with the elements in
+    no particular order.
+    
+    This is the first step in computing a dependency graph.
+    """
+    matches = [m.group(0) for m in _symbol_pattern.finditer(expr)]
+    return set([symtab[m] for m in matches if m in symtab])
+
+def _substitute(expr,mapping):
+    """
+    Replace all occurrences of symbol s with mapping[s] for s in mapping.
+    """
+    # Find the symbols and the mapping
+    matches = [(m.start(),m.end(),mapping[m.group(1)])
+               for m in _symbol_pattern.finditer(expr)
+               if m.group(1) in mapping]
+
+    # Split the expression in to pieces, with new symbols replacing old
+    pieces = []
+    offset = 0
+    for start,end,text in matches:
+        pieces += [expr[offset:start],text]
+        offset = end
+    pieces.append(expr[offset:])
+    
+    # Join the pieces and return them
+    return "".join(pieces)
+
+def _find_dependencies(symtab, exprs):
+    """
+    Returns a list of pair-wise dependencies from the parameter expressions.
+    
+    For example, if p3 = p1+p2, then find_dependencies([p1,p2,p3]) will
+    return [(p3,p1),(p3,p2)].  For base expressions without dependencies,
+    such as p4 = 2*pi, this should return [(p4, None)]
+    """
+    deps = [(target,source)
+            for target,expr in exprs.items()
+            for source in _symbols_or_none(expr,symtab)]
+    return deps
+
+# Hack to deal with expressions without dependencies --- return a fake
+# dependency of None.
+# The better solution is fix order_dependencies so that it takes a
+# dictionary of {symbol: dependency_list}, for which no dependencies
+# is simply []; fix in parameter_mapping as well
+def _symbols_or_none(expr,symtab):
+    syms = _symbols(expr,symtab)
+    return syms if len(syms) else [None]
+
+def _parameter_mapping(pairs):
+    """
+    Find the parameter substitution we need so that expressions can
+    be evaluated without having to traverse a chain of 
+    model.layer.parameter.value
+    """
+    left,right = zip(*pairs)
+    pars = list(sorted(p for p in set(left+right) if p is not None))
+    definition = dict( ('P%d'%i,p)  for i,p in enumerate(pars) )
+    # p is None when there is an expression with no dependencies
+    substitution = dict( (p,'P%d.value'%i)
+                    for i,p in enumerate(sorted(pars))
+                    if p is not None)
+    return definition, substitution
+
+def no_constraints(): 
+    """
+    This parameter set has no constraints between the parameters.
+    """
+    pass
+
+def compile_constraints(symtab, exprs, context={}):
+    """
+    Build and return a function to evaluate all parameter expressions in
+    the proper order.
+
+    Input:
+
+        *symtab* is the symbol table for the model: { 'name': parameter }
+
+        *exprs* is the set of computed symbols: { 'name': 'expression' }
+
+        *context* is any additional context needed to evaluate the expression
+
+    Return:
+
+        updater function which sets parameter.value for each expression
+
+    Raises:
+
+       AssertionError - model, parameter or function is missing
+
+       SyntaxError - improper expression syntax
+
+       ValueError - expressions have circular dependencies
+
+    This function is not terribly sophisticated, and it would be easy to
+    trick.  However it handles the common cases cleanly and generates
+    reasonable messages for the common errors.
+
+    This code has not been fully audited for security.  While we have
+    removed the builtins and the ability to import modules, there may
+    be other vectors for users to perform more than simple function
+    evaluations.  Unauthenticated users should not be running this code.
+
+    Parameter names are assumed to contain only _.a-zA-Z0-9#[]
+    
+    Both names are provided for inverse functions, e.g., acos and arccos.
+
+    Should try running the function to identify syntax errors before
+    running it in a fit.
+    
+    Use help(fn) to see the code generated for the returned function fn.
+    dis.dis(fn) will show the corresponding python vm instructions.
+    """
+
+    # Sort the parameters in the order they need to be evaluated
+    deps = _find_dependencies(symtab, exprs)
+    if deps == []: return no_constraints
+    order = order_dependencies(deps)
+
+
+    # Rather than using the full path to the parameters in the parameter
+    # expressions, instead use Pn, and substitute Pn.value for each occurrence
+    # of the parameter in the expression.
+    names = list(sorted(symtab.keys()))
+    parameters = dict(('P%d'%i, symtab[k]) for i,k in enumerate(names))
+    mapping = dict((k, 'P%d.value'%i) for i,k in enumerate(names))
+
+
+    # Initialize dictionary with available functions
+    globals = {}
+    globals.update(math.__dict__)
+    globals.update(dict(arcsin=math.asin,arccos=math.acos,
+                        arctan=math.atan,arctan2=math.atan2))
+    globals.update(context)
+    globals.update(parameters)
+    globals['id'] = id
+    locals = {}
+
+    # Define the constraints function
+    assignments = ["=".join((p,exprs[p])) for p in order]
+    code = [_substitute(s, mapping) for s in assignments]
+    functiondef = """
+def eval_expressions():
+    '''
+    %s
+    '''
+    %s
+    return 0
+"""%("\n    ".join(assignments),"\n    ".join(code))
+
+    #print("Function: "+functiondef)
+    exec functiondef in globals,locals
+    retfn = locals['eval_expressions']
+
+    # Remove garbage added to globals by exec
+    globals.pop('__doc__',None)
+    globals.pop('__name__',None)
+    globals.pop('__file__',None)
+    globals.pop('__builtins__')
+    #print globals.keys()
+
+    return retfn
+
+def order_dependencies(pairs):
+    """
+    Order elements from pairs so that b comes before a in the
+    ordered list for all pairs (a,b).
+    """
+    #print "order_dependencies",pairs
+    emptyset = set()
+    order = []
+
+    # Break pairs into left set and right set
+    left,right = [set(s) for s in zip(*pairs)] if pairs != [] else ([],[])
+    while pairs != []:
+        #print "within",pairs
+        # Find which items only occur on the right
+        independent = right - left
+        if independent == emptyset:
+            cycleset = ", ".join(str(s) for s in left)
+            raise ValueError,"Cyclic dependencies amongst %s"%cycleset
+
+        # The possibly resolvable items are those that depend on the independents
+        dependent = set([a for a,b in pairs if b in independent])
+        pairs = [(a,b) for a,b in pairs if b not in independent]
+        if pairs == []:
+            resolved = dependent
+        else:
+            left,right = [set(s) for s in zip(*pairs)]
+            resolved = dependent - left
+        #print "independent",independent,"dependent",dependent,"resolvable",resolved
+        order += resolved
+        #print "new order",order
+    order.reverse()
+    return order
+
+# ========= Test code ========
+def _check(msg,pairs):
+    """
+    Verify that the list n contains the given items, and that the list
+    satisfies the partial ordering given by the pairs in partial order.
+    """
+    left,right = zip(*pairs) if pairs != [] else ([],[])
+    items = set(left)
+    n = order_dependencies(pairs)
+    if set(n) != items or len(n) != len(items):
+        n.sort()
+        items = list(items); items.sort()
+        raise Exception,"%s expect %s to contain %s for %s"%(msg,n,items,pairs)
+    for lo,hi in pairs:
+        if lo in n and hi in n and n.index(lo) >= n.index(hi):
+            raise Exception,"%s expect %s before %s in %s for %s"%(msg,lo,hi,n,pairs)
+
+def test_deps():
+    import numpy
+
+    # Null case
+    _check("test empty",[])
+
+    # Some dependencies
+    _check("test1",[(2,7),(1,5),(1,4),(2,1),(3,1),(5,6)])
+    _check("test1 renumbered",[(6,1),(7,3),(7,4),(6,7),(5,7),(3,2)])
+    _check("test1 numpy",numpy.array([(2,7),(1,5),(1,4),(2,1),(3,1),(5,6)]))
+
+    # No dependencies
+    _check("test2",[(4,1),(3,2),(8,4)])
+
+    # Cycle test
+    pairs = [(1,4),(4,3),(4,5),(5,1)]
+    try: n = order_dependencies(pairs)
+    except ValueError: pass
+    else: raise Exception,"test3 expect ValueError exception for %s"%(pairs,)
+
+    # large test for gross speed check
+    A = numpy.random.randint(4000,size=(1000,2))
+    A[:,1] += 4000  # Avoid cycles
+    _check("test-large",A)
+
+    # depth tests
+    k = 200
+    A = numpy.array([range(0,k),range(1,k+1)]).T
+    _check("depth-1",A)
+
+    A = numpy.array([range(1,k+1),range(0,k)]).T
+    _check("depth-2",A)
+
+def test_expr():
+    import inspect, dis
+    import math
+    
+    symtab = {'a.b.x':1, 'a.c':2, 'a.b':3, 'b.x':4}
+    expr = 'a.b.x + sin(4*pi*a.c) + a.b.x/a.b'
+    
+    # Check symbol lookup
+    assert _symbols(expr, symtab) == set([1,2,3])
+
+    # Check symbol rename
+    assert _substitute(expr,{'a.b.x':'Q'}) == 'Q + sin(4*pi*a.c) + Q/a.b'
+    assert _substitute(expr,{'a.b':'Q'}) == 'a.b.x + sin(4*pi*a.c) + a.b.x/Q'
+
+
+    # Check dependency builder
+    # Fake parameter class
+    class Parameter:
+        def __init__(self, name, value=0, expression=''):
+            self.path = name
+            self.value = value
+            self.expression = expression
+        def iscomputed(self): return (self.expression != '')
+        def __repr__(self): return self.path
+    def world(*pars):
+        symtab = dict((p.path,p) for p in pars)
+        exprs = dict((p.path,p.expression) for p in pars if p.iscomputed())
+        return symtab, exprs
+    p1 = Parameter('G0.sigma',5)
+    p2 = Parameter('other',expression='2*pi*sin(G0.sigma/.1875) + M1.G1')
+    p3 = Parameter('M1.G1',6)
+    p4 = Parameter('constant',expression='2*pi*35')
+    # Simple chain
+    assert set(_find_dependencies(*world(p1,p2,p3))) == set([(p2.path,p1),(p2.path,p3)])
+    # Constant expression
+    assert set(_find_dependencies(*world(p1,p4))) == set([(p4.path,None)])
+    # No dependencies
+    assert set(_find_dependencies(*world(p1,p3))) == set([])
+
+    # Check function builder
+    fn = compile_constraints(*world(p1,p2,p3))
+
+    # Inspect the resulting function
+    if 0:
+        print(inspect.getdoc(fn))
+        print(dis.dis(fn))
+
+    # Evaluate the function and see if it updates the
+    # target value as expected
+    fn()
+    expected = 2*math.pi*math.sin(5/.1875) + 6
+    assert p2.value == expected,"Value was %s, not %s"%(p2.value,expected)
+    
+    # Check empty dependency set doesn't crash
+    fn = compile_constraints(*world(p1,p3))
+    fn()
+
+    # Check that constants are evaluated properly
+    fn = compile_constraints(*world(p4))
+    fn()
+    assert p4.value == 2*math.pi*35
+
+    # Check additional context example; this also tests multiple
+    # expressions
+    class Table:
+        Si = 2.09
+        values = {'Si': 2.07}
+    tbl = Table()
+    p5 = Parameter('lookup',expression="tbl.Si")
+    fn = compile_constraints(*world(p1,p2,p3,p5),context=dict(tbl=tbl))
+    fn()
+    assert p5.value == 2.09,"Value for %s was %s"%(p5.expression,p5.value)
+    p5.expression = "tbl.values['Si']"
+    fn = compile_constraints(*world(p1,p2,p3,p5),context=dict(tbl=tbl))
+    fn()
+    assert p5.value == 2.07,"Value for %s was %s"%(p5.expression,p5.value)
+    
+
+    # Verify that we capture invalid expressions
+    for expr in ['G4.cage', 'M0.cage', 'M1.G1 + *2', 
+                 'piddle',
+                 '5; import sys; print "p0wned"',
+                 '__import__("sys").argv']:
+        try:
+            p6 = Parameter('broken',expression=expr)
+            fn = compile_constraints(*world(p6))
+            fn()
+        except Exception as msg:
+            #print(msg)
+            pass
+        else:
+            raise "Failed to raise error for %s"%expr
+
+if __name__ == "__main__":
+    test_expr()
+    test_deps()
Index: src/sas/sascalc/invariant/invariant.py
===================================================================
--- src/sas/sascalc/invariant/invariant.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/invariant/invariant.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,962 @@
+# pylint: disable=invalid-name
+#####################################################################
+#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.
+#See the license text in license.txt
+#copyright 2010, University of Tennessee
+######################################################################
+
+"""
+This module implements invariant and its related computations.
+
+:author: Gervaise B. Alina/UTK
+:author: Mathieu Doucet/UTK
+:author: Jae Cho/UTK
+
+"""
+import math
+import numpy
+
+from sas.sascalc.dataloader.data_info import Data1D as LoaderData1D
+
+# The minimum q-value to be used when extrapolating
+Q_MINIMUM = 1e-5
+
+# The maximum q-value to be used when extrapolating
+Q_MAXIMUM = 10
+
+# Number of steps in the extrapolation
+INTEGRATION_NSTEPS = 1000
+
+class Transform(object):
+    """
+    Define interface that need to compute a function or an inverse
+    function given some x, y
+    """
+
+    def linearize_data(self, data):
+        """
+        Linearize data so that a linear fit can be performed.
+        Filter out the data that can't be transformed.
+
+        :param data: LoadData1D instance
+
+        """
+        # Check that the vector lengths are equal
+        assert len(data.x) == len(data.y)
+        if data.dy is not None:
+            assert len(data.x) == len(data.dy)
+            dy = data.dy
+        else:
+            dy = numpy.ones(len(data.y))
+
+        # Transform the data
+        data_points = zip(data.x, data.y, dy)
+
+        output_points = [(self.linearize_q_value(p[0]),
+                          math.log(p[1]),
+                          p[2] / p[1]) for p in data_points if p[0] > 0 and \
+                          p[1] > 0 and p[2] > 0]
+
+        x_out, y_out, dy_out = zip(*output_points)
+
+        # Create Data1D object
+        x_out = numpy.asarray(x_out)
+        y_out = numpy.asarray(y_out)
+        dy_out = numpy.asarray(dy_out)
+        linear_data = LoaderData1D(x=x_out, y=y_out, dy=dy_out)
+
+        return linear_data
+
+    def get_allowed_bins(self, data):
+        """
+        Goes through the data points and returns a list of boolean values
+        to indicate whether each points is allowed by the model or not.
+
+        :param data: Data1D object
+        """
+        return [p[0] > 0 and p[1] > 0 and p[2] > 0 for p in zip(data.x, data.y,
+                                                                data.dy)]
+
+    def linearize_q_value(self, value):
+        """
+        Transform the input q-value for linearization
+        """
+        return NotImplemented
+
+    def extract_model_parameters(self, constant, slope, dconstant=0, dslope=0):
+        """
+        set private member
+        """
+        return NotImplemented
+
+    def evaluate_model(self, x):
+        """
+        Returns an array f(x) values where f is the Transform function.
+        """
+        return NotImplemented
+
+    def evaluate_model_errors(self, x):
+        """
+        Returns an array of I(q) errors
+        """
+        return NotImplemented
+
+class Guinier(Transform):
+    """
+    class of type Transform that performs operations related to guinier
+    function
+    """
+    def __init__(self, scale=1, radius=60):
+        Transform.__init__(self)
+        self.scale = scale
+        self.radius = radius
+        ## Uncertainty of scale parameter
+        self.dscale = 0
+        ## Unvertainty of radius parameter
+        self.dradius = 0
+
+    def linearize_q_value(self, value):
+        """
+        Transform the input q-value for linearization
+
+        :param value: q-value
+
+        :return: q*q
+        """
+        return value * value
+
+    def extract_model_parameters(self, constant, slope, dconstant=0, dslope=0):
+        """
+	    assign new value to the scale and the radius
+    	"""
+        self.scale = math.exp(constant)
+        if slope > 0:
+            slope = 0.0
+        self.radius = math.sqrt(-3 * slope)
+        # Errors
+        self.dscale = math.exp(constant) * dconstant
+        if slope == 0.0:
+            n_zero = -1.0e-24
+            self.dradius = -3.0 / 2.0 / math.sqrt(-3 * n_zero) * dslope
+        else:
+            self.dradius = -3.0 / 2.0 / math.sqrt(-3 * slope) * dslope
+
+        return [self.radius, self.scale], [self.dradius, self.dscale]
+
+    def evaluate_model(self, x):
+        """
+        return F(x)= scale* e-((radius*x)**2/3)
+        """
+        return self._guinier(x)
+
+    def evaluate_model_errors(self, x):
+        """
+        Returns the error on I(q) for the given array of q-values
+
+        :param x: array of q-values
+        """
+        p1 = numpy.array([self.dscale * math.exp(-((self.radius * q) ** 2 / 3)) \
+                          for q in x])
+        p2 = numpy.array([self.scale * math.exp(-((self.radius * q) ** 2 / 3))\
+                     * (-(q ** 2 / 3)) * 2 * self.radius * self.dradius for q in x])
+        diq2 = p1 * p1 + p2 * p2
+        return numpy.array([math.sqrt(err) for err in diq2])
+
+    def _guinier(self, x):
+        """
+        Retrieve the guinier function after apply an inverse guinier function
+        to x
+        Compute a F(x) = scale* e-((radius*x)**2/3).
+
+        :param x: a vector of q values
+        :param scale: the scale value
+        :param radius: the guinier radius value
+
+        :return: F(x)
+        """
+        # transform the radius of coming from the inverse guinier function to a
+        # a radius of a guinier function
+        if self.radius <= 0:
+            msg = "Rg expected positive value, but got %s" % self.radius
+            raise ValueError(msg)
+        value = numpy.array([math.exp(-((self.radius * i) ** 2 / 3)) for i in x])
+        return self.scale * value
+
+class PowerLaw(Transform):
+    """
+    class of type transform that perform operation related to power_law
+    function
+    """
+    def __init__(self, scale=1, power=4):
+        Transform.__init__(self)
+        self.scale = scale
+        self.power = power
+        self.dscale = 0.0
+        self.dpower = 0.0
+
+    def linearize_q_value(self, value):
+        """
+        Transform the input q-value for linearization
+
+        :param value: q-value
+
+        :return: log(q)
+        """
+        return math.log(value)
+
+    def extract_model_parameters(self, constant, slope, dconstant=0, dslope=0):
+        """
+        Assign new value to the scale and the power
+        """
+        self.power = -slope
+        self.scale = math.exp(constant)
+
+        # Errors
+        self.dscale = math.exp(constant) * dconstant
+        self.dpower = -dslope
+
+        return [self.power, self.scale], [self.dpower, self.dscale]
+
+    def evaluate_model(self, x):
+        """
+        given a scale and a radius transform x, y using a power_law
+        function
+        """
+        return self._power_law(x)
+
+    def evaluate_model_errors(self, x):
+        """
+        Returns the error on I(q) for the given array of q-values
+        :param x: array of q-values
+        """
+        p1 = numpy.array([self.dscale * math.pow(q, -self.power) for q in x])
+        p2 = numpy.array([self.scale * self.power * math.pow(q, -self.power - 1)\
+                           * self.dpower for q in x])
+        diq2 = p1 * p1 + p2 * p2
+        return numpy.array([math.sqrt(err) for err in diq2])
+
+    def _power_law(self, x):
+        """
+        F(x) = scale* (x)^(-power)
+            when power= 4. the model is porod
+            else power_law
+        The model has three parameters: ::
+            1. x: a vector of q values
+            2. power: power of the function
+            3. scale : scale factor value
+
+        :param x: array
+        :return: F(x)
+        """
+        if self.power <= 0:
+            msg = "Power_law function expected positive power,"
+            msg += " but got %s" % self.power
+            raise ValueError(msg)
+        if self.scale <= 0:
+            msg = "scale expected positive value, but got %s" % self.scale
+            raise ValueError(msg)
+
+        value = numpy.array([math.pow(i, -self.power) for i in x])
+        return self.scale * value
+
+class Extrapolator(object):
+    """
+    Extrapolate I(q) distribution using a given model
+    """
+    def __init__(self, data, model=None):
+        """
+        Determine a and b given a linear equation y = ax + b
+
+        If a model is given, it will be used to linearize the data before
+        the extrapolation is performed. If None,
+        a simple linear fit will be done.
+
+        :param data: data containing x and y  such as  y = ax + b
+        :param model: optional Transform object
+        """
+        self.data = data
+        self.model = model
+
+        # Set qmin as the lowest non-zero value
+        self.qmin = Q_MINIMUM
+        for q_value in self.data.x:
+            if q_value > 0:
+                self.qmin = q_value
+                break
+        self.qmax = max(self.data.x)
+
+    def fit(self, power=None, qmin=None, qmax=None):
+        """
+        Fit data for y = ax + b  return a and b
+
+        :param power: a fixed, otherwise None
+        :param qmin: Minimum Q-value
+        :param qmax: Maximum Q-value
+        """
+        if qmin is None:
+            qmin = self.qmin
+        if qmax is None:
+            qmax = self.qmax
+
+        # Identify the bin range for the fit
+        idx = (self.data.x >= qmin) & (self.data.x <= qmax)
+
+        fx = numpy.zeros(len(self.data.x))
+
+        # Uncertainty
+        if type(self.data.dy) == numpy.ndarray and \
+            len(self.data.dy) == len(self.data.x) and \
+            numpy.all(self.data.dy > 0):
+            sigma = self.data.dy
+        else:
+            sigma = numpy.ones(len(self.data.x))
+
+        # Compute theory data f(x)
+        fx[idx] = self.data.y[idx]
+
+        # Linearize the data
+        if self.model is not None:
+            linearized_data = self.model.linearize_data(\
+                                            LoaderData1D(self.data.x[idx],
+                                                         fx[idx],
+                                                         dy=sigma[idx]))
+        else:
+            linearized_data = LoaderData1D(self.data.x[idx],
+                                           fx[idx],
+                                           dy=sigma[idx])
+
+        ##power is given only for function = power_law
+        if power != None:
+            sigma2 = linearized_data.dy * linearized_data.dy
+            a = -(power)
+            b = (numpy.sum(linearized_data.y / sigma2) \
+                 - a * numpy.sum(linearized_data.x / sigma2)) / numpy.sum(1.0 / sigma2)
+
+
+            deltas = linearized_data.x * a + \
+                    numpy.ones(len(linearized_data.x)) * b - linearized_data.y
+            residuals = numpy.sum(deltas * deltas / sigma2)
+
+            err = math.fabs(residuals) / numpy.sum(1.0 / sigma2)
+            return [a, b], [0, math.sqrt(err)]
+        else:
+            A = numpy.vstack([linearized_data.x / linearized_data.dy, 1.0 / linearized_data.dy]).T
+            (p, residuals, _, _) = numpy.linalg.lstsq(A, linearized_data.y / linearized_data.dy)
+
+            # Get the covariance matrix, defined as inv_cov = a_transposed * a
+            err = numpy.zeros(2)
+            try:
+                inv_cov = numpy.dot(A.transpose(), A)
+                cov = numpy.linalg.pinv(inv_cov)
+                err_matrix = math.fabs(residuals) * cov
+                err = [math.sqrt(err_matrix[0][0]), math.sqrt(err_matrix[1][1])]
+            except:
+                err = [-1.0, -1.0]
+
+            return p, err
+
+
+class InvariantCalculator(object):
+    """
+    Compute invariant if data is given.
+    Can provide volume fraction and surface area if the user provides
+    Porod constant  and contrast values.
+
+    :precondition:  the user must send a data of type DataLoader.Data1D
+                    the user provide background and scale values.
+
+    :note: Some computations depends on each others.
+    """
+    def __init__(self, data, background=0, scale=1):
+        """
+        Initialize variables.
+
+        :param data: data must be of type DataLoader.Data1D
+        :param background: Background value. The data will be corrected
+            before processing
+        :param scale: Scaling factor for I(q). The data will be corrected
+            before processing
+        """
+        # Background and scale should be private data member if the only way to
+        # change them are by instantiating a new object.
+        self._background = background
+        self._scale = scale
+        # slit height for smeared data
+        self._smeared = None
+        # The data should be private
+        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:
+            # assumes constant dxl
+            self._smeared = self._data.dxl[0]
+
+        # Since there are multiple variants of Q*, you should force the
+        # user to use the get method and keep Q* a private data member
+        self._qstar = None
+
+        # You should keep the error on Q* so you can reuse it without
+        # recomputing the whole thing.
+        self._qstar_err = 0
+
+        # Extrapolation parameters
+        self._low_extrapolation_npts = 4
+        self._low_extrapolation_function = Guinier()
+        self._low_extrapolation_power = None
+        self._low_extrapolation_power_fitted = None
+
+        self._high_extrapolation_npts = 4
+        self._high_extrapolation_function = PowerLaw()
+        self._high_extrapolation_power = None
+        self._high_extrapolation_power_fitted = None
+
+        # Extrapolation range
+        self._low_q_limit = Q_MINIMUM
+
+    def _get_data(self, data):
+        """
+        :note: this function must be call before computing any type
+         of invariant
+
+        :return: new data = self._scale *data - self._background
+        """
+        if not issubclass(data.__class__, LoaderData1D):
+            #Process only data that inherited from DataLoader.Data_info.Data1D
+            raise ValueError, "Data must be of type DataLoader.Data1D"
+        #from copy import deepcopy
+        new_data = (self._scale * data) - self._background
+
+        # Check that the vector lengths are equal
+        assert len(new_data.x) == len(new_data.y)
+
+        # Verify that the errors are set correctly
+        if new_data.dy is None or len(new_data.x) != len(new_data.dy) or \
+            (min(new_data.dy) == 0 and max(new_data.dy) == 0):
+            new_data.dy = numpy.ones(len(new_data.x))
+        return  new_data
+
+    def _fit(self, model, qmin=Q_MINIMUM, qmax=Q_MAXIMUM, power=None):
+        """
+        fit data with function using
+        data = self._get_data()
+        fx = Functor(data , function)
+        y = data.y
+        slope, constant = linalg.lstsq(y,fx)
+
+        :param qmin: data first q value to consider during the fit
+        :param qmax: data last q value to consider during the fit
+        :param power : power value to consider for power-law
+        :param function: the function to use during the fit
+
+        :return a: the scale of the function
+        :return b: the other parameter of the function for guinier will be radius
+                for power_law will be the power value
+        """
+        extrapolator = Extrapolator(data=self._data, model=model)
+        p, dp = extrapolator.fit(power=power, qmin=qmin, qmax=qmax)
+
+        return model.extract_model_parameters(constant=p[1], slope=p[0],
+                                              dconstant=dp[1], dslope=dp[0])
+
+    def _get_qstar(self, data):
+        """
+        Compute invariant for pinhole data.
+        This invariant is given by: ::
+
+            q_star = x0**2 *y0 *dx0 +x1**2 *y1 *dx1
+                        + ..+ xn**2 *yn *dxn    for non smeared data
+
+            q_star = dxl0 *x0 *y0 *dx0 +dxl1 *x1 *y1 *dx1
+                        + ..+ dlxn *xn *yn *dxn    for smeared data
+
+            where n >= len(data.x)-1
+            dxl = slit height dQl
+            dxi = 1/2*(xi+1 - xi) + (xi - xi-1)
+            dx0 = (x1 - x0)/2
+            dxn = (xn - xn-1)/2
+
+        :param data: the data to use to compute invariant.
+
+        :return q_star: invariant value for pinhole data. q_star > 0
+        """
+        if len(data.x) <= 1 or len(data.y) <= 1 or len(data.x) != len(data.y):
+            msg = "Length x and y must be equal"
+            msg += " and greater than 1; got x=%s, y=%s" % (len(data.x), len(data.y))
+            raise ValueError, msg
+        else:
+            # Take care of smeared data
+            if self._smeared is None:
+                gx = data.x * data.x
+            # assumes that len(x) == len(dxl).
+            else:
+                gx = data.dxl * data.x
+
+            n = len(data.x) - 1
+            #compute the first delta q
+            dx0 = (data.x[1] - data.x[0]) / 2
+            #compute the last delta q
+            dxn = (data.x[n] - data.x[n - 1]) / 2
+            total = 0
+            total += gx[0] * data.y[0] * dx0
+            total += gx[n] * data.y[n] * dxn
+
+            if len(data.x) == 2:
+                return total
+            else:
+                #iterate between for element different
+                #from the first and the last
+                for i in xrange(1, n - 1):
+                    dxi = (data.x[i + 1] - data.x[i - 1]) / 2
+                    total += gx[i] * data.y[i] * dxi
+                return total
+
+    def _get_qstar_uncertainty(self, data):
+        """
+        Compute invariant uncertainty with with pinhole data.
+        This uncertainty is given as follow: ::
+
+           dq_star = math.sqrt[(x0**2*(dy0)*dx0)**2 +
+                (x1**2 *(dy1)*dx1)**2 + ..+ (xn**2 *(dyn)*dxn)**2 ]
+        where n >= len(data.x)-1
+        dxi = 1/2*(xi+1 - xi) + (xi - xi-1)
+        dx0 = (x1 - x0)/2
+        dxn = (xn - xn-1)/2
+        dyn: error on dy
+
+        :param data:
+        :note: if data doesn't contain dy assume dy= math.sqrt(data.y)
+        """
+        if len(data.x) <= 1 or len(data.y) <= 1 or \
+            len(data.x) != len(data.y) or \
+            (data.dy is not None and (len(data.dy) != len(data.y))):
+            msg = "Length of data.x and data.y must be equal"
+            msg += " and greater than 1; got x=%s, y=%s" % (len(data.x), len(data.y))
+            raise ValueError, msg
+        else:
+            #Create error for data without dy error
+            if data.dy is None:
+                dy = math.sqrt(data.y)
+            else:
+                dy = data.dy
+            # Take care of smeared data
+            if self._smeared is None:
+                gx = data.x * data.x
+            # assumes that len(x) == len(dxl).
+            else:
+                gx = data.dxl * data.x
+
+            n = len(data.x) - 1
+            #compute the first delta
+            dx0 = (data.x[1] - data.x[0]) / 2
+            #compute the last delta
+            dxn = (data.x[n] - data.x[n - 1]) / 2
+            total = 0
+            total += (gx[0] * dy[0] * dx0) ** 2
+            total += (gx[n] * dy[n] * dxn) ** 2
+            if len(data.x) == 2:
+                return math.sqrt(total)
+            else:
+                #iterate between for element different
+                #from the first and the last
+                for i in xrange(1, n - 1):
+                    dxi = (data.x[i + 1] - data.x[i - 1]) / 2
+                    total += (gx[i] * dy[i] * dxi) ** 2
+                return math.sqrt(total)
+
+    def _get_extrapolated_data(self, model, npts=INTEGRATION_NSTEPS,
+                               q_start=Q_MINIMUM, q_end=Q_MAXIMUM):
+        """
+        :return: extrapolate data create from data
+        """
+        #create new Data1D to compute the invariant
+        q = numpy.linspace(start=q_start,
+                           stop=q_end,
+                           num=npts,
+                           endpoint=True)
+        iq = model.evaluate_model(q)
+        diq = model.evaluate_model_errors(q)
+
+        result_data = LoaderData1D(x=q, y=iq, dy=diq)
+        if self._smeared != None:
+            result_data.dxl = self._smeared * numpy.ones(len(q))
+        return result_data
+
+    def get_data(self):
+        """
+        :return: self._data
+        """
+        return self._data
+
+    def get_extrapolation_power(self, range='high'):
+        """
+        :return: the fitted power for power law function for a given
+            extrapolation range
+        """
+        if range == 'low':
+            return self._low_extrapolation_power_fitted
+        return self._high_extrapolation_power_fitted
+
+    def get_qstar_low(self):
+        """
+        Compute the invariant for extrapolated data at low q range.
+
+        Implementation:
+            data = self._get_extra_data_low()
+            return self._get_qstar()
+
+        :return q_star: the invariant for data extrapolated at low q.
+        """
+        # Data boundaries for fitting
+        qmin = self._data.x[0]
+        qmax = self._data.x[self._low_extrapolation_npts - 1]
+
+        # Extrapolate the low-Q data
+        p, _ = self._fit(model=self._low_extrapolation_function,
+                         qmin=qmin,
+                         qmax=qmax,
+                         power=self._low_extrapolation_power)
+        self._low_extrapolation_power_fitted = p[0]
+
+        # Distribution starting point
+        self._low_q_limit = Q_MINIMUM
+        if Q_MINIMUM >= qmin:
+            self._low_q_limit = qmin / 10
+
+        data = self._get_extrapolated_data(\
+                                    model=self._low_extrapolation_function,
+                                    npts=INTEGRATION_NSTEPS,
+                                    q_start=self._low_q_limit, q_end=qmin)
+
+        # Systematic error
+        # If we have smearing, the shape of the I(q) distribution at low Q will
+        # may not be a Guinier or simple power law. The following is
+        # a conservative estimation for the systematic error.
+        err = qmin * qmin * math.fabs((qmin - self._low_q_limit) * \
+                                  (data.y[0] - data.y[INTEGRATION_NSTEPS - 1]))
+        return self._get_qstar(data), self._get_qstar_uncertainty(data) + err
+
+    def get_qstar_high(self):
+        """
+        Compute the invariant for extrapolated data at high q range.
+
+        Implementation:
+            data = self._get_extra_data_high()
+            return self._get_qstar()
+
+        :return q_star: the invariant for data extrapolated at high q.
+        """
+        # Data boundaries for fitting
+        x_len = len(self._data.x) - 1
+        qmin = self._data.x[x_len - (self._high_extrapolation_npts - 1)]
+        qmax = self._data.x[x_len]
+
+        # fit the data with a model to get the appropriate parameters
+        p, _ = self._fit(model=self._high_extrapolation_function,
+                         qmin=qmin,
+                         qmax=qmax,
+                         power=self._high_extrapolation_power)
+        self._high_extrapolation_power_fitted = p[0]
+
+        #create new Data1D to compute the invariant
+        data = self._get_extrapolated_data(\
+                                    model=self._high_extrapolation_function,
+                                    npts=INTEGRATION_NSTEPS,
+                                    q_start=qmax, q_end=Q_MAXIMUM)
+
+        return self._get_qstar(data), self._get_qstar_uncertainty(data)
+
+    def get_extra_data_low(self, npts_in=None, q_start=None, npts=20):
+        """
+        Returns the extrapolated data used for the loew-Q invariant calculation.
+        By default, the distribution will cover the data points used for the
+        extrapolation. The number of overlap points is a parameter (npts_in).
+        By default, the maximum q-value of the distribution will be
+        the minimum q-value used when extrapolating for the purpose of the
+        invariant calculation.
+
+        :param npts_in: number of data points for which
+            the extrapolated data overlap
+        :param q_start: is the minimum value to uses for extrapolated data
+        :param npts: the number of points in the extrapolated distribution
+
+        """
+        # Get extrapolation range
+        if q_start is None:
+            q_start = self._low_q_limit
+
+        if npts_in is None:
+            npts_in = self._low_extrapolation_npts
+        q_end = self._data.x[max(0, npts_in - 1)]
+
+        if q_start >= q_end:
+            return numpy.zeros(0), numpy.zeros(0)
+
+        return self._get_extrapolated_data(\
+                                    model=self._low_extrapolation_function,
+                                    npts=npts,
+                                    q_start=q_start, q_end=q_end)
+
+    def get_extra_data_high(self, npts_in=None, q_end=Q_MAXIMUM, npts=20):
+        """
+        Returns the extrapolated data used for the high-Q invariant calculation.
+        By default, the distribution will cover the data points used for the
+        extrapolation. The number of overlap points is a parameter (npts_in).
+        By default, the maximum q-value of the distribution will be Q_MAXIMUM,
+        the maximum q-value used when extrapolating for the purpose of the
+        invariant calculation.
+
+        :param npts_in: number of data points for which the
+            extrapolated data overlap
+        :param q_end: is the maximum value to uses for extrapolated data
+        :param npts: the number of points in the extrapolated distribution
+        """
+        # Get extrapolation range
+        if npts_in is None:
+            npts_in = self._high_extrapolation_npts
+        _npts = len(self._data.x)
+        q_start = self._data.x[min(_npts, _npts - npts_in)]
+
+        if q_start >= q_end:
+            return numpy.zeros(0), numpy.zeros(0)
+
+        return self._get_extrapolated_data(\
+                                model=self._high_extrapolation_function,
+                                npts=npts,
+                                q_start=q_start, q_end=q_end)
+
+    def set_extrapolation(self, range, npts=4, function=None, power=None):
+        """
+        Set the extrapolation parameters for the high or low Q-range.
+        Note that this does not turn extrapolation on or off.
+
+        :param range: a keyword set the type of extrapolation . type string
+        :param npts: the numbers of q points of data to consider
+            for extrapolation
+        :param function: a keyword to select the function to use
+            for extrapolation.
+            of type string.
+        :param power: an power to apply power_low function
+
+        """
+        range = range.lower()
+        if range not in ['high', 'low']:
+            raise ValueError, "Extrapolation range should be 'high' or 'low'"
+        function = function.lower()
+        if function not in ['power_law', 'guinier']:
+            msg = "Extrapolation function should be 'guinier' or 'power_law'"
+            raise ValueError, msg
+
+        if range == 'high':
+            if function != 'power_law':
+                msg = "Extrapolation only allows a power law at high Q"
+                raise ValueError, msg
+            self._high_extrapolation_npts = npts
+            self._high_extrapolation_power = power
+            self._high_extrapolation_power_fitted = power
+        else:
+            if function == 'power_law':
+                self._low_extrapolation_function = PowerLaw()
+            else:
+                self._low_extrapolation_function = Guinier()
+            self._low_extrapolation_npts = npts
+            self._low_extrapolation_power = power
+            self._low_extrapolation_power_fitted = power
+
+    def get_qstar(self, extrapolation=None):
+        """
+        Compute the invariant of the local copy of data.
+
+        :param extrapolation: string to apply optional extrapolation
+
+        :return q_star: invariant of the data within data's q range
+
+        :warning: When using setting data to Data1D ,
+            the user is responsible of
+            checking that the scale and the background are
+            properly apply to the data
+
+        """
+        self._qstar = self._get_qstar(self._data)
+        self._qstar_err = self._get_qstar_uncertainty(self._data)
+
+        if extrapolation is None:
+            return self._qstar
+
+        # Compute invariant plus invariant of extrapolated data
+        extrapolation = extrapolation.lower()
+        if extrapolation == "low":
+            qs_low, dqs_low = self.get_qstar_low()
+            qs_hi, dqs_hi = 0, 0
+
+        elif extrapolation == "high":
+            qs_low, dqs_low = 0, 0
+            qs_hi, dqs_hi = self.get_qstar_high()
+
+        elif extrapolation == "both":
+            qs_low, dqs_low = self.get_qstar_low()
+            qs_hi, dqs_hi = self.get_qstar_high()
+
+        self._qstar += qs_low + qs_hi
+        self._qstar_err = math.sqrt(self._qstar_err * self._qstar_err \
+                                    + dqs_low * dqs_low + dqs_hi * dqs_hi)
+
+        return self._qstar
+
+    def get_surface(self, contrast, porod_const, extrapolation=None):
+        """
+        Compute the specific surface from the data.
+
+        Implementation::
+
+          V =  self.get_volume_fraction(contrast, extrapolation)
+
+          Compute the surface given by:
+            surface = (2*pi *V(1- V)*porod_const)/ q_star
+
+        :param contrast: contrast value to compute the volume
+        :param porod_const: Porod constant to compute the surface
+        :param extrapolation: string to apply optional extrapolation
+
+        :return: specific surface
+        """
+        # Compute the volume
+        volume = self.get_volume_fraction(contrast, extrapolation)
+        return 2 * math.pi * volume * (1 - volume) * \
+            float(porod_const) / self._qstar
+
+    def get_volume_fraction(self, contrast, extrapolation=None):
+        """
+        Compute volume fraction is deduced as follow: ::
+
+            q_star = 2*(pi*contrast)**2* volume( 1- volume)
+            for k = 10^(-8)*q_star/(2*(pi*|contrast|)**2)
+            we get 2 values of volume:
+                 with   1 - 4 * k >= 0
+                 volume1 = (1- sqrt(1- 4*k))/2
+                 volume2 = (1+ sqrt(1- 4*k))/2
+
+            q_star: the invariant value included extrapolation is applied
+                         unit  1/A^(3)*1/cm
+                    q_star = self.get_qstar()
+
+            the result returned will be 0 <= volume <= 1
+
+        :param contrast: contrast value provides by the user of type float.
+                 contrast unit is 1/A^(2)= 10^(16)cm^(2)
+        :param extrapolation: string to apply optional extrapolation
+
+        :return: volume fraction
+
+        :note: volume fraction must have no unit
+        """
+        if contrast <= 0:
+            raise ValueError, "The contrast parameter must be greater than zero"
+
+        # Make sure Q star is up to date
+        self.get_qstar(extrapolation)
+
+        if self._qstar <= 0:
+            msg = "Invalid invariant: Invariant Q* must be greater than zero"
+            raise RuntimeError, msg
+
+        # Compute intermediate constant
+        k = 1.e-8 * self._qstar / (2 * (math.pi * math.fabs(float(contrast))) ** 2)
+        # Check discriminant value
+        discrim = 1 - 4 * k
+
+        # Compute volume fraction
+        if discrim < 0:
+            msg = "Could not compute the volume fraction: negative discriminant"
+            raise RuntimeError, msg
+        elif discrim == 0:
+            return 1 / 2
+        else:
+            volume1 = 0.5 * (1 - math.sqrt(discrim))
+            volume2 = 0.5 * (1 + math.sqrt(discrim))
+
+            if 0 <= volume1 and volume1 <= 1:
+                return volume1
+            elif 0 <= volume2 and volume2 <= 1:
+                return volume2
+            msg = "Could not compute the volume fraction: inconsistent results"
+            raise RuntimeError, msg
+
+    def get_qstar_with_error(self, extrapolation=None):
+        """
+        Compute the invariant uncertainty.
+        This uncertainty computation depends on whether or not the data is
+        smeared.
+
+        :param extrapolation: string to apply optional extrapolation
+
+        :return: invariant, the invariant uncertainty
+        """
+        self.get_qstar(extrapolation)
+        return self._qstar, self._qstar_err
+
+    def get_volume_fraction_with_error(self, contrast, extrapolation=None):
+        """
+        Compute uncertainty on volume value as well as the volume fraction
+        This uncertainty is given by the following equation: ::
+
+            dV = 0.5 * (4*k* dq_star) /(2* math.sqrt(1-k* q_star))
+
+            for k = 10^(-8)*q_star/(2*(pi*|contrast|)**2)
+
+            q_star: the invariant value including extrapolated value if existing
+            dq_star: the invariant uncertainty
+            dV: the volume uncertainty
+
+        The uncertainty will be set to -1 if it can't be computed.
+
+        :param contrast: contrast value
+        :param extrapolation: string to apply optional extrapolation
+
+        :return: V, dV = volume fraction, error on volume fraction
+        """
+        volume = self.get_volume_fraction(contrast, extrapolation)
+
+        # Compute error
+        k = 1.e-8 * self._qstar / (2 * (math.pi * math.fabs(float(contrast))) ** 2)
+        # Check value inside the sqrt function
+        value = 1 - k * self._qstar
+        if (value) <= 0:
+            uncertainty = -1
+        # Compute uncertainty
+        uncertainty = math.fabs((0.5 * 4 * k * \
+                        self._qstar_err) / (2 * math.sqrt(1 - k * self._qstar)))
+
+        return volume, uncertainty
+
+    def get_surface_with_error(self, contrast, porod_const, extrapolation=None):
+        """
+        Compute uncertainty of the surface value as well as the surface value.
+        The uncertainty is given as follow: ::
+
+            dS = porod_const *2*pi[( dV -2*V*dV)/q_star
+                 + dq_star(v-v**2)
+
+            q_star: the invariant value
+            dq_star: the invariant uncertainty
+            V: the volume fraction value
+            dV: the volume uncertainty
+
+        :param contrast: contrast value
+        :param porod_const: porod constant value
+        :param extrapolation: string to apply optional extrapolation
+
+        :return S, dS: the surface, with its uncertainty
+        """
+        # We get the volume fraction, with error
+        #   get_volume_fraction_with_error calls get_volume_fraction
+        #   get_volume_fraction calls get_qstar
+        #   which computes Qstar and dQstar
+        v, dv = self.get_volume_fraction_with_error(contrast, extrapolation)
+
+        s = self.get_surface(contrast=contrast, porod_const=porod_const,
+                             extrapolation=extrapolation)
+        ds = porod_const * 2 * math.pi * ((dv - 2 * v * dv) / self._qstar\
+                 + self._qstar_err * (v - v ** 2))
+
+        return s, ds
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 b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,48 @@
+"""
+This module is a wrapper to a map function. It allows to loop through 
+different invariant objects to call the same function
+"""
+
+
+def get_qstar(inv, extrapolation=None):
+    """
+    Get invariant value (Q*)
+    """
+    return inv.get_qstar(extrapolation)
+
+def get_qstar_with_error(inv, extrapolation=None):
+    """
+    Get invariant value with uncertainty
+    """
+    return inv.get_qstar_with_error(extrapolation)
+
+def get_volume_fraction(inv, contrast, extrapolation=None):
+    """
+    Get volume fraction
+    """
+    return inv.get_volume_fraction(contrast, extrapolation)
+
+def get_volume_fraction_with_error(inv, contrast, extrapolation=None):
+    """
+    Get volume fraction with uncertainty
+    """
+    return inv.get_volume_fraction_with_error(contrast,
+                                                    extrapolation)
+
+def get_surface(inv, contrast, porod_const, extrapolation=None):
+    """
+    Get surface with uncertainty
+    """
+    return inv.get_surface(contrast=contrast,
+                                      porod_const=porod_const,
+                                      extrapolation=extrapolation)
+
+def get_surface_with_error(inv, contrast, 
+                           porod_const, extrapolation=None):
+    """
+    Get surface with uncertainty
+    """
+    return inv.get_surface_with_error(contrast=contrast,
+                                      porod_const=porod_const,
+                                      extrapolation=extrapolation)
+
Index: src/sas/sascalc/pr/Doxyfile
===================================================================
--- src/sas/sascalc/pr/Doxyfile	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/Doxyfile	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,263 @@
+# Doxyfile 1.5.1-p1
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME           = saspr
+PROJECT_NUMBER         = 
+OUTPUT_DIRECTORY       = doc
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+USE_WINDOWS_ENCODING   = NO
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = 
+STRIP_FROM_INC_PATH    = 
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 4
+ALIASES                = 
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = YES
+BUILTIN_STL_SUPPORT    = NO
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = NO
+EXTRACT_PRIVATE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = YES
+FILE_VERSION_FILTER    = 
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = .
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.d \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.idl \
+                         *.odl \
+                         *.cs \
+                         *.php \
+                         *.php3 \
+                         *.inc \
+                         *.m \
+                         *.mm \
+                         *.dox \
+                         *.py
+RECURSIVE              = YES
+EXCLUDE                = build \
+                         dist \
+                         test
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = *
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = doc
+INPUT_FILTER           = 
+FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = NO
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = NO
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             = 
+XML_DTD                = 
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = NO
+GROUP_GRAPHS           = YES
+UML_LOOK               = YES
+TEMPLATE_RELATIONS     = YES
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = YES
+CALLER_GRAPH           = YES
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = 
+DOTFILE_DIRS           = 
+MAX_DOT_GRAPH_WIDTH    = 1024
+MAX_DOT_GRAPH_HEIGHT   = 1024
+MAX_DOT_GRAPH_DEPTH    = 1000
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
Index: src/sas/sascalc/pr/__init__.py
===================================================================
--- src/sas/sascalc/pr/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,106 @@
+"""
+    P(r) inversion for SAS
+"""
+## \mainpage P(r) inversion for SAS
+#
+# \section intro_sec Introduction
+# This module provides calculations to transform scattering intensity data 
+# I(q) into distance distribution function P(r). A description of the 
+# technique can be found elsewhere [1-5]. The module is useable as a 
+# standalone application but its functionality is meant to be presented 
+# to end-users through the user interface developed as part of the SAS 
+# flagship application. 
+#
+# Procedure: We will follow the procedure of Moore [1].
+# 
+# [1] P.B. Moore, J.Appl. Cryst (1980) 13, 168-175.
+#
+# [2] O. Glatter, J.Appl. Cryst (1977) 10, 415-421.
+#
+# [3] D.I. Svergun, J.Appl. Cryst (1991) 24, 485-492.
+#
+# [4] D.I. Svergun, J.Appl. Cryst (1992) 25, 495-503.
+#
+# [5] S. Hansen and J. Skov Pedersen, J.Appl. Cryst (1991) 24, 541-548.
+#
+## \subsection class Class Diagram:
+# The following shows a partial class diagram with the main attributes
+# and methods of the invertor.
+#
+# \image html architecture.png
+#
+# \section install_sec Installation
+#
+# \subsection obtain Obtaining the Code
+#
+# The code is available here:
+# \verbatim
+#$ svn co svn://danse.us/sas/pr_inversion
+# \endverbatim
+#
+# \subsection depends External Dependencies
+# scipy, numpy
+#
+# \subsection build Building the code
+# The standard python package can be built with distutils.
+# \verbatim
+#$ python setup.py build
+#$ python setup.py install
+# \endverbatim
+#
+#
+# \subsection Tutorial
+# To create an inversion object:
+# \verbatim
+#from sas.sascalc.pr.invertor import Invertor
+#    invertor = Invertor()
+# \endverbatim
+#
+# To set the maximum distance between any two points:
+# \verbatim
+#    invertor.d_max = 160.0
+# \endverbatim
+#
+# To set the regularization constant:
+# \verbatim
+#    invertor.alpha = 0.0007
+# \endverbatim
+#
+# To set the q, I(q) and error on I(q):
+# \verbatim
+#    invertor.x = q_vector
+#    invertor.y = Iq_vector
+#    invertor.err = dIq_vector
+# \endverbatim
+#
+# To perform the inversion. In this example, we choose
+# a P(r) expension wit 10 base functions.
+# \verbatim
+#    c_out, c_cov = invertor.invert(10)
+# \endverbatim
+# The c_out and c_cov are the set of coefficients and the covariance 
+# matrix for those coefficients, respectively.
+#
+# To get P(r):
+# \verbatim
+#    r = 10.0
+#    pr = invertor.pr(c_out, r)
+# \endverbatim
+# Alternatively, one can get P(r) with the error on P(r):
+# \verbatim
+#    r = 10.0
+#    pr, dpr = invertor.pr_err(c_out, c_cov, r)
+# \endverbatim
+#
+# To get the output I(q) from the set of coefficients found:
+# \verbatim
+#    q = 0.001
+#    iq = invertor.iq(c_out, q)
+# \endverbatim
+#
+# Examples are available as unit tests under sas.pr_inversion.test.
+#
+# \section help_sec Contact Info
+# Code and Documentation produced as part of the DANSE project.
+
+__author__ = 'University of Tennessee'
Index: src/sas/sascalc/pr/c_extensions/Cinvertor.c
===================================================================
--- src/sas/sascalc/pr/c_extensions/Cinvertor.c	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/c_extensions/Cinvertor.c	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1133 @@
+/**
+ * C implementation of the P(r) inversion
+ * Cinvertor is the base class for the Invertor class
+ * and provides the underlying computations.
+ *
+ */
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "invertor.h"
+
+
+/// Error object for raised exceptions
+PyObject * CinvertorError;
+
+#define INVECTOR(obj,buf,len)										\
+    do { \
+        int err = PyObject_AsReadBuffer(obj, (const void **)(&buf), &len); \
+        if (err < 0) return NULL; \
+        len /= sizeof(*buf); \
+    } while (0)
+
+#define OUTVECTOR(obj,buf,len) \
+    do { \
+        int err = PyObject_AsWriteBuffer(obj, (void **)(&buf), &len); \
+        if (err < 0) return NULL; \
+        len /= sizeof(*buf); \
+    } while (0)
+
+
+// Class definition
+/**
+ * C implementation of the P(r) inversion
+ * Cinvertor is the base class for the Invertor class
+ * and provides the underlying computations.
+ *
+ */
+typedef struct {
+    PyObject_HEAD
+    /// Internal data structure
+    Invertor_params params;
+} Cinvertor;
+
+
+static void
+Cinvertor_dealloc(Cinvertor* self)
+{
+    invertor_dealloc(&(self->params));
+
+    self->ob_type->tp_free((PyObject*)self);
+
+}
+
+static PyObject *
+Cinvertor_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    Cinvertor *self;
+
+    self = (Cinvertor *)type->tp_alloc(type, 0);
+
+    return (PyObject *)self;
+}
+
+static int
+Cinvertor_init(Cinvertor *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	// Create parameters
+    	invertor_init(&(self->params));
+    }
+    return 0;
+}
+
+static PyMemberDef Cinvertor_members[] = {
+    //{"params", T_OBJECT, offsetof(Cinvertor, params), 0,
+    // "Parameters"},
+    {NULL}  /* Sentinel */
+};
+
+const char set_x_doc[] =
+	"Function to set the x data\n"
+	"Takes an array of doubles as input.\n"
+	" @return: number of entries found";
+
+/**
+ * Function to set the x data
+ * Takes an array of doubles as input
+ * Returns the number of entries found
+ */
+static PyObject * set_x(Cinvertor *self, PyObject *args) {
+	PyObject *data_obj;
+	Py_ssize_t ndata;
+	double *data;
+	int i;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj,data,ndata);
+
+	free(self->params.x);
+	self->params.x = (double*) malloc(ndata*sizeof(double));
+
+	if(self->params.x==NULL) {
+	    PyErr_SetString(CinvertorError,
+	    	"Cinvertor.set_x: problem allocating memory.");
+		return NULL;
+	}
+
+	for (i=0; i<ndata; i++) {
+		self->params.x[i] = data[i];
+	}
+
+	//self->params.x = data;
+	self->params.npoints = ndata;
+	return Py_BuildValue("i", self->params.npoints);
+}
+
+const char get_x_doc[] =
+	"Function to get the x data\n"
+	"Takes an array of doubles as input.\n"
+	" @return: number of entries found";
+
+static PyObject * get_x(Cinvertor *self, PyObject *args) {
+	PyObject *data_obj;
+	Py_ssize_t ndata;
+	double *data;
+    int i;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj, data, ndata);
+
+	// Check that the input array is large enough
+	if (ndata < self->params.npoints) {
+	    PyErr_SetString(CinvertorError,
+	    	"Cinvertor.get_x: input array too short for data.");
+		return NULL;
+	}
+
+	for(i=0; i<self->params.npoints; i++){
+		data[i] = self->params.x[i];
+	}
+
+	return Py_BuildValue("i", self->params.npoints);
+}
+
+const char set_y_doc[] =
+	"Function to set the y data\n"
+	"Takes an array of doubles as input.\n"
+	" @return: number of entries found";
+
+/**
+ * Function to set the y data
+ * Takes an array of doubles as input
+ * Returns the number of entries found
+ */
+static PyObject * set_y(Cinvertor *self, PyObject *args) {
+	PyObject *data_obj;
+	Py_ssize_t ndata;
+	double *data;
+	int i;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj,data,ndata);
+
+	free(self->params.y);
+	self->params.y = (double*) malloc(ndata*sizeof(double));
+
+	if(self->params.y==NULL) {
+	    PyErr_SetString(CinvertorError,
+	    	"Cinvertor.set_y: problem allocating memory.");
+		return NULL;
+	}
+
+	for (i=0; i<ndata; i++) {
+		self->params.y[i] = data[i];
+	}
+
+	//self->params.y = data;
+	self->params.ny = ndata;
+	return Py_BuildValue("i", self->params.ny);
+}
+
+const char get_y_doc[] =
+	"Function to get the y data\n"
+	"Takes an array of doubles as input.\n"
+	" @return: number of entries found";
+
+static PyObject * get_y(Cinvertor *self, PyObject *args) {
+	PyObject *data_obj;
+	Py_ssize_t ndata;
+	double *data;
+    int i;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj, data, ndata);
+
+	// Check that the input array is large enough
+	if (ndata < self->params.ny) {
+	    PyErr_SetString(CinvertorError,
+	    	"Cinvertor.get_y: input array too short for data.");
+		return NULL;
+	}
+
+	for(i=0; i<self->params.ny; i++){
+		data[i] = self->params.y[i];
+	}
+
+	return Py_BuildValue("i", self->params.npoints);
+}
+
+const char set_err_doc[] =
+	"Function to set the err data\n"
+	"Takes an array of doubles as input.\n"
+	" @return: number of entries found";
+
+/**
+ * Function to set the x data
+ * Takes an array of doubles as input
+ * Returns the number of entries found
+ */
+static PyObject * set_err(Cinvertor *self, PyObject *args) {
+	PyObject *data_obj;
+	Py_ssize_t ndata;
+	double *data;
+	int i;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj,data,ndata);
+
+	free(self->params.err);
+	self->params.err = (double*) malloc(ndata*sizeof(double));
+
+	if(self->params.err==NULL) {
+	    PyErr_SetString(CinvertorError,
+	    	"Cinvertor.set_err: problem allocating memory.");
+		return NULL;
+	}
+
+	for (i=0; i<ndata; i++) {
+		self->params.err[i] = data[i];
+	}
+
+	//self->params.err = data;
+	self->params.nerr = ndata;
+	return Py_BuildValue("i", self->params.nerr);
+}
+
+const char get_err_doc[] =
+	"Function to get the err data\n"
+	"Takes an array of doubles as input.\n"
+	" @return: number of entries found";
+
+static PyObject * get_err(Cinvertor *self, PyObject *args) {
+	PyObject *data_obj;
+	Py_ssize_t ndata;
+	double *data;
+    int i;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj, data, ndata);
+
+	// Check that the input array is large enough
+	if (ndata < self->params.nerr) {
+	    PyErr_SetString(CinvertorError,
+	    	"Cinvertor.get_err: input array too short for data.");
+		return NULL;
+	}
+
+	for(i=0; i<self->params.nerr; i++){
+		data[i] = self->params.err[i];
+	}
+
+	return Py_BuildValue("i", self->params.npoints);
+}
+
+const char is_valid_doc[] =
+	"Check the validity of the stored data\n"
+	" @return: Returns the number of points if it's all good, -1 otherwise";
+
+/**
+ * Check the validity of the stored data
+ * Returns the number of points if it's all good, -1 otherwise
+ */
+static PyObject * is_valid(Cinvertor *self, PyObject *args) {
+	if(self->params.npoints==self->params.ny &&
+			self->params.npoints==self->params.nerr) {
+		return Py_BuildValue("i", self->params.npoints);
+	} else {
+		return Py_BuildValue("i", -1);
+	}
+}
+
+const char set_has_bck_doc[] =
+	"Sets background flag\n";
+
+/**
+ * Sets the maximum distance
+ */
+static PyObject * set_has_bck(Cinvertor *self, PyObject *args) {
+	int has_bck;
+
+	if (!PyArg_ParseTuple(args, "i", &has_bck)) return NULL;
+	self->params.has_bck = has_bck;
+	return Py_BuildValue("i", self->params.has_bck);
+}
+
+const char get_has_bck_doc[] =
+	"Gets background flag\n";
+
+/**
+ * Gets the maximum distance
+ */
+static PyObject * get_has_bck(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("i", self->params.has_bck);
+}
+
+const char set_dmax_doc[] =
+	"Sets the maximum distance\n";
+
+/**
+ * Sets the maximum distance
+ */
+static PyObject * set_dmax(Cinvertor *self, PyObject *args) {
+	double d_max;
+
+	if (!PyArg_ParseTuple(args, "d", &d_max)) return NULL;
+	self->params.d_max = d_max;
+	return Py_BuildValue("d", self->params.d_max);
+}
+
+const char get_dmax_doc[] =
+	"Gets the maximum distance\n";
+
+/**
+ * Gets the maximum distance
+ */
+static PyObject * get_dmax(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("d", self->params.d_max);
+}
+
+const char set_slit_height_doc[] =
+	"Sets the slit height in units of q [A-1]\n";
+
+/**
+ * Sets the slit height
+ */
+static PyObject * set_slit_height(Cinvertor *self, PyObject *args) {
+	double slit_height;
+
+	if (!PyArg_ParseTuple(args, "d", &slit_height)) return NULL;
+	self->params.slit_height = slit_height;
+	return Py_BuildValue("d", self->params.slit_height);
+}
+
+const char get_slit_height_doc[] =
+	"Gets the slit height\n";
+
+/**
+ * Gets the slit height
+ */
+static PyObject * get_slit_height(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("d", self->params.slit_height);
+}
+
+const char set_slit_width_doc[] =
+	"Sets the slit width in units of q [A-1]\n";
+
+/**
+ * Sets the slit width
+ */
+static PyObject * set_slit_width(Cinvertor *self, PyObject *args) {
+	double slit_width;
+
+	if (!PyArg_ParseTuple(args, "d", &slit_width)) return NULL;
+	self->params.slit_width = slit_width;
+	return Py_BuildValue("d", self->params.slit_width);
+}
+
+const char get_slit_width_doc[] =
+	"Gets the slit width\n";
+
+/**
+ * Gets the slit width
+ */
+static PyObject * get_slit_width(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("d", self->params.slit_width);
+}
+
+
+const char set_qmin_doc[] =
+	"Sets the minimum q\n";
+
+/**
+ * Sets the minimum q
+ */
+static PyObject * set_qmin(Cinvertor *self, PyObject *args) {
+	double q_min;
+
+	if (!PyArg_ParseTuple(args, "d", &q_min)) return NULL;
+	self->params.q_min = q_min;
+	return Py_BuildValue("d", self->params.q_min);
+}
+
+const char get_qmin_doc[] =
+	"Gets the minimum q\n";
+
+/**
+ * Gets the minimum q
+ */
+static PyObject * get_qmin(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("d", self->params.q_min);
+}
+
+const char set_qmax_doc[] =
+	"Sets the maximum q\n";
+
+/**
+ * Sets the maximum q
+ */
+static PyObject * set_qmax(Cinvertor *self, PyObject *args) {
+	double q_max;
+
+	if (!PyArg_ParseTuple(args, "d", &q_max)) return NULL;
+	self->params.q_max = q_max;
+	return Py_BuildValue("d", self->params.q_max);
+}
+
+const char get_qmax_doc[] =
+	"Gets the maximum q\n";
+
+/**
+ * Gets the maximum q
+ */
+static PyObject * get_qmax(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("d", self->params.q_max);
+}
+
+const char set_alpha_doc[] =
+	"Sets the alpha parameter\n";
+
+static PyObject * set_alpha(Cinvertor *self, PyObject *args) {
+	double alpha;
+
+	if (!PyArg_ParseTuple(args, "d", &alpha)) return NULL;
+	self->params.alpha = alpha;
+	return Py_BuildValue("d", self->params.alpha);
+}
+
+const char get_alpha_doc[] =
+	"Gets the alpha parameter\n";
+
+/**
+ * Gets the maximum distance
+ */
+static PyObject * get_alpha(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("d", self->params.alpha);
+}
+
+const char get_nx_doc[] =
+	"Gets the number of x points\n";
+
+/**
+ * Gets the number of x points
+ */
+static PyObject * get_nx(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("i", self->params.npoints);
+}
+
+const char get_ny_doc[] =
+	"Gets the number of y points\n";
+
+/**
+ * Gets the number of y points
+ */
+static PyObject * get_ny(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("i", self->params.ny);
+}
+
+const char get_nerr_doc[] =
+	"Gets the number of err points\n";
+
+/**
+ * Gets the number of error points
+ */
+static PyObject * get_nerr(Cinvertor *self, PyObject *args) {
+	return Py_BuildValue("i", self->params.nerr);
+}
+
+
+const char residuals_doc[] =
+	"Function to call to evaluate the residuals\n"
+	"for P(r) inversion\n"
+	" @param args: input parameters\n"
+	" @return: list of residuals";
+
+/**
+ * Function to call to evaluate the residuals
+ * @param args: input parameters
+ * @return: list of residuals
+ */
+static PyObject * residuals(Cinvertor *self, PyObject *args) {
+	double *pars;
+	PyObject* residuals;
+	int i;
+	double residual, diff;
+	// Regularization factor
+	double regterm = 0.0;
+	// Number of slices in regularization term estimate
+	int nslice = 25;
+
+	PyObject *data_obj;
+	Py_ssize_t npars;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+
+	OUTVECTOR(data_obj,pars,npars);
+
+    // PyList of residuals
+	// Should create this list only once and refill it
+    residuals = PyList_New(self->params.npoints);
+
+    regterm = reg_term(pars, self->params.d_max, npars, nslice);
+
+    for(i=0; i<self->params.npoints; i++) {
+    	diff = self->params.y[i] - iq(pars, self->params.d_max, npars, self->params.x[i]);
+    	residual = diff*diff / (self->params.err[i]*self->params.err[i]);
+
+    	// regularization term
+    	residual += self->params.alpha * regterm;
+
+    	if (PyList_SetItem(residuals, i, Py_BuildValue("d",residual) ) < 0){
+    	    PyErr_SetString(CinvertorError,
+    	    	"Cinvertor.residuals: error setting residual.");
+    		return NULL;
+    	};
+    }
+	return residuals;
+}
+
+const char pr_residuals_doc[] =
+	"Function to call to evaluate the residuals\n"
+	"for P(r) minimization (for testing purposes)\n"
+	" @param args: input parameters\n"
+	" @return: list of residuals";
+
+/**
+ * Function to call to evaluate the residuals
+ * for P(r) minimization (for testing purposes)
+ * @param args: input parameters
+ * @return: list of residuals
+ */
+static PyObject * pr_residuals(Cinvertor *self, PyObject *args) {
+	double *pars;
+	PyObject* residuals;
+	int i;
+	double residual, diff;
+	// Regularization factor
+	double regterm = 0.0;
+	// Number of slices in regularization term estimate
+	int nslice = 25;
+
+	PyObject *data_obj;
+	Py_ssize_t npars;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+
+	OUTVECTOR(data_obj,pars,npars);
+
+	// Should create this list only once and refill it
+    residuals = PyList_New(self->params.npoints);
+
+    regterm = reg_term(pars, self->params.d_max, npars, nslice);
+
+
+    for(i=0; i<self->params.npoints; i++) {
+    	diff = self->params.y[i] - pr(pars, self->params.d_max, npars, self->params.x[i]);
+    	residual = diff*diff / (self->params.err[i]*self->params.err[i]);
+
+    	// regularization term
+    	residual += self->params.alpha * regterm;
+
+    	if (PyList_SetItem(residuals, i, Py_BuildValue("d",residual) ) < 0){
+    	    PyErr_SetString(CinvertorError,
+    	    	"Cinvertor.residuals: error setting residual.");
+    		return NULL;
+    	};
+    }
+	return residuals;
+}
+
+const char get_iq_doc[] =
+	"Function to call to evaluate the scattering intensity\n"
+	" @param args: c-parameters, and q\n"
+	" @return: I(q)";
+
+/**
+ * Function to call to evaluate the scattering intensity
+ * @param args: c-parameters, and q
+ * @return: I(q)
+ */
+static PyObject * get_iq(Cinvertor *self, PyObject *args) {
+	double *pars;
+	double q, iq_value;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+
+	if (!PyArg_ParseTuple(args, "Od", &data_obj, &q)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	iq_value = iq(pars, self->params.d_max, npars, q);
+	return Py_BuildValue("f", iq_value);
+}
+
+const char get_iq_smeared_doc[] =
+	"Function to call to evaluate the scattering intensity.\n"
+	"The scattering intensity is slit-smeared."
+	" @param args: c-parameters, and q\n"
+	" @return: I(q)";
+
+/**
+ * Function to call to evaluate the scattering intensity
+ * The scattering intensity is slit-smeared.
+ * @param args: c-parameters, and q
+ * @return: I(q)
+ */
+static PyObject * get_iq_smeared(Cinvertor *self, PyObject *args) {
+	double *pars;
+	double q, iq_value;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+
+	if (!PyArg_ParseTuple(args, "Od", &data_obj, &q)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	iq_value = iq_smeared(pars, self->params.d_max, npars,
+							self->params.slit_height, self->params.slit_width,
+							q, 21);
+	return Py_BuildValue("f", iq_value);
+}
+
+const char get_pr_doc[] =
+	"Function to call to evaluate P(r)\n"
+	" @param args: c-parameters and r\n"
+	" @return: P(r)";
+
+/**
+ * Function to call to evaluate P(r)
+ * @param args: c-parameters and r
+ * @return: P(r)
+ */
+static PyObject * get_pr(Cinvertor *self, PyObject *args) {
+	double *pars;
+	double r, pr_value;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+
+	if (!PyArg_ParseTuple(args, "Od", &data_obj, &r)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	pr_value = pr(pars, self->params.d_max, npars, r);
+	return Py_BuildValue("f", pr_value);
+}
+
+const char get_pr_err_doc[] =
+	"Function to call to evaluate P(r) with errors\n"
+	" @param args: c-parameters and r\n"
+	" @return: (P(r),dP(r))";
+
+/**
+ * Function to call to evaluate P(r) with errors
+ * @param args: c-parameters and r
+ * @return: P(r)
+ */
+static PyObject * get_pr_err(Cinvertor *self, PyObject *args) {
+	double *pars;
+	double *pars_err;
+	double pr_err_value;
+	double r, pr_value;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+	PyObject *err_obj;
+	Py_ssize_t npars2;
+
+	if (!PyArg_ParseTuple(args, "OOd", &data_obj, &err_obj, &r)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	if (err_obj == Py_None) {
+		pr_value = pr(pars, self->params.d_max, npars, r);
+		pr_err_value = 0.0;
+	} else {
+		OUTVECTOR(err_obj,pars_err,npars2);
+		pr_err(pars, pars_err, self->params.d_max, npars, r, &pr_value, &pr_err_value);
+	}
+	return Py_BuildValue("ff", pr_value, pr_err_value);
+}
+
+const char basefunc_ft_doc[] =
+	"Returns the value of the nth Fourier transofrmed base function\n"
+	" @param args: c-parameters, n and q\n"
+	" @return: nth Fourier transformed base function, evaluated at q";
+
+static PyObject * basefunc_ft(Cinvertor *self, PyObject *args) {
+	double d_max, q;
+	int n;
+
+	if (!PyArg_ParseTuple(args, "did", &d_max, &n, &q)) return NULL;
+	return Py_BuildValue("f", ortho_transformed(d_max, n, q));
+
+}
+
+const char oscillations_doc[] =
+	"Returns the value of the oscillation figure of merit for\n"
+	"the given set of coefficients. For a sphere, the oscillation\n"
+	"figure of merit is 1.1.\n"
+	" @param args: c-parameters\n"
+	" @return: oscillation figure of merit";
+
+static PyObject * oscillations(Cinvertor *self, PyObject *args) {
+	double *pars;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+	double oscill, norm;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	oscill = reg_term(pars, self->params.d_max, npars, 100);
+	norm   = int_p2(pars, self->params.d_max, npars, 100);
+	return Py_BuildValue("f", sqrt(oscill/norm)/acos(-1.0)*self->params.d_max );
+
+}
+
+const char get_peaks_doc[] =
+	"Returns the number of peaks in the output P(r) distrubution\n"
+	"for the given set of coefficients.\n"
+	" @param args: c-parameters\n"
+	" @return: number of P(r) peaks";
+
+static PyObject * get_peaks(Cinvertor *self, PyObject *args) {
+	double *pars;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+	int count;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	count = npeaks(pars, self->params.d_max, npars, 100);
+
+	return Py_BuildValue("i", count );
+
+}
+
+const char get_positive_doc[] =
+	"Returns the fraction of P(r) that is positive over\n"
+	"the full range of r for the given set of coefficients.\n"
+	" @param args: c-parameters\n"
+	" @return: fraction of P(r) that is positive";
+
+static PyObject * get_positive(Cinvertor *self, PyObject *args) {
+	double *pars;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+	double fraction;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	fraction = positive_integral(pars, self->params.d_max, npars, 100);
+
+	return Py_BuildValue("f", fraction );
+
+}
+
+const char get_pos_err_doc[] =
+	"Returns the fraction of P(r) that is 1 standard deviation\n"
+	"above zero over the full range of r for the given set of coefficients.\n"
+	" @param args: c-parameters\n"
+	" @return: fraction of P(r) that is positive";
+
+static PyObject * get_pos_err(Cinvertor *self, PyObject *args) {
+	double *pars;
+	double *pars_err;
+	PyObject *data_obj;
+	PyObject *err_obj;
+	Py_ssize_t npars;
+	Py_ssize_t npars2;
+	double fraction;
+
+	if (!PyArg_ParseTuple(args, "OO", &data_obj, &err_obj)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+	OUTVECTOR(err_obj,pars_err,npars2);
+
+	fraction = positive_errors(pars, pars_err, self->params.d_max, npars, 51);
+
+	return Py_BuildValue("f", fraction );
+
+}
+
+const char get_rg_doc[] =
+	"Returns the value of the radius of gyration Rg.\n"
+	" @param args: c-parameters\n"
+	" @return: Rg";
+
+static PyObject * get_rg(Cinvertor *self, PyObject *args) {
+	double *pars;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+	double value;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	value = rg(pars, self->params.d_max, npars, 101);
+
+	return Py_BuildValue("f", value );
+
+}
+
+const char get_iq0_doc[] =
+	"Returns the value of I(q=0).\n"
+	" @param args: c-parameters\n"
+	" @return: I(q=0)";
+
+static PyObject * get_iq0(Cinvertor *self, PyObject *args) {
+	double *pars;
+	PyObject *data_obj;
+	Py_ssize_t npars;
+	double value;
+
+	if (!PyArg_ParseTuple(args, "O", &data_obj)) return NULL;
+	OUTVECTOR(data_obj,pars,npars);
+
+	value = 4.0*acos(-1.0)*int_pr(pars, self->params.d_max, npars, 101);
+
+	return Py_BuildValue("f", value );
+
+}
+
+/**
+ * Check whether a q-value is within acceptabel limits
+ * Return 1 if accepted, 0 if rejected.
+ */
+int accept_q(Cinvertor *self, double q) {
+    if (self->params.q_min>0 && q<self->params.q_min) return 0;
+    if (self->params.q_max>0 && q>self->params.q_max) return 0;
+    return 1;
+}
+
+const char get_matrix_doc[] =
+	"Returns A matrix and b vector for least square problem.\n"
+	" @param nfunc: number of base functions\n"
+	" @param nr: number of r-points used when evaluating reg term.\n"
+	" @param a: A array to fill\n"
+	" @param b: b vector to fill\n"
+	" @return: 0";
+
+static PyObject * get_matrix(Cinvertor *self, PyObject *args) {
+	double *a;
+	double *b;
+	PyObject *a_obj;
+	PyObject *b_obj;
+	Py_ssize_t n_a;
+	Py_ssize_t n_b;
+	// Number of bins for regularization term evaluation
+	int nr, nfunc;
+	int i, j, i_r;
+	double r, sqrt_alpha, pi;
+	double tmp;
+	int offset;
+
+	if (!PyArg_ParseTuple(args, "iiOO", &nfunc, &nr, &a_obj, &b_obj)) return NULL;
+	OUTVECTOR(a_obj,a,n_a);
+	OUTVECTOR(b_obj,b,n_b);
+
+	assert(n_b>=nfunc);
+	assert(n_a>=nfunc*(nr+self->params.npoints));
+
+	sqrt_alpha = sqrt(self->params.alpha);
+	pi = acos(-1.0);
+	offset = (self->params.has_bck==1) ? 0 : 1;
+
+    for (j=0; j<nfunc; j++) {
+        for (i=0; i<self->params.npoints; i++) {
+            if (self->params.err[i]==0.0) {
+              PyErr_SetString(CinvertorError,
+                "Cinvertor.get_matrix: Some I(Q) points have no error.");
+              return NULL;
+            }
+            if (accept_q(self, self->params.x[i])){
+                if (self->params.has_bck==1 && j==0) {
+                    a[i*nfunc+j] = 1.0/self->params.err[i];
+                } else {
+                	if (self->params.slit_width>0 || self->params.slit_height>0) {
+                		a[i*nfunc+j] = ortho_transformed_smeared(self->params.d_max,
+                				j+offset, self->params.slit_height, self->params.slit_width,
+                				self->params.x[i], 21)/self->params.err[i];
+                	} else {
+                		a[i*nfunc+j] = ortho_transformed(self->params.d_max, j+offset, self->params.x[i])/self->params.err[i];
+                	}
+            	}
+            }
+        }
+        for (i_r=0; i_r<nr; i_r++){
+            if (self->params.has_bck==1 && j==0) {
+                a[(i_r+self->params.npoints)*nfunc+j] = 0.0;
+            } else {
+	            r = self->params.d_max/nr*i_r;
+	            tmp = pi*(j+offset)/self->params.d_max;
+	            a[(i_r+self->params.npoints)*nfunc+j] = sqrt_alpha * 1.0/nr*self->params.d_max*2.0*
+	            (2.0*pi*(j+offset)/self->params.d_max*cos(pi*(j+offset)*r/self->params.d_max) +
+	            tmp*tmp*r * sin(pi*(j+offset)*r/self->params.d_max));
+	        }
+        }
+    }
+
+    for (i=0; i<self->params.npoints; i++) {
+        if (accept_q(self, self->params.x[i])){
+            b[i] = self->params.y[i]/self->params.err[i];
+         }
+	}
+
+	return Py_BuildValue("i", 0);
+
+}
+
+const char get_invcov_matrix_doc[] =
+	" Compute the inverse covariance matrix, defined as inv_cov = a_transposed x a.\n"
+	" @param nfunc: number of base functions\n"
+	" @param nr: number of r-points used when evaluating reg term.\n"
+	" @param a: A array to fill\n"
+	" @param inv_cov: inverse covariance array to be filled\n"
+	" @return: 0";
+
+static PyObject * get_invcov_matrix(Cinvertor *self, PyObject *args) {
+	double *a;
+	PyObject *a_obj;
+	Py_ssize_t n_a;
+	double *inv_cov;
+	PyObject *cov_obj;
+	Py_ssize_t n_cov;
+	int nr, nfunc;
+	int i, j, k;
+
+	if (!PyArg_ParseTuple(args, "iiOO", &nfunc, &nr, &a_obj, &cov_obj)) return NULL;
+	OUTVECTOR(a_obj,a,n_a);
+	OUTVECTOR(cov_obj,inv_cov,n_cov);
+
+	assert(n_cov>=nfunc*nfunc);
+	assert(n_a>=nfunc*(nr+self->params.npoints));
+
+	for (i=0; i<nfunc; i++) {
+		for (j=0; j<nfunc; j++) {
+			inv_cov[i*nfunc+j] = 0.0;
+			for (k=0; k<nr+self->params.npoints; k++) {
+				inv_cov[i*nfunc+j] += a[k*nfunc+i]*a[k*nfunc+j];
+			}
+		}
+	}
+	return Py_BuildValue("i", 0);
+}
+
+const char get_reg_size_doc[] =
+	" Compute the covariance matrix, defined as inv_cov = a_transposed x a.\n"
+	" @param nfunc: number of base functions\n"
+	" @param nr: number of r-points used when evaluating reg term.\n"
+	" @param a: A array to fill\n"
+	" @param inv_cov: inverse covariance array to be filled\n"
+	" @return: 0";
+
+static PyObject * get_reg_size(Cinvertor *self, PyObject *args) {
+	double *a;
+	PyObject *a_obj;
+	Py_ssize_t n_a;
+	int nr, nfunc;
+	int i, j;
+	double sum_sig, sum_reg;
+
+	if (!PyArg_ParseTuple(args, "iiO", &nfunc, &nr, &a_obj)) return NULL;
+	OUTVECTOR(a_obj,a,n_a);
+
+	assert(n_a>=nfunc*(nr+self->params.npoints));
+
+    sum_sig = 0.0;
+    sum_reg = 0.0;
+    for (j=0; j<nfunc; j++){
+        for (i=0; i<self->params.npoints; i++){
+            if (accept_q(self, self->params.x[i])==1)
+                sum_sig += (a[i*nfunc+j])*(a[i*nfunc+j]);
+        }
+        for (i=0; i<nr; i++){
+            sum_reg += (a[(i+self->params.npoints)*nfunc+j])*(a[(i+self->params.npoints)*nfunc+j]);
+        }
+    }
+    return Py_BuildValue("ff", sum_sig, sum_reg);
+}
+
+const char eeeget_qmin_doc[] = "\
+This is a multiline doc string.\n\
+\n\
+This is the second line.";
+const char eeeset_qmin_doc[] =
+	"This is a multiline doc string.\n"
+	"\n"
+	"This is the second line.";
+
+static PyMethodDef Cinvertor_methods[] = {
+		   {"residuals", (PyCFunction)residuals, METH_VARARGS, residuals_doc},
+		   {"pr_residuals", (PyCFunction)pr_residuals, METH_VARARGS, pr_residuals_doc},
+		   {"set_x", (PyCFunction)set_x, METH_VARARGS, set_x_doc},
+		   {"get_x", (PyCFunction)get_x, METH_VARARGS, get_x_doc},
+		   {"set_y", (PyCFunction)set_y, METH_VARARGS, set_y_doc},
+		   {"get_y", (PyCFunction)get_y, METH_VARARGS, get_y_doc},
+		   {"set_err", (PyCFunction)set_err, METH_VARARGS, set_err_doc},
+		   {"get_err", (PyCFunction)get_err, METH_VARARGS, get_err_doc},
+		   {"set_dmax", (PyCFunction)set_dmax, METH_VARARGS, set_dmax_doc},
+		   {"get_dmax", (PyCFunction)get_dmax, METH_VARARGS, get_dmax_doc},
+		   {"set_qmin", (PyCFunction)set_qmin, METH_VARARGS, set_qmin_doc},
+		   {"get_qmin", (PyCFunction)get_qmin, METH_VARARGS, get_qmin_doc},
+		   {"set_qmax", (PyCFunction)set_qmax, METH_VARARGS, set_qmax_doc},
+		   {"get_qmax", (PyCFunction)get_qmax, METH_VARARGS, get_qmax_doc},
+		   {"set_alpha", (PyCFunction)set_alpha, METH_VARARGS, set_alpha_doc},
+		   {"get_alpha", (PyCFunction)get_alpha, METH_VARARGS, get_alpha_doc},
+		   {"set_slit_width", (PyCFunction)set_slit_width, METH_VARARGS, set_slit_width_doc},
+		   {"get_slit_width", (PyCFunction)get_slit_width, METH_VARARGS, get_slit_width_doc},
+		   {"set_slit_height", (PyCFunction)set_slit_height, METH_VARARGS, set_slit_height_doc},
+		   {"get_slit_height", (PyCFunction)get_slit_height, METH_VARARGS, get_slit_height_doc},
+		   {"set_has_bck", (PyCFunction)set_has_bck, METH_VARARGS, set_has_bck_doc},
+		   {"get_has_bck", (PyCFunction)get_has_bck, METH_VARARGS, get_has_bck_doc},
+		   {"get_nx", (PyCFunction)get_nx, METH_VARARGS, get_nx_doc},
+		   {"get_ny", (PyCFunction)get_ny, METH_VARARGS, get_ny_doc},
+		   {"get_nerr", (PyCFunction)get_nerr, METH_VARARGS, get_nerr_doc},
+		   {"iq", (PyCFunction)get_iq, METH_VARARGS, get_iq_doc},
+		   {"iq_smeared", (PyCFunction)get_iq_smeared, METH_VARARGS, get_iq_smeared_doc},
+		   {"pr", (PyCFunction)get_pr, METH_VARARGS, get_pr_doc},
+		   {"get_pr_err", (PyCFunction)get_pr_err, METH_VARARGS, get_pr_err_doc},
+		   {"is_valid", (PyCFunction)is_valid, METH_VARARGS, is_valid_doc},
+		   {"basefunc_ft", (PyCFunction)basefunc_ft, METH_VARARGS, basefunc_ft_doc},
+		   {"oscillations", (PyCFunction)oscillations, METH_VARARGS, oscillations_doc},
+		   {"get_peaks", (PyCFunction)get_peaks, METH_VARARGS, get_peaks_doc},
+		   {"get_positive", (PyCFunction)get_positive, METH_VARARGS, get_positive_doc},
+		   {"get_pos_err", (PyCFunction)get_pos_err, METH_VARARGS, get_pos_err_doc},
+		   {"rg", (PyCFunction)get_rg, METH_VARARGS, get_rg_doc},
+		   {"iq0", (PyCFunction)get_iq0, METH_VARARGS, get_iq0_doc},
+		   {"_get_matrix", (PyCFunction)get_matrix, METH_VARARGS, get_matrix_doc},
+		   {"_get_invcov_matrix", (PyCFunction)get_invcov_matrix, METH_VARARGS, get_invcov_matrix_doc},
+		   {"_get_reg_size", (PyCFunction)get_reg_size, METH_VARARGS, get_reg_size_doc},
+
+   {NULL}
+};
+
+static PyTypeObject CinvertorType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "Cinvertor",             /*tp_name*/
+    sizeof(Cinvertor),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)Cinvertor_dealloc, /*tp_dealloc*/
+    0,                         /*tp_print*/
+    0,                         /*tp_getattr*/
+    0,                         /*tp_setattr*/
+    0,                         /*tp_compare*/
+    0,                         /*tp_repr*/
+    0,                         /*tp_as_number*/
+    0,                         /*tp_as_sequence*/
+    0,                         /*tp_as_mapping*/
+    0,                         /*tp_hash */
+    0,                         /*tp_call*/
+    0,                         /*tp_str*/
+    0,                         /*tp_getattro*/
+    0,                         /*tp_setattro*/
+    0,                         /*tp_as_buffer*/
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+    "Cinvertor objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    Cinvertor_methods,             /* tp_methods */
+    Cinvertor_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)Cinvertor_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    Cinvertor_new,                 /* tp_new */
+};
+
+
+static PyMethodDef module_methods[] = {
+    {NULL}
+};
+
+/**
+ * Function used to add the model class to a module
+ * @param module: module to add the class to
+ */
+void addCinvertor(PyObject *module) {
+	PyObject *d;
+
+    if (PyType_Ready(&CinvertorType) < 0)
+        return;
+
+    Py_INCREF(&CinvertorType);
+    PyModule_AddObject(module, "Cinvertor", (PyObject *)&CinvertorType);
+
+    d = PyModule_GetDict(module);
+    CinvertorError = PyErr_NewException("sas.sascalc.pr.invertor.Cinvertor.InvertorError", PyExc_RuntimeError, NULL);
+    PyDict_SetItemString(d, "CinvertorError", CinvertorError);
+}
+
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+initpr_inversion(void)
+{
+    PyObject* m;
+
+    m = Py_InitModule3("pr_inversion", module_methods,
+                       "C extension module for inversion to P(r).");
+
+    addCinvertor(m);
+}
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 b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,3 @@
+"""
+    C extensions to provide the P(r) inversion computations.
+"""
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 b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,315 @@
+#include <math.h>
+#include "invertor.h"
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+double pi = 3.1416;
+
+/**
+ * Deallocate memory
+ */
+void invertor_dealloc(Invertor_params *pars) {
+	free(pars->x);
+	free(pars->y);
+	free(pars->err);
+}
+
+void invertor_init(Invertor_params *pars) {
+	pars->d_max = 180;
+	pars->q_min = -1.0;
+	pars->q_max = -1.0;
+	pars->has_bck = 0;
+}
+
+
+/**
+ * P(r) of a sphere, for test purposes
+ *
+ * @param R: radius of the sphere
+ * @param r: distance, in the same units as the radius
+ * @return: P(r)
+ */
+double pr_sphere(double R, double r) {
+    if (r <= 2.0*R) {
+        return 12.0* pow(0.5*r/R, 2.0) * pow(1.0-0.5*r/R, 2.0) * ( 2.0 + 0.5*r/R );
+    } else {
+        return 0.0;
+    }
+}
+
+/**
+ * Orthogonal functions:
+ * B(r) = 2r sin(pi*nr/d)
+ *
+ */
+double ortho(double d_max, int n, double r) {
+	return 2.0*r*sin(pi*n*r/d_max);
+}
+
+/**
+ * Fourier transform of the nth orthogonal function
+ *
+ */
+double ortho_transformed(double d_max, int n, double q) {
+	return 8.0*pow(pi, 2.0)/q * d_max * n * pow(-1.0, n+1)
+		*sin(q*d_max) / ( pow(pi*n, 2.0) - pow(q*d_max, 2.0) );
+}
+
+/**
+ * Slit-smeared Fourier transform of the nth orthogonal function.
+ * Smearing follows Lake, Acta Cryst. (1967) 23, 191.
+ */
+double ortho_transformed_smeared(double d_max, int n, double height, double width, double q, int npts) {
+	double sum, y, z;
+	int i, j, n_height, n_width;
+	double count_w;
+	double fnpts;
+	sum = 0.0;
+	fnpts = (float)npts-1.0;
+
+	// Check for zero slit size
+	n_height = (height>0) ? npts : 1;
+	n_width  = (width>0)  ? npts : 1;
+
+	count_w = 0.0;
+
+	for(j=0; j<n_height; j++) {
+		if(height>0){
+			z = height/fnpts*(float)j;
+		} else {
+			z = 0.0;
+		}
+
+		for(i=0; i<n_width; i++) {
+			if(width>0){
+				y = -width/2.0+width/fnpts*(float)i;
+			} else {
+				y = 0.0;
+			}
+			if (((q-y)*(q-y)+z*z)<=0.0) continue;
+			count_w += 1.0;
+			sum += ortho_transformed(d_max, n, sqrt((q-y)*(q-y)+z*z));
+		}
+	}
+	return sum/count_w;
+}
+
+/**
+ * First derivative in of the orthogonal function dB(r)/dr
+ *
+ */
+double ortho_derived(double d_max, int n, double r) {
+    return 2.0*sin(pi*n*r/d_max) + 2.0*r*cos(pi*n*r/d_max);
+}
+
+/**
+ * Scattering intensity calculated from the expansion.
+ */
+double iq(double *pars, double d_max, int n_c, double q) {
+    double sum = 0.0;
+	int i;
+    for (i=0; i<n_c; i++) {
+        sum += pars[i] * ortho_transformed(d_max, i+1, q);
+    }
+    return sum;
+}
+
+/**
+ * Scattering intensity calculated from the expansion,
+ * slit-smeared.
+ */
+double iq_smeared(double *pars, double d_max, int n_c, double height, double width, double q, int npts) {
+    double sum = 0.0;
+	int i;
+    for (i=0; i<n_c; i++) {
+        sum += pars[i] * ortho_transformed_smeared(d_max, i+1, height, width, q, npts);
+    }
+    return sum;
+}
+
+/**
+ * P(r) calculated from the expansion.
+ */
+double pr(double *pars, double d_max, int n_c, double r) {
+    double sum = 0.0;
+	int i;
+    for (i=0; i<n_c; i++) {
+        sum += pars[i] * ortho(d_max, i+1, r);
+    }
+    return sum;
+}
+
+/**
+ * P(r) calculated from the expansion, with errors
+ */
+void pr_err(double *pars, double *err, double d_max, int n_c,
+		double r, double *pr_value, double *pr_value_err) {
+    double sum = 0.0;
+    double sum_err = 0.0;
+    double func_value;
+	int i;
+    for (i=0; i<n_c; i++) {
+    	func_value = ortho(d_max, i+1, r);
+        sum += pars[i] * func_value;
+        //sum_err += err[i]*err[i]*func_value*func_value;
+        sum_err += err[i*n_c+i]*func_value*func_value;
+    }
+    *pr_value = sum;
+    if (sum_err>0) {
+    	*pr_value_err = sqrt(sum_err);
+    } else {
+    	*pr_value_err = sum;
+    }
+}
+
+/**
+ * dP(r)/dr calculated from the expansion.
+ */
+double dprdr(double *pars, double d_max, int n_c, double r) {
+    double sum = 0.0;
+	int i;
+    for (i=0; i<n_c; i++) {
+        sum += pars[i] * 2.0*(sin(pi*(i+1)*r/d_max) + pi*(i+1)*r/d_max * cos(pi*(i+1)*r/d_max));
+    }
+    return sum;
+}
+
+/**
+ * regularization term calculated from the expansion.
+ */
+double reg_term(double *pars, double d_max, int n_c, int nslice) {
+    double sum = 0.0;
+    double r;
+    double deriv;
+	int i;
+    for (i=0; i<nslice; i++) {
+    	r = d_max/(1.0*nslice)*i;
+    	deriv = dprdr(pars, d_max, n_c, r);
+        sum += deriv*deriv;
+    }
+    return sum/(1.0*nslice)*d_max;
+}
+
+/**
+ * regularization term calculated from the expansion.
+ */
+double int_p2(double *pars, double d_max, int n_c, int nslice) {
+    double sum = 0.0;
+    double r;
+    double value;
+	int i;
+    for (i=0; i<nslice; i++) {
+    	r = d_max/(1.0*nslice)*i;
+    	value = pr(pars, d_max, n_c, r);
+        sum += value*value;
+    }
+    return sum/(1.0*nslice)*d_max;
+}
+
+/**
+ * Integral of P(r)
+ */
+double int_pr(double *pars, double d_max, int n_c, int nslice) {
+    double sum = 0.0;
+    double r;
+    double value;
+	int i;
+    for (i=0; i<nslice; i++) {
+    	r = d_max/(1.0*nslice)*i;
+    	value = pr(pars, d_max, n_c, r);
+        sum += value;
+    }
+    return sum/(1.0*nslice)*d_max;
+}
+
+/**
+ * Get the number of P(r) peaks.
+ */
+int npeaks(double *pars, double d_max, int n_c, int nslice) {
+    double r;
+    double value;
+	int i;
+	double previous = 0.0;
+	double slope    = 0.0;
+	int count = 0;
+    for (i=0; i<nslice; i++) {
+    	r = d_max/(1.0*nslice)*i;
+    	value = pr(pars, d_max, n_c, r);
+    	if (previous<=value){
+    		//if (slope<0) count += 1;
+    		slope = 1;
+    	} else {
+    		//printf("slope -1");
+    		if (slope>0) count += 1;
+    		slope = -1;
+    	}
+    	previous = value;
+    }
+    return count;
+}
+
+/**
+ * Get the fraction of the integral of P(r) over the whole range
+ * of r that is above zero.
+ * A valid P(r) is define as being positive for all r.
+ */
+double positive_integral(double *pars, double d_max, int n_c, int nslice) {
+    double r;
+    double value;
+	int i;
+	double sum_pos = 0.0;
+	double sum = 0.0;
+
+    for (i=0; i<nslice; i++) {
+    	r = d_max/(1.0*nslice)*i;
+    	value = pr(pars, d_max, n_c, r);
+    	if (value>0.0) sum_pos += value;
+    	sum += fabs(value);
+    }
+    return sum_pos/sum;
+}
+
+/**
+ * Get the fraction of the integral of P(r) over the whole range
+ * of r that is at least one sigma above zero.
+ */
+double positive_errors(double *pars, double *err, double d_max, int n_c, int nslice) {
+    double r;
+	int i;
+	double sum_pos = 0.0;
+	double sum = 0.0;
+	double pr_val;
+	double pr_val_err;
+
+    for (i=0; i<nslice; i++) {
+    	r = d_max/(1.0*nslice)*i;
+    	pr_err(pars, err, d_max, n_c, r, &pr_val, &pr_val_err);
+    	if (pr_val>pr_val_err) sum_pos += pr_val;
+    	sum += fabs(pr_val);
+
+
+    }
+    return sum_pos/sum;
+}
+
+/**
+ * R_g radius of gyration calculation
+ *
+ * R_g**2 = integral[r**2 * p(r) dr] /  (2.0 * integral[p(r) dr])
+ */
+double rg(double *pars, double d_max, int n_c, int nslice) {
+    double sum_r2 = 0.0;
+    double sum    = 0.0;
+    double r;
+    double value;
+	int i;
+    for (i=0; i<nslice; i++) {
+    	r = d_max/(1.0*nslice)*i;
+    	value = pr(pars, d_max, n_c, r);
+        sum += value;
+        sum_r2 += r*r*value;
+    }
+    return sqrt(sum_r2/(2.0*sum));
+}
+
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 b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,59 @@
+#if !defined(invertor_h)
+#define invertor_h
+
+/**
+ * Internal data structure for P(r) inversion
+ */
+typedef struct {
+	/// Maximum distance between any two points in the system
+    double d_max;
+    /// q data
+    double *x;
+    /// I(q) data
+    double *y;
+    /// dI(q) data
+    double *err;
+    /// Number of q points
+    int npoints;
+    /// Number of I(q) points
+    int ny;
+    /// Number of dI(q) points
+    int nerr;
+    /// Alpha value
+    double alpha;
+    /// Minimum q to include in inversion
+    double q_min;
+    /// Maximum q to include in inversion
+    double q_max;
+    /// Flag for whether or not to evalute a constant background while inverting
+    int has_bck;
+    /// Slit height in units of q [A-1]
+    double slit_height;
+    /// Slit width in units of q [A-1]
+    double slit_width;
+} Invertor_params;
+
+void invertor_dealloc(Invertor_params *pars);
+
+void invertor_init(Invertor_params *pars);
+
+double pr_sphere(double R, double r);
+double ortho(double d_max, int n, double r);
+double ortho_transformed(double d_max, int n, double q);
+double ortho_derived(double d_max, int n, double r);
+double iq(double *c, double d_max, int n_c, double q);
+double pr(double *c, double d_max, int n_c, double r);
+double dprdr(double *pars, double d_max, int n_c, double r);
+double reg_term(double *pars, double d_max, int n_c, int nslice);
+double int_p2(double *pars, double d_max, int n_c, int nslice);
+void pr_err(double *pars, double *err, double d_max, int n_c,
+		double r, double *pr_value, double *pr_value_err);
+int npeaks(double *pars, double d_max, int n_c, int nslice);
+double positive_integral(double *pars, double d_max, int n_c, int nslice);
+double positive_errors(double *pars, double *err, double d_max, int n_c, int nslice);
+double rg(double *pars, double d_max, int n_c, int nslice);
+double int_pr(double *pars, double d_max, int n_c, int nslice);
+double ortho_transformed_smeared(double d_max, int n, double heigth, double width, double q, int npts);
+double iq_smeared(double *pars, double d_max, int n_c, double height, double width, double q, int npts);
+
+#endif
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 b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,107 @@
+
+################################################################################
+#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.
+#
+#See the license text in license.txt
+#
+#copyright 2009, University of Tennessee
+################################################################################
+
+"""
+Module to explore the P(r) inversion results for a range
+of D_max value. User picks a number of points and a range of
+distances, then get a series of outputs as a function of D_max
+over that range.
+"""
+import sys
+
+
+class Results(object):
+    """
+    Class to hold the inversion output parameters
+    as a function of D_max
+    """
+    def __init__(self):
+        """
+        Initialization. Create empty arrays
+        and dictionary of labels.
+        """
+        # Array of output for each inversion
+        self.chi2 = []
+        self.osc = []
+        self.pos = []
+        self.pos_err = []
+        self.rg = []
+        self.iq0 = []
+        self.bck = []
+        self.d_max = []
+        ## List of errors found during the last exploration
+        self.errors = []
+
+
+class DistExplorer(object):
+    """
+    The explorer class
+    """
+
+    def __init__(self, pr_state):
+        """
+        Initialization.
+
+        :param pr_state: sas.sascalc.pr.invertor.Invertor object
+
+        """
+        self.pr_state = pr_state
+        self._default_min = 0.8 * self.pr_state.d_max
+        self._default_max = 1.2 * self.pr_state.d_max
+
+    def __call__(self, dmin=None, dmax=None, npts=10):
+        """
+        Compute the outputs as a function of D_max.
+
+        :param dmin: minimum value for D_max
+        :param dmax: maximum value for D_max
+        :param npts: number of points for D_max
+
+        """
+        # Take care of the defaults if needed
+        if dmin is None:
+            dmin = self._default_min
+
+        if dmax is None:
+            dmax = self._default_max
+
+        # Results object to store the computation outputs.
+        results = Results()
+
+        # Loop over d_max values
+        for i in range(npts):
+            d = dmin + i * (dmax - dmin) / (npts - 1.0)
+            self.pr_state.d_max = d
+            try:
+                out, cov = self.pr_state.invert(self.pr_state.nfunc)
+
+                # Store results
+                iq0 = self.pr_state.iq0(out)
+                rg = self.pr_state.rg(out)
+                pos = self.pr_state.get_positive(out)
+                pos_err = self.pr_state.get_pos_err(out, cov)
+                osc = self.pr_state.oscillations(out)
+
+                results.d_max.append(self.pr_state.d_max)
+                results.bck.append(self.pr_state.background)
+                results.chi2.append(self.pr_state.chi2)
+                results.iq0.append(iq0)
+                results.rg.append(rg)
+                results.pos.append(pos)
+                results.pos_err.append(pos_err)
+                results.osc.append(osc)
+            except:
+                # This inversion failed, skip this D_max value
+                msg = "ExploreDialog: inversion failed for "
+                msg += "D_max=%s\n %s" % (str(d), sys.exc_value)
+                results.errors.append(msg)
+
+        return results
Index: src/sas/sascalc/pr/fit/AbstractFitEngine.py
===================================================================
--- src/sas/sascalc/pr/fit/AbstractFitEngine.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/fit/AbstractFitEngine.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,632 @@
+
+import  copy
+#import logging
+import sys
+import math
+import numpy
+
+from sas.sascalc.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.data_info import Data2D
+_SMALLVALUE = 1.0e-10
+
+class FitHandler(object):
+    """
+    Abstract interface for fit thread handler.
+
+    The methods in this class are called by the optimizer as the fit
+    progresses.
+
+    Note that it is up to the optimizer to call the fit handler correctly,
+    reporting all status changes and maintaining the 'done' flag.
+    """
+    done = False
+    """True when the fit job is complete"""
+    result = None
+    """The current best result of the fit"""
+
+    def improvement(self):
+        """
+        Called when a result is observed which is better than previous
+        results from the fit.
+
+        result is a FitResult object, with parameters, #calls and fitness.
+        """
+    def error(self, msg):
+        """
+        Model had an error; print traceback
+        """
+    def progress(self, current, expected):
+        """
+        Called each cycle of the fit, reporting the current and the
+        expected amount of work.   The meaning of these values is
+        optimizer dependent, but they can be converted into a percent
+        complete using (100*current)//expected.
+
+        Progress is updated each iteration of the fit, whatever that
+        means for the particular optimization algorithm.  It is called
+        after any calls to improvement for the iteration so that the
+        update handler can control I/O bandwidth by suppressing
+        intermediate improvements until the fit is complete.
+        """
+    def finalize(self):
+        """
+        Fit is complete; best results are reported
+        """
+    def abort(self):
+        """
+        Fit was aborted.
+        """
+
+    # TODO: not sure how these are used, but they are needed for running the fit
+    def update_fit(self, last=False): pass
+    def set_result(self, result=None): self.result = result
+
+class Model:
+    """
+    Fit wrapper for SAS models.
+    """
+    def __init__(self, sas_model, sas_data=None, **kw):
+        """
+        :param sas_model: the sas model to wrap for fitting
+
+        """
+        self.model = sas_model
+        self.name = sas_model.name
+        self.data = sas_data
+
+    def get_params(self, fitparams):
+        """
+        return a list of value of paramter to fit
+
+        :param fitparams: list of paramaters name to fit
+
+        """
+        return [self.model.getParam(k) for k in fitparams]
+
+    def set_params(self, paramlist, params):
+        """
+        Set value for parameters to fit
+
+        :param params: list of value for parameters to fit
+
+        """
+        for k,v in zip(paramlist, params):
+            self.model.setParam(k,v)
+
+    def set(self, **kw):
+        self.set_params(*zip(*kw.items()))
+
+    def eval(self, x):
+        """
+            Override eval method of model.
+
+            :param x: the x value used to compute a function
+        """
+        try:
+            return self.model.evalDistribution(x)
+        except:
+            raise
+
+    def eval_derivs(self, x, pars=[]):
+        """
+        Evaluate the model and derivatives wrt pars at x.
+
+        pars is a list of the names of the parameters for which derivatives
+        are desired.
+
+        This method needs to be specialized in the model to evaluate the
+        model function.  Alternatively, the model can implement is own
+        version of residuals which calculates the residuals directly
+        instead of calling eval.
+        """
+        raise NotImplementedError('no derivatives available')
+
+    def __call__(self, x):
+        return self.eval(x)
+
+class FitData1D(Data1D):
+    """
+        Wrapper class  for SAS data
+        FitData1D inherits from DataLoader.data_info.Data1D. Implements
+        a way to get residuals from data.
+    """
+    def __init__(self, x, y, dx=None, dy=None, smearer=None, data=None):
+        """
+            :param smearer: is an object of class QSmearer or SlitSmearer
+               that will smear the theory data (slit smearing or resolution
+               smearing) when set.
+            
+            The proper way to set the smearing object would be to
+            do the following: ::
+            
+                from sas.models.qsmearing import smear_selection
+                smearer = smear_selection(some_data)
+                fitdata1d = FitData1D( x= [1,3,..,],
+                                        y= [3,4,..,8],
+                                        dx=None,
+                                        dy=[1,2...], smearer= smearer)
+           
+            :Note: that some_data _HAS_ to be of
+                class DataLoader.data_info.Data1D
+                Setting it back to None will turn smearing off.
+                
+        """
+        Data1D.__init__(self, x=x, y=y, dx=dx, dy=dy)
+        self.num_points = len(x)
+        self.sas_data = data
+        self.smearer = smearer
+        self._first_unsmeared_bin = None
+        self._last_unsmeared_bin = None
+        # Check error bar; if no error bar found, set it constant(=1)
+        # TODO: Should provide an option for users to set it like percent,
+        # constant, or dy data
+        if dy is None or dy == [] or dy.all() == 0:
+            self.dy = numpy.ones(len(y))
+        else:
+            self.dy = numpy.asarray(dy).copy()
+
+        ## Min Q-value
+        #Skip the Q=0 point, especially when y(q=0)=None at x[0].
+        if min(self.x) == 0.0 and self.x[0] == 0 and\
+                     not numpy.isfinite(self.y[0]):
+            self.qmin = min(self.x[self.x != 0])
+        else:
+            self.qmin = min(self.x)
+        ## Max Q-value
+        self.qmax = max(self.x)
+        
+        # Range used for input to smearing
+        self._qmin_unsmeared = self.qmin
+        self._qmax_unsmeared = self.qmax
+        # Identify the bin range for the unsmeared and smeared spaces
+        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
+        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
+                            & (self.x <= self._qmax_unsmeared)
+  
+    def set_fit_range(self, qmin=None, qmax=None):
+        """ to set the fit range"""
+        # Skip Q=0 point, (especially for y(q=0)=None at x[0]).
+        # ToDo: Find better way to do it.
+        if qmin == 0.0 and not numpy.isfinite(self.y[qmin]):
+            self.qmin = min(self.x[self.x != 0])
+        elif qmin != None:
+            self.qmin = qmin
+        if qmax != None:
+            self.qmax = qmax
+        # Determine the range needed in unsmeared-Q to cover
+        # the smeared Q range
+        self._qmin_unsmeared = self.qmin
+        self._qmax_unsmeared = self.qmax
+        
+        self._first_unsmeared_bin = 0
+        self._last_unsmeared_bin = len(self.x) - 1
+        
+        if self.smearer != None:
+            self._first_unsmeared_bin, self._last_unsmeared_bin = \
+                    self.smearer.get_bin_range(self.qmin, self.qmax)
+            self._qmin_unsmeared = self.x[self._first_unsmeared_bin]
+            self._qmax_unsmeared = self.x[self._last_unsmeared_bin]
+            
+        # Identify the bin range for the unsmeared and smeared spaces
+        self.idx = (self.x >= self.qmin) & (self.x <= self.qmax)
+        ## zero error can not participate for fitting
+        self.idx = self.idx & (self.dy != 0)
+        self.idx_unsmeared = (self.x >= self._qmin_unsmeared) \
+                            & (self.x <= self._qmax_unsmeared)
+
+    def get_fit_range(self):
+        """
+            Return the range of data.x to fit
+        """
+        return self.qmin, self.qmax
+
+    def size(self):
+        """
+        Number of measurement points in data set after masking, etc.
+        """
+        return len(self.x)
+
+    def residuals(self, fn):
+        """
+            Compute residuals.
+            
+            If self.smearer has been set, use if to smear
+            the data before computing chi squared.
+            
+            :param fn: function that return model value
+            
+            :return: residuals
+        """
+        # Compute theory data f(x)
+        fx = numpy.zeros(len(self.x))
+        fx[self.idx_unsmeared] = fn(self.x[self.idx_unsmeared])
+       
+        ## Smear theory data
+        if self.smearer is not None:
+            fx = self.smearer(fx, self._first_unsmeared_bin,
+                              self._last_unsmeared_bin)
+        ## Sanity check
+        if numpy.size(self.dy) != numpy.size(fx):
+            msg = "FitData1D: invalid error array "
+            msg += "%d <> %d" % (numpy.shape(self.dy), numpy.size(fx))
+            raise RuntimeError, msg
+        return (self.y[self.idx] - fx[self.idx]) / self.dy[self.idx], fx[self.idx]
+            
+    def residuals_deriv(self, model, pars=[]):
+        """
+            :return: residuals derivatives .
+            
+            :note: in this case just return empty array 
+        """
+        return []
+
+
+class FitData2D(Data2D):
+    """
+        Wrapper class  for SAS data
+    """
+    def __init__(self, sas_data2d, data=None, err_data=None):
+        Data2D.__init__(self, data=data, err_data=err_data)
+        # Data can be initialized with a sas plottable or with vectors.
+        self.res_err_image = []
+        self.num_points = 0 # will be set by set_data
+        self.idx = []
+        self.qmin = None
+        self.qmax = None
+        self.smearer = None
+        self.radius = 0
+        self.res_err_data = []
+        self.sas_data = sas_data2d
+        self.set_data(sas_data2d)
+
+    def set_data(self, sas_data2d, qmin=None, qmax=None):
+        """
+            Determine the correct qx_data and qy_data within range to fit
+        """
+        self.data = sas_data2d.data
+        self.err_data = sas_data2d.err_data
+        self.qx_data = sas_data2d.qx_data
+        self.qy_data = sas_data2d.qy_data
+        self.mask = sas_data2d.mask
+
+        x_max = max(math.fabs(sas_data2d.xmin), math.fabs(sas_data2d.xmax))
+        y_max = max(math.fabs(sas_data2d.ymin), math.fabs(sas_data2d.ymax))
+        
+        ## fitting range
+        if qmin == None:
+            self.qmin = 1e-16
+        if qmax == 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 == []:
+            self.res_err_data = numpy.ones(len(self.data))
+        else:
+            self.res_err_data = copy.deepcopy(self.err_data)
+        #self.res_err_data[self.res_err_data==0]=1
+        
+        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
+        
+        # Note: mask = True: for MASK while mask = False for NOT to mask
+        self.idx = ((self.qmin <= self.radius) &\
+                            (self.radius <= self.qmax))
+        self.idx = (self.idx) & (self.mask)
+        self.idx = (self.idx) & (numpy.isfinite(self.data))
+        self.num_points = numpy.sum(self.idx)
+
+    def set_smearer(self, smearer):
+        """
+            Set smearer
+        """
+        if smearer == None:
+            return
+        self.smearer = smearer
+        self.smearer.set_index(self.idx)
+        self.smearer.get_data()
+
+    def set_fit_range(self, qmin=None, qmax=None):
+        """
+            To set the fit range
+        """
+        if qmin == 0.0:
+            self.qmin = 1e-16
+        elif qmin != None:
+            self.qmin = qmin
+        if qmax != None:
+            self.qmax = qmax
+        self.radius = numpy.sqrt(self.qx_data**2 + self.qy_data**2)
+        self.idx = ((self.qmin <= self.radius) &\
+                            (self.radius <= self.qmax))
+        self.idx = (self.idx) & (self.mask)
+        self.idx = (self.idx) & (numpy.isfinite(self.data))
+        self.idx = (self.idx) & (self.res_err_data != 0)
+
+    def get_fit_range(self):
+        """
+        return the range of data.x to fit
+        """
+        return self.qmin, self.qmax
+
+    def size(self):
+        """
+        Number of measurement points in data set after masking, etc.
+        """
+        return numpy.sum(self.idx)
+
+    def residuals(self, fn):
+        """
+        return the residuals
+        """
+        if self.smearer != None:
+            fn.set_index(self.idx)
+            # Get necessary data from self.data and set the data for smearing
+            fn.get_data()
+
+            gn = fn.get_value()
+        else:
+            gn = fn([self.qx_data[self.idx],
+                     self.qy_data[self.idx]])
+        # use only the data point within ROI range
+        res = (self.data[self.idx] - gn) / self.res_err_data[self.idx]
+
+        return res, gn
+        
+    def residuals_deriv(self, model, pars=[]):
+        """
+        :return: residuals derivatives .
+        
+        :note: in this case just return empty array
+        
+        """
+        return []
+    
+    
+class FitAbort(Exception):
+    """
+    Exception raise to stop the fit
+    """
+    #pass
+    #print"Creating fit abort Exception"
+
+
+
+class FitEngine:
+    def __init__(self):
+        """
+        Base class for the fit engine
+        """
+        #Dictionnary of fitArrange element (fit problems)
+        self.fit_arrange_dict = {}
+        self.fitter_id = None
+        
+    def set_model(self, model, id, pars=[], constraints=[], data=None):
+        """
+        set a model on a given  in the fit engine.
+        
+        :param model: sas.models type 
+        :param id: is the key of the fitArrange dictionary where model is saved as a value
+        :param pars: the list of parameters to fit 
+        :param constraints: list of 
+            tuple (name of parameter, value of parameters)
+            the value of parameter must be a string to constraint 2 different
+            parameters.
+            Example:  
+            we want to fit 2 model M1 and M2 both have parameters A and B.
+            constraints can be ``constraints = [(M1.A, M2.B+2), (M1.B= M2.A *5),...,]``
+            
+             
+        :note: pars must contains only name of existing model's parameters
+        
+        """
+        if not pars:
+            raise ValueError("no fitting parameters")
+
+        if model is None:
+            raise ValueError("no model to fit")
+
+        if not issubclass(model.__class__, Model):
+            model = Model(model, data)
+
+        sasmodel = model.model
+        available_parameters = sasmodel.getParamList()
+        for p in pars:
+            if p not in available_parameters:
+                raise ValueError("parameter %s not available in model %s; use one of [%s] instead"
+                                 %(p, sasmodel.name, ", ".join(available_parameters)))
+
+        if id not in self.fit_arrange_dict:
+            self.fit_arrange_dict[id] = FitArrange()
+
+        self.fit_arrange_dict[id].set_model(model)
+        self.fit_arrange_dict[id].pars = pars
+        self.fit_arrange_dict[id].vals = [sasmodel.getParam(name) for name in pars]
+        self.fit_arrange_dict[id].constraints = constraints
+
+    def set_data(self, data, id, smearer=None, qmin=None, qmax=None):
+        """
+        Receives plottable, creates a list of data to fit,set data
+        in a FitArrange object and adds that object in a dictionary
+        with key id.
+        
+        :param data: data added
+        :param id: unique key corresponding to a fitArrange object with data
+        """
+        if data.__class__.__name__ == 'Data2D':
+            fitdata = FitData2D(sas_data2d=data, data=data.data,
+                                 err_data=data.err_data)
+        else:
+            fitdata = FitData1D(x=data.x, y=data.y,
+                                 dx=data.dx, dy=data.dy, smearer=smearer)
+        fitdata.sas_data = data
+       
+        fitdata.set_fit_range(qmin=qmin, qmax=qmax)
+        #A fitArrange is already created but contains model only at id
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].add_data(fitdata)
+        else:
+        #no fitArrange object has been create with this id
+            fitproblem = FitArrange()
+            fitproblem.add_data(fitdata)
+            self.fit_arrange_dict[id] = fitproblem
+   
+    def get_model(self, id):
+        """
+        :param id: id is key in the dictionary containing the model to return
+        
+        :return:  a model at this id or None if no FitArrange element was
+            created with this id
+        """
+        if id in self.fit_arrange_dict:
+            return self.fit_arrange_dict[id].get_model()
+        else:
+            return None
+    
+    def remove_fit_problem(self, id):
+        """remove   fitarrange in id"""
+        if id in self.fit_arrange_dict:
+            del self.fit_arrange_dict[id]
+            
+    def select_problem_for_fit(self, id, value):
+        """
+        select a couple of model and data at the id position in dictionary
+        and set in self.selected value to value
+        
+        :param value: the value to allow fitting.
+                can only have the value one or zero
+        """
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].set_to_fit(value)
+             
+    def get_problem_to_fit(self, id):
+        """
+        return the self.selected value of the fit problem of id
+        
+        :param id: the id of the problem
+        """
+        if id in self.fit_arrange_dict:
+            self.fit_arrange_dict[id].get_to_fit()
+    
+    
+class FitArrange:
+    def __init__(self):
+        """
+        Class FitArrange contains a set of data for a given model
+        to perform the Fit.FitArrange must contain exactly one model
+        and at least one data for the fit to be performed.
+        
+        model: the model selected by the user
+        Ldata: a list of data what the user wants to fit
+            
+        """
+        self.model = None
+        self.data_list = []
+        self.pars = []
+        self.vals = []
+        self.selected = 0
+
+    def set_model(self, model):
+        """
+        set_model save a copy of the model
+        
+        :param model: the model being set
+        """
+        self.model = model
+        
+    def add_data(self, data):
+        """
+        add_data fill a self.data_list with data to fit
+        
+        :param data: Data to add in the list
+        """
+        if not data in self.data_list:
+            self.data_list.append(data)
+            
+    def get_model(self):
+        """
+        :return: saved model
+        """
+        return self.model
+     
+    def get_data(self):
+        """
+        :return: list of data data_list
+        """
+        return self.data_list[0]
+      
+    def remove_data(self, data):
+        """
+        Remove one element from the list
+        
+        :param data: Data to remove from data_list
+        """
+        if data in self.data_list:
+            self.data_list.remove(data)
+            
+    def set_to_fit(self, value=0):
+        """
+        set self.selected to 0 or 1  for other values raise an exception
+        
+        :param value: integer between 0 or 1
+        """
+        self.selected = value
+        
+    def get_to_fit(self):
+        """
+        return self.selected value
+        """
+        return self.selected
+
+class FResult(object):
+    """
+    Storing fit result
+    """
+    def __init__(self, model=None, param_list=None, data=None):
+        self.calls = None
+        self.fitness = None
+        self.chisqr = None
+        self.pvec = []
+        self.cov = []
+        self.info = None
+        self.mesg = None
+        self.success = None
+        self.stderr = None
+        self.residuals = []
+        self.index = []
+        self.model = model
+        self.data = data
+        self.theory = []
+        self.param_list = param_list
+        self.iterations = 0
+        self.inputs = []
+        self.fitter_id = None
+        if self.model is not None and self.data is not None:
+            self.inputs = [(self.model, self.data)]
+     
+    def set_model(self, model):
+        """
+        """
+        self.model = model
+        
+    def set_fitness(self, fitness):
+        """
+        """
+        self.fitness = fitness
+        
+    def __str__(self):
+        """
+        """
+        if self.pvec == None and self.model is None and self.param_list is None:
+            return "No results"
+
+        sasmodel = self.model.model
+        pars = enumerate(sasmodel.getParamList())
+        msg1 = "[Iteration #: %s ]" % self.iterations
+        msg3 = "=== goodness of fit: %s ===" % (str(self.fitness))
+        msg2 = ["P%-3d  %s......|.....%s" % (i, v, sasmodel.getParam(v))
+                for i,v in pars if v in self.param_list]
+        msg = [msg1, msg3] + msg2
+        return "\n".join(msg)
+    
+    def print_summary(self):
+        """
+        """
+        print str(self)
Index: src/sas/sascalc/pr/fit/BumpsFitting.py
===================================================================
--- src/sas/sascalc/pr/fit/BumpsFitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/fit/BumpsFitting.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,365 @@
+"""
+BumpsFitting module runs the bumps optimizer.
+"""
+import os
+from datetime import timedelta, datetime
+
+import numpy
+
+from bumps import fitters
+try:
+    from bumps.options import FIT_CONFIG
+    # Default bumps to use the Levenberg-Marquardt optimizer
+    FIT_CONFIG.selected_id = fitters.LevenbergMarquardtFit.id
+    def get_fitter():
+        return FIT_CONFIG.selected_fitter, FIT_CONFIG.selected_values
+except:
+    # CRUFT: Bumps changed its handling of fit options around 0.7.5.6
+    # Default bumps to use the Levenberg-Marquardt optimizer
+    fitters.FIT_DEFAULT = 'lm'
+    def get_fitter():
+        fitopts = fitters.FIT_OPTIONS[fitters.FIT_DEFAULT]
+        return fitopts.fitclass, fitopts.options.copy()
+
+
+from bumps.mapper import SerialMapper, MPMapper
+from bumps import parameter
+from bumps.fitproblem import FitProblem
+
+
+from sas.sascalc.fit.AbstractFitEngine import FitEngine
+from sas.sascalc.fit.AbstractFitEngine import FResult
+from sas.sascalc.fit.expression import compile_constraints
+
+class Progress(object):
+    def __init__(self, history, max_step, pars, dof):
+        remaining_time = int(history.time[0]*(float(max_step)/history.step[0]-1))
+        # Depending on the time remaining, either display the expected
+        # time of completion, or the amount of time remaining.  Use precision
+        # appropriate for the duration.
+        if remaining_time >= 1800:
+            completion_time = datetime.now() + timedelta(seconds=remaining_time)
+            if remaining_time >= 36000:
+                time = completion_time.strftime('%Y-%m-%d %H:%M')
+            else:
+                time = completion_time.strftime('%H:%M')
+        else:
+            if remaining_time >= 3600:
+                time = '%dh %dm'%(remaining_time//3600, (remaining_time%3600)//60)
+            elif remaining_time >= 60:
+                time = '%dm %ds'%(remaining_time//60, remaining_time%60)
+            else:
+                time = '%ds'%remaining_time
+        chisq = "%.3g"%(2*history.value[0]/dof)
+        step = "%d of %d"%(history.step[0], max_step)
+        header = "=== Steps: %s  chisq: %s  ETA: %s\n"%(step, chisq, time)
+        parameters = ["%15s: %-10.3g%s"%(k,v,("\n" if i%3==2 else " | "))
+                      for i,(k,v) in enumerate(zip(pars,history.point[0]))]
+        self.msg = "".join([header]+parameters)
+
+    def __str__(self):
+        return self.msg
+
+
+class BumpsMonitor(object):
+    def __init__(self, handler, max_step, pars, dof):
+        self.handler = handler
+        self.max_step = max_step
+        self.pars = pars
+        self.dof = dof
+
+    def config_history(self, history):
+        history.requires(time=1, value=2, point=1, step=1)
+
+    def __call__(self, history):
+        if self.handler is None: return
+        self.handler.set_result(Progress(history, self.max_step, self.pars, self.dof))
+        self.handler.progress(history.step[0], self.max_step)
+        if len(history.step)>1 and history.step[1] > history.step[0]:
+            self.handler.improvement()
+        self.handler.update_fit()
+
+class ConvergenceMonitor(object):
+    """
+    ConvergenceMonitor contains population summary statistics to show progress
+    of the fit.  This is a list [ (best, 0%, 25%, 50%, 75%, 100%) ] or
+    just a list [ (best, ) ] if population size is 1.
+    """
+    def __init__(self):
+        self.convergence = []
+
+    def config_history(self, history):
+        history.requires(value=1, population_values=1)
+
+    def __call__(self, history):
+        best = history.value[0]
+        try:
+            p = history.population_values[0]
+            n,p = len(p), numpy.sort(p)
+            QI,Qmid, = int(0.2*n),int(0.5*n)
+            self.convergence.append((best, p[0],p[QI],p[Qmid],p[-1-QI],p[-1]))
+        except:
+            self.convergence.append((best, best,best,best,best,best))
+
+
+# Note: currently using bumps parameters for each parameter object so that
+# a SasFitness can be used directly in bumps with the usual semantics.
+# The disadvantage of this technique is that we need to copy every parameter
+# back into the model each time the function is evaluated.  We could instead
+# define reference parameters for each sas parameter, but then we would not
+# be able to express constraints using python expressions in the usual way
+# from bumps, and would instead need to use string expressions.
+class SasFitness(object):
+    """
+    Wrap SAS model as a bumps fitness object
+    """
+    def __init__(self, model, data, fitted=[], constraints={},
+                 initial_values=None, **kw):
+        self.name = model.name
+        self.model = model.model
+        self.data = data
+        if self.data.smearer is not None:
+            self.data.smearer.model = self.model
+        self._define_pars()
+        self._init_pars(kw)
+        if initial_values is not None:
+            self._reset_pars(fitted, initial_values)
+        self.constraints = dict(constraints)
+        self.set_fitted(fitted)
+        self.update()
+
+    def _reset_pars(self, names, values):
+        for k,v in zip(names, values):
+            self._pars[k].value = v
+
+    def _define_pars(self):
+        self._pars = {}
+        for k in self.model.getParamList():
+            name = ".".join((self.name,k))
+            value = self.model.getParam(k)
+            bounds = self.model.details.get(k,["",None,None])[1:3]
+            self._pars[k] = parameter.Parameter(value=value, bounds=bounds,
+                                                fixed=True, name=name)
+        #print parameter.summarize(self._pars.values())
+
+    def _init_pars(self, kw):
+        for k,v in kw.items():
+            # dispersion parameters initialized with _field instead of .field
+            if k.endswith('_width'): k = k[:-6]+'.width'
+            elif k.endswith('_npts'): k = k[:-5]+'.npts'
+            elif k.endswith('_nsigmas'): k = k[:-7]+'.nsigmas'
+            elif k.endswith('_type'): k = k[:-5]+'.type'
+            if k not in self._pars:
+                formatted_pars = ", ".join(sorted(self._pars.keys()))
+                raise KeyError("invalid parameter %r for %s--use one of: %s"
+                               %(k, self.model, formatted_pars))
+            if '.' in k and not k.endswith('.width'):
+                self.model.setParam(k, v)
+            elif isinstance(v, parameter.BaseParameter):
+                self._pars[k] = v
+            elif isinstance(v, (tuple,list)):
+                low, high = v
+                self._pars[k].value = (low+high)/2
+                self._pars[k].range(low,high)
+            else:
+                self._pars[k].value = v
+
+    def set_fitted(self, param_list):
+        """
+        Flag a set of parameters as fitted parameters.
+        """
+        for k,p in self._pars.items():
+            p.fixed = (k not in param_list or k in self.constraints)
+        self.fitted_par_names = [k for k in param_list if k not in self.constraints]
+        self.computed_par_names = [k for k in param_list if k in self.constraints]
+        self.fitted_pars = [self._pars[k] for k in self.fitted_par_names]
+        self.computed_pars = [self._pars[k] for k in self.computed_par_names]
+
+    # ===== Fitness interface ====
+    def parameters(self):
+        return self._pars
+
+    def update(self):
+        for k,v in self._pars.items():
+            #print "updating",k,v,v.value
+            self.model.setParam(k,v.value)
+        self._dirty = True
+
+    def _recalculate(self):
+        if self._dirty:
+            self._residuals, self._theory \
+                = self.data.residuals(self.model.evalDistribution)
+            self._dirty = False
+
+    def numpoints(self):
+        return numpy.sum(self.data.idx) # number of fitted points
+
+    def nllf(self):
+        return 0.5*numpy.sum(self.residuals()**2)
+
+    def theory(self):
+        self._recalculate()
+        return self._theory
+
+    def residuals(self):
+        self._recalculate()
+        return self._residuals
+
+    # Not implementing the data methods for now:
+    #
+    #     resynth_data/restore_data/save/plot
+
+class ParameterExpressions(object):
+    def __init__(self, models):
+        self.models = models
+        self._setup()
+
+    def _setup(self):
+        exprs = {}
+        for M in self.models:
+            exprs.update((".".join((M.name, k)), v) for k, v in M.constraints.items())
+        if exprs:
+            symtab = dict((".".join((M.name, k)), p)
+                          for M in self.models
+                          for k,p in M.parameters().items())
+            self.update = compile_constraints(symtab, exprs)
+        else:
+            self.update = lambda: 0
+
+    def __call__(self):
+        self.update()
+
+    def __getstate__(self):
+        return self.models
+
+    def __setstate__(self, state):
+        self.models = state
+        self._setup()
+
+class BumpsFit(FitEngine):
+    """
+    Fit a model using bumps.
+    """
+    def __init__(self):
+        """
+        Creates a dictionary (self.fit_arrange_dict={})of FitArrange elements
+        with Uid as keys
+        """
+        FitEngine.__init__(self)
+        self.curr_thread = None
+
+    def fit(self, msg_q=None,
+            q=None, handler=None, curr_thread=None,
+            ftol=1.49012e-8, reset_flag=False):
+        # Build collection of bumps fitness calculators
+        models = [SasFitness(model=M.get_model(),
+                             data=M.get_data(),
+                             constraints=M.constraints,
+                             fitted=M.pars,
+                             initial_values=M.vals if reset_flag else None)
+                  for M in self.fit_arrange_dict.values()
+                  if M.get_to_fit()]
+        if len(models) == 0:
+            raise RuntimeError("Nothing to fit")
+        problem = FitProblem(models)
+
+        # TODO: need better handling of parameter expressions and bounds constraints
+        # so that they are applied during polydispersity calculations.  This
+        # will remove the immediate need for the setp_hook in bumps, though
+        # bumps may still need something similar, such as a sane class structure
+        # which allows a subclass to override setp.
+        problem.setp_hook = ParameterExpressions(models)
+
+        # Run the fit
+        result = run_bumps(problem, handler, curr_thread)
+        if handler is not None:
+            handler.update_fit(last=True)
+
+        # TODO: shouldn't reference internal parameters of fit problem
+        varying = problem._parameters
+        # collect the results
+        all_results = []
+        for M in problem.models:
+            fitness = M.fitness
+            fitted_index = [varying.index(p) for p in fitness.fitted_pars]
+            param_list = fitness.fitted_par_names + fitness.computed_par_names
+            R = FResult(model=fitness.model, data=fitness.data,
+                        param_list=param_list)
+            R.theory = fitness.theory()
+            R.residuals = fitness.residuals()
+            R.index = fitness.data.idx
+            R.fitter_id = self.fitter_id
+            # TODO: should scale stderr by sqrt(chisq/DOF) if dy is unknown
+            R.success = result['success']
+            if R.success:
+                R.stderr = numpy.hstack((result['stderr'][fitted_index],
+                                         numpy.NaN*numpy.ones(len(fitness.computed_pars))))
+                R.pvec = numpy.hstack((result['value'][fitted_index],
+                                      [p.value for p in fitness.computed_pars]))
+                R.fitness = numpy.sum(R.residuals**2)/(fitness.numpoints() - len(fitted_index))
+            else:
+                R.stderr = numpy.NaN*numpy.ones(len(param_list))
+                R.pvec = numpy.asarray( [p.value for p in fitness.fitted_pars+fitness.computed_pars])
+                R.fitness = numpy.NaN
+            R.convergence = result['convergence']
+            if result['uncertainty'] is not None:
+                R.uncertainty_state = result['uncertainty']
+            all_results.append(R)
+
+        if q is not None:
+            q.put(all_results)
+            return q
+        else:
+            return all_results
+
+def run_bumps(problem, handler, curr_thread):
+    def abort_test():
+        if curr_thread is None: return False
+        try: curr_thread.isquit()
+        except KeyboardInterrupt:
+            if handler is not None:
+                handler.stop("Fitting: Terminated!!!")
+            return True
+        return False
+
+    fitclass, options = get_fitter()
+    steps = options.get('steps', 0)
+    if steps == 0:
+        pop = options.get('pop',0)*len(problem._parameters)
+        samples = options.get('samples', 0)
+        steps = (samples+pop-1)/pop if pop != 0 else samples
+    max_step = steps + options.get('burn', 0)
+    pars = [p.name for p in problem._parameters]
+    #x0 = numpy.asarray([p.value for p in problem._parameters])
+    options['monitors'] = [
+        BumpsMonitor(handler, max_step, pars, problem.dof),
+        ConvergenceMonitor(),
+        ]
+    fitdriver = fitters.FitDriver(fitclass, problem=problem,
+                                  abort_test=abort_test, **options)
+    omp_threads = int(os.environ.get('OMP_NUM_THREADS','0'))
+    mapper = MPMapper if omp_threads == 1 else SerialMapper
+    fitdriver.mapper = mapper.start_mapper(problem, None)
+    #import time; T0 = time.time()
+    try:
+        best, fbest = fitdriver.fit()
+    except:
+        import traceback; traceback.print_exc()
+        raise
+    finally:
+        mapper.stop_mapper(fitdriver.mapper)
+
+
+    convergence_list = options['monitors'][-1].convergence
+    convergence = (2*numpy.asarray(convergence_list)/problem.dof
+                   if convergence_list else numpy.empty((0,1),'d'))
+
+    success = best is not None
+    return {
+        'value': best if success else None,
+        'stderr': fitdriver.stderr() if success else None,
+        'success': success,
+        'convergence': convergence,
+        'uncertainty': getattr(fitdriver.fitter, 'state', None),
+        }
+
Index: src/sas/sascalc/pr/fit/Loader.py
===================================================================
--- src/sas/sascalc/pr/fit/Loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/fit/Loader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,86 @@
+# class Loader  to load any king of file
+#import wx
+#import string
+import numpy
+
+class Load:
+    """
+    This class is loading values from given file or value giving by the user
+    """
+    def __init__(self, x=None, y=None, dx=None, dy=None):
+        raise NotImplementedError("a code search shows that this code is not active, and you are not seeing this message")
+        # variable to store loaded values
+        self.x = x
+        self.y = y
+        self.dx = dx
+        self.dy = dy
+        self.filename = None
+        
+    def set_filename(self, path=None):
+        """
+        Store path into a variable.If the user doesn't give 
+        a path as a parameter a pop-up
+        window appears to select the file.
+        
+        :param path: the path given by the user
+        
+        """
+        self.filename = path
+       
+    def get_filename(self):
+        """ return the file's path"""
+        return self.filename
+    
+    def set_values(self):
+        """ Store the values loaded from file in local variables"""
+        if not self.filename == None:
+            input_f =  open(self.filename, 'r')
+            buff = input_f.read()
+            lines = buff.split('\n')
+            self.x = []
+            self.y = []
+            self.dx = [] 
+            self.dy = []
+            for line in lines:
+                try:
+                    toks = line.split()
+                    x = float(toks[0])
+                    y = float(toks[1])
+                    dy = float(toks[2])
+                    
+                    self.x.append(x)
+                    self.y.append(y)
+                    self.dy.append(dy)
+                    self.dx = numpy.zeros(len(self.x))
+                except:
+                    print "READ ERROR", line
+            # Sanity check
+            if not len(self.x) == len(self.dx):
+                raise ValueError, "x and dx have different length"
+            if not len(self.y) == len(self.dy):
+                raise ValueError, "y and dy have different length"
+            
+            
+    def get_values(self):
+        """ Return x, y, dx, dy"""
+        return self.x, self.y, self.dx, self.dy
+    
+    def load_data(self, data):
+        """ Return plottable"""
+        #load data
+        data.x = self.x
+        data.y = self.y
+        data.dx = self.dx
+        data.dy = self.dy
+        #Load its View class
+        #plottable.reset_view()
+       
+    
+if __name__ == "__main__": 
+    load = Load()
+    load.set_filename("testdata_line.txt")
+    print load.get_filename() 
+    load.set_values()
+    print load.get_values()
+    
+            
Index: src/sas/sascalc/pr/fit/__init__.py
===================================================================
--- src/sas/sascalc/pr/fit/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/fit/__init__.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,1 @@
+from .AbstractFitEngine import FitHandler
Index: src/sas/sascalc/pr/fit/expression.py
===================================================================
--- src/sas/sascalc/pr/fit/expression.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/fit/expression.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,400 @@
+# This program is public domain
+"""
+Parameter expression evaluator.
+
+For systems in which constraints are expressed as string expressions rather
+than python code, :func:`compile_constraints` can construct an expression
+evaluator that substitutes the computed values of the expressions into the
+parameters.
+
+The compiler requires a symbol table, an expression set and a context.
+The symbol table maps strings containing fully qualified names such as
+'M1.c[3].full_width' to parameter objects with a 'value' property that
+can be queried and set.  The expression set maps symbol names from the
+symbol table to string expressions.  The context provides additional symbols
+for the expressions in addition to the usual mathematical functions and
+constants.
+
+The expressions are compiled and interpreted by python, with only minimal
+effort to make sure that they don't contain bad code.  The resulting
+constraints function returns 0 so it can be used directly in a fit problem
+definition.
+
+Extracting the symbol table from the model depends on the structure of the
+model.  If fitness.parameters() is set correctly, then this should simply
+be a matter of walking the parameter data, remembering the path to each
+parameter in the symbol table.  For compactness, dictionary elements should
+be referenced by .name rather than ["name"].  Model name can be used as the
+top level.
+
+Getting the parameter expressions applied correctly is challenging.
+The following monkey patch works by overriding model_update in FitProblem
+so that after setp(p) is called and, the constraints expression can be
+applied before telling the underlying fitness function that the model
+is out of date::
+
+        # Override model update so that parameter constraints are applied
+        problem._model_update = problem.model_update
+        def model_update():
+            constraints()
+            problem._model_update()
+        problem.model_update = model_update
+
+Ideally, this interface will change
+"""
+import math
+import re
+
+# simple pattern which matches symbols.  Note that it will also match
+# invalid substrings such as a3...9, but given syntactically correct
+# input it will only match symbols.
+_symbol_pattern = re.compile('([a-zA-Z_][a-zA-Z_0-9.]*)')
+
+def _symbols(expr,symtab):
+    """
+    Given an expression string and a symbol table, return the set of symbols
+    used in the expression.  Symbols are only returned once even if they
+    occur multiple times.  The return value is a set with the elements in
+    no particular order.
+    
+    This is the first step in computing a dependency graph.
+    """
+    matches = [m.group(0) for m in _symbol_pattern.finditer(expr)]
+    return set([symtab[m] for m in matches if m in symtab])
+
+def _substitute(expr,mapping):
+    """
+    Replace all occurrences of symbol s with mapping[s] for s in mapping.
+    """
+    # Find the symbols and the mapping
+    matches = [(m.start(),m.end(),mapping[m.group(1)])
+               for m in _symbol_pattern.finditer(expr)
+               if m.group(1) in mapping]
+
+    # Split the expression in to pieces, with new symbols replacing old
+    pieces = []
+    offset = 0
+    for start,end,text in matches:
+        pieces += [expr[offset:start],text]
+        offset = end
+    pieces.append(expr[offset:])
+    
+    # Join the pieces and return them
+    return "".join(pieces)
+
+def _find_dependencies(symtab, exprs):
+    """
+    Returns a list of pair-wise dependencies from the parameter expressions.
+    
+    For example, if p3 = p1+p2, then find_dependencies([p1,p2,p3]) will
+    return [(p3,p1),(p3,p2)].  For base expressions without dependencies,
+    such as p4 = 2*pi, this should return [(p4, None)]
+    """
+    deps = [(target,source)
+            for target,expr in exprs.items()
+            for source in _symbols_or_none(expr,symtab)]
+    return deps
+
+# Hack to deal with expressions without dependencies --- return a fake
+# dependency of None.
+# The better solution is fix order_dependencies so that it takes a
+# dictionary of {symbol: dependency_list}, for which no dependencies
+# is simply []; fix in parameter_mapping as well
+def _symbols_or_none(expr,symtab):
+    syms = _symbols(expr,symtab)
+    return syms if len(syms) else [None]
+
+def _parameter_mapping(pairs):
+    """
+    Find the parameter substitution we need so that expressions can
+    be evaluated without having to traverse a chain of 
+    model.layer.parameter.value
+    """
+    left,right = zip(*pairs)
+    pars = list(sorted(p for p in set(left+right) if p is not None))
+    definition = dict( ('P%d'%i,p)  for i,p in enumerate(pars) )
+    # p is None when there is an expression with no dependencies
+    substitution = dict( (p,'P%d.value'%i)
+                    for i,p in enumerate(sorted(pars))
+                    if p is not None)
+    return definition, substitution
+
+def no_constraints(): 
+    """
+    This parameter set has no constraints between the parameters.
+    """
+    pass
+
+def compile_constraints(symtab, exprs, context={}):
+    """
+    Build and return a function to evaluate all parameter expressions in
+    the proper order.
+
+    Input:
+
+        *symtab* is the symbol table for the model: { 'name': parameter }
+
+        *exprs* is the set of computed symbols: { 'name': 'expression' }
+
+        *context* is any additional context needed to evaluate the expression
+
+    Return:
+
+        updater function which sets parameter.value for each expression
+
+    Raises:
+
+       AssertionError - model, parameter or function is missing
+
+       SyntaxError - improper expression syntax
+
+       ValueError - expressions have circular dependencies
+
+    This function is not terribly sophisticated, and it would be easy to
+    trick.  However it handles the common cases cleanly and generates
+    reasonable messages for the common errors.
+
+    This code has not been fully audited for security.  While we have
+    removed the builtins and the ability to import modules, there may
+    be other vectors for users to perform more than simple function
+    evaluations.  Unauthenticated users should not be running this code.
+
+    Parameter names are assumed to contain only _.a-zA-Z0-9#[]
+    
+    Both names are provided for inverse functions, e.g., acos and arccos.
+
+    Should try running the function to identify syntax errors before
+    running it in a fit.
+    
+    Use help(fn) to see the code generated for the returned function fn.
+    dis.dis(fn) will show the corresponding python vm instructions.
+    """
+
+    # Sort the parameters in the order they need to be evaluated
+    deps = _find_dependencies(symtab, exprs)
+    if deps == []: return no_constraints
+    order = order_dependencies(deps)
+
+
+    # Rather than using the full path to the parameters in the parameter
+    # expressions, instead use Pn, and substitute Pn.value for each occurrence
+    # of the parameter in the expression.
+    names = list(sorted(symtab.keys()))
+    parameters = dict(('P%d'%i, symtab[k]) for i,k in enumerate(names))
+    mapping = dict((k, 'P%d.value'%i) for i,k in enumerate(names))
+
+
+    # Initialize dictionary with available functions
+    globals = {}
+    globals.update(math.__dict__)
+    globals.update(dict(arcsin=math.asin,arccos=math.acos,
+                        arctan=math.atan,arctan2=math.atan2))
+    globals.update(context)
+    globals.update(parameters)
+    globals['id'] = id
+    locals = {}
+
+    # Define the constraints function
+    assignments = ["=".join((p,exprs[p])) for p in order]
+    code = [_substitute(s, mapping) for s in assignments]
+    functiondef = """
+def eval_expressions():
+    '''
+    %s
+    '''
+    %s
+    return 0
+"""%("\n    ".join(assignments),"\n    ".join(code))
+
+    #print("Function: "+functiondef)
+    exec functiondef in globals,locals
+    retfn = locals['eval_expressions']
+
+    # Remove garbage added to globals by exec
+    globals.pop('__doc__',None)
+    globals.pop('__name__',None)
+    globals.pop('__file__',None)
+    globals.pop('__builtins__')
+    #print globals.keys()
+
+    return retfn
+
+def order_dependencies(pairs):
+    """
+    Order elements from pairs so that b comes before a in the
+    ordered list for all pairs (a,b).
+    """
+    #print "order_dependencies",pairs
+    emptyset = set()
+    order = []
+
+    # Break pairs into left set and right set
+    left,right = [set(s) for s in zip(*pairs)] if pairs != [] else ([],[])
+    while pairs != []:
+        #print "within",pairs
+        # Find which items only occur on the right
+        independent = right - left
+        if independent == emptyset:
+            cycleset = ", ".join(str(s) for s in left)
+            raise ValueError,"Cyclic dependencies amongst %s"%cycleset
+
+        # The possibly resolvable items are those that depend on the independents
+        dependent = set([a for a,b in pairs if b in independent])
+        pairs = [(a,b) for a,b in pairs if b not in independent]
+        if pairs == []:
+            resolved = dependent
+        else:
+            left,right = [set(s) for s in zip(*pairs)]
+            resolved = dependent - left
+        #print "independent",independent,"dependent",dependent,"resolvable",resolved
+        order += resolved
+        #print "new order",order
+    order.reverse()
+    return order
+
+# ========= Test code ========
+def _check(msg,pairs):
+    """
+    Verify that the list n contains the given items, and that the list
+    satisfies the partial ordering given by the pairs in partial order.
+    """
+    left,right = zip(*pairs) if pairs != [] else ([],[])
+    items = set(left)
+    n = order_dependencies(pairs)
+    if set(n) != items or len(n) != len(items):
+        n.sort()
+        items = list(items); items.sort()
+        raise Exception,"%s expect %s to contain %s for %s"%(msg,n,items,pairs)
+    for lo,hi in pairs:
+        if lo in n and hi in n and n.index(lo) >= n.index(hi):
+            raise Exception,"%s expect %s before %s in %s for %s"%(msg,lo,hi,n,pairs)
+
+def test_deps():
+    import numpy
+
+    # Null case
+    _check("test empty",[])
+
+    # Some dependencies
+    _check("test1",[(2,7),(1,5),(1,4),(2,1),(3,1),(5,6)])
+    _check("test1 renumbered",[(6,1),(7,3),(7,4),(6,7),(5,7),(3,2)])
+    _check("test1 numpy",numpy.array([(2,7),(1,5),(1,4),(2,1),(3,1),(5,6)]))
+
+    # No dependencies
+    _check("test2",[(4,1),(3,2),(8,4)])
+
+    # Cycle test
+    pairs = [(1,4),(4,3),(4,5),(5,1)]
+    try: n = order_dependencies(pairs)
+    except ValueError: pass
+    else: raise Exception,"test3 expect ValueError exception for %s"%(pairs,)
+
+    # large test for gross speed check
+    A = numpy.random.randint(4000,size=(1000,2))
+    A[:,1] += 4000  # Avoid cycles
+    _check("test-large",A)
+
+    # depth tests
+    k = 200
+    A = numpy.array([range(0,k),range(1,k+1)]).T
+    _check("depth-1",A)
+
+    A = numpy.array([range(1,k+1),range(0,k)]).T
+    _check("depth-2",A)
+
+def test_expr():
+    import inspect, dis
+    import math
+    
+    symtab = {'a.b.x':1, 'a.c':2, 'a.b':3, 'b.x':4}
+    expr = 'a.b.x + sin(4*pi*a.c) + a.b.x/a.b'
+    
+    # Check symbol lookup
+    assert _symbols(expr, symtab) == set([1,2,3])
+
+    # Check symbol rename
+    assert _substitute(expr,{'a.b.x':'Q'}) == 'Q + sin(4*pi*a.c) + Q/a.b'
+    assert _substitute(expr,{'a.b':'Q'}) == 'a.b.x + sin(4*pi*a.c) + a.b.x/Q'
+
+
+    # Check dependency builder
+    # Fake parameter class
+    class Parameter:
+        def __init__(self, name, value=0, expression=''):
+            self.path = name
+            self.value = value
+            self.expression = expression
+        def iscomputed(self): return (self.expression != '')
+        def __repr__(self): return self.path
+    def world(*pars):
+        symtab = dict((p.path,p) for p in pars)
+        exprs = dict((p.path,p.expression) for p in pars if p.iscomputed())
+        return symtab, exprs
+    p1 = Parameter('G0.sigma',5)
+    p2 = Parameter('other',expression='2*pi*sin(G0.sigma/.1875) + M1.G1')
+    p3 = Parameter('M1.G1',6)
+    p4 = Parameter('constant',expression='2*pi*35')
+    # Simple chain
+    assert set(_find_dependencies(*world(p1,p2,p3))) == set([(p2.path,p1),(p2.path,p3)])
+    # Constant expression
+    assert set(_find_dependencies(*world(p1,p4))) == set([(p4.path,None)])
+    # No dependencies
+    assert set(_find_dependencies(*world(p1,p3))) == set([])
+
+    # Check function builder
+    fn = compile_constraints(*world(p1,p2,p3))
+
+    # Inspect the resulting function
+    if 0:
+        print(inspect.getdoc(fn))
+        print(dis.dis(fn))
+
+    # Evaluate the function and see if it updates the
+    # target value as expected
+    fn()
+    expected = 2*math.pi*math.sin(5/.1875) + 6
+    assert p2.value == expected,"Value was %s, not %s"%(p2.value,expected)
+    
+    # Check empty dependency set doesn't crash
+    fn = compile_constraints(*world(p1,p3))
+    fn()
+
+    # Check that constants are evaluated properly
+    fn = compile_constraints(*world(p4))
+    fn()
+    assert p4.value == 2*math.pi*35
+
+    # Check additional context example; this also tests multiple
+    # expressions
+    class Table:
+        Si = 2.09
+        values = {'Si': 2.07}
+    tbl = Table()
+    p5 = Parameter('lookup',expression="tbl.Si")
+    fn = compile_constraints(*world(p1,p2,p3,p5),context=dict(tbl=tbl))
+    fn()
+    assert p5.value == 2.09,"Value for %s was %s"%(p5.expression,p5.value)
+    p5.expression = "tbl.values['Si']"
+    fn = compile_constraints(*world(p1,p2,p3,p5),context=dict(tbl=tbl))
+    fn()
+    assert p5.value == 2.07,"Value for %s was %s"%(p5.expression,p5.value)
+    
+
+    # Verify that we capture invalid expressions
+    for expr in ['G4.cage', 'M0.cage', 'M1.G1 + *2', 
+                 'piddle',
+                 '5; import sys; print "p0wned"',
+                 '__import__("sys").argv']:
+        try:
+            p6 = Parameter('broken',expression=expr)
+            fn = compile_constraints(*world(p6))
+            fn()
+        except Exception as msg:
+            #print(msg)
+            pass
+        else:
+            raise "Failed to raise error for %s"%expr
+
+if __name__ == "__main__":
+    test_expr()
+    test_deps()
Index: src/sas/sascalc/pr/invertor.py
===================================================================
--- src/sas/sascalc/pr/invertor.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/invertor.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,750 @@
+# pylint: disable=invalid-name
+"""
+Module to perform P(r) inversion.
+The module contains the Invertor class.
+
+FIXME: The way the Invertor interacts with its C component should be cleaned up
+"""
+
+import numpy
+import sys
+import math
+import time
+import copy
+import os
+import re
+import logging
+from numpy.linalg import lstsq
+from scipy import optimize
+from sas.sascalc.pr.core.pr_inversion import Cinvertor
+
+def help():
+    """
+    Provide general online help text
+    Future work: extend this function to allow topic selection
+    """
+    info_txt = "The inversion approach is based on Moore, J. Appl. Cryst. "
+    info_txt += "(1980) 13, 168-175.\n\n"
+    info_txt += "P(r) is set to be equal to an expansion of base functions "
+    info_txt += "of the type "
+    info_txt += "phi_n(r) = 2*r*sin(pi*n*r/D_max). The coefficient of each "
+    info_txt += "base functions "
+    info_txt += "in the expansion is found by performing a least square fit "
+    info_txt += "with the "
+    info_txt += "following fit function:\n\n"
+    info_txt += "chi**2 = sum_i[ I_meas(q_i) - I_th(q_i) ]**2/error**2 +"
+    info_txt += "Reg_term\n\n"
+    info_txt += "where I_meas(q) is the measured scattering intensity and "
+    info_txt += "I_th(q) is "
+    info_txt += "the prediction from the Fourier transform of the P(r) "
+    info_txt += "expansion. "
+    info_txt += "The Reg_term term is a regularization term set to the second"
+    info_txt += " derivative "
+    info_txt += "d**2P(r)/dr**2 integrated over r. It is used to produce "
+    info_txt += "a smooth P(r) output.\n\n"
+    info_txt += "The following are user inputs:\n\n"
+    info_txt += "   - Number of terms: the number of base functions in the P(r)"
+    info_txt += " expansion.\n\n"
+    info_txt += "   - Regularization constant: a multiplicative constant "
+    info_txt += "to set the size of "
+    info_txt += "the regularization term.\n\n"
+    info_txt += "   - Maximum distance: the maximum distance between any "
+    info_txt += "two points in the system.\n"
+
+    return info_txt
+
+
+class Invertor(Cinvertor):
+    """
+    Invertor class to perform P(r) inversion
+
+    The problem is solved by posing the problem as  Ax = b,
+    where x is the set of coefficients we are looking for.
+
+    Npts is the number of points.
+
+    In the following i refers to the ith base function coefficient.
+    The matrix has its entries j in its first Npts rows set to ::
+
+        A[j][i] = (Fourier transformed base function for point j)
+
+    We them choose a number of r-points, n_r, to evaluate the second
+    derivative of P(r) at. This is used as our regularization term.
+    For a vector r of length n_r, the following n_r rows are set to ::
+
+        A[j+Npts][i] = (2nd derivative of P(r), d**2(P(r))/d(r)**2,
+        evaluated at r[j])
+
+    The vector b has its first Npts entries set to ::
+
+        b[j] = (I(q) observed for point j)
+
+    The following n_r entries are set to zero.
+
+    The result is found by using scipy.linalg.basic.lstsq to invert
+    the matrix and find the coefficients x.
+
+    Methods inherited from Cinvertor:
+
+    * ``get_peaks(pars)``: returns the number of P(r) peaks
+    * ``oscillations(pars)``: returns the oscillation parameters for the output P(r)
+    * ``get_positive(pars)``: returns the fraction of P(r) that is above zero
+    * ``get_pos_err(pars)``: returns the fraction of P(r) that is 1-sigma above zero
+    """
+    ## Chisqr of the last computation
+    chi2 = 0
+    ## Time elapsed for last computation
+    elapsed = 0
+    ## Alpha to get the reg term the same size as the signal
+    suggested_alpha = 0
+    ## Last number of base functions used
+    nfunc = 10
+    ## Last output values
+    out = None
+    ## Last errors on output values
+    cov = None
+    ## Background value
+    background = 0
+    ## Information dictionary for application use
+    info = {}
+
+    def __init__(self):
+        Cinvertor.__init__(self)
+
+    def __setstate__(self, state):
+        """
+        restore the state of invertor for pickle
+        """
+        (self.__dict__, self.alpha, self.d_max,
+         self.q_min, self.q_max,
+         self.x, self.y,
+         self.err, self.has_bck,
+         self.slit_height, self.slit_width) = state
+
+    def __reduce_ex__(self, proto):
+        """
+        Overwrite the __reduce_ex__
+        """
+
+        state = (self.__dict__,
+                 self.alpha, self.d_max,
+                 self.q_min, self.q_max,
+                 self.x, self.y,
+                 self.err, self.has_bck,
+                 self.slit_height, self.slit_width,
+                )
+        return (Invertor, tuple(), state, None, None)
+
+    def __setattr__(self, name, value):
+        """
+        Set the value of an attribute.
+        Access the parent class methods for
+        x, y, err, d_max, q_min, q_max and alpha
+        """
+        if   name == 'x':
+            if 0.0 in value:
+                msg = "Invertor: one of your q-values is zero. "
+                msg += "Delete that entry before proceeding"
+                raise ValueError, msg
+            return self.set_x(value)
+        elif name == 'y':
+            return self.set_y(value)
+        elif name == 'err':
+            value2 = abs(value)
+            return self.set_err(value2)
+        elif name == 'd_max':
+            return self.set_dmax(value)
+        elif name == 'q_min':
+            if value == None:
+                return self.set_qmin(-1.0)
+            return self.set_qmin(value)
+        elif name == 'q_max':
+            if value == None:
+                return self.set_qmax(-1.0)
+            return self.set_qmax(value)
+        elif name == 'alpha':
+            return self.set_alpha(value)
+        elif name == 'slit_height':
+            return self.set_slit_height(value)
+        elif name == 'slit_width':
+            return self.set_slit_width(value)
+        elif name == 'has_bck':
+            if value == True:
+                return self.set_has_bck(1)
+            elif value == False:
+                return self.set_has_bck(0)
+            else:
+                raise ValueError, "Invertor: has_bck can only be True or False"
+
+        return Cinvertor.__setattr__(self, name, value)
+
+    def __getattr__(self, name):
+        """
+        Return the value of an attribute
+        """
+        #import numpy
+        if name == 'x':
+            out = numpy.ones(self.get_nx())
+            self.get_x(out)
+            return out
+        elif name == 'y':
+            out = numpy.ones(self.get_ny())
+            self.get_y(out)
+            return out
+        elif name == 'err':
+            out = numpy.ones(self.get_nerr())
+            self.get_err(out)
+            return out
+        elif name == 'd_max':
+            return self.get_dmax()
+        elif name == 'q_min':
+            qmin = self.get_qmin()
+            if qmin < 0:
+                return None
+            return qmin
+        elif name == 'q_max':
+            qmax = self.get_qmax()
+            if qmax < 0:
+                return None
+            return qmax
+        elif name == 'alpha':
+            return self.get_alpha()
+        elif name == 'slit_height':
+            return self.get_slit_height()
+        elif name == 'slit_width':
+            return self.get_slit_width()
+        elif name == 'has_bck':
+            value = self.get_has_bck()
+            if value == 1:
+                return True
+            else:
+                return False
+        elif name in self.__dict__:
+            return self.__dict__[name]
+        return None
+
+    def clone(self):
+        """
+        Return a clone of this instance
+        """
+        #import copy
+
+        invertor = Invertor()
+        invertor.chi2 = self.chi2
+        invertor.elapsed = self.elapsed
+        invertor.nfunc = self.nfunc
+        invertor.alpha = self.alpha
+        invertor.d_max = self.d_max
+        invertor.q_min = self.q_min
+        invertor.q_max = self.q_max
+
+        invertor.x = self.x
+        invertor.y = self.y
+        invertor.err = self.err
+        invertor.has_bck = self.has_bck
+        invertor.slit_height = self.slit_height
+        invertor.slit_width = self.slit_width
+
+        invertor.info = copy.deepcopy(self.info)
+
+        return invertor
+
+    def invert(self, nfunc=10, nr=20):
+        """
+        Perform inversion to P(r)
+
+        The problem is solved by posing the problem as  Ax = b,
+        where x is the set of coefficients we are looking for.
+
+        Npts is the number of points.
+
+        In the following i refers to the ith base function coefficient.
+        The matrix has its entries j in its first Npts rows set to ::
+
+            A[i][j] = (Fourier transformed base function for point j)
+
+        We them choose a number of r-points, n_r, to evaluate the second
+        derivative of P(r) at. This is used as our regularization term.
+        For a vector r of length n_r, the following n_r rows are set to ::
+
+            A[i+Npts][j] = (2nd derivative of P(r), d**2(P(r))/d(r)**2, evaluated at r[j])
+
+        The vector b has its first Npts entries set to ::
+
+            b[j] = (I(q) observed for point j)
+
+        The following n_r entries are set to zero.
+
+        The result is found by using scipy.linalg.basic.lstsq to invert
+        the matrix and find the coefficients x.
+
+        :param nfunc: number of base functions to use.
+        :param nr: number of r points to evaluate the 2nd derivative at for the reg. term.
+        :return: c_out, c_cov - the coefficients with covariance matrix
+        """
+        # Reset the background value before proceeding
+        self.background = 0.0
+        return self.lstsq(nfunc, nr=nr)
+
+    def iq(self, out, q):
+        """
+        Function to call to evaluate the scattering intensity
+
+        :param args: c-parameters, and q
+        :return: I(q)
+
+        """
+        return Cinvertor.iq(self, out, q) + self.background
+
+    def invert_optimize(self, nfunc=10, nr=20):
+        """
+        Slower version of the P(r) inversion that uses scipy.optimize.leastsq.
+
+        This probably produce more reliable results, but is much slower.
+        The minimization function is set to
+        sum_i[ (I_obs(q_i) - I_theo(q_i))/err**2 ] + alpha * reg_term,
+        where the reg_term is given by Svergun: it is the integral of
+        the square of the first derivative
+        of P(r), d(P(r))/dr, integrated over the full range of r.
+
+        :param nfunc: number of base functions to use.
+        :param nr: number of r points to evaluate the 2nd derivative at
+            for the reg. term.
+
+        :return: c_out, c_cov - the coefficients with covariance matrix
+
+        """
+        self.nfunc = nfunc
+        # First, check that the current data is valid
+        if self.is_valid() <= 0:
+            msg = "Invertor.invert: Data array are of different length"
+            raise RuntimeError, msg
+
+        p = numpy.ones(nfunc)
+        t_0 = time.time()
+        out, cov_x, _, _, _ = optimize.leastsq(self.residuals, p, full_output=1)
+
+        # Compute chi^2
+        res = self.residuals(out)
+        chisqr = 0
+        for i in range(len(res)):
+            chisqr += res[i]
+
+        self.chi2 = chisqr
+
+        # Store computation time
+        self.elapsed = time.time() - t_0
+
+        if cov_x is None:
+            cov_x = numpy.ones([nfunc, nfunc])
+            cov_x *= math.fabs(chisqr)
+        return out, cov_x
+
+    def pr_fit(self, nfunc=5):
+        """
+        This is a direct fit to a given P(r). It assumes that the y data
+        is set to some P(r) distribution that we are trying to reproduce
+        with a set of base functions.
+
+        This method is provided as a test.
+        """
+        # First, check that the current data is valid
+        if self.is_valid() <= 0:
+            msg = "Invertor.invert: Data arrays are of different length"
+            raise RuntimeError, msg
+
+        p = numpy.ones(nfunc)
+        t_0 = time.time()
+        out, cov_x, _, _, _ = optimize.leastsq(self.pr_residuals, p, full_output=1)
+
+        # Compute chi^2
+        res = self.pr_residuals(out)
+        chisqr = 0
+        for i in range(len(res)):
+            chisqr += res[i]
+
+        self.chisqr = chisqr
+
+        # Store computation time
+        self.elapsed = time.time() - t_0
+
+        return out, cov_x
+
+    def pr_err(self, c, c_cov, r):
+        """
+        Returns the value of P(r) for a given r, and base function
+        coefficients, with error.
+
+        :param c: base function coefficients
+        :param c_cov: covariance matrice of the base function coefficients
+        :param r: r-value to evaluate P(r) at
+
+        :return: P(r)
+
+        """
+        return self.get_pr_err(c, c_cov, r)
+
+    def _accept_q(self, q):
+        """
+        Check q-value against user-defined range
+        """
+        if not self.q_min == None and q < self.q_min:
+            return False
+        if not self.q_max == None and q > self.q_max:
+            return False
+        return True
+
+    def lstsq(self, nfunc=5, nr=20):
+        """
+        The problem is solved by posing the problem as  Ax = b,
+        where x is the set of coefficients we are looking for.
+
+        Npts is the number of points.
+
+        In the following i refers to the ith base function coefficient.
+        The matrix has its entries j in its first Npts rows set to ::
+
+            A[i][j] = (Fourier transformed base function for point j)
+
+        We them choose a number of r-points, n_r, to evaluate the second
+        derivative of P(r) at. This is used as our regularization term.
+        For a vector r of length n_r, the following n_r rows are set to ::
+
+            A[i+Npts][j] = (2nd derivative of P(r), d**2(P(r))/d(r)**2,
+            evaluated at r[j])
+
+        The vector b has its first Npts entries set to ::
+
+            b[j] = (I(q) observed for point j)
+
+        The following n_r entries are set to zero.
+
+        The result is found by using scipy.linalg.basic.lstsq to invert
+        the matrix and find the coefficients x.
+
+        :param nfunc: number of base functions to use.
+        :param nr: number of r points to evaluate the 2nd derivative at for the reg. term.
+
+        If the result does not allow us to compute the covariance matrix,
+        a matrix filled with zeros will be returned.
+
+        """
+        # Note: To make sure an array is contiguous:
+        # blah = numpy.ascontiguousarray(blah_original)
+        # ... before passing it to C
+
+        if self.is_valid() < 0:
+            msg = "Invertor: invalid data; incompatible data lengths."
+            raise RuntimeError, msg
+
+        self.nfunc = nfunc
+        # a -- An M x N matrix.
+        # b -- An M x nrhs matrix or M vector.
+        npts = len(self.x)
+        nq = nr
+        sqrt_alpha = math.sqrt(math.fabs(self.alpha))
+        if sqrt_alpha < 0.0:
+            nq = 0
+
+        # If we need to fit the background, add a term
+        if self.has_bck == True:
+            nfunc_0 = nfunc
+            nfunc += 1
+
+        a = numpy.zeros([npts + nq, nfunc])
+        b = numpy.zeros(npts + nq)
+        err = numpy.zeros([nfunc, nfunc])
+
+        # Construct the a matrix and b vector that represent the problem
+        t_0 = time.time()
+        try:
+            self._get_matrix(nfunc, nq, a, b)
+        except:
+            raise RuntimeError, "Invertor: could not invert I(Q)\n  %s" % sys.exc_value
+
+        # Perform the inversion (least square fit)
+        c, chi2, _, _ = lstsq(a, b)
+        # Sanity check
+        try:
+            float(chi2)
+        except:
+            chi2 = -1.0
+        self.chi2 = chi2
+
+        inv_cov = numpy.zeros([nfunc, nfunc])
+        # Get the covariance matrix, defined as inv_cov = a_transposed * a
+        self._get_invcov_matrix(nfunc, nr, a, inv_cov)
+
+        # Compute the reg term size for the output
+        sum_sig, sum_reg = self._get_reg_size(nfunc, nr, a)
+
+        if math.fabs(self.alpha) > 0:
+            new_alpha = sum_sig / (sum_reg / self.alpha)
+        else:
+            new_alpha = 0.0
+        self.suggested_alpha = new_alpha
+
+        try:
+            cov = numpy.linalg.pinv(inv_cov)
+            err = math.fabs(chi2 / float(npts - nfunc)) * cov
+        except:
+            # We were not able to estimate the errors
+            # Return an empty error matrix
+            logging.error(sys.exc_value)
+
+        # Keep a copy of the last output
+        if self.has_bck == False:
+            self.background = 0
+            self.out = c
+            self.cov = err
+        else:
+            self.background = c[0]
+
+            err_0 = numpy.zeros([nfunc, nfunc])
+            c_0 = numpy.zeros(nfunc)
+
+            for i in range(nfunc_0):
+                c_0[i] = c[i + 1]
+                for j in range(nfunc_0):
+                    err_0[i][j] = err[i + 1][j + 1]
+
+            self.out = c_0
+            self.cov = err_0
+
+        # Store computation time
+        self.elapsed = time.time() - t_0
+
+        return self.out, self.cov
+
+    def estimate_numterms(self, isquit_func=None):
+        """
+        Returns a reasonable guess for the
+        number of terms
+
+        :param isquit_func:
+          reference to thread function to call to check whether the computation needs to
+          be stopped.
+
+        :return: number of terms, alpha, message
+
+        """
+        from num_term import NTermEstimator
+        estimator = NTermEstimator(self.clone())
+        try:
+            return estimator.num_terms(isquit_func)
+        except:
+            # If we fail, estimate alpha and return the default
+            # number of terms
+            best_alpha, _, _ = self.estimate_alpha(self.nfunc)
+            logging.warning("Invertor.estimate_numterms: %s" % sys.exc_value)
+            return self.nfunc, best_alpha, "Could not estimate number of terms"
+
+    def estimate_alpha(self, nfunc):
+        """
+        Returns a reasonable guess for the
+        regularization constant alpha
+
+        :param nfunc: number of terms to use in the expansion.
+
+        :return: alpha, message, elapsed
+
+        where alpha is the estimate for alpha,
+        message is a message for the user,
+        elapsed is the computation time
+        """
+        #import time
+        try:
+            pr = self.clone()
+
+            # T_0 for computation time
+            starttime = time.time()
+            elapsed = 0
+
+            # If the current alpha is zero, try
+            # another value
+            if pr.alpha <= 0:
+                pr.alpha = 0.0001
+
+            # Perform inversion to find the largest alpha
+            out, _ = pr.invert(nfunc)
+            elapsed = time.time() - starttime
+            initial_alpha = pr.alpha
+            initial_peaks = pr.get_peaks(out)
+
+            # Try the inversion with the estimated alpha
+            pr.alpha = pr.suggested_alpha
+            out, _ = pr.invert(nfunc)
+
+            npeaks = pr.get_peaks(out)
+            # if more than one peak to start with
+            # just return the estimate
+            if npeaks > 1:
+                #message = "Your P(r) is not smooth,
+                #please check your inversion parameters"
+                message = None
+                return pr.suggested_alpha, message, elapsed
+            else:
+
+                # Look at smaller values
+                # We assume that for the suggested alpha, we have 1 peak
+                # if not, send a message to change parameters
+                alpha = pr.suggested_alpha
+                best_alpha = pr.suggested_alpha
+                found = False
+                for i in range(10):
+                    pr.alpha = (0.33) ** (i + 1) * alpha
+                    out, _ = pr.invert(nfunc)
+
+                    peaks = pr.get_peaks(out)
+                    if peaks > 1:
+                        found = True
+                        break
+                    best_alpha = pr.alpha
+
+                # If we didn't find a turning point for alpha and
+                # the initial alpha already had only one peak,
+                # just return that
+                if not found and initial_peaks == 1 and \
+                    initial_alpha < best_alpha:
+                    best_alpha = initial_alpha
+
+                # Check whether the size makes sense
+                message = ''
+
+                if not found:
+                    message = None
+                elif best_alpha >= 0.5 * pr.suggested_alpha:
+                    # best alpha is too big, return a
+                    # reasonable value
+                    message = "The estimated alpha for your system is too "
+                    message += "large. "
+                    message += "Try increasing your maximum distance."
+
+                return best_alpha, message, elapsed
+
+        except:
+            message = "Invertor.estimate_alpha: %s" % sys.exc_value
+            return 0, message, elapsed
+
+    def to_file(self, path, npts=100):
+        """
+        Save the state to a file that will be readable
+        by SliceView.
+
+        :param path: path of the file to write
+        :param npts: number of P(r) points to be written
+
+        """
+        file = open(path, 'w')
+        file.write("#d_max=%g\n" % self.d_max)
+        file.write("#nfunc=%g\n" % self.nfunc)
+        file.write("#alpha=%g\n" % self.alpha)
+        file.write("#chi2=%g\n" % self.chi2)
+        file.write("#elapsed=%g\n" % self.elapsed)
+        file.write("#qmin=%s\n" % str(self.q_min))
+        file.write("#qmax=%s\n" % str(self.q_max))
+        file.write("#slit_height=%g\n" % self.slit_height)
+        file.write("#slit_width=%g\n" % self.slit_width)
+        file.write("#background=%g\n" % self.background)
+        if self.has_bck == True:
+            file.write("#has_bck=1\n")
+        else:
+            file.write("#has_bck=0\n")
+        file.write("#alpha_estimate=%g\n" % self.suggested_alpha)
+        if not self.out == None:
+            if len(self.out) == len(self.cov):
+                for i in range(len(self.out)):
+                    file.write("#C_%i=%s+-%s\n" % (i, str(self.out[i]),
+                                                   str(self.cov[i][i])))
+        file.write("<r>  <Pr>  <dPr>\n")
+        r = numpy.arange(0.0, self.d_max, self.d_max / npts)
+
+        for r_i in r:
+            (value, err) = self.pr_err(self.out, self.cov, r_i)
+            file.write("%g  %g  %g\n" % (r_i, value, err))
+
+        file.close()
+
+    def from_file(self, path):
+        """
+        Load the state of the Invertor from a file,
+        to be able to generate P(r) from a set of
+        parameters.
+
+        :param path: path of the file to load
+
+        """
+        #import os
+        #import re
+        if os.path.isfile(path):
+            try:
+                fd = open(path, 'r')
+
+                buff = fd.read()
+                lines = buff.split('\n')
+                for line in lines:
+                    if line.startswith('#d_max='):
+                        toks = line.split('=')
+                        self.d_max = float(toks[1])
+                    elif line.startswith('#nfunc='):
+                        toks = line.split('=')
+                        self.nfunc = int(toks[1])
+                        self.out = numpy.zeros(self.nfunc)
+                        self.cov = numpy.zeros([self.nfunc, self.nfunc])
+                    elif line.startswith('#alpha='):
+                        toks = line.split('=')
+                        self.alpha = float(toks[1])
+                    elif line.startswith('#chi2='):
+                        toks = line.split('=')
+                        self.chi2 = float(toks[1])
+                    elif line.startswith('#elapsed='):
+                        toks = line.split('=')
+                        self.elapsed = float(toks[1])
+                    elif line.startswith('#alpha_estimate='):
+                        toks = line.split('=')
+                        self.suggested_alpha = float(toks[1])
+                    elif line.startswith('#qmin='):
+                        toks = line.split('=')
+                        try:
+                            self.q_min = float(toks[1])
+                        except:
+                            self.q_min = None
+                    elif line.startswith('#qmax='):
+                        toks = line.split('=')
+                        try:
+                            self.q_max = float(toks[1])
+                        except:
+                            self.q_max = None
+                    elif line.startswith('#slit_height='):
+                        toks = line.split('=')
+                        self.slit_height = float(toks[1])
+                    elif line.startswith('#slit_width='):
+                        toks = line.split('=')
+                        self.slit_width = float(toks[1])
+                    elif line.startswith('#background='):
+                        toks = line.split('=')
+                        self.background = float(toks[1])
+                    elif line.startswith('#has_bck='):
+                        toks = line.split('=')
+                        if int(toks[1]) == 1:
+                            self.has_bck = True
+                        else:
+                            self.has_bck = False
+
+                    # Now read in the parameters
+                    elif line.startswith('#C_'):
+                        toks = line.split('=')
+                        p = re.compile('#C_([0-9]+)')
+                        m = p.search(toks[0])
+                        toks2 = toks[1].split('+-')
+                        i = int(m.group(1))
+                        self.out[i] = float(toks2[0])
+
+                        self.cov[i][i] = float(toks2[1])
+
+            except:
+                msg = "Invertor.from_file: corrupted file\n%s" % sys.exc_value
+                raise RuntimeError, msg
+        else:
+            msg = "Invertor.from_file: '%s' is not a file" % str(path)
+            raise RuntimeError, msg
Index: src/sas/sascalc/pr/num_term.py
===================================================================
--- src/sas/sascalc/pr/num_term.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/num_term.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,197 @@
+import math
+import numpy
+import copy
+import sys
+import logging
+from sas.sascalc.pr.invertor import Invertor
+
+class NTermEstimator(object):
+    """
+    """
+    def __init__(self, invertor):
+        """
+        """
+        self.invertor = invertor
+        self.nterm_min = 10
+        self.nterm_max = len(self.invertor.x)
+        if self.nterm_max > 50:
+            self.nterm_max = 50
+        self.isquit_func = None
+
+        self.osc_list = []
+        self.err_list = []
+        self.alpha_list = []
+        self.mess_list = []
+        self.dataset = []
+
+    def is_odd(self, n):
+        """
+        """
+        return bool(n % 2)
+
+    def sort_osc(self):
+        """
+        """
+        #import copy
+        osc = copy.deepcopy(self.dataset)
+        lis = []
+        for i in range(len(osc)):
+            osc.sort()
+            re = osc.pop(0)
+            lis.append(re)
+        return lis
+
+    def median_osc(self):
+        """
+        """
+        osc = self.sort_osc()
+        dv = len(osc)
+        med = float(dv) / 2.0
+        odd = self.is_odd(dv)
+        medi = 0
+        for i in range(dv):
+            if odd == True:
+                medi = osc[int(med)]
+            else:
+                medi = osc[int(med) - 1]
+        return medi
+
+    def get0_out(self):
+        """
+        """
+        inver = self.invertor
+        self.osc_list = []
+        self.err_list = []
+        self.alpha_list = []
+        for k in range(self.nterm_min, self.nterm_max, 1):
+            if self.isquit_func != None:
+                self.isquit_func()
+            best_alpha, message, _ = inver.estimate_alpha(k)
+            inver.alpha = best_alpha
+            inver.out, inver.cov = inver.lstsq(k)
+            osc = inver.oscillations(inver.out)
+            err = inver.get_pos_err(inver.out, inver.cov)
+            if osc > 10.0:
+                break
+            self.osc_list.append(osc)
+            self.err_list.append(err)
+            self.alpha_list.append(inver.alpha)
+            self.mess_list.append(message)
+
+        new_osc1 = []
+        new_osc2 = []
+        new_osc3 = []
+        flag9 = False
+        flag8 = False
+        for i in range(len(self.err_list)):
+            if self.err_list[i] <= 1.0 and self.err_list[i] >= 0.9:
+                new_osc1.append(self.osc_list[i])
+                flag9 = True
+            if self.err_list[i] < 0.9 and self.err_list[i] >= 0.8:
+                new_osc2.append(self.osc_list[i])
+                flag8 = True
+            if self.err_list[i] < 0.8 and self.err_list[i] >= 0.7:
+                new_osc3.append(self.osc_list[i])
+
+        if flag9 == True:
+            self.dataset = new_osc1
+        elif flag8 == True:
+            self.dataset = new_osc2
+        else:
+            self.dataset = new_osc3
+
+        return self.dataset
+
+    def ls_osc(self):
+        """
+        """
+        # Generate data
+        self.get0_out()
+        med = self.median_osc()
+
+        #TODO: check 1
+        ls_osc = self.dataset
+        ls = []
+        for i in range(len(ls_osc)):
+            if int(med) == int(ls_osc[i]):
+                ls.append(ls_osc[i])
+        return ls
+
+    def compare_err(self):
+        """
+        """
+        ls = self.ls_osc()
+        nt_ls = []
+        for i in range(len(ls)):
+            r = ls[i]
+            n = self.osc_list.index(r) + 10
+            nt_ls.append(n)
+        return nt_ls
+
+    def num_terms(self, isquit_func=None):
+        """
+        """
+        try:
+            self.isquit_func = isquit_func
+            nts = self.compare_err()
+            div = len(nts)
+            tem = float(div) / 2.0
+            odd = self.is_odd(div)
+            if odd == True:
+                nt = nts[int(tem)]
+            else:
+                nt = nts[int(tem) - 1]
+            return nt, self.alpha_list[nt - 10], self.mess_list[nt - 10]
+        except:
+            #TODO: check the logic above and make sure it doesn't 
+            # rely on the try-except.
+            return self.nterm_min, self.invertor.alpha, ''
+
+
+#For testing
+def load(path):
+    # Read the data from the data file
+    data_x = numpy.zeros(0)
+    data_y = numpy.zeros(0)
+    data_err = numpy.zeros(0)
+    scale = None
+    min_err = 0.0
+    if not path == None:
+        input_f = open(path, 'r')
+        buff = input_f.read()
+        lines = buff.split('\n')
+        for line in lines:
+            try:
+                toks = line.split()
+                test_x = float(toks[0])
+                test_y = float(toks[1])
+                if len(toks) > 2:
+                    err = float(toks[2])
+                else:
+                    if scale == None:
+                        scale = 0.05 * math.sqrt(test_y)
+                        #scale = 0.05/math.sqrt(y)
+                        min_err = 0.01 * y
+                    err = scale * math.sqrt(test_y) + min_err
+                    #err = 0
+
+                data_x = numpy.append(data_x, test_x)
+                data_y = numpy.append(data_y, test_y)
+                data_err = numpy.append(data_err, err)
+            except:
+                logging.error(sys.exc_value)
+
+    return data_x, data_y, data_err
+
+
+if __name__ == "__main__":
+    invert = Invertor()
+    x, y, erro = load("test/Cyl_A_D102.txt")
+    invert.d_max = 102.0
+    invert.nfunc = 10
+    invert.x = x
+    invert.y = y
+    invert.err = erro
+    # Testing estimator
+    est = NTermEstimator(invert)
+    print est.num_terms()
Index: src/sas/sascalc/pr/release_notes.txt
===================================================================
--- src/sas/sascalc/pr/release_notes.txt	(revision b699768550de9d4b35b087234ec1d2a461922bab)
+++ src/sas/sascalc/pr/release_notes.txt	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -0,0 +1,64 @@
+Release Notes
+=============
+
+Package name: pr_inversion 0.2
+
+1- 	Version 0.2.2
+	- Release date: 7/4/2009
+	- Minor fixes
+	  
+ 	Version 0.2.1
+	- Release date: 7/8/2008
+	- Minor change to clone method
+	  
+	Version 0.2.0
+	
+	- Release date: 7/4/2008
+	- Number of term estimator
+	- Faster implementation
+
+	Version 0.1.0
+
+	- Release date: 5/28/2008
+	- Provide python module sas.pr
+
+2- Downloading and Installing
+
+	2.1- System Requirements:
+		- Python version >= 2.4 should be running on the system
+
+	2.2- Installing:
+		- Get the code from svn://danse.us/sas/releases/pr_inversion-0.1.0
+		- The following modules are required:
+			* matplotlib
+			* numpy
+			* scipy
+
+3- Known Issues
+
+	3.1- All systems:
+		- None
+
+	3.2- Windows:
+		- None
+
+	3.3- Linux:
+		- None
+
+4- Troubleshooting
+
+	- None
+
+5- Frequently Asked Questions
+
+	- None
+
+6- Other Resources
+
+	- See: http://danse.chem.utk.edu/prview.html
+
+	
+
+	
+
+
Index: test/pr_inversion/test/test_module.py
===================================================================
--- test/pr_inversion/test/test_module.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/pr_inversion/test/test_module.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -1,3 +1,3 @@
-from sas.pr.core.pr_inversion import Cinvertor
+from sas.sascalc.pr.core.pr_inversion import Cinvertor
 import numpy
 
Index: test/pr_inversion/test/utest_explorer.py
===================================================================
--- test/pr_inversion/test/utest_explorer.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/pr_inversion/test/utest_explorer.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -5,6 +5,6 @@
 import unittest, math, numpy
 from utest_invertor import load
-from sas.pr.invertor import Invertor
-from sas.pr.distance_explorer import DistExplorer
+from sas.sascalc.pr.invertor import Invertor
+from sas.sascalc.pr.distance_explorer import DistExplorer
         
 class TestExplorer(unittest.TestCase):
Index: test/pr_inversion/test/utest_invertor.py
===================================================================
--- test/pr_inversion/test/utest_invertor.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/pr_inversion/test/utest_invertor.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -9,5 +9,5 @@
 
 import unittest, math, numpy
-from sas.pr.invertor import Invertor
+from sas.sascalc.pr.invertor import Invertor
         
 class TestFiguresOfMerit(unittest.TestCase):
Index: test/sascalculator/test/utest_resolution_calculator.py
===================================================================
--- test/sascalculator/test/utest_resolution_calculator.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sascalculator/test/utest_resolution_calculator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -4,5 +4,5 @@
 
 import unittest
-from  sas.calculator.resolution_calculator import ResolutionCalculator \
+from  sas.sascalc.calculator.resolution_calculator import ResolutionCalculator \
                                             as calculator
 
Index: test/sascalculator/test/utest_sas_gen.py
===================================================================
--- test/sascalculator/test/utest_sas_gen.py	(revision fd5ac0db8089df5bd882587f157d4bf317cfcf67)
+++ test/sascalculator/test/utest_sas_gen.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,5 +6,5 @@
 
 import unittest
-from sas.calculator import sas_gen
+from sas.sascalc.calculator import sas_gen
 from sas.models.SphereModel import SphereModel
 
Index: test/sascalculator/test/utest_slit_length_calculator.py
===================================================================
--- test/sascalculator/test/utest_slit_length_calculator.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sascalculator/test/utest_slit_length_calculator.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -4,6 +4,6 @@
 
 import unittest
-from sas.dataloader.readers.ascii_reader import Reader
-from  sas.calculator.slit_length_calculator import SlitlengthCalculator as calculator
+from sas.sascalc.dataloader.readers.ascii_reader import Reader
+from  sas.sascalc.calculator.slit_length_calculator import SlitlengthCalculator as calculator
 
 import os.path
Index: test/sasdataloader/plugins/test_reader.py
===================================================================
--- test/sasdataloader/plugins/test_reader.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sasdataloader/plugins/test_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -9,5 +9,5 @@
 """
 import numpy, os
-from sas.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.data_info import Data1D
 
 
Index: test/sasdataloader/test/error_conditions.py
===================================================================
--- test/sasdataloader/test/error_conditions.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sasdataloader/test/error_conditions.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -1,3 +1,3 @@
-from sas.dataloader.loader import Loader
+from sas.sascalc.dataloader.loader import Loader
 import unittest
 
Index: test/sasdataloader/test/isis_1_0_write_test.xml
===================================================================
--- test/sasdataloader/test/isis_1_0_write_test.xml	(revision 400155bab01bdb35b3cf83069f57cf9f47005bca)
+++ test/sasdataloader/test/isis_1_0_write_test.xml	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -1,865 +1,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<?xml-stylesheet type="text/xsl" href="cansasxml-html.xsl" ?>
-<SASroot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="cansas1d/1.0" version="1.0" xsi:schemaLocation="cansas1d/1.0 http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd">
-  <SASentry>
-    <Title>TK49 c10_SANS</Title>
-    <Run>79680</Run>
-    <SASdata>
-      <Idata>
-        <Q unit="1/A">0.009</Q>
-        <I unit="1/cm">85.3333</I>
-        <Idev unit="1/cm">0.852491</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.011</Q>
-        <I unit="1/cm">72.5398</I>
-        <Idev unit="1/cm">0.639548</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.013</Q>
-        <I unit="1/cm">63.3582</I>
-        <Idev unit="1/cm">0.494127</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.015</Q>
-        <I unit="1/cm">57.0952</I>
-        <Idev unit="1/cm">0.414116</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.017</Q>
-        <I unit="1/cm">51.0139</I>
-        <Idev unit="1/cm">0.35145</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.019</Q>
-        <I unit="1/cm">46.4746</I>
-        <Idev unit="1/cm">0.308755</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.021</Q>
-        <I unit="1/cm">41.7191</I>
-        <Idev unit="1/cm">0.271641</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.023</Q>
-        <I unit="1/cm">37.4208</I>
-        <Idev unit="1/cm">0.244658</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.025</Q>
-        <I unit="1/cm">33.7352</I>
-        <Idev unit="1/cm">0.219594</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.027</Q>
-        <I unit="1/cm">30.6073</I>
-        <Idev unit="1/cm">0.203138</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.029</Q>
-        <I unit="1/cm">27.7309</I>
-        <Idev unit="1/cm">0.185859</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.031</Q>
-        <I unit="1/cm">25.2579</I>
-        <Idev unit="1/cm">0.171356</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.033</Q>
-        <I unit="1/cm">22.7712</I>
-        <Idev unit="1/cm">0.159062</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.035</Q>
-        <I unit="1/cm">20.5323</I>
-        <Idev unit="1/cm">0.146135</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.037</Q>
-        <I unit="1/cm">19.0744</I>
-        <Idev unit="1/cm">0.136393</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.039</Q>
-        <I unit="1/cm">17.2832</I>
-        <Idev unit="1/cm">0.127623</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.041</Q>
-        <I unit="1/cm">15.7399</I>
-        <Idev unit="1/cm">0.118288</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.043</Q>
-        <I unit="1/cm">14.5598</I>
-        <Idev unit="1/cm">0.111759</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.045</Q>
-        <I unit="1/cm">13.5906</I>
-        <Idev unit="1/cm">0.105148</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.047</Q>
-        <I unit="1/cm">12.6707</I>
-        <Idev unit="1/cm">0.0996579</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.049</Q>
-        <I unit="1/cm">11.5247</I>
-        <Idev unit="1/cm">0.0929646</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.051</Q>
-        <I unit="1/cm">10.7089</I>
-        <Idev unit="1/cm">0.0888575</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.053</Q>
-        <I unit="1/cm">10.1436</I>
-        <Idev unit="1/cm">0.0849394</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.055</Q>
-        <I unit="1/cm">9.49446</I>
-        <Idev unit="1/cm">0.0811205</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.057</Q>
-        <I unit="1/cm">8.80775</I>
-        <Idev unit="1/cm">0.0773967</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.059</Q>
-        <I unit="1/cm">8.33366</I>
-        <Idev unit="1/cm">0.0748147</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.061</Q>
-        <I unit="1/cm">7.79157</I>
-        <Idev unit="1/cm">0.0720107</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.063</Q>
-        <I unit="1/cm">7.25902</I>
-        <Idev unit="1/cm">0.0693582</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.065</Q>
-        <I unit="1/cm">7.04789</I>
-        <Idev unit="1/cm">0.0677591</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.067</Q>
-        <I unit="1/cm">6.53311</I>
-        <Idev unit="1/cm">0.0652207</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.069</Q>
-        <I unit="1/cm">6.2453</I>
-        <Idev unit="1/cm">0.0642016</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.071</Q>
-        <I unit="1/cm">5.94152</I>
-        <Idev unit="1/cm">0.0625768</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.073</Q>
-        <I unit="1/cm">5.70816</I>
-        <Idev unit="1/cm">0.0612955</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.075</Q>
-        <I unit="1/cm">5.24449</I>
-        <Idev unit="1/cm">0.060141</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.077</Q>
-        <I unit="1/cm">5.08291</I>
-        <Idev unit="1/cm">0.0582239</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.079</Q>
-        <I unit="1/cm">4.88135</I>
-        <Idev unit="1/cm">0.057969</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.081</Q>
-        <I unit="1/cm">4.60942</I>
-        <Idev unit="1/cm">0.056813</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.083</Q>
-        <I unit="1/cm">4.46606</I>
-        <Idev unit="1/cm">0.0569381</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.085</Q>
-        <I unit="1/cm">4.32473</I>
-        <Idev unit="1/cm">0.0558766</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.087</Q>
-        <I unit="1/cm">4.14144</I>
-        <Idev unit="1/cm">0.0552525</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.089</Q>
-        <I unit="1/cm">3.86504</I>
-        <Idev unit="1/cm">0.0544569</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.091</Q>
-        <I unit="1/cm">3.74272</I>
-        <Idev unit="1/cm">0.0529596</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.093</Q>
-        <I unit="1/cm">3.68281</I>
-        <Idev unit="1/cm">0.052891</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.095</Q>
-        <I unit="1/cm">3.46991</I>
-        <Idev unit="1/cm">0.0517565</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.097</Q>
-        <I unit="1/cm">3.37244</I>
-        <Idev unit="1/cm">0.0509328</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.099</Q>
-        <I unit="1/cm">3.27074</I>
-        <Idev unit="1/cm">0.0505183</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.101</Q>
-        <I unit="1/cm">3.13326</I>
-        <Idev unit="1/cm">0.0490847</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.103</Q>
-        <I unit="1/cm">3.01387</I>
-        <Idev unit="1/cm">0.0487012</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.105</Q>
-        <I unit="1/cm">2.87628</I>
-        <Idev unit="1/cm">0.0484307</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.107</Q>
-        <I unit="1/cm">2.85043</I>
-        <Idev unit="1/cm">0.0476786</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.109</Q>
-        <I unit="1/cm">2.72799</I>
-        <Idev unit="1/cm">0.0475526</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.111</Q>
-        <I unit="1/cm">2.61206</I>
-        <Idev unit="1/cm">0.0467475</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.113</Q>
-        <I unit="1/cm">2.58895</I>
-        <Idev unit="1/cm">0.0469767</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.115</Q>
-        <I unit="1/cm">2.45987</I>
-        <Idev unit="1/cm">0.0460139</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.117</Q>
-        <I unit="1/cm">2.37723</I>
-        <Idev unit="1/cm">0.045272</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.119</Q>
-        <I unit="1/cm">2.34122</I>
-        <Idev unit="1/cm">0.0453741</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.121</Q>
-        <I unit="1/cm">2.21745</I>
-        <Idev unit="1/cm">0.0444485</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.123</Q>
-        <I unit="1/cm">2.12646</I>
-        <Idev unit="1/cm">0.0438385</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.125</Q>
-        <I unit="1/cm">2.11264</I>
-        <Idev unit="1/cm">0.0441662</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.127</Q>
-        <I unit="1/cm">2.07523</I>
-        <Idev unit="1/cm">0.0438979</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.129</Q>
-        <I unit="1/cm">2.03548</I>
-        <Idev unit="1/cm">0.0442422</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.131</Q>
-        <I unit="1/cm">1.92452</I>
-        <Idev unit="1/cm">0.0435959</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.133</Q>
-        <I unit="1/cm">1.90866</I>
-        <Idev unit="1/cm">0.0437401</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.135</Q>
-        <I unit="1/cm">1.91252</I>
-        <Idev unit="1/cm">0.0443163</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.137</Q>
-        <I unit="1/cm">1.80405</I>
-        <Idev unit="1/cm">0.043466</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.139</Q>
-        <I unit="1/cm">1.72166</I>
-        <Idev unit="1/cm">0.0429246</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.141</Q>
-        <I unit="1/cm">1.67199</I>
-        <Idev unit="1/cm">0.042469</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.143</Q>
-        <I unit="1/cm">1.63282</I>
-        <Idev unit="1/cm">0.0430074</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.145</Q>
-        <I unit="1/cm">1.63712</I>
-        <Idev unit="1/cm">0.0438557</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.147</Q>
-        <I unit="1/cm">1.5862</I>
-        <Idev unit="1/cm">0.0434135</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.149</Q>
-        <I unit="1/cm">1.56121</I>
-        <Idev unit="1/cm">0.0432591</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.151</Q>
-        <I unit="1/cm">1.49764</I>
-        <Idev unit="1/cm">0.0426577</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.153</Q>
-        <I unit="1/cm">1.53828</I>
-        <Idev unit="1/cm">0.0446481</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.155</Q>
-        <I unit="1/cm">1.47366</I>
-        <Idev unit="1/cm">0.0441973</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.157</Q>
-        <I unit="1/cm">1.40582</I>
-        <Idev unit="1/cm">0.0430461</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.159</Q>
-        <I unit="1/cm">1.40491</I>
-        <Idev unit="1/cm">0.0439883</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.161</Q>
-        <I unit="1/cm">1.34639</I>
-        <Idev unit="1/cm">0.044193</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.163</Q>
-        <I unit="1/cm">1.32238</I>
-        <Idev unit="1/cm">0.0438008</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.165</Q>
-        <I unit="1/cm">1.33885</I>
-        <Idev unit="1/cm">0.0445383</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.167</Q>
-        <I unit="1/cm">1.29608</I>
-        <Idev unit="1/cm">0.0451575</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.169</Q>
-        <I unit="1/cm">1.30562</I>
-        <Idev unit="1/cm">0.0460656</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.171</Q>
-        <I unit="1/cm">1.17147</I>
-        <Idev unit="1/cm">0.0435717</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.173</Q>
-        <I unit="1/cm">1.18565</I>
-        <Idev unit="1/cm">0.0449072</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.175</Q>
-        <I unit="1/cm">1.18944</I>
-        <Idev unit="1/cm">0.0465319</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.177</Q>
-        <I unit="1/cm">1.17055</I>
-        <Idev unit="1/cm">0.0456562</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.179</Q>
-        <I unit="1/cm">1.08571</I>
-        <Idev unit="1/cm">0.0453852</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.181</Q>
-        <I unit="1/cm">1.06208</I>
-        <Idev unit="1/cm">0.0463335</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.183</Q>
-        <I unit="1/cm">1.06187</I>
-        <Idev unit="1/cm">0.0458284</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.185</Q>
-        <I unit="1/cm">1.02727</I>
-        <Idev unit="1/cm">0.0472613</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.187</Q>
-        <I unit="1/cm">0.995169</I>
-        <Idev unit="1/cm">0.0465526</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.189</Q>
-        <I unit="1/cm">1.02569</I>
-        <Idev unit="1/cm">0.0483728</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.191</Q>
-        <I unit="1/cm">0.981154</I>
-        <Idev unit="1/cm">0.0490559</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.193</Q>
-        <I unit="1/cm">1.01831</I>
-        <Idev unit="1/cm">0.0502348</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.195</Q>
-        <I unit="1/cm">0.968102</I>
-        <Idev unit="1/cm">0.0504426</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.197</Q>
-        <I unit="1/cm">0.940656</I>
-        <Idev unit="1/cm">0.0512734</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.199</Q>
-        <I unit="1/cm">1.05762</I>
-        <Idev unit="1/cm">0.0547469</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.201</Q>
-        <I unit="1/cm">0.949872</I>
-        <Idev unit="1/cm">0.055229</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.203</Q>
-        <I unit="1/cm">0.891716</I>
-        <Idev unit="1/cm">0.0531346</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.205</Q>
-        <I unit="1/cm">0.876084</I>
-        <Idev unit="1/cm">0.0548681</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.207</Q>
-        <I unit="1/cm">0.932101</I>
-        <Idev unit="1/cm">0.0576812</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.209</Q>
-        <I unit="1/cm">0.927755</I>
-        <Idev unit="1/cm">0.0593404</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.211</Q>
-        <I unit="1/cm">0.87669</I>
-        <Idev unit="1/cm">0.0600369</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.213</Q>
-        <I unit="1/cm">0.809125</I>
-        <Idev unit="1/cm">0.0588107</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.215</Q>
-        <I unit="1/cm">0.941202</I>
-        <Idev unit="1/cm">0.0659121</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.217</Q>
-        <I unit="1/cm">0.946175</I>
-        <Idev unit="1/cm">0.0690784</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.219</Q>
-        <I unit="1/cm">0.859875</I>
-        <Idev unit="1/cm">0.0674435</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.221</Q>
-        <I unit="1/cm">0.959508</I>
-        <Idev unit="1/cm">0.0718856</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.223</Q>
-        <I unit="1/cm">0.763222</I>
-        <Idev unit="1/cm">0.0702866</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.225</Q>
-        <I unit="1/cm">0.785467</I>
-        <Idev unit="1/cm">0.0742144</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.227</Q>
-        <I unit="1/cm">0.81876</I>
-        <Idev unit="1/cm">0.074266</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.229</Q>
-        <I unit="1/cm">0.678311</I>
-        <Idev unit="1/cm">0.0722578</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.231</Q>
-        <I unit="1/cm">0.761442</I>
-        <Idev unit="1/cm">0.0814516</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.233</Q>
-        <I unit="1/cm">0.757076</I>
-        <Idev unit="1/cm">0.0825262</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.235</Q>
-        <I unit="1/cm">0.600636</I>
-        <Idev unit="1/cm">0.075041</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.237</Q>
-        <I unit="1/cm">0.685635</I>
-        <Idev unit="1/cm">0.0842392</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.239</Q>
-        <I unit="1/cm">0.799186</I>
-        <Idev unit="1/cm">0.0974365</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.241</Q>
-        <I unit="1/cm">0.737526</I>
-        <Idev unit="1/cm">0.0957286</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.243</Q>
-        <I unit="1/cm">0.716415</I>
-        <Idev unit="1/cm">0.0970448</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.245</Q>
-        <I unit="1/cm">0.720413</I>
-        <Idev unit="1/cm">0.0981996</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.247</Q>
-        <I unit="1/cm">0.604059</I>
-        <Idev unit="1/cm">0.100869</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.249</Q>
-        <I unit="1/cm">0.649073</I>
-        <Idev unit="1/cm">0.109281</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.251</Q>
-        <I unit="1/cm">0.700691</I>
-        <Idev unit="1/cm">0.114325</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.253</Q>
-        <I unit="1/cm">0.586789</I>
-        <Idev unit="1/cm">0.108324</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.255</Q>
-        <I unit="1/cm">0.765838</I>
-        <Idev unit="1/cm">0.139579</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.257</Q>
-        <I unit="1/cm">0.828968</I>
-        <Idev unit="1/cm">0.156723</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.259</Q>
-        <I unit="1/cm">0.616499</I>
-        <Idev unit="1/cm">0.137866</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.261</Q>
-        <I unit="1/cm">0.526011</I>
-        <Idev unit="1/cm">0.126209</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.263</Q>
-        <I unit="1/cm">0.51277</I>
-        <Idev unit="1/cm">0.126339</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.265</Q>
-        <I unit="1/cm">0.604189</I>
-        <Idev unit="1/cm">0.166393</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.267</Q>
-        <I unit="1/cm">0.738765</I>
-        <Idev unit="1/cm">0.194006</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.269</Q>
-        <I unit="1/cm">0.462803</I>
-        <Idev unit="1/cm">0.155354</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.271</Q>
-        <I unit="1/cm">0.478694</I>
-        <Idev unit="1/cm">0.171676</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.273</Q>
-        <I unit="1/cm">0.711983</I>
-        <Idev unit="1/cm">0.255539</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.275</Q>
-        <I unit="1/cm">0.473304</I>
-        <Idev unit="1/cm">0.230752</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.277</Q>
-        <I unit="1/cm">0.536817</I>
-        <Idev unit="1/cm">0.262627</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.279</Q>
-        <I unit="1/cm">0.660654</I>
-        <Idev unit="1/cm">0.297519</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.281</Q>
-        <I unit="1/cm">0.408902</I>
-        <Idev unit="1/cm">0.24585</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.283</Q>
-        <I unit="1/cm">0.0</I>
-        <Idev unit="1/cm">0.0</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-    </SASdata>
-    <SASsample name="">
-      <ID>TK49 c10_SANS</ID>
-    </SASsample>
-    <SASinstrument>
-      <name>LOQ</name>
-      <SASsource>
-        <radiation>neutron</radiation>
-      </SASsource>
-      <SAScollimation/>
-      <SASdetector>
-        <name>HAB</name>
-        <SDD unit="mm">575.0</SDD>
-      </SASdetector>
-      <SASdetector>
-        <name>main-detector-bank</name>
-        <SDD unit="mm">4145.02</SDD>
-      </SASdetector>
-    </SASinstrument>
-    <SASprocess>
-      <name>Mantid generated CanSAS1D XML</name>
-      <date>02-Aug-2013 16:54:14</date>
-      <description></description>
-      <term name="svn">2.5.3</term>
-      <term name="user_file">K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt</term>
-      <SASprocessnote></SASprocessnote>
-    </SASprocess>
-    <SASnote></SASnote>
-  </SASentry>
-</SASroot>
Index: test/sasdataloader/test/isis_1_1_write_test.xml
===================================================================
--- test/sasdataloader/test/isis_1_1_write_test.xml	(revision 400155bab01bdb35b3cf83069f57cf9f47005bca)
+++ test/sasdataloader/test/isis_1_1_write_test.xml	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -1,1557 +1,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<?xml-stylesheet type="text/xsl" href="cansas1d.xsl" ?>
-<SASroot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:cansas1d:1.1" version="1.1" xsi:schemaLocation="urn:cansas1d:1.1 http://www.cansas.org/formats/1.1/cansas1d.xsd">
-  <SASentry>
-    <Title>TK49 c10_SANS</Title>
-    <Run>79680</Run>
-    <SASdata>
-      <Idata>
-        <Q unit="1/A">0.009</Q>
-        <I unit="1/cm">85.3333</I>
-        <Idev unit="1/cm">0.852491</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.011</Q>
-        <I unit="1/cm">72.5398</I>
-        <Idev unit="1/cm">0.639548</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.013</Q>
-        <I unit="1/cm">63.3582</I>
-        <Idev unit="1/cm">0.494127</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.015</Q>
-        <I unit="1/cm">57.0952</I>
-        <Idev unit="1/cm">0.414116</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.017</Q>
-        <I unit="1/cm">51.0139</I>
-        <Idev unit="1/cm">0.35145</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.019</Q>
-        <I unit="1/cm">46.4746</I>
-        <Idev unit="1/cm">0.308755</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.021</Q>
-        <I unit="1/cm">41.7191</I>
-        <Idev unit="1/cm">0.271641</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.023</Q>
-        <I unit="1/cm">37.4208</I>
-        <Idev unit="1/cm">0.244658</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.025</Q>
-        <I unit="1/cm">33.7352</I>
-        <Idev unit="1/cm">0.219594</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.027</Q>
-        <I unit="1/cm">30.6073</I>
-        <Idev unit="1/cm">0.203138</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.029</Q>
-        <I unit="1/cm">27.7309</I>
-        <Idev unit="1/cm">0.185859</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.031</Q>
-        <I unit="1/cm">25.2579</I>
-        <Idev unit="1/cm">0.171356</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.033</Q>
-        <I unit="1/cm">22.7712</I>
-        <Idev unit="1/cm">0.159062</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.035</Q>
-        <I unit="1/cm">20.5323</I>
-        <Idev unit="1/cm">0.146135</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.037</Q>
-        <I unit="1/cm">19.0744</I>
-        <Idev unit="1/cm">0.136393</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.039</Q>
-        <I unit="1/cm">17.2832</I>
-        <Idev unit="1/cm">0.127623</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.041</Q>
-        <I unit="1/cm">15.7399</I>
-        <Idev unit="1/cm">0.118288</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.043</Q>
-        <I unit="1/cm">14.5598</I>
-        <Idev unit="1/cm">0.111759</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.045</Q>
-        <I unit="1/cm">13.5906</I>
-        <Idev unit="1/cm">0.105148</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.047</Q>
-        <I unit="1/cm">12.6707</I>
-        <Idev unit="1/cm">0.0996579</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.049</Q>
-        <I unit="1/cm">11.5247</I>
-        <Idev unit="1/cm">0.0929646</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.051</Q>
-        <I unit="1/cm">10.7089</I>
-        <Idev unit="1/cm">0.0888575</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.053</Q>
-        <I unit="1/cm">10.1436</I>
-        <Idev unit="1/cm">0.0849394</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.055</Q>
-        <I unit="1/cm">9.49446</I>
-        <Idev unit="1/cm">0.0811205</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.057</Q>
-        <I unit="1/cm">8.80775</I>
-        <Idev unit="1/cm">0.0773967</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.059</Q>
-        <I unit="1/cm">8.33366</I>
-        <Idev unit="1/cm">0.0748147</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.061</Q>
-        <I unit="1/cm">7.79157</I>
-        <Idev unit="1/cm">0.0720107</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.063</Q>
-        <I unit="1/cm">7.25902</I>
-        <Idev unit="1/cm">0.0693582</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.065</Q>
-        <I unit="1/cm">7.04789</I>
-        <Idev unit="1/cm">0.0677591</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.067</Q>
-        <I unit="1/cm">6.53311</I>
-        <Idev unit="1/cm">0.0652207</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.069</Q>
-        <I unit="1/cm">6.2453</I>
-        <Idev unit="1/cm">0.0642016</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.071</Q>
-        <I unit="1/cm">5.94152</I>
-        <Idev unit="1/cm">0.0625768</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.073</Q>
-        <I unit="1/cm">5.70816</I>
-        <Idev unit="1/cm">0.0612955</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.075</Q>
-        <I unit="1/cm">5.24449</I>
-        <Idev unit="1/cm">0.060141</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.077</Q>
-        <I unit="1/cm">5.08291</I>
-        <Idev unit="1/cm">0.0582239</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.079</Q>
-        <I unit="1/cm">4.88135</I>
-        <Idev unit="1/cm">0.057969</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.081</Q>
-        <I unit="1/cm">4.60942</I>
-        <Idev unit="1/cm">0.056813</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.083</Q>
-        <I unit="1/cm">4.46606</I>
-        <Idev unit="1/cm">0.0569381</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.085</Q>
-        <I unit="1/cm">4.32473</I>
-        <Idev unit="1/cm">0.0558766</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.087</Q>
-        <I unit="1/cm">4.14144</I>
-        <Idev unit="1/cm">0.0552525</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.089</Q>
-        <I unit="1/cm">3.86504</I>
-        <Idev unit="1/cm">0.0544569</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.091</Q>
-        <I unit="1/cm">3.74272</I>
-        <Idev unit="1/cm">0.0529596</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.093</Q>
-        <I unit="1/cm">3.68281</I>
-        <Idev unit="1/cm">0.052891</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.095</Q>
-        <I unit="1/cm">3.46991</I>
-        <Idev unit="1/cm">0.0517565</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.097</Q>
-        <I unit="1/cm">3.37244</I>
-        <Idev unit="1/cm">0.0509328</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.099</Q>
-        <I unit="1/cm">3.27074</I>
-        <Idev unit="1/cm">0.0505183</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.101</Q>
-        <I unit="1/cm">3.13326</I>
-        <Idev unit="1/cm">0.0490847</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.103</Q>
-        <I unit="1/cm">3.01387</I>
-        <Idev unit="1/cm">0.0487012</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.105</Q>
-        <I unit="1/cm">2.87628</I>
-        <Idev unit="1/cm">0.0484307</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.107</Q>
-        <I unit="1/cm">2.85043</I>
-        <Idev unit="1/cm">0.0476786</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.109</Q>
-        <I unit="1/cm">2.72799</I>
-        <Idev unit="1/cm">0.0475526</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.111</Q>
-        <I unit="1/cm">2.61206</I>
-        <Idev unit="1/cm">0.0467475</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.113</Q>
-        <I unit="1/cm">2.58895</I>
-        <Idev unit="1/cm">0.0469767</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.115</Q>
-        <I unit="1/cm">2.45987</I>
-        <Idev unit="1/cm">0.0460139</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.117</Q>
-        <I unit="1/cm">2.37723</I>
-        <Idev unit="1/cm">0.045272</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.119</Q>
-        <I unit="1/cm">2.34122</I>
-        <Idev unit="1/cm">0.0453741</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.121</Q>
-        <I unit="1/cm">2.21745</I>
-        <Idev unit="1/cm">0.0444485</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.123</Q>
-        <I unit="1/cm">2.12646</I>
-        <Idev unit="1/cm">0.0438385</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.125</Q>
-        <I unit="1/cm">2.11264</I>
-        <Idev unit="1/cm">0.0441662</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.127</Q>
-        <I unit="1/cm">2.07523</I>
-        <Idev unit="1/cm">0.0438979</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.129</Q>
-        <I unit="1/cm">2.03548</I>
-        <Idev unit="1/cm">0.0442422</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.131</Q>
-        <I unit="1/cm">1.92452</I>
-        <Idev unit="1/cm">0.0435959</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.133</Q>
-        <I unit="1/cm">1.90866</I>
-        <Idev unit="1/cm">0.0437401</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.135</Q>
-        <I unit="1/cm">1.91252</I>
-        <Idev unit="1/cm">0.0443163</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.137</Q>
-        <I unit="1/cm">1.80405</I>
-        <Idev unit="1/cm">0.043466</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.139</Q>
-        <I unit="1/cm">1.72166</I>
-        <Idev unit="1/cm">0.0429246</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.141</Q>
-        <I unit="1/cm">1.67199</I>
-        <Idev unit="1/cm">0.042469</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.143</Q>
-        <I unit="1/cm">1.63282</I>
-        <Idev unit="1/cm">0.0430074</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.145</Q>
-        <I unit="1/cm">1.63712</I>
-        <Idev unit="1/cm">0.0438557</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.147</Q>
-        <I unit="1/cm">1.5862</I>
-        <Idev unit="1/cm">0.0434135</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.149</Q>
-        <I unit="1/cm">1.56121</I>
-        <Idev unit="1/cm">0.0432591</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.151</Q>
-        <I unit="1/cm">1.49764</I>
-        <Idev unit="1/cm">0.0426577</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.153</Q>
-        <I unit="1/cm">1.53828</I>
-        <Idev unit="1/cm">0.0446481</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.155</Q>
-        <I unit="1/cm">1.47366</I>
-        <Idev unit="1/cm">0.0441973</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.157</Q>
-        <I unit="1/cm">1.40582</I>
-        <Idev unit="1/cm">0.0430461</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.159</Q>
-        <I unit="1/cm">1.40491</I>
-        <Idev unit="1/cm">0.0439883</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.161</Q>
-        <I unit="1/cm">1.34639</I>
-        <Idev unit="1/cm">0.044193</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.163</Q>
-        <I unit="1/cm">1.32238</I>
-        <Idev unit="1/cm">0.0438008</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.165</Q>
-        <I unit="1/cm">1.33885</I>
-        <Idev unit="1/cm">0.0445383</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.167</Q>
-        <I unit="1/cm">1.29608</I>
-        <Idev unit="1/cm">0.0451575</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.169</Q>
-        <I unit="1/cm">1.30562</I>
-        <Idev unit="1/cm">0.0460656</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.171</Q>
-        <I unit="1/cm">1.17147</I>
-        <Idev unit="1/cm">0.0435717</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.173</Q>
-        <I unit="1/cm">1.18565</I>
-        <Idev unit="1/cm">0.0449072</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.175</Q>
-        <I unit="1/cm">1.18944</I>
-        <Idev unit="1/cm">0.0465319</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.177</Q>
-        <I unit="1/cm">1.17055</I>
-        <Idev unit="1/cm">0.0456562</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.179</Q>
-        <I unit="1/cm">1.08571</I>
-        <Idev unit="1/cm">0.0453852</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.181</Q>
-        <I unit="1/cm">1.06208</I>
-        <Idev unit="1/cm">0.0463335</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.183</Q>
-        <I unit="1/cm">1.06187</I>
-        <Idev unit="1/cm">0.0458284</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.185</Q>
-        <I unit="1/cm">1.02727</I>
-        <Idev unit="1/cm">0.0472613</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.187</Q>
-        <I unit="1/cm">0.995169</I>
-        <Idev unit="1/cm">0.0465526</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.189</Q>
-        <I unit="1/cm">1.02569</I>
-        <Idev unit="1/cm">0.0483728</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.191</Q>
-        <I unit="1/cm">0.981154</I>
-        <Idev unit="1/cm">0.0490559</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.193</Q>
-        <I unit="1/cm">1.01831</I>
-        <Idev unit="1/cm">0.0502348</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.195</Q>
-        <I unit="1/cm">0.968102</I>
-        <Idev unit="1/cm">0.0504426</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.197</Q>
-        <I unit="1/cm">0.940656</I>
-        <Idev unit="1/cm">0.0512734</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.199</Q>
-        <I unit="1/cm">1.05762</I>
-        <Idev unit="1/cm">0.0547469</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.201</Q>
-        <I unit="1/cm">0.949872</I>
-        <Idev unit="1/cm">0.055229</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.203</Q>
-        <I unit="1/cm">0.891716</I>
-        <Idev unit="1/cm">0.0531346</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.205</Q>
-        <I unit="1/cm">0.876084</I>
-        <Idev unit="1/cm">0.0548681</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.207</Q>
-        <I unit="1/cm">0.932101</I>
-        <Idev unit="1/cm">0.0576812</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.209</Q>
-        <I unit="1/cm">0.927755</I>
-        <Idev unit="1/cm">0.0593404</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.211</Q>
-        <I unit="1/cm">0.87669</I>
-        <Idev unit="1/cm">0.0600369</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.213</Q>
-        <I unit="1/cm">0.809125</I>
-        <Idev unit="1/cm">0.0588107</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.215</Q>
-        <I unit="1/cm">0.941202</I>
-        <Idev unit="1/cm">0.0659121</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.217</Q>
-        <I unit="1/cm">0.946175</I>
-        <Idev unit="1/cm">0.0690784</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.219</Q>
-        <I unit="1/cm">0.859875</I>
-        <Idev unit="1/cm">0.0674435</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.221</Q>
-        <I unit="1/cm">0.959508</I>
-        <Idev unit="1/cm">0.0718856</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.223</Q>
-        <I unit="1/cm">0.763222</I>
-        <Idev unit="1/cm">0.0702866</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.225</Q>
-        <I unit="1/cm">0.785467</I>
-        <Idev unit="1/cm">0.0742144</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.227</Q>
-        <I unit="1/cm">0.81876</I>
-        <Idev unit="1/cm">0.074266</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.229</Q>
-        <I unit="1/cm">0.678311</I>
-        <Idev unit="1/cm">0.0722578</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.231</Q>
-        <I unit="1/cm">0.761442</I>
-        <Idev unit="1/cm">0.0814516</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.233</Q>
-        <I unit="1/cm">0.757076</I>
-        <Idev unit="1/cm">0.0825262</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.235</Q>
-        <I unit="1/cm">0.600636</I>
-        <Idev unit="1/cm">0.075041</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.237</Q>
-        <I unit="1/cm">0.685635</I>
-        <Idev unit="1/cm">0.0842392</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.239</Q>
-        <I unit="1/cm">0.799186</I>
-        <Idev unit="1/cm">0.0974365</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.241</Q>
-        <I unit="1/cm">0.737526</I>
-        <Idev unit="1/cm">0.0957286</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.243</Q>
-        <I unit="1/cm">0.716415</I>
-        <Idev unit="1/cm">0.0970448</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.245</Q>
-        <I unit="1/cm">0.720413</I>
-        <Idev unit="1/cm">0.0981996</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.247</Q>
-        <I unit="1/cm">0.604059</I>
-        <Idev unit="1/cm">0.100869</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.249</Q>
-        <I unit="1/cm">0.649073</I>
-        <Idev unit="1/cm">0.109281</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.251</Q>
-        <I unit="1/cm">0.700691</I>
-        <Idev unit="1/cm">0.114325</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.253</Q>
-        <I unit="1/cm">0.586789</I>
-        <Idev unit="1/cm">0.108324</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.255</Q>
-        <I unit="1/cm">0.765838</I>
-        <Idev unit="1/cm">0.139579</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.257</Q>
-        <I unit="1/cm">0.828968</I>
-        <Idev unit="1/cm">0.156723</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.259</Q>
-        <I unit="1/cm">0.616499</I>
-        <Idev unit="1/cm">0.137866</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.261</Q>
-        <I unit="1/cm">0.526011</I>
-        <Idev unit="1/cm">0.126209</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.263</Q>
-        <I unit="1/cm">0.51277</I>
-        <Idev unit="1/cm">0.126339</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.265</Q>
-        <I unit="1/cm">0.604189</I>
-        <Idev unit="1/cm">0.166393</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.267</Q>
-        <I unit="1/cm">0.738765</I>
-        <Idev unit="1/cm">0.194006</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.269</Q>
-        <I unit="1/cm">0.462803</I>
-        <Idev unit="1/cm">0.155354</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.271</Q>
-        <I unit="1/cm">0.478694</I>
-        <Idev unit="1/cm">0.171676</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.273</Q>
-        <I unit="1/cm">0.711983</I>
-        <Idev unit="1/cm">0.255539</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.275</Q>
-        <I unit="1/cm">0.473304</I>
-        <Idev unit="1/cm">0.230752</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.277</Q>
-        <I unit="1/cm">0.536817</I>
-        <Idev unit="1/cm">0.262627</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.279</Q>
-        <I unit="1/cm">0.660654</I>
-        <Idev unit="1/cm">0.297519</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.281</Q>
-        <I unit="1/cm">0.408902</I>
-        <Idev unit="1/cm">0.24585</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.283</Q>
-        <I unit="1/cm">0.0</I>
-        <Idev unit="1/cm">0.0</Idev>
-        <Qdev unit="1/A">0.0</Qdev>
-      </Idata>
-    </SASdata>
-    <SAStransmission_spectrum name="sample">
-      <Tdata>
-        <Lambda unit="A">2.2385</Lambda>
-        <T unit="none">0.81703</T>
-        <Tdev unit="none">0.0130497</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.31685</Lambda>
-        <T unit="none">0.801978</T>
-        <Tdev unit="none">0.0120051</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.39794</Lambda>
-        <T unit="none">0.782436</T>
-        <Tdev unit="none">0.0112577</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.48186</Lambda>
-        <T unit="none">0.795586</T>
-        <Tdev unit="none">0.01104</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.56873</Lambda>
-        <T unit="none">0.794175</T>
-        <Tdev unit="none">0.0107071</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.65864</Lambda>
-        <T unit="none">0.807059</T>
-        <Tdev unit="none">0.010757</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.75169</Lambda>
-        <T unit="none">0.797216</T>
-        <Tdev unit="none">0.0106204</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.848</Lambda>
-        <T unit="none">0.811763</T>
-        <Tdev unit="none">0.0107288</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.94768</Lambda>
-        <T unit="none">0.805778</T>
-        <Tdev unit="none">0.0105482</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.05085</Lambda>
-        <T unit="none">0.787672</T>
-        <Tdev unit="none">0.0103436</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.15763</Lambda>
-        <T unit="none">0.804061</T>
-        <Tdev unit="none">0.0105877</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.26814</Lambda>
-        <T unit="none">0.799089</T>
-        <Tdev unit="none">0.0105895</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.38253</Lambda>
-        <T unit="none">0.810401</T>
-        <Tdev unit="none">0.0109026</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.50092</Lambda>
-        <T unit="none">0.797859</T>
-        <Tdev unit="none">0.0108399</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.62345</Lambda>
-        <T unit="none">0.801003</T>
-        <Tdev unit="none">0.0110517</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.75027</Lambda>
-        <T unit="none">0.78509</T>
-        <Tdev unit="none">0.0110209</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.88153</Lambda>
-        <T unit="none">0.785537</T>
-        <Tdev unit="none">0.0111882</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.01738</Lambda>
-        <T unit="none">0.758743</T>
-        <Tdev unit="none">0.0110145</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.15799</Lambda>
-        <T unit="none">0.767664</T>
-        <Tdev unit="none">0.0110323</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.30352</Lambda>
-        <T unit="none">0.772965</T>
-        <Tdev unit="none">0.0110456</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.45414</Lambda>
-        <T unit="none">0.773616</T>
-        <Tdev unit="none">0.0109689</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.61004</Lambda>
-        <T unit="none">0.72275</T>
-        <Tdev unit="none">0.0106718</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.77139</Lambda>
-        <T unit="none">0.754627</T>
-        <Tdev unit="none">0.0107276</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.93839</Lambda>
-        <T unit="none">0.759325</T>
-        <Tdev unit="none">0.0105454</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.11123</Lambda>
-        <T unit="none">0.7544</T>
-        <Tdev unit="none">0.0106594</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.29012</Lambda>
-        <T unit="none">0.75748</T>
-        <Tdev unit="none">0.0107767</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.47528</Lambda>
-        <T unit="none">0.746847</T>
-        <Tdev unit="none">0.0107133</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.66691</Lambda>
-        <T unit="none">0.740591</T>
-        <Tdev unit="none">0.0109161</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.86525</Lambda>
-        <T unit="none">0.752582</T>
-        <Tdev unit="none">0.0113942</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.07054</Lambda>
-        <T unit="none">0.733384</T>
-        <Tdev unit="none">0.0114788</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.28301</Lambda>
-        <T unit="none">0.740944</T>
-        <Tdev unit="none">0.0120655</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.50291</Lambda>
-        <T unit="none">0.731187</T>
-        <Tdev unit="none">0.0119684</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.73051</Lambda>
-        <T unit="none">0.72167</T>
-        <Tdev unit="none">0.0125797</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.96608</Lambda>
-        <T unit="none">0.720803</T>
-        <Tdev unit="none">0.0135324</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">7.2099</Lambda>
-        <T unit="none">0.704751</T>
-        <Tdev unit="none">0.0140129</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">7.46224</Lambda>
-        <T unit="none">0.700147</T>
-        <Tdev unit="none">0.014837</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">7.72342</Lambda>
-        <T unit="none">0.707843</T>
-        <Tdev unit="none">0.015985</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">7.99374</Lambda>
-        <T unit="none">0.68084</T>
-        <Tdev unit="none">0.0167449</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.27352</Lambda>
-        <T unit="none">0.690755</T>
-        <Tdev unit="none">0.0181648</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.56309</Lambda>
-        <T unit="none">0.695218</T>
-        <Tdev unit="none">0.0197214</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.8628</Lambda>
-        <T unit="none">0.663369</T>
-        <Tdev unit="none">0.0205081</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">9.173</Lambda>
-        <T unit="none">0.656758</T>
-        <Tdev unit="none">0.0219522</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">9.49406</Lambda>
-        <T unit="none">0.668971</T>
-        <Tdev unit="none">0.0248015</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">9.82867</Lambda>
-        <T unit="none">0.643715</T>
-        <Tdev unit="none">0.0263197</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.0</Lambda>
-        <T unit="none">-3.78577e-270</T>
-        <Tdev unit="none">-8.61158000229e-315</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">-1.24052e-265</Lambda>
-        <T unit="none">0.0130497</T>
-        <Tdev unit="none">3919.89</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.22902e-151</Lambda>
-        <T unit="none">0.0120051</T>
-        <Tdev unit="none">4462.63</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.22902e-151</Lambda>
-        <T unit="none">0.0112577</T>
-        <Tdev unit="none">4830.61</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.18146e+15</Lambda>
-        <T unit="none">0.01104</T>
-        <Tdev unit="none">5193.21</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.18146e+15</Lambda>
-        <T unit="none">0.0107071</T>
-        <Tdev unit="none">5501.58</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.83244494916e-320</Lambda>
-        <T unit="none">0.010757</T>
-        <Tdev unit="none">5628.96</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.82009e-73</Lambda>
-        <T unit="none">0.0106204</T>
-        <Tdev unit="none">5634.75</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.86436e+126</Lambda>
-        <T unit="none">0.0107288</T>
-        <Tdev unit="none">5724.73</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.43127e+247</Lambda>
-        <T unit="none">0.0105482</T>
-        <Tdev unit="none">5835.49</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.43127e+247</Lambda>
-        <T unit="none">0.0103436</T>
-        <Tdev unit="none">5798.97</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">-2.37127e+226</Lambda>
-        <T unit="none">0.0105877</T>
-        <Tdev unit="none">5767.32</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.86442e+126</Lambda>
-        <T unit="none">0.0105895</T>
-        <Tdev unit="none">5694.24</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.52737e-309</Lambda>
-        <T unit="none">0.0109026</T>
-        <Tdev unit="none">5525.05</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.46672e-310</Lambda>
-        <T unit="none">0.0108399</T>
-        <Tdev unit="none">5417.49</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.66641e+178</Lambda>
-        <T unit="none">0.0110517</T>
-        <Tdev unit="none">5253.01</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.66641e+178</Lambda>
-        <T unit="none">0.0110209</T>
-        <Tdev unit="none">5074.68</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.62375e+211</Lambda>
-        <T unit="none">0.0111882</T>
-        <Tdev unit="none">4929.65</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.62375e+211</Lambda>
-        <T unit="none">0.0110145</T>
-        <Tdev unit="none">4745.3</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.03531e-87</Lambda>
-        <T unit="none">0.0110323</T>
-        <Tdev unit="none">4841.86</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.97389e-39</Lambda>
-        <T unit="none">0.0110456</T>
-        <Tdev unit="none">4897.12</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.97389e-39</Lambda>
-        <T unit="none">0.0109689</T>
-        <Tdev unit="none">4974.21</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.18818e-212</Lambda>
-        <T unit="none">0.0106718</T>
-        <Tdev unit="none">4586.67</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.38734e+245</Lambda>
-        <T unit="none">0.0107276</T>
-        <Tdev unit="none">4948.31</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.38734e+245</Lambda>
-        <T unit="none">0.0105454</T>
-        <Tdev unit="none">5184.8</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">-1.81729e+131</Lambda>
-        <T unit="none">0.0106594</T>
-        <Tdev unit="none">5008.83</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">-1.81729e+131</Lambda>
-        <T unit="none">0.0107767</T>
-        <Tdev unit="none">4940.5</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.44115e-232</Lambda>
-        <T unit="none">0.0107133</T>
-        <Tdev unit="none">4859.81</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">6.44115e-232</Lambda>
-        <T unit="none">0.0109161</T>
-        <Tdev unit="none">4602.81</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.36995e+245</Lambda>
-        <T unit="none">0.0113942</T>
-        <Tdev unit="none">4362.55</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.36995e+245</Lambda>
-        <T unit="none">0.0114788</T>
-        <Tdev unit="none">4081.97</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.9782e-39</Lambda>
-        <T unit="none">0.0120655</T>
-        <Tdev unit="none">3771.18</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.83667e-73</Lambda>
-        <T unit="none">0.0119684</T>
-        <Tdev unit="none">3732.39</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.74941e+64</Lambda>
-        <T unit="none">0.0125797</T>
-        <Tdev unit="none">3291.09</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.74941e+64</Lambda>
-        <T unit="none">0.0135324</T>
-        <Tdev unit="none">2837.17</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">-1.81729e+131</Lambda>
-        <T unit="none">0.0140129</T>
-        <Tdev unit="none">2529.37</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">-1.81729e+131</Lambda>
-        <T unit="none">0.014837</T>
-        <Tdev unit="none">2226.82</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.40721e-280</Lambda>
-        <T unit="none">0.015985</T>
-        <Tdev unit="none">1960.88</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.40721e-280</Lambda>
-        <T unit="none">0.0167449</T>
-        <Tdev unit="none">1653.2</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.84467e+19</Lambda>
-        <T unit="none">0.0181648</T>
-        <Tdev unit="none">1446.05</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">5.67321e+216</Lambda>
-        <T unit="none">0.0197214</T>
-        <Tdev unit="none">1242.7</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.49371e+245</Lambda>
-        <T unit="none">0.0205081</T>
-        <Tdev unit="none">1046.31</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.49371e+245</Lambda>
-        <T unit="none">0.0219522</T>
-        <Tdev unit="none">895.069</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.69054e+192</Lambda>
-        <T unit="none">0.0248015</T>
-        <Tdev unit="none">727.543</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.62169e+211</Lambda>
-        <T unit="none">0.0263197</T>
-        <Tdev unit="none">598.171</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.62169e+211</Lambda>
-        <T unit="none">-8.61158000229e-315</T>
-        <Tdev unit="none">-8.61158000229e-315</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.74996e-68</Lambda>
-        <T unit="none">3919.89</T>
-        <Tdev unit="none">2.2385</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.75213e-78</Lambda>
-        <T unit="none">4462.63</T>
-        <Tdev unit="none">2.31685</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">-3.14338999988e-315</Lambda>
-        <T unit="none">4830.61</T>
-        <Tdev unit="none">2.39794</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.08255e-226</Lambda>
-        <T unit="none">5193.21</T>
-        <Tdev unit="none">2.48186</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.07499e+102</Lambda>
-        <T unit="none">5501.58</T>
-        <Tdev unit="none">2.56873</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.07499e+102</Lambda>
-        <T unit="none">5628.96</T>
-        <Tdev unit="none">2.65864</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.07508e+102</Lambda>
-        <T unit="none">5634.75</T>
-        <Tdev unit="none">2.75169</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.07508e+102</Lambda>
-        <T unit="none">5724.73</T>
-        <Tdev unit="none">2.848</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.81253e-303</Lambda>
-        <T unit="none">5835.49</T>
-        <Tdev unit="none">2.94768</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.35808e-312</Lambda>
-        <T unit="none">5798.97</T>
-        <Tdev unit="none">3.05085</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.07513e+102</Lambda>
-        <T unit="none">5767.32</T>
-        <Tdev unit="none">3.15763</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.07513e+102</Lambda>
-        <T unit="none">5694.24</T>
-        <Tdev unit="none">3.26814</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.38310999756e-315</Lambda>
-        <T unit="none">5525.05</T>
-        <Tdev unit="none">3.38253</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.52703999979e-316</Lambda>
-        <T unit="none">5417.49</T>
-        <Tdev unit="none">3.50092</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">5253.01</T>
-        <Tdev unit="none">3.62345</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">5074.68</T>
-        <Tdev unit="none">3.75027</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">4929.65</T>
-        <Tdev unit="none">3.88153</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.54616000203e-315</Lambda>
-        <T unit="none">4745.3</T>
-        <Tdev unit="none">4.01738</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.54616000203e-315</Lambda>
-        <T unit="none">4841.86</T>
-        <Tdev unit="none">4.15799</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.71357e+104</Lambda>
-        <T unit="none">4897.12</T>
-        <Tdev unit="none">4.30352</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.72432e+104</Lambda>
-        <T unit="none">4974.21</T>
-        <Tdev unit="none">4.45414</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.15015e+102</Lambda>
-        <T unit="none">4586.67</T>
-        <Tdev unit="none">4.61004</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.07508e+102</Lambda>
-        <T unit="none">4948.31</T>
-        <Tdev unit="none">4.77139</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.53040999951e-315</Lambda>
-        <T unit="none">5184.8</T>
-        <Tdev unit="none">4.93839</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">5008.83</T>
-        <Tdev unit="none">5.11123</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">4940.5</T>
-        <Tdev unit="none">5.29012</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.00379e-270</Lambda>
-        <T unit="none">4859.81</T>
-        <Tdev unit="none">5.47528</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">2.00379e-270</Lambda>
-        <T unit="none">4602.81</T>
-        <Tdev unit="none">5.66691</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.58351e-312</Lambda>
-        <T unit="none">4362.55</T>
-        <Tdev unit="none">5.86525</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.7472e-287</Lambda>
-        <T unit="none">4081.97</T>
-        <Tdev unit="none">6.07054</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.74845e-287</Lambda>
-        <T unit="none">3771.18</T>
-        <Tdev unit="none">6.28301</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.24882e-290</Lambda>
-        <T unit="none">3732.39</T>
-        <Tdev unit="none">6.50291</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">3291.09</T>
-        <Tdev unit="none">6.73051</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">2837.17</T>
-        <Tdev unit="none">6.96608</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">2529.37</T>
-        <Tdev unit="none">7.2099</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">2226.82</T>
-        <Tdev unit="none">7.46224</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">1.07499e+102</Lambda>
-        <T unit="none">1960.88</T>
-        <Tdev unit="none">7.72342</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.32977e-311</Lambda>
-        <T unit="none">1653.2</T>
-        <Tdev unit="none">7.99374</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">7.19961e-310</Lambda>
-        <T unit="none">1446.05</T>
-        <Tdev unit="none">8.27352</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">7.19962e-310</Lambda>
-        <T unit="none">1242.7</T>
-        <Tdev unit="none">8.56309</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">8.2010700122e-316</Lambda>
-        <T unit="none">1046.31</T>
-        <Tdev unit="none">8.8628</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.18299000022e-314</Lambda>
-        <T unit="none">895.069</T>
-        <Tdev unit="none">9.173</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">7.42699000005e-314</Lambda>
-        <T unit="none">727.543</T>
-        <Tdev unit="none">9.49406</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">4.24398999993e-314</Lambda>
-        <T unit="none">598.171</T>
-        <Tdev unit="none">9.82867</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.16202013338e-322</Lambda>
-        <T unit="none">-8.61158000229e-315</T>
-        <Tdev unit="none">-3.78577e-270</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">2.2385</T>
-        <Tdev unit="none">0.00307782</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">0.0</Lambda>
-        <T unit="none">2.31685</T>
-        <Tdev unit="none">0.00300224</Tdev>
-      </Tdata>
-      <Tdata>
-        <Lambda unit="A">3.53040999951e-315</Lambda>
-        <T unit="none">2.39794</T>
-        <Tdev unit="none">0.00292546</Tdev>
-      </Tdata>
-    </SAStransmission_spectrum>
-    <SASsample name="">
-      <ID>TK49 c10_SANS</ID>
-    </SASsample>
-    <SASinstrument>
-      <name>LOQ</name>
-      <SASsource>
-        <radiation>neutron</radiation>
-      </SASsource>
-      <SAScollimation/>
-      <SASdetector>
-        <name>HAB</name>
-        <SDD unit="mm">575.0</SDD>
-      </SASdetector>
-      <SASdetector>
-        <name>main-detector-bank</name>
-        <SDD unit="mm">4145.02</SDD>
-      </SASdetector>
-    </SASinstrument>
-    <SASprocess>
-      <name>Mantid generated CanSAS1D XML</name>
-      <date>02-Aug-2013 16:53:56</date>
-      <description></description>
-      <term name="svn">2.5.3</term>
-      <term name="user_file">K:/masks/MASKLOQ_MAN_132E_Lu_Banjo_12mm.txt</term>
-      <SASprocessnote></SASprocessnote>
-    </SASprocess>
-    <SASnote></SASnote>
-  </SASentry>
-</SASroot>
Index: test/sasdataloader/test/testLoad.py
===================================================================
--- test/sasdataloader/test/testLoad.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sasdataloader/test/testLoad.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -14,6 +14,6 @@
 import unittest
 import math
-import sas.dataloader
-from sas.dataloader.loader import  Loader
+import sas.sascalc.dataloader
+from sas.sascalc.dataloader.loader import  Loader
 
 # Check whether we should test image loading on this system 
Index: test/sasdataloader/test/testplugings.py
===================================================================
--- test/sasdataloader/test/testplugings.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sasdataloader/test/testplugings.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -4,5 +4,5 @@
 
 import unittest
-from sas.dataloader.loader import  Loader, Registry
+from sas.sascalc.dataloader.loader import  Loader, Registry
 class testLoader(unittest.TestCase):
     
Index: test/sasdataloader/test/utest_abs_reader.py
===================================================================
--- test/sasdataloader/test/utest_abs_reader.py	(revision bb074b36760e63579017efa665cb14bd42eb1880)
+++ test/sasdataloader/test/utest_abs_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -5,6 +5,6 @@
 import unittest
 import numpy, math
-from sas.dataloader.loader import  Loader
-from sas.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.loader import  Loader
+from sas.sascalc.dataloader.data_info import Data1D
  
 import os.path
@@ -13,5 +13,5 @@
     
     def setUp(self):
-        from sas.dataloader.readers.abs_reader import Reader
+        from sas.sascalc.dataloader.readers.abs_reader import Reader
         self.data = Reader().read("jan08002.ABS")
         
@@ -311,5 +311,5 @@
         
     def test_writer(self):
-        from sas.dataloader.readers.cansas_reader import Reader
+        from sas.sascalc.dataloader.readers.cansas_reader import Reader
         r = Reader()
         x = numpy.ones(5)
Index: test/sasdataloader/test/utest_ascii.py
===================================================================
--- test/sasdataloader/test/utest_ascii.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sasdataloader/test/utest_ascii.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,5 +6,5 @@
 
 import unittest
-from sas.dataloader.loader import  Loader
+from sas.sascalc.dataloader.loader import  Loader
  
 import os.path
Index: test/sasdataloader/test/utest_averaging.py
===================================================================
--- test/sasdataloader/test/utest_averaging.py	(revision a10364b52df3f74f54be6b644163d5d92b3a675b)
+++ test/sasdataloader/test/utest_averaging.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -2,10 +2,10 @@
 import unittest
 
-from sas.dataloader.loader import  Loader
-from sas.dataloader.manipulations import Ring, CircularAverage, SectorPhi, get_q,reader2D_converter
+from sas.sascalc.dataloader.loader import  Loader
+from sas.sascalc.dataloader.manipulations import Ring, CircularAverage, SectorPhi, get_q,reader2D_converter
  
 import os.path
 import numpy, math
-import sas.dataloader.data_info as data_info
+import sas.sascalc.dataloader.data_info as data_info
 
 class Averaging(unittest.TestCase):
@@ -139,5 +139,5 @@
             The test data was not generated by IGOR.
         """
-        from sas.dataloader.manipulations import Boxsum, Boxavg
+        from sas.sascalc.dataloader.manipulations import Boxsum, Boxavg
         
         r = Boxsum(x_min=.01, x_max=.015, y_min=0.01, y_max=0.015)
@@ -157,5 +157,5 @@
             The test data was not generated by IGOR.
         """
-        from sas.dataloader.manipulations import SlabX
+        from sas.sascalc.dataloader.manipulations import SlabX
         
         r = SlabX(x_min=-.01, x_max=.01, y_min=-0.0002, y_max=0.0002, bin_width=0.0004)
@@ -174,5 +174,5 @@
             The test data was not generated by IGOR.
         """
-        from sas.dataloader.manipulations import SlabY
+        from sas.sascalc.dataloader.manipulations import SlabY
         
         r = SlabY(x_min=.005, x_max=.01, y_min=-0.01, y_max=0.01, bin_width=0.0004)
@@ -193,5 +193,5 @@
             The test data was not generated by IGOR.
         """
-        from sas.dataloader.manipulations import SectorPhi
+        from sas.sascalc.dataloader.manipulations import SectorPhi
         import math
         
@@ -218,5 +218,5 @@
             The test data was not generated by IGOR.
         """
-        from sas.dataloader.manipulations import SectorPhi
+        from sas.sascalc.dataloader.manipulations import SectorPhi
         import math
         
@@ -236,5 +236,5 @@
             The test data was not generated by IGOR.
         """
-        from sas.dataloader.manipulations import SectorQ
+        from sas.sascalc.dataloader.manipulations import SectorQ
         import math
         
Index: test/sasdataloader/test/utest_cansas.py
===================================================================
--- test/sasdataloader/test/utest_cansas.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sasdataloader/test/utest_cansas.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,10 +6,10 @@
 warnings.simplefilter("ignore")
 
-import sas.dataloader.readers.cansas_reader as cansas
-from sas.dataloader.loader import Loader
-from sas.dataloader.data_info import Data1D
-from sas.dataloader.readers.xml_reader import XMLreader
-from sas.dataloader.readers.cansas_reader import Reader
-from sas.dataloader.readers.cansas_constants import CansasConstants
+import sas.sascalc.dataloader.readers.cansas_reader as cansas
+from sas.sascalc.dataloader.loader import Loader
+from sas.sascalc.dataloader.data_info import Data1D
+from sas.sascalc.dataloader.readers.xml_reader import XMLreader
+from sas.sascalc.dataloader.readers.cansas_reader import Reader
+from sas.sascalc.dataloader.readers.cansas_constants import CansasConstants
 
 import os
Index: test/sasdataloader/test/utest_red2d_reader.py
===================================================================
--- test/sasdataloader/test/utest_red2d_reader.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sasdataloader/test/utest_red2d_reader.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,5 +6,5 @@
 
 import unittest
-from sas.dataloader.loader import  Loader
+from sas.sascalc.dataloader.loader import  Loader
  
 import os.path
Index: test/sasdataloader/test/write_test.xml
===================================================================
--- test/sasdataloader/test/write_test.xml	(revision 400155bab01bdb35b3cf83069f57cf9f47005bca)
+++ test/sasdataloader/test/write_test.xml	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -1,114 +1,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<SASroot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="cansas1d/1.0" version="1.0" xsi:schemaLocation="cansas1d/1.0 http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd">
-  <SASentry>
-    <Title>Test title</Title>
-    <Run name="run name">1234</Run>
-    <SASdata>
-      <Idata>
-        <Q unit="1/A">0.02</Q>
-        <I unit="1/cm">1000.0</I>
-        <Idev unit="1/cm">3.0</Idev>
-        <Qdev unit="1/A">0.01</Qdev>
-      </Idata>
-      <Idata>
-        <Q unit="1/A">0.03</Q>
-        <I unit="1/cm">1001.0</I>
-        <Idev unit="1/cm">4.0</Idev>
-        <Qdev unit="1/A">0.02</Qdev>
-      </Idata>
-    </SASdata>
-    <SASsample name="my sample">
-      <ID>SI600-new-long</ID>
-      <thickness unit="mm">1.03</thickness>
-      <transmission>0.327</transmission>
-      <temperature unit="C">0.0</temperature>
-      <position>
-        <x unit="mm">10.0</x>
-        <y unit="mm">0.0</y>
-      </position>
-      <orientation>
-        <roll unit="degree">22.5</roll>
-        <pitch unit="degree">0.02</pitch>
-      </orientation>
-      <details>http://chemtools.chem.soton.ac.uk/projects/blog/blogs.php/bit_id/2720</details>
-      <details>Some text here</details>
-    </SASsample>
-    <SASinstrument>
-      <name>canSAS instrument</name>
-      <SASsource name="source name">
-        <radiation>neutron</radiation>
-        <beam_size name="bm">
-          <x unit="mm">12.0</x>
-          <y unit="mm">13.0</y>
-        </beam_size>
-        <beam_shape>disc</beam_shape>
-        <wavelength unit="A">6.0</wavelength>
-        <wavelength_min unit="nm">0.22</wavelength_min>
-        <wavelength_max unit="nm">1.0</wavelength_max>
-        <wavelength_spread unit="percent">14.3</wavelength_spread>
-      </SASsource>
-      <SAScollimation name="test coll name">
-        <length unit="mm">123.0</length>
-        <aperture name="source" type="radius">
-          <size>
-            <x unit="mm">50.0</x>
-          </size>
-          <distance unit="mm">11000.0</distance>
-        </aperture>
-        <aperture name="sample" type="radius">
-          <size>
-            <x unit="mm">1.0</x>
-          </size>
-        </aperture>
-      </SAScollimation>
-      <SASdetector>
-        <name>fictional hybrid</name>
-        <SDD unit="mm">4150.0</SDD>
-        <offset>
-          <x unit="m">0.001</x>
-          <y unit="m">0.002</y>
-        </offset>
-        <orientation>
-          <roll unit="degree">1.0</roll>
-          <pitch unit="degree">0.0</pitch>
-          <yaw unit="degree">0.0</yaw>
-        </orientation>
-        <beam_center>
-          <x unit="mm">322.64</x>
-          <y unit="mm">327.68</y>
-        </beam_center>
-        <pixel_size>
-          <x unit="mm">5.0</x>
-          <y unit="mm">5.0</y>
-        </pixel_size>
-      </SASdetector>
-    </SASinstrument>
-    <SASprocess>
-      <name>spol</name>
-      <date>04-Sep-2007 18:35:02</date>
-      <description></description>
-      <term name="radialstep" unit="mm">10.000</term>
-      <term name="sector_width" unit="degree">180.0</term>
-      <term name="sector_orient" unit="degree">0.0</term>
-      <term name="MASK_file">USER:MASK.COM</term>
-      <SASprocessnote></SASprocessnote>
-    </SASprocess>
-    <SASprocess>
-      <name>NCNR-IGOR</name>
-      <date>03-SEP-2006 11:42:47</date>
-      <description></description>
-      <term name="average_type">Circular</term>
-      <term name="SAM_file">SEP06064.SA3_AJJ_L205</term>
-      <term name="BKD_file">SEP06064.SA3_AJJ_L205</term>
-      <term name="EMP_file">SEP06064.SA3_AJJ_L205</term>
-      <term name="DIV_file">SEP06064.SA3_AJJ_L205</term>
-      <term name="MASK_file">SEP06064.SA3_AJJ_L205</term>
-      <term name="ABS:TSTAND">1</term>
-      <term name="ABS:DSTAND" unit="mm">1</term>
-      <term name="ABS:IZERO">230.09</term>
-      <term name="ABS:XSECT" unit="mm">1</term>
-      <SASprocessnote></SASprocessnote>
-    </SASprocess>
-    <SASnote/>
-  </SASentry>
-</SASroot>
Index: test/sasfit/test/batch_fit.py
===================================================================
--- test/sasfit/test/batch_fit.py	(revision acf8e4a58aad50df871a259b771db1d582942abb)
+++ test/sasfit/test/batch_fit.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -2,6 +2,6 @@
 import time
 import unittest
-from sas.dataloader.loader import Loader
-from sas.fit.Fitting import BumpsFit as Fit
+from sas.sascalc.dataloader.loader import Loader
+from sas.sascalc.fit.Fitting import BumpsFit as Fit
 from sas.models.CylinderModel import CylinderModel
 import sas.models.dispersion_models 
Index: test/sasfit/test/utest_fit_cylinder.py
===================================================================
--- test/sasfit/test/utest_fit_cylinder.py	(revision acf8e4a58aad50df871a259b771db1d582942abb)
+++ test/sasfit/test/utest_fit_cylinder.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,7 +6,7 @@
 import math
 
-from sas.fit.AbstractFitEngine import Model
-from sas.fit.BumpsFitting import BumpsFit as Fit
-from sas.dataloader.loader import Loader
+from sas.sascalc.fit.AbstractFitEngine import Model
+from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
+from sas.sascalc.dataloader.loader import Loader
 
 class TestSingleFit(unittest.TestCase):
Index: test/sasfit/test/utest_fit_line.py
===================================================================
--- test/sasfit/test/utest_fit_line.py	(revision bc87305316c560a93eeaf77db05eb6721733d636)
+++ test/sasfit/test/utest_fit_line.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,7 +6,7 @@
 import math
 
-from sas.fit.AbstractFitEngine import Model, FitHandler
-from sas.dataloader.loader import Loader
-from sas.fit.BumpsFitting import BumpsFit as Fit
+from sas.sascalc.fit.AbstractFitEngine import Model, FitHandler
+from sas.sascalc.dataloader.loader import Loader
+from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
 from sas.models.LineModel import LineModel
 from sas.models.Constant import Constant
Index: test/sasfit/test/utest_fit_smeared.py
===================================================================
--- test/sasfit/test/utest_fit_smeared.py	(revision bc87305316c560a93eeaf77db05eb6721733d636)
+++ test/sasfit/test/utest_fit_smeared.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -7,7 +7,7 @@
 
 import numpy
-from sas.fit.AbstractFitEngine import Model
-from sas.fit.BumpsFitting import BumpsFit as Fit
-from sas.dataloader.loader import Loader
+from sas.sascalc.fit.AbstractFitEngine import Model
+from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
+from sas.sascalc.dataloader.loader import Loader
 from sas.models.qsmearing import smear_selection
 from sas.models.CylinderModel import CylinderModel
Index: test/sasfit/test/utest_multiplicative_model.py
===================================================================
--- test/sasfit/test/utest_multiplicative_model.py	(revision acf8e4a58aad50df871a259b771db1d582942abb)
+++ test/sasfit/test/utest_multiplicative_model.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,8 +6,8 @@
 import math
 
-from sas.dataloader.data_info import Data1D 
-from sas.fit.AbstractFitEngine import Model,FitData1D
-from sas.fit.BumpsFitting import BumpsFit as Fit
-from sas.dataloader.loader import Loader
+from sas.sascalc.dataloader.data_info import Data1D 
+from sas.sascalc.fit.AbstractFitEngine import Model,FitData1D
+from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
+from sas.sascalc.dataloader.loader import Loader
 from sas.models.MultiplicationModel import MultiplicationModel
 from sas.models.CylinderModel import CylinderModel
Index: test/sasfit/test/utest_small_test.py
===================================================================
--- test/sasfit/test/utest_small_test.py	(revision acf8e4a58aad50df871a259b771db1d582942abb)
+++ test/sasfit/test/utest_small_test.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -4,6 +4,6 @@
 import unittest
 import math
-from sas.fit.BumpsFitting import BumpsFit as Fit
-from sas.dataloader.loader import Loader
+from sas.sascalc.fit.BumpsFitting import BumpsFit as Fit
+from sas.sascalc.dataloader.loader import Loader
 import bumps.fitters
 bumps.fitters.FIT_DEFAULT = 'lm'
Index: test/sasguiframe/test/utest_manipulations.py
===================================================================
--- test/sasguiframe/test/utest_manipulations.py	(revision 35ec2791ba4240f8154f552ce6ead4e850779a20)
+++ test/sasguiframe/test/utest_manipulations.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,5 +6,5 @@
 import unittest
 import numpy, math
-from sas.dataloader.loader import  Loader
+from sas.sascalc.dataloader.loader import  Loader
 from sas.guiframe.dataFitting import Data1D, Data2D
 from sas.guiframe.dataFitting import Data1D as Theory1D
Index: test/sasinvariant/test/utest_data_handling.py
===================================================================
--- test/sasinvariant/test/utest_data_handling.py	(revision 8c9ffde290eafed946f800881bf4901b48445d4c)
+++ test/sasinvariant/test/utest_data_handling.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -10,7 +10,8 @@
 import unittest
 import numpy, math
-from sas.dataloader.loader import  Loader
-from sas.dataloader.data_info import Data1D
-from sas.invariant import invariant
+from sas.sascalc.dataloader.loader import  Loader
+from sas.sascalc.dataloader.data_info import Data1D
+
+from sas.sascalc.invariant import invariant
     
 class TestLinearFit(unittest.TestCase):
Index: test/sasinvariant/test/utest_use_cases.py
===================================================================
--- test/sasinvariant/test/utest_use_cases.py	(revision 8c9ffde290eafed946f800881bf4901b48445d4c)
+++ test/sasinvariant/test/utest_use_cases.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,6 +6,7 @@
 import unittest
 import numpy
-from sas.dataloader.loader import  Loader
-from sas.invariant import invariant
+from sas.sascalc.dataloader.loader import  Loader
+
+from sas.sascalc.invariant import invariant
 
 class Data1D:
Index: test/sasmodels/test/utest_smearing.py
===================================================================
--- test/sasmodels/test/utest_smearing.py	(revision 8c9ffde290eafed946f800881bf4901b48445d4c)
+++ test/sasmodels/test/utest_smearing.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -6,6 +6,6 @@
 import unittest
 import numpy, math
-from sas.dataloader.loader import  Loader
-from sas.dataloader.data_info import Data1D, Data2D
+from sas.sascalc.dataloader.loader import  Loader
+from sas.sascalc.dataloader.data_info import Data1D, Data2D
 #from DataLoader.qsmearing import SlitSmearer, QSmearer, smear_selection
 from sas.models.qsmearing import SlitSmearer, QSmearer, smear_selection
Index: test/sasmodels/test/utest_sphere_dispersity.py
===================================================================
--- test/sasmodels/test/utest_sphere_dispersity.py	(revision 8c9ffde290eafed946f800881bf4901b48445d4c)
+++ test/sasmodels/test/utest_sphere_dispersity.py	(revision b699768550de9d4b35b087234ec1d2a461922bab)
@@ -5,5 +5,5 @@
 
 import unittest, math, numpy
-from sas.dataloader.loader import  Loader
+from sas.sascalc.dataloader.loader import  Loader
 
         
