Index: DataLoader/data_info.py
===================================================================
--- DataLoader/data_info.py	(revision 442f42fc528a8171701cedfb4110b6e4fabe9210)
+++ DataLoader/data_info.py	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
@@ -36,8 +36,13 @@
         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
@@ -47,9 +52,11 @@
     _yunit = ''
     
-    def __init__(self,x,y,dx=None,dy=None):
+    def __init__(self,x,y,dx=None,dy=None,dxl=None,dxw=None):
         self.x = x
         self.y = y
         self.dx = dx
         self.dy = dy
+        self.dxl = dxl
+        self.dxw = dxw
 
     def xaxis(self, label, unit):
Index: DataLoader/qsmearing.py
===================================================================
--- DataLoader/qsmearing.py	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
+++ DataLoader/qsmearing.py	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
@@ -0,0 +1,277 @@
+"""
+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 math
+import scipy.special
+
+def smear_selection(data1D):
+    """
+        Creates the right type of smearer according 
+        to the data
+    """
+    pass
+
+class _BaseSmearer(object):
+    
+    def __init__(self):
+        self.nbins = 0
+        self._weights = None
+        
+    def _compute_matrix(self): return NotImplemented
+
+    def __call__(self, iq):
+        """
+            Return the smeared I(q) value at the given q.
+            The smeared I(q) is computed using a predetermined 
+            smearing matrix for a particular binning.
+        
+            @param q: I(q) array
+            @return: smeared I(q)
+        """
+        # Sanity check
+        if len(iq) != self.nbins:
+            raise RuntimeError, "Invalid I(q) vector: inconsistent array length %s <> %s" % (len(iq), self.nbins)
+        
+        if self._weights == None:
+            self._compute_matrix()
+            
+        iq_smeared = numpy.zeros(self.nbins)
+        # Loop over q-values
+        for q_i in range(self.nbins):
+            sum = 0.0
+            counts = 0.0    
+            
+            for i in range(self.nbins):
+                sum += iq[i] * self._weights[q_i][i]
+                counts += self._weights[q_i][i]
+                
+            iq_smeared[q_i] = sum/counts 
+                
+        return iq_smeared    
+    
+class _SlitSmearer(_BaseSmearer):
+    """
+        Slit smearing for I(q) array
+    """
+    
+    def __init__(self, nbins=None, width=None, height=None, min=None, max=None):
+        """
+            Initialization
+            
+            @param iq: I(q) array [cm-1]
+            @param width: slit width [A-1]
+            @param height: slit height [A-1]
+            @param min: Q_min [A-1]
+            @param max: Q_max [A-1]
+        """
+        ## Slit width in Q units
+        self.width  = width
+        ## Slit height in Q units
+        self.height = height
+        ## Q_min (Min Q-value for I(q))
+        self.min    = min
+        ## Q_max (Max Q_value for I(q))
+        self.max    = max
+        ## Number of Q bins 
+        self.nbins  = nbins
+        ## Number of points used in the smearing computation
+        self.npts   = 1000
+        ## Smearing matrix
+        self._weights = None
+        
+    def _compute_matrix(self):
+        """
+            Compute the smearing matrix for the current I(q) array
+        """
+        weights = numpy.zeros([self.nbins, self.nbins])
+        
+        # Loop over all q-values
+        for i in range(self.nbins):
+            q = self.min + i*(self.max-self.min)/float(self.nbins-1.0)
+            
+            # For each q-value, compute the weight of each other q-bin
+            # in the I(q) array
+            npts_h = self.npts if self.height>0 else 1
+            npts_w = self.npts if self.width>0 else 1
+            
+            # If both height and width are great than zero,
+            # modify the number of points in each direction so 
+            # that the total number of points is still what 
+            # the user would expect (downgrade resolution)
+            if npts_h>1 and npts_w>1:
+                npts_h = int(math.ceil(math.sqrt(self.npts)))
+                npts_w = npts_h
+                
+            for k in range(npts_h):
+                if npts_h==1:
+                    shift_h = 0
+                else:
+                    shift_h = self.height/(float(npts_h-1.0)) * k
+                for j in range(npts_w):
+                    if npts_w==1:
+                        shift_w = 0
+                    else:
+                        shift_w = self.width/(float(npts_w-1.0)) * j
+                    q_shifted = math.sqrt( ((q-shift_w)*(q-shift_w) + shift_h*shift_h) )
+                    q_i = int(math.floor( (q_shifted-self.min)/((self.max-self.min)/(self.nbins -1.0)) ))
+                    
+                    # Skip the entries outside our I(q) range
+                    #TODO: be careful with edge effect
+                    if q_i<self.nbins:
+                        weights[i][q_i] = weights[i][q_i]+1.0
+                                
+        self._weights = weights
+        return self._weights
+
+class SlitSmearer(_SlitSmearer):
+    """
+        Adaptor for slit smearing class and SANS data
+    """
+    def __init__(self, data1D):
+        """
+            Assumption: equally spaced bins of increasing q-values.
+            
+            @param data1D: data used to set the smearing parameters
+        """
+        # Initialization from parent class
+        super(SlitSmearer, self).__init__()
+        
+        ## Slit width
+        self.width = 0
+        if data1D.dxw is not None and len(data1D.dxw)==len(data1D.x):
+            self.width = data1D.dxw[0]
+            # Sanity check
+            for value in data1D.dxw:
+                if value != self.width:
+                    raise RuntimeError, "Slit smearing parameters must be the same for all data"
+                
+        ## Slit height
+        self.height = 0
+        if data1D.dxl is not None and len(data1D.dxl)==len(data1D.x):
+            self.height = data1D.dxl[0]
+            # Sanity check
+            for value in data1D.dxl:
+                if value != self.height:
+                    raise RuntimeError, "Slit smearing parameters must be the same for all data"
+        
+        ## Number of Q bins
+        self.nbins = len(data1D.x)
+        ## Minimum Q 
+        self.min = data1D.x[0]
+        ## Maximum
+        self.max = data1D.x[len(data1D.x)-1]        
+
+
+class _QSmearer(_BaseSmearer):
+    """
+        Perform Gaussian Q smearing
+    """
+        
+    def __init__(self, nbins=None, width=None, min=None, max=None):
+        """
+            Initialization
+            
+            @param nbins: number of Q bins
+            @param width: standard deviation in Q [A-1]
+            @param min: Q_min [A-1]
+            @param max: Q_max [A-1]
+        """
+        ## Standard deviation in Q [A-1]
+        self.width  = width
+        ## Q_min (Min Q-value for I(q))
+        self.min    = min
+        ## Q_max (Max Q_value for I(q))
+        self.max    = max
+        ## Number of Q bins 
+        self.nbins  = nbins
+        ## Smearing matrix
+        self._weights = None
+        
+    def _compute_matrix(self):
+        """
+            Compute the smearing matrix for the current I(q) array
+        """
+        weights = numpy.zeros([self.nbins, self.nbins])
+        
+        # Loop over all q-values
+        step = (self.max-self.min)/float(self.nbins-1.0)
+        for i in range(self.nbins):
+            q = self.min + i*step
+            q_min = q - 0.5*step
+            q_max = q + 0.5*step
+            
+            for j in range(self.nbins):
+                q_j = self.min + j*step
+                
+                # Compute the fraction of the Gaussian contributing
+                # to the q bin between q_min and q_max
+                value =  scipy.special.erf( (q_max-q_j)/(math.sqrt(2.0)*self.width) ) 
+                value -=scipy.special.erf( (q_min-q_j)/(math.sqrt(2.0)*self.width) ) 
+
+                weights[i][j] += value
+                                
+        self._weights = weights
+        return self._weights
+        
+class QSmearer(_QSmearer):
+    """
+        Adaptor for Gaussian Q smearing class and SANS data
+    """
+    def __init__(self, data1D):
+        """
+            Assumption: equally spaced bins of increasing q-values.
+            
+            @param data1D: data used to set the smearing parameters
+        """
+        # Initialization from parent class
+        super(QSmearer, self).__init__()
+        
+        ## Slit width
+        self.width = 0
+        if data1D.dx is not None and len(data1D.dx)==len(data1D.x):
+            self.width = data1D.dx[0]
+            # Sanity check
+            for value in data1D.dx:
+                if value != self.width:
+                    raise RuntimeError, "dQ must be the same for all data"
+        
+        ## Number of Q bins
+        self.nbins = len(data1D.x)
+        ## Minimum Q 
+        self.min = data1D.x[0]
+        ## Maximum
+        self.max = data1D.x[len(data1D.x)-1]        
+
+
+if __name__ == '__main__':
+    x = 0.001*numpy.arange(1,11)
+    y = 12.0-numpy.arange(1,11)
+    print x
+    #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)) )) 
+
+    
+    s = _SlitSmearer(nbins=10, width=0.0, height=0.005, min=0.001, max=0.010)
+    #s = _QSmearer(nbins=10, width=0.001, min=0.001, max=0.010)
+    s._compute_matrix()
+
+    sy = s(y)
+    print sy
+    
+    if True:
+        for i in range(10):
+            print x[i], sy[i]
+            #print q, ' : ', s.weight(q), s._compute_iq(q) 
+            #print q, ' : ', s(q), s._compute_iq(q) 
+            #s._compute_iq(q) 
+
+
+
+
Index: DataLoader/readers/cansas_reader.py
===================================================================
--- DataLoader/readers/cansas_reader.py	(revision e3909330e1535bc5d73b41d2bca645ae2236eade)
+++ DataLoader/readers/cansas_reader.py	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
@@ -460,10 +460,18 @@
         dx = numpy.zeros(0)
         dy = numpy.zeros(0)
+        dxw = numpy.zeros(0)
+        dxl = numpy.zeros(0)
         
         for item in nodes:
             _x, attr = get_float('Q', item)
             _dx, attr_d = get_float('Qdev', item)
+            _dxl, attr_l = get_float('dQl', item)
+            _dxw, attr_w = get_float('dQw', item)
             if _dx == None:
                 _dx = 0.0
+            if _dxl == None:
+                _dxl = 0.0
+            if _dxw == None:
+                _dxw = 0.0
                 
             if attr.has_key('unit') and attr['unit'].lower() != data_info.x_unit.lower():
@@ -478,4 +486,5 @@
                     raise ValueError, "CanSAS reader: unrecognized Q unit [%s]; expecting [%s]" \
                         % (attr['unit'], data_info.x_unit)
+            # Error in Q
             if attr_d.has_key('unit') and attr_d['unit'].lower() != data_info.x_unit.lower():
                 if has_converter==True:
@@ -488,4 +497,28 @@
                 else:
                     raise ValueError, "CanSAS reader: unrecognized dQ unit [%s]; expecting [%s]" \
+                        % (attr['unit'], data_info.x_unit)
+            # Slit length
+            if attr_l.has_key('unit') and attr_l['unit'].lower() != data_info.x_unit.lower():
+                if has_converter==True:
+                    try:
+                        data_conv_q = Converter(attr_l['unit'])
+                        _dxl = data_conv_q(_dxl, units=data_info.x_unit)
+                    except:
+                        raise ValueError, "CanSAS reader: could not convert dQl unit [%s]; expecting [%s]\n  %s" \
+                        % (attr['unit'], data_info.x_unit, sys.exc_value)
+                else:
+                    raise ValueError, "CanSAS reader: unrecognized dQl unit [%s]; expecting [%s]" \
+                        % (attr['unit'], data_info.x_unit)
+            # Slit width
+            if attr_w.has_key('unit') and attr_w['unit'].lower() != data_info.x_unit.lower():
+                if has_converter==True:
+                    try:
+                        data_conv_q = Converter(attr_w['unit'])
+                        _dxw = data_conv_q(_dxw, units=data_info.x_unit)
+                    except:
+                        raise ValueError, "CanSAS reader: could not convert dQw unit [%s]; expecting [%s]\n  %s" \
+                        % (attr['unit'], data_info.x_unit, sys.exc_value)
+                else:
+                    raise ValueError, "CanSAS reader: unrecognized dQw unit [%s]; expecting [%s]" \
                         % (attr['unit'], data_info.x_unit)
                     
@@ -522,4 +555,7 @@
                 dx = numpy.append(dx, _dx)
                 dy = numpy.append(dy, _dy)
+                dxl = numpy.append(dxl, _dxl)
+                dxw = numpy.append(dxw, _dxw)
+                
             
         data_info.x = x
@@ -527,4 +563,6 @@
         data_info.dx = dx
         data_info.dy = dy
+        data_info.dxl = dxl
+        data_info.dxw = dxw
         
         data_conv_q = None
Index: DataLoader/test/cansas1d_slit.xml
===================================================================
--- DataLoader/test/cansas1d_slit.xml	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
+++ DataLoader/test/cansas1d_slit.xml	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
@@ -0,0 +1,141 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="example.xsl" ?>
+<SASroot version="1.0"
+    xmlns="cansas1d/1.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    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</I>
+				<Idev unit="1/cm">3</Idev>
+				<dQl unit="1/A">0.005</dQl>
+				<dQw unit="1/A">0.001</dQw>
+				<Shadowfactor><!-- Shadowfactor is optional --></Shadowfactor>
+			</Idata>
+			<Idata>
+				<Q unit="1/A">0.03</Q>
+				<I unit="1/cm">1001</I>
+				<Idev unit="1/cm">4</Idev>
+				<dQl unit="1/A">0.005</dQl>
+				<dQw unit="1/A">0.001</dQw>
+				<Shadowfactor><!-- Shadowfactor is optional --></Shadowfactor>
+			</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.0000</temperature>
+			<position>
+				<x unit="mm">10.00</x>
+				<y unit="mm">0.00</y>
+			</position>
+			<orientation>
+				<roll unit="degree">22.5<!-- was: sample_orientation --></roll>
+				<pitch unit="degree">0.020<!-- was: sample_offset_angle --></pitch>
+			</orientation>
+			<details>
+				<!-- was: sample_prep -->
+				http://chemtools.chem.soton.ac.uk/projects/blog/blogs.php/bit_id/2720
+			</details>
+			<details>
+				Some text here
+			</details>
+			
+		</SASsample>
+		<SASinstrument name="TEST instrument">
+			<name>canSAS instrument</name>
+			<SASsource name="source name">
+				<radiation>neutron</radiation>
+				<beam_size name='bm'>
+					<x unit="mm">12.00</x>
+					<y unit="mm">13.00</y>
+				</beam_size>
+				<beam_shape>disc</beam_shape>
+				<wavelength unit="A">6.00</wavelength>
+				<wavelength_min unit="nm">0.22</wavelength_min>
+				<wavelength_max unit="nm">1.00</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</x>
+					</size>
+					<distance unit="m">11.000<!-- was: distance_coll --></distance>
+				</aperture>
+				<aperture name="sample" type="radius">
+					<size name='size name'>
+						<x unit="mm">1</x>
+					</size>
+				</aperture>
+			</SAScollimation>
+			<SASdetector>
+				<name>fictional hybrid</name>
+				<SDD unit="m">
+					<!-- sample-to-detector distance -->
+					4.150
+				</SDD>
+				<orientation>
+					<roll unit="degree">1.00</roll>
+					<pitch unit="degree">0.00</pitch>
+					<yaw unit="degree">0.00</yaw>
+				</orientation>
+				<offset>
+					<x unit="mm">1</x>
+					<y unit="mm">2</y>
+				</offset>
+				<beam_center>
+					<x unit="mm">322.64</x>
+					<y unit="mm">327.68</y>
+				</beam_center>
+				<pixel_size>
+					<x unit="mm">5.00</x>
+					<y unit="mm">5.00</y>
+				</pixel_size>
+			</SASdetector>
+		</SASinstrument>
+		<SASprocess>
+			<name>spol</name>
+			<date>04-Sep-2007 18:35:02</date>
+			<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>
+				AvA1 0.0000E+00 AsA2 1.0000E+00 XvA3 1.0526E+03 XsA4
+				5.2200E-02 XfA5 0.0000E+00
+			</SASprocessnote>
+			<SASprocessnote>
+				S... 13597 0 2.26E+02 2A 5mM 0%D2O Sbak 13594 0 1.13E+02
+				H2O Buffer
+			</SASprocessnote>
+			<SASprocessnote>V... 13552 3 1.00E+00 H2O5m</SASprocessnote>
+		</SASprocess>
+		<SASprocess>
+			<name>NCNR-IGOR</name>
+			<date>03-SEP-2006 11:42:47</date>
+			<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/>
+		</SASprocess>
+		<SASnote />
+	</SASentry>
+</SASroot>
Index: DataLoader/test/utest_abs_reader.py
===================================================================
--- DataLoader/test/utest_abs_reader.py	(revision 76e23691e12caa20c95e83c3161ff5e9ba24edb5)
+++ DataLoader/test/utest_abs_reader.py	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
@@ -326,5 +326,31 @@
         
         
-        
+    def test_slits(self):
+        """
+            Check slit data
+        """
+        filename = "cansas1d_slit.xml"
+        self.data = Loader().load(filename)
+        self.assertEqual(self.data.filename, filename)
+        self.assertEqual(self.data.run[0], "1234")
+        
+        # Data
+        self.assertEqual(len(self.data.x), 2)
+        self.assertEqual(self.data.x_unit, '1/A')
+        self.assertEqual(self.data.y_unit, '1/cm')
+        self.assertEqual(self.data.x[0], 0.02)
+        self.assertEqual(self.data.y[0], 1000)
+        self.assertEqual(self.data.dxl[0], 0.005)
+        self.assertEqual(self.data.dxw[0], 0.001)
+        self.assertEqual(self.data.dy[0], 3)
+        self.assertEqual(self.data.x[1], 0.03)
+        self.assertAlmostEquals(self.data.y[1], 1001.0)
+        self.assertEqual(self.data.dx[1], 0.0)
+        self.assertEqual(self.data.dxl[1], 0.005)
+        self.assertEqual(self.data.dxw[1], 0.001)
+        self.assertEqual(self.data.dy[1], 4)
+        self.assertEqual(self.data.run_name['1234'], 'run name')
+        self.assertEqual(self.data.title, "Test title")
+
             
 
Index: DataLoader/test/utest_smearing.py
===================================================================
--- DataLoader/test/utest_smearing.py	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
+++ DataLoader/test/utest_smearing.py	(revision d00f8ff74790ad97d3fddff0a584009680fdda7d)
@@ -0,0 +1,92 @@
+"""
+    Unit tests for data manipulations
+"""
+
+
+import unittest
+import numpy, math
+from DataLoader.loader import  Loader
+from DataLoader.data_info import Data1D, Data2D
+from DataLoader.qsmearing import SlitSmearer, QSmearer
+ 
+import os.path
+
+class smear_tests(unittest.TestCase):
+    
+    def setUp(self):
+        self.data = Loader().load("cansas1d_slit.xml")
+        
+        x = 0.001*numpy.arange(1,11)
+        y = 12.0-numpy.arange(1,11)
+        dxl = 0.00*numpy.ones(10)
+        dxw = 0.00*numpy.ones(10)
+        
+        self.data.x = x
+        self.data.y = y
+        self.data.dxl = dxl
+        self.data.dxw = dxw
+        
+    def test_slit(self):
+        """
+            Test identity smearing
+        """
+        # Create smearer for our data
+        s = SlitSmearer(self.data)
+        
+        input = 12.0-numpy.arange(1,11)
+        output = s(input)
+        for i in range(len(input)):
+            self.assertEquals(input[i], output[i])
+
+    def test_slit2(self):
+        """
+            Test basic smearing
+        """
+        dxl = 0.005*numpy.ones(10)
+        dxw = 0.0*numpy.ones(10)
+        self.data.dxl = dxl
+        self.data.dxw = dxw
+        # Create smearer for our data
+        s = SlitSmearer(self.data)
+        
+        input = 12.0-numpy.arange(1,11)
+        output = s(input)
+        answer = [ 9.666,  9.056,  8.329,  7.494,  6.642,  5.721,  4.774,  3.824,  2.871, 2.   ]
+        for i in range(len(input)):
+            self.assertAlmostEqual(answer[i], output[i], 5)
+        
+    def test_q(self):
+        """
+            Test identity resolution smearing
+        """
+        # Create smearer for our data
+        s = QSmearer(self.data)
+        
+        input = 12.0-numpy.arange(1,11)
+        output = s(input)
+        for i in range(len(input)):
+            self.assertEquals(input[i], output[i])
+
+    def test_q2(self):
+        """
+            Test basic smearing
+        """
+        dx = 0.001*numpy.ones(10)
+        self.data.dx = dx
+
+        # Create smearer for our data
+        s = QSmearer(self.data)
+        
+        input = 12.0-numpy.arange(1,11)
+        output = s(input)
+        
+        answer = [ 10.44785079,   9.84991299,   8.98101708,   
+                  7.99906585,   6.99998311,   6.00001689,
+                  5.00093415,   4.01898292,   3.15008701,   2.55214921]
+        for i in range(len(input)):
+            self.assertAlmostEqual(answer[i], output[i], 5)
+      
+
+if __name__ == '__main__':
+    unittest.main()
+   
