Index: sansmodels/src/sans/models/AddComponent.py
===================================================================
--- sansmodels/src/sans/models/AddComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/AddComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+""" Provide base functionality for all model components
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+# info
+__author__ = "Mathieu Doucet /  UTK"
+__id__ = "$Id: BaseComponent.py,v 1.2 2007/03/14 21:04:40 doucet Exp $"
+   
+# imports   
+from sans.models.BaseComponent import BaseComponent
+
+class AddComponent(BaseComponent):
+    """ Basic model component for Addition
+        Provides basic arithmetics
+    """
+
+    def __init__(self, base=None, other=None):
+        """
+            @param base: component to add to 
+            @param other: component to add
+        """
+        BaseComponent.__init__(self)
+        # Component to add to
+        self.operateOn = base
+        # Component to add
+        self.other = other
+        # name
+        self.name = 'AddComponent'
+        
+    def run(self, x=0):
+        """
+            Evaluate each part of the component and sum the results
+            @param x: input parameter
+            @return: value of the model at x
+        """
+        return self.operateOn.run(x) + self.other.run(x)
+        
+    def setParam(self, name, value):
+        """ 
+            Set the value of a model parameter
+            @param name: name of parameter to set
+            @param value: value to give the paramter
+        """
+        return BaseComponent.setParamWithToken(self, name, 
+                                               value, 'add', self.other)
+    
+    def getParam(self, name):
+        """ 
+            Set the value of a model parameter
+            @param name: name of the parameter
+            @return: value of the parameter
+        """
+        return BaseComponent.getParamWithToken(self, name, 'add', self.other)
+    
+    def getParamList(self):
+        """ Return a list of all available parameters for the model 
+        """
+        return BaseComponent.getParamListWithToken(self, 'add', self.other)
+        
+    
+# End of file
Index: sansmodels/src/sans/models/BaseComponent.py
===================================================================
--- sansmodels/src/sans/models/BaseComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/BaseComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,313 @@
+#!/usr/bin/env python
+""" Provide base functionality for all model components
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+# info
+__author__ = "Mathieu Doucet /  UTK"
+__id__ = "$Id: BaseComponent.py,v 1.2 2007/03/14 21:04:40 doucet Exp $"
+   
+# imports   
+import copy
+   
+class BaseComponent:
+    """ Basic model component
+        Provides basic arithmetics
+        
+        The supported operations are: add (+), subtract (-), multiply (*) and divide (/).
+        
+        Here's an arithmetics example to define a function
+        f = ( 1+sin(x) )**2 + 3.0
+        and evaluate it at 1.4.
+        
+        
+        sin   = Sin()
+        const1 = Constant()
+        const2 = Constant()
+        const2.setParam('value', 3.0)
+         
+        func = (const1 + sin)*(const1 + sin) + const2
+        
+        print func(1.4) 
+        
+    """
+
+    def __init__(self):
+        """ Initialization"""
+        
+        ## Name of the model
+        self.name   = "BaseComponent"
+        ## Component left of operator
+        self.operateOn = None
+        ## Component right of operator
+        self.other = None
+
+        
+        ## Parameters to be accessed by client
+        self.params = {}
+           
+    def __str__(self):
+        """ Prints an XML representation of the model
+            @return: string representation
+        """
+        return self.name
+   
+    def run(self, x=0): #pylint: disable-msg=R0201
+        """ Evaluate the model
+            Dummy function
+            @param x: input value
+            @return: value of the model
+        """
+        return 0
+    
+    def clone(self):
+        """ Returns a new object identical to the current object """
+        
+        obj = copy.deepcopy(self)
+        obj.params = copy.deepcopy(self.params)
+    
+        # Check for standard data members of arithmetics sub-classes
+        if hasattr(self, 'operateOn') and \
+            not self.operateOn == None:
+            obj.operateOn = self.operateOn.clone()
+
+        if hasattr(self, 'other') and \
+            not self.other == None:
+            obj.other = self.other.clone()
+        
+        return obj
+
+    def __add__(self, other):
+        """ Overload addition operator
+            @param other: model to add
+        """
+        from sans.models.AddComponent import AddComponent
+        return AddComponent(self.clone(), other)
+        
+    def __sub__(self, other):
+        """ Overload subtraction operator
+            @param other: model to subtract
+        """
+        from sans.models.SubComponent import SubComponent
+        return SubComponent(self.clone(), other)
+        
+    def __mul__(self, other):
+        """ Overload multiplication operator
+            @param other: model to multiply
+        """
+        from sans.models.MulComponent import MulComponent
+        return MulComponent(self.clone(), other)
+        
+    def __div__(self, other):
+        """ Overload division operator
+            @param other: mode to divide by
+        """
+        from sans.models.DivComponent import DivComponent
+        return DivComponent(self.clone(), other)
+        
+    def setParam(self, name, value):
+        """ Set the value of a model parameter
+        
+            A list of all the parameters of a model can
+            be obtained with the getParamList() method.
+            For models resulting from an arithmetic
+            operation between two models, the names of
+            the parameters are modified according to
+            the following rule:
+             
+            For parameter 'a' of model A and 
+            parameter 'b' of model B:
+             
+            C = A + B
+                C.setParam('base.a') to access 'a'
+                C.setParam('add.b') to access 'b'
+                 
+            C = A - B
+                C.setParam('base.a') to access 'a'
+                C.setParam('sub.b') to access 'b'
+                 
+            C = A * B
+                C.setParam('base.a') to access 'a'
+                C.setParam('mul.b') to access 'b'
+                 
+            C = A / B
+                C.setParam('base.a') to access 'a'
+                C.setParam('div.b') to access 'b'
+                         
+            @param name: name of the parameter
+            @param value: value of the parameter
+        """
+        # Lowercase for case insensitivity
+        name = name.lower()
+        
+        keys = self.params.keys()
+        found = False
+        
+        # Loop through the keys and find the right one
+        # The case should not matter
+        for key in keys:
+            if name == key.lower():
+                self.params[key] = value
+                found = True
+                break
+        
+        # Check whether we found the key
+        if not found:
+            raise ValueError, "Model does not contain parameter %s" % name
+
+    def setParamWithToken(self, name, value, token, member):
+        """ 
+            Returns value of a parameter that is part of an internal component
+            @param name: name of the parameter
+            @param value: value of the parameter
+            @param token: parameter prefix
+            @param member: data member pointing to the component
+        """
+        # Lowercase for case insensitivity
+        name = name.lower()
+        
+        # Look for sub-model access
+        toks = name.split('.')
+        if len(toks)>1:
+            short_name = '.'.join(toks[1:])
+            if toks[0] == 'base':
+                return self.operateOn.setParam(short_name, value)
+            elif toks[0] == token:
+                return member.setParam(short_name, value)
+            else:
+                raise ValueError, "Model does not contain parameter %s" % name
+   
+        # If we didn't find a token we know, call the default behavior
+        return BaseComponent.setParam(self, name, value)
+
+        
+    def getParam(self, name):
+        """ Set the value of a model parameter
+
+            A list of all the parameters of a model can
+            be obtained with the getParamList() method.
+            For models resulting from an arithmetic
+            operation between two models, the names of
+            the parameters are modified according to
+            the following rule:
+             
+            For parameter 'a' of model A and 
+            parameter 'b' of model B:
+             
+            C = A + B
+                C.getParam('base.a') to access 'a'
+                C.getParam('add.b') to access 'b'
+                 
+            C = A - B
+                C.getParam('base.a') to access 'a'
+                C.getParam('sub.b') to access 'b'
+                 
+            C = A * B
+                C.getParam('base.a') to access 'a'
+                C.getParam('mul.b') to access 'b'
+                 
+            C = A / B
+                C.getParam('base.a') to access 'a'
+                C.getParam('div.b') to access 'b'
+                         
+            @param name: name of the parameter
+            @param value: value of the parameter
+        """
+        # Lowercase for case insensitivity
+        name = name.lower()
+        
+        keys = self.params.keys()
+        value = None
+        
+        # Loop through the keys and find the right one
+        # The case should not matter
+        for key in keys:
+            if name == key.lower():
+                value = self.params[key]
+                break
+        
+        # Check whether we found the key
+        if value == None:
+            raise ValueError, "Model does not contain parameter %s" % name
+        
+        return value
+        
+    def getParamWithToken(self, name, token, member):
+        """
+            Returns value of a parameter that is part of an internal component
+            @param name: name of the parameter
+            @param token: parameter prefix
+            @param member: data member pointing to the component
+            @return: value of the parameter
+        """
+        # Lowercase for case insensitivity
+        name = name.lower()
+        
+        # Look for sub-model access
+        toks = name.split('.')
+        if len(toks)>1:
+            #short_name = string.join(toks[1:],'.')
+            short_name = '.'.join(toks[1:])
+            if toks[0] == 'base':
+                return self.operateOn.getParam(short_name)
+            elif toks[0] == token:
+                return member.getParam(short_name)
+            else:
+                raise ValueError, "Model does not contain parameter %s" % name
+   
+        # If we didn't find a token we know, call the default behavior
+        return BaseComponent.getParam(self, name)
+        
+         
+    def getParamList(self):
+        """ Return a list of all available parameters for the model 
+        
+            For models resulting from an arithmetic
+            operation between two models, the names of
+            the parameters are modified according to
+            the following rule:
+             
+            For parameter 'a' of model A and 
+            parameter 'b' of model B:
+             
+            C = A + B
+                C.getParam('base.a') to access 'a'
+                C.getParam('add.b') to access 'b'
+                 
+            C = A - B
+                C.getParam('base.a') to access 'a'
+                C.getParam('sub.b') to access 'b'
+                 
+            C = A * B
+                C.getParam('base.a') to access 'a'
+                C.getParam('mul.b') to access 'b'
+                 
+            C = A / B
+                C.getParam('base.a') to access 'a'
+                C.getParam('div.b') to access 'b'
+                                 
+        """
+        param_list = self.params.keys()
+        return param_list
+        
+    def getParamListWithToken(self, token, member):
+        """ 
+            Return a list of all available parameters for the model 
+            
+            @param token: parameter prefix
+            @param member: data member pointing to internal component
+            @return: list of parameters
+        """
+        param_list = self.params.keys()
+        if not self.operateOn == None:
+            sub_list = self.operateOn.params.keys()
+            for p in sub_list:
+                param_list.append("base.%s" % p)
+        if not member == None:
+            sub_list = member.params.keys()
+            for p in sub_list:
+                param_list.append("%s.%s" % (token, p))
+        return param_list
+  
+# End of file
Index: sansmodels/src/sans/models/Constant.py
===================================================================
--- sansmodels/src/sans/models/Constant.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/Constant.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+""" Provide constant function as a BaseComponent model
+"""
+
+from sans.models.BaseComponent import BaseComponent
+import copy
+
+class Constant(BaseComponent):
+    """ Class that evaluates a constant model. 
+        List of default parameters:
+         value           = 1.0 
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        
+        ## Name of the model
+        self.name = "Constant"
+
+        ## Parameter details [units, min, max]
+        self.details = {}
+        self.details['value'] = ['', None, None]
+        self.params['value'] = 1.0
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = Constant()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+    
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: unused
+            @return: (constant value)
+        """
+        return self.params['value']
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model
+            @param x: unused
+            @return: constant value
+        """
+        return self.params['value']
+   
+# End of file
Index: sansmodels/src/sans/models/CoreShellCylinderModel.py
===================================================================
--- sansmodels/src/sans/models/CoreShellCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/CoreShellCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY core_shell_cylinder.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import CCoreShellCylinderModel
+import copy    
+    
+class CoreShellCylinderModel(CCoreShellCylinderModel, BaseComponent):
+    """ Class that evaluates a CoreShellCylinderModel model. 
+    	This file was auto-generated from core_shell_cylinder.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius          = 20.0 A
+         thickness       = 10.0 A
+         length          = 400.0 A
+         core_sld        = 1e-006 A-2
+         shell_sld       = 4e-006 A-2
+         solvent_sld     = 1e-006 A-2
+         background      = 0.0 cm-1
+         axis_theta      = 1.57 rad
+         axis_phi        = 0.0 rad
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CCoreShellCylinderModel.__init__(self)
+        
+        ## Name of the model
+        self.name = "CoreShellCylinderModel"
+
+		## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['radius'] = ['A', None, None]
+        self.details['thickness'] = ['A', None, None]
+        self.details['length'] = ['A', None, None]
+        self.details['core_sld'] = ['A-2', None, None]
+        self.details['shell_sld'] = ['A-2', None, None]
+        self.details['solvent_sld'] = ['A-2', None, None]
+        self.details['background'] = ['cm-1', None, None]
+        self.details['axis_theta'] = ['rad', None, None]
+        self.details['axis_phi'] = ['rad', None, None]
+
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = CoreShellCylinderModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CCoreShellCylinderModel.run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return CCoreShellCylinderModel.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/CoreShellModel.py
===================================================================
--- sansmodels/src/sans/models/CoreShellModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/CoreShellModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY core_shell.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import CCoreShellModel
+import copy    
+    
+class CoreShellModel(CCoreShellModel, BaseComponent):
+    """ Class that evaluates a CoreShellModel model. 
+    	This file was auto-generated from core_shell.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius          = 60.0 A
+         thickness       = 10.0 A
+         core_sld        = 1e-006 A-2
+         shell_sld       = 2e-006 A-2
+         solvent_sld     = 3e-006 A-2
+         background      = 0.0 cm-1
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CCoreShellModel.__init__(self)
+        
+        ## Name of the model
+        self.name = "CoreShellModel"
+
+		## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['radius'] = ['A', None, None]
+        self.details['thickness'] = ['A', None, None]
+        self.details['core_sld'] = ['A-2', None, None]
+        self.details['shell_sld'] = ['A-2', None, None]
+        self.details['solvent_sld'] = ['A-2', None, None]
+        self.details['background'] = ['cm-1', None, None]
+
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = CoreShellModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CCoreShellModel.run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return CCoreShellModel.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/Cos.py
===================================================================
--- sansmodels/src/sans/models/Cos.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/Cos.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+""" Provide cos(x) function as a BaseComponent model
+"""
+
+from sans.models.BaseComponent import BaseComponent
+import math
+ 
+class Cos(BaseComponent):
+    """ Class that evaluates a cos(x) model. 
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        
+        ## Name of the model
+        self.name = "Cos"
+
+        ## Parameter details [units, min, max]
+        self.details = {}
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        return Cos()
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input x, or [x, phi] [radian]
+            @return: cos(x) or cos(x*cos(phi))*cos(x*cos(phi))
+        """
+        if x.__class__.__name__ == 'list':
+            return math.cos(x[0]*math.cos(x[1]))*math.cos(x[0]*math.sin(x[1]))
+        elif x.__class__.__name__ == 'tuple':
+            raise ValueError, "Tuples are not allowed as input to BaseComponent models"
+        else:
+            return math.cos(x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input x, or [x, y] [radian]
+            @return: cos(x) or cos(x)*cos(y)
+        """
+        if x.__class__.__name__ == 'list':
+            return math.cos(x[0])*math.cos(x[1])
+        elif x.__class__.__name__ == 'tuple':
+            raise ValueError, "Tuples are not allowed as input to BaseComponent models"
+        else:
+            return math.cos(x)
+   
+# End of file
Index: sansmodels/src/sans/models/CylinderModel.py
===================================================================
--- sansmodels/src/sans/models/CylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/CylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY cylinder.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import CCylinderModel
+import copy    
+    
+class CylinderModel(CCylinderModel, BaseComponent):
+    """ Class that evaluates a CylinderModel model. 
+    	This file was auto-generated from cylinder.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius          = 20.0 A
+         length          = 400.0 A
+         contrast        = 3e-006 A-2
+         background      = 0.0 cm-1
+         cyl_theta       = 1.0 rad
+         cyl_phi         = 1.0 rad
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CCylinderModel.__init__(self)
+        
+        ## Name of the model
+        self.name = "CylinderModel"
+
+		## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['radius'] = ['A', None, None]
+        self.details['length'] = ['A', None, None]
+        self.details['contrast'] = ['A-2', None, None]
+        self.details['background'] = ['cm-1', None, None]
+        self.details['cyl_theta'] = ['rad', None, None]
+        self.details['cyl_phi'] = ['rad', None, None]
+
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = CylinderModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CCylinderModel.run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return CCylinderModel.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/DisperseModel.py
===================================================================
--- sansmodels/src/sans/models/DisperseModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/DisperseModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+""" 
+	Wrapper for the Disperser class extension
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import Disperser
+    
+class DisperseModel(Disperser, BaseComponent):
+    """ 
+		Wrapper class for the Disperser extension
+		Python class that takes a model and averages its output
+		for a distribution of its parameters.
+		  
+		The parameters to be varied are specified at instantiation time.
+		The distributions are Gaussian, with std deviations specified for
+		each parameter at instantiation time.
+		
+		Example:
+			cyl = ModelFactory().getModel("CylinderModel")
+			disp = DisperseModel(cyl, ['cyl_phi'], [0.3])
+			disp.run([0.01, 1.57])
+    """
+        
+    def __init__(self, model, paramList, sigmaList):
+        """ Initialization 
+        	@param model: Model to disperse [BaseComponent]
+        	@param paramList: list of parameters to disperse [List of strings]
+        	@param sigmaList: list of std deviations for Gaussian dispersion [List of floats]
+        """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        Disperser.__init__(self, model, paramList, sigmaList)
+        ## Name of the model
+        self.name = model.name
+        ## Keep track of the underlying model
+        self.model = model
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = DisperseModel(self.model, self.params['paramList'], self.params['sigmaList'])
+        return obj   
+   
+    def setParam(self, name, value):
+    	if name.lower() in self.params:
+    		BaseComponent.setParam(self, name, value)
+    	else:
+    		self.model.setParam(name, value)
+   
+    def getParam(self, name):
+    	if name.lower() in self.params:
+    		return BaseComponent.getParam(self, name)
+    	else:
+    		return self.model.getParam(name)
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        return Disperser.run(self, x)
+           
+    def runXY(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        return Disperser.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/DivComponent.py
===================================================================
--- sansmodels/src/sans/models/DivComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/DivComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+""" Provide base functionality for all model components
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+# info
+__author__ = "Mathieu Doucet /  UTK"
+__id__ = "$Id: BaseComponent.py,v 1.2 2007/03/14 21:04:40 doucet Exp $"
+   
+# imports  
+from sans.models.BaseComponent import BaseComponent
+
+class DivComponent(BaseComponent):
+    """ Basic model component for Division
+        Provides basic arithmetics
+    """
+
+    def __init__(self, base=None, other=None):
+        """
+            @param base: component to div
+            @param other: component to div by
+        """
+        BaseComponent.__init__(self)
+        # Component to divide
+        self.operateOn = base
+        # Component to divide by
+        self.other = other
+        # name
+        self.name = 'DivComponent'
+        
+    def run(self, x=0):
+        """
+            Evaluate each part of the component and sum the results
+            @param x: input parameter
+            @return: value of the model at x
+        """
+        return self.operateOn.run(x) / self.other.run(x)
+        
+    def setParam(self, name, value):
+        """ 
+            Set the value of a model parameter
+            @param name: name of parameter to set
+            @param value: value to give the paramter
+        """
+        return BaseComponent.setParamWithToken(self, name, 
+                                               value, 'div', self.other)
+    
+    def getParam(self, name):
+        """ 
+            Set the value of a model parameter
+            @param name: name of the parameter
+            @return: value of the parameter
+        """
+        return BaseComponent.getParamWithToken(self, name, 'div', self.other)
+    
+    def getParamList(self):
+        """ Return a list of all available parameters for the model 
+        """
+        return BaseComponent.getParamListWithToken(self, 'div', self.other)
+    
+# End of file
Index: sansmodels/src/sans/models/EllipsoidModel.py
===================================================================
--- sansmodels/src/sans/models/EllipsoidModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/EllipsoidModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY ellipsoid.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import CEllipsoidModel
+import copy    
+    
+class EllipsoidModel(CEllipsoidModel, BaseComponent):
+    """ Class that evaluates a EllipsoidModel model. 
+    	This file was auto-generated from ellipsoid.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius_a        = 20.0 A
+         radius_b        = 400.0 A
+         contrast        = 3e-006 A-2
+         background      = 0.0 cm-1
+         axis_theta      = 1.57 rad
+         axis_phi        = 0.0 rad
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CEllipsoidModel.__init__(self)
+        
+        ## Name of the model
+        self.name = "EllipsoidModel"
+
+		## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['radius_a'] = ['A', None, None]
+        self.details['radius_b'] = ['A', None, None]
+        self.details['contrast'] = ['A-2', None, None]
+        self.details['background'] = ['cm-1', None, None]
+        self.details['axis_theta'] = ['rad', None, None]
+        self.details['axis_phi'] = ['rad', None, None]
+
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = EllipsoidModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CEllipsoidModel.run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return CEllipsoidModel.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/EllipticalCylinderModel.py
===================================================================
--- sansmodels/src/sans/models/EllipticalCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/EllipticalCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY elliptical_cylinder.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import CEllipticalCylinderModel
+import copy    
+    
+class EllipticalCylinderModel(CEllipticalCylinderModel, BaseComponent):
+    """ Class that evaluates a EllipticalCylinderModel model. 
+    	This file was auto-generated from elliptical_cylinder.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         r_minor         = 20.0 A
+         r_ratio         = 1.5 A
+         length          = 400.0 A
+         contrast        = 3e-006 A-2
+         background      = 0.0 cm-1
+         cyl_theta       = 1.57 rad
+         cyl_phi         = 0.0 rad
+         cyl_psi         = 0.0 rad
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CEllipticalCylinderModel.__init__(self)
+        
+        ## Name of the model
+        self.name = "EllipticalCylinderModel"
+
+		## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['r_minor'] = ['A', None, None]
+        self.details['r_ratio'] = ['A', None, None]
+        self.details['length'] = ['A', None, None]
+        self.details['contrast'] = ['A-2', None, None]
+        self.details['background'] = ['cm-1', None, None]
+        self.details['cyl_theta'] = ['rad', None, None]
+        self.details['cyl_phi'] = ['rad', None, None]
+        self.details['cyl_psi'] = ['rad', None, None]
+
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = EllipticalCylinderModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CEllipticalCylinderModel.run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return CEllipticalCylinderModel.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/Gaussian.py
===================================================================
--- sansmodels/src/sans/models/Gaussian.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/Gaussian.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY gaussian.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import CGaussian
+import copy    
+    
+class Gaussian(CGaussian, BaseComponent):
+    """ Class that evaluates a Gaussian model. 
+    	This file was auto-generated from gaussian.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         sigma           = 1.0 
+         center          = 0.0 
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CGaussian.__init__(self)
+        
+        ## Name of the model
+        self.name = "Gaussian"
+
+		## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['sigma'] = ['', None, None]
+        self.details['center'] = ['', None, None]
+
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = Gaussian()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CGaussian.run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return CGaussian.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/Lorentzian.py
===================================================================
--- sansmodels/src/sans/models/Lorentzian.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/Lorentzian.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY lorentzian.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import CLorentzian
+import copy    
+    
+class Lorentzian(CLorentzian, BaseComponent):
+    """ Class that evaluates a Lorentzian model. 
+    	This file was auto-generated from lorentzian.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         gamma           = 1.0 
+         center          = 0.0 
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CLorentzian.__init__(self)
+        
+        ## Name of the model
+        self.name = "Lorentzian"
+
+		## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['gamma'] = ['', None, None]
+        self.details['center'] = ['', None, None]
+
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = Lorentzian()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CLorentzian.run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return CLorentzian.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/ModelFactory.py
===================================================================
--- sansmodels/src/sans/models/ModelFactory.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/ModelFactory.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+""" Model factory
+    Class that creates models by looking up the name of a model
+    in the models directory
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+import os
+import imp
+
+# info
+__author__ = "Mathieu Doucet"   
+
+class ModelFactory: #pylint: disable-msg=W0232
+    """ Class to create available models """
+
+    def getModel(self, name="BaseComponent"): #pylint: disable-msg=R0201
+        """ Returns the model if found 
+            @param name: name of the model to look for
+            @return: the model object
+        """
+         
+        # Look up the files in the model directory
+         
+        # Find correct directory first...
+        path = os.path.dirname(__file__)
+        if(os.path.isfile("%s/%s.py" % (path, name))):
+            fObj, path, descr = imp.find_module(name, [path])
+            modObj = imp.load_module(name, fObj, path, descr)
+            if hasattr(modObj, name):
+                return getattr(modObj, name)()
+        else:
+            raise ValueError, "Could not find module file"
+            
+        raise ValueError, "Could not find module %s" % name
+      
+    def getAllModels(self): #pylint: disable-msg=R0201
+        """ Returns a list of all available models 
+            @return: python list object
+        """
+        
+        # from AbstractModel import AbstractModel
+        # Find correct directory first...
+        path = os.path.dirname(__file__)
+        filelist = os.listdir(path)
+        internal_components = ["BaseComponent", "AddComponent",
+                "SubComponent", "DivComponent", "MulComponent",
+                "DisperseModel", "ModelFactory", "ModelIO"]
+    
+        funclist = []
+        for filename in filelist:
+            toks = filename.split('.')
+            # Look for exceptions... 
+            # TODO: These should be in another directory
+            if len(toks)==2 and toks[0].count('__')==0 and toks[1]=='py' \
+                and not toks[0] in internal_components:
+                funclist.append(toks[0])
+     
+        return funclist
+             
+# main
+if __name__ == '__main__':
+    print ModelFactory().getAllModels()
+    app = ModelFactory().getModel('CylinderModel')
+    print "%s: f(45) = %g" % (app.name, app.run(1))
+         
+# End of file
Index: sansmodels/src/sans/models/ModelIO.py
===================================================================
--- sansmodels/src/sans/models/ModelIO.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/ModelIO.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+""" Model IO
+    Class that saves and loads composite models from files
+    The parameters of the models are not set. The loaded
+    model object will have the default parameters for all
+    sub-models.
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+import sys
+
+class ModelIO:
+    """ Class to create composite models from file or save a model """
+    
+    def __init__(self, factory):
+        """ Initizalization 
+            @param factory: ModelFactory object
+        """
+        
+        ## Factory used to create the right type of component
+        self.factory = factory
+    
+    def modelToXML(self, model):
+        """ Saves XML representation of the component
+            @param model: model to save
+            @param file: name of file to write to (if None, a string is returned)
+        """
+
+        try:
+            # Type
+            
+            repr = "<Component class='%s' name='%s'>\n" \
+                % (model.__class__.__name__, model.name)
+            
+            # Parameters 
+            
+            #for p in model.params:
+            #    repr += "  <Param name='%s'>\n" % p
+            #    par_type = ''
+            #    try:
+            #        type_tok = str(type(model.params[p])).split("\'")
+            #        if len(type_tok)>2:
+            #            par_type = type_tok[1]
+            #    except:
+            #        print sys.exc_value
+            #    repr += "    <Type>%s</Type>\n" % par_type
+            #    repr += "    <Value>%s</Value>\n" % model.params[p]
+            #    repr += "  </Param>"
+        
+            # Operation parameters
+            if model.__class__.__name__ in ['AddComponent', 'SubComponent', 
+                                            'MulComponent', 'DivComponent']:
+                repr += "  <operateOn>\n"
+                repr += "    %s" % self.modelToXML(model.operateOn)
+                repr += "  </operateOn>\n"
+                
+            if model.__class__.__name__ == "AddComponent":
+                repr += "  <toAdd>\n"
+                repr += "    %s" % self.modelToXML(model.other)
+                repr += "  </toAdd>\n"
+                
+            if model.__class__.__name__ == "SubComponent":
+                repr += "  <toSub>\n"
+                repr += "    %s" % self.modelToXML(model.other)
+                repr += "  </toSub>\n"
+                    
+            if model.__class__.__name__ == "MulComponent":
+                repr += "  <toMul>\n"
+                repr += "    %s" % self.modelToXML(model.other)
+                repr += "  </toMul>\n"
+                
+            if model.__class__.__name__ == "DivComponent":
+                repr += "  <toDiv>\n"
+                repr += "    %s" % self.modelToXML(model.other)
+                repr += "  </toDiv>\n"
+                
+            repr += "</Component>\n"
+        except:
+            text_out = "ModelIO.save: Error occured while reading info\n  %s" \
+                % sys.exc_value
+            raise ValueError, text_out
+        
+        # Return the XML representation of the model
+        return repr
+        
+    def save(self, model, filename):
+        """ Save the XML representation of a model to a file
+            @param model: XML representation of the model
+            @param filename: the path of the file to write to
+            @return: True if everything went well
+        """
+        file_obj = open(filename,'w')
+        file_obj.write(self.modelToXML(model))
+        file_obj.close()
+        return True
+    
+    def load(self, filename):
+        """ Load a model from a file
+            @param file: file to load
+            @return: a new BaseComponent
+        """
+        # Recurse through XML tree
+        from xml.dom.minidom import parse
+        dom = parse(filename)
+        
+        # Look for Component nodes
+        return self.lookupComponentNodes(dom)
+        
+             
+    def lookupComponentNodes(self, node):  
+        """ Look up a component node among the children of a node 
+            @param node: the minidom node to investigate
+            @return: the model object described by the component node, None otherwise
+        """
+        # Look for Component nodes
+        if node.hasChildNodes():
+            for n in node.childNodes:
+                if n.nodeName == "Component":
+                    name = None
+                    type = None
+                    if n.hasAttributes():
+                        # Get the class name & name attribute of the object
+                        for i in range(len(n.attributes)):
+                            attr_name = n.attributes.item(i).name
+                            if attr_name == "class":
+                                type = n.attributes.item(i).nodeValue
+                            elif attr_name == "name":
+                                name = n.attributes.item(i).nodeValue
+                            
+                    # Check that all the information is there
+                    if name == None or type == None:
+                        raise ValueError, \
+                        "Incomplete model description: missing name or class"
+                    else:
+                        # Create an object of the type found
+                        model_object = self.factory.getModel(type)
+                    
+                        # Process other nodes that might be attached 
+                        # to this model
+                        self.processComponentNode(n, model_object)
+                        
+                        # Return the object instantiated from the 
+                        # first node found
+                        return model_object
+                    
+        return None
+                    
+        
+    def processComponentNode(self, node, model):
+        """ Process an XML 'Component' node in a model file 
+            @param node: XML minidom node to process
+            @param model: model object to build from
+        """
+        if node.hasChildNodes():
+            for n in node.childNodes:
+                if n.nodeName == "operateOn":
+                    model.operateOn = self.lookupComponentNodes(n)
+                elif n.nodeName == "toAdd":
+                    model.other = self.lookupComponentNodes(n)
+                elif n.nodeName == "toSub":
+                    model.other = self.lookupComponentNodes(n)
+                elif n.nodeName == "toMul":
+                    model.other = self.lookupComponentNodes(n)
+                elif n.nodeName == "toDiv":
+                    model.other = self.lookupComponentNodes(n)       
+
+# main
+if __name__ == '__main__':
+    from ModelFactory import ModelFactory
+    fac = ModelFactory()
+    cyl = fac.getModel('CylinderModel')
+    sph = fac.getModel('SphereModel')
+    c = cyl+sph
+    io = ModelIO(fac)
+    io.save(c,"myModel.xml")
+    value = c.run(1)
+    print "%s: f(1) = %g" % (c.name, value)
+    loaded = io.load("myModel.xml")
+    #print io.modelToXML(loaded)    
+    value2 = loaded.run(1)
+    print "%s: f(1) = %g" % (c.name, value2)
+    if not value == value2:
+        print "AN ERROR OCCURED"
+      
+# End of file
Index: sansmodels/src/sans/models/MulComponent.py
===================================================================
--- sansmodels/src/sans/models/MulComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/MulComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+""" Provide base functionality for all model components
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+# info
+__author__ = "Mathieu Doucet /  UTK"
+__id__ = "$Id: BaseComponent.py,v 1.2 2007/03/14 21:04:40 doucet Exp $"
+   
+# imports   
+from sans.models.BaseComponent import BaseComponent
+
+class MulComponent(BaseComponent):
+    """ Basic model component for Multiply
+        Provides basic arithmetics
+    """
+
+    def __init__(self, base=None, other=None):
+        """
+            @param base: component to multiply
+            @param other: component to multiply by
+        """
+        BaseComponent.__init__(self)
+        # Component to multiply
+        self.operateOn = base
+        # Component to multiply by
+        self.other = other
+        # name
+        self.name = 'MulComponent'
+        
+    def run(self, x=0):
+        """
+            Evaluate each part of the component and sum the results
+            @param x: input parameter
+            @return: value of the model at x
+        """
+        return self.operateOn.run(x) * self.other.run(x)
+        
+    def setParam(self, name, value):
+        """ 
+            Set the value of a model parameter
+            @param name: name of parameter to set
+            @param value: value to give the paramter
+        """
+        return BaseComponent.setParamWithToken(self, name, 
+                                               value, 'mul', self.other)
+    
+    def getParam(self, name):
+        """ 
+            Set the value of a model parameter
+            @param name: name of the parameter
+            @return: value of the parameter
+        """
+        return BaseComponent.getParamWithToken(self, name, 'mul', self.other)
+
+    def getParamList(self):
+        """ Return a list of all available parameters for the model 
+        """
+        return BaseComponent.getParamListWithToken(self, 'mul', self.other)
+    
+# End of file
Index: sansmodels/src/sans/models/Sin.py
===================================================================
--- sansmodels/src/sans/models/Sin.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/Sin.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+""" Provide sin(x) function as a BaseComponent model
+"""
+
+from sans.models.BaseComponent import BaseComponent
+import math
+ 
+class Sin(BaseComponent):
+    """ Class that evaluates a sin(x) model. 
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        
+        ## Name of the model
+        self.name = "Sin"
+
+        ## Parameter details [units, min, max]
+        self.details = {}
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        return Sin()
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input x, or [x, phi] [radian]
+            @return: sin(x) or sin(x*cos(phi))*sin(x*sin(phi))
+        """
+        if x.__class__.__name__ == 'list':
+            return math.sin(x[0]*math.cos(x[1]))*math.sin(x[0]*math.sin(x[1]))
+        elif x.__class__.__name__ == 'tuple':
+            raise ValueError, "Tuples are not allowed as input to BaseComponent models"
+        else:
+            return math.sin(x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input x, or [x, y] [radian]
+            @return: sin(x) or sin(x)*sin(y)
+        """
+        if x.__class__.__name__ == 'list':
+            return math.sin(x[0])*math.sin(x[1])
+        elif x.__class__.__name__ == 'tuple':
+            raise ValueError, "Tuples are not allowed as input to BaseComponent models"
+        else:
+            return math.sin(x)
+   
+# End of file
Index: sansmodels/src/sans/models/SphereModel.py
===================================================================
--- sansmodels/src/sans/models/SphereModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/SphereModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY sphere.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import CSphereModel
+import copy    
+    
+class SphereModel(CSphereModel, BaseComponent):
+    """ Class that evaluates a SphereModel model. 
+    	This file was auto-generated from sphere.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1e-006 
+         radius          = 60.0 A
+         contrast        = 1.0 A-2
+         background      = 0.0 cm-1
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CSphereModel.__init__(self)
+        
+        ## Name of the model
+        self.name = "SphereModel"
+
+		## Parameter details [units, min, max]
+        self.details = {}
+        self.details['scale'] = ['', None, None]
+        self.details['radius'] = ['A', None, None]
+        self.details['contrast'] = ['A-2', None, None]
+        self.details['background'] = ['cm-1', None, None]
+
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = SphereModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CSphereModel.run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return CSphereModel.runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/SubComponent.py
===================================================================
--- sansmodels/src/sans/models/SubComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/SubComponent.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+""" Provide base functionality for all model components
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+# info
+__author__ = "Mathieu Doucet /  UTK"
+__id__ = "$Id: BaseComponent.py,v 1.2 2007/03/14 21:04:40 doucet Exp $"
+   
+# imports   
+from sans.models.BaseComponent import BaseComponent
+
+class SubComponent(BaseComponent):
+    """ Basic model component for Subtraction
+        Provides basic arithmetics
+    """
+
+    def __init__(self, base=None, other=None):
+        """
+            @param base: component to subtract from
+            @param other: component to subtract
+        """
+        BaseComponent.__init__(self)
+        # Component to subtract from
+        self.operateOn = base
+        # Component to subtract
+        self.other = other
+        # name
+        self.name = 'SubComponent'
+        
+    def run(self, x=0):
+        """
+            Evaluate each part of the component and sum the results
+            @param x: input parameter
+            @return: value of the model at x
+        """
+        return self.operateOn.run(x) - self.other.run(x)
+        
+    def setParam(self, name, value):
+        """ 
+            Set the value of a model parameter
+            @param name: name of parameter to set
+            @param value: value to give the paramter
+        """
+        return BaseComponent.setParamWithToken(self, name, 
+                                               value, 'sub', self.other)
+    
+    def getParam(self, name):
+        """ 
+            Set the value of a model parameter
+            @param name: name of the parameter
+            @return: value of the parameter
+        """
+        return BaseComponent.getParamWithToken(self, name, 'sub', self.other)
+
+    def getParamList(self):
+        """ Return a list of all available parameters for the model 
+        """
+        return BaseComponent.getParamListWithToken(self, 'sub', self.other)
+    
+# End of file
Index: sansmodels/src/sans/models/__init__.py
===================================================================
--- sansmodels/src/sans/models/__init__.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/__init__.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,74 @@
+"""
+    1D Modeling for SANS
+"""
+## \mainpage Analytical Modeling for SANS
+#
+# \section intro_sec Introduction
+# This module provides theoretical models for the scattering 
+# intensity for SANS. 
+#
+# Documentation can be found here: 
+#    http://danse.us/trac/sans/wiki/8_2_2_1DModelFitting
+#    http://danse.us/trac/sans/wiki/8_2_3_2DModeling
+#
+# \section install_sec Installation
+#
+# \subsection obtain Obtaining the Code
+#
+# The code is available here:
+# \verbatim
+#$ svn co svn://danse.us/sans/sansmodels
+# \endverbatim
+#
+# \subsection depends External Dependencies
+# None
+#
+# \subsection build Building the code
+# The standard python package can be built with distutils.
+# \verbatim
+#$ python setup.py build
+#$ python setup.py install
+# \endverbatim
+#
+# \section overview_sec Package Overview
+# 
+# \subsection class Class Diagram:
+# \image html class_diag.png
+# Note that the CCylinderModel is written as C code. 
+# CylinderModel acts as an adaptor class for the C extension.
+# Most model classes will be written that way.
+#
+# \subsection behav Behavior enumeration for pyre-level architecture:
+# \image html behavior_pyre.png
+#
+# \subsection behav Behavior enumeration for under-lying architecture:
+# \image html behavior.jpg
+#
+# \subsection Tutorial
+# To create a model:
+# \verbatim
+#from sans.models.ModelFactory import ModelFactory
+#    cyl = ModelFactory().getModel('CylinderModel')
+# \endverbatim
+#
+# To evaluate a model (at x=0.1 in this example):
+# \verbatim
+#    output = cyl.run(0.1)
+# \endverbatim
+#
+# To change a parameter:
+# \verbatim
+#    cyl.setParam('scale', 0.1)
+# \endverbatim
+#
+# To get the value of a parameter:
+# \verbatim
+#    cyl.getParam('scale')
+# \endverbatim
+#
+# Other examples are available as unit tests under sans.models.test.
+#
+# \section help_sec Contact Info
+# Code and Documentation by Mathieu Doucet as part of the DANSE project.
+
+__author__ = 'Mathieu Doucet'
Index: sansmodels/src/sans/models/c_extensions/CCoreShellCylinderModel.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/CCoreShellCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/CCoreShellCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,311 @@
+/** CCoreShellCylinderModel
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY core_shell_cylinder.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "core_shell_cylinder.h"
+
+/// Error object for raised exceptions
+static PyObject * CCoreShellCylinderModelError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	CoreShellCylinderParameters model_pars;
+} CCoreShellCylinderModel;
+
+
+static void
+CCoreShellCylinderModel_dealloc(CCoreShellCylinderModel* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CCoreShellCylinderModel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CCoreShellCylinderModel *self;
+    
+    self = (CCoreShellCylinderModel *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CCoreShellCylinderModel_init(CCoreShellCylinderModel *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"axis_theta",Py_BuildValue("d",1.570000));
+        PyDict_SetItemString(self->params,"solvent_sld",Py_BuildValue("d",0.000001));
+        PyDict_SetItemString(self->params,"thickness",Py_BuildValue("d",10.000000));
+        PyDict_SetItemString(self->params,"axis_phi",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"length",Py_BuildValue("d",400.000000));
+        PyDict_SetItemString(self->params,"core_sld",Py_BuildValue("d",0.000001));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"background",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"shell_sld",Py_BuildValue("d",0.000004));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CCoreShellCylinderModel_members[] = {
+    {"params", T_OBJECT, offsetof(CCoreShellCylinderModel, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CCoreShellCylinderModel, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CCoreShellCylinderModel_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CCoreShellCylinderModel *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.axis_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "axis_theta") );
+    self->model_pars.solvent_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "solvent_sld") );
+    self->model_pars.thickness = PyFloat_AsDouble( PyDict_GetItemString(self->params, "thickness") );
+    self->model_pars.axis_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "axis_phi") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.core_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "core_sld") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.shell_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "shell_sld") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CCoreShellCylinderModelError, 
+	    	"CCoreShellCylinderModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CCoreShellCylinderModelError, 
+	    		"CCoreShellCylinderModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CCoreShellCylinderModel_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CCoreShellCylinderModel_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",core_shell_cylinder_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CCoreShellCylinderModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",core_shell_cylinder_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY(CCoreShellCylinderModel *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.axis_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "axis_theta") );
+    self->model_pars.solvent_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "solvent_sld") );
+    self->model_pars.thickness = PyFloat_AsDouble( PyDict_GetItemString(self->params, "thickness") );
+    self->model_pars.axis_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "axis_phi") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.core_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "core_sld") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.shell_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "shell_sld") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CCoreShellCylinderModelError, 
+	    	"CCoreShellCylinderModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CCoreShellCylinderModelError, 
+	    		"CCoreShellCylinderModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = CCoreShellCylinderModel_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = CCoreShellCylinderModel_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",core_shell_cylinder_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = CCoreShellCylinderModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",core_shell_cylinder_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset(CCoreShellCylinderModel *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CCoreShellCylinderModel_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CCoreShellCylinderModelType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CCoreShellCylinderModel",             /*tp_name*/
+    sizeof(CCoreShellCylinderModel),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CCoreShellCylinderModel_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*/
+    "CCoreShellCylinderModel objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CCoreShellCylinderModel_methods,             /* tp_methods */
+    CCoreShellCylinderModel_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CCoreShellCylinderModel_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CCoreShellCylinderModel_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 addCCoreShellCylinderModel(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CCoreShellCylinderModelType) < 0)
+        return;
+
+    Py_INCREF(&CCoreShellCylinderModelType);
+    PyModule_AddObject(module, "CCoreShellCylinderModel", (PyObject *)&CCoreShellCylinderModelType);
+    
+    d = PyModule_GetDict(module);
+    CCoreShellCylinderModelError = PyErr_NewException("CCoreShellCylinderModel.error", NULL, NULL);
+    PyDict_SetItemString(d, "CCoreShellCylinderModelError", CCoreShellCylinderModelError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/CCoreShellModel.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/CCoreShellModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/CCoreShellModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,302 @@
+/** CCoreShellModel
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY core_shell.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "core_shell.h"
+
+/// Error object for raised exceptions
+static PyObject * CCoreShellModelError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	CoreShellParameters model_pars;
+} CCoreShellModel;
+
+
+static void
+CCoreShellModel_dealloc(CCoreShellModel* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CCoreShellModel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CCoreShellModel *self;
+    
+    self = (CCoreShellModel *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CCoreShellModel_init(CCoreShellModel *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"core_sld",Py_BuildValue("d",0.000001));
+        PyDict_SetItemString(self->params,"thickness",Py_BuildValue("d",10.000000));
+        PyDict_SetItemString(self->params,"solvent_sld",Py_BuildValue("d",0.000003));
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",60.000000));
+        PyDict_SetItemString(self->params,"background",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"shell_sld",Py_BuildValue("d",0.000002));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CCoreShellModel_members[] = {
+    {"params", T_OBJECT, offsetof(CCoreShellModel, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CCoreShellModel, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CCoreShellModel_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CCoreShellModel *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.core_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "core_sld") );
+    self->model_pars.thickness = PyFloat_AsDouble( PyDict_GetItemString(self->params, "thickness") );
+    self->model_pars.solvent_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "solvent_sld") );
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.shell_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "shell_sld") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CCoreShellModelError, 
+	    	"CCoreShellModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CCoreShellModelError, 
+	    		"CCoreShellModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CCoreShellModel_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CCoreShellModel_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",core_shell_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CCoreShellModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",core_shell_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY(CCoreShellModel *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.core_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "core_sld") );
+    self->model_pars.thickness = PyFloat_AsDouble( PyDict_GetItemString(self->params, "thickness") );
+    self->model_pars.solvent_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "solvent_sld") );
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.shell_sld = PyFloat_AsDouble( PyDict_GetItemString(self->params, "shell_sld") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CCoreShellModelError, 
+	    	"CCoreShellModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CCoreShellModelError, 
+	    		"CCoreShellModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = CCoreShellModel_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = CCoreShellModel_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",core_shell_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = CCoreShellModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",core_shell_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset(CCoreShellModel *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CCoreShellModel_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CCoreShellModelType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CCoreShellModel",             /*tp_name*/
+    sizeof(CCoreShellModel),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CCoreShellModel_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*/
+    "CCoreShellModel objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CCoreShellModel_methods,             /* tp_methods */
+    CCoreShellModel_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CCoreShellModel_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CCoreShellModel_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 addCCoreShellModel(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CCoreShellModelType) < 0)
+        return;
+
+    Py_INCREF(&CCoreShellModelType);
+    PyModule_AddObject(module, "CCoreShellModel", (PyObject *)&CCoreShellModelType);
+    
+    d = PyModule_GetDict(module);
+    CCoreShellModelError = PyErr_NewException("CCoreShellModel.error", NULL, NULL);
+    PyDict_SetItemString(d, "CCoreShellModelError", CCoreShellModelError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/CCylinderModel.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/CCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/CCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,302 @@
+/** CCylinderModel
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY cylinder.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "cylinder.h"
+
+/// Error object for raised exceptions
+static PyObject * CCylinderModelError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	CylinderParameters model_pars;
+} CCylinderModel;
+
+
+static void
+CCylinderModel_dealloc(CCylinderModel* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CCylinderModel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CCylinderModel *self;
+    
+    self = (CCylinderModel *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CCylinderModel_init(CCylinderModel *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"length",Py_BuildValue("d",400.000000));
+        PyDict_SetItemString(self->params,"cyl_theta",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"background",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"contrast",Py_BuildValue("d",0.000003));
+        PyDict_SetItemString(self->params,"cyl_phi",Py_BuildValue("d",1.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CCylinderModel_members[] = {
+    {"params", T_OBJECT, offsetof(CCylinderModel, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CCylinderModel, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CCylinderModel_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CCylinderModel *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.cyl_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_theta") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+    self->model_pars.cyl_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_phi") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CCylinderModelError, 
+	    	"CCylinderModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CCylinderModelError, 
+	    		"CCylinderModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CCylinderModel_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CCylinderModel_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",cylinder_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CCylinderModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",cylinder_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY(CCylinderModel *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.cyl_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_theta") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+    self->model_pars.cyl_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_phi") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CCylinderModelError, 
+	    	"CCylinderModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CCylinderModelError, 
+	    		"CCylinderModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = CCylinderModel_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = CCylinderModel_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",cylinder_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = CCylinderModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",cylinder_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset(CCylinderModel *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CCylinderModel_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CCylinderModelType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CCylinderModel",             /*tp_name*/
+    sizeof(CCylinderModel),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CCylinderModel_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*/
+    "CCylinderModel objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CCylinderModel_methods,             /* tp_methods */
+    CCylinderModel_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CCylinderModel_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CCylinderModel_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 addCCylinderModel(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CCylinderModelType) < 0)
+        return;
+
+    Py_INCREF(&CCylinderModelType);
+    PyModule_AddObject(module, "CCylinderModel", (PyObject *)&CCylinderModelType);
+    
+    d = PyModule_GetDict(module);
+    CCylinderModelError = PyErr_NewException("CCylinderModel.error", NULL, NULL);
+    PyDict_SetItemString(d, "CCylinderModelError", CCylinderModelError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/CEllipsoidModel.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/CEllipsoidModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/CEllipsoidModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,302 @@
+/** CEllipsoidModel
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY ellipsoid.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "ellipsoid.h"
+
+/// Error object for raised exceptions
+static PyObject * CEllipsoidModelError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	EllipsoidParameters model_pars;
+} CEllipsoidModel;
+
+
+static void
+CEllipsoidModel_dealloc(CEllipsoidModel* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CEllipsoidModel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CEllipsoidModel *self;
+    
+    self = (CEllipsoidModel *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CEllipsoidModel_init(CEllipsoidModel *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"axis_theta",Py_BuildValue("d",1.570000));
+        PyDict_SetItemString(self->params,"radius_b",Py_BuildValue("d",400.000000));
+        PyDict_SetItemString(self->params,"radius_a",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"axis_phi",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"background",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"contrast",Py_BuildValue("d",0.000003));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CEllipsoidModel_members[] = {
+    {"params", T_OBJECT, offsetof(CEllipsoidModel, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CEllipsoidModel, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CEllipsoidModel_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CEllipsoidModel *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.axis_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "axis_theta") );
+    self->model_pars.radius_b = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius_b") );
+    self->model_pars.radius_a = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius_a") );
+    self->model_pars.axis_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "axis_phi") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CEllipsoidModelError, 
+	    	"CEllipsoidModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CEllipsoidModelError, 
+	    		"CEllipsoidModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CEllipsoidModel_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CEllipsoidModel_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",ellipsoid_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CEllipsoidModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",ellipsoid_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY(CEllipsoidModel *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.axis_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "axis_theta") );
+    self->model_pars.radius_b = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius_b") );
+    self->model_pars.radius_a = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius_a") );
+    self->model_pars.axis_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "axis_phi") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CEllipsoidModelError, 
+	    	"CEllipsoidModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CEllipsoidModelError, 
+	    		"CEllipsoidModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = CEllipsoidModel_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = CEllipsoidModel_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",ellipsoid_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = CEllipsoidModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",ellipsoid_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset(CEllipsoidModel *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CEllipsoidModel_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CEllipsoidModelType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CEllipsoidModel",             /*tp_name*/
+    sizeof(CEllipsoidModel),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CEllipsoidModel_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*/
+    "CEllipsoidModel objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CEllipsoidModel_methods,             /* tp_methods */
+    CEllipsoidModel_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CEllipsoidModel_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CEllipsoidModel_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 addCEllipsoidModel(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CEllipsoidModelType) < 0)
+        return;
+
+    Py_INCREF(&CEllipsoidModelType);
+    PyModule_AddObject(module, "CEllipsoidModel", (PyObject *)&CEllipsoidModelType);
+    
+    d = PyModule_GetDict(module);
+    CEllipsoidModelError = PyErr_NewException("CEllipsoidModel.error", NULL, NULL);
+    PyDict_SetItemString(d, "CEllipsoidModelError", CEllipsoidModelError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/CEllipticalCylinderModel.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/CEllipticalCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/CEllipticalCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,308 @@
+/** CEllipticalCylinderModel
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY elliptical_cylinder.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "elliptical_cylinder.h"
+
+/// Error object for raised exceptions
+static PyObject * CEllipticalCylinderModelError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	EllipticalCylinderParameters model_pars;
+} CEllipticalCylinderModel;
+
+
+static void
+CEllipticalCylinderModel_dealloc(CEllipticalCylinderModel* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CEllipticalCylinderModel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CEllipticalCylinderModel *self;
+    
+    self = (CEllipticalCylinderModel *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CEllipticalCylinderModel_init(CEllipticalCylinderModel *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"cyl_psi",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"length",Py_BuildValue("d",400.000000));
+        PyDict_SetItemString(self->params,"r_minor",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"cyl_theta",Py_BuildValue("d",1.570000));
+        PyDict_SetItemString(self->params,"background",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"r_ratio",Py_BuildValue("d",1.500000));
+        PyDict_SetItemString(self->params,"contrast",Py_BuildValue("d",0.000003));
+        PyDict_SetItemString(self->params,"cyl_phi",Py_BuildValue("d",0.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CEllipticalCylinderModel_members[] = {
+    {"params", T_OBJECT, offsetof(CEllipticalCylinderModel, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CEllipticalCylinderModel, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CEllipticalCylinderModel_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CEllipticalCylinderModel *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.cyl_psi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_psi") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.r_minor = PyFloat_AsDouble( PyDict_GetItemString(self->params, "r_minor") );
+    self->model_pars.cyl_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_theta") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.r_ratio = PyFloat_AsDouble( PyDict_GetItemString(self->params, "r_ratio") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+    self->model_pars.cyl_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_phi") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CEllipticalCylinderModelError, 
+	    	"CEllipticalCylinderModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CEllipticalCylinderModelError, 
+	    		"CEllipticalCylinderModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CEllipticalCylinderModel_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CEllipticalCylinderModel_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",elliptical_cylinder_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CEllipticalCylinderModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",elliptical_cylinder_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY(CEllipticalCylinderModel *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.cyl_psi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_psi") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.r_minor = PyFloat_AsDouble( PyDict_GetItemString(self->params, "r_minor") );
+    self->model_pars.cyl_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_theta") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.r_ratio = PyFloat_AsDouble( PyDict_GetItemString(self->params, "r_ratio") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+    self->model_pars.cyl_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_phi") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CEllipticalCylinderModelError, 
+	    	"CEllipticalCylinderModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CEllipticalCylinderModelError, 
+	    		"CEllipticalCylinderModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = CEllipticalCylinderModel_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = CEllipticalCylinderModel_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",elliptical_cylinder_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = CEllipticalCylinderModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",elliptical_cylinder_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset(CEllipticalCylinderModel *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CEllipticalCylinderModel_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CEllipticalCylinderModelType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CEllipticalCylinderModel",             /*tp_name*/
+    sizeof(CEllipticalCylinderModel),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CEllipticalCylinderModel_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*/
+    "CEllipticalCylinderModel objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CEllipticalCylinderModel_methods,             /* tp_methods */
+    CEllipticalCylinderModel_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CEllipticalCylinderModel_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CEllipticalCylinderModel_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 addCEllipticalCylinderModel(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CEllipticalCylinderModelType) < 0)
+        return;
+
+    Py_INCREF(&CEllipticalCylinderModelType);
+    PyModule_AddObject(module, "CEllipticalCylinderModel", (PyObject *)&CEllipticalCylinderModelType);
+    
+    d = PyModule_GetDict(module);
+    CEllipticalCylinderModelError = PyErr_NewException("CEllipticalCylinderModel.error", NULL, NULL);
+    PyDict_SetItemString(d, "CEllipticalCylinderModelError", CEllipticalCylinderModelError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/CGaussian.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/CGaussian.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/CGaussian.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,290 @@
+/** CGaussian
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY gaussian.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "gaussian.h"
+
+/// Error object for raised exceptions
+static PyObject * CGaussianError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	GaussianParameters model_pars;
+} CGaussian;
+
+
+static void
+CGaussian_dealloc(CGaussian* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CGaussian_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CGaussian *self;
+    
+    self = (CGaussian *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CGaussian_init(CGaussian *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"sigma",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"center",Py_BuildValue("d",0.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CGaussian_members[] = {
+    {"params", T_OBJECT, offsetof(CGaussian, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CGaussian, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CGaussian_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CGaussian *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.sigma = PyFloat_AsDouble( PyDict_GetItemString(self->params, "sigma") );
+    self->model_pars.center = PyFloat_AsDouble( PyDict_GetItemString(self->params, "center") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CGaussianError, 
+	    	"CGaussian.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CGaussianError, 
+	    		"CGaussian.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CGaussian_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CGaussian_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",gaussian_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CGaussian_readDouble(pars);		
+		
+		return Py_BuildValue("d",gaussian_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY(CGaussian *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.sigma = PyFloat_AsDouble( PyDict_GetItemString(self->params, "sigma") );
+    self->model_pars.center = PyFloat_AsDouble( PyDict_GetItemString(self->params, "center") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CGaussianError, 
+	    	"CGaussian.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CGaussianError, 
+	    		"CGaussian.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = CGaussian_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = CGaussian_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",gaussian_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = CGaussian_readDouble(pars);		
+		
+		return Py_BuildValue("d",gaussian_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset(CGaussian *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CGaussian_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CGaussianType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CGaussian",             /*tp_name*/
+    sizeof(CGaussian),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CGaussian_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*/
+    "CGaussian objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CGaussian_methods,             /* tp_methods */
+    CGaussian_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CGaussian_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CGaussian_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 addCGaussian(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CGaussianType) < 0)
+        return;
+
+    Py_INCREF(&CGaussianType);
+    PyModule_AddObject(module, "CGaussian", (PyObject *)&CGaussianType);
+    
+    d = PyModule_GetDict(module);
+    CGaussianError = PyErr_NewException("CGaussian.error", NULL, NULL);
+    PyDict_SetItemString(d, "CGaussianError", CGaussianError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/CLorentzian.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/CLorentzian.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/CLorentzian.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,290 @@
+/** CLorentzian
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY lorentzian.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "lorentzian.h"
+
+/// Error object for raised exceptions
+static PyObject * CLorentzianError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	LorentzianParameters model_pars;
+} CLorentzian;
+
+
+static void
+CLorentzian_dealloc(CLorentzian* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CLorentzian_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CLorentzian *self;
+    
+    self = (CLorentzian *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CLorentzian_init(CLorentzian *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"center",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"gamma",Py_BuildValue("d",1.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CLorentzian_members[] = {
+    {"params", T_OBJECT, offsetof(CLorentzian, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CLorentzian, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CLorentzian_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CLorentzian *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.center = PyFloat_AsDouble( PyDict_GetItemString(self->params, "center") );
+    self->model_pars.gamma = PyFloat_AsDouble( PyDict_GetItemString(self->params, "gamma") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CLorentzianError, 
+	    	"CLorentzian.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CLorentzianError, 
+	    		"CLorentzian.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CLorentzian_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CLorentzian_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",lorentzian_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CLorentzian_readDouble(pars);		
+		
+		return Py_BuildValue("d",lorentzian_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY(CLorentzian *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.center = PyFloat_AsDouble( PyDict_GetItemString(self->params, "center") );
+    self->model_pars.gamma = PyFloat_AsDouble( PyDict_GetItemString(self->params, "gamma") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CLorentzianError, 
+	    	"CLorentzian.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CLorentzianError, 
+	    		"CLorentzian.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = CLorentzian_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = CLorentzian_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",lorentzian_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = CLorentzian_readDouble(pars);		
+		
+		return Py_BuildValue("d",lorentzian_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset(CLorentzian *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CLorentzian_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CLorentzianType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CLorentzian",             /*tp_name*/
+    sizeof(CLorentzian),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CLorentzian_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*/
+    "CLorentzian objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CLorentzian_methods,             /* tp_methods */
+    CLorentzian_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CLorentzian_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CLorentzian_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 addCLorentzian(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CLorentzianType) < 0)
+        return;
+
+    Py_INCREF(&CLorentzianType);
+    PyModule_AddObject(module, "CLorentzian", (PyObject *)&CLorentzianType);
+    
+    d = PyModule_GetDict(module);
+    CLorentzianError = PyErr_NewException("CLorentzian.error", NULL, NULL);
+    PyDict_SetItemString(d, "CLorentzianError", CLorentzianError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/CSphereModel.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/CSphereModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/CSphereModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,293 @@
+/** CSphereModel
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY sphere.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "sphere.h"
+
+/// Error object for raised exceptions
+static PyObject * CSphereModelError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	SphereParameters model_pars;
+} CSphereModel;
+
+
+static void
+CSphereModel_dealloc(CSphereModel* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CSphereModel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CSphereModel *self;
+    
+    self = (CSphereModel *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CSphereModel_init(CSphereModel *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",0.000001));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",60.000000));
+        PyDict_SetItemString(self->params,"background",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"contrast",Py_BuildValue("d",1.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CSphereModel_members[] = {
+    {"params", T_OBJECT, offsetof(CSphereModel, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CSphereModel, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CSphereModel_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CSphereModel *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CSphereModelError, 
+	    	"CSphereModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CSphereModelError, 
+	    		"CSphereModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CSphereModel_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CSphereModel_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",sphere_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CSphereModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",sphere_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY(CSphereModel *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CSphereModelError, 
+	    	"CSphereModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CSphereModelError, 
+	    		"CSphereModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = CSphereModel_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = CSphereModel_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",sphere_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = CSphereModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",sphere_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset(CSphereModel *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CSphereModel_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CSphereModelType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CSphereModel",             /*tp_name*/
+    sizeof(CSphereModel),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CSphereModel_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*/
+    "CSphereModel objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CSphereModel_methods,             /* tp_methods */
+    CSphereModel_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CSphereModel_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CSphereModel_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 addCSphereModel(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CSphereModelType) < 0)
+        return;
+
+    Py_INCREF(&CSphereModelType);
+    PyModule_AddObject(module, "CSphereModel", (PyObject *)&CSphereModelType);
+    
+    d = PyModule_GetDict(module);
+    CSphereModelError = PyErr_NewException("CSphereModel.error", NULL, NULL);
+    PyDict_SetItemString(d, "CSphereModelError", CSphereModelError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/WrapperGenerator.py
===================================================================
--- sansmodels/src/sans/models/c_extensions/WrapperGenerator.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/WrapperGenerator.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,314 @@
+#!/usr/bin/env python
+""" WrapperGenerator class to generate model code automatically.
+"""
+
+import os, sys
+
+class WrapperGenerator:
+    """ Python wrapper generator for C models
+    
+        The developer must provide a header file describing 
+        the new model.
+        
+        To provide the name of the Python class to be
+        generated, the .h file must contain the following 
+        string in the comments:
+        
+        // [PYTHONCLASS] = my_model
+        
+        where my_model must be replaced by the name of the
+        class that you want to import from sans.models.
+        (example: [PYTHONCLASS] = MyModel
+          will create a class MyModel in sans.models.MyModel.
+          It will also create a class CMyModel in 
+          sans_extension.c_models.)
+          
+        Also in comments, each parameter of the params 
+        dictionary must be declared with a default value
+        in the following way:
+        
+        // [DEFAULT]=param_name=default_value
+        
+        (example:
+            //  [DEFAULT]=radius=20.0
+        )
+          
+        See cylinder.h for an example.
+        
+        
+        A .c file corresponding to the .h file should also
+        be provided (example: my_model.h, my_model.c).
+    
+        The .h file should define two function definitions. For example,
+        cylinder.h defines the following:
+        
+            /// 1D scattering function
+            double cylinder_analytical_1D(CylinderParameters *pars, double q);
+            
+            /// 2D scattering function
+            double cylinder_analytical_2D(CylinderParameters *pars, double q, double phi);
+            
+        The .c file implements those functions.
+        
+        @author: Mathieu Doucet / UTK
+        @contact: mathieu.doucet@nist.gov
+    """
+    
+    def __init__(self, filename):
+        """ Initialization """
+        
+        ## Name of .h file to generate wrapper from
+        self.file = filename
+        
+        # Info read from file
+        
+        ## Name of python class to write
+        self.pythonClass = None
+        ## Parser in struct section
+        self.inStruct = False
+        ## Name of struct for the c object
+        self.structName = None
+        ## Dictionary of parameters
+        self.params = {}
+        ## ModelCalculation module flag
+        self.modelCalcFlag = False
+        ## List of default parameters (text)
+        self.default_list = ""
+        ## Dictionary of units
+        self.details = ""
+        
+    def __repr__(self):
+        """ Simple output for printing """
+        
+        rep  = "Python class: %s\n" % self.pythonClass
+        rep += "  struc name: %s\n" % self.structName
+        rep += "  params:     %s\n" % self.params
+        return rep
+        
+    def read(self):
+        """ Reads in the .h file to catch parameters of the wrapper """
+        
+        # Check if the file is there
+        if not os.path.isfile(self.file):
+            raise ValueError, "File %s is not a regular file" % self.file
+        
+        # Read file
+        f = open(self.file,'r')
+        buf = f.read()
+        
+        self.default_list = "List of default parameters:\n"
+        #lines = string.split(buf,'\n')
+        lines = buf.split('\n')
+        self.details  = "## Parameter details [units, min, max]\n"
+        self.details += "        self.details = {}\n"
+        for line in lines:
+            
+            # Catch class name
+            key = "[PYTHONCLASS]"
+            if line.count(key)>0:
+                try:
+                    index = line.index(key)
+                    #toks = string.split( line[index:], "=" )
+                    toks = line[index:].split("=" )
+                    self.pythonClass = toks[1].lstrip().rstrip()
+                except:
+                    raise ValueError, "Could not parse file %s" % self.file
+                
+            # Catch struct name
+            if line.count("typedef struct")>0:
+                # We are entering a struct block
+                self.inStruct = True
+            
+            if self.inStruct and line.count("}")>0:
+                # We are exiting a struct block
+                self.inStruct = False
+    
+                # Catch the name of the struct
+                index = line.index("}")
+                #toks = string.split(line[index+1:],";")
+                toks = line[index+1:].split(";")
+                # Catch pointer definition
+                #toks2 = string.split(toks[0],',')
+                toks2 = toks[0].split(',')
+                self.structName = toks2[0].lstrip().rstrip()
+                
+            # Catch struct content
+            key = "[DEFAULT]"
+            if self.inStruct and line.count(key)>0:
+                # Found a new parameter
+                try:
+                    index = line.index(key)
+                    toks = line[index:].split("=")
+                    toks2 = toks[2].split()
+                    val = float(toks2[0])
+                    self.params[toks[1]] = val
+                    #self.pythonClass = toks[1].lstrip().rstrip()
+                    units = ""
+                    if len(toks2) >= 2:
+                        units = toks2[1]
+                    self.default_list += "         %-15s = %s %s\n" % \
+                        (toks[1], val, units)
+                    
+                    # Check for min and max
+                    min = "None"
+                    max = "None"
+                    if len(toks2) == 4:
+                        min = toks2[2]
+                        max = toks2[3]
+                    
+                    self.details += "        self.details['%s'] = ['%s', %s, %s]\n" % \
+                        (toks[1].lstrip().rstrip(), units.lstrip().rstrip(), min, max)
+                except:
+                    raise ValueError, "Could not parse input file %s \n  %s" % \
+                        (self.file, sys.exc_value)
+                
+                
+            # Catch need for numerical calculations
+            key = "CalcParameters calcPars"
+            if line.count(key)>0:
+                self.modelCalcFlag = True
+                
+                
+                
+    def write_c_wrapper(self):
+        """ Writes the C file to create the python extension class 
+            The file is written in C[PYTHONCLASS].c
+        """
+        
+        file = open("C"+self.pythonClass+'.c', 'w')
+        template = open("classTemplate.txt", 'r')
+        
+        tmp_buf = template.read()
+        #tmp_lines = string.split(tmp_buf,'\n')
+        tmp_lines = tmp_buf.split('\n')
+        
+        for tmp_line in tmp_lines:
+            
+            # Catch class name
+            newline = self.replaceToken(tmp_line, 
+                                        "[PYTHONCLASS]", 'C'+self.pythonClass)
+            
+            # Catch class name
+            newline = self.replaceToken(newline, 
+                                        "[MODELSTRUCT]", self.structName)
+            
+            # Dictionary initialization
+            param_str = "// Initialize parameter dictionary\n"
+            for par in self.params:
+                param_str += "        PyDict_SetItemString(self->params,\"%s\",Py_BuildValue(\"d\",%f));\n" % \
+                    (par, self.params[par])
+                
+            newline = self.replaceToken(newline,
+                                        "[INITDICTIONARY]", param_str)
+            
+            # Read dictionary
+            param_str = "// Reader parameter dictionary\n"
+            for par in self.params:
+                param_str += "    self->model_pars.%s = PyFloat_AsDouble( PyDict_GetItemString(self->params, \"%s\") );\n" % \
+                    (par, par)
+                
+            newline = self.replaceToken(newline, "[READDICTIONARY]", param_str)
+                
+            # Name of .c file
+            #toks = string.split(self.file,'.')
+            toks = self.file.split('.')
+            newline = self.replaceToken(newline, "[C_FILENAME]", toks[0])
+            
+            # Include file
+            newline = self.replaceToken(newline, 
+                                        "[INCLUDE_FILE]", self.file)           
+                
+            # Numerical calcs dealloc
+            dealloc_str = "\n"
+            if self.modelCalcFlag:
+                dealloc_str = "    modelcalculations_dealloc(&(self->model_pars.calcPars));\n"
+            newline = self.replaceToken(newline, 
+                                        "[NUMERICAL_DEALLOC]", dealloc_str)     
+                
+            # Numerical calcs init
+            init_str = "\n"
+            if self.modelCalcFlag:
+                init_str = "        modelcalculations_init(&(self->model_pars.calcPars));\n"
+            newline = self.replaceToken(newline, 
+                                        "[NUMERICAL_INIT]", init_str)     
+                
+            # Numerical calcs reset
+            reset_str = "\n"
+            if self.modelCalcFlag:
+                reset_str = "modelcalculations_reset(&(self->model_pars.calcPars));\n"
+            newline = self.replaceToken(newline, 
+                                        "[NUMERICAL_RESET]", reset_str)     
+                
+            # Write new line to the wrapper .c file
+            file.write(newline+'\n')
+            
+            
+        file.close()
+        
+    def write_python_wrapper(self):
+        """ Writes the python file to create the python extension class 
+            The file is written in ../[PYTHONCLASS].py
+        """
+        
+        file = open("../"+self.pythonClass+'.py', 'w')
+        template = open("modelTemplate.txt", 'r')
+        
+        tmp_buf = template.read()
+        tmp_lines = tmp_buf.split('\n')
+        
+        for tmp_line in tmp_lines:
+            
+            # Catch class name
+            newline = self.replaceToken(tmp_line, 
+                                        "[CPYTHONCLASS]", 'C'+self.pythonClass)
+            
+            # Catch class name
+            newline = self.replaceToken(newline, 
+                                        "[PYTHONCLASS]", self.pythonClass)
+            
+            # Include file
+            newline = self.replaceToken(newline, 
+                                        "[INCLUDE_FILE]", self.file)    
+                   
+            # Include file
+            newline = self.replaceToken(newline, 
+                                        "[DEFAULT_LIST]", self.default_list)
+
+            # Parameter details
+            newline = self.replaceToken(newline, 
+                                        "[PAR_DETAILS]", self.details)
+
+            # Write new line to the wrapper .c file
+            file.write(newline+'\n')
+               
+        file.close()
+        
+        
+    def replaceToken(self, line, key, value): #pylint: disable-msg=R0201
+        """ Replace a token in the template file 
+            @param line: line of text to inspect
+            @param key: token to look for
+            @param value: string value to replace the token with
+            @return: new string value
+        """
+        lenkey = len(key)
+        newline = line
+        while newline.count(key)>0:
+            index = newline.index(key)
+            newline = newline[:index]+value+newline[index+lenkey:]
+        return newline
+        
+        
+# main
+if __name__ == '__main__':
+    if len(sys.argv)>1:
+        print "Will look for file %s" % sys.argv[1]
+        app = WrapperGenerator(sys.argv[1])
+    else:
+        app = WrapperGenerator("test.h")
+    app.read()
+    app.write_c_wrapper()
+    app.write_python_wrapper()
+    print app
+   
+# End of file        
Index: sansmodels/src/sans/models/c_extensions/c_models.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/c_models.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/c_models.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,38 @@
+/** c_models
+ *
+ * Module containing all SANS model extensions 
+ *
+ * @author   M.Doucet / UTK
+ */
+#include <Python.h>
+
+/**
+ * Define empty module
+ */
+static PyMethodDef module_methods[] = {
+    {NULL} 
+};
+
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+initc_models(void) 
+{
+    PyObject* m;
+
+    m = Py_InitModule3("c_models", module_methods,
+                       "C extension module for SANS scattering models.");
+                       
+	addCCylinderModel(m);
+	addCCoreShellCylinderModel(m);
+	addCCoreShellModel(m);
+	addCEllipsoidModel(m);
+	addCSphereModel(m);
+	addCEllipticalCylinderModel(m);
+	addDisperser(m);
+	addCGaussian(m);
+	addCLorentzian(m);
+
+}
Index: sansmodels/src/sans/models/c_extensions/classTemplate.txt
===================================================================
--- sansmodels/src/sans/models/c_extensions/classTemplate.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/classTemplate.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,274 @@
+/** [PYTHONCLASS]
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY [INCLUDE_FILE]
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "[INCLUDE_FILE]"
+
+/// Error object for raised exceptions
+static PyObject * [PYTHONCLASS]Error = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	[MODELSTRUCT] model_pars;
+} [PYTHONCLASS];
+
+
+static void
+[PYTHONCLASS]_dealloc([PYTHONCLASS]* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    [NUMERICAL_DEALLOC]
+}
+
+static PyObject *
+[PYTHONCLASS]_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    [PYTHONCLASS] *self;
+    
+    self = ([PYTHONCLASS] *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+[PYTHONCLASS]_init([PYTHONCLASS] *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        [INITDICTIONARY]
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        [NUMERICAL_INIT]
+    }
+    return 0;
+}
+
+static PyMemberDef [PYTHONCLASS]_members[] = {
+    {"params", T_OBJECT, offsetof([PYTHONCLASS], params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof([PYTHONCLASS], log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double [PYTHONCLASS]_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run([PYTHONCLASS] *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	[READDICTIONARY]
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString([PYTHONCLASS]Error, 
+	    	"[PYTHONCLASS].run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString([PYTHONCLASS]Error, 
+	    		"[PYTHONCLASS].run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = [PYTHONCLASS]_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = [PYTHONCLASS]_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",[C_FILENAME]_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = [PYTHONCLASS]_readDouble(pars);		
+		
+		return Py_BuildValue("d",[C_FILENAME]_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+/**
+ * Function to call to evaluate model in cartesian coordinates
+ * @param args: input q or [qx, qy]]
+ * @return: function value
+ */
+static PyObject * runXY([PYTHONCLASS] *self, PyObject *args) {
+	double qx_value, qy_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	[READDICTIONARY]
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString([PYTHONCLASS]Error, 
+	    	"[PYTHONCLASS].run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(qx, qy))
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString([PYTHONCLASS]Error, 
+	    		"[PYTHONCLASS].run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the qx and qy values at which
+	    // to evaluate I(qx,qy)
+	    qx_value = [PYTHONCLASS]_readDouble(PyList_GET_ITEM(pars,0));
+	    qy_value = [PYTHONCLASS]_readDouble(PyList_GET_ITEM(pars,1));
+		return Py_BuildValue("d",[C_FILENAME]_analytical_2DXY(&(self->model_pars),qx_value,qy_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		qx_value = [PYTHONCLASS]_readDouble(pars);		
+		
+		return Py_BuildValue("d",[C_FILENAME]_analytical_1D(&(self->model_pars),qx_value));
+	}	
+}
+
+static PyObject * reset([PYTHONCLASS] *self, PyObject *args) {
+    [NUMERICAL_RESET]
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef [PYTHONCLASS]_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q or Q, phi"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given Q or Qx, Qy"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject [PYTHONCLASS]Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "[PYTHONCLASS]",             /*tp_name*/
+    sizeof([PYTHONCLASS]),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)[PYTHONCLASS]_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*/
+    "[PYTHONCLASS] objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    [PYTHONCLASS]_methods,             /* tp_methods */
+    [PYTHONCLASS]_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)[PYTHONCLASS]_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    [PYTHONCLASS]_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 add[PYTHONCLASS](PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&[PYTHONCLASS]Type) < 0)
+        return;
+
+    Py_INCREF(&[PYTHONCLASS]Type);
+    PyModule_AddObject(module, "[PYTHONCLASS]", (PyObject *)&[PYTHONCLASS]Type);
+    
+    d = PyModule_GetDict(module);
+    [PYTHONCLASS]Error = PyErr_NewException("[PYTHONCLASS].error", NULL, NULL);
+    PyDict_SetItemString(d, "[PYTHONCLASS]Error", [PYTHONCLASS]Error);
+}
Index: sansmodels/src/sans/models/c_extensions/core_shell.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/core_shell.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/core_shell.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,43 @@
+/**
+ * Scattering model for a core-shell sphere
+ * @author: Mathieu Doucet / UTK
+ */
+
+#include "core_shell.h"
+#include "libSphere.h"
+#include <math.h>
+
+
+/**
+ * Function to evaluate 1D scattering function
+ * @param pars: parameters of the core-shell
+ * @param q: q-value
+ * @return: function value
+ */
+double core_shell_analytical_1D(CoreShellParameters *pars, double q) {
+	double dp[7];
+	
+	dp[0] = pars->scale;
+	dp[1] = pars->radius;
+	dp[2] = pars->thickness;
+	dp[3] = pars->core_sld;
+	dp[4] = pars->shell_sld;
+	dp[5] = pars->solvent_sld;
+	dp[6] = pars->background;
+	
+	return CoreShellForm(dp, q);
+}
+    
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the core-shell
+ * @param q: q-value
+ * @return: function value
+ */
+double core_shell_analytical_2D(CoreShellParameters *pars, double q, double phi) {
+	return core_shell_analytical_1D(pars,q);
+}
+
+double core_shell_analytical_2DXY(CoreShellParameters *pars, double qx, double qy){
+	return core_shell_analytical_1D(pars,sqrt(qx*qx+qy*qy));	
+}
Index: sansmodels/src/sans/models/c_extensions/core_shell.h
===================================================================
--- sansmodels/src/sans/models/c_extensions/core_shell.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/core_shell.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,41 @@
+#if !defined(core_shell_h)
+#define core_shell_h
+
+/** 
+ * Structure definition for core-shell parameters 
+ */
+ //[PYTHONCLASS] = CoreShellModel
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    ///	Core Radius (A) 60.0
+    //  [DEFAULT]=radius=60.0 A
+    double radius;
+    /// Shell Thickness (A) 10.0
+    //  [DEFAULT]=thickness=10 A
+    double thickness;
+    ///	Core SLD (Å-2) 1.0e-6
+    //  [DEFAULT]=core_sld=1.0e-6 A-2
+    double core_sld;
+	/// Shell SLD (Å-2) 2.0e-6
+	//  [DEFAULT]=shell_sld=2.0e-6 A-2
+	double shell_sld;
+	/// Solvent SLD (Å-2) 3.0e-6
+	//  [DEFAULT]=solvent_sld=3.0e-6 A-2
+	double solvent_sld;
+	/// Incoherent Background (cm-1) 0.000
+	//  [DEFAULT]=background=0 cm-1
+	double background;    
+} CoreShellParameters;
+
+
+
+/// 1D scattering function
+double core_shell_analytical_1D(CoreShellParameters *pars, double q);
+
+/// 2D scattering function
+double core_shell_analytical_2D(CoreShellParameters *pars, double q, double phi);
+double core_shell_analytical_2DXY(CoreShellParameters *pars, double qx, double qy);
+
+#endif
Index: sansmodels/src/sans/models/c_extensions/core_shell_cylinder.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/core_shell_cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/core_shell_cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,111 @@
+/**
+ * Scattering model for a core-shell cylinder
+ * @author: Mathieu Doucet / UTK
+ */
+
+#include "core_shell_cylinder.h"
+#include "libCylinder.h"
+#include <math.h>
+#include <stdio.h>
+
+
+/**
+ * Function to evaluate 1D scattering function
+ * @param pars: parameters of the core-shell cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double core_shell_cylinder_analytical_1D(CoreShellCylinderParameters *pars, double q) {
+	double dp[8];
+	
+	dp[0] = pars->scale;
+	dp[1] = pars->radius;
+	dp[2] = pars->thickness;
+	dp[3] = pars->length;
+	dp[4] = pars->core_sld;
+	dp[5] = pars->shell_sld;
+	dp[6] = pars->solvent_sld;
+	dp[7] = pars->background;
+	
+	return CoreShellCylinder(dp, q);
+}
+    
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the core-shell cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double core_shell_cylinder_analytical_2DXY(CoreShellCylinderParameters *pars, double qx, double qy) {
+	double q;
+	q = sqrt(qx*qx+qy*qy);
+    return core_shell_cylinder_analytical_2D_scaled(pars, q, qx/q, qy/q);
+} 
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the core-shell cylinder
+ * @param q: q-value
+ * @param phi: angle phi
+ * @return: function value
+ */
+double core_shell_cylinder_analytical_2D(CoreShellCylinderParameters *pars, double q, double phi) {
+    return core_shell_cylinder_analytical_2D_scaled(pars, q, cos(phi), sin(phi));
+} 
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the core-shell cylinder
+ * @param q: q-value
+ * @param q_x: q_x / q
+ * @param q_y: q_y / q
+ * @return: function value
+ */
+double core_shell_cylinder_analytical_2D_scaled(CoreShellCylinderParameters *pars, double q, double q_x, double q_y) {
+	double cyl_x, cyl_y, cyl_z;
+	double q_z, lenq;
+	double theta, alpha, f, vol, sin_val, cos_val;
+	double answer;
+        
+    // Cylinder orientation
+    cyl_x = sin(pars->axis_theta) * cos(pars->axis_phi);
+    cyl_y = sin(pars->axis_theta) * sin(pars->axis_phi);
+    cyl_z = cos(pars->axis_theta);
+     
+    // q vector
+    q_z = 0;
+        
+    // Compute the angle btw vector q and the
+    // axis of the cylinder
+    cos_val = cyl_x*q_x + cyl_y*q_y + cyl_z*q_z;
+    
+    // The following test should always pass
+    if (fabs(cos_val)>1.0) {
+    	printf("core_shell_cylinder_analytical_2D: Unexpected error: cos(alpha)=%g\n", cos_val);
+     	return 0;
+    }
+    
+	alpha = acos( cos_val );
+	
+	// Call the IGOR library function to get the kernel
+	answer = CoreShellCylKernel(q, pars->radius, pars->thickness, 
+								pars->core_sld,pars->shell_sld, 
+								pars->solvent_sld, pars->length/2.0, alpha) / sin(alpha);
+	
+	//normalize by cylinder volume
+	vol=acos(-1.0)*(pars->radius+pars->thickness)
+			*(pars->radius+pars->thickness)
+			*(pars->length+2.0*pars->thickness);
+	answer /= vol;
+	
+	//convert to [cm-1]
+	answer *= 1.0e8;
+	
+	//Scale
+	answer *= pars->scale;
+	
+	// add in the background
+	answer += pars->background;
+	
+	return answer;
+}
Index: sansmodels/src/sans/models/c_extensions/core_shell_cylinder.h
===================================================================
--- sansmodels/src/sans/models/c_extensions/core_shell_cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/core_shell_cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,61 @@
+#if !defined(core_shell_cylinder_h)
+#define core_shell_cylinder_h
+
+/** 
+ * Structure definition for core-shell cylinder parameters 
+ */
+ //[PYTHONCLASS] = CoreShellCylinderModel
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    
+    /// Core radius [A]
+    //  [DEFAULT]=radius=20.0 A
+    double radius;
+    
+    /// Shell thickness [A]
+    //  [DEFAULT]=thickness=10.0 A
+    double thickness;
+    
+    /// Core length [A]
+    //  [DEFAULT]=length=400.0 A
+    double length;
+    
+    /// Core SLD [A-2]
+    //  [DEFAULT]=core_sld=1.0e-6 A-2
+    double core_sld;
+    
+    /// Shell SLD [A-2]
+    //  [DEFAULT]=shell_sld=4.0e-6 A-2
+    double shell_sld;
+    
+    /// Solvent SLD [A-2]
+    //  [DEFAULT]=solvent_sld=1.0e-6 A-2
+    double solvent_sld;
+    
+	/// Incoherent Background [cm-1]
+	//  [DEFAULT]=background=0 cm-1
+	double background;    
+	
+    /// Orientation of the long axis of the core-shell cylinder w/respect incoming beam [rad]
+    //  [DEFAULT]=axis_theta=1.57 rad
+    double axis_theta;
+    
+    /// Orientation of the long axis of the core-shell cylinder in the plane of the detector [rad]
+    //  [DEFAULT]=axis_phi=0.0 rad
+    double axis_phi;    
+	
+} CoreShellCylinderParameters;
+
+
+
+/// 1D scattering function
+double core_shell_cylinder_analytical_1D(CoreShellCylinderParameters *pars, double q);
+
+/// 2D scattering function
+double core_shell_cylinder_analytical_2D(CoreShellCylinderParameters *pars, double q, double phi);
+double core_shell_cylinder_analytical_2DXY(CoreShellCylinderParameters *pars, double qx, double qy);
+double core_shell_cylinder_analytical_2D_scaled(CoreShellCylinderParameters *pars, double q, double q_x, double q_y);
+
+#endif
Index: sansmodels/src/sans/models/c_extensions/cylinder.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,115 @@
+/**
+ * Scattering model for a cylinder
+ * @author: Mathieu Doucet / UTK
+ */
+
+#include "cylinder.h"
+#include <math.h>
+#include "libCylinder.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/**
+ * Function to evaluate 1D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double cylinder_analytical_1D(CylinderParameters *pars, double q) {
+	double dp[5];
+	
+	// Fill paramater array
+	dp[0] = pars->scale;
+	dp[1] = pars->radius;
+	dp[2] = pars->length;
+	dp[3] = pars->contrast;
+	dp[4] = pars->background;
+	
+	// Call library function to evaluate model
+	return CylinderForm(dp, q);	
+}
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double cylinder_analytical_2DXY(CylinderParameters *pars, double qx, double qy) {
+	double q;
+	q = sqrt(qx*qx+qy*qy);
+    return cylinder_analytical_2D_scaled(pars, q, qx/q, qy/q);
+} 
+
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @param phi: angle phi
+ * @return: function value
+ */
+double cylinder_analytical_2D(CylinderParameters *pars, double q, double phi) {
+    return cylinder_analytical_2D_scaled(pars, q, cos(phi), sin(phi));
+} 
+        
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @param q_x: q_x / q
+ * @param q_y: q_y / q
+ * @return: function value
+ */
+double cylinder_analytical_2D_scaled(CylinderParameters *pars, double q, double q_x, double q_y) {
+	double cyl_x, cyl_y, cyl_z;
+	double q_z;
+	double alpha, vol, cos_val;
+	double answer;
+        
+    // Cylinder orientation
+    cyl_x = sin(pars->cyl_theta) * cos(pars->cyl_phi);
+    cyl_y = sin(pars->cyl_theta) * sin(pars->cyl_phi);
+    cyl_z = cos(pars->cyl_theta);
+     
+    // q vector
+    q_z = 0;
+        
+    // Compute the angle btw vector q and the
+    // axis of the cylinder
+    cos_val = cyl_x*q_x + cyl_y*q_y + cyl_z*q_z;
+    
+    // The following test should always pass
+    if (fabs(cos_val)>1.0) {
+    	printf("cyl_ana_2D: Unexpected error: cos(alpha)>1\n");
+     	return 0;
+    }
+    
+    // Note: cos(alpha) = 0 and 1 will get an
+    // undefined value from CylKernel
+	alpha = acos( cos_val );
+	
+	// Call the IGOR library function to get the kernel
+	answer = CylKernel(q, pars->radius, pars->length/2.0, alpha) / sin(alpha);
+	
+	// Multiply by contrast^2
+	answer *= pars->contrast*pars->contrast;
+	
+	//normalize by cylinder volume
+	//NOTE that for this (Fournet) definition of the integral, one must MULTIPLY by Vcyl
+    vol = acos(-1.0) * pars->radius * pars->radius * pars->length;
+	answer *= vol;
+	
+	//convert to [cm-1]
+	answer *= 1.0e8;
+	
+	//Scale
+	answer *= pars->scale;
+	
+	// add in the background
+	answer += pars->background;
+	
+	return answer;
+}
+    
Index: sansmodels/src/sans/models/c_extensions/cylinder.h
===================================================================
--- sansmodels/src/sans/models/c_extensions/cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,41 @@
+#if !defined(cylinder_h)
+#define cylinder_h
+
+/** Structure definition for cylinder parameters 
+ * [PYTHONCLASS] = CylinderModel
+ * */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    /// Radius of the cylinder [A]
+    //  [DEFAULT]=radius=20.0 A 
+    double radius;
+    /// Length of the cylinder [A]
+    //  [DEFAULT]=length=400.0 A
+    double length;
+    /// Contrast [A-2]
+    //  [DEFAULT]=contrast=3.0e-6 A-2
+    double contrast;
+	/// Incoherent Background (cm-1) 0.000
+	//  [DEFAULT]=background=0 cm-1
+	double background;    
+    /// Orientation of the cylinder axis w/respect incoming beam [rad]
+    //  [DEFAULT]=cyl_theta=1.0 rad
+    double cyl_theta;
+    /// Orientation of the cylinder in the plane of the detector [rad]
+    //  [DEFAULT]=cyl_phi=1.0 rad
+    double cyl_phi;    
+} CylinderParameters;
+
+
+
+/// 1D scattering function
+double cylinder_analytical_1D(CylinderParameters *pars, double q);
+
+/// 2D scattering function
+double cylinder_analytical_2D(CylinderParameters *pars, double q, double phi);
+double cylinder_analytical_2DXY(CylinderParameters *pars, double qx, double qy);
+double cylinder_analytical_2D_scaled(CylinderParameters *pars, double q, double q_x, double q_y);
+
+#endif
Index: sansmodels/src/sans/models/c_extensions/disperser.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/disperser.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/disperser.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,470 @@
+/** 
+ * Disperser
+ * 
+ * Python class that takes a model and averages its output
+ * for a distribution of its parameters.
+ * 
+ * The parameters to be varied are specified at instantiation time.
+ * The distributions are Gaussian, with std deviations specified for
+ * each parameter at instantiation time.
+ * 
+ * @author   M.Doucet / UTK 
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+/// Error object for raised exceptions
+static PyObject * DisperserError = NULL;
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Dictionary of parameters for this class
+    PyObject * params;
+    /// Model object to disperse
+    PyObject * model;
+    /// Array of original values
+    double * centers;
+    /// Number of parameters to disperse
+    int n_pars;
+    /// Coordinate system flag
+    int cylindrical;
+} Disperser;
+
+// Function definitions
+double disperseParam(Disperser *self, int iPar, PyObject * pars);
+double Disperser_readDouble(PyObject *p);
+
+/**
+ * Function to deallocate memory for the object
+ */
+static void
+Disperser_dealloc(Disperser * self) {
+    free(self->centers);
+    self->ob_type->tp_free((PyObject*)self);
+}
+
+/**
+ * Function to create a new Disperser object
+ */
+static PyObject *
+Disperser_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    Disperser *self;
+    
+    self = (Disperser *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+/**
+ * Initialization of the object (__init__)
+ */
+static int
+Disperser_init(Disperser *self, PyObject *args, PyObject *kwds)
+{
+	PyObject * model;
+	PyObject * paramList;
+	PyObject * sigmaList;
+	
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+		// Get input
+		if ( !PyArg_ParseTuple(args,"OOO",&model, &paramList, &sigmaList) ) {
+		    PyErr_SetString(DisperserError, 
+		    	"Disperser: could not parse to initialize. Need Disperser(model, paramList, sigmaList)");
+			return -1;
+		}
+
+		// Check that the lists are of the right type
+		if( PyList_Check(paramList)!=1 || PyList_Check(sigmaList)!=1 ) {
+			PyErr_SetString(DisperserError,
+				"Disperser: bad input parameters; did not find lists.");
+			return -1;
+		}
+		
+		// Check that the list lengths are compatible
+		if( PyList_GET_SIZE(paramList) != PyList_GET_SIZE(sigmaList) ) {
+			PyErr_SetString(DisperserError,
+				"Disperser: supplied lists are not of the same length.");
+			return -1;
+		} 
+		
+		// Set the data members
+		self->model = model;
+		
+		// Number of parameters to disperse
+		// Let run() find out since values can change
+		self->n_pars = 0;
+
+		// Set default coordinate system as cartesian
+		self->cylindrical = 0;
+
+        // Initialize parameter dictionary
+        // Store the list of parameter names (strings)
+        PyDict_SetItemString(self->params,"paramList", paramList);
+        
+        // Store the list of std deviations (floats)
+        PyDict_SetItemString(self->params,"sigmaList", sigmaList);
+        
+        // Set the default number of points to average over
+        PyDict_SetItemString(self->params,"n_pts",Py_BuildValue("i",25));
+    }
+    return 0;
+}
+
+/**
+ * List of member to make public
+ * Only the parameters and the model are available to python users
+ */
+static PyMemberDef Disperser_members[] = {
+    {"params", T_OBJECT, offsetof(Disperser, params), 0,
+     "Parameters"},
+    {"model", T_OBJECT, offsetof(Disperser, model), 0,
+     "Model to disperse"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+ *	TODO: THIS SHOULD REALLY BE ELSEWHERE!
+ *   @param p PyObject
+ *   @return double
+ */
+double Disperser_readDouble(PyObject *p) {
+	double value = 0.0;
+	
+    if (PyFloat_Check(p)==1) {
+        value = (double)(((PyFloatObject *)(p))->ob_fval);
+        //Py_DECREF(p);
+        return value;
+    } else if (PyInt_Check(p)==1) {
+        value = (double)(((PyIntObject *)(p))->ob_ival);
+    	//Py_DECREF(p);
+        return value;
+    } else if (PyLong_Check(p)==1) {
+        value = (double)PyLong_AsLong(p);
+    	//Py_DECREF(p);
+        return value;
+    } else {
+    	//Py_DECREF(p);    	
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [qx,qy]
+ * @return: function value
+ */
+static PyObject * runXY(Disperser *self, PyObject *args) {
+	PyObject* pars;
+	int n_pts;
+	int i;
+	double value;
+	PyObject * result;
+	PyObject * temp;
+	
+	// Get parameters
+	
+	// Check that the lists are of the right type
+	if( PyList_Check(PyDict_GetItemString(self->params, "paramList"))!=1 
+		|| PyList_Check(PyDict_GetItemString(self->params, "sigmaList"))!=1 ) {
+		PyErr_SetString(DisperserError,
+			"Disperser: bad input parameters; did not find lists.");
+		return NULL;
+	}
+
+	// Reader parameter dictionary
+    n_pts = PyInt_AsLong( PyDict_GetItemString(self->params, "n_pts") );
+    
+	// Check that the list lengths are compatible
+	if( PyList_GET_SIZE(PyDict_GetItemString(self->params, "paramList")) 
+		!= PyList_GET_SIZE(PyDict_GetItemString(self->params, "sigmaList")) ) {
+		PyErr_SetString(DisperserError,
+			"Disperser: supplied lists are not of the same length.");
+		return NULL;
+	} 
+
+    // Number of parameters to disperse
+    self->n_pars = PyList_GET_SIZE(PyDict_GetItemString(self->params, "paramList"));
+    
+    // Allocate memory for centers
+    free(self->centers);
+    self->centers = (double *)malloc(self->n_pars * sizeof(double));
+	if(self->centers==NULL) {
+	    PyErr_SetString(DisperserError, 
+	    	"Disperser.run could not allocate memory.");
+		return NULL;
+	}
+    
+    // Store initial values of those parameters    
+    for( i=0; i<self->n_pars; i++ ) {
+    	result = PyObject_CallMethod(self->model, "getParam", "(O)", 
+    		PyList_GetItem( PyDict_GetItemString(self->params, "paramList"), i ));
+    		
+    	if( result == NULL ) {
+		    PyErr_SetString(DisperserError, 
+		    	"Disperser.run could not get current parameter values.");
+    		return NULL;
+    	} else {
+    		self->centers[i] = Disperser_readDouble(result);
+    		Py_DECREF(result);
+    	}; 
+    }
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(DisperserError, 
+	    	"Disperser.run expects a q value.");
+		return NULL;
+	}    
+        
+    // Evaluate or calculate average
+    if( self->n_pars > 0 ) {
+		value = disperseParam(self, 0, pars);
+    } else {
+    	 if (self->cylindrical==1) {
+    	 	result = PyObject_CallMethod(self->model, "run", "(O)",pars);
+    	 } else {
+    	 	result = PyObject_CallMethod(self->model, "runXY", "(O)",pars);
+    	 }
+    	 
+    	 if( result == NULL ) {
+		    PyErr_SetString(DisperserError, 
+		    	"Disperser.run failed.");
+    		return NULL; 	 	
+    	 } else {
+    		Py_DECREF(result);
+    	 	return result;
+    	 }
+    }
+    
+    // Put back the original model parameter values
+    for( i=0; i<self->n_pars; i++ ) {
+    	// Maybe we need to decref the double here?
+    	temp = Py_BuildValue("d", self->centers[i]);
+    	result = PyObject_CallMethod(self->model, "setParam", "(OO)", 
+    		PyList_GetItem( PyDict_GetItemString(self->params, "paramList"), i ),
+    		temp );
+    	Py_DECREF(temp);
+    		
+    	if( result == NULL ) {
+		    PyErr_SetString(DisperserError, 
+		    	"Disperser.run could not set back original parameter values.");
+    		return NULL;
+    	} else {
+    		Py_DECREF(result);
+    	}; 
+    }
+    
+	return Py_BuildValue("d",value);    	
+
+}
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(Disperser *self, PyObject *args) {
+	PyObject * output;
+	self->cylindrical = 1;
+	output = runXY(self, args);
+	self->cylindrical = 0;
+	return output;
+}
+
+
+
+
+/**
+ * Gaussian weight
+ * @param mean: mean value of the Gaussian
+ * @param sigma: standard deviation of the Gaussian
+ * @param x: value at which the Gaussian is evaluated
+ * @return: value of the Gaussian
+ */
+double gaussian_weight(double mean, double sigma, double x) {
+	double vary, expo_value;
+    vary = x-mean;
+    expo_value = -vary*vary/(2*sigma*sigma);
+    //return 1.0;
+    return exp(expo_value);
+}
+
+/**
+ * @param iPar: ID of parameter to disperse
+ * @param pars: input parameter to the evaluation function
+ */
+double disperseParam(Disperser *self, int iPar, PyObject * pars) {
+	double sigma;
+	double min_value, max_value;
+	double step;
+	double prev_value;
+	double value_sum;
+	double gauss_sum;
+	double gauss_value;
+	double func_value;
+	double error_sys;
+	double value;
+	int i, n_pts;
+	PyObject *result;
+	PyObject *temp;
+	
+	
+    n_pts = PyInt_AsLong( PyDict_GetItemString(self->params, "n_pts") );
+    
+	// If we exhausted the parameter array, simply evaluate
+    // the model
+    if( iPar < self->n_pars ) {
+            
+		// Average over Gaussian distribution (2 sigmas)
+        value_sum = 0.0;
+        gauss_sum = 0.0;
+            
+        // Get the standard deviation for this parameter
+        sigma = Disperser_readDouble(PyList_GetItem( PyDict_GetItemString(self->params, "sigmaList"), iPar ));
+            
+        // Average over 4 sigmas wide
+        min_value = self->centers[iPar] - 2*sigma;
+        max_value = self->centers[iPar] + 2*sigma;
+            
+        // Calculate step size
+        step = (max_value - min_value)/(n_pts-1);
+            
+        // If we are not changing the parameter, just return the 
+        // value of the function
+        if (step == 0.0) {
+            return disperseParam(self, iPar+1, pars);
+        }
+            
+        // Compute average
+        prev_value = 0.0;
+        error_sys  = 0.0;
+        for( i=0; i<n_pts; i++ ) {
+            // Set the parameter value           
+            value = min_value + (double)i*step;
+                
+            temp = Py_BuildValue("d", value);
+    		result = PyObject_CallMethod(self->model, "setParam", "(OO)",
+    			PyList_GetItem( PyDict_GetItemString(self->params, "paramList"), iPar ), 
+    				temp);
+    		Py_DECREF(temp);
+    			
+		    if( result == NULL ) {
+	  	 	 	printf("Could not set param %i\n", iPar);
+	    	 	// return a value that will create an NAN
+	    	 	return 0.0/(step-step);
+		    } else {
+		    	Py_DECREF(result);
+		    }
+                
+			gauss_value = gaussian_weight(self->centers[iPar], sigma, value);
+            func_value = disperseParam(self, iPar+1, pars);
+
+            value_sum += gauss_value * func_value;
+        	gauss_sum += gauss_value;	
+        }   
+        return value_sum/gauss_sum;
+        
+    } else {
+    	 if (self->cylindrical==1) {
+    	 	result = PyObject_CallMethod(self->model, "run", "(O)",pars);
+    	 } else {
+    	 	result = PyObject_CallMethod(self->model, "runXY", "(O)",pars);
+    	 }
+    	 if( result == NULL ) {
+    	 	printf("Model.run() could not return\n");
+    	 	// return a value that will create an NAN
+    	 	return 0.0/(step-step);
+    	 } else {
+    	 	value = Disperser_readDouble(result);    	 	
+    	 	Py_DECREF(result);
+    	 	return value;
+    	 }
+    }
+	
+}
+
+static PyMethodDef Disperser_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given input value"},
+    {"runXY",      (PyCFunction)runXY     , METH_VARARGS,
+      "Evaluate the model at a given input value"},
+   {NULL}
+};
+
+static PyTypeObject DisperserType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "Disperser",             /*tp_name*/
+    sizeof(Disperser),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)Disperser_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*/
+    "Disperser objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    Disperser_methods,             /* tp_methods */
+    Disperser_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)Disperser_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    Disperser_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 addDisperser(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&DisperserType) < 0)
+        return;
+
+    Py_INCREF(&DisperserType);
+    PyModule_AddObject(module, "Disperser", (PyObject *)&DisperserType);
+    
+    d = PyModule_GetDict(module);
+    DisperserError = PyErr_NewException("Disperser.error", NULL, NULL);
+    PyDict_SetItemString(d, "DisperserError", DisperserError);
+}
+
Index: sansmodels/src/sans/models/c_extensions/ellipsoid.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/ellipsoid.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/ellipsoid.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,135 @@
+/**
+ * Scattering model for an ellipsoid
+ * @author: Mathieu Doucet / UTK
+ */
+
+#include "ellipsoid.h"
+#include "libCylinder.h"
+#include <math.h>
+#include <stdio.h>
+
+/**
+ * Test kernel for validation
+ * 
+ * @param q: q-value
+ * @param r_small: small axis length
+ * @param r_long: rotation axis length
+ * @param angle: angle between rotation axis and q vector
+ * @return: oriented kernel value
+ */
+double kernel(double q, double r_small, double r_long, double angle) {
+	double length;
+	double sin_alpha;
+	double cos_alpha;
+	double sph_func;
+	
+	sin_alpha = sin(angle);
+	cos_alpha = cos(angle);
+	
+	// Modified length for phase
+	length = r_small*sqrt(sin_alpha*sin_alpha + 
+			r_long*r_long/(r_small*r_small)*cos_alpha*cos_alpha);
+	
+	// Spherical scattering ampliture, with modified length for ellipsoid
+	sph_func = 3*( sin(q*length) - q*length*cos(q*length) ) / (q*q*q*length*length*length);
+	
+	return sph_func*sph_func;
+}
+
+/**
+ * Function to evaluate 1D scattering function
+ * @param pars: parameters of the ellipsoid
+ * @param q: q-value
+ * @return: function value
+ */
+double ellipsoid_analytical_1D(EllipsoidParameters *pars, double q) {
+	double dp[5];
+	
+	dp[0] = pars->scale;
+	dp[1] = pars->radius_a;
+	dp[2] = pars->radius_b;
+	dp[3] = pars->contrast;
+	dp[4] = pars->background;
+	
+	return EllipsoidForm(dp, q);
+}
+    
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the ellipsoid
+ * @param q: q-value
+ * @return: function value
+ */
+double ellipsoid_analytical_2DXY(EllipsoidParameters *pars, double qx, double qy) {
+	double q;
+	q = sqrt(qx*qx+qy*qy);
+    return ellipsoid_analytical_2D_scaled(pars, q, qx/q, qy/q);
+} 
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the ellipsoid
+ * @param q: q-value
+ * @param phi: angle phi
+ * @return: function value
+ */
+double ellipsoid_analytical_2D(EllipsoidParameters *pars, double q, double phi) {
+    return ellipsoid_analytical_2D_scaled(pars, q, cos(phi), sin(phi));
+} 
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the ellipsoid
+ * @param q: q-value
+ * @param q_x: q_x / q
+ * @param q_y: q_y / q
+ * @return: function value
+ */
+double ellipsoid_analytical_2D_scaled(EllipsoidParameters *pars, double q, double q_x, double q_y) {
+	double cyl_x, cyl_y, cyl_z;
+	double q_z, lenq;
+	double theta, alpha, f, vol, sin_val, cos_val;
+	double answer;
+        
+    // Ellipsoid orientation
+    cyl_x = sin(pars->axis_theta) * cos(pars->axis_phi);
+    cyl_y = sin(pars->axis_theta) * sin(pars->axis_phi);
+    cyl_z = cos(pars->axis_theta);
+     
+    // q vector
+    q_z = 0;
+        
+    // Compute the angle btw vector q and the
+    // axis of the cylinder
+    cos_val = cyl_x*q_x + cyl_y*q_y + cyl_z*q_z;
+    
+    // The following test should always pass
+    if (fabs(cos_val)>1.0) {
+    	printf("ellipsoid_ana_2D: Unexpected error: cos(alpha)>1\n");
+     	return 0;
+    }
+    
+    // Angle between rotation axis and q vector
+	alpha = acos( cos_val );
+	
+	// Call the IGOR library function to get the kernel
+	answer = EllipsoidKernel(q, pars->radius_b, pars->radius_a, cos_val);
+	
+	// Multiply by contrast^2
+	answer *= pars->contrast*pars->contrast;
+	
+	//normalize by cylinder volume
+    vol = 4.0/3.0 * acos(-1.0) * pars->radius_b * pars->radius_b * pars->radius_a;
+	answer *= vol;
+	
+	//convert to [cm-1]
+	answer *= 1.0e8;
+	
+	//Scale
+	answer *= pars->scale;
+	
+	// add in the background
+	answer += pars->background;
+	
+	return answer;
+}
Index: sansmodels/src/sans/models/c_extensions/ellipsoid.h
===================================================================
--- sansmodels/src/sans/models/c_extensions/ellipsoid.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/ellipsoid.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,49 @@
+#if !defined(ellipsoid_h)
+#define ellipsoid_h
+
+/** 
+ * Structure definition for ellipsoid parameters 
+ * The ellipsoid has axes radius_b, radius_b, radius_a.
+ * Ref: Jan Skov Pedersen, Advances in Colloid and Interface Science, 70 (1997) 171-210
+ */
+ //[PYTHONCLASS] = EllipsoidModel
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    
+    ///	Rotation axis radius_a [A] 
+    //  [DEFAULT]=radius_a=20.0 A
+    double radius_a;
+    
+    /// Radius_b [A] 
+    //  [DEFAULT]=radius_b=400 A
+    double radius_b;
+    
+    ///	Contrast [Å-2] 
+    //  [DEFAULT]=contrast=3.0e-6 A-2
+    double contrast;
+    
+	/// Incoherent Background [cm-1]
+	//  [DEFAULT]=background=0 cm-1
+	double background;    
+	
+    /// Orientation of the long axis of the ellipsoid w/respect incoming beam [rad]
+    //  [DEFAULT]=axis_theta=1.57 rad
+    double axis_theta;
+    /// Orientation of the long axis of the ellipsoid in the plane of the detector [rad]
+    //  [DEFAULT]=axis_phi=0.0 rad
+    double axis_phi;    
+} EllipsoidParameters;
+
+
+
+/// 1D scattering function
+double ellipsoid_analytical_1D(EllipsoidParameters *pars, double q);
+
+/// 2D scattering function
+double ellipsoid_analytical_2D(EllipsoidParameters *pars, double q, double phi);
+double ellipsoid_analytical_2DXY(EllipsoidParameters *pars, double qx, double qy);
+double ellipsoid_analytical_2D_scaled(EllipsoidParameters *pars, double q, double q_x, double q_y);
+
+#endif
Index: sansmodels/src/sans/models/c_extensions/elliptical_cylinder.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/elliptical_cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/elliptical_cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,128 @@
+/**
+ * Scattering model for a cylinder with elliptical cross-section
+ */
+
+#include "elliptical_cylinder.h"
+#include <math.h>
+#include "libCylinder.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/**
+ * Function to evaluate 1D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double elliptical_cylinder_analytical_1D(EllipticalCylinderParameters *pars, double q) {
+	double dp[6];
+	
+	// Fill paramater array
+	dp[0] = pars->scale;
+	dp[1] = pars->r_minor;
+	dp[2] = pars->r_ratio;
+	dp[3] = pars->length;
+	dp[4] = pars->contrast;
+	dp[5] = pars->background;
+	
+	// Call library function to evaluate model
+	return EllipCyl20(dp, q);	
+}
+
+double elliptical_cylinder_kernel(EllipticalCylinderParameters *pars, double q, double alpha, double psi) {
+	double qr;
+	double qL;
+	double r_major;
+	double kernel;
+	
+	r_major = pars->r_ratio * pars->r_minor;
+
+	qr = q*sin(alpha)*sqrt( r_major*r_major*sin(psi)*sin(psi) + pars->r_minor*pars->r_minor*cos(psi)*cos(psi) );
+	qL = q*pars->length*cos(alpha)/2.0;
+	
+	kernel = 2.0*NR_BessJ1(qr)/qr * sin(qL)/qL;
+	return kernel*kernel;
+}
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double elliptical_cylinder_analytical_2DXY(EllipticalCylinderParameters *pars, double qx, double qy) {
+	double q;
+	q = sqrt(qx*qx+qy*qy);
+    return elliptical_cylinder_analytical_2D_scaled(pars, q, qx/q, qy/q);
+} 
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @param phi: angle phi
+ * @return: function value
+ */
+double elliptical_cylinder_analytical_2D(EllipticalCylinderParameters *pars, double q, double phi) {
+    return elliptical_cylinder_analytical_2D_scaled(pars, q, cos(phi), sin(phi));
+} 
+
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @param q_x: q_x / q
+ * @param q_y: q_y / q
+ * @return: function value
+ */
+double elliptical_cylinder_analytical_2D_scaled(EllipticalCylinderParameters *pars, double q, double q_x, double q_y) {
+	double cyl_x, cyl_y, cyl_z;
+	double q_z;
+	double alpha, vol, cos_val;
+	double answer;
+        
+    // Cylinder orientation
+    cyl_x = sin(pars->cyl_theta) * cos(pars->cyl_phi);
+    cyl_y = sin(pars->cyl_theta) * sin(pars->cyl_phi);
+    cyl_z = cos(pars->cyl_theta);
+     
+    // q vector
+    q_z = 0;
+        
+    // Compute the angle btw vector q and the
+    // axis of the cylinder
+    cos_val = cyl_x*q_x + cyl_y*q_y + cyl_z*q_z;
+    
+    // The following test should always pass
+    if (fabs(cos_val)>1.0) {
+    	printf("cyl_ana_2D: Unexpected error: cos(alpha)>1\n");
+     	return 0;
+    }
+    
+    // Note: cos(alpha) = 0 and 1 will get an
+    // undefined value from CylKernel
+	alpha = acos( cos_val );
+	
+	answer = elliptical_cylinder_kernel(pars, q, alpha, pars->cyl_psi);
+	
+	// Multiply by contrast^2
+	answer *= pars->contrast*pars->contrast;
+	
+	//normalize by cylinder volume
+	//NOTE that for this (Fournet) definition of the integral, one must MULTIPLY by Vcyl
+    vol = acos(-1.0) * pars->r_minor * pars->r_minor * pars->r_ratio * pars->length;
+	answer *= vol;
+	
+	//convert to [cm-1]
+	answer *= 1.0e8;
+	
+	//Scale
+	answer *= pars->scale;
+	
+	// add in the background
+	answer += pars->background;
+	
+	return answer;
+}
+    
Index: sansmodels/src/sans/models/c_extensions/elliptical_cylinder.h
===================================================================
--- sansmodels/src/sans/models/c_extensions/elliptical_cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/elliptical_cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,47 @@
+#if !defined(ell_cylinder_h)
+#define ell_cylinder_h
+
+/** Structure definition for cylinder parameters 
+ * [PYTHONCLASS] = EllipticalCylinderModel
+ * */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    /// Minor radius [A]
+    //  [DEFAULT]=r_minor=20.0 A
+    double r_minor;
+    /// Ratio of major/minor radii
+    //  [DEFAULT]=r_ratio=1.5 A
+    double r_ratio;
+    /// Length of the cylinder [A]
+    //  [DEFAULT]=length=400.0 A
+    double length;
+    /// Contrast [A-2]
+    //  [DEFAULT]=contrast=3.0e-6 A-2
+    double contrast;
+	/// Incoherent Background (cm-1) 0.000
+	//  [DEFAULT]=background=0 cm-1
+	double background;    
+    /// Orientation of the cylinder axis w/respect incoming beam [rad]
+    //  [DEFAULT]=cyl_theta=1.57 rad
+    double cyl_theta;
+    /// Orientation of the cylinder in the plane of the detector [rad]
+    //  [DEFAULT]=cyl_phi=0.0 rad
+    double cyl_phi;    
+    /// Orientation of major radius of the cross-section w/respect vector q [rad]
+    //  [DEFAULT]=cyl_psi=0.0 rad
+    double cyl_psi;    
+} EllipticalCylinderParameters;
+
+
+
+/// 1D scattering function
+double elliptical_cylinder_analytical_1D(EllipticalCylinderParameters *pars, double q);
+
+/// 2D scattering function
+double elliptical_cylinder_analytical_2D(EllipticalCylinderParameters *pars, double q, double phi);
+double elliptical_cylinder_analytical_2DXY(EllipticalCylinderParameters *pars, double qx, double qy);
+double elliptical_cylinder_analytical_2D_scaled(EllipticalCylinderParameters *pars, double q, double q_x, double q_y);
+
+#endif
Index: sansmodels/src/sans/models/c_extensions/gaussian.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/gaussian.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/gaussian.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,58 @@
+/**
+ * Gaussian function
+ */
+
+#include "gaussian.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/**
+ * Function to evaluate 1D Gaussian function.
+ * The function is normalized to the 'scale' parameter.
+ * 
+ * f(x)=scale * 1/(sigma^2*2pi)e^(-(x-mu)^2/2sigma^2)
+ * 
+ * @param pars: parameters of the gaussian
+ * @param x: x-value
+ * @return: function value
+ */
+double gaussian_analytical_1D(GaussianParameters *pars, double x) {
+	double sigma2 = pow(pars->sigma, 2);	
+	return pars->scale / sigma2 * exp( -pow((x-pars->center), 2) / (2*sigma2));
+}
+
+/**
+ * Function to evaluate 2D Gaussian  function
+ * The function is normalized to the 'scale' parameter.
+ * 
+ * f(x,y) = Gaussian(x) * Gaussian(y)
+ * 
+ * where both Gaussians share the same parameters.
+ * 
+ * @param pars: parameters of the gaussian
+ * @param x: x-value
+ * @param y: y-value
+ * @return: function value
+ */
+double gaussian_analytical_2DXY(GaussianParameters *pars, double x, double y) {
+    return gaussian_analytical_1D(pars, x) * gaussian_analytical_1D(pars, y);
+} 
+
+/**
+ * Function to evaluate 2D Gaussian  function
+ * The function is normalized to the 'scale' parameter.
+ * 
+ * f(x,y) = Gaussian(x) * Gaussian(y)
+ * 
+ * where both Gaussians share the same parameters.
+ * 
+ * @param pars: parameters of the gaussian
+ * @param length: length of the (x,y) vector
+ * @param phi: angle relative to x
+ * @return: function value
+ */
+double gaussian_analytical_2D(GaussianParameters *pars, double length, double phi) {
+    return gaussian_analytical_2DXY(pars, length*cos(phi), length*sin(phi));
+} 
Index: sansmodels/src/sans/models/c_extensions/gaussian.h
===================================================================
--- sansmodels/src/sans/models/c_extensions/gaussian.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/gaussian.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,30 @@
+#if !defined(gaussian_h)
+#define gaussian_h
+
+/** Structure definition for Gaussian parameters.
+ * The Gaussian is normalized to the 'scale' parameter.
+ *  
+ * f(x)=scale * 1/(sigma^2*2pi)e^(-(x-mu)^2/2sigma^2)
+ * 
+ * [PYTHONCLASS] = Gaussian
+ */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    /// Standard deviation
+    //  [DEFAULT]=sigma=1 
+    double sigma;
+    /// Center of the Gaussian distribution
+    //  [DEFAULT]=center=0.0 
+    double center;
+} GaussianParameters;
+
+/// 1D scattering function
+double gaussian_analytical_1D(GaussianParameters *pars, double x);
+
+/// 2D scattering function
+double gaussian_analytical_2D(GaussianParameters *pars, double x, double phi);
+double gaussian_analytical_2DXY(GaussianParameters *pars, double x, double y);
+
+#endif
Index: sansmodels/src/sans/models/c_extensions/lorentzian.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/lorentzian.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/lorentzian.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,59 @@
+/**
+ * Lorentzian function
+ */
+
+#include "lorentzian.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/**
+ * Function to evaluate 1D Lorentzian function.
+ * The function is normalized to the 'scale' parameter.
+ * 
+ * f(x)=scale * 1/pi 0.5gamma / [ (x-x_0)^2 + (0.5gamma)^2 ]
+ * 
+ * @param pars: parameters of the Lorentzian
+ * @param x: x-value
+ * @return: function value
+ */
+double lorentzian_analytical_1D(LorentzianParameters *pars, double x) {
+	double delta2 = pow( (x-pars->center), 2);
+	return pars->scale / acos(-1.0) * 0.5*pars->gamma /
+		(delta2 + 0.25*pars->gamma*pars->gamma);
+}
+
+/**
+ * Function to evaluate 2D Lorentzian  function
+ * The function is normalized to the 'scale' parameter.
+ * 
+ * f(x,y) = Lorentzian(x) * Lorentzian(y)
+ * 
+ * where both Lorentzians share the same parameters.
+ * 
+ * @param pars: parameters of the Lorentzian
+ * @param x: x-value
+ * @param y: y-value
+ * @return: function value
+ */
+double lorentzian_analytical_2DXY(LorentzianParameters *pars, double x, double y) {
+    return lorentzian_analytical_1D(pars, x) * lorentzian_analytical_1D(pars, y);
+} 
+
+/**
+ * Function to evaluate 2D Lorentzian  function
+ * The function is normalized to the 'scale' parameter.
+ * 
+ * f(x,y) = Lorentzian(x) * Lorentzian(y)
+ * 
+ * where both Lorentzians share the same parameters.
+ * 
+ * @param pars: parameters of the Lorentzian
+ * @param length: length of the (x,y) vector
+ * @param phi: angle relative to x
+ * @return: function value
+ */
+double lorentzian_analytical_2D(LorentzianParameters *pars, double length, double phi) {
+    return lorentzian_analytical_2DXY(pars, length*cos(phi), length*sin(phi));
+} 
Index: sansmodels/src/sans/models/c_extensions/lorentzian.h
===================================================================
--- sansmodels/src/sans/models/c_extensions/lorentzian.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/lorentzian.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,30 @@
+#if !defined(lorentzian_h)
+#define lorentzian_h
+
+/** Structure definition for Lorentzian parameters.
+ * The Lorentzian is normalized to the 'scale' parameter.
+ *  
+ * f(x)=scale * 1/pi 0.5gamma / [ (x-x_0)^2 + (0.5gamma)^2 ]
+ * 
+ * [PYTHONCLASS] = Lorentzian
+ */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    /// Width
+    //  [DEFAULT]=gamma=1.0
+    double gamma;
+    /// Center of the Lorentzian distribution
+    //  [DEFAULT]=center=0.0 
+    double center;
+} LorentzianParameters;
+
+/// 1D lorentzian function
+double lorentzian_analytical_1D(LorentzianParameters *pars, double x);
+
+/// 2D lorentzian function
+double lorentzian_analytical_2D(LorentzianParameters *pars, double x, double phi);
+double lorentzian_analytical_2DXY(LorentzianParameters *pars, double x, double y);
+
+#endif
Index: sansmodels/src/sans/models/c_extensions/modelTemplate.txt
===================================================================
--- sansmodels/src/sans/models/c_extensions/modelTemplate.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/modelTemplate.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY [INCLUDE_FILE]
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.c_models import [CPYTHONCLASS]
+import copy    
+    
+class [PYTHONCLASS]([CPYTHONCLASS], BaseComponent):
+    """ Class that evaluates a [PYTHONCLASS] model. 
+    	This file was auto-generated from [INCLUDE_FILE].
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	[DEFAULT_LIST]
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        [CPYTHONCLASS].__init__(self)
+        
+        ## Name of the model
+        self.name = "[PYTHONCLASS]"
+
+		[PAR_DETAILS]
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = [PYTHONCLASS]()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return [CPYTHONCLASS].run(self, x)
+   
+    def runXY(self, x = 0.0):
+        """ Evaluate the model in cartesian coordinates
+            @param x: input q, or [qx, qy]
+            @return: scattering function P(q)
+        """
+        
+        return [CPYTHONCLASS].runXY(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/c_extensions/sphere.c
===================================================================
--- sansmodels/src/sans/models/c_extensions/sphere.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/sphere.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,40 @@
+/**
+ * Scattering model for a sphere
+ * @author: Mathieu Doucet / UTK
+ */
+
+#include "sphere.h"
+#include "libSphere.h"
+#include <math.h>
+
+
+/**
+ * Function to evaluate 1D scattering function
+ * @param pars: parameters of the sphere
+ * @param q: q-value
+ * @return: function value
+ */
+double sphere_analytical_1D(SphereParameters *pars, double q) {
+	double dp[5];
+	
+	dp[0] = pars->scale;
+	dp[1] = pars->radius;
+	dp[2] = pars->contrast;
+	dp[3] = pars->background;
+	
+	return SphereForm(dp, q);
+}
+    
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the sphere
+ * @param q: q-value
+ * @return: function value
+ */
+double sphere_analytical_2D(SphereParameters *pars, double q, double phi) {
+	return sphere_analytical_1D(pars,q);
+}
+
+double sphere_analytical_2DXY(SphereParameters *pars, double qx, double qy){
+	return sphere_analytical_1D(pars,sqrt(qx*qx+qy*qy));	
+}
Index: sansmodels/src/sans/models/c_extensions/sphere.h
===================================================================
--- sansmodels/src/sans/models/c_extensions/sphere.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/c_extensions/sphere.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,35 @@
+#if !defined(sphere_h)
+#define sphere_h
+
+/** 
+ * Structure definition for sphere parameters 
+ */
+ //[PYTHONCLASS] = SphereModel
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0e-6
+    double scale;
+    
+    ///	Radius of sphere [A] 
+    //  [DEFAULT]=radius=60.0 A
+    double radius;
+    
+    ///	Contrast [Å-2] 
+    //  [DEFAULT]=contrast=1.0 A-2
+    double contrast;
+    
+	/// Incoherent Background [cm-1]
+	//  [DEFAULT]=background=0 cm-1
+	double background;    
+} SphereParameters;
+
+
+
+/// 1D scattering function
+double sphere_analytical_1D(SphereParameters *pars, double q);
+
+/// 2D scattering function
+double sphere_analytical_2D(SphereParameters *pars, double q, double phi);
+double sphere_analytical_2DXY(SphereParameters *pars, double qx, double qy);
+
+#endif
Index: sansmodels/src/sans/models/prototypes/DispCylinderModel.py
===================================================================
--- sansmodels/src/sans/models/prototypes/DispCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/DispCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY disperse_cylinder.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.prototypes.c_models import CDispCylinderModel
+import copy    
+    
+class DispCylinderModel(CDispCylinderModel, BaseComponent):
+    """ Class that evaluates a DispCylinderModel model. 
+    	This file was auto-generated from disperse_cylinder.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius          = 20.0 A
+         length          = 400.0 A
+         contrast        = 3e-006 A-2
+         background      = 0.0 cm-1
+         cyl_theta       = 1.57 rad
+         cyl_phi         = 0.0 rad
+         cyl_phi         = 0.0 rad
+         sigma_theta     = 0.0 rad
+         sigma_phi       = 0.0 rad
+         sigma_radius    = 0.0 rad
+         sigma_radius    = 0.0 rad
+         n_pts           = 25.0 
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CDispCylinderModel.__init__(self)
+        
+        ## Name of the model
+        self.name = "DispCylinderModel"
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = DispCylinderModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CDispCylinderModel.run(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/DisperseCylinderModel.py
===================================================================
--- sansmodels/src/sans/models/prototypes/DisperseCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/DisperseCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.CylinderModel import CylinderModel
+from sans_extension.c_models import Disperser
+from sans.models.DisperseModel import DisperseModel
+import copy    
+    
+class DisperseCylinderModel(CylinderModel):
+    """ Class that evaluates a SmearCylinderModel model. 
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        CylinderModel.__init__(self)
+        self.params['sigma_phi'] = 0.0
+        self.params['sigma_theta'] = 0.0
+        self.params['n_pts'] = 20
+        
+        
+        ## Name of the model
+        self.name = "DisperseCylinderModel"
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = DisperseCylinderModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        model = CylinderModel()
+        model.params = copy.deepcopy(self.params)
+        #d = Disperser(model, ["cyl_phi", "cyl_theta"], 
+		#				  [self.params['sigma_phi'], self.params['sigma_theta']])
+        d = DisperseModel(model, ["cyl_phi", "cyl_theta"], 
+					  [self.params['sigma_phi'], self.params['sigma_theta']])
+        d.params['n_pts'] = self.params['n_pts']
+        return d.run(x)
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/DisperseEllipsoidModel.py
===================================================================
--- sansmodels/src/sans/models/prototypes/DisperseEllipsoidModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/DisperseEllipsoidModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.EllipsoidModel import EllipsoidModel
+from sans.models.DisperseModel import DisperseModel
+import copy    
+    
+class DisperseEllipsoidModel(EllipsoidModel):
+    """ Class that evaluates a DisperseEllipsoidModel model. 
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        EllipsoidModel.__init__(self)
+        self.params['sigma_phi'] = 0.0
+        self.params['sigma_theta'] = 0.0
+        self.params['n_pts'] = 20
+        
+        
+        ## Name of the model
+        self.name = "DisperseEllipsoidModel"
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = DisperseEllipsoidModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        model = EllipsoidModel()
+        model.params = copy.deepcopy(self.params)
+        #d = Disperser(model, ["cyl_phi", "cyl_theta"], 
+		#				  [self.params['sigma_phi'], self.params['sigma_theta']])
+        d = DisperseModel(model, ["axis_phi", "axis_theta"], 
+					  [self.params['sigma_phi'], self.params['sigma_theta']])
+        d.params['n_pts'] = self.params['n_pts']
+        return d.run(x)
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/SimCylinder.py
===================================================================
--- sansmodels/src/sans/models/prototypes/SimCylinder.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/SimCylinder.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY simcylinder.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.prototypes.c_models import CSimCylinder
+import copy    
+    
+class SimCylinder(CSimCylinder, BaseComponent):
+    """ Class that evaluates a SimCylinder model. 
+    	This file was auto-generated from simcylinder.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius          = 20.0 
+         length          = 400.0 
+         theta           = 0.0 
+         phi             = 0.0 
+         qmax            = 0.1 
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CSimCylinder.__init__(self)
+        
+        ## Name of the model
+        self.name = "SimCylinder"
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = SimCylinder()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CSimCylinder.run(self, x)
+# main
+if __name__ == '__main__':
+    app = SimCylinder()
+    print "%s: f(1) = %g" % (app.name, app.run(1))
+    print "%s: f(1,1.3) = %g" % (app.name, app.run([1, 1.3]))
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/SimCylinderF.py
===================================================================
--- sansmodels/src/sans/models/prototypes/SimCylinderF.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/SimCylinderF.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY ..\prototypes\src\simcylinder_fast.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.prototypes.c_models import CSimCylinderF
+import copy    
+    
+class SimCylinderF(CSimCylinderF, BaseComponent):
+    """ Class that evaluates a SimCylinderF model. 
+    	This file was auto-generated from ..\prototypes\src\simcylinder_fast.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius          = 20.0 
+         length          = 400.0 
+         theta           = 0.0 
+         phi             = 0.0 
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CSimCylinderF.__init__(self)
+        
+        ## Name of the model
+        self.name = "SimCylinderF"
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = SimCylinderF()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CSimCylinderF.run(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/SimSphereF.py
===================================================================
--- sansmodels/src/sans/models/prototypes/SimSphereF.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/SimSphereF.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY ../prototypes/src/sphere_fast.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.prototypes.c_models import CSimSphereF
+import copy    
+    
+class SimSphereF(CSimSphereF, BaseComponent):
+    """ Class that evaluates a SimSphereF model. 
+    	This file was auto-generated from ../prototypes/src/sphere_fast.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius          = 20.0 
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CSimSphereF.__init__(self)
+        
+        ## Name of the model
+        self.name = "SimSphereF"
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = SimSphereF()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CSimSphereF.run(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/SmearCylinderModel.py
===================================================================
--- sansmodels/src/sans/models/prototypes/SmearCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/SmearCylinderModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY smeared_cylinder.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.prototypes.c_models import CSmearCylinderModel
+import copy    
+    
+class SmearCylinderModel(CSmearCylinderModel, BaseComponent):
+    """ Class that evaluates a SmearCylinderModel model. 
+    	This file was auto-generated from smeared_cylinder.h.
+    	Refer to that file and the structure it contains
+    	for details of the model.
+    	List of default parameters:
+         scale           = 1.0 
+         radius          = 20.0 A
+         length          = 400.0 A
+         contrast        = 3e-006 A-2
+         background      = 0.0 cm-1
+         cyl_theta       = 1.0 rad
+         cyl_phi         = 1.0 rad
+         cyl_phi         = 0.0 rad
+         sigma_theta     = 0.0 rad
+         sigma_phi       = 0.0 rad
+         sigma_radius    = 0.0 rad
+
+    """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CSmearCylinderModel.__init__(self)
+        
+        ## Name of the model
+        self.name = "SmearCylinderModel"
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = SmearCylinderModel()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CSmearCylinderModel.run(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/TestSphere2.py
===================================================================
--- sansmodels/src/sans/models/prototypes/TestSphere2.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/TestSphere2.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+""" Provide functionality for a C extension model
+
+	WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ 	         DO NOT MODIFY THIS FILE, MODIFY testsphere.h
+ 	         AND RE-RUN THE GENERATOR SCRIPT
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+from sans.models.BaseComponent import BaseComponent
+from sans_extension.prototypes.c_models import CTestSphere2
+import copy    
+    
+class TestSphere2(CTestSphere2, BaseComponent):
+    """ Scattering model based on a cylinder """
+        
+    def __init__(self):
+        """ Initialization """
+        
+        # Initialize BaseComponent first, then sphere
+        BaseComponent.__init__(self)
+        CTestSphere2.__init__(self)
+        
+        ## Name of the model
+        self.name = "TestSphere2"
+   
+    def clone(self):
+        """ Return a identical copy of self """
+        obj = TestSphere2()
+        obj.params = copy.deepcopy(self.params)
+        return obj   
+    
+    def run(self, x=0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        
+        return CTestSphere2.run(self, x)
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/WeightModel.py
===================================================================
--- sansmodels/src/sans/models/prototypes/WeightModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/WeightModel.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+""" 
+    Wrapper for the Disperser class extension
+
+    @author: Mathieu Doucet / UTK
+    @contact: mathieu.doucet@nist.gov
+"""
+
+import math
+import os, string, sys
+from sans.models.BaseComponent import BaseComponent
+from sans.models.CylinderModel import CylinderModel
+    
+class WeightModel(CylinderModel):
+    """ 
+    """
+        
+    def __init__(self):
+        """ Initialization 
+            @param model: Model to disperse [BaseComponent]
+            @param param: Parameter to disperse [string]
+        """
+        
+        # Initialize BaseComponent first, then sphere
+        CylinderModel.__init__(self)
+        self.params['datafile'] = '' 
+        
+        self.d = None
+        ## Name of the model
+        self.name = 'WeightModel'
+   
+    def run(self, x = 0.0):
+        """ Evaluate the model
+            @param x: input q, or [q,phi]
+            @return: scattering function P(q)
+        """
+        if len(self.d.x)==0:
+            return 0.0
+        try:
+            total = 0
+            norma = 0
+            for i in range(len(self.d.x)):
+                self.params['cyl_phi'] = self.d.x[i]*math.pi/180.0
+                total += CylinderModel.run(self, x) * self.d.y[i]
+                
+                
+                self.params['cyl_phi'] = -self.d.x[i]*math.pi/180.0
+                total += CylinderModel.run(self, x) * self.d.y[i]
+                
+                norma += 2.0*self.d.y[i]
+            return total/norma
+        except:
+            print sys.exc_value
+            
+        return 0.0
+           
+    def readData(self):
+                self.d = DataReader(self.params['datafile'])
+                self.d.read()
+
+   
+class DataReader:
+    """ Simple data reader for Igor data files """
+    
+    def __init__(self, filename):
+        """ Init
+            @param filename: Name of Igor data file to read
+        """
+        self.file = filename
+        self.x = []
+        self.y = []
+        
+    def read(self):
+        """ Read file """
+        # Check if the file is there
+        if not os.path.isfile(self.file):
+            raise ValueError, \
+            "Specified file %s is not a regular file" % self.file
+        
+        # Read file
+        f = open(self.file,'r')
+        buf = f.read()
+        
+        # Get content
+        dataStarted = False
+        
+        
+        lines = string.split(buf,'\n')
+        itot = 0
+        self.x = []
+        self.y = []
+        for line in lines:
+            if line.count("Angle")>0:
+                dataStarted = True
+                continue
+                
+            if dataStarted == True:
+                
+                try:
+                    toks = line.split()
+                except:
+                    print sys.exc_value
+                
+                self.x.append(float(toks[0]))
+                
+                self.y.append(float(toks[1]))
+                
+                
+                itot += 1
+                    
+        print "Read %g points from file %s" % (len(self.image), self.file)
+                
+       
+if __name__ == '__main__':
+    app = WeightModel()
+    cyl = CylinderModel()
+    print "%s: f(1) = %g" % (app.name, app.run(1))
+   
+# End of file
Index: sansmodels/src/sans/models/prototypes/__init__.py
===================================================================
--- sansmodels/src/sans/models/prototypes/__init__.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/__init__.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,8 @@
+"""
+    Extension for testing of SANS models
+    
+    This module is experimental and serves as a repository for prototypes
+    and tests for the modeling functionality for SANS.
+    
+    Use this module at your own risks.
+"""
Index: sansmodels/src/sans/models/prototypes/setup.py
===================================================================
--- sansmodels/src/sans/models/prototypes/setup.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/setup.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,61 @@
+"""
+ Installation script for SANS models
+
+  - To compile and install:
+      python setup.py install
+  - To create distribution:
+      python setup.py bdist_wininst
+
+"""
+
+from distutils.core import setup, Extension
+
+
+# Build the module name
+srcdir  = "src"
+igordir = "../../../libigor"
+extdir  = "../c_extensions"
+
+print "Installing SANS models"
+
+setup(
+    name="models",
+    version = "0.1",
+    description = "Python module for various tests of SANS scattering models",
+    author = "Mathieu Doucet",
+    author_email = "doucet@nist.gov",
+    url = "http://danse.us/trac/sans",
+    
+    # Place this module under the sans package
+    #ext_package = "sans",
+    
+    # Use the pure python modules
+    package_dir = {"sans_extension.prototypes":"."},
+    
+    packages = ["sans_extension.prototypes"],
+    
+    ext_modules = [ Extension("sans_extension.prototypes.c_models",
+     sources = [
+        srcdir+"/c_models.c",
+        "../../../igor_wrapper/c_disperser.c",
+        srcdir+"/disperse_cylinder.c",
+        srcdir+"/CDispCylinderModel.c",
+        srcdir+"/CSimCylinderF.c",
+        srcdir+"/simcylinder_fast.c",
+        srcdir+"/CSimSphereF.c",
+        srcdir+"/sphere_fast.c",
+        srcdir+"/CTestSphere2.c",
+        srcdir+"/testsphere.c",
+        srcdir+"/CSimCylinder.c",
+        srcdir+"/simcylinder.c",
+        srcdir+"/modelCalculations.c",
+        srcdir+"/CSmearCylinderModel.c",
+        srcdir+"/smeared_cylinder.c",
+        igordir+"/libCylinder.c",
+        igordir+"/libSphere.c",
+        extdir+"/cylinder.c",
+        srcdir+"/CCanvas.c",
+        srcdir+"/canvas.c"
+            ],
+         include_dirs=[igordir, extdir])])
+        
Index: sansmodels/src/sans/models/prototypes/src/CCanvas.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/CCanvas.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/CCanvas.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,223 @@
+
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "canvas.h"
+
+/// Error object for raised exceptions
+static PyObject * CCanvasError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Model parameters
+	CanvasParams canvas_pars;
+} CCanvas;
+
+
+static void
+CCanvas_dealloc(CCanvas* self)
+{
+    canvas_dealloc(&(self->canvas_pars));
+     
+    self->ob_type->tp_free((PyObject*)self);
+
+}
+
+static PyObject *
+CCanvas_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CCanvas *self;
+    
+    self = (CCanvas *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CCanvas_init(CCanvas *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+		srand(1001);	
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+  		canvas_init(&(self->canvas_pars));
+    }
+    return 0;
+}
+
+static PyMemberDef CCanvas_members[] = {
+    {"params", T_OBJECT, offsetof(CCanvas, params), 0,
+     "Parameters"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CCanvas_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CCanvas *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CCanvasError, 
+	    	"CCanvas.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CCanvasError, 
+	    		"CCanvas.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CCanvas_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CCanvas_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",canvas_intensity(&(self->canvas_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CCanvas_readDouble(pars);		
+		
+		return Py_BuildValue("d",0.0);
+	}	
+}
+
+static PyObject * add(CCanvas *self, PyObject *args) {
+	int id;
+	
+	id = canvas_add(&(self->canvas_pars),REALSPACE_SPHERE);
+    return Py_BuildValue("i",id);
+}
+
+static PyObject * setParam(CCanvas *self, PyObject *args) {
+	int id, par_id;
+	double value;
+	double radius;
+
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"iid",&id, &par_id, &value) ) {
+	    PyErr_SetString(CCanvasError, 
+	    	"CCanvas.run expects a q value.");
+		return NULL;
+	}
+
+	canvas_setParam(&(self->canvas_pars),id,par_id,value);
+	
+	return Py_BuildValue("i",id);
+}
+
+
+static PyMethodDef CCanvas_methods[] = {
+    {"evaluate",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q"},
+    {"add",    (PyCFunction)add   , METH_VARARGS, "Add a shape object"},
+    {"setParam",    (PyCFunction)setParam   , METH_VARARGS, "Set a parameter"},
+   {NULL}
+};
+
+static PyTypeObject CCanvasType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CCanvas",             /*tp_name*/
+    sizeof(CCanvas),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CCanvas_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*/
+    "CCanvas objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CCanvas_methods,             /* tp_methods */
+    CCanvas_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CCanvas_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CCanvas_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 addCCanvas(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CCanvasType) < 0)
+        return;
+
+    Py_INCREF(&CCanvasType);
+    PyModule_AddObject(module, "CCanvas", (PyObject *)&CCanvasType);
+    
+    d = PyModule_GetDict(module);
+    CCanvasError = PyErr_NewException("CCanvas.error", NULL, NULL);
+    PyDict_SetItemString(d, "CCanvasError", CCanvasError);
+}
+
Index: sansmodels/src/sans/models/prototypes/src/CDispCylinderModel.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/CDispCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/CDispCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,256 @@
+/** CDispCylinderModel
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY disperse_cylinder.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "disperse_cylinder.h"
+
+static double UNDEFINED = 1000010010.1234567;
+
+/// Error object for raised exceptions
+static PyObject * CDispCylinderModelError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	DispCylinderParameters model_pars;
+} CDispCylinderModel;
+
+
+static void
+CDispCylinderModel_dealloc(CDispCylinderModel* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CDispCylinderModel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CDispCylinderModel *self;
+    
+    self = (CDispCylinderModel *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CDispCylinderModel_init(CDispCylinderModel *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"sigma_phi",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"sigma_radius",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"sigma_theta",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"length",Py_BuildValue("d",400.000000));
+        PyDict_SetItemString(self->params,"cyl_theta",Py_BuildValue("d",1.570000));
+        PyDict_SetItemString(self->params,"background",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"n_pts",Py_BuildValue("d",25.000000));
+        PyDict_SetItemString(self->params,"contrast",Py_BuildValue("d",0.000003));
+        PyDict_SetItemString(self->params,"cyl_phi",Py_BuildValue("d",0.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CDispCylinderModel_members[] = {
+    {"params", T_OBJECT, offsetof(CDispCylinderModel, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CDispCylinderModel, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CDispCylinderModel_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CDispCylinderModel *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.sigma_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "sigma_phi") );
+    self->model_pars.sigma_radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "sigma_radius") );
+    self->model_pars.sigma_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "sigma_theta") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.cyl_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_theta") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.n_pts = PyFloat_AsDouble( PyDict_GetItemString(self->params, "n_pts") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+    self->model_pars.cyl_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_phi") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CDispCylinderModelError, 
+	    	"CDispCylinderModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CDispCylinderModelError, 
+	    		"CDispCylinderModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CDispCylinderModel_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CDispCylinderModel_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",disperse_cylinder_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CDispCylinderModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",disperse_cylinder_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+static PyObject * reset(CDispCylinderModel *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CDispCylinderModel_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CDispCylinderModelType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CDispCylinderModel",             /*tp_name*/
+    sizeof(CDispCylinderModel),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CDispCylinderModel_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*/
+    "CDispCylinderModel objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CDispCylinderModel_methods,             /* tp_methods */
+    CDispCylinderModel_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CDispCylinderModel_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CDispCylinderModel_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 addCDispCylinderModel(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CDispCylinderModelType) < 0)
+        return;
+
+    Py_INCREF(&CDispCylinderModelType);
+    PyModule_AddObject(module, "CDispCylinderModel", (PyObject *)&CDispCylinderModelType);
+    
+    d = PyModule_GetDict(module);
+    CDispCylinderModelError = PyErr_NewException("CDispCylinderModel.error", NULL, NULL);
+    PyDict_SetItemString(d, "CDispCylinderModelError", CDispCylinderModelError);
+}
+
Index: sansmodels/src/sans/models/prototypes/src/CSimCylinder.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/CSimCylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/CSimCylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,246 @@
+/** CSimCylinder
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY simcylinder.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "simcylinder.h"
+
+static double UNDEFINED = 1000010010.1234567;
+
+/// Error object for raised exceptions
+static PyObject * CSimCylinderError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	SimCylinderParameters model_pars;
+} CSimCylinder;
+
+
+static void
+CSimCylinder_dealloc(CSimCylinder* self)
+{
+        modelcalculations_dealloc(&(self->model_pars.calcPars));
+    self->ob_type->tp_free((PyObject*)self);
+
+}
+
+static PyObject *
+CSimCylinder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CSimCylinder *self;
+    
+    self = (CSimCylinder *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CSimCylinder_init(CSimCylinder *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"phi",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"qmax",Py_BuildValue("d",0.200000));
+        PyDict_SetItemString(self->params,"length",Py_BuildValue("d",200.000000));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",80.000000));
+        PyDict_SetItemString(self->params,"theta",Py_BuildValue("d",1.570000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+                modelcalculations_init(&(self->model_pars.calcPars));
+
+    }
+    return 0;
+}
+
+static PyMemberDef CSimCylinder_members[] = {
+    {"params", T_OBJECT, offsetof(CSimCylinder, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CSimCylinder, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CSimCylinder_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CSimCylinder *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "phi") );
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.qmax = PyFloat_AsDouble( PyDict_GetItemString(self->params, "qmax") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "theta") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CSimCylinderError, 
+	    	"CSimCylinder.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CSimCylinderError, 
+	    		"CSimCylinder.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CSimCylinder_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CSimCylinder_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",simcylinder_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CSimCylinder_readDouble(pars);		
+		
+		return Py_BuildValue("d",simcylinder_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+static PyObject * reset(CSimCylinder *self, PyObject *args) {
+    modelcalculations_reset(&(self->model_pars.calcPars));
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CSimCylinder_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CSimCylinderType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CSimCylinder",             /*tp_name*/
+    sizeof(CSimCylinder),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CSimCylinder_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*/
+    "CSimCylinder objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CSimCylinder_methods,             /* tp_methods */
+    CSimCylinder_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CSimCylinder_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CSimCylinder_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 addCSimCylinder(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CSimCylinderType) < 0)
+        return;
+
+    Py_INCREF(&CSimCylinderType);
+    PyModule_AddObject(module, "CSimCylinder", (PyObject *)&CSimCylinderType);
+    
+    d = PyModule_GetDict(module);
+    CSimCylinderError = PyErr_NewException("CSimCylinder.error", NULL, NULL);
+    PyDict_SetItemString(d, "CSimCylinderError", CSimCylinderError);
+}
+
Index: sansmodels/src/sans/models/prototypes/src/CSimCylinderF.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/CSimCylinderF.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/CSimCylinderF.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,246 @@
+/** CSimCylinderF
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY ..\prototypes\src\simcylinder_fast.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "simcylinder_fast.h"
+
+/// Error object for raised exceptions
+static PyObject * CSimCylinderFError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	SimCylinderFParameters model_pars;
+} CSimCylinderF;
+
+
+static void
+CSimCylinderF_dealloc(CSimCylinderF* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+        modelcalculations_dealloc(&(self->model_pars.calcPars));
+
+}
+
+static PyObject *
+CSimCylinderF_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CSimCylinderF *self;
+    
+    self = (CSimCylinderF *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CSimCylinderF_init(CSimCylinderF *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"theta",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"length",Py_BuildValue("d",400.000000));
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"phi",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"npoints",Py_BuildValue("d",50000.000000));
+        PyDict_SetItemString(self->params,"seed",Py_BuildValue("d",1000.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+                modelcalculations_init(&(self->model_pars.calcPars));
+
+    }
+    return 0;
+}
+
+static PyMemberDef CSimCylinderF_members[] = {
+    {"params", T_OBJECT, offsetof(CSimCylinderF, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CSimCylinderF, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CSimCylinderF_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CSimCylinderF *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "theta") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "phi") );
+    self->model_pars.npoints = PyFloat_AsDouble( PyDict_GetItemString(self->params, "npoints") );
+    self->model_pars.seed = PyFloat_AsDouble( PyDict_GetItemString(self->params, "seed") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CSimCylinderFError, 
+	    	"CSimCylinderF.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CSimCylinderFError, 
+	    		"CSimCylinderF.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CSimCylinderF_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CSimCylinderF_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",simcylinder_fast_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CSimCylinderF_readDouble(pars);		
+		
+		return Py_BuildValue("d",simcylinder_fast_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+static PyObject * reset(CSimCylinderF *self, PyObject *args) {
+    modelcalculations_reset(&(self->model_pars.calcPars));
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CSimCylinderF_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CSimCylinderFType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CSimCylinderF",             /*tp_name*/
+    sizeof(CSimCylinderF),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CSimCylinderF_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*/
+    "CSimCylinderF objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CSimCylinderF_methods,             /* tp_methods */
+    CSimCylinderF_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CSimCylinderF_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CSimCylinderF_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 addCSimCylinderF(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CSimCylinderFType) < 0)
+        return;
+
+    Py_INCREF(&CSimCylinderFType);
+    PyModule_AddObject(module, "CSimCylinderF", (PyObject *)&CSimCylinderFType);
+    
+    d = PyModule_GetDict(module);
+    CSimCylinderFError = PyErr_NewException("CSimCylinderF.error", NULL, NULL);
+    PyDict_SetItemString(d, "CSimCylinderFError", CSimCylinderFError);
+}
+
Index: sansmodels/src/sans/models/prototypes/src/CSimSphereF.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/CSimSphereF.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/CSimSphereF.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,240 @@
+/** CSimSphereF
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY ../prototypes/src/sphere_fast.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "sphere_fast.h"
+
+/// Error object for raised exceptions
+static PyObject * CSimSphereFError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	SimSphereFParameters model_pars;
+} CSimSphereF;
+
+
+static void
+CSimSphereF_dealloc(CSimSphereF* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+        modelcalculations_dealloc(&(self->model_pars.calcPars));
+
+}
+
+static PyObject *
+CSimSphereF_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CSimSphereF *self;
+    
+    self = (CSimSphereF *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CSimSphereF_init(CSimSphereF *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"npoints",Py_BuildValue("d",50000.000000));
+        PyDict_SetItemString(self->params,"seed",Py_BuildValue("d",1000.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+                modelcalculations_init(&(self->model_pars.calcPars));
+
+    }
+    return 0;
+}
+
+static PyMemberDef CSimSphereF_members[] = {
+    {"params", T_OBJECT, offsetof(CSimSphereF, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CSimSphereF, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CSimSphereF_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CSimSphereF *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.npoints = PyFloat_AsDouble( PyDict_GetItemString(self->params, "npoints") );
+    self->model_pars.seed = PyFloat_AsDouble( PyDict_GetItemString(self->params, "seed") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CSimSphereFError, 
+	    	"CSimSphereF.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CSimSphereFError, 
+	    		"CSimSphereF.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CSimSphereF_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CSimSphereF_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",sphere_fast_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CSimSphereF_readDouble(pars);		
+		
+		return Py_BuildValue("d",sphere_fast_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+static PyObject * reset(CSimSphereF *self, PyObject *args) {
+    modelcalculations_reset(&(self->model_pars.calcPars));
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CSimSphereF_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CSimSphereFType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CSimSphereF",             /*tp_name*/
+    sizeof(CSimSphereF),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CSimSphereF_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*/
+    "CSimSphereF objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CSimSphereF_methods,             /* tp_methods */
+    CSimSphereF_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CSimSphereF_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CSimSphereF_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 addCSimSphereF(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CSimSphereFType) < 0)
+        return;
+
+    Py_INCREF(&CSimSphereFType);
+    PyModule_AddObject(module, "CSimSphereF", (PyObject *)&CSimSphereFType);
+    
+    d = PyModule_GetDict(module);
+    CSimSphereFError = PyErr_NewException("CSimSphereF.error", NULL, NULL);
+    PyDict_SetItemString(d, "CSimSphereFError", CSimSphereFError);
+}
+
Index: sansmodels/src/sans/models/prototypes/src/CSmearCylinderModel.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/CSmearCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/CSmearCylinderModel.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,254 @@
+/** CSmearCylinderModel
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY smeared_cylinder.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "smeared_cylinder.h"
+
+static double UNDEFINED = 1000010010.1234567;
+
+/// Error object for raised exceptions
+static PyObject * CSmearCylinderModelError = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	SmearCylinderParameters model_pars;
+} CSmearCylinderModel;
+
+
+static void
+CSmearCylinderModel_dealloc(CSmearCylinderModel* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+    
+
+}
+
+static PyObject *
+CSmearCylinderModel_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CSmearCylinderModel *self;
+    
+    self = (CSmearCylinderModel *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CSmearCylinderModel_init(CSmearCylinderModel *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"sigma_phi",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"sigma_radius",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"sigma_theta",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"length",Py_BuildValue("d",400.000000));
+        PyDict_SetItemString(self->params,"cyl_theta",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"background",Py_BuildValue("d",0.000000));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"contrast",Py_BuildValue("d",0.000003));
+        PyDict_SetItemString(self->params,"cyl_phi",Py_BuildValue("d",0.000000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+        
+
+    }
+    return 0;
+}
+
+static PyMemberDef CSmearCylinderModel_members[] = {
+    {"params", T_OBJECT, offsetof(CSmearCylinderModel, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CSmearCylinderModel, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CSmearCylinderModel_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CSmearCylinderModel *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.sigma_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "sigma_phi") );
+    self->model_pars.sigma_radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "sigma_radius") );
+    self->model_pars.sigma_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "sigma_theta") );
+    self->model_pars.length = PyFloat_AsDouble( PyDict_GetItemString(self->params, "length") );
+    self->model_pars.cyl_theta = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_theta") );
+    self->model_pars.background = PyFloat_AsDouble( PyDict_GetItemString(self->params, "background") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.contrast = PyFloat_AsDouble( PyDict_GetItemString(self->params, "contrast") );
+    self->model_pars.cyl_phi = PyFloat_AsDouble( PyDict_GetItemString(self->params, "cyl_phi") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CSmearCylinderModelError, 
+	    	"CSmearCylinderModel.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CSmearCylinderModelError, 
+	    		"CSmearCylinderModel.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CSmearCylinderModel_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CSmearCylinderModel_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",smeared_cylinder_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CSmearCylinderModel_readDouble(pars);		
+		
+		return Py_BuildValue("d",smeared_cylinder_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+static PyObject * reset(CSmearCylinderModel *self, PyObject *args) {
+    
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CSmearCylinderModel_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CSmearCylinderModelType = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CSmearCylinderModel",             /*tp_name*/
+    sizeof(CSmearCylinderModel),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CSmearCylinderModel_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*/
+    "CSmearCylinderModel objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CSmearCylinderModel_methods,             /* tp_methods */
+    CSmearCylinderModel_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CSmearCylinderModel_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CSmearCylinderModel_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 addCSmearCylinderModel(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CSmearCylinderModelType) < 0)
+        return;
+
+    Py_INCREF(&CSmearCylinderModelType);
+    PyModule_AddObject(module, "CSmearCylinderModel", (PyObject *)&CSmearCylinderModelType);
+    
+    d = PyModule_GetDict(module);
+    CSmearCylinderModelError = PyErr_NewException("CSmearCylinderModel.error", NULL, NULL);
+    PyDict_SetItemString(d, "CSmearCylinderModelError", CSmearCylinderModelError);
+}
+
Index: sansmodels/src/sans/models/prototypes/src/CTestSphere2.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/CTestSphere2.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/CTestSphere2.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,240 @@
+/** CTestSphere2
+ *
+ * C extension 
+ *
+ * WARNING: THIS FILE WAS GENERATED BY WRAPPERGENERATOR.PY
+ *          DO NOT MODIFY THIS FILE, MODIFY testsphere.h
+ *          AND RE-RUN THE GENERATOR SCRIPT
+ *
+ * @author   M.Doucet / UTK
+ */
+ 
+#include <Python.h>
+#include "structmember.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+
+#include "testsphere.h"
+
+static double UNDEFINED = 1000010010.1234567;
+
+/// Error object for raised exceptions
+static PyObject * CTestSphere2Error = NULL;
+
+
+// Class definition
+typedef struct {
+    PyObject_HEAD
+    /// Parameters
+    PyObject * params;
+    /// Log for unit testing
+    PyObject * log;
+    /// Model parameters
+	TestSphereParameters model_pars;
+} CTestSphere2;
+
+
+static void
+CTestSphere2_dealloc(CTestSphere2* self)
+{
+    self->ob_type->tp_free((PyObject*)self);
+        modelcalculations_dealloc(&(self->model_pars.calcPars));
+
+}
+
+static PyObject *
+CTestSphere2_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    CTestSphere2 *self;
+    
+    self = (CTestSphere2 *)type->tp_alloc(type, 0);
+   
+    return (PyObject *)self;
+}
+
+static int
+CTestSphere2_init(CTestSphere2 *self, PyObject *args, PyObject *kwds)
+{
+    if (self != NULL) {
+    	
+    	// Create parameters
+        self->params = PyDict_New();
+        
+        // Initialize parameter dictionary
+        PyDict_SetItemString(self->params,"scale",Py_BuildValue("d",1.000000));
+        PyDict_SetItemString(self->params,"radius",Py_BuildValue("d",20.000000));
+        PyDict_SetItemString(self->params,"qmax",Py_BuildValue("d",0.100000));
+
+         
+        // Create empty log
+        self->log = PyDict_New();
+        
+                modelcalculations_init(&(self->model_pars.calcPars));
+
+    }
+    return 0;
+}
+
+static PyMemberDef CTestSphere2_members[] = {
+    {"params", T_OBJECT, offsetof(CTestSphere2, params), 0,
+     "Parameters"},
+    {"log", T_OBJECT, offsetof(CTestSphere2, log), 0,
+     "Log"},
+    {NULL}  /* Sentinel */
+};
+
+/** Read double from PyObject
+    @param p PyObject
+    @return double
+*/
+double CTestSphere2_readDouble(PyObject *p) {
+    if (PyFloat_Check(p)==1) {
+        return (double)(((PyFloatObject *)(p))->ob_fval);
+    } else if (PyInt_Check(p)==1) {
+        return (double)(((PyIntObject *)(p))->ob_ival);
+    } else if (PyLong_Check(p)==1) {
+        return (double)PyLong_AsLong(p);
+    } else {
+        return 0.0;
+    }
+}
+
+
+/**
+ * Function to call to evaluate model
+ * @param args: input q or [q,phi]
+ * @return: function value
+ */
+static PyObject * run(CTestSphere2 *self, PyObject *args) {
+	double q_value, phi_value;
+	PyObject* pars;
+	int npars;
+	
+	// Get parameters
+	
+	// Reader parameter dictionary
+    self->model_pars.scale = PyFloat_AsDouble( PyDict_GetItemString(self->params, "scale") );
+    self->model_pars.radius = PyFloat_AsDouble( PyDict_GetItemString(self->params, "radius") );
+    self->model_pars.qmax = PyFloat_AsDouble( PyDict_GetItemString(self->params, "qmax") );
+
+	
+	// Get input and determine whether we have to supply a 1D or 2D return value.
+	if ( !PyArg_ParseTuple(args,"O",&pars) ) {
+	    PyErr_SetString(CTestSphere2Error, 
+	    	"CTestSphere2.run expects a q value.");
+		return NULL;
+	}
+	  
+	// Check params
+	if( PyList_Check(pars)==1) {
+		
+		// Length of list should be 2 for I(q,phi)
+	    npars = PyList_GET_SIZE(pars); 
+	    if(npars!=2) {
+	    	PyErr_SetString(CTestSphere2Error, 
+	    		"CTestSphere2.run expects a double or a list of dimension 2.");
+	    	return NULL;
+	    }
+	    // We have a vector q, get the q and phi values at which
+	    // to evaluate I(q,phi)
+	    q_value = CTestSphere2_readDouble(PyList_GET_ITEM(pars,0));
+	    phi_value = CTestSphere2_readDouble(PyList_GET_ITEM(pars,1));
+	    // Skip zero
+	    if (q_value==0) {
+	    	return Py_BuildValue("d",0.0);
+	    }
+		return Py_BuildValue("d",testsphere_analytical_2D(&(self->model_pars),q_value,phi_value));
+
+	} else {
+
+		// We have a scalar q, we will evaluate I(q)
+		q_value = CTestSphere2_readDouble(pars);		
+		
+		return Py_BuildValue("d",testsphere_analytical_1D(&(self->model_pars),q_value));
+	}	
+}
+
+static PyObject * reset(CTestSphere2 *self, PyObject *args) {
+    modelcalculations_reset(&(self->model_pars.calcPars));
+
+    return Py_BuildValue("d",0.0);
+}
+
+
+static PyMethodDef CTestSphere2_methods[] = {
+    {"run",      (PyCFunction)run     , METH_VARARGS,
+      "Evaluate the model at a given Q"},
+    {"reset",    (PyCFunction)reset   , METH_VARARGS,
+      "Reset pair correlation"},
+    //{"numerical_1D",      (PyCFunction)numerical_1D     , METH_VARARGS,
+    //  "Evaluate the 1D model at a given Q"},
+   {NULL}
+};
+
+static PyTypeObject CTestSphere2Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /*ob_size*/
+    "CTestSphere2",             /*tp_name*/
+    sizeof(CTestSphere2),             /*tp_basicsize*/
+    0,                         /*tp_itemsize*/
+    (destructor)CTestSphere2_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*/
+    "CTestSphere2 objects",           /* tp_doc */
+    0,		               /* tp_traverse */
+    0,		               /* tp_clear */
+    0,		               /* tp_richcompare */
+    0,		               /* tp_weaklistoffset */
+    0,		               /* tp_iter */
+    0,		               /* tp_iternext */
+    CTestSphere2_methods,             /* tp_methods */
+    CTestSphere2_members,             /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)CTestSphere2_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    CTestSphere2_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 addCTestSphere2(PyObject *module) {
+	PyObject *d;
+	
+    if (PyType_Ready(&CTestSphere2Type) < 0)
+        return;
+
+    Py_INCREF(&CTestSphere2Type);
+    PyModule_AddObject(module, "CTestSphere2", (PyObject *)&CTestSphere2Type);
+    
+    d = PyModule_GetDict(module);
+    CTestSphere2Error = PyErr_NewException("CTestSphere2.error", NULL, NULL);
+    PyDict_SetItemString(d, "CTestSphere2Error", CTestSphere2Error);
+}
+
Index: sansmodels/src/sans/models/prototypes/src/c_models.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/c_models.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/c_models.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,39 @@
+/** c_models
+ *
+ * Module containing all SANS model extensions 
+ *
+ * @author   M.Doucet / UTK
+ */
+#include <Python.h>
+
+/// CVS information
+static char cvsid[] = "$Id: BaseCComponent.c,v 1.4 2007/03/23 20:50:58 doucet Exp $";
+
+
+/**
+ * Define empty module
+ */
+static PyMethodDef module_methods[] = {
+    {NULL} 
+};
+
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+initc_models(void) 
+{
+    PyObject* m;
+
+    m = Py_InitModule3("c_models", module_methods,
+                       "C extension module for SANS scattering models.");
+                       
+	addCTestSphere2(m);
+	addCSimCylinder(m);
+	addCSmearCylinderModel(m);
+	addCDispCylinderModel(m);
+	addCSimCylinderF(m);
+	addCSimSphereF(m);
+	addCCanvas(m);
+}
Index: sansmodels/src/sans/models/prototypes/src/canvas.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/canvas.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/canvas.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,291 @@
+#include "modelCalculations.h"
+#include "canvas.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <memory.h>
+#include <time.h>
+
+/**
+ * Initialization function for simulation structure
+ */
+int canvas_init(CanvasParams *pars) {
+	int i;
+	
+	pars->n_shapes = 0;
+	pars->max_shapes = 3; 
+	
+	// Allocate memory
+	pars->shapes = (SpaceObject*) malloc(pars->max_shapes*sizeof(SpaceObject));
+	
+	if(pars->shapes==NULL) {
+		printf("canvas_init: Problem allocating memory for space objects\n");
+		return -1;
+	}
+	
+	return 0;
+}
+
+int canvas_dealloc(CanvasParams *self) {
+	int i;
+	printf("dealloc\n");
+	/*
+	//for(i=0; i<self->n_shapes; i++) {
+	for(i=0; i<self->max_shapes; i++) {
+		free(self->shapes[i].points);
+		free(self->shapes[i].params);
+		//free(self->shapes[i].generate);
+	}
+	*/
+	free(self->shapes);
+	printf("deallocated\n");
+	return 0;
+}
+
+int canvas_add(CanvasParams *self, int objectCode) {
+	int id;
+	id = self->n_shapes;
+	
+	if(objectCode==REALSPACE_SPHERE) {
+		initSphereObject( &(self->shapes[id]) );
+	}
+	
+	self->shapes[id].layer = id;
+	self->n_shapes++;
+	
+	return id;
+}
+
+int canvas_setParam(CanvasParams *self, int shapeID, int paramID, double value) {
+	self->shapes[shapeID].params[paramID]=value;
+	self->shapes[shapeID].points_available = 0;
+	
+	// update volume
+	self->shapes[shapeID].volume = (*self->shapes[shapeID].getVolume)(&(self->shapes[shapeID].params));
+	
+	return 0;
+}
+
+double canvas_intensity(CanvasParams *self, double q, double phi) {
+	double phase, cos_term, sin_term;
+	double qx, qy, vol;
+	int i, j, npts;
+	double pars[4];
+	
+	double cos_shape, sin_shape;
+	
+	double vol_core, vol_shell, vol_sum;
+	
+	pars[0] = 1.0;
+	pars[1] = 40.0;
+	
+	cos_term = 0.0;
+	sin_term = 0.0;
+	
+	vol_sum = 0.0;
+	
+	vol = self->shapes[0].volume;
+	vol_core = self->shapes[1].volume;
+	vol_shell = vol-vol_core;
+
+	npts = 0;
+	
+	qx = q*cos(phi);
+	qy = q*sin(phi);
+	
+	for(i=0; i<self->n_shapes; i++) {
+		if (self->shapes[i].points_available==0) {
+			(*self->shapes[i].generate)( self->shapes[i].points, self->shapes[i].params, self->shapes[i].npts);
+			self->shapes[i].points_available=1;
+		}
+		npts = 0;
+		cos_shape = 0;
+		sin_shape = 0;
+		for(j=0; j<self->shapes[i].npts; j++) {
+			
+			if(canvas_PointAllowed(self, i, j)==1) {
+				npts++;
+				
+				phase = qx*self->shapes[i].points[j].x + qy*self->shapes[i].points[j].y;
+				//phase = 0.0;
+				
+				cos_shape += cos(phase);
+				sin_shape += sin(phase);
+			}   			
+		}
+
+		//printf("ID %i: cos=%g sin=%g fac=%g\n",i,cos_shape, sin_shape, self->shapes[i].volume /self->shapes[i].npts  * self->shapes[i].params[2]);
+
+
+		
+		
+		// TODO: must find an efficient way to get the exact volume of each SLD regions.
+		// For instance, if a volume is completely included in another (and it has a higher layer
+		// number, we can simply subtract the two "theoretical" volumes instead of using the 
+		// ratio of the points.
+
+		// The following are the three lines we would like to write:
+		
+		//cos_term += cos_shape * self->shapes[i].volume /npts  * self->shapes[i].params[2];
+		//sin_term += sin_shape * self->shapes[i].volume /npts  * self->shapes[i].params[2];
+		//vol_sum += self->shapes[i].volume;
+		
+		// In that case, the volume is approx
+		//     vol = (full volume) - sum(volume parts overlapping with other objects)
+		// That can easily be done for object completely contained by others.
+
+		cos_term += cos_shape * self->shapes[i].volume /self->shapes[i].npts  * self->shapes[i].params[2];
+		sin_term += sin_shape * self->shapes[i].volume /self->shapes[i].npts  * self->shapes[i].params[2];
+		vol_sum += self->shapes[i].volume*npts/self->shapes[i].npts;
+		
+		
+		//vol_sum += self->shapes[i].volume;
+
+		// The following is more precise (but implies better investigation of the topology)
+		/*
+		if(i==0){
+			cos_term += cos_shape * vol_shell/npts  * self->shapes[i].params[2];
+			sin_term += sin_shape * vol_shell/npts  * self->shapes[i].params[2];
+			vol_sum += vol_shell;
+		} else {
+			cos_term += cos_shape * vol_core/npts  * self->shapes[i].params[2];
+			sin_term += sin_shape * vol_core/npts  * self->shapes[i].params[2];
+			vol_sum += vol_core;
+		}
+		*/
+	}
+	
+	return 1.0e8*(cos_term*cos_term + sin_term*sin_term)/vol_sum;	
+} 
+
+int canvas_PointAllowed(CanvasParams *self, int object_id, int i_pt) {
+	// Check whether space point i_pt is already covered by another object
+	int i;
+	
+	for(i=0; i<self->n_shapes; i++) {
+		// Check the object object_id is underneath
+		if(self->shapes[object_id].layer < self->shapes[i].layer) {
+			// Is the point overlapping, if so skip it.
+			if(self->shapes[i].isContained(&self->shapes[object_id].points[i_pt], 
+				self->shapes[i].params) == 1) {
+				return 0;
+			}
+		} 
+	}
+	return 1;
+} 
+
+
+
+// SPHERE object ----------------------------------------------------------------------
+
+
+int initSphereObject(SpaceObject *pars) {
+	
+	pars->x = 0.0;
+	pars->y = 0.0;
+	pars->z = 0.0;
+	
+	pars->o1 = 0.0;
+	pars->o2 = 0.0;
+	pars->o3 = 0.0;
+	
+	/*
+	free(pars->params);
+	
+	printf("allocating parameters\n");
+	pars->params = (double*) malloc(5*sizeof(double));
+	if(pars->params==NULL) {
+		printf("initSphereObject: Problem allocating memory\n");
+		return -1;	
+	}
+	*/
+
+	// Scale
+	pars->params[0] = 1.0;
+	// Radius
+	pars->params[1] = 40.0;
+	// Contrast
+	pars->params[2] = 1.0;
+	 
+	pars->points_available = 0;
+	
+	pars->npts = 150000;
+	free(pars->points);
+	
+	// Generation function
+	pars->generate = &generateSphereObject;
+	// isContained function
+	pars->isContained = &sphere_IsContained;
+	pars->getVolume = &sphere_getVolume;
+	
+	pars->volume = acos(-1.0)*4.0/3.0*pars->params[1]*pars->params[1]*pars->params[1];
+	
+	pars->layer = 0;
+	
+	return 0;
+	
+}
+
+int sphere_IsContained(SpacePoint *point, double *params) {
+	if( sqrt(point->x*point->x + point->y*point->y + point->z*point->z) < params[1]) {
+	  return 1;
+	}
+	return 0;
+}
+
+double sphere_getVolume(double *params) {
+	printf("radius=%g\n",params[1]);
+	return acos(-1.0)*4.0/3.0*params[1]*params[1]*params[1];
+}
+
+
+int generateSphereObject(SpacePoint *points, double *params, int npts) {
+	int i, testcounter;
+	double x, y, z;
+	SpacePoint tmp;
+	
+	printf("radius=%g, npts=%i\n", params[1], npts);
+	
+	testcounter = 0;
+	
+	/*
+	free(points);
+	printf("allocating points\n");
+	points = (SpacePoint*) malloc(npts*sizeof(SpacePoint));
+	if(points==NULL) {
+		printf("generateSphereObject: Problem allocating memory\n");
+		return -1;	
+	}
+	*/
+	memset(points,0,npts*sizeof(SpacePoint));
+
+	for(i=0;i<npts;i++) {
+		// Generate in a box centered around zero
+		x = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * params[1];
+		y = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * params[1];
+		z = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * params[1];
+		
+		// reject those that are not within the volume
+//		if( sqrt(x*x+y*y+z*z) < params[1]) {
+		tmp.x = x;
+		tmp.y = y;
+		tmp.z = z;
+		if( sphere_IsContained(&tmp, params) == 1 ) {
+			points[i].x =  x;
+			points[i].y =  y;
+			points[i].z =  z;
+			testcounter++;
+		} else {
+			i--;
+		}
+	}
+	
+	// Consistency check
+	if(testcounter != npts) {
+		return -1;
+	}
+	
+	return testcounter;
+	
+}
Index: sansmodels/src/sans/models/prototypes/src/canvas.h
===================================================================
--- sansmodels/src/sans/models/prototypes/src/canvas.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/canvas.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,65 @@
+#if !defined(simcanvas_h)
+#define simcanvas_h
+
+#include "modelCalculations.h"
+
+#define REALSPACE_SPHERE 1
+
+typedef struct {
+	// Position
+	double x;
+	double y;
+	double z;
+	// Orientation
+	double o1;
+	double o2;
+	double o3;
+	// Vector of parameters
+	double params[15];
+	
+	// Pointer to function to generate space points
+	int (*generate)(SpacePoint *points, double *params, int npts);
+	// Pointer to function to check whether a point is contained by an object
+	int (*isContained)(SpacePoint *point, double *params);
+	// Pointer to function to calculate the object's volume
+	double (*getVolume)(double *params);
+	
+	
+	// Points available
+	int points_available;
+	// Space points
+	SpacePoint points[151000];
+	//SpacePoint *points;
+	// Number of space points
+	int npts;
+	double volume;
+	int layer;
+	
+} SpaceObject;
+
+
+typedef struct {
+	// Maximum number of shapes
+	int max_shapes;
+	// List of real-space objects
+	SpaceObject *shapes;
+	int n_shapes;
+} CanvasParams;
+
+
+int canvas_dealloc(CanvasParams *self);
+int canvas_add(CanvasParams *pars, SpaceObject *shape);
+double canvas_intensity(CanvasParams *self, double q, double phi);
+int canvas_init(CanvasParams *pars);
+int canvas_add(CanvasParams *self, int objectCode);
+int canvas_setParam(CanvasParams *self, int shapeID, int paramID, double value);
+int canvas_PointAllowed(CanvasParams *self, int object_id, int i_pt);
+
+
+// SPHERE object ----------------------------------------------------------------------
+int initSphereObject(SpaceObject *pars);
+int generateSphereObject(SpacePoint *points, double *params, int npts);
+int sphere_IsContained(SpacePoint *point, double *params);
+double sphere_getVolume(double *params);
+
+#endif
Index: sansmodels/src/sans/models/prototypes/src/disperse_cylinder.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/disperse_cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/disperse_cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,86 @@
+/**
+ * Scattering model for a cylinder
+ * @author: Mathieu Doucet / UTK
+ */
+
+#include "cylinder.h"
+#include "disperse_cylinder.h"
+#include <math.h>
+#include "libCylinder.h"
+
+double c_disperser( double (*eval)(), double dp[], int n_pars, 
+					int *idList, double *sigmaList, int n_pts, double q, double phi );
+
+/**
+ * Function to evaluate 1D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double disperse_cylinder_analytical_1D(DispCylinderParameters *pars, double q) {
+	double dp[5];
+	
+	// Fill paramater array
+	dp[0] = pars->scale;
+	dp[1] = pars->radius;
+	dp[2] = pars->length;
+	dp[3] = pars->contrast;
+	dp[4] = pars->background;
+	
+	return CylinderForm(dp, q);
+}
+	
+double disp_cyl_run(double pars[], double q, double phi) {
+	CylinderParameters danse_pars;
+	danse_pars.scale        = pars[0];
+	danse_pars.radius       = pars[1];
+	danse_pars.length       = pars[2];
+	danse_pars.contrast     = pars[3];
+	danse_pars.background   = pars[4];
+	danse_pars.cyl_theta    = pars[5];
+	danse_pars.cyl_phi      = pars[6];
+
+	return cylinder_analytical_2D(&danse_pars, q, phi);		
+		
+}
+	
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double disperse_cylinder_analytical_2D(DispCylinderParameters *pars, double q, double phi) {
+	CylinderParameters cyl_pars;
+	double dp[6];
+	int paramList[3];
+	double sigmaList[3];
+	int npts;
+	
+	
+	// Fill paramater struct
+	dp[0] = pars->scale;
+	dp[1] = pars->radius;
+	dp[2] = pars->length;
+	dp[3] = pars->contrast;
+	dp[4] = pars->background;
+	dp[6] = pars->cyl_phi;
+	dp[5] = pars->cyl_theta;
+	
+	paramList[0] = 6;
+	paramList[1] = 5;
+	paramList[2] = 1;
+	
+	
+	sigmaList[0] = pars->sigma_phi;
+	sigmaList[1] = pars->sigma_theta;
+	sigmaList[2] = pars->sigma_radius;
+	
+	npts = (int)(floor(pars->n_pts));
+	
+	return c_disperser( &disp_cyl_run, &dp, 3, 
+					paramList, sigmaList, npts, q, phi ); 
+
+	
+}
+    
Index: sansmodels/src/sans/models/prototypes/src/disperse_cylinder.h
===================================================================
--- sansmodels/src/sans/models/prototypes/src/disperse_cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/disperse_cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,67 @@
+#if !defined(disperse_cylinder_h)
+#define disperse_cylinder_h
+
+/** Structure definition for dispersed cylinder parameters 
+ * [PYTHONCLASS] = DispCylinderModel
+ * [BASESTRUCT]  = CylinderParameters 
+ * [BASE_H]      = cylinder
+ * */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    /// Radius of the cylinder [A]
+    //  [DEFAULT]=radius=20.0 A
+    double radius;
+    /// Length of the cylinder [A]
+    //  [DEFAULT]=length=400.0 A
+    double length;
+    /// Contrast [A-2]
+    //  [DEFAULT]=contrast=3.0e-6 A-2
+    double contrast;
+	/// Incoherent Background (cm-1) 0.000
+	//  [DEFAULT]=background=0 cm-1
+	double background;    
+    /// Orientation of the cylinder axis w/respect incoming beam [rad]
+    //  [DEFAULT]=cyl_theta=1.57 rad
+    double cyl_theta;
+    /// Orientation of the cylinder in the plane of the detector [rad]
+    //  [DEFAULT]=cyl_phi=0.0 rad
+    
+    ///
+    //  [DEFAULT]=cyl_phi=0.0 rad
+    double cyl_phi;    
+    
+    ///
+    //  [DEFAULT]=sigma_theta=0.0 rad
+    double sigma_theta;
+    
+    ///
+    //  [DEFAULT]=sigma_phi=0.0 rad
+    double sigma_phi;
+    
+    ///
+    //  [DEFAULT]=sigma_radius=0.0 rad
+    double sigma_radius;
+    
+    ///
+    //  [DEFAULT]=sigma_radius=0.0 rad
+    double sigma_length;
+    
+    ///
+    //  [DEFAULT]=n_pts=25
+    double n_pts;
+    
+    
+} DispCylinderParameters;
+
+
+
+/// 1D scattering function
+double disperse_cylinder_analytical_1D(DispCylinderParameters *pars, double q);
+
+/// 2D scattering function
+double disperse_cylinder_analytical_2D(DispCylinderParameters *pars, double q, double phi);
+double disperse_cylinder_dist( double x, double mean, double sigma );
+
+#endif
Index: sansmodels/src/sans/models/prototypes/src/modelCalculations.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/modelCalculations.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/modelCalculations.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,445 @@
+#include "modelCalculations.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <memory.h>
+#include <time.h>
+
+/**
+ * Initialization function for simulation structure
+ */
+void modelcalculations_init(CalcParameters *pars) {
+        pars->isRhoAvailable         = 0;
+        pars->isPointMemAllocated    = 0;
+        pars->isRhoAvailable_2D      = 0;
+        pars->isPointMemAllocated_2D = 0;
+       
+       	pars->volume_points = 0;
+       	pars->r_points = 0;
+       
+		pars->timePr_1D = 0;
+    	pars->timePr_2D = 0;
+    	pars->timeIq_1D = 0;
+    	pars->timeIq_2D = 0;
+    
+    	pars->errorOccured = 0;
+    	
+}
+
+/**
+ * Reset function for simulation structure
+ */
+void modelcalculations_reset(CalcParameters *pars) {
+       modelcalculations_dealloc(pars);
+       modelcalculations_init(pars);
+}
+
+/**
+ * Deallocate memory of simullation structure
+ */
+void modelcalculations_dealloc(CalcParameters *pars) {
+	free(pars->rho);
+	free(pars->points);
+	free(pars->rho_2D);
+	free(pars->points_2D);
+}
+
+/**
+ * Calculate pair correlation for 1D simulation
+ */
+int modelcalculations_calculatePairCorrelation_1D(SpacePoint * points, int volume_points, double * rho, int r_points, double bin_width) {
+	int i,j;
+	double dx,dy,dz,dist;
+	int i_bin;
+	//double bin_width;
+	double delta_t;
+	double average;
+	double closest;
+	time_t start_time;
+	clock_t start;
+	clock_t finish;
+	//struct tm *timeStruct;
+	
+	
+	// Allocate memory
+	/*
+	rho = (double*) malloc(r_points*sizeof(double));
+	if(rho==NULL){
+		printf("Problem allocating memory for 1D correlation points\n");
+		return -1;
+	}
+	*/
+	
+	// Clear vector
+	memset(rho,0,r_points*sizeof(double));
+
+	// R bin width
+    //bin_width = 2.0*size/r_points;
+    
+		time(&start_time);
+		start = clock();
+		
+		
+	average = 0;
+	for(i=0;i<volume_points-1;i++) {
+		closest = -1;
+		for(j=i+1;j<volume_points;j++) {
+            dx = (points[i].x-points[j].x);
+            dy = (points[i].y-points[j].y);
+            dz = (points[i].z-points[j].z);
+			dist = sqrt(dx*dx + dy*dy + dz*dz);
+			
+			if(closest<0 || dist<closest) {
+				closest = dist;
+			}
+			
+			//i_bin = (int)dist/bin_width;
+			i_bin = (int)floor(dist/bin_width);
+            
+            //rho[i_bin] = rho[i_bin] + 1.0/9000000.0;
+            if(i_bin >= r_points) {
+            	printf("problem! %i > %i\n", i_bin, r_points);
+            } else {
+            	rho[i_bin] = rho[i_bin] + 1.0;
+            }
+		}
+		average += closest;
+	}
+	average = average/(double)volume_points;
+	printf("average distance %f\n",average);
+	
+		finish = clock();
+        //time(&end_time);
+        //delta_t = difftime(end_time,start_time);
+        delta_t = ((double)(finish-start))/CLOCKS_PER_SEC;
+        printf("------------->PR calc time = %f\n", delta_t);
+    return 1;
+}
+
+/**
+ * Calculate I(q) for 1D simulation 
+ */
+double modelcalculations_calculateIq_1D(double * rho, int r_points, double r_step, double q) {
+	int i;
+	double value;
+	//double r_step;
+	//double vol;
+	double sum;
+	double qr;
+	clock_t start;
+	clock_t finish;
+	double delta_t;
+	
+	start = clock();
+	
+    //vol = 4.0*acos(-1.0)/3.0*radius*radius*radius;
+	//r_step = 2.0*radius/((double)(r_points));
+	
+	value = 0.0;
+	sum = 0.0;
+	for(i=1; i<r_points; i++) {
+		qr = q*r_step*(double)i;
+		value = value + rho[i] * sin(qr) / qr;
+		sum = sum + rho[i];
+	}
+	
+	
+	value = value/sum;
+	
+	finish = clock();
+    delta_t = ((double)(finish-start))/CLOCKS_PER_SEC;
+    //printf("------------->IQ calc time = %f\n", delta_t);
+	
+	
+	return value;
+}
+
+/**
+ * Calculate pair correlation function for 2D simulation using
+ * a 3D array to store P(r)
+ */
+int modelcalculations_calculatePairCorrelation_2D_3Darray(SpacePoint * points, int volume_points, float * rho, int r_points, double bin_width) {
+	int i,j;
+	int ix_bin, iy_bin, iz_bin;
+	
+	clock_t start;
+	clock_t finish;
+	
+	// Clear vector
+	memset(rho,0,r_points*r_points*r_points*sizeof(float));
+
+    start = clock();
+	for(i=0;i<volume_points-1;i++) {
+		for(j=i+1;j<volume_points;j++) {
+			
+            // Add entry to the matrix
+            ix_bin = (int)floor(fabs(points[i].x-points[j].x)/bin_width);
+            iy_bin = (int)floor(fabs(points[i].y-points[j].y)/bin_width);
+            iz_bin = (int)floor(fabs(points[i].z-points[j].z)/bin_width);
+            
+            if(ix_bin < r_points && iy_bin < r_points && iz_bin < r_points) {
+            	rho[(ix_bin*r_points+iy_bin)*r_points+iz_bin] += 1.0;
+            } else {
+            	printf("Bad point! %i %i %i\n", ix_bin, iy_bin, iz_bin);
+            }
+			
+		}
+	}
+	
+	finish = clock();
+    printf("-------------> Pair Correlation time = %f\n", ((double)(finish-start))/CLOCKS_PER_SEC);
+    return 0;
+}
+
+/**
+ * Calculate pair correlation function for 2D simulation by storing P(r) in a 2D array.
+ * Allows for rotation of object in space by specifying theta, phi, omage of the beam
+ */
+int modelcalculations_calculatePairCorrelation_2D_vector(SpacePoint * points, int volume_points, float * rho, 
+			int r_points, double bin_width, double theta_beam, double phi_beam, double omega_beam) {
+	int i,j;
+	int ix_bin, iy_bin;
+	SpacePoint p1, p2;
+	
+	clock_t start;
+	clock_t finish;
+	
+	// Clear vector
+	memset(rho,0,r_points*r_points*sizeof(float));
+	//printf("P(r) with theta=%g phi=%g\n", theta_beam, phi_beam);
+    start = clock();
+	for(i=0;i<volume_points-1;i++) {
+		// Rotate point
+		p1 = modelcalculations_rotate(points[i], theta_beam, phi_beam, omega_beam);
+		//printf("p = %g %g %g -> %g %g %g\n", points[i].x,points[i].y,points[i].z, p1.x,p1.y,p1.z);
+		for(j=i+1;j<volume_points;j++) {
+			// Rotate point
+			p2 = modelcalculations_rotate(points[j], theta_beam, phi_beam, omega_beam);
+			
+			
+			// Calculate distance in plane perpendicular to beam (z)
+            ix_bin = (int)floor(fabs(p1.x-p2.x)/bin_width);
+            iy_bin = (int)floor(fabs(p1.y-p2.y)/bin_width);
+            //iz_bin = (int)floor((points[i].z-points[j].z)/bin_width+r_points/2);
+            
+            rho[ix_bin*r_points+iy_bin] += 1.0;
+			
+		}
+	}
+	
+	finish = clock();
+    printf("-------------> 2D (v) Pair Correlation time = %f\n", ((double)(finish-start))/CLOCKS_PER_SEC);
+    return 1;
+}
+
+/**
+ * Calculate pair correlation function for 2D simulation by storing P(r) in a 2D array
+ */
+int modelcalculations_calculatePairCorrelation_2D(SpacePoint * points, int volume_points, float * rho, int r_points, double bin_width) {
+	int i,j;
+	int ix_bin, iy_bin;
+	
+	clock_t start;
+	clock_t finish;
+	
+	// Clear vector
+	memset(rho,0,r_points*r_points*sizeof(float));
+
+	//return 1;
+	
+    start = clock();
+	for(i=0;i<volume_points-1;i++) {
+		for(j=i+1;j<volume_points;j++) {
+			
+			// Calculate distance in plane perpendicular to beam (z)
+            ix_bin = (int)floor(fabs(points[i].x-points[j].x)/bin_width);
+            iy_bin = (int)floor(fabs(points[i].y-points[j].y)/bin_width);
+            //iz_bin = (int)floor(fabs(points[i].z-points[j].z)/bin_width);
+            
+            rho[ix_bin*r_points+iy_bin] += 1.0;
+            //rho[ix_bin*r_points+iy_bin] += fabs(points[i].z-points[j].z);
+		}
+	}
+	
+	finish = clock();
+    printf("-------------> Pair Correlation time = %f\n", ((double)(finish-start))/CLOCKS_PER_SEC);
+    
+    /*
+     * for(i=0;i<r_points;i++) {
+    	for(j=0;j<r_points;j++) {
+    		printf("Pr(%i, %i) = %g\n", i, j, rho[i*r_points+j]);
+    	}	
+    	
+    }
+    */
+	return 0;
+}
+
+/**
+ * Calculate I(q) for 2D simulation from 3D array
+ */
+double modelcalculations_calculateIq_2D_3Darray(float * rho, int r_points, double r_step, double q, double phi) {
+	//TODO: make rho an array of ints.
+	
+	int ix,iy,iz;
+	
+	// This should be a parameter
+	double lambda = 1.6;
+	double theta;
+	double value;
+	double sum;
+	// This should also be a parameter, about value of radius
+	double r_max = 1;
+	//double r_step =1;
+	
+	double c1;
+	double c2;
+	double c3;
+	int r2;
+	double f3;
+	double iz_c;
+	clock_t start; 
+	clock_t finish;
+	
+	theta = 2*asin(q*lambda/(4*acos(-1.0)));
+	
+	value = 0.0;
+	sum = 0.0;
+	//c1 = lambda*(cos(theta)-1)*r_step;
+	//c2 = lambda*sin(theta)*cos(phi)*r_step;
+	//c3 = lambda*sin(theta)*sin(phi)*r_step;
+	
+	c1 = 0;
+	c2 = q*cos(phi)*r_step;
+	c3 = q*sin(phi)*r_step;
+	
+	start = clock();
+	
+	// TODO: sum(rho) should be equal to the number of points^2 !
+	
+	for(ix=0; ix<r_points; ix++) {
+		for(iy=0; iy<r_points; iy++) {
+			r2 = ix*r_points*r_points+iy*r_points;
+			f3 = c2*(ix-r_points/2) + c3*(iy-r_points/2);
+			for(iz=0; iz<r_points; iz++) {
+				iz_c = (double)iz-r_points/2; 
+				value = value + rho[r2+iz] * cos( c1*iz_c + f3 );
+				sum = sum + rho[r2+iz];
+			}
+		}
+	}
+	finish = clock();
+    //printf("-------------> I(Q) time = %f, (%f %f %f)\n", ((double)(finish-start))/CLOCKS_PER_SEC, q, phi,value/sum);
+	
+	
+	value = value /sum;
+	return value;
+}
+
+/**
+ * Pair correlation function for a sphere
+ * 	@param r: distance value
+ */
+double pair_corr_sphere(double r) {
+	
+	return r*r*(1.0 - 0.75*r + r*r*r/16.0);	
+}
+
+/** 
+ * Calculate I(q) for 2D simulation from 2D array P(r)
+ */
+double modelcalculations_calculateIq_2D(float * rho, int r_points, double r_step, double q, double phi) {
+	//TODO: make rho an array of ints.
+	
+	int ix,iy;
+	
+	// This should be a parameter
+	double lambda = 1.0;
+	double value;
+	double sum;
+	// This should also be a parameter, about value of radius
+	double r_max = 1;
+	//double r_step =1;
+	
+	double c2;
+	double c3;
+	int ibin;
+	clock_t start; 
+	clock_t finish;
+	
+	//theta = 2*asin(q*lambda/(4*acos(-1.0)));
+	value = 0.0;
+	sum = 0.0;
+	c2 = q*cos(phi)*r_step;
+	c3 = q*sin(phi)*r_step;
+	//printf("phi = %g, c2=%g, c3=%g, q=%g, r_step=%g\n",phi, c2, c3,q,r_step);
+	
+	start = clock();
+	
+	for(ix=-r_points+1; ix<r_points; ix++) {
+		for(iy=-r_points+1; iy<r_points; iy++) {
+			
+			//value += rho[ix*r_points+iy] * cos( c2*((double)ix+0.5) + c3*((double)iy+0.5) );
+			//sum += rho[ix*r_points+iy];
+			
+			ibin = (int)(floor(sqrt(1.0*ix*ix)))*r_points+(int)(floor(sqrt(1.0*iy*iy)));
+			
+			if (ibin<r_points*r_points) {
+			
+				//value += rho[ibin] * cos( c2*((double)ix+0.5) + c3*((double)iy+0.5) );
+			
+				value += rho[ibin] * cos( c2*((double)ix) + c3*((double)iy) );
+				
+				sum += rho[ibin];
+			} else {
+				printf("Error computing IQ %i >= %i (%i %i)\n", ibin, r_points*r_points,ix, iy);
+			};
+			
+			
+			//dx = r_step*((double)ix+0.5);
+			//dy = r_step*((double)iy+0.5);
+			
+			// Only works for sphere of radius = 20
+			//f3 = pair_corr_sphere(sqrt(dx*dx+dy*dy)/20.0);
+			
+			//value += f3 * cos( c2*(dx) + c3*(dy) );
+			//sum += f3;		
+		}
+	}
+	finish = clock();
+	
+	value = value /sum;
+	return value;
+}
+
+
+/**
+ *  Rotation of a space point
+ */
+SpacePoint modelcalculations_rotate(SpacePoint p, double theta, double phi, double omega) {
+	SpacePoint new_point;
+	double x_1, x_2;
+	double y_1, y_2;
+	double z_1, z_2;
+	// P(r) assumes beam along z-axis. Rotate point accordingly
+	
+	
+	// Omega, around z-axis (doesn't change anything for cylindrical symmetry
+	x_1 = p.x*cos(omega) - p.y*sin(omega);
+	y_1 = p.x*sin(omega) + p.y*cos(omega);
+	z_1 = p.z;	
+	
+	// Theta, around y-axis
+	x_2 = x_1*cos(theta) + z_1*sin(theta);
+	y_2 = y_1;
+	z_2 = -x_1*sin(theta) + z_1*cos(theta);
+	
+	// Phi, around z-axis
+	new_point.x = x_2*cos(phi) - y_2*sin(phi);
+	new_point.y = x_2*sin(phi) + y_2*cos(phi);
+	new_point.z = z_2;
+	
+	return new_point;	
+}
+ 
+ 
Index: sansmodels/src/sans/models/prototypes/src/modelCalculations.h
===================================================================
--- sansmodels/src/sans/models/prototypes/src/modelCalculations.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/modelCalculations.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,64 @@
+#if !defined(modelcalcs_h)
+#define modelcalcs_h
+
+typedef struct {
+	double x;
+	double y;
+	double z;
+} SpacePoint;
+
+
+
+typedef struct {
+    /// Volume points
+    SpacePoint * points;
+    /// Pair correlation function
+    double * rho;
+    /// Is rho already generated?
+    int isRhoAvailable;
+    /// Memory allocation flag 
+    int isPointMemAllocated;
+    
+    /// Volume points
+    SpacePoint * points_2D;
+    /// Pair correlation function
+    float * rho_2D;
+    /// Is rho already generated?
+    int isRhoAvailable_2D;
+    /// Memory allocation flag
+    int isPointMemAllocated_2D;
+    
+    //TODO: add volume_points, r_points, step here...
+    
+    int volume_points;
+    int r_points;
+    
+    float timePr_1D;
+    float timePr_2D;
+    float timeIq_1D;
+    float timeIq_2D;
+    
+    int errorOccured;
+    
+    
+} CalcParameters;
+
+void modelcalculations_dealloc(CalcParameters *pars);
+void modelcalculations_init(CalcParameters *pars);
+void modelcalculations_reset(CalcParameters *pars);
+
+
+double modelcalculations_calculateIq_1D(double * rho, int r_points, double step, double q);
+int modelcalculations_calculatePairCorrelation_1D(SpacePoint * points, int volume_points, double * rho, int r_points, double size);
+
+double modelcalculations_calculateIq_2D(float * rho, int r_points, double step, double q, double phi);
+int modelcalculations_calculatePairCorrelation_2D(SpacePoint * points, int volume_points, float * rho, int r_points, double size);
+
+double modelcalculations_calculateIq_2D_3Darray(float * rho, int r_points, double step, double q, double phi);
+int modelcalculations_calculatePairCorrelation_2D_3Darray(SpacePoint * points, int volume_points, float * rho, int r_points, double size);
+SpacePoint modelcalculations_rotate(SpacePoint p, double theta, double phi, double omega);
+
+int modelcalculations_calculatePairCorrelation_2D_vector(SpacePoint * points, int volume_points, float * rho, 
+			int r_points, double bin_width, double theta_beam, double phi_beam, double omega_beam);
+
+#endif
Index: sansmodels/src/sans/models/prototypes/src/simcylinder.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/simcylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/simcylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,334 @@
+#include "simcylinder.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <memory.h>
+#include "modelCalculations.h"
+#include "libCylinder.h"
+
+double test_analytical_2D(SimCylinderParameters *pars, double q, double phi) {
+	double cyl_x, cyl_y, cyl_z;
+	double q_x, q_y, q_z, lenq;
+	double theta, alpha, f, vol, sin_val, cos_val;
+	double answer;
+        
+    // Cylinder orientation
+    cyl_x = sin(pars->theta) * cos(pars->phi);
+    cyl_y = sin(pars->theta) * sin(pars->phi);
+    cyl_z = cos(pars->theta);
+     
+    // q orientation
+    
+    q_x = cos(phi);
+    q_y = sin(phi);
+    q_z = 0;
+    
+    // Length of q vector
+    //lenq = sqrt(q_x*q_x + q_y*q_y + q_z*q_z);
+    
+    // Normalize unit vector q
+    //q_x = q_x/lenq;
+    //q_y = q_y/lenq;
+    //q_z = q_z/lenq;
+    
+    // Compute the angle btw vector q and the
+    // axis of the cylinder
+    cos_val = cyl_x*q_x + cyl_y*q_y + cyl_z*q_z;
+    
+    // The following test should always pass
+    if (fabs(cos_val)>1.0) {
+    	printf("cyl_ana_2D: Unexpected error: cos(alpha)>1\n");
+     	return 0;
+    }
+    
+	alpha = acos( cos_val );
+	
+	// Call the IGOR library function to get the kernel
+	answer = acos(-1.0)/2.0 * CylKernel(q, pars->radius, pars->length/2.0, alpha)/sin(alpha);
+	
+	//normalize by cylinder volume
+	//NOTE that for this (Fournet) definition of the integral, one must MULTIPLY by Vcyl
+    vol = acos(-1.0) * pars->radius * pars->radius * pars->length;
+	answer *= vol;
+	
+	//Scale
+	answer *= pars->scale;
+	
+	//convert to [cm-1]
+	//answer *= 1.0e8;
+	
+	return answer;
+}
+
+
+/// 1D scattering function
+double simcylinder_analytical_1D(SimCylinderParameters *pars, double q) {
+	/***
+	 * Things to keep here:
+	 * 	- volume calc
+	 *  - point generation
+	 *  
+	 */
+	
+	// Check if Rho array is available
+	int volume_points;
+	int r_points;
+	int ptsGenerated;
+	
+	double r_step;
+	double vol;
+	double bin_width;
+	double twopiq;
+	double tmp;
+		
+	//printf("returning %g %g\n",test_analytical_2D(pars, q, 0.0), pars->length);
+	return test_analytical_2D(pars, q, 0.0);
+	
+	
+	// These should be parameters
+	vol = 1.0*acos(-1.0)*pars->radius*pars->radius*pars->length;
+	
+	
+	r_points      = pars->calcPars.r_points;
+	volume_points = pars->calcPars.volume_points;
+	
+	if(pars->calcPars.isPointMemAllocated==0) {
+		
+		// Call modelCalc function here to init_volume
+		
+		//twopiq        = (2*acos(-1.0)*pars->qmax);
+		//tmp           = twopiq*twopiq*twopiq*vol;
+		//volume_points = (int) floor(tmp);
+		//r_points = (int) floor(10.0*pow(tmp,0.3333));
+		//r_points      = (int) floor(pow(tmp,0.3333));
+		//printf("v, r = %d, %d\n",volume_points, r_points); 
+		
+		// TEST
+		volume_points = 1000;
+		r_points = 1000;
+		
+		pars->calcPars.volume_points = volume_points;
+		pars->calcPars.r_points      = r_points;
+		
+		// Memory allocation
+		pars->calcPars.points = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points==NULL) {
+			printf("Problem allocating memory for 1D volume points\n");
+			return -1.0;
+		}
+		pars->calcPars.isPointMemAllocated=1;
+	}
+
+    r_step = sqrt(pars->radius*pars->radius*4.0+pars->length*pars->length)/(double)r_points;
+	
+	//printf("step=%g  r=%g  l=%g\n", r_step, pars->radius, pars->length);
+	if(pars->calcPars.isRhoAvailable==0) {
+		// Generate random points accross the volume
+		ptsGenerated = simcylinder_generatePoints(pars->calcPars.points, volume_points, pars->radius, pars->length);
+		
+		// Consistency check
+		if(ptsGenerated <= 0) {
+			// Set error code here
+			return 0;
+		}
+		
+		// Allocate memory
+		
+		pars->calcPars.rho = (double*) malloc(r_points*sizeof(double));
+		if(pars->calcPars.rho==NULL){
+			printf("Problem allocating memory for 1D correlation points\n");
+			return 0;
+		}
+		
+				
+		if(modelcalculations_calculatePairCorrelation_1D(pars->calcPars.points, volume_points, pars->calcPars.rho, r_points, r_step)<=0){
+			printf("Error occured!\n");
+			return 0;
+		};
+		pars->calcPars.isRhoAvailable=1;
+	} 
+	
+	// Calculate I(q,phi) and return that value
+	
+	return acos(-1.0)/2.0*modelcalculations_calculateIq_1D(pars->calcPars.rho, r_points, r_step, q) * vol;
+
+}
+
+/// 1D scattering function
+double simcylinder_analytical_2D(SimCylinderParameters *pars, double q, double phi) {
+	// Check if Rho array is available
+	int volume_points;
+	int r_points;
+	int ptsGenerated;
+	double bin_width;
+	double r_step;
+	double vol;
+	double retval;
+	
+	// These should be parameters
+	//r_points      = pars->calcPars.r_points;
+	//volume_points = pars->calcPars.volume_points;
+	volume_points = 50000;
+	r_points = 100;
+	if(pars->calcPars.isPointMemAllocated_2D==0) {
+		//volume_points = 1000;
+		//r_points = 100;
+		pars->calcPars.points_2D = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points_2D==NULL) {
+			printf("Problem allocating memory for 2D volume points\n");
+			return -1.0;
+		}
+		pars->calcPars.isPointMemAllocated_2D=1;
+	}
+		  
+    r_step = sqrt(pars->radius*pars->radius*4.0+pars->length*pars->length)/(double)r_points;
+	if(pars->calcPars.isRhoAvailable_2D==0) {
+	    // Initialize random number generator 
+		int seed;    
+		seed = 10000;
+		srand(seed);
+		
+		// Generate random points accross the volume
+		ptsGenerated = simcylinder_generatePoints(pars->calcPars.points_2D, volume_points, pars->radius, pars->length);
+		
+		// Calculate correlation function
+		pars->calcPars.rho_2D = (float*) malloc(r_points*r_points*sizeof(float));
+		if(pars->calcPars.rho_2D==NULL){
+			printf("Problem allocating memory for 2D correlations points\n");
+			return -1.0;
+		}
+		//if(modelcalculations_calculatePairCorrelation_2D(pars->calcPars.points_2D, volume_points, pars->calcPars.rho_2D, r_points, r_step)==NULL){
+		if(modelcalculations_calculatePairCorrelation_2D_vector(pars->calcPars.points_2D, 
+			volume_points, pars->calcPars.rho_2D, r_points, r_step,
+			pars->theta, pars->phi,0.0)==0){
+			return 0;
+		};
+		pars->calcPars.isRhoAvailable_2D=1;
+	} 
+	// Calculate I(q,phi) and return that value
+	vol = acos(-1.0)*pars->radius*pars->radius*pars->length;
+	
+	//printf("in ana_2D %f %f\n",q, phi);
+	retval = modelcalculations_calculateIq_2D(pars->calcPars.rho_2D, r_points, r_step, q, phi)*vol;	
+	//printf("I=%g %f %f\n",retval, q, pars->theta);
+	return acos(-1.0)/2.0*retval;
+}
+
+/// 1D scattering function
+double simcylinder_analytical_2D_3Darray(SimCylinderParameters *pars, double q, double phi) {
+	// Check if Rho array is available
+	int volume_points;
+	int r_points;
+	int ptsGenerated;
+	double bin_width;
+	double r_step;
+	double vol;
+	
+	// These should be parameters
+	volume_points = 5000;
+	r_points = 100;
+	
+	if(pars->calcPars.isPointMemAllocated_2D==0) {
+		pars->calcPars.points_2D = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points_2D==NULL) {
+			printf("Problem allocating memory for 2D volume points\n");
+			return -1.0;
+		}
+		pars->calcPars.isPointMemAllocated_2D=1;
+	}
+		  
+    r_step = sqrt(pars->radius*pars->radius*4.0+pars->length*pars->length)/(double)r_points;
+	
+	if(pars->calcPars.isRhoAvailable_2D==0) {
+	    // Initialize random number generator 
+		int seed;    
+		seed = 10000;
+		srand(seed);
+		 
+		// Generate random points accross the volume
+		ptsGenerated = simcylinder_generatePoints(pars->calcPars.points_2D, volume_points, pars->radius, pars->length);
+		
+		// Calculate correlation function
+		// TODO: Check memory leaks
+		printf("A\n");
+		pars->calcPars.rho_2D = (float*) malloc(r_points*r_points*r_points*sizeof(float));
+		if(pars->calcPars.rho_2D==NULL){
+			printf("Problem allocating memory for 2D correlations points\n");
+			return -1.0;
+		}
+		printf("B\n");
+		if(modelcalculations_calculatePairCorrelation_2D_3Darray(pars->calcPars.points_2D, 
+			volume_points, pars->calcPars.rho_2D, r_points, r_step)<0){
+			return 0;
+		};
+		printf("C\n");
+		pars->calcPars.isRhoAvailable_2D=1;
+	} 
+	// Calculate I(q,phi) and return that value
+	vol = acos(-1.0)*pars->radius*pars->radius*pars->length;
+	
+	printf("in ana_2D %f %f\n",q, phi);
+	return modelcalculations_calculateIq_2D_3Darray(pars->calcPars.rho_2D, r_points, r_step, q, phi)*vol;
+	
+	
+}
+
+
+/**
+ * Generate points randomly accross the volume
+ * @param points [SpacePoint*] Array of 3D points to be filled
+ * @param n [int] Number of points to generat
+ * @param radius [double] Radius of the sphere
+ * @return Number of points generated
+ */
+int simcylinder_generatePoints(SpacePoint * points, int n, double radius, double length) {
+	int i;
+	int testcounter;
+	double x, y, z;
+	
+	// Create points	
+	// To have a uniform density, you want to generate
+	// random points in a box and keep only those that are
+	// within the volume.
+	
+	// Initialize random number generator
+	int seed;    
+	time_t now;
+	
+	time(&now);
+	//seed = 10000;
+	
+	seed = (int)floor(fmod(now,10000));
+	//seed = 10009;
+	srand(seed);	
+	printf("Seed = %i\n", seed);
+	
+	testcounter = 0;
+		
+	memset(points,0,n*sizeof(SpacePoint));
+	for(i=0;i<n;i++) {
+		// Generate in a box centered around zero
+		x = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		y = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		z = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * length/2.0;
+		
+		// reject those that are not within the volume
+		if( sqrt(x*x+y*y) < radius && fabs(z)<length/2.0) {
+			points[i].x =  x;
+			points[i].y =  y;
+			points[i].z =  z;
+			testcounter++;
+		} else {
+			i--;
+		}
+	}
+	//printf("test counter = %d\n", testcounter);
+	
+	// Consistency check
+	if(testcounter != n) {
+		return -1;
+	}
+		
+	return testcounter;
+}
Index: sansmodels/src/sans/models/prototypes/src/simcylinder.h
===================================================================
--- sansmodels/src/sans/models/prototypes/src/simcylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/simcylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,46 @@
+#if !defined(simcylinder_h)
+#define simcylinder_h
+
+#include "modelCalculations.h"
+
+/** Structure definition for simulated cylinder parameters 
+ * [PYTHONCLASS] = SimCylinder
+ * */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+
+    /// Radius of the cylinder
+    //  [DEFAULT]=radius=20.0
+    double radius;
+
+    /// Length of the cylinder
+    //  [DEFAULT]=length=400.0
+    double length;
+
+    /// Theta angle relative to beam
+    //  [DEFAULT]=theta=0.0
+    double theta;
+
+    /// Phi angle relative to x-axis
+    //  [DEFAULT]=phi=0.0
+    double phi;
+
+    /// Maximum Q (for numerical calculations)
+    //  [DEFAULT]=qmax=0.1
+    double qmax;
+    // Variables for numerical calculations 
+    CalcParameters calcPars;
+    
+} SimCylinderParameters;
+
+/// 1D scattering function
+double simcylinder_analytical_1D(SimCylinderParameters *pars, double q);
+
+/// 2D scattering function
+double simcylinder_analytical_2D(SimCylinderParameters *pars, double q, double phi);
+
+int simcylinder_generatePoints(SpacePoint * points, int n, double radius, double length);
+
+#endif
Index: sansmodels/src/sans/models/prototypes/src/simcylinder_fast.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/simcylinder_fast.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/simcylinder_fast.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,316 @@
+#include "simcylinder_fast.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <memory.h>
+
+
+double simcylinder_fast_analytical_1D_test(SimCylinderFParameters *pars, double q) {
+	/***
+	 * Things to keep here:
+	 * 	- volume calc
+	 *  - point generation
+	 *  
+	 */
+		// Check if Rho array is available
+	int volume_points;
+	int ptsGenerated, npts;
+	double vol;
+	
+	int i, j, k, m;
+	SpacePoint *p1;
+	
+	double sum;
+	double theta_cyl, phi;
+	double phase;
+	
+	double cos_term;
+	double sin_term;
+	double qx, qy;
+	double pi_step;
+	
+	
+	volume_points = (int)floor(pars->npoints);
+	
+		
+	// Generate random points accross the volume
+	if(pars->calcPars.isPointMemAllocated_2D==0) {
+		pars->calcPars.points_2D = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points_2D==NULL) {
+			printf("Problem allocating memory for 2D volume points\n");
+			return -1.0;
+		}
+		
+		ptsGenerated = simcylinder_fast_generatePoints(pars->calcPars.points_2D, 
+			volume_points, pars->radius, pars->length, (int)floor(pars->seed));
+		
+		pars->calcPars.isPointMemAllocated_2D=1;
+	}
+		  
+	// Loop over theta_cyl
+	
+			
+	sum = 0;
+	npts = 21;
+	// Allocate temporary memory
+	p1 = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+	
+	pi_step =  acos(-1.0)/npts;
+	for(i=0; i<npts; i++) {
+		theta_cyl = pi_step * i;
+		// Rotate the points
+		
+		for(k=0; k<volume_points; k++) { 
+			p1[k] = fast_rotate(pars->calcPars.points_2D[k], theta_cyl, 0.0, 0.0);
+		}
+		
+		// Loop over phi_cyl
+		// Equivalent to looping over phi (detector)		
+		for(j=0; j<npts; j++) {
+			phi = 2*pi_step * j;
+			
+			cos_term = 0.0;
+			sin_term = 0.0;
+			
+			qx = q*cos(phi);
+			qy = q*sin(phi);
+		
+			for(m=0;m<volume_points;m++) {
+				phase = qx*p1[m].x + qy*p1[m].y;
+				cos_term += cos(phase);
+				sin_term += sin(phase);
+			}
+		
+		    sum += sin(theta_cyl)* (cos_term*cos_term + sin_term*sin_term);
+		}
+		
+	}
+	free(p1);
+	// Calculate I(q,phi) and return that value
+	vol = acos(-1.0)*pars->radius*pars->radius*pars->length;
+	sum = 1.0e8/volume_points/volume_points*vol*sum*acos(-1.0)/2;
+	return sum/npts/npts;
+}
+
+/// 1D scattering function
+double simcylinder_fast_analytical_1D(SimCylinderFParameters *pars, double q) {
+	int i, j, npts, volume_points;
+	double sum, vol, psi;
+	
+	//return simcylinder_fast_analytical_1D_test(pars, q);
+	
+	
+	sum = 0;
+	npts = 51;
+	volume_points = (int)floor(pars->npoints);
+	
+	for(i=0; i<npts; i++) {
+		psi = 2*acos(-1.0)/npts * i;
+		pars->phi = psi;
+		
+		for(j=0; j<npts; j++) {
+			pars->theta = acos(-1.0)/npts * j;
+			sum += sin(pars->theta)*simcylinder_simple_analytical_2D(pars, q, 0.0);
+		}
+	}
+	// Calculate I(q,phi) and return that value
+	vol = acos(-1.0)*pars->radius*pars->radius*pars->length;
+	sum = 1.0e8/volume_points/volume_points*vol*sum*acos(-1.0)/2;
+	return sum/npts/npts;
+}
+
+
+/// 2D scattering function
+double simcylinder_fast_analytical_2D(SimCylinderFParameters *pars, double q, double phi) {
+	int volume_points;
+	int ptsGenerated;
+	double vol;
+	
+	int i;
+	SpacePoint p1;
+	
+	double cos_term;
+	double sin_term;
+	double qx, qy, qz;
+	double phase;
+
+	volume_points = (int)floor(pars->npoints);
+	cos_term = 0.0;
+	sin_term = 0.0;
+	
+	qx = q*cos(phi);
+	qy = q*sin(phi);
+	qz = 0.0;
+		
+	// Generate random points accross the volume
+	if(pars->calcPars.isPointMemAllocated_2D==0) {
+		pars->calcPars.points_2D = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points_2D==NULL) {
+			printf("Problem allocating memory for 2D volume points\n");
+			return -1.0;
+		}
+		
+	    ptsGenerated = simcylinder_fast_generatePoints(pars->calcPars.points_2D, 
+			volume_points, pars->radius, pars->length, (int)floor(pars->seed));
+		
+		for(i=0;i<volume_points;i++) {
+			p1 = fast_rotate(pars->calcPars.points_2D[i], pars->theta, pars->phi, 0.0);
+			pars->calcPars.points_2D[i] = p1;
+		}
+				
+		pars->calcPars.isPointMemAllocated_2D=1;
+	}
+		  
+			
+	for(i=0;i<volume_points-1;i++) {
+		p1 = pars->calcPars.points_2D[i];
+		phase = qx*p1.x + qy*p1.y;
+		cos_term += cos(phase);
+		sin_term += sin(phase);
+		    
+	}
+	
+	// Calculate I(q,phi) and return that value
+	vol = acos(-1.0)*pars->radius*pars->radius*pars->length;
+	return 1.0e8/volume_points/volume_points*vol*acos(-1)/2*(cos_term*cos_term + sin_term*sin_term);	
+}
+
+double simcylinder_simple_analytical_2D(SimCylinderFParameters *pars, double q, double phi) {
+	int ptsGenerated;
+	
+	int i;
+	SpacePoint p1;
+	
+	double cos_term;
+	double sin_term;
+	double qx, qy, qz;
+	double phase;
+	int volume_points;
+	
+	cos_term = 0.0;
+	sin_term = 0.0;
+	
+	qx = q*cos(phi);
+	qy = q*sin(phi);
+	qz = 0.0;
+
+	volume_points = (int)floor(pars->npoints);
+	
+	// Generate random points accross the volume
+	if(pars->calcPars.isPointMemAllocated_2D==0) {
+		pars->calcPars.points_2D = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points_2D==NULL) {
+			printf("Problem allocating memory for 2D volume points\n");
+			return -1.0;
+		}
+		
+	    ptsGenerated = simcylinder_fast_generatePoints(pars->calcPars.points_2D, 
+			volume_points, pars->radius, pars->length, (int)floor(pars->seed));
+		
+		for(i=0;i<volume_points;i++) {
+			p1 = fast_rotate(pars->calcPars.points_2D[i], pars->theta, pars->phi, 0.0);
+			pars->calcPars.points_2D[i] = p1;
+		}
+		
+		pars->calcPars.isPointMemAllocated_2D=1;
+	}
+			
+	for(i=0;i<volume_points;i++) {
+		p1 = fast_rotate(pars->calcPars.points_2D[i], pars->theta, pars->phi, 0.0);
+		phase = qx*p1.x + qy*p1.y;
+		cos_term += cos(phase);
+		sin_term += sin(phase);
+	}
+
+    return (cos_term*cos_term + sin_term*sin_term);
+}
+
+/**
+ *  Rotation of pair correlation function
+ */
+SpacePoint fast_rotate(SpacePoint p, double theta, double phi, double omega) {
+	SpacePoint new_point;
+	double x_1, x_2;
+	double y_1, y_2;
+	double z_1, z_2;
+	
+	
+	// Omega, around z-axis (doesn't change anything for cylindrical symmetry
+	x_1 = p.x*cos(omega) - p.y*sin(omega);
+	y_1 = p.x*sin(omega) + p.y*cos(omega);
+	z_1 = p.z;	
+	
+	// Theta, around y-axis
+	x_2 = x_1*cos(theta) + z_1*sin(theta);
+	y_2 = y_1;
+	z_2 = -x_1*sin(theta) + z_1*cos(theta);
+	
+	// Phi, around z-axis
+	new_point.x = x_2*cos(phi) - y_2*sin(phi);
+	new_point.y = x_2*sin(phi) + y_2*cos(phi);
+	new_point.z = z_2;
+	
+	return new_point;
+	
+	
+}
+ 
+
+
+/**
+ * Generate points randomly accross the volume
+ * @param points [SpacePoint*] Array of 3D points to be filled
+ * @param n [int] Number of points to generat
+ * @param radius [double] Radius of the sphere
+ * @return Number of points generated
+ */
+int simcylinder_fast_generatePoints(SpacePoint * points, int n, double radius, double length, int seed) {
+	int i;
+	int testcounter;
+	double x, y, z;
+	
+	// Create points	
+	// To have a uniform density, you want to generate
+	// random points in a box and keep only those that are
+	// within the volume.
+	
+	// Initialize random number generator
+	//int seed;    
+	time_t now;
+	
+	time(&now);
+	//seed = 10000;
+	
+	//seed = (int)floor(fmod(now,10000));
+	//seed = 10009; 
+	srand(seed);	
+	//printf("Seed = %i\n", seed);
+	
+	testcounter = 0;
+		
+	memset(points,0,n*sizeof(SpacePoint));
+	for(i=0;i<n;i++) {
+		// Generate in a box centered around zero
+		x = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		y = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		z = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * length/2.0;
+		
+		// reject those that are not within the volume
+		if( sqrt(x*x+y*y) < radius && fabs(z)<length/2.0) {
+			points[i].x =  x;
+			points[i].y =  y;
+			points[i].z =  z;
+			testcounter++;
+		} else {
+			i--;
+		}
+	}
+	
+	// Consistency check
+	if(testcounter != n) {
+		return -1;
+	}
+		
+	return testcounter;
+}
Index: sansmodels/src/sans/models/prototypes/src/simcylinder_fast.h
===================================================================
--- sansmodels/src/sans/models/prototypes/src/simcylinder_fast.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/simcylinder_fast.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,53 @@
+#if !defined(simcylinderf_h)
+#define simcylinderf_h
+
+#include "modelCalculations.h"
+
+/** Structure definition for simulated cylinder parameters 
+ * [PYTHONCLASS] = SimCylinderF
+ * */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+
+    /// Radius of the cylinder
+    //  [DEFAULT]=radius=20.0
+    double radius;
+
+    /// Length of the cylinder
+    //  [DEFAULT]=length=400.0
+    double length;
+
+    /// Theta angle relative to beam
+    //  [DEFAULT]=theta=0.0
+    double theta;
+
+    /// Phi angle relative to x-axis
+    //  [DEFAULT]=phi=0.0
+    double phi;
+
+    /// Number of space points
+    //  [DEFAULT]=npoints=50000
+    double npoints;
+
+    /// Random number seed
+    //  [DEFAULT]=seed=1000.0
+    double seed;
+
+    // Variables for numerical calculations 
+    CalcParameters calcPars;
+    
+} SimCylinderFParameters;
+
+/// 1D scattering function
+double simcylinder_fast_analytical_1D(SimCylinderFParameters *pars, double q);
+
+/// 2D scattering function
+double simcylinder_fast_analytical_2D(SimCylinderFParameters *pars, double q, double phi);
+
+int simcylinder_fast_generatePoints(SpacePoint * points, int n, double radius, double length, int seed);
+SpacePoint fast_rotate(SpacePoint p, double theta, double phi, double omega);
+double simcylinder_simple_analytical_2D(SimCylinderFParameters *pars, double q, double phi);
+
+#endif
Index: sansmodels/src/sans/models/prototypes/src/smeared_cylinder.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/smeared_cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/smeared_cylinder.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,178 @@
+/**
+ * Scattering model for a cylinder
+ * @author: Mathieu Doucet / UTK
+ */
+
+#include "cylinder.h"
+#include "smeared_cylinder.h"
+#include <math.h>
+#include "libCylinder.h"
+
+
+/**
+ * Function to evaluate 1D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double smeared_cylinder_analytical_1D(SmearCylinderParameters *pars, double q) {
+	double dp[5];
+	int i_r;
+	double r_0, r, step_r, min_r;
+	int npts;
+	double weight, func, norm;
+	
+	// Fill paramater array
+	dp[0] = pars->scale;
+	dp[1] = pars->radius;
+	dp[2] = pars->length;
+	dp[3] = pars->contrast;
+	dp[4] = pars->background;
+	
+	if(pars->sigma_radius==0) {
+		return CylinderForm(dp, q);
+	}
+	
+	// Central value is the current value 
+	r_0     = pars->radius;
+	
+	npts = 100;
+	step_r     = 4.0*pars->sigma_radius/npts;
+	min_r     = r_0 - 2.0*pars->sigma_radius;
+	
+	
+	norm = 0.0;
+	func = 0.0;
+	
+	for (i_r=0; i_r<100; i_r++) {
+			r = min_r + step_r*i_r;
+			dp[1] = r;
+			
+			// Weigth for that position
+			weight = smeared_cylinder_dist( r, r_0, pars->sigma_radius );
+			norm += weight;
+			
+			// Evaluate I(q) at that r-value
+			func += weight * CylinderForm(dp, q);	
+	}
+	
+	return func/norm;
+}
+	
+	
+/**
+ * Function to evaluate 2D scattering function
+ * @param pars: parameters of the cylinder
+ * @param q: q-value
+ * @return: function value
+ */
+double smeared_cylinder_analytical_2D(SmearCylinderParameters *pars, double q, double phi) {
+	CylinderParameters cyl_pars;
+	int i_theta, i_phi, i_r;
+	int n_theta, n_phi, n_r;
+	double theta_0, phi_0, r_0;
+	int npts;
+	double weight_theta, weight_phi, weight_r;
+	double min_theta, min_phi, min_r;
+	double step_theta, step_phi, step_r;
+	double func, norm;
+	double n_width = 3.0;
+	
+	// Fill paramater struct
+	cyl_pars.scale      = pars->scale;
+	cyl_pars.radius     = pars->radius;
+	cyl_pars.length     = pars->length;
+	cyl_pars.contrast   = pars->contrast;
+	cyl_pars.background = pars->background;
+	cyl_pars.cyl_phi    = pars->cyl_phi;
+	cyl_pars.cyl_theta  = pars->cyl_theta;
+	
+	theta_0 = pars->cyl_theta;
+	phi_0   = pars->cyl_phi;
+	r_0     = pars->radius;
+	
+	npts = 25;
+	step_theta = 2.0*n_width*pars->sigma_theta/npts;
+	step_phi   = 2.0*n_width*pars->sigma_phi/npts;
+	step_r     = 2.0*n_width*pars->sigma_radius/npts;
+	
+	if (step_theta>0) {
+		n_theta = npts;
+	} else {
+		n_theta = 1;
+	}
+	
+	if (step_phi>0) {
+		n_phi = npts;
+	} else {
+		n_phi = 1;
+	}
+	
+	if (step_r>0) {
+		n_r = npts;
+	} else {
+		n_r = 1;
+	}
+	
+	
+	
+	min_theta = theta_0 - n_width*pars->sigma_theta;
+	min_phi   = phi_0 - n_width*pars->sigma_phi;
+	min_r     = r_0 - n_width*pars->sigma_radius;
+	
+	func = 0.0;
+	norm = 0.0;
+	
+	for (i_theta=0; i_theta<n_theta; i_theta++) {
+			
+		// Weight for that position
+		if(pars->sigma_theta>0) {
+			cyl_pars.cyl_theta = min_theta + step_theta*i_theta;
+			weight_theta = smeared_cylinder_dist( cyl_pars.cyl_theta, theta_0, pars->sigma_theta );
+		} else {
+			weight_theta = 1.0;
+		}
+						
+		for (i_phi=0; i_phi<n_phi; i_phi++) {
+				
+			// Weight for that position
+			if(pars->sigma_phi>0) {
+				cyl_pars.cyl_phi = min_phi + step_phi*i_phi;
+				weight_phi = smeared_cylinder_dist( cyl_pars.cyl_phi, phi_0, pars->sigma_phi );
+			} else {
+				weight_phi = 1.0;
+			}
+		
+			for (i_r=0; i_r<n_r; i_r++) {
+				
+				if(pars->sigma_radius>0) {
+					cyl_pars.radius = min_r + step_r*i_r;
+					
+					// Weight for that position
+					weight_r = smeared_cylinder_dist( cyl_pars.radius, r_0, pars->sigma_radius );
+				} else {
+					weight_r = 1.0;
+				}
+					
+				// Evaluate I(q) at that r-value
+				func += weight_theta * weight_r * weight_phi * cylinder_analytical_2D(&cyl_pars, q, phi);	
+				norm += weight_theta * weight_r * weight_phi;
+			}
+		}
+	}
+	
+
+	return func/norm;
+}
+    
+double smeared_cylinder_dist( double x, double mean, double sigma ) {
+	double vary;
+	double expo;
+		
+	//return 1.0;
+		
+	vary = x-mean;
+    expo = -vary*vary/(2.0*sigma*sigma);
+	return exp(expo);
+
+}
Index: sansmodels/src/sans/models/prototypes/src/smeared_cylinder.h
===================================================================
--- sansmodels/src/sans/models/prototypes/src/smeared_cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/smeared_cylinder.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,56 @@
+#if !defined(smeared_cylinder_h)
+#define smeared_cylinder_h
+
+/** Structure definition for cylinder parameters 
+ * [PYTHONCLASS] = SmearCylinderModel
+ * */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    /// Radius of the cylinder [A]
+    //  [DEFAULT]=radius=20.0 A
+    double radius;
+    /// Length of the cylinder [A]
+    //  [DEFAULT]=length=400.0 A
+    double length;
+    /// Contrast [A-2]
+    //  [DEFAULT]=contrast=3.0e-6 A-2
+    double contrast;
+	/// Incoherent Background (cm-1) 0.000
+	//  [DEFAULT]=background=0 cm-1
+	double background;    
+    /// Orientation of the cylinder axis w/respect incoming beam [rad]
+    //  [DEFAULT]=cyl_theta=1.0 rad
+    double cyl_theta;
+    /// Orientation of the cylinder in the plane of the detector [rad]
+    //  [DEFAULT]=cyl_phi=1.0 rad
+    
+    ///
+    //  [DEFAULT]=cyl_phi=0.0 rad
+    double cyl_phi;    
+    
+    ///
+    //  [DEFAULT]=sigma_theta=0.0 rad
+    double sigma_theta;
+    
+    ///
+    //  [DEFAULT]=sigma_phi=0.0 rad
+    double sigma_phi;
+    
+    ///
+    //  [DEFAULT]=sigma_radius=0.0 rad
+    double sigma_radius;
+    
+} SmearCylinderParameters;
+
+
+
+/// 1D scattering function
+double smeared_cylinder_analytical_1D(SmearCylinderParameters *pars, double q);
+
+/// 2D scattering function
+double smeared_cylinder_analytical_2D(SmearCylinderParameters *pars, double q, double phi);
+double smeared_cylinder_dist( double x, double mean, double sigma );
+
+#endif
Index: sansmodels/src/sans/models/prototypes/src/sphere_fast.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/sphere_fast.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/sphere_fast.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,160 @@
+#include "sphere_fast.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <memory.h>
+
+
+/// 1D scattering function
+double sphere_fast_analytical_1D(SimSphereFParameters *pars, double q) {
+	int j, npts, volume_points;
+	double sum, phi, vol;
+	
+	return simcylinder_fast_analytical_2D(pars, q, 0.0);
+	
+	//return simcylinder_fast_analytical_1D_average(pars, q);
+	
+	sum = 0;
+	npts = 21;
+	volume_points = (int)floor(pars->npoints);
+	
+	for(j=0; j<npts; j++) {
+		phi = acos(-1.0)/npts * j;
+		//sum += simcylinder_simple_analytical_2D(pars, q, phi, volume_points);
+		sum += simcylinder_fast_analytical_2D(pars, q, phi);
+	}
+
+	// Calculate I(q,phi) and return that value
+	vol = 4.0/3.0*acos(-1.0)*pars->radius*pars->radius*pars->radius;
+	sum = 1.0e8/volume_points/volume_points*vol*sum*acos(-1.0)/2;
+	return sum/npts;
+}
+
+
+/// 1D scattering function
+double sphere_fast_analytical_2D(SimSphereFParameters *pars, double q, double phi) {
+	// Check if Rho array is available
+	int volume_points;
+	int r_points;
+	int ptsGenerated;
+	double bin_width;
+	double r_step;
+	double vol;
+	double retval;
+	
+	int i,j;
+	int ix_bin, iy_bin;
+	SpacePoint p1, p2;
+	
+	clock_t start;
+	clock_t finish;
+	double cos_term;
+	double sin_term;
+	double qx, qy, qz;
+	double phase;
+	double cyl_x, cyl_y, cyl_z;
+	double q_x, q_y, q_z;
+	double cos_val, alpha;
+	
+	
+	volume_points = (int)floor(pars->npoints);
+	cos_term = 0.0;
+	sin_term = 0.0;
+	
+	qx = q*cos(phi);
+	qy = q*sin(phi);
+	qz = 0.0;
+		
+	// Generate random points accross the volume
+	if(pars->calcPars.isPointMemAllocated_2D==0) {
+		pars->calcPars.points_2D = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points_2D==NULL) {
+			printf("Problem allocating memory for 2D volume points\n");
+			return -1.0;
+		}
+		
+	    ptsGenerated = sphere_fast_generatePoints(pars->calcPars.points_2D, 
+			volume_points, pars->radius, (int)floor(pars->seed));
+		
+		pars->calcPars.isPointMemAllocated_2D=1;
+	}
+		  
+			
+	for(i=0;i<volume_points-1;i++) {
+		
+		//p1 = fast_rotate(pars->calcPars.points_2D[i], pars->theta, pars->phi, 0.0);
+		p1 = pars->calcPars.points_2D[i];
+		phase = qx*p1.x + qy*p1.y;
+		cos_term += cos(phase);
+		sin_term += sin(phase);
+		    
+	}
+	
+	// Calculate I(q,phi) and return that value
+	vol = 4.0/3.0*acos(-1.0)*pars->radius*pars->radius*pars->radius;
+	
+    return 1.0e8/volume_points/volume_points*vol*(cos_term*cos_term + sin_term*sin_term);	
+    //return 1.0e8/volume_points/volume_points*vol*(cos_term*cos_term);	
+}
+
+
+ 
+
+
+/**
+ * Generate points randomly accross the volume
+ * @param points [SpacePoint*] Array of 3D points to be filled
+ * @param n [int] Number of points to generat
+ * @param radius [double] Radius of the sphere
+ * @return Number of points generated
+ */
+int sphere_fast_generatePoints(SpacePoint * points, int n, double radius, int seed) {
+	int i;
+	int testcounter;
+	double x, y, z;
+	
+	// Create points	
+	// To have a uniform density, you want to generate
+	// random points in a box and keep only those that are
+	// within the volume.
+	
+	// Initialize random number generator
+	//int seed;    
+	time_t now;
+	
+	time(&now);
+	//seed = 10000;
+	
+	//seed = (int)floor(fmod(now,10000));
+	//seed = 10009; 
+	srand(seed);	
+	//printf("Seed = %i\n", seed);
+	
+	testcounter = 0;
+		
+	memset(points,0,n*sizeof(SpacePoint));
+	for(i=0;i<n;i++) {
+		// Generate in a box centered around zero
+		x = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		y = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		z = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		
+		// reject those that are not within the volume
+		if( sqrt(x*x+y*y+z*z) < radius ) {
+			points[i].x =  x;
+			points[i].y =  y;
+			points[i].z =  z;
+			testcounter++;
+		} else {
+			i--;
+		}
+	}
+	
+	// Consistency check
+	if(testcounter != n) {
+		return -1;
+	}
+		
+	return testcounter;
+}
Index: sansmodels/src/sans/models/prototypes/src/sphere_fast.h
===================================================================
--- sansmodels/src/sans/models/prototypes/src/sphere_fast.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/sphere_fast.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,39 @@
+#if !defined(simspheref_h)
+#define simspheref_h
+
+#include "modelCalculations.h"
+
+/** Structure definition for simulated cylinder parameters 
+ * [PYTHONCLASS] = SimSphereF
+ * */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+
+    /// Radius of the cylinder
+    //  [DEFAULT]=radius=20.0
+    double radius;
+
+    /// Number of space points
+    //  [DEFAULT]=npoints=50000
+    double npoints;
+
+    /// Random number seed
+    //  [DEFAULT]=seed=1000
+    double seed;
+
+    // Variables for numerical calculations 
+    CalcParameters calcPars;
+    
+} SimSphereFParameters;
+
+/// 1D scattering function
+double sphere_fast_analytical_1D(SimSphereFParameters *pars, double q);
+
+/// 2D scattering function
+double sphere_fast_analytical_2D(SimSphereFParameters *pars, double q, double phi);
+
+int sphere_fast_generatePoints(SpacePoint * points, int n, double radius, int seed);
+
+#endif
Index: sansmodels/src/sans/models/prototypes/src/testsphere.c
===================================================================
--- sansmodels/src/sans/models/prototypes/src/testsphere.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/testsphere.c	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,271 @@
+#include "testsphere.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <time.h>
+#include <memory.h>
+#include "modelCalculations.h"
+
+
+/// 1D scattering function
+double testsphere_analytical_1D(TestSphereParameters *pars, double q) {
+	/***
+	 * Things to keep here:
+	 * 	- volume calc
+	 *  - point generation
+	 *  
+	 */
+	
+	// Check if Rho array is available
+	int volume_points;
+	int r_points;
+	int ptsGenerated;
+	
+	double r_step;
+	double vol;
+	double bin_width;
+	double twopiq;
+	double tmp;
+		
+	// These should be parameters
+	vol = 4.0*acos(-1.0)/3.0*pars->radius*pars->radius*pars->radius;
+	
+	
+	r_points      = pars->calcPars.r_points;
+	volume_points = pars->calcPars.volume_points;
+	
+	if(pars->calcPars.isPointMemAllocated==0) {
+		
+		// Call modelCalc function here to init_volume
+		
+		twopiq        = (2*acos(-1.0)*pars->qmax);
+		tmp           = twopiq*twopiq*twopiq*vol;
+		volume_points = (int) floor(tmp);
+		//r_points = (int) floor(10.0*pow(tmp,0.3333));
+		r_points      = (int) floor(pow(tmp,0.3333));
+		printf("v, r = %d, %d\n",volume_points, r_points); 
+		
+		// TEST
+		volume_points = 1000;
+		r_points = 1000;
+		
+		pars->calcPars.volume_points = volume_points;
+		pars->calcPars.r_points      = r_points;
+		
+		// Memory allocation
+		pars->calcPars.points = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points==NULL) {
+			printf("Problem allocating memory for 1D volume points\n");
+			return -1.0;
+		}
+		pars->calcPars.isPointMemAllocated=1;
+	}
+	
+	if(pars->calcPars.isRhoAvailable==0) {
+		// Generate random points accross the volume
+		ptsGenerated = testsphere_generatePoints(pars->calcPars.points, volume_points, pars->radius);
+		
+		// Consistency check
+		if(ptsGenerated <= 0) {
+			// Set error code here
+			return 0;
+		}
+		
+		// Allocate memory
+		
+		pars->calcPars.rho = (double*) malloc(r_points*sizeof(double));
+		if(pars->calcPars.rho==NULL){
+			printf("Problem allocating memory for 1D correlation points\n");
+			return 0;
+		}
+		
+		
+		// Lump all this in modelCalc function
+		
+		bin_width = 2.0*pars->radius/r_points;
+		
+		if(modelcalculations_calculatePairCorrelation_1D(pars->calcPars.points, volume_points, pars->calcPars.rho, r_points, bin_width)<=0){
+			printf("Error occured!\n");
+			return 0;
+		};
+		pars->calcPars.isRhoAvailable=1;
+	} 
+	
+	// Calculate I(q,phi) and return that value
+	r_step = 2.0*pars->radius/((double)(r_points));
+	
+	return modelcalculations_calculateIq_1D(pars->calcPars.rho, r_points, r_step, q) * vol;
+
+}
+/// 1D scattering function
+double testsphere_analytical_2D_3Darray(TestSphereParameters *pars, double q, double phi) {
+	// Check if Rho array is available
+	int volume_points;
+	int r_points;
+	int ptsGenerated;
+	double bin_width;
+	double r_step;
+	double vol;
+	
+	// These should be parameters
+	volume_points = 1000;
+	r_points = 10;
+	
+	if(pars->calcPars.isPointMemAllocated_2D==0) {
+		pars->calcPars.points_2D = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points_2D==NULL) {
+			printf("Problem allocating memory for 2D volume points\n");
+			return -1.0;
+		}
+		pars->calcPars.isPointMemAllocated_2D=1;
+	}
+		  
+	//r_step = 2.0*pars->radius/((double)(r_points));
+	// Allow negative values....
+	r_step = 4.0*pars->radius/((double)(r_points));
+	
+	if(pars->calcPars.isRhoAvailable_2D==0) {
+	    // Initialize random number generator 
+		int seed;    
+		seed = 10000;
+		srand(seed);
+		 
+		// Generate random points accross the volume
+		ptsGenerated = testsphere_generatePoints(pars->calcPars.points_2D, volume_points, pars->radius);
+		
+		// Calculate correlation function
+		pars->calcPars.rho_2D = (float*) malloc(r_points*r_points*r_points*sizeof(float));
+		if(pars->calcPars.rho_2D==NULL){
+			printf("Problem allocating memory for 2D correlations points\n");
+			return -1.0;
+		}
+		if(modelcalculations_calculatePairCorrelation_2D_3Darray(pars->calcPars.points_2D, volume_points, pars->calcPars.rho_2D, r_points, r_step)==NULL){
+			return 0;
+		};
+		pars->calcPars.isRhoAvailable_2D=1;
+	} 
+	// Calculate I(q,phi) and return that value
+	vol = 4.0*acos(-1.0)/3.0*pars->radius*pars->radius*pars->radius;
+	
+	//printf("in ana_2D %f %f\n",q, phi);
+	return modelcalculations_calculateIq_2D_3Darray(pars->calcPars.rho_2D, r_points, r_step, q, phi)*vol;
+	
+	
+}
+
+/// 1D scattering function
+double testsphere_analytical_2D(TestSphereParameters *pars, double q, double phi) {
+	// Check if Rho array is available
+	int volume_points;
+	int r_points;
+	int ptsGenerated;
+	double bin_width;
+	double r_step;
+	double vol;
+	
+	// These should be parameters
+	//r_points      = pars->calcPars.r_points;
+	//volume_points = pars->calcPars.volume_points;
+	volume_points = 5000;
+	r_points = 100;
+	
+	if(pars->calcPars.isPointMemAllocated_2D==0) {
+		//volume_points = 2000;
+		//r_points = 100;
+		//pars->calcPars.volume_points = volume_points;
+		//pars->calcPars.r_points      = r_points;
+		pars->calcPars.points_2D = (SpacePoint*)malloc(volume_points*sizeof(SpacePoint));
+		if(pars->calcPars.points_2D==NULL) {
+			printf("Problem allocating memory for 2D volume points\n");
+			return -1.0;
+		}
+		pars->calcPars.isPointMemAllocated_2D=1;
+	}
+		  
+	r_step = 2.0*pars->radius/((double)(r_points));
+	
+	if(pars->calcPars.isRhoAvailable_2D==0) {
+	    // Initialize random number generator 
+		int seed;    
+		seed = 10000;
+		srand(seed);
+		
+		// Generate random points accross the volume
+		ptsGenerated = testsphere_generatePoints(pars->calcPars.points_2D, volume_points, pars->radius);
+		
+		// Calculate correlation function
+		pars->calcPars.rho_2D = (float*) malloc(r_points*r_points*sizeof(float));
+		if(pars->calcPars.rho_2D==NULL){
+			printf("Problem allocating memory for 2D correlations points\n");
+			return -1.0;
+		}
+		if(modelcalculations_calculatePairCorrelation_2D(pars->calcPars.points_2D, volume_points, pars->calcPars.rho_2D, r_points, r_step)==NULL){
+		//if(modelcalculations_calculatePairCorrelation_2D_vector(pars->calcPars.points_2D, volume_points, pars->calcPars.rho_2D, r_points, r_step,0.0,0.0,0.0)==NULL){
+			return 0;
+		};
+		pars->calcPars.isRhoAvailable_2D=1;
+	} 
+	// Calculate I(q,phi) and return that value
+	vol = 4.0*acos(-1.0)/3.0*pars->radius*pars->radius*pars->radius;
+	
+	//printf("in ana_2D %f %f\n",q, phi);
+	return modelcalculations_calculateIq_2D(pars->calcPars.rho_2D, r_points, r_step, q, phi)*vol;	
+	
+}
+
+/// 1D scattering function
+double testsphere_numerical_1D(TestSphereParameters *pars, int *array, double q) { return 0;}
+
+/// 2D scattering function
+double testsphere_numerical_2D(TestSphereParameters *pars, int *array, double q, double phi) {return 0;}
+
+/**
+ * Generate points randomly accross the volume
+ * @param points [SpacePoint*] Array of 3D points to be filled
+ * @param n [int] Number of points to generat
+ * @param radius [double] Radius of the sphere
+ * @return Number of points generated
+ */
+int testsphere_generatePoints(SpacePoint * points, int n, double radius) {
+	int i;
+	int testcounter;
+	double x, y, z;
+	
+	// Create points	
+	// To have a uniform density, you want to generate
+	// random points in a box and keep only those that are
+	// within the volume.
+	
+	// Initialize random number generator
+	int seed;    
+	seed = 10000;
+	srand(seed);	
+	
+	testcounter = 0;
+		
+	memset(points,0,n*sizeof(SpacePoint));
+	for(i=0;i<n;i++) {
+		// Generate in a box centered around zero
+		x = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		y = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		z = (2.0*((double)rand())/((double)(RAND_MAX)+(double)(1))-1.0) * radius;
+		
+		// reject those that are not within the volume
+		if( sqrt(x*x+y*y+z*z) <= radius ) {
+			points[i].x =  x;
+			points[i].y =  y;
+			points[i].z =  z;
+			testcounter++;
+		} else {
+			i--;
+		}
+	}
+	//printf("test counter = %d\n", testcounter);
+	
+	// Consistency check
+	if(testcounter != n) {
+		return -1;
+	}
+		
+	return testcounter;
+}
Index: sansmodels/src/sans/models/prototypes/src/testsphere.h
===================================================================
--- sansmodels/src/sans/models/prototypes/src/testsphere.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/prototypes/src/testsphere.h	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,38 @@
+#if !defined(testsphere_h)
+#define testsphere_h
+
+#include "modelCalculations.h"
+
+/** Structure definition for sphere parameters 
+ * [PYTHONCLASS] = TestSphere2
+ * */
+typedef struct {
+    /// Scale factor 
+    //  [DEFAULT]=scale=1.0
+    double scale;
+    /// Radius of the sphere
+    //  [DEFAULT]=radius=20.0
+    double radius;
+    /// Maximum Q (for numerical calculations)
+    //  [DEFAULT]=qmax=0.1
+    double qmax;
+    // Variables for numerical calculations 
+    CalcParameters calcPars;
+    
+} TestSphereParameters;
+
+/// 1D scattering function
+double testsphere_analytical_1D(TestSphereParameters *pars, double q);
+
+/// 2D scattering function
+double testsphere_analytical_2D(TestSphereParameters *pars, double q, double phi);
+
+/// 1D scattering function
+double testsphere_numerical_1D(TestSphereParameters *pars, int *array, double q);
+
+/// 2D scattering function
+double testsphere_numerical_2D(TestSphereParameters *pars, int *array, double q, double phi);
+
+int testsphere_generatePoints(SpacePoint * points, int n, double radius);
+
+#endif
Index: sansmodels/src/sans/models/pyre/CoreShellCylinderModel.odb
===================================================================
--- sansmodels/src/sans/models/pyre/CoreShellCylinderModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/CoreShellCylinderModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,15 @@
+"""
+  Facility for SANS model
+
+  WARNING: THIS FILE WAS AUTOGENERATED AT INSTALL TIME
+           DO NOT MODIFY
+
+  This code was written as part of the DANSE project
+  http://danse.us/trac/sans/
+  @copyright 2007:  Mathieu Doucet (University of Tennessee), for the DANSE project
+
+"""
+def model():
+    from ScatteringIntensityFactory import ScatteringIntensityFactory
+    from sans.models.CoreShellCylinderModel import CoreShellCylinderModel
+    return ScatteringIntensityFactory(CoreShellCylinderModel)('CoreShellCylinderModel')
Index: sansmodels/src/sans/models/pyre/CoreShellModel.odb
===================================================================
--- sansmodels/src/sans/models/pyre/CoreShellModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/CoreShellModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,15 @@
+"""
+  Facility for SANS model
+
+  WARNING: THIS FILE WAS AUTOGENERATED AT INSTALL TIME
+           DO NOT MODIFY
+
+  This code was written as part of the DANSE project
+  http://danse.us/trac/sans/
+  @copyright 2007:  Mathieu Doucet (University of Tennessee), for the DANSE project
+
+"""
+def model():
+    from ScatteringIntensityFactory import ScatteringIntensityFactory
+    from sans.models.CoreShellModel import CoreShellModel
+    return ScatteringIntensityFactory(CoreShellModel)('CoreShellModel')
Index: sansmodels/src/sans/models/pyre/CylinderModel.odb
===================================================================
--- sansmodels/src/sans/models/pyre/CylinderModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/CylinderModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,15 @@
+"""
+  Facility for SANS model
+
+  WARNING: THIS FILE WAS AUTOGENERATED AT INSTALL TIME
+           DO NOT MODIFY
+
+  This code was written as part of the DANSE project
+  http://danse.us/trac/sans/
+  @copyright 2007:  Mathieu Doucet (University of Tennessee), for the DANSE project
+
+"""
+def model():
+    from ScatteringIntensityFactory import ScatteringIntensityFactory
+    from sans.models.CylinderModel import CylinderModel
+    return ScatteringIntensityFactory(CylinderModel)('CylinderModel')
Index: sansmodels/src/sans/models/pyre/EllipsoidModel.odb
===================================================================
--- sansmodels/src/sans/models/pyre/EllipsoidModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/EllipsoidModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,15 @@
+"""
+  Facility for SANS model
+
+  WARNING: THIS FILE WAS AUTOGENERATED AT INSTALL TIME
+           DO NOT MODIFY
+
+  This code was written as part of the DANSE project
+  http://danse.us/trac/sans/
+  @copyright 2007:  Mathieu Doucet (University of Tennessee), for the DANSE project
+
+"""
+def model():
+    from ScatteringIntensityFactory import ScatteringIntensityFactory
+    from sans.models.EllipsoidModel import EllipsoidModel
+    return ScatteringIntensityFactory(EllipsoidModel)('EllipsoidModel')
Index: sansmodels/src/sans/models/pyre/ScatteringIntensityFactory.py
===================================================================
--- sansmodels/src/sans/models/pyre/ScatteringIntensityFactory.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/ScatteringIntensityFactory.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+""" Pyre Component Model factory
+    Factory class that creates pyre component models
+
+    @copyright: Mathieu Doucet (University of Tennessee), for the DANSE project
+"""
+#pylint: disable-msg=R0904
+#pylint: disable-msg=R0912
+#pylint: disable-msg=R0201
+#pylint: disable-msg=W0142
+
+from pyre.components.Component import Component
+
+def ScatteringIntensityFactory( ModelClass ):
+    """ Returns the model if found 
+        @param name: class of the model [class]
+        @return: the pyre component class [class]
+    """
+
+    ## Model object to inspect to write the adapter
+    model = ModelClass()
+     
+    class ScatteringModel(Component):
+        """
+            Pyre component adapter class for the specified model
+        """
+        has_changed = False
+
+        class Inventory(Component.Inventory):
+            """
+                Inventory class for the model adapter
+            """
+            import pyre.inventory as pyre_inventory
+            
+            # Create the inventory items from the parameters 
+            # of the model
+            for name in model.params:
+                line = '%s=pyre_inventory.float( name, default = %g )' \
+                    % (name, model.params[name])
+                exec line in locals()
+                continue
+        
+        def __init__(self, name, facility="sans"):
+            """ Initialize the component
+                @param name: name of the component object
+                @param facility: name of the facility for the object
+            """
+            Component.__init__(self, name, facility)
+            
+            ## Model to be manipulated through the adapter
+            self._engine = ModelClass()
+            return
+
+                    
+        def __call__(self, *args, **kwds):  
+            """ 
+                Evaluate the model
+            """
+            return self._engine.run(args[0])
+            
+        def _configure(self):
+            """
+                Configure the component
+            """
+            Component._configure(self)
+            
+            # Add the inventory items to the list of attributes
+            for name in model.params: 
+                line = "self.%s = self.inventory.%s" % (name, name)
+                exec line in locals()
+                continue
+            return
+                    
+            
+        def _init(self):
+            """ 
+                Initialization of the underlying model 
+            """
+            Component._init(self)
+            
+            # Set the parameters of the underlying model
+            for name in model.params: 
+                if hasattr(self, name):
+                    self.set(name, getattr(self, name))
+                else:
+                    raise ValueError, "%s not an attribute" % name
+            
+            return
+            
+        def set(self, key, value):
+            """ 
+                Set a parameter of the underlying model 
+            """
+            
+            # Check that the parameter to set is available
+            if key not in model.params: 
+                raise ValueError, "Model doesn't have param %s" % key
+            
+            # Make use of  pyre's validator to check the inputs
+            cmd = "self.inventory.%s = value" % key
+            exec cmd in locals()
+            
+            # Pass the parameter to the underlying model
+            self._engine.setParam(key, value)
+            self.has_changed = True
+            
+        def __setattr__(self, key, value):
+            self.__dict__["has_changed"] = True
+            return Component.__setattr__(self, key, value)
+        
+        def changed(self):
+            tmp = self.has_changed
+            self.has_changed = False    
+            return tmp
+        
+        def xaxis(self): 
+            return "time", "t", "s"
+        def yaxis(self): 
+            return "amplitude", "A", ""
+            
+            
+            
+    return ScatteringModel
Index: sansmodels/src/sans/models/pyre/SphereModel.odb
===================================================================
--- sansmodels/src/sans/models/pyre/SphereModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/SphereModel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,15 @@
+"""
+  Facility for SANS model
+
+  WARNING: THIS FILE WAS AUTOGENERATED AT INSTALL TIME
+           DO NOT MODIFY
+
+  This code was written as part of the DANSE project
+  http://danse.us/trac/sans/
+  @copyright 2007:  Mathieu Doucet (University of Tennessee), for the DANSE project
+
+"""
+def model():
+    from ScatteringIntensityFactory import ScatteringIntensityFactory
+    from sans.models.SphereModel import SphereModel
+    return ScatteringIntensityFactory(SphereModel)('SphereModel')
Index: sansmodels/src/sans/models/pyre/cylinder.odb
===================================================================
--- sansmodels/src/sans/models/pyre/cylinder.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/cylinder.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,8 @@
+
+
+def model():
+    from ScatteringIntensityFactory import ScatteringIntensityFactory
+    from sans.models.CylinderModel import CylinderModel
+    
+    # Invoke ModelFactory to get a model object
+    return ScatteringIntensityFactory( CylinderModel )('cylinder')
Index: sansmodels/src/sans/models/pyre/pyre_plottable.py
===================================================================
--- sansmodels/src/sans/models/pyre/pyre_plottable.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/pyre_plottable.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,164 @@
+"""
+plottables.pyre is a module for registering pyre objects as plottables.
+
+Uses plottables from the reflectivity repository
+Plottables require Python 2.5
+
+"""
+from plottables import Plottable
+
+def label(model, collection):
+    """Build a label mostly unique within a collection"""
+    # Find all items in the collection of the same type
+    conflicts = []
+        
+    model_object = model.component
+    
+    for item in collection:
+        if item.component.name == model_object.name:
+            conflicts.append(item.component)
+        
+    # If no conflict, return name
+    if len(conflicts) == 0:
+        return model_object.name
+        
+    # Fill out the usual detail from the inventory
+    detail = {}
+    #for item in model_object.properties():
+    #    if not item.name.startswith("help"):
+    #        detail[item.name] = item.name
+            
+    # Check which fields differ (only to the first level!)
+    for c in conflicts:
+        
+        # Loop through the local properties and find differences
+        for item in model_object.properties():
+            if not item.name.startswith("help"):
+                
+                # Check common parameters for now
+                if hasattr(c, item.name) and hasattr(model_object, item.name):
+                    c_val = getattr(c, item.name)
+                    o_val = getattr(model_object, item.name)
+                    
+                    if not c_val == o_val:
+                        detail[item.name] = o_val
+                            
+    # Build a label out of the distinctions
+    # TODO: how do we force natural order traversal on detail keys?
+    label = model_object.name
+    for item in detail:
+        label += " %s=%s" % (item, str(detail[item]))
+
+    return label
+
+class Model(Plottable):
+    """
+    Prototype pyre model plottable.  
+    
+    Being 'model' rather than 'data' means that it will have certain style 
+    attributes (e.g., lines rather than symbols) and respond in certain ways 
+    to the callbacks (e.g., by recomputing the model when the limits change).
+    
+    We have the following attributes:
+    
+    - inventory: manage user visible state
+    - changed(): return true if a replot is required.
+    - x,y = data(): return the plottable data.
+    
+    """
+    def __init__(self, component):
+        Plottable.__init__(self)
+        self.component = component
+        self._xaxis, symbol, self._xunit = component.xaxis()
+        self._yaxis, symbol, self._yunit = component.yaxis()
+        
+        self.min = 0.1
+        self.max = 1.0
+        self.n = 20
+        
+        self.has_changed = False
+        self.dirty = True
+        
+        # Fill inventory backup
+        # Pyre Trait have no way to notify us that
+        # it has changed. Hack it for now.
+        self.value_dict = {}
+        for item in self.component.properties():
+            if not item.name.startswith("help"):
+                descr = self.component.inventory.getTraitDescriptor(item.name)                
+                self.value_dict[item.name] = descr.value
+
+    def __setattr__(self, key, value):
+        if key in ["min", "max", "n"]:
+            self.has_changed = True
+            
+        self.__dict__[key] = value
+    
+    def changed(self):
+        """
+        Return true if a replot is required.
+        
+        Queries our inventory and the inventory of our attached
+        model to see if any aspects of the model have changed, forcing a
+        replot.  Specialized plottables will be able to query the inventory
+        intelligently.  
+       
+        changed() could also be used to provide 'holographic update', where
+        the first pass does very coarse sampling, and this gets refined at
+        the next idle.  That way we can remain responsive to the mouse while
+        expensive calculations go on.
+        """
+        self.dirty = self.dirty or self.has_changed or self.component.changed()
+        return self.dirty
+    
+    def data(self):
+        """
+        Return the plottable data.  This will automatically respond to 
+        changes in inventory by recalculating.
+        
+        The plottables graph does not use this function directly, but 
+        rather calls it through render.  Later the default render for 1D 
+        theory style may want to call back to data.
+        """
+        if self.dirty:
+            import numpy as nx
+            self.x = nx.linspace(self.min, self.max, self.n)
+            self.y = []
+            import math
+            for x in self.x:
+                self.y.append(math.log(self.component(x)))
+            self.dirty = False    
+        return self.x, self.y
+
+    def update_xlim(self, lo, hi):
+        """
+        Record the change in the graph limits.  This updates the xrange
+        stored in the model plottable inventory. Later, when the application 
+        is idle, obj.changed() will note the change in inventory and ask
+        the data to recalculate.
+        """
+        self.inventory.min = lo
+        self.inventory.max = hi
+        
+    def render(self, plot, **kw):
+        """
+        Add the appropriate lines to the plot for the component.
+        
+        The plot interface implements generic styles for particular types
+        of data and formalizes the callback mechanism.  See the methods
+        available in mplplot for details.
+        """
+        Plottable.render(self, plot)
+        x, y = self.data()
+        plot.xaxis(self._xaxis, self._xunit)
+        plot.yaxis(self._yaxis, self._yunit)
+        plot.curve(x, y, **kw)
+        #plot.connect('xlim',self.update_xlim)
+
+    @classmethod
+    def labels(cls, collection):
+        """Build a label mostly unique within a collection"""
+        map = {}
+        for item in collection:
+            map[item] = label(item, collection)
+        return map
Index: sansmodels/src/sans/models/pyre/sphere.odb
===================================================================
--- sansmodels/src/sans/models/pyre/sphere.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/sphere.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,8 @@
+
+
+def model():
+    from ScatteringIntensityFactory import ScatteringIntensityFactory
+    from sans.models.SphereModel import SphereModel
+    
+    # Invoke ModelFactory to get a model object
+    return ScatteringIntensityFactory( SphereModel )('sphere')
Index: sansmodels/src/sans/models/pyre/test3d.py
===================================================================
--- sansmodels/src/sans/models/pyre/test3d.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/test3d.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,154 @@
+import matplotlib.pylab as pylab
+import matplotlib.axes3d as axes3d
+from matplotlib.axes3d import Axes3D
+
+# Read this: http://www.scipy.org/Cookbook/Matplotlib/mplot3D
+
+import random
+import numpy as npy
+
+
+
+def test_scatter():
+
+    fig=pylab.figure()
+    ax = Axes3D(fig)
+    #
+    #
+    n = 100
+    for c,zl,zh in [('r',-50,-25),('b',-30,-5)]:
+        xs,ys,zs = zip(*
+                       [(random.randrange(23,32),
+                         random.randrange(100),
+                         random.randrange(zl,zh)
+                         ) for i in range(n)])
+        ax.scatter3D(xs,ys,zs, c=c)
+    #
+    ax.set_xlabel('------------ X Label --------------------')
+    ax.set_ylabel('------------ Y Label --------------------')
+    ax.set_zlabel('------------ Z Label --------------------')
+
+def get_test_data(delta=0.05):
+    from matplotlib.mlab import meshgrid, bivariate_normal
+    x = y = npy.arange(-3.0, 3.0, delta)
+    X, Y = meshgrid(x,y)
+
+    Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
+    Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
+    Z = Z2-Z1
+
+    X = X * 10
+    Y = Y * 10
+    Z = Z * 500
+    return X,Y,Z
+
+def test_wire():
+    fig=pylab.figure()
+    ax = Axes3D(fig)
+
+    X,Y,Z = get_test_data(0.05)
+    ax.plot_wireframe(X,Y,Z, rstride=10,cstride=10)
+    #
+    ax.set_xlabel('X')
+    ax.set_ylabel('Y')
+    ax.set_zlabel('Z')
+
+def test_surface():
+    fig=pylab.figure()
+    ax = Axes3D(fig)
+
+    X,Y,Z = get_test_data(0.05)
+    ax.plot_surface(X,Y,Z, rstride=10,cstride=10)
+    #
+    ax.set_xlabel('X')
+    ax.set_ylabel('Y')
+    ax.set_zlabel('Z')
+
+
+def test_contour():
+    fig=pylab.figure()
+    ax = Axes3D(fig)
+
+    X,Y,Z = get_test_data(0.05)
+    cset = ax.contour3D(X,Y,Z)
+    ax.clabel(cset, fontsize=9, inline=1)
+    #
+    ax.set_xlabel('X')
+    ax.set_ylabel('Y')
+    ax.set_zlabel('Z')
+
+def test_plot():
+    fig=pylab.figure()
+    ax = Axes3D(fig)
+    xs = npy.arange(0,4*npy.pi+0.1,0.1)
+    ys = npy.sin(xs)
+    ax.plot(xs,ys, label='zl')
+    ax.plot(xs,ys+max(xs),label='zh')
+    ax.plot(xs,ys,dir='x', label='xl')
+    ax.plot(xs,ys,dir='x', z=max(xs),label='xh')
+    ax.plot(xs,ys,dir='y', label='yl')
+    ax.plot(xs,ys,dir='y', z=max(xs), label='yh')
+    ax.set_xlabel('X')
+    ax.set_ylabel('Y')
+    ax.set_zlabel('Z')
+    ax.legend()
+
+
+def test_polys():
+    from matplotlib.collections import LineCollection, PolyCollection
+    from matplotlib.colors import colorConverter
+
+    cc = lambda arg: colorConverter.to_rgba(arg, alpha=0.6)
+
+    fig=pylab.figure()
+    ax = Axes3D(fig)
+    xs = npy.arange(0,10,0.4)
+    verts = []
+    zs = [0.0,1.0,2.0,3.0]
+    for z in zs:
+        ys = [random.random() for x in xs]
+        ys[0],ys[-1] = 0,0
+        verts.append(zip(xs,ys))
+
+    poly = PolyCollection(verts, facecolors = [cc('r'),cc('g'),cc('b'),
+                                               cc('y')])
+    #patches = art3d.Poly3DCollectionW(poly, zs=zs, dir='y')
+    #poly = PolyCollection(verts)
+    ax.add_collection(poly,zs=zs,dir='y')
+    #ax.wrapped.add_collection(poly)
+    #
+    ax.plot(xs,ys, z=z, dir='y', c='r')
+    ax.set_xlim(0,10)
+    ax.set_ylim(-1,4)
+    ax.set_zlim(0,1)
+
+def test_scatter2D():
+    xs = [random.random() for i in range(20)]
+    ys = [random.random() for x in xs]
+    fig=pylab.figure()
+    ax = Axes3D(fig)
+    ax.scatter(xs,ys)
+    ax.scatter(xs,ys, dir='y', c='r')
+    ax.scatter(xs,ys, dir='x', c='g')
+
+def test_bar2D():
+    fig=pylab.figure()
+    ax = Axes3D(fig)
+
+    for c,z in zip(['r','g','b','y'],[30,20,10,0]):
+        xs = npy.arange(20)
+        ys = [random.random() for x in xs]
+        ax.bar(xs,ys,z=z,dir='y',color=c)
+    #ax.plot(xs,ys)
+
+if __name__ == "__main__":
+    
+    #test_scatter()
+    test_wire()
+    #test_surface()
+    #test_contour()
+    #test_plot()
+    #test_polys()
+    #test_scatter2D()
+    #test_bar2D()
+    pylab.show()
Index: sansmodels/src/sans/models/pyre/test_pyre_scatt.py
===================================================================
--- sansmodels/src/sans/models/pyre/test_pyre_scatt.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/test_pyre_scatt.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+from pyre.applications.Script import Script
+import sys
+
+#pylint: disable-msg=W0212
+#pylint: disable-msg=W0613
+  
+
+from pyre.components.Component import Component
+class ToyModel(Component):
+
+    has_changed = False
+
+    class Inventory(Component.Inventory):
+        import pyre.inventory as pyre_inventory
+        
+        A = pyre_inventory.float( "A", default = 1.0 )
+        B = pyre_inventory.float( "B", default = 2.0 )
+
+    def __call__(self, *args, **kwds):  
+        """ Evaluate the model.
+            For the real component, evaluate will
+            be done through a model object.
+        """
+        return self.A*10.0*args[0] + self.B
+    
+    def _configure(self):
+        Component._configure(self)
+        self.A = self.inventory.A
+        self.B = self.inventory.B
+        
+    def set(self, key, value):
+        """ For the real component, we will push the
+            values to the underlying model object
+            using this method.
+        """
+        if hasattr(self, key.upper()):
+            setattr(self, key.upper(), value)
+        else:
+            raise ValueError, "ToyModel does not have %s" % key      
+        
+    def __setattr__(self, key, value):
+        self.__dict__["has_changed"] = True
+        return Component.__setattr__(self, key, value)
+    
+    def changed(self):
+        tmp = self.has_changed
+        self.has_changed = False    
+        return tmp
+    
+    def xaxis(self): 
+        return "time", "t", "s"
+    def yaxis(self): 
+        return "amplitude", "A", ""
+        
+    
+
+  
+class DemoApp(Script):
+    """ Demo application to demonstrate the kind of object manipulations
+        our SANS applications will need to do.
+    """
+    
+    class Inventory(Script.Inventory):  
+        import pyre.inventory  
+        ## We need a model object to manipulate
+        model = pyre.inventory.facility('model', default = 'sphere')
+
+    def demo_plotter(self,graph):
+        import wx
+        from mplplotter import Plotter
+    
+        # Make a frame to show it
+        frame = wx.Frame(None,-1,'Plottables')
+        self.plotter = Plotter(frame)
+        frame.Show()
+    
+        # render the graph to the pylab plotter
+        graph.render(self.plotter)
+        
+        def onIdle(event):
+            if graph.changed(): 
+                graph.render(self.plotter)
+                event.RequestMore()
+        frame.Bind(wx.EVT_IDLE,onIdle)
+
+
+    def main(self, *args, **kwds):
+        """
+            Main loop
+        """
+        import wx
+        app = wx.PySimpleApp()
+
+        print "Model output (%s)= %g" % (self.model.name, self.model(.1))
+        
+        # Popup graph
+        import plottables
+        import pyre_plottable
+        
+        model2 = ToyModel('toymodel','sans')
+        model2._configure()
+        model2._init()        
+        model2.set("A", 4.0)
+        
+        
+        m = pyre_plottable.Model(self.model)
+        m2 = pyre_plottable.Model(model2)
+        self.graph = plottables.Graph()
+        self.graph.title('TEST')
+        self.graph.add(m)
+        self.graph.add(m2)
+
+        self.demo_plotter(self.graph)
+        
+        import thread
+        thread.start_new(self.console,())
+               
+        app.MainLoop()
+        print "Bye"
+        return
+  
+    def newModelTest(self):
+        """ 
+            Create a cylinder model directly
+        """
+        ComponentClass = ScatteringIntensityFactory(CylinderModel)
+        self.model._fini()
+        self.model = ComponentClass('cylinder')
+        self.model._configure()
+        self.model._init()        
+        
+    def newModel(self, model_name):
+        """
+            Instantiate a new model
+            @param model_name: name of new model [string]
+        """
+        import pyre.inventory
+        
+        fac = pyre.inventory.facility('model', default = model_name)
+        new_model, locator = fac._getDefaultValue(self.inventory)
+        new_model._configure()
+        new_model._init()
+        
+        self.model._fini()    
+        self.model = new_model
+  
+    def _configure(self):
+        """
+            Get the inventory items
+        """
+        Script._configure(self)
+        self.model = self.inventory.model
+        return
+  
+    def console(self):
+        # Read user commands
+        continue_flag = True
+        while(continue_flag):
+            command = raw_input(">").lower()
+            
+            # Quit the application ######################################
+            if command.count('q')>0:
+                continue_flag = False
+                
+            # Get the value of a parameter ##############################
+            elif command.count('get')>0:
+                toks = command.split()
+                if len(toks)==2:
+                    try: 
+                        # Get the attribute with the given name
+                        print "%s = %s" % (toks[1], getattr(self.model, toks[1]))
+                    except:
+                        print sys.exc_value
+                else:
+                    print "Usage: Get <name of parameter>"
+
+            # Set the value of a parameter ##############################
+            elif command.count('set')>0:
+                toks = command.split()
+                if len(toks)==3:
+                    try:
+                        self.model.set(toks[1].lstrip().rstrip(), float(toks[2]))
+                        print "Model output: ", self.model(1.0)                     
+                    except:
+                        print sys.exc_value
+                else:
+                    print "Usage: Set <name of parameter> <value>"
+                
+            # Set the active model ######################################
+            elif command.count('model')>0:
+                toks = command.split()
+                if len(toks)==2:
+                    try:
+                        new_name = toks[1].lstrip().rstrip()
+                        self.newModel(new_name)
+                        print "Model output (%s) = %g" % (new_name, self.model(1.0))
+                    except:
+                        print sys.exc_value
+                else:
+                    print "Usage: model <name of the model>"
+
+            # Help ######################################################
+            else:
+                print "Available commands:"
+                print "   get <parameter>"
+                print "   set <parameter> <value>"
+                print "   model <model name>"
+                print "   q"
+        
+            if self.graph.changed(): 
+                self.graph.render(self.plotter)
+        
+  
+# main
+if __name__ == '__main__':
+    app = DemoApp('sans')
+    app.run()
+    
+  
+# version
+__id__ = "$Id$"
+ 
+# End of file
Index: sansmodels/src/sans/models/pyre/toymodel.odb
===================================================================
--- sansmodels/src/sans/models/pyre/toymodel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/toymodel.odb	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,7 @@
+
+
+def model():
+    from test_pyre_scatt import ToyModel
+    
+    # Invoke ModelFactory to get a model object
+    return ToyModel('toymodel', facility='sans')
Index: sansmodels/src/sans/models/pyre/utest_pyremodels.py
===================================================================
--- sansmodels/src/sans/models/pyre/utest_pyremodels.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/pyre/utest_pyremodels.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,68 @@
+"""
+    Unit tests for specific models
+    @author: Mathieu Doucet / UTK
+"""
+
+import unittest
+from pyre.applications.Script import Script
+from sans.models.ModelFactory import ModelFactory
+
+# Disable "missing docstring" complaint
+# pylint: disable-msg=C0111
+# Disable "too many methods" complaint 
+# pylint: disable-msg=R0904 
+# Disable "could be a function" complaint 
+# pylint: disable-msg=R0201
+
+class TestScript(Script):
+    
+    def main(self, *args, **kwds):
+        pass
+
+    def newModel(self, model_name):
+        """
+            Instantiate a new model
+            @param model_name: name of new model [string]
+        """
+        import pyre.inventory
+        
+        fac = pyre.inventory.facility('model', default = model_name)
+        new_model, locator = fac._getDefaultValue(self.inventory)
+        new_model._configure()
+        new_model._init()
+        
+        return new_model
+
+class TestPyreComponent(unittest.TestCase):
+    """ Unit tests for sphere model """
+    
+    def setUp(self):
+        self.pyre_script = TestScript('test')
+        self.pyre_script.run()
+
+    def testSphere(self):
+        sphere = self.pyre_script.newModel('sphere')
+        oracle = ModelFactory().getModel("SphereModel")
+        self.assertEqual(sphere(1.0), oracle.run(1.0))
+
+    def testCylinder(self):
+        sphere = self.pyre_script.newModel('cylinder')
+        oracle = ModelFactory().getModel("CylinderModel")
+        self.assertEqual(sphere(1.0), oracle.run(1.0))
+
+    def test2DSphere(self):
+        sphere = self.pyre_script.newModel('sphere')
+        oracle = ModelFactory().getModel("SphereModel")
+        self.assertEqual(sphere([1.0, 1.57]), oracle.run(1.0))
+
+    def testSetParam(self):
+        sphere = self.pyre_script.newModel('sphere')
+        sphere.set('radius', 10.0)
+        oracle = ModelFactory().getModel("SphereModel")
+        oracle.setParam('radius', 10.0)
+        self.assertEqual(sphere(1.0), oracle.run(1.0))
+
+ 
+
+if __name__ == '__main__':
+    unittest.main()
Index: sansmodels/src/sans/models/test/SmearList.py
===================================================================
--- sansmodels/src/sans/models/test/SmearList.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/SmearList.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,197 @@
+
+from sans.models.ModelFactory import ModelFactory
+from sans.models.BaseComponent import BaseComponent
+import math
+
+class Smear:
+    
+    def __init__(self, model, param, sigma):
+        """
+            @param model: model to smear
+            @param param: parameter to smear
+            @param sigma: std deviations for parameter
+        """
+
+        
+        ## Model to smear
+        #self.model = factory.getModel(model_name)
+        self.model = model
+        ## Standard deviation of the smearing
+        self.sigmas = sigma
+        ## Parameter to smear
+        self.params = param
+        ## Nominal value of the smeared parameter
+        self.centers = []
+        ## Error on last evaluation
+        self.error = 0.0
+        ## Error flag
+        self.doErrors = False
+        for par in self.params:
+            self.centers.append(self.model.getParam(par))
+        
+    def smearParam(self, id, x):
+        """
+            @param x: input value
+        """
+        # from random import random
+        # If we exhausted the parameter array, simply evaluate
+        # the model
+        if id < len(self.params):
+            #print "smearing", self.params[id]
+            
+            # Average over Gaussian distribution (2 sigmas)
+            value_sum = 0.0
+            gauss_sum = 0.0
+            
+            min_value = self.centers[id] - 2*self.sigmas[id]
+            max_value = self.centers[id] + 2*self.sigmas[id]
+            n_pts = 25
+            step = (max_value - min_value)/(n_pts-1)
+            #print min_value, max_value, step, max_value-min_value
+            if step == 0.0:
+                return self.smearParam(id+1,x)
+            
+            # Gaussian function used to weigh points
+            #gaussian = ModelFactory().getModel("Gaussian")
+            gaussian = Gaussian()
+            gaussian.setParam('sigma', self.sigmas[id])
+            gaussian.setParam('mean', self.centers[id])
+                    
+            # Compute average
+            prev_value = None
+            error_sys = 0.0
+            for i in range(n_pts):
+                # Set the parameter value           
+                value = min_value + i*step
+                # value = random()*4.0*self.sigmas[id] + min_value
+                # print value
+                self.model.setParam(self.params[id], value)
+                gauss_value = gaussian.run(value)
+                #gauss_value = 1.0
+                #if id==0: print value, gauss_value
+                func_value, error_1 = self.smearParam(id+1, x)
+                if self.doErrors:
+                    if not prev_value == None:
+                        error_sys += (func_value-prev_value)*(func_value-prev_value)/4.0
+                    prev_value = func_value
+
+                value_sum += gauss_value * func_value
+                gauss_sum += gauss_value
+                
+            #print "Error", math.sqrt(error)
+            return value_sum/gauss_sum, math.sqrt(error_sys)
+        
+        else:
+            return self.model.run(x), 0.0
+        
+    def run(self, x):
+        """
+            @param x: input
+        """
+        
+        # Go through the list of parameters
+        n_par = len(self.params)
+        
+        # Check array lengths
+        if not len(self.centers) == n_par or\
+            not len(self.sigmas) == n_par:
+            raise ValueError, "Incompatible array lengths"
+        
+        # Smear first parameters
+        if n_par > 0:
+            value, error = self.smearParam(0, x)
+            self.error = error
+            
+            # Put back original values
+            for i in range(len(self.centers)):
+                self.model.setParam(self.params[i], self.centers[i])
+            
+            
+            return value
+        
+class Gaussian(BaseComponent):
+    """ Gaussian function """
+    
+    def __init__(self):
+        """ Initialization """
+        BaseComponent.__init__(self)
+        
+        ## Name of the model
+        self.name = "Gaussian"
+        ## Scale factor
+        self.params['scale']  = 1.0
+        ## Mean value
+        self.params['mean']   = 0.0
+        ## Standard deviation
+        self.params['sigma']  = 1.0
+        ## Internal log
+        self.log = {}
+        return
+    
+    def run(self, x=0):
+        """ Evaluate the function 
+            @param x: input q or [q,phi]
+            @return: scattering function
+        """
+        if(type(x)==type([])):
+            # vector input
+            if(len(x)==2):
+                return self.analytical_2D(x)
+            else:
+                raise ValueError, "Gaussian takes a scalar or a 2D point"
+        else:
+            return self.analytical_1D(x)
+
+    def analytical_2D(self, x):
+        """ Evaluate 2D model 
+            @param x: input [q,phi]
+            @return: scattering function
+        """
+        
+        # 2D sphere is the same as 1D sphere
+        return self.analytical_1D(x[0])
+
+    def analytical_1D(self, x):
+        """ Evaluate 1D model
+            @param x: input q
+            @return: scattering function
+        """
+        vary = x-self.params['mean']
+        expo_value = -vary*vary/(2*self.params['sigma']*self.params['sigma'])
+        value = self.params['scale'] *  math.exp(expo_value)
+        
+        return value
+        
+# main
+if __name__ == '__main__':
+    import math
+    if True:
+        cyl = ModelFactory().getModel("CylinderModel")
+        cyl.setParam('length', 2000.0)
+        cyl.setParam('cyl_phi', .45)
+        cyl.setParam('cyl_theta', 2.)
+        #app = Smear(cyl,['cyl_theta', 'cyl_phi'], [.01, .01])
+        #app = Smear(cyl,['cyl_theta', 'cyl_phi'], [2.0, 2.0])
+        #app = Smear(cyl,['cyl_theta'], [2.5])
+        app = Smear(cyl, ['cyl_phi', 'cyl_theta'], [math.pi, math.pi])
+        #app = Smear(cyl,['scale', 'scale'],[.5,1.0])
+        val_no = cyl.run([.001, 1.0])
+        print "Cylinder (no smear) f(.1):", val_no
+        val_sm =  app.run([.001, 1.0])
+        print "Cylinder (smear)    f(.1):", val_sm, app.error, app.error/val_sm
+        print "Cylinder (1D)    f(.1):", cyl.run(.001), cyl.run(.001)/val_sm
+      
+      
+    print "--------------"
+    sinsin = ModelFactory().getModel("SinSin")
+    sinsin.setParam('a', -1.7)
+    sinsin.setParam('b', 5.45)
+    #app = Smear(sinsin, ['A'], [3.1])
+    #app = Smear(sinsin, ['A', 'b'], [math.pi/2.0, .0])
+    app = Smear(sinsin, ['A'], [math.pi/2.0])
+    #app = Smear(cyl,['scale', 'scale'],[.5,1.0])
+    val_no = sinsin.run(1.0)
+    print "SinSin (no smear) :", val_no
+    val_sm =  app.run(1.0)
+    print "SinSin (smear)    : %g +- %g" % (val_sm, app.error)
+      
Index: sansmodels/src/sans/models/test/core_shell_cyl_testdata.txt
===================================================================
--- sansmodels/src/sans/models/test/core_shell_cyl_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/core_shell_cyl_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,139 @@
+pythonclass = CoreShellCylinderModel
+scale = 1
+radius = 20
+thickness = 10
+length = 400
+core_sld = 1e-6
+shell_sld = 4e-6
+solvent_sld = 1e-6
+background = 0.01
+
+DATA
+0.001	353.560132167546
+0.00105251266011778	353.353125068201
+0.00110778289970821	353.124000569665
+0.00116595552660488	352.87041875967
+0.00122718295288593	352.58979743803
+0.00129162559419317	352.279288228264
+0.00135945229002046	351.935750610798
+0.00143084074607265	351.555723763133
+0.00150597799985384	351.13539610369
+0.00158506091070502	350.67057245352
+0.00166829667557485	350.156638755392
+0.00175590337187494	349.588524324878
+0.00184811052884188	348.96066165541
+0.00194515972890305	348.2669438617
+0.00204730524062173	347.50067992682
+0.00215481468487986	346.654548021734
+0.00226796973604376	345.720547296668
+0.00238706685995004	344.689948707106
+0.00251241809064502	343.553245639307
+0.00264435184791283	342.300105348302
+0.00278321379773411	340.919322522823
+0.00292936775792964	339.398776654985
+0.00308319665136179	337.725395326242
+0.00324510350919104	335.885126033787
+0.00341551252681621	333.862919780817
+0.00359487017526494	331.642730345873
+0.00378364637094618	329.207533933654
+0.00398233570682956	326.539374789731
+0.00419145874827721	323.619443324298
+0.0044115633969232	320.42819431362
+0.00464322632617388	316.945513794367
+0.00488705449209019	313.150944275563
+0.0051436867236104	309.023978776314
+0.0054137953962797	304.544434827648
+0.00569808819387176	299.692919778901
+0.00599730996251769	294.451398290194
+0.00631224466220038	288.803871471549
+0.00664371742072679	282.737174369416
+0.00699259669556001	276.241893948161
+0.00735979654917468	269.313402842951
+0.00774627904389752	261.952994401996
+0.00815305676250721	254.169091326557
+0.00858119546119774	245.978483087029
+0.00903181686185589	237.40752600812
+0.00950610159096858	228.493214681787
+0.0100052922728602	219.284005115922
+0.010530696785364	209.840240785234
+0.0110836916864573	200.234006071171
+0.0116657258208385	190.548212990096
+0.0122783241158954	180.874724485493
+0.0129230915770094	171.311341206724
+0.0136017174926639	161.957540886819
+0.0143159798603742	152.908972976731
+0.0150677500450351	144.250886565509
+0.0158589976818897	136.05091014724
+0.0166917958369675	128.351896721801
+0.0175683264385096	121.165863476228
+0.0184908859936133	114.4703267421
+0.0194618916050726	108.208458104453
+0.0204838873041789	102.294332218686
+0.0215595507160742	96.6239575011789
+0.0226917000751195	91.0916748532794
+0.0238833016086589	85.6098975590415
+0.0251374773085249	80.1282952267494
+0.0264575131106459	74.6469601970146
+0.027846867504187	69.2177167568136
+0.0293091805927793	63.9295437880283
+0.0308482836315787	58.8787182407255
+0.0324682090651407	54.1312654799973
+0.0341732010924115	49.69226426132
+0.035967726786514	45.4991807718727
+0.0378564877984635	41.4499135076893
+0.0398444326754772	37.4589544254216
+0.0419367698261504	33.5130282576843
+0.0441389811664687	29.686116638358
+0.0464568364824087	26.0906695351733
+0.0488964085467568	22.7896027400326
+0.0514640890297529	19.7418575728741
+0.0541666052452436	16.8470027329151
+0.0570110377762212	14.0641733834676
+0.0600048390259259	11.4767624934243
+0.0631558527431166	9.2013416146032
+0.0664723345726646	7.23621720450609
+0.0699629736853145	5.47667717868581
+0.0736369155432808	3.90694235857881
+0.0775037858613269	2.64520254302911
+0.0815737158261042	1.71888244584613
+0.085857368639825	1.00616951922904
+0.0903659674578153	0.505746479270218
+0.0951113247931421	0.309316869918116
+0.100105873465356	0.308828468967212
+0.105362699174436	0.429865649540495
+0.110895574785276	0.739968811735628
+0.116718996412541	1.09493668549302
+0.122848221400442	1.46082520272542
+0.129299308296917	1.82653039088647
+0.136089158926978	2.05737723523194
+0.143235562675425	2.20629099045896
+0.150757243094979	2.15813904059397
+0.158673906961919	1.99386071154923
+0.167006295907771	1.66050707894721
+0.175776240762306	1.26841056680328
+0.185006718750238	0.836447930777851
+0.194721913691476	0.449693930627265
+0.20494727936264	0.185700967900882
+0.215709606185875	0.0512394957384413
+0.227037091419654	0.0559048063587326
+0.238959413035505	0.168095485620378
+0.251507807474183	0.311108248903106
+0.264715151485044	0.406870878371388
+0.278616048263005	0.408344224667358
+0.2932469181088	0.316510838067329
+0.308646093850034	0.178888365548314
+0.324853921273063	0.0632726105995981
+0.341912864828804	0.0166666303781699
+0.359867618889456	0.0363079940206598
+0.378765224847594	0.0772563511682604
+0.398655194364451	0.0908219781360385
+0.4195896390903	0.0626486424147243
+0.441623407196792	0.0256750929944015
+0.464814227078974	0.0110636566475161
+0.489222858603482	0.0167465728125504
+0.514913252299177	0.0199946004105147
+0.541952716907305	0.0145028620055464
+0.570412095730168	0.0104003959468734
+0.600365952240318	0.0104967016337968
+0.631892765436603	0.0102949185411522
+0.665075135458861	0.0106147900550481
Index: sansmodels/src/sans/models/test/core_shell_testdata.txt
===================================================================
--- sansmodels/src/sans/models/test/core_shell_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/core_shell_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,138 @@
+pythonclass = CoreShellModel
+scale = 1
+radius = 60
+thickness = 10
+core_sld = 1e-6
+shell_sld = 2e-6
+solvent_sld = 3e-6
+background = 0.001
+
+DATA
+0.001	381.274046971472
+0.00105251266011778	381.237899910898
+0.00110778289970821	381.19786027418
+0.00116595552660488	381.153509293754
+0.00122718295288593	381.104383244174
+0.00129162559419317	381.049968637346
+0.00135945229002046	380.989696908168
+0.00143084074607265	380.922938539463
+0.00150597799985384	380.848996566762
+0.00158506091070502	380.767099400611
+0.00166829667557485	380.676392895334
+0.00175590337187494	380.575931589119
+0.00184811052884188	380.464669030575
+0.00194515972890305	380.341447101163
+0.00204730524062173	380.204984233923
+0.00215481468487986	380.053862419402
+0.00226796973604376	379.886512882077
+0.00238706685995004	379.701200298987
+0.00251241809064502	379.49600542349
+0.00264435184791283	379.268805965744
+0.00278321379773411	379.017255571336
+0.00292936775792964	378.738760728792
+0.00308319665136179	378.430455426155
+0.00324510350919104	378.08917336769
+0.00341551252681621	377.711417552685
+0.00359487017526494	377.293327011804
+0.00378364637094618	376.830640491906
+0.00398233570682956	376.318656879007
+0.00419145874827721	375.752192152612
+0.0044115633969232	375.125532673916
+0.00464322632617388	374.432384627427
+0.00488705449209019	373.665819462742
+0.0051436867236104	372.818215222885
+0.0054137953962797	371.881193701371
+0.00569808819387176	370.845553445896
+0.00599730996251769	369.701198727091
+0.00631224466220038	368.437064721844
+0.00664371742072679	367.041039329271
+0.00699259669556001	365.499882251454
+0.00735979654917468	363.799142240043
+0.00774627904389752	361.923073744821
+0.00815305676250721	359.854554613775
+0.00858119546119774	357.575007000233
+0.00903181686185589	355.064324247026
+0.00950610159096858	352.300807257296
+0.0100052922728602	349.261114744241
+0.010530696785364	345.920232795758
+0.0110836916864573	342.251470410986
+0.0116657258208385	338.226489077737
+0.0122783241158954	333.81537607021
+0.0129230915770094	328.986772953036
+0.0136017174926639	323.708072763327
+0.0143159798603742	317.945701467637
+0.0150677500450351	311.665501484756
+0.0158589976818897	304.833237213943
+0.0166917958369675	297.415244439261
+0.0175683264385096	289.379246945833
+0.0184908859936133	280.695364337768
+0.0194618916050726	271.337334425244
+0.0204838873041789	261.283971041376
+0.0215595507160742	250.520872986184
+0.0226917000751195	239.042391027563
+0.0238833016086589	226.853846399068
+0.0251374773085249	213.973974766497
+0.0264575131106459	200.437542879074
+0.027846867504187	186.298049862679
+0.0293091805927793	171.630380485316
+0.0308482836315787	156.533223593014
+0.0324682090651407	141.131006418686
+0.0341732010924115	125.57502775677
+0.035967726786514	110.043406138698
+0.0378564877984635	94.7394031434915
+0.0398444326754772	79.8876517594616
+0.0419367698261504	65.7278357602731
+0.0441389811664687	52.5054542084486
+0.0464568364824087	40.4594948569723
+0.0488964085467568	29.8071600221693
+0.0514640890297529	20.726258352271
+0.0541666052452436	13.336494524359
+0.0570110377762212	7.68161679902745
+0.0600048390259259	3.71512224140784
+0.0631558527431166	1.29279888095064
+0.0664723345726646	0.175550519014221
+0.0699629736853145	0.0453924538681532
+0.0736369155432808	0.535923418986669
+0.0775037858613269	1.27580609656724
+0.0815737158261042	1.93998544566538
+0.085857368639825	2.29923118349953
+0.0903659674578153	2.25545560606361
+0.0951113247931421	1.85001590113763
+0.100105873465356	1.23672780785815
+0.105362699174436	0.621355016508782
+0.110895574785276	0.183149542475723
+0.116718996412541	0.00639177282429009
+0.122848221400442	0.0529088860080835
+0.129299308296917	0.193331375536727
+0.136089158926978	0.285780213218503
+0.143235562675425	0.258850751719227
+0.150757243094979	0.144682310792552
+0.158673906961919	0.0362122531752963
+0.167006295907771	0.00113663058210361
+0.175776240762306	0.0277097331697254
+0.185006718750238	0.0531103329757048
+0.194721913691476	0.0401179463806756
+0.20494727936264	0.010881272301514
+0.215709606185875	0.0011822843814875
+0.227037091419654	0.00839678281024765
+0.238959413035505	0.00914771323458895
+0.251507807474183	0.00271488007246614
+0.264715151485044	0.00119017076116013
+0.278616048263005	0.00209189028269908
+0.2932469181088	0.00118078317349688
+0.308646093850034	0.00108286779061982
+0.324853921273063	0.00100867939845478
+0.341912864828804	0.00130885256583451
+0.359867618889456	0.00111691184987341
+0.378765224847594	0.00155107403368663
+0.398655194364451	0.00179962321843482
+0.4195896390903	0.00131961072539651
+0.441623407196792	0.00251915473759882
+0.464814227078974	0.00118345764149102
+0.489222858603482	0.0026873874933769
+0.514913252299177	0.00137597045735508
+0.541952716907305	0.00210439596198283
+0.570412095730168	0.00207013597449065
+0.600365952240318	0.00107370624743048
+0.631892765436603	0.00208956797079987
+0.665075135458861	0.00152395959676715
Index: sansmodels/src/sans/models/test/cylinder_testdata.txt
===================================================================
--- sansmodels/src/sans/models/test/cylinder_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/cylinder_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,136 @@
+pythonclass = CylinderModel
+scale = 1
+radius = 20
+length = 400
+contrast = 3e-6
+background = 0.01
+
+DATA
+0.001	450.365065779858
+0.00105251266011778	450.146580159158
+0.00110778289970821	449.904721058613
+0.00116595552660488	449.637008936793
+0.00122718295288593	449.340705967062
+0.00129162559419317	449.012790215663
+0.00135945229002046	448.649927487565
+0.00143084074607265	448.248440688541
+0.00150597799985384	447.804276557381
+0.00158506091070502	447.312969632777
+0.00166829667557485	446.769603336734
+0.00175590337187494	446.16876808224
+0.00184811052884188	445.504516349513
+0.00194515972890305	444.770314725176
+0.00204730524062173	443.958992965425
+0.00215481468487986	443.06269023145
+0.00226796973604376	442.072798757619
+0.00238706685995004	440.979905355641
+0.00251241809064502	439.773731337189
+0.00264435184791283	438.443071660573
+0.00278321379773411	436.975734382002
+0.00292936775792964	435.358481827868
+0.00308319665136179	433.576975311161
+0.00324510350919104	431.615725703122
+0.00341551252681621	429.45805275105
+0.00359487017526494	427.086056715235
+0.00378364637094618	424.480606690776
+0.00398233570682956	421.621350889575
+0.00419145874827721	418.48675518485
+0.0044115633969232	415.054177358106
+0.00464322632617388	411.299985718283
+0.00488705449209019	407.199732049438
+0.0051436867236104	402.728390128864
+0.0054137953962797	397.860672252773
+0.00569808819387176	392.571437181701
+0.00599730996251769	386.836203490749
+0.00631224466220038	380.631782234131
+0.00664371742072679	373.937041785537
+0.00699259669556001	366.733815283289
+0.00735979654917468	359.007956784618
+0.00774627904389752	350.750545416413
+0.00815305676250721	341.959226825518
+0.00858119546119774	332.639667374524
+0.00903181686185589	322.807078144569
+0.00950610159096858	312.487742427288
+0.0100052922728602	301.720451940157
+0.010530696785364	290.557724098633
+0.0110836916864573	279.066637024047
+0.0116657258208385	267.329083828761
+0.0122783241158954	255.441218487576
+0.0129230915770094	243.511850295617
+0.0136017174926639	231.659553491498
+0.0143159798603742	220.008306831172
+0.0150677500450351	208.681580188645
+0.0158589976818897	197.79495631257
+0.0166917958369675	187.447625061693
+0.0175683264385096	177.713411852586
+0.0184908859936133	168.632377005123
+0.0194618916050726	160.204390996683
+0.0204838873041789	152.38635368558
+0.0215595507160742	145.094741759756
+0.0226917000751195	138.214768436832
+0.0238833016086589	131.616467303681
+0.0251374773085249	125.176404569476
+0.0264575131106459	118.801620763363
+0.027846867504187	112.450266533729
+0.0293091805927793	106.142075700885
+0.0308482836315787	99.9524453550689
+0.0324682090651407	93.9874997719109
+0.0341732010924115	88.3443041234747
+0.035967726786514	83.0688232388444
+0.0378564877984635	78.1304426729725
+0.0398444326754772	73.4304494970684
+0.0419367698261504	68.8486707842003
+0.0441389811664687	64.3094541736059
+0.0464568364824087	59.8273466085108
+0.0488964085467568	55.4936826961488
+0.0514640890297529	51.4009604588362
+0.0541666052452436	47.5581111282103
+0.0570110377762212	43.8774155688553
+0.0600048390259259	40.2624605003613
+0.0631558527431166	36.7152777493163
+0.0664723345726646	33.3300934619011
+0.0699629736853145	30.1616336003056
+0.0736369155432808	27.1450621666718
+0.0775037858613269	24.2065619286448
+0.0815737158261042	21.3959787911772
+0.085857368639825	18.7909944516912
+0.0903659674578153	16.3453512004984
+0.0951113247931421	14.0105824738489
+0.100105873465356	11.8613539021756
+0.105362699174436	9.91011586652733
+0.110895574785276	8.09924233046156
+0.116718996412541	6.48721642322365
+0.122848221400442	5.07262211522658
+0.129299308296917	3.8241007701892
+0.136089158926978	2.7902311341927
+0.143235562675425	1.92376715107624
+0.150757243094979	1.25195840473638
+0.158673906961919	0.739247462316389
+0.167006295907771	0.39034943849544
+0.175776240762306	0.166847976328158
+0.185006718750238	0.0638252857300758
+0.194721913691476	0.0371817504957719
+0.20494727936264	0.0700301931370014
+0.215709606185875	0.132358655755811
+0.227037091419654	0.193912768174811
+0.238959413035505	0.239497654621447
+0.251507807474183	0.257679805850731
+0.264715151485044	0.243166233829726
+0.278616048263005	0.200630401151877
+0.2932469181088	0.142354013759544
+0.308646093850034	0.083781005054643
+0.324853921273063	0.0388578776547404
+0.341912864828804	0.0157446742258134
+0.359867618889456	0.0142232167943077
+0.378765224847594	0.0263426031970756
+0.398655194364451	0.0404298608524238
+0.4195896390903	0.0459660214615077
+0.441623407196792	0.0387976446053645
+0.464814227078974	0.0251520976537578
+0.489222858603482	0.0133956386353553
+0.514913252299177	0.0108065514382524
+0.541952716907305	0.0152710060258391
+0.570412095730168	0.0196514304875119
+0.600365952240318	0.0182845678719795
+0.631892765436603	0.0130198222806718
+0.665075135458861	0.0101535864652661
Index: sansmodels/src/sans/models/test/ellipsoid_testdata.txt
===================================================================
--- sansmodels/src/sans/models/test/ellipsoid_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/ellipsoid_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,136 @@
+pythonclass = EllipsoidModel
+scale = 1
+radius_a = 20
+radius_b = 400
+contrast = 3e-6
+background = 0.01
+
+DATA
+0.001	11808.8528968632
+0.00105251266011778	11781.7147338649
+0.00110778289970821	11751.726514487
+0.00116595552660488	11718.5978599118
+0.00122718295288593	11682.01083219
+0.00129162559419317	11641.6177848645
+0.00135945229002046	11597.0391584392
+0.00143084074607265	11547.8612513417
+0.00150597799985384	11493.6340074247
+0.00158506091070502	11433.8688737723
+0.00166829667557485	11368.0367979944
+0.00175590337187494	11295.5664527249
+0.00184811052884188	11215.8427971077
+0.00194515972890305	11128.2061110769
+0.00204730524062173	11031.951668581
+0.00215481468487986	10926.3302508532
+0.00226796973604376	10810.5497404861
+0.00238706685995004	10683.7780812871
+0.00251241809064502	10545.1479370896
+0.00264435184791283	10393.7634337298
+0.00278321379773411	10228.7094202917
+0.00292936775792964	10049.0637353733
+0.00308319665136179	9853.91300696029
+0.00324510350919104	9642.37254402161
+0.00341551252681621	9413.61088528121
+0.00359487017526494	9166.879543996
+0.00378364637094618	8901.54841182498
+0.00398233570682956	8617.14714114096
+0.00419145874827721	8313.41259076583
+0.0044115633969232	7990.34206915047
+0.00464322632617388	7648.25161351986
+0.00488705449209019	7287.83787614165
+0.0051436867236104	6910.24132742693
+0.0054137953962797	6517.10741990347
+0.00569808819387176	6110.64109931719
+0.00599730996251769	5693.64864736002
+0.00631224466220038	5269.55939642207
+0.00664371742072679	4842.41854557534
+0.00699259669556001	4416.8413977311
+0.00735979654917468	3997.91920819247
+0.00774627904389752	3591.06797139074
+0.00815305676250721	3201.81444399825
+0.00858119546119774	2835.51908599765
+0.00903181686185589	2497.04384279986
+0.00950610159096858	2190.38388293401
+0.0100052922728602	1918.29597467291
+0.010530696785364	1681.97052793915
+0.0110836916864573	1480.80647627844
+0.0116657258208385	1312.35366329635
+0.0122783241158954	1172.4806119466
+0.0129230915770094	1055.80087419096
+0.0136017174926639	956.345199427954
+0.0143159798603742	868.401686020703
+0.0150677500450351	787.373295795928
+0.0158589976818897	710.444579006174
+0.0166917958369675	636.839691442685
+0.0175683264385096	567.524137445761
+0.0184908859936133	504.366315112346
+0.0194618916050726	449.001613576719
+0.0204838873041789	401.841966414852
+0.0215595507160742	361.713843354515
+0.0226917000751195	326.376840678275
+0.0238833016086589	293.695004896232
+0.0251374773085249	262.744038847928
+0.0264575131106459	234.039847131565
+0.027846867504187	208.639158819275
+0.0293091805927793	186.837277235271
+0.0308482836315787	167.70997478874
+0.0324682090651407	150.021720507628
+0.0341732010924115	133.469048132347
+0.035967726786514	118.660929907549
+0.0378564877984635	105.851355256071
+0.0398444326754772	94.3663066816175
+0.0419367698261504	83.6578396413674
+0.0441389811664687	74.0303846471908
+0.0464568364824087	65.6488966587392
+0.0488964085467568	58.0126389704112
+0.0514640890297529	51.0164068972012
+0.0541666052452436	44.9003014032841
+0.0570110377762212	39.3721336546038
+0.0600048390259259	34.3529627586149
+0.0631558527431166	29.958497372189
+0.0664723345726646	25.9550620560306
+0.0699629736853145	22.4261198409402
+0.0736369155432808	19.2679866604944
+0.0775037858613269	16.4682212334822
+0.0815737158261042	13.9888621639924
+0.085857368639825	11.805997960264
+0.0903659674578153	9.88077806606173
+0.0951113247931421	8.21127352905187
+0.100105873465356	6.74958811344309
+0.105362699174436	5.49602638898874
+0.110895574785276	4.42590111015658
+0.116718996412541	3.51634335501236
+0.122848221400442	2.7564115317264
+0.129299308296917	2.13145505928053
+0.136089158926978	1.62491960968472
+0.143235562675425	1.22192025374086
+0.150757243094979	0.908755828531692
+0.158673906961919	0.672118368226188
+0.167006295907771	0.499015180776053
+0.175776240762306	0.376993265291224
+0.185006718750238	0.294475487249178
+0.194721913691476	0.240999006101438
+0.20494727936264	0.206976804274189
+0.215709606185875	0.184084804995347
+0.227037091419654	0.166799187053838
+0.238959413035505	0.150583285805888
+0.251507807474183	0.133330958053468
+0.264715151485044	0.113698351753489
+0.278616048263005	0.0866638841974435
+0.2932469181088	0.0612447728443078
+0.308646093850034	0.07146333544507
+0.324853921273063	0.0642081900887752
+0.341912864828804	0.0396251834721476
+0.359867618889456	0.0210274910924806
+0.378765224847594	0.0199010559646776
+0.398655194364451	0.0279754666458064
+0.4195896390903	0.0319472487548983
+0.441623407196792	0.0268350754071031
+0.464814227078974	0.0181555013515734
+0.489222858603482	0.0134732506169512
+0.514913252299177	0.0143447123658664
+0.541952716907305	0.0163992637354073
+0.570412095730168	0.0156477366167964
+0.600365952240318	0.012898427036931
+0.631892765436603	0.0114950061180364
+0.665075135458861	0.0126814563174163
Index: sansmodels/src/sans/models/test/elliptical_cylinder_testdata.txt
===================================================================
--- sansmodels/src/sans/models/test/elliptical_cylinder_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/elliptical_cylinder_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,51 @@
+pythonclass = EllipticalCylinderModel
+scale = 1
+r_minor = 20
+r_ratio = 1.5
+length = 400
+contrast = 3e-6
+background = 0.00
+
+DATA
+0.001	675.50440232505
+0.00113999241252094	674.585621554061
+0.00129958270060531	673.394210762832
+0.00148151441813353	671.850289410856
+0.00168891519571259	669.851269662357
+0.00192535050850367	667.265881328373
+0.00219488497113752	663.926958947588
+0.00250215221345301	659.622979404263
+0.00285243453830891	654.088557911805
+0.00325175373088482	646.99452081442
+0.00370697458059535	637.938887875081
+0.00422592289528669	626.441265201386
+0.00481752003652534	611.944943469895
+0.00549193628880649	593.833534033594
+0.0062607656992878	571.472138550979
+0.00713722539375944	544.286072836033
+0.00813638279533754	511.890903244197
+0.0092754146520507	474.281385973753
+0.0105739023263233	432.06585649542
+0.0120541684227461	386.686741391944
+0.0137416605411801	340.493801750454
+0.0156653887523837	296.461181585324
+0.0178584243169082	257.357995056469
+0.0203584682208548	224.472404683279
+0.0232084993023232	196.656975592684
+0.0264575131106459	171.051533112635
+0.0301613642003096	145.892282588722
+0.0343837263396336	122.492775456219
+0.0391971871413787	102.387787023423
+0.044684495933335	83.9644239362009
+0.0509399863213247	66.6884221063028
+0.0580711979002305	51.7673903991309
+0.0662007249922647	38.2008300068913
+0.0754683241945671	26.8220307031804
+0.0860333169674769	17.3310728335251
+0.0980773285669326	10.0671608021353
+0.111807410406626	5.06032753961084
+0.127459599527169	2.09807907940937
+0.14530297636393	0.74349155534215
+0.165644290571589	0.362431309457757
+0.188833234429025	0.310054845776307
+
Index: sansmodels/src/sans/models/test/load_test_model.xml
===================================================================
--- sansmodels/src/sans/models/test/load_test_model.xml	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/load_test_model.xml	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,10 @@
+<Component class='AddComponent' name='AddComponent'>
+  <operateOn>
+    <Component class='SphereModel' name='SphereModel'>
+</Component>
+  </operateOn>
+  <toAdd>
+    <Component class='SphereModel' name='SphereModel'>
+</Component>
+  </toAdd>
+</Component>
Index: sansmodels/src/sans/models/test/prototypes/test_2dsim.py
===================================================================
--- sansmodels/src/sans/models/test/prototypes/test_2dsim.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/prototypes/test_2dsim.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,82 @@
+from sans_extension.prototypes.c_models import CCanvas
+from sans.models.SphereModel import SphereModel
+from sans.models.CoreShellModel import CoreShellModel
+
+import math
+
+# Sphere ###############################
+sphere = SphereModel()
+sphere.setParam('radius',40.0)
+sphere.setParam('contrast',1.0)
+sphere.setParam('scale',1.0)
+sphere.setParam('background',0.0)
+
+can = CCanvas()
+
+print "ID", can.add()
+can.setParam(0,1,40.0)
+ 
+
+for q in [1,0.1,0.05,0.02,0.01,0.001,0.0001]:
+    ana = sphere.run([q,1.0])
+    sim = can.evaluate([q,1.0])
+
+    print "Sphere: ANA = %g   SIM = %g   (%g)" %(ana, sim, sim/ana)
+
+
+
+
+# Core-shell ###########################
+# This is a core shell model where the weight-average
+# of the SLDs is zero. The I(q) should go to zero as q->0.
+
+import time
+t_0 = time.time()
+
+sphere = CoreShellModel()
+# Core radius
+radius = 10.0
+thickness = 5.0
+sphere.setParam('radius', radius)
+# Shell thickness
+sphere.setParam('thickness', thickness)
+sphere.setParam('core_sld', 1.0)
+
+core_vol = 4.0/3.0*math.pi*radius*radius*radius
+outer_radius = radius+thickness
+shell_vol = 4.0/3.0*math.pi*outer_radius*outer_radius*outer_radius - core_vol
+shell_sld = -1.0*core_vol/shell_vol
+print "Shell SLD", shell_sld
+
+sphere.setParam('shell_sld', shell_sld)
+sphere.setParam('solvent_sld',0.0)
+sphere.setParam('background',0.0)
+sphere.setParam('scale',1.0)
+
+can = CCanvas()
+
+print "ID", can.add()
+can.setParam(0,1, outer_radius)
+can.setParam(0,2, shell_sld)
+
+
+print "ID", can.add()
+can.setParam(1,1, radius)
+can.setParam(1,2, 1.0)
+
+print "VOLS inner = %g, outer = %g, shell = %g" % (core_vol, shell_vol+core_vol, shell_vol)
+
+f = open('core-shell.txt','w')
+f.write("<q_cs>   <ans_cs>   <sim_cs>\n")
+for q in [1,0.1,0.05,0.04,0.03,0.02,0.01,0.005,0.001,0.0001]:
+#for i in range(400):
+    #q = 0.0001+i*0.005
+    ana = sphere.run([q,1.0])
+    sim = can.evaluate([q,1.0])
+    f.write("%g  %g  %g\n" %(q, ana, sim))
+    print "Core-shell: q=%g  ANA = %g   SIM = %g   (%g)" %(q, ana, sim, sim/ana)
+
+f.close()
+
+
+print "Time: ", time.time()-t_0
Index: sansmodels/src/sans/models/test/prototypes/test_cyl_err2.py
===================================================================
--- sansmodels/src/sans/models/test/prototypes/test_cyl_err2.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/prototypes/test_cyl_err2.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,164 @@
+try:
+    from sans.models.prototypes.SimCylinderF import SimCylinderF
+except:
+    print "This test uses the prototypes module."
+from sans.models.CylinderModel import CylinderModel
+import time, math, random
+
+def reset(seed=0):
+    sim = SimCylinderF()
+    sim.setParam('radius', 20)
+    sim.setParam('length', 200)
+    sim.setParam('phi', 1)
+    sim.setParam('theta', 1)
+    sim.setParam('seed', math.fmod(time.time()*100+seed,10000))
+    return sim
+
+# number of times to repeat each point
+N_q = 50
+# Relative error on the simulation
+sig_val = 0.20
+
+
+counter = 0
+
+sim = reset()
+sph = CylinderModel()
+
+
+sph.setParam('radius', 20)
+sph.setParam('length', 200)
+sph.setParam('scale', 1)
+sph.setParam('contrast',1)
+sph.setParam('length', 200)
+sph.setParam('cyl_phi', 1)
+sph.setParam('cyl_theta', 1)
+
+vol = math.pi*20*20*200
+
+f = open('sim_err_cyl.txt', 'w')
+f.write("<q>  <npts>  <sigma>\n")
+
+
+
+
+for i_npts in range(1):
+
+    # Find which q has an error of sig_val
+    #print err    
+    q_sigma = 0
+    i_min = 0
+    i_max = 0
+    i_last_max = 0
+    val_min = 0
+    val_max = 0
+    last_error = 0
+    last_val = 0
+    error_vec = []
+    
+    found_it = False
+    i_vec = 0
+        
+    
+    t_0 = time.time()
+    npts = 50000
+    #npts = 50000*i_npts+1000
+    #npts = 1000 * math.pow(2.0,(i_npts+1))
+    
+    sim = reset()
+    #sim.setParam('npoints', npts)
+    
+    # Vectors of errors
+    err = []
+    for i in range(80):
+        q = 0.40*(i) /50
+        
+        
+        # For each Q, repeat the simulation N_q times
+        sum_q_ana = 0
+        sum_q_sim = 0
+        sum = 0
+        vec = []
+        for i_q in range(N_q):
+            counter += 1
+            sim = reset(counter)
+            #sim.setParam('npoints', npts)
+                    
+            simval = sim.run([q, 0])
+            sum_q_ana += sph.run([q, 0])
+            sum_q_sim += simval
+            
+            vec.append(simval)
+            
+        mean = sum_q_sim/N_q
+        for val in range(N_q):
+            diff = vec[val] - mean
+            sum += diff * diff
+        sum /= (N_q-1)
+
+        ana_value = sum_q_ana/N_q
+        sim_value = sum_q_sim/N_q
+        
+         
+        try:    
+            err_i = [q, (sim_value-ana_value)/ana_value, ana_value, math.sqrt(sum)/ana_value]
+        except:
+            err_i = [q,0,0,0]
+        
+        err.append(err_i)
+                    
+        print err_i
+    
+        
+        # update extrema
+        if val_min > err_i[2]:
+            val_min = err_i[2]
+            
+        if val_max < err_i[2]:
+            val_max = err_i[2]
+        
+        # Find a minimum, where the error changes sign
+        if last_val==val_min and err_i[2] > val_min:
+            
+            
+            #print "  min", err_i[0]
+            if i_min > 0:
+                i_plateau = i_last_max
+      
+                # check error for plateau
+                #if math.fabs(err[i_plateau][1])>sig_val:
+                if math.fabs(err[i_max][3])>sig_val:
+                    error_vec.append(err[i_plateau])
+                    t_f = time.time()
+                    pred = sph.run([err[i_plateau][0],0])
+                    print '[',t_f-t_0,']',math.pow((vol/npts),-.6666), 'n=',npts, 'q=',err[i_plateau][0], \
+                        'err=',math.fabs(err[i_plateau][1]), i_min, 'sig=',err[i_plateau][3]
+                        
+                    f.write("%10g %10g %10g\n" % (err[i_plateau][0], npts, err[i_plateau][3]))
+                    found_it = True
+                    break
+                
+            i_min = i_vec
+            # Get ready for next maximum
+            val_max = val_min
+            
+        # check if we are at max
+        elif last_val==val_max and err_i[2]<val_max:
+            #print "   max", err[i_vec-1][0], err[i_vec-1][1]
+            i_last_max = i_max
+            i_max = i_vec-1
+            # get ready for next minimum
+            val_min = val_max
+
+        last_error = err_i[1]
+        last_val = err_i[2]
+        i_vec += 1
+
+        
+    if found_it == False:
+        print "Could not complete", npts
+    #print "Npts = %g; %s" %(npts, str(len(error_vec)))
+        
+
+f.close()
+
Index: sansmodels/src/sans/models/test/prototypes/test_cyl_iq.py
===================================================================
--- sansmodels/src/sans/models/test/prototypes/test_cyl_iq.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/prototypes/test_cyl_iq.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,77 @@
+try:
+    from sans.models.prototypes.TestSphere2 import TestSphere2
+    from sans.models.prototypes.SimCylinderF import SimCylinderF
+    from sans.models.prototypes.SimCylinder import SimCylinder
+except:
+    print "This test uses the prototypes module."
+from sans.models.SphereModel import SphereModel
+from sans.models.CylinderModel import CylinderModel
+import math, time
+
+
+def test_lores():
+
+    lores = open('lores.iq', 'r')
+    f = open('lores_check.txt', 'w')
+    content = lores.read()
+    lines = content.split('\n')
+    inum = 0
+    norma = 0.
+    for line in lines:
+        toks = line.split()
+        if len(toks)==2:
+            inum += 1
+            q = float(toks[0])
+            ana = sph.run(q)
+            if inum==1:
+                continue
+            elif inum==2:
+                norma = ana/float(toks[1])
+            lor = float(toks[1])*norma
+            f.write("%g %g %g\n" % (q, ana, lor))
+    f.close()
+            
+
+#sim = TestSphere2()
+#sph = SphereModel()
+sim = SimCylinderF()
+sph = CylinderModel()
+
+sim.setParam('radius', 20)
+sim.setParam('length', 200)
+sim.setParam('phi', 1)
+sim.setParam('theta', 1)
+sim.setParam('npoints',2500)
+#print sph.params
+
+sph.setParam('radius', 20)
+sph.setParam('scale', 1)
+sph.setParam('contrast',1)
+sph.setParam('length', 200)
+sph.setParam('cyl_phi', 1)
+sph.setParam('cyl_theta', 1)
+
+#print "ANA", sph.run(0.1)
+#print "SIM", sim.run(0.1)
+
+f = open('sim_iq.txt', 'w')
+f.write("<q>  <ana>  <sim>\n")
+
+t_0 = time.time()
+
+for i in range(60):
+    q = 0.01 * (i+1) /3.0
+    #ana_value = sph.run([q,0])
+    #sim_value = sim.run([q,0])
+    ana_value = sph.run(q)
+    sim_value = sim.run(q)
+    ratio = 0
+    if sim_value>0:
+        ratio = sim_value/ana_value
+    print q, ana_value, sim_value, ratio
+    f.write("%10g %10g %10g\n" % (q, ana_value, sim_value))
+
+t_f = time.time()
+print "Time = ", t_f-t_0
+f.close()
+
Index: sansmodels/src/sans/models/test/prototypes/test_simulation.py
===================================================================
--- sansmodels/src/sans/models/test/prototypes/test_simulation.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/prototypes/test_simulation.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,275 @@
+try:
+    from sans.models.prototypes.SimCylinder import SimCylinder
+except:
+    print "This test uses the prototypes module."
+    
+from sans.models.CylinderModel import CylinderModel
+
+import os, sys
+
+def simulate(filename="simul.txt", npts=500):
+    output_file = open(filename, 'w')
+    #output_file = open("simul_phi=0_theta=1_57_l=400_r=20_9.txt", 'w')
+    
+    sim = SimCylinder()
+    sim.setParam('scale', 1.0)
+    #sim.setParam('length', 100.0)
+    #sim.setParam('radius', 40.0)
+    sim.setParam('length', 400.0)
+    sim.setParam('radius', 20.0)
+    sim.setParam('theta', 1.57)
+    sim.setParam('phi', 0.0)
+    sim.setParam('qmax', 0.2)
+    
+    output_file.write("<q_value>  <ana_value>  <sim_value>\n")
+    norma = 0
+    for i in range(npts):
+        q = 1.0/npts*(i+1)
+        
+        cyl_val = sim.run(q)
+        sim_val = sim.run([q, 0.0])
+        if i==0:
+            norma = cyl_val/sim_val
+        
+        #print q, cyl_val, sim_val, sim_val/cyl_val
+        output_file.write("%10g %10g %10g\n" % (q, cyl_val, sim_val*norma))
+        
+    output_file.close()
+     
+def create_model():
+    sim = SimCylinder()
+    sim.setParam('scale', 1.0)
+    sim.setParam('length', 100.0)
+    sim.setParam('radius', 40.0)
+    # simul and simul_2
+    #sim.setParam('theta', 1.57)
+    #sim.setParam('phi', 0.0)
+    sim.setParam('theta', 0.0)
+    sim.setParam('phi', 1.0)
+    sim.setParam('qmax', 0.2)
+    return sim
+    
+def comp(): 
+    """
+        Simple test that should give 1 for the ratio
+        of simulated and analytical
+    """
+    sim = create_model()
+    
+    ana_val = sim.run(.05)
+    sim_val = sim.run([0.05, 0])
+    print ana_val, sim_val, ana_val/sim_val
+    
+    
+#    ana_val = sim.run(.05)
+#    sim_val = sim.run([0.05, 1])
+#    print ana_val, sim_val, ana_val/sim_val
+    
+#    sim = create_model()
+#    sim.setParam('theta', 1.0)
+#    sim.setParam('phi', 1.0)   
+#    ana_val = sim.run(.05)
+#    sim_val = sim.run([0.05, 1])
+#    print ana_val, sim_val, ana_val/sim_val
+    
+#    sim = create_model()
+#    sim.setParam('theta', 1.0)
+#    sim.setParam('phi', 2.0)
+#    ana_val = sim.run(.05)
+#    sim_val = sim.run([0.05, 1])
+#    print ana_val, sim_val, ana_val/sim_val
+    
+    sim = create_model()
+    sim.setParam('theta', 1.0)
+    sim.setParam('phi', -2.0)
+    ana_val = sim.run(.05)
+    sim_val = sim.run([0.15, 1])
+    print ana_val, sim_val, ana_val/sim_val
+    
+def add_error_to_file(prefix = "", dir = "output"):
+    import os, math
+    
+    # Open standard deviation file
+    std_file = open("%s/std_simul.txt" % dir, 'r')
+    std_content = std_file.read()
+    std_lines = std_content.split('\n')
+    
+    std_values = {}
+    for line in std_lines:
+        if line == std_lines[0]:
+            continue
+        
+        toks = line.split()
+        if not len(toks) > 3:
+            continue
+
+        std_values[toks[0]] = float(toks[2])
+    
+    std_file.close()
+    
+    # Read the files
+    
+    file_list = os.listdir(dir)
+    
+    for file in file_list:
+        if file.count(prefix)>0:
+            print "Reading", file
+    
+            file_obj = open("%s/%s" % (dir, file), 'r')
+            
+            # open new file
+            file_new = open("%s/error-%s" % (dir, file), 'w')
+            file_content = file_obj.read()
+            lines = file_content.split('\n')
+            for line in lines:
+                if line == lines[0]:
+                    file_new.write(line+'\n')
+                    continue
+                    
+                toks = line.split()
+                
+                if not len(toks) == 3:
+                    file_new.write(line+'\n')
+                    continue
+            
+                error = 0
+                if toks[0].lower() in std_values:
+                    error = float(toks[2]) * std_values[toks[0].lower()]
+                    
+                file_new.write(line+" %10g\n" % error)
+                
+            file_obj.close()
+            file_new.close()
+    
+    
+    
+def std_estimate(prefix = "", dir="output"):
+    import os, math
+    
+    file_list = os.listdir(dir)
+    output_file = open("%s/std_simul.txt" % dir, 'w')
+    output_file.write("<q_value>   <std>  <frac>\n")
+    values = {}
+    
+    for file in file_list:
+        if file.count(prefix)>0:
+            print "Reading", file
+            file_obj = open("%s/%s" % (dir, file), 'r')
+            file_content = file_obj.read()
+            lines = file_content.split('\n')
+            for line in lines:
+                if line == lines[0]:
+                    continue
+                toks = line.split()
+                
+                if not len(toks) == 3:
+                    continue
+                
+                if toks[0] not in values:
+                    values[toks[0]] = []
+                    
+                values[toks[0]].append(float(toks[2]))
+                                       
+    q_list = values.keys()
+    q_list.sort()                 
+    for q in q_list:
+        num = len(values[q])
+        sum = 0
+        mean = 0
+        for val in range(num):
+            mean += values[q][val] 
+        mean /= num
+            
+        for val in range(num):
+            diff = values[q][val] - mean
+            sum += diff * diff
+        sum /= (num-1)
+        #print q, math.sqrt(sum)
+    
+        output_file.write("%10s %10g %10g %10g\n" % (q, math.sqrt(sum), math.sqrt(sum)/mean, math.sqrt(sum)/mean*100.0))
+        
+    output_file.close()
+    
+    
+    # sqrt( 1/(N-1) sum[ (x-mean)**2 ] )
+    
+def test_volume_pts():
+    import random, math, time
+    
+    print "Generating points"
+    pt_list = []
+    for i in range(50000):
+        x = random.random()
+        y = random.random()
+        z = random.random()
+        pt_list.append([x, y, z])
+        
+    # Compute list of closest distance for each point
+    print "Computing distances"
+    closest_list = []
+    t_init = time.time()
+    
+    for i in range(len(pt_list)):
+        if math.fmod(i,100)==0:
+            print i, time.time()-t_init
+            t_init = time.time()
+        pt = pt_list[i]
+        
+        min_dist = -1.0
+        for j in range(i+1, len(pt_list)):
+            pt_2 = pt_list[j]
+            #print pt_2
+            dx = pt[0]-pt_2[0]
+            dy = pt[1]-pt_2[1]
+            dz = pt[2]-pt_2[2]
+            dist = math.sqrt( dx*dx + dy*dy + dz*dz ) 
+            if min_dist<0 or dist < min_dist:
+                min_dist = dist
+             
+        closest_list.append(min_dist)        
+        
+    # Analyze list of distances
+    print "Analyzing"
+    sum = 0
+    for d in closest_list:
+        sum += d
+    average = sum/len(closest_list)
+    
+    sum = 0
+    for d in closest_list:
+        diff = d - average
+        sum += diff*diff
+    sum /= len(closest_list)
+    
+    print "%5g +/- %5g)" % (average, math.sqrt(sum))
+    
+                              
+            
+    
+if __name__ == '__main__':
+    #test_volume_pts()
+    #simulate("simul_phi=0_theta=1_57_l=400_r=20_test.txt")
+    #std_estimate("simul_phi=0_theta=1_57_l=400_r=20")
+    #add_error_to_file("l=400_r=20.txt")
+    
+    option = "error"
+    
+    #if len(sys.argv)>1:
+    if len(option)>0:
+        if option=="gen":
+            ifile = 0
+            for i in range(100):
+                ifile += 1
+                #print "simul_qmax=1_phi=0_theta=1_57_l=400_r=20_%g.txt" % ifile
+                #simulate("simul_phi=0_theta=1_57_l=400_r=20_%g.txt" % ifile)
+                print "simul_qmax=1_phi=0_theta=1_57_l=400_r=20_%g.txt" % ifile
+                simulate("output/pt5000/simul_phi=0_theta=1_57_l=400_r=20_%g.txt" % ifile)
+        elif option=="comp":
+            comp()
+        elif option=="std":
+            #std_estimate("simul_phi=0_theta=1_57_l=400_r=20")
+            std_estimate("simul_phi=0_theta=1_57_l=400_r=20", "output/pt5000")
+        elif option=="error":
+            #add_error_to_file("l=400_r=20.txt")
+            add_error_to_file("l=400_r=20_1.txt", "output/pt5000")
+            
Index: sansmodels/src/sans/models/test/prototypes/test_sphere_err2.py
===================================================================
--- sansmodels/src/sans/models/test/prototypes/test_sphere_err2.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/prototypes/test_sphere_err2.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,156 @@
+try:
+    from sans.models.prototypes.SimSphereF import SimSphereF
+except:
+    print "This test uses the prototypes module."
+from sans.models.SphereModel import SphereModel
+import time, math, random
+
+def reset(seed=0):
+    sim = SimSphereF()
+    sim.setParam('radius', 60)
+    sim.setParam('seed', math.fmod(time.time()*100+seed,10000))
+    return sim
+
+# number of times to repeat each point
+N_q = 50
+# Relative error on the simulation
+sig_val = 0.10
+
+
+counter = 0
+
+sim = reset()
+sph = SphereModel()
+
+
+sph.setParam('radius', 60)
+sph.setParam('scale', 1)
+
+vol = 4/3*math.pi*60*60*60
+
+f = open('sim_err_4.txt', 'w')
+f.write("<q>  <npts>  <sigma>\n")
+
+
+
+
+for i_npts in range(15):
+
+    # Find which q has an error of sig_val
+    #print err    
+    q_sigma = 0
+    i_min = 0
+    i_max = 0
+    i_last_max = 0
+    val_min = 0
+    val_max = 0
+    last_error = 0
+    last_val = 0
+    error_vec = []
+    
+    found_it = False
+    i_vec = 0
+        
+    
+    t_0 = time.time()
+    npts = 50000*i_npts+1000
+    #npts = 1000 * math.pow(2.0,(i_npts+1))
+    
+    sim = reset()
+    sim.setParam('npoints', npts)
+    
+    # Vectors of errors
+    err = []
+    for i in range(80):
+        q = 0.40*(i) /50
+        
+        
+        # For each Q, repeat the simulation N_q times
+        sum_q_ana = 0
+        sum_q_sim = 0
+        sum = 0
+        vec = []
+        for i_q in range(N_q):
+            counter += 1
+            sim = reset(counter)
+            sim.setParam('npoints', npts)
+                    
+            simval = sim.run([q, 0])
+            sum_q_ana += sph.run([q, 0])
+            sum_q_sim += simval
+            
+            vec.append(simval)
+            
+        mean = sum_q_sim/N_q
+        for val in range(N_q):
+            diff = vec[val] - mean
+            sum += diff * diff
+        sum /= (N_q-1)
+
+        ana_value = sum_q_ana/N_q
+        sim_value = sum_q_sim/N_q
+        
+        
+        
+        try:    
+            err_i = [q, (sim_value-ana_value)/ana_value, ana_value, math.sqrt(sum)/ana_value]
+        except:
+            err_i = [q,0,0,0]
+        
+        err.append(err_i)
+                    
+         
+    
+        
+        # update extrema
+        if val_min > err_i[2]:
+            val_min = err_i[2]
+            
+        if val_max < err_i[2]:
+            val_max = err_i[2]
+        
+        # Find a minimum, where the error changes sign
+        if last_val==val_min and err_i[2] > val_min:
+            
+            
+            #print "  min", err_i[0]
+            if i_min > 0:
+                i_plateau = i_last_max
+      
+                # check error for plateau
+                #if math.fabs(err[i_plateau][1])>sig_val:
+                if math.fabs(err[i_max][3])>sig_val:
+                    error_vec.append(err[i_plateau])
+                    t_f = time.time()
+                    pred = sph.run([err[i_plateau][0],0])
+                    print '[',t_f-t_0,']',math.pow((vol/npts),-.6666), 'n=',npts, 'q=',err[i_plateau][0], \
+                        'err=',math.fabs(err[i_plateau][1]), i_min, 'sig=',err[i_plateau][3]
+                        
+                    f.write("%10g %10g %10g\n" % (err[i_plateau][0], npts, err[i_plateau][3]))
+                    found_it = True
+                    break
+                
+            i_min = i_vec
+            # Get ready for next maximum
+            val_max = val_min
+            
+        # check if we are at max
+        elif last_val==val_max and err_i[2]<val_max:
+            #print "   max", err[i_vec-1][0], err[i_vec-1][1]
+            i_last_max = i_max
+            i_max = i_vec-1
+            # get ready for next minimum
+            val_min = val_max
+
+        last_error = err_i[1]
+        last_val = err_i[2]
+        i_vec += 1
+
+        
+    if found_it == False:
+        print "Could not complete", npts
+    #print "Npts = %g; %s" %(npts, str(len(error_vec)))
+        
+
+f.close()
+
Index: sansmodels/src/sans/models/test/prototypes/test_sphere_iq.py
===================================================================
--- sansmodels/src/sans/models/test/prototypes/test_sphere_iq.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/prototypes/test_sphere_iq.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,43 @@
+try:
+    from sans.models.prototypes.SimSphereF import SimSphereF
+except:
+    print "This test uses the prototypes module."
+from sans.models.SphereModel import SphereModel
+import math, time
+
+
+
+sim = SimSphereF()
+sph = SphereModel()
+
+sim.setParam('radius', 60)
+sim.setParam('npoints', 50000)
+
+sph.setParam('radius', 60)
+sph.setParam('scale', 1)
+
+
+f = open('sim_iq.txt', 'w')
+f.write("<q>  <ana>  <sim> <err>\n")
+
+t_0 = time.time()
+
+#for i in range(60):
+for i in range(100):
+    #q = 0.01 * (i+1) /3.0
+    q = 0.05 + 0.20*(i+1) /100
+    
+    ana_value = sph.run([q,0])
+    sim_value = sim.run([q,0])
+#    ana_value = sph.run(q)
+#    sim_value = sim.run(q)
+    ratio = 0
+    if sim_value>0:
+        ratio = sim_value/ana_value
+    print q, ana_value, sim_value, ratio
+    f.write("%10g %10g %10g %10g\n" % (q, ana_value, sim_value, (sim_value-ana_value)/ana_value))
+
+t_f = time.time()
+print "Time = ", t_f-t_0
+f.close()
+
Index: sansmodels/src/sans/models/test/sphere_testdata.txt
===================================================================
--- sansmodels/src/sans/models/test/sphere_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/sphere_testdata.txt	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,135 @@
+pythonclass = SphereModel
+scale = 1
+radius = 60
+contrast = 1e-6
+background = 0.01
+
+DATA
+0.001	90.4227444561307
+0.00105251266011778	90.4157276053359
+0.00110778289970821	90.4079550029624
+0.00116595552660488	90.3993453156916
+0.00122718295288593	90.3898084718774
+0.00129162559419317	90.3792447263187
+0.00135945229002046	90.3675436251717
+0.00143084074607265	90.3545828615347
+0.00150597799985384	90.3402270095186
+0.00158506091070502	90.3243261248614
+0.00166829667557485	90.3067141976634
+0.00175590337187494	90.287207442629
+0.00184811052884188	90.2656024098205
+0.00194515972890305	90.2416738978344
+0.00204730524062173	90.2151726495366
+0.00215481468487986	90.185822808414
+0.00226796973604376	90.1533191119972
+0.00238706685995004	90.1173237963576
+0.00251241809064502	90.0774631837544
+0.00264435184791283	90.0333239229256
+0.00278321379773411	89.9844488493339
+0.00292936775792964	89.9303324299529
+0.00308319665136179	89.8704157547851
+0.00324510350919104	89.8040810347349
+0.00341551252681621	89.7306455629486
+0.00359487017526494	89.6493550944954
+0.00378364637094618	89.5593765971608
+0.00398233570682956	89.4597903244572
+0.00419145874827721	89.3495811609297
+0.0044115633969232	89.2276291894534
+0.00464322632617388	89.0926994310602
+0.00488705449209019	88.9434307099749
+0.0051436867236104	88.7783236005616
+0.0054137953962797	88.5957274193056
+0.00569808819387176	88.393826234303
+0.00599730996251769	88.1706238779775
+0.00631224466220038	87.9239279666488
+0.00664371742072679	87.6513329544612
+0.00699259669556001	87.3502022802804
+0.00735979654917468	87.0176497061855
+0.00774627904389752	86.6505199970915
+0.00815305676250721	86.2453691549684
+0.00858119546119774	85.7984445008535
+0.00903181686185589	85.3056649962709
+0.00950610159096858	84.7626023162297
+0.0100052922728602	84.1644633324426
+0.010530696785364	83.5060748418634
+0.0110836916864573	82.7818715864011
+0.0116657258208385	81.9858888591154
+0.0122783241158954	81.1117612843166
+0.0129230915770094	80.1527296972877
+0.0136017174926639	79.1016584357033
+0.0143159798603742	77.9510657891023
+0.0150677500450351	76.6931708311579
+0.0158589976818897	75.3199603730699
+0.0166917958369675	73.8232803087866
+0.0175683264385096	72.1949561473611
+0.0184908859936133	70.4269480041077
+0.0194618916050726	68.5115456917836
+0.0204838873041789	66.4416097339716
+0.0215595507160742	64.2108640045017
+0.0226917000751195	61.8142451337187
+0.0238833016086589	59.248312629239
+0.0251374773085249	56.5117216066172
+0.0264575131106459	53.605756841905
+0.027846867504187	50.5349222351867
+0.0293091805927793	47.3075733855052
+0.0308482836315787	43.9365725134711
+0.0324682090651407	40.4399341936915
+0.0341732010924115	36.8414172059712
+0.035967726786514	33.1710025095031
+0.0378564877984635	29.4651805895993
+0.0398444326754772	25.7669546222864
+0.0419367698261504	22.1254514069004
+0.0441389811664687	18.5950234076791
+0.0464568364824087	15.2337275099316
+0.0488964085467568	12.1010856393634
+0.0514640890297529	9.2550766770957
+0.0541666052452436	6.74838567895613
+0.0570110377762212	4.62405104866114
+0.0600048390259259	2.9108039900483
+0.0631558527431166	1.61857916581746
+0.0664723345726646	0.734868643264245
+0.0699629736853145	0.222751268886955
+0.0736369155432808	0.021493170785741
+0.0775037858613269	0.0505007176184419
+0.0815737158261042	0.217030561955665
+0.085857368639825	0.427366151211124
+0.0903659674578153	0.600177099372201
+0.0951113247931421	0.679643993347096
+0.100105873465356	0.644995876938007
+0.105362699174436	0.512882672304445
+0.110895574785276	0.330050926618079
+0.116718996412541	0.156436599531048
+0.122848221400442	0.0427161530728255
+0.129299308296917	0.0102364291202494
+0.136089158926978	0.0427676396140632
+0.143235562675425	0.0962906936732686
+0.150757243094979	0.124412512424065
+0.158673906961919	0.106245316452041
+0.167006295907771	0.0581157186256502
+0.175776240762306	0.0180003127669836
+0.185006718750238	0.0120232942917672
+0.194721913691476	0.0314717764737715
+0.20494727936264	0.0445914093009115
+0.215709606185875	0.0335942223501167
+0.227037091419654	0.0143439512133508
+0.238959413035505	0.0113779690004859
+0.251507807474183	0.0214441689681625
+0.264715151485044	0.0221334416546839
+0.278616048263005	0.012423248413131
+0.2932469181088	0.0111309676391173
+0.308646093850034	0.0164251291976273
+0.324853921273063	0.0133484308747301
+0.341912864828804	0.0100937607456987
+0.359867618889456	0.0133061305111565
+0.378765224847594	0.0115497267629865
+0.398655194364451	0.0103763850351245
+0.4195896390903	0.0120165913778557
+0.441623407196792	0.0100464754133085
+0.464814227078974	0.0111896261669603
+0.489222858603482	0.0102144295433158
+0.514913252299177	0.0106974772728633
+0.541952716907305	0.0101315993523187
+0.570412095730168	0.0105409279971524
+0.600365952240318	0.0100029843668479
+0.631892765436603	0.0103719406877445
+0.665075135458861	0.0101206894788735
Index: sansmodels/src/sans/models/test/testcase_generator.py
===================================================================
--- sansmodels/src/sans/models/test/testcase_generator.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/testcase_generator.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,721 @@
+"""
+    Random test-case generator for BaseComponents
+    
+    @author: Mathieu Doucet / UTK
+    @license: This software is provided as part of the DANSE project.
+"""
+import time
+
+def randomModel():
+    """ Return a random model name """
+    from sans.models.ModelFactory import ModelFactory
+    from random import random
+    from math import floor
+    
+    model_list = ModelFactory().getAllModels()
+    ready = False
+    while not ready:
+        rnd_id = int(floor(random()*len(model_list)))
+        if model_list[rnd_id] not in ["TestSphere2", "DataModel"]:
+            ready = True
+    return model_list[rnd_id]
+
+class ReportCard: # pylint: disable-msg=R0902
+    """ Class to hold test-case results """
+    
+    def __init__(self):
+        """ Initialization """
+        ## Number of test cases
+        self.n_cases = 0
+        ## Number of passed test cases
+        self.n_cases_pass = 0
+        ## Number of Evaluation calls
+        self.n_eval = 0
+        ## Number of passed Evaluation calls
+        self.n_eval_pass = 0
+        ## Number of SetParam calls
+        self.n_set = 0
+        ## Number of passed Set calls
+        self.n_set_pass = 0
+        ## Number of GetParam calls
+        self.n_get = 0
+        ## Number of passed Get calls
+        self.n_get_pass = 0
+        ## Number of load calls
+        self.n_load = 0
+        ## Number of passed Load calls
+        self.n_load_pass = 0
+        ## Number of save calls
+        self.n_save = 0
+        ## Number of passed Save calls
+        self.n_save_pass = 0
+        ## Number of Add calls
+        self.n_add = 0
+        ## Number of passed Add calls
+        self.n_add_pass = 0
+        ## Number of Sub calls
+        self.n_sub = 0
+        ## Number of passed Sub calls
+        self.n_sub_pass = 0
+        ## Number of Div calls
+        self.n_div = 0
+        ## Number of passed Div calls
+        self.n_div_pass = 0
+        ## Number of mul calls
+        self.n_mul = 0
+        ## Number of passed Mul calls
+        self.n_mul_pass = 0
+        ## Test log
+        self.log = ""
+        ## Trace
+        self.trace = ""
+        ## Model tested
+        self.model = ""
+        ## List of all models tested
+        self.modelList = []
+        
+        
+    def __add__(self, other):
+        """ Add two report cards
+            @param other: other report to add
+            @return: updated self
+        """
+        
+        self.n_cases += other.n_cases
+        self.n_cases_pass += other.n_cases_pass
+        self.n_eval += other.n_eval
+        self.n_eval_pass += other.n_eval_pass
+        self.n_set += other.n_set
+        self.n_set_pass += other.n_set_pass
+        self.n_get += other.n_get
+        self.n_get_pass += other.n_get_pass
+        self.n_load += other.n_load
+        self.n_load_pass += other.n_load_pass
+        self.n_save += other.n_save
+        self.n_save_pass += other.n_save_pass
+        self.n_add += other.n_add
+        self.n_add_pass += other.n_add_pass
+        self.n_sub += other.n_sub
+        self.n_sub_pass += other.n_sub_pass
+        self.n_mul += other.n_mul
+        self.n_mul_pass += other.n_mul_pass
+        self.n_div += other.n_div
+        self.n_div_pass += other.n_div_pass
+        if len(other.log)>0:
+            self.log += other.log
+        if len(other.trace)>0:
+            self.trace += other.trace
+            
+        if not other.model in self.modelList:
+            self.modelList.append(other.model)
+            
+        return self
+        
+    def isPassed(self):
+        """ Returns true if no error was found """
+        if self.n_cases_pass < self.n_cases \
+            or self.n_save_pass < self.n_save \
+            or self.n_load_pass < self.n_load \
+            or self.n_set_pass < self.n_set \
+            or self.n_get_pass < self.n_get \
+            or self.n_eval_pass < self.n_eval \
+            or self.n_add_pass < self.n_add \
+            or self.n_sub_pass < self.n_sub \
+            or self.n_mul_pass < self.n_mul \
+            or self.n_div_pass < self.n_div:
+            return False
+        return True
+        
+    def __str__(self):
+        """ String representation of the report card """
+        from sans.models.ModelFactory import ModelFactory
+        
+        rep = "Detailed output:\n"
+        rep += self.log
+        rep += "\n"
+        rep += "Total number of cases: %g\n" % self.n_cases
+        rep += "   Passed:             %g\n" % self.n_cases_pass
+        rep += "\n"
+        self.modelList.sort()
+        rep += "Models tested: %s\n" % self.modelList
+        all_models = ModelFactory().getAllModels()
+        all_models.sort()
+        rep += "\n"
+        rep += "Available models: %s\n" % all_models
+        rep += "\n"
+        rep += "Breakdown:\n"
+        rep += "   Eval:          %g / %g\n" % (self.n_eval_pass, self.n_eval)
+        rep += "   Set:           %g / %g\n" % (self.n_set_pass, self.n_set)
+        rep += "   Get:           %g / %g\n" % (self.n_get_pass, self.n_get)
+        rep += "   Load:          %g / %g\n" % (self.n_load_pass, self.n_load)
+        rep += "   Save:          %g / %g\n" % (self.n_save_pass, self.n_save)
+        rep += "   Add:           %g / %g\n" % (self.n_add_pass, self.n_add)
+        rep += "   Sub:           %g / %g\n" % (self.n_sub_pass, self.n_sub)
+        rep += "   Mul:           %g / %g\n" % (self.n_mul_pass, self.n_mul)
+        rep += "   Div:           %g / %g\n" % (self.n_div_pass, self.n_div)
+        return rep
+    
+        
+
+class TestCaseGenerator:
+    """ Generator for suite of test-cases
+    """
+    
+    def __init__(self):
+        """ Initialization
+        """
+        
+        self.n_tests = 0
+        self.n_passed = 0
+        self.time = 0
+        self.reportCard = ReportCard()
+    
+    def generateFiles(self, number, file_prefix):
+        """ Generate test-case files
+            @param number: number of files to generate
+            @param file_prefix: prefix for the file names
+        """
+        
+        for i in range(number):
+            filename = "%s_%d.xml" % (file_prefix, i)
+            self.generateFileTest(filename)
+            self.n_tests += 1
+            
+    def generateAndRun(self, number):
+        """ Generate test-cases and run them
+            @param number: number of test-cases to generate
+        """
+        start_time = time.time()
+        for i in range(number):
+            textcase = self.generateTest()
+            t = TestCase(text = textcase)
+            passed = t.run()
+            self.reportCard += t.reportCard
+            if not passed:
+                t = time.localtime()
+                xmloutput = open("error_%i-%i-%i-%i-%i-%i_%i.xml" % \
+                 (t[0],t[1],t[2],t[3],t[4],t[5],self.reportCard.n_cases),'w')
+                xmloutput.write(textcase)
+                xmloutput.close()
+
+                
+        self.time += time.time()-start_time
+        print self.reportCard        
+        
+    def generateFileTest(self, filename = "tmp.xml"):
+        """
+            Write a random test-case in an XML file
+            @param filename: name of file to write to
+        """
+        text = self.generateTest()
+        # Write test case in file
+        xmlfile = open(filename,'w')
+        xmlfile.write(text)
+        xmlfile.close()
+    
+    
+    def generateTest(self):
+        """
+            Generate an XML representation of a random test-case
+        """
+        import random
+        
+        #t = TestCase()
+        text = "<?xml version=\"1.0\"?>\n"
+        text  += "<TestCase>\n"
+        stimulus = "eval"
+    
+        loop = True
+        while loop:
+            rnd = random.random()
+            
+            # run 50%
+            if rnd <= 0.5:
+                stimulus = "eval"
+            elif rnd>0.5 and rnd<=0.7:
+                stimulus = "set"
+            elif rnd>0.7 and rnd<=0.72:
+                stimulus = "save"
+            elif rnd>0.72 and rnd<=0.74:
+                stimulus = "load"
+            elif rnd>0.74 and rnd<=0.8:
+                stimulus = "get"
+            elif rnd>0.8 and rnd<=0.81:
+                stimulus = "add"
+            elif rnd>0.81 and rnd<=0.82:
+                stimulus = "div"
+            elif rnd>0.82 and rnd<=0.83:
+                stimulus = "mul"
+            elif rnd>0.83 and rnd<=0.84:
+                stimulus = "sub"
+            else:
+                # just run and quit
+                stimulus = "eval"
+                loop = False
+                
+            text += "  <Stimulus id=\"%s\"/>\n" % stimulus
+        text += "</TestCase>"
+        
+        return text
+    
+        
+class TestCase:
+    """ Test-case class """
+    
+    def __init__(self, filename = None, text = None):
+        """ Initialization
+            @param filename: name of file containing the test case
+        """
+        #from sans.models.ModelFactory import ModelFactory
+        self.filename = filename
+        self.text = text
+        #self.model = ModelFactory().getModel(randomModel())
+        self.model = getRandomModelObject()
+        #self.model = ModelFactory().getModel("SphereModel")
+        self.passed = True
+        self.reportCard = ReportCard()
+        
+    
+    def run(self):
+        """ Read the test case and execute it """
+        from xml.dom.minidom import parse
+        from xml.dom.minidom import parseString
+        
+        # Initialize report
+        self.reportCard = ReportCard()
+        self.reportCard.model = self.model.name
+        self.reportCard.n_cases = 1
+        
+        if not self.text == None:
+            dom = parseString(self.text)
+        elif not self.filename == None:
+            dom = parse(self.filename)
+        else:
+            print "No input to parse"
+            return False
+        
+        if dom.hasChildNodes():
+            for n in dom.childNodes:
+                if n.nodeName == "TestCase":
+                    self.processStimuli(n)
+            
+        # Update report card        
+        if self.passed:
+            self.reportCard.n_cases_pass = 1
+            
+        return self.passed
+                          
+    def processStimuli(self, node):
+        """ Process the stimuli list in the TestCase node 
+            of an XML test-case file
+            @param node: test-case node
+        """
+        import testcase_generator as generator
+        report = ReportCard()
+        report.trace = "%s\n" % self.model.name
+        
+        self.passed = True
+        if node.hasChildNodes():
+            for n in node.childNodes:
+                if n.nodeName == "Stimulus":
+                    
+                    s_type = None
+                    if n.hasAttributes():
+                        # Get stilumus ID
+                        for i in range(len(n.attributes)):
+                            attr_name = n.attributes.item(i).name
+                            if attr_name == "id":
+                                s_type = n.attributes.item(i).nodeValue
+                    if hasattr(generator,"%sStimulus" % s_type):
+                        stimulus = getattr(generator,"%sStimulus" % s_type)
+                        #print s_type, self.model.name
+                        m, res = stimulus(self.model)
+                        #print "     ", m.name
+                        self.model = m
+                        if not res.isPassed():
+                            self.passed = False
+                        
+                        report += res
+                        
+                    else:
+                        print "Stimulus %s not found" % s_type
+                        
+        self.reportCard += report
+        
+        if not self.passed:
+            print report.trace
+        return self.passed
+        
+    
+def evalStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwise
+    """
+    minval = 0
+    maxval = 20
+    # Call run with random input
+    import random, math
+    input_value = random.random()*(maxval-minval)+minval
+    
+    # Catch division by zero, which can happen
+    # when creating random models
+    try:
+        # Choose whether we want 1D or 2D
+        if random.random()>0.5:
+            output = model.run(input_value)
+        else:
+            output = model.run([input_value, 2*math.pi*random.random()])            
+    except ZeroDivisionError:
+        output = -1
+        
+    #print "Eval: %g" % output
+    
+    # Oracle bit - just check that we have a number...
+    passed = False
+    if math.fabs(output) >= 0: 
+        passed = True
+    
+    report = ReportCard()
+    report.n_eval = 1
+    if passed:
+        report.n_eval_pass = 1
+    else:
+        report.log = "Eval: bad output value %g\n" % output
+        
+    report.trace = "Eval(%g) = %g %i\n" % (input_value, output, passed)    
+    return model, report
+
+def setStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwize
+    """
+    # Call run with random input
+    import random, math
+    minval = 1
+    maxval = 50
+    
+    # Choose a parameter to change
+    keys = model.getParamList()
+    if len(keys)==0:
+        #print model.name+" has no params"
+        return model, ReportCard()
+    
+    p_len = len(keys)
+    i_param = int(math.floor(random.random()*p_len))
+    p_name  = keys[i_param]
+    
+    # Chose a value
+    input_val = random.random()*(maxval-minval)+minval
+    model.setParam(p_name, input_val)
+    
+    # Read back
+    check_val = model.getParam(p_name)
+    #print "Set: %s = %g" % (p_name, check_val)
+    
+    # Oracle bit
+    passed = False
+    if check_val == input_val: 
+        passed = True
+    
+    report = ReportCard()
+    report.n_set = 1
+    if passed:
+        report.n_set_pass = 1
+    else:
+        report.log = "Set: parameter %s not set properly %g <> %g\n" % \
+            (p_name, input_val, check_val)
+        
+    report.trace = "Set %s = %g %i\n" % (p_name, input_val, passed)    
+    return model, report
+
+def getStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwise
+    """
+    import random, math
+    # Choose a parameter to change
+    keys = model.getParamList()
+    if len(keys)==0:
+        #print model.name+" has no params"
+        return model, ReportCard()    
+    
+    p_len = len(keys)
+    i_param = int(math.floor(random.random()*p_len))
+    p_name  = keys[i_param]
+    
+    # Read back
+    check_val = model.getParam(p_name)
+    #print "Get: %s = %g" % (p_name, check_val)
+    
+    # Oracle bit
+    passed = False
+    if math.fabs(check_val) >= 0: 
+        passed = True
+        
+    report = ReportCard()
+    report.n_get = 1
+    if passed:
+        report.n_get_pass = 1
+    else:
+        report.log = "Get: bad value for parameter %s: %g\n" % \
+            (p_name, check_val)
+        
+    report.trace = "Get %s = %g %i\n" % (p_name, check_val, passed)    
+    return model, report
+
+def loadStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwize
+    """
+    from sans.models.ModelIO import ModelIO
+    from sans.models.ModelFactory import ModelFactory
+    factory = ModelFactory()
+    io = ModelIO(factory)
+    
+    # testModel.xml should be in the directory
+    loaded = io.load("load_test_model.xml")
+    value2 = loaded.run(1)
+    
+    # Oracle bit
+    passed = False
+    if not value2 == 0: 
+        passed = True
+        
+    report = ReportCard()
+    report.n_load = 1
+    if passed:
+        report.n_load_pass = 1
+    else:
+        report.log = "Load: bad loaded model\n"
+        
+    report.trace = "Load = %s %i\n" % (loaded.name, passed)    
+    return model, report
+
+def saveStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwize
+    """
+    from sans.models.ModelIO import ModelIO
+    from sans.models.ModelFactory import ModelFactory
+    factory = ModelFactory()
+    io = ModelIO(factory)
+    io.save(model,"testModel.xml")
+    #value = model.run(1)
+
+    # Check it 
+    loaded = io.load("testModel.xml")
+    try:
+        value2 = loaded.run(1)
+    except ZeroDivisionError:
+        value2 = -1
+    
+    # Oracle bit
+    passed = False
+    
+    # The two outputs do not need to be the same
+    # since we do not save the parameters!
+    # If it's the subtraction of two identical models, 
+    # we will also get zero!
+    #if value2 == value: 
+    #    passed = True
+    
+    passed = True
+
+        
+    report = ReportCard()
+    report.n_save = 1
+    if passed:
+        report.n_save_pass = 1
+    else:
+        report.log = "Save: bad output from saved model %g\n" % value2
+        
+    report.trace = "Save %s %i\n" % (model.name, passed)    
+    return model, report
+
+def getRandomModelObject():
+    """
+        Return a random model object
+    """
+    from sans.models.ModelFactory import ModelFactory
+    while True:
+        try:
+            modelname = randomModel()
+            add_model = ModelFactory().getModel(modelname)
+            return add_model
+        except:
+            # Don't complain when a model can't be loaded.
+            # A list of tested models will appear in the 
+            # file report, which can be compared with the 
+            # list of available models, also printed.
+            pass
+            #print "Could not load ", modelname
+    
+
+def addStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwize
+    """
+    #from sans.models.ModelFactory import ModelFactory
+    #factory = ModelFactory()
+    #add_model = factory.getModel("SphereModel")
+    add_model = getRandomModelObject()
+            
+    tmp = model + add_model
+    model = tmp
+    
+    # Oracle bit
+    passed = False
+    
+    try:
+        value2 = model.run(1)
+        value2 = float(value2)
+    except:
+        passed = False
+    
+    # If we made it this far, we have a float
+    passed = True 
+        
+    report = ReportCard()
+    report.n_add = 1
+    if passed:
+        report.n_add_pass = 1
+    else:
+        report.log = "Add: bad output from composite model\n"
+        
+    report.trace = "Div %s %i\n" % (model.name, passed)    
+    return model, report
+
+def subStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwize
+    """
+    from sans.models.ModelFactory import ModelFactory
+    from random import random
+    
+    #factory = ModelFactory()
+    #sub_model = factory.getModel("CylinderModel")
+    #sub_model = factory.getModel(randomModel())
+    sub_model = getRandomModelObject()
+
+    
+    sub_model.params["scale"] = 1.1*random()
+    tmp = model - sub_model
+    
+    # Oracle bit
+    passed = False
+    
+    try:
+        value2 = tmp.run(1)
+        value2 = float(value2)
+    except:
+        passed = False
+    
+    # If we made it this far, we have a float
+    passed = True 
+        
+    report = ReportCard()
+    report.n_sub = 1
+    if passed:
+        report.n_sub_pass = 1
+    else:
+        report.log = "Sub: bad output from composite model\n"
+
+    report.trace = "Sub %s (%g - %g = %g) %i\n" % \
+        (model.name, model.run(1), \
+         sub_model.run(1), value2, passed)                
+    return tmp, report
+
+def mulStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwize
+    """
+    #from sans.models.ModelFactory import ModelFactory
+    #factory = ModelFactory()
+    #mul_model = factory.getModel("SphereModel")
+    #mul_model = factory.getModel(randomModel())
+    mul_model = getRandomModelObject()
+    tmp = model * mul_model
+    
+    # Oracle bit
+    passed = False
+
+    try:
+        value2 = tmp.run(1)
+        value2 = float(value2)
+    except:
+        passed = False
+    
+    # If we made it this far, we have a float
+    passed = True 
+        
+    report = ReportCard()
+    report.n_mul = 1
+    if passed:
+        report.n_mul_pass = 1
+    else:
+        report.log = "Mul: bad output from composite model\n"
+        
+    report.trace = "Mul %s (%g * %g = %g) %i\n" % \
+      (model.name, model.run(1), \
+       mul_model.run(1), value2, passed)                
+    return tmp, report
+
+def divStimulus(model):
+    """ Apply the stimulus to the supplied model object
+        @param model: model to apply the stimulus to
+        @return: True if passed, False otherwise
+    """
+    #from sans.models.ModelFactory import ModelFactory
+    #factory = ModelFactory()
+    #div_model = factory.getModel("SphereModel")
+    #div_model = factory.getModel(randomModel())
+    div_model = getRandomModelObject()
+    
+    tmp = model / div_model
+    
+    # Oracle bit
+    passed = False
+    
+    try:
+        from random import random
+        input_val = 1.5 * random()
+        if div_model.run(input_val)==0:
+            print "Skipped (DIV) because denominator evaluated to zero"
+        else:
+            value2 = tmp.run(input_val)
+            value2 = float(value2)
+    except:
+        passed = False
+    
+    # If we made it this far, we have a float
+    passed = True 
+        
+    report = ReportCard()
+    report.n_div = 1
+    if passed:
+        report.n_div_pass = 1
+    else:
+        report.log = "Div: bad output from composite model\n"
+        
+    report.trace = "Div %s (%g / %g = %g) %i\n" % \
+        (model.name, model.run(1), \
+         div_model.run(1), value2, passed)                
+    return tmp, report
+
+if __name__ == '__main__':
+
+    #print randomModel()
+    g = TestCaseGenerator()
+    g.generateAndRun(2000)
+    
+    #t = TestCase(filename = "error_1.17721e+009.xml")
+    #print t.run()
+    
Index: sansmodels/src/sans/models/test/utest_basemodels.py
===================================================================
--- sansmodels/src/sans/models/test/utest_basemodels.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/utest_basemodels.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,318 @@
+"""
+    Unit tests for BaseComponent 
+    @author: Mathieu Doucet / UTK
+"""
+# Disable "missing docstring" complaint
+# pylint: disable-msg=C0111
+# Disable "too many methods" complaint 
+# pylint: disable-msg=R0904 
+
+
+import unittest, math
+from sans.models.BaseComponent import BaseComponent
+
+class ConstantComponent(BaseComponent):
+    """ Test component """
+    def __init__(self, value=0):
+        BaseComponent.__init__(self)
+        self.params['value'] = value
+        self.dim = 0
+        self.name = 'constant'
+    def run(self, x=0):
+        """ Evaluate method """
+        return self.params['value']
+        
+class IdentityComponent(BaseComponent):
+    """ Test identity component """
+    def __init__(self):
+        BaseComponent.__init__(self)
+        self.dim = 1
+        self.name = 'id'
+    def run(self, x=0):
+        """ Evaluate method """
+        return x
+        
+class SinComponent(BaseComponent):
+    """ Test sin(x) component """
+    def __init__(self):
+        BaseComponent.__init__(self)
+        self.params['norm'] = 1
+        self.dim = 1
+        self.name = 'sin'
+    def run(self, x=0):
+        """ Evaluate method """
+        return self.params['norm']*math.sin(x)
+        
+class TwoDimComponent(BaseComponent):
+    """ Test 2D component """
+    def __init__(self):
+        BaseComponent.__init__(self)
+        self.params['norm'] = 1
+        self.dim = 2
+        self.name = '2D'
+    def run(self, x):
+        """ Evaluate method """
+        return self.params['norm']*x[0]*math.sin(x[1])
+        
+class ThreeDimComponent(BaseComponent):
+    """ Test 3D component """
+    def __init__(self):
+        BaseComponent.__init__(self)
+        self.params['norm'] = 1
+        self.dim = 3
+        self.name = '3D'
+    def run(self, x):
+        """ Evaluate method """
+        return self.params['norm']*x[0]*math.sin(x[1])*x[2]*x[2]
+        
+
+
+class TestBasicComponent(unittest.TestCase):
+    
+    def setUp(self):
+        self.comp = BaseComponent()
+
+    def testrun(self):
+        input = 45
+        self.assertEqual(self.comp.run(input), 0)
+        
+class TestArithmetic(unittest.TestCase):
+    def setUp(self):
+        self.value1 = 5
+        self.comp1 = ConstantComponent(self.value1)
+        
+        self.value2 = 7
+        self.comp2 = ConstantComponent(self.value2)
+        
+    def testrun(self):
+        self.assertEqual(self.comp1.run(45), self.value1)
+        
+    def testadd(self):
+        newcomp = self.comp1+self.comp2
+        self.assertEqual(newcomp.run(45), self.value1+self.value2)
+        
+    def testmultiply(self):
+        newcomp = self.comp1*self.comp2
+        self.assertEqual(newcomp.run(45), self.value1*self.value2)
+        
+    def testsubtract(self):
+        newcomp = self.comp1-self.comp2
+        self.assertEqual(newcomp.run(45), self.value1-self.value2)
+        
+    def testdivide(self):
+        newcomp = self.comp1/self.comp2
+        self.assertEqual(newcomp.run(45), self.value1/self.value2)
+        print newcomp
+        
+    def vtestcomplic1(self):
+        val = (self.value1+self.value2)/self.value2
+        newcomp = (self.comp1+self.comp2)
+        newcomp2 = newcomp/self.comp2
+        self.assertEqual(newcomp2.run(45), val)
+
+    def testcomplic2(self):
+        # A + B 
+        newcomp = self.comp1+self.comp2
+        # Value of A
+        self.assertEqual(newcomp.operateOn.params['value'], self.value1)
+        # Value of B
+        self.assertEqual(newcomp.other.params['value'], self.value2)
+        # Value of D = A+B
+        self.assertEqual(newcomp.run(), self.value1+self.value2)
+        # D + C
+        newcomp2 = newcomp+self.comp2
+        # Value of D
+        self.assertEqual(newcomp2.operateOn.run(), self.value1+self.value2)
+        # Value of B
+        self.assertEqual(newcomp2.other.params['value'], self.value2)
+        
+        
+        self.assertEqual(newcomp2.run(45), self.value1+self.value2+self.value2)
+
+    def testcomplic3(self):
+        # Same as testcomplic2, but all in one go
+        newcomp = self.comp1+self.comp2+self.comp2
+        self.assertEqual(newcomp.run(45), self.value1+self.value2+self.value2)
+        
+    def testcomplic4(self):
+        output = (self.value1+self.value1)/(self.value1+self.value2)
+        newcomp = (self.comp1+self.comp1)/(self.comp1+self.comp2)
+        self.assertEqual(newcomp.run(45), output)
+                
+class TestIO(unittest.TestCase):
+    def setUp(self):
+        from sans.models.ModelFactory import ModelFactory
+        from sans.models.ModelIO import ModelIO
+        self.factory = ModelFactory()
+        self.io = ModelIO(self.factory)
+        self.sph = self.factory.getModel("SphereModel")
+        self.cyl = self.factory.getModel("CylinderModel")
+        
+        
+    def saveLoadAndCompare(self, combo):
+        # Save
+        self.io.save(combo,"test_io.xml")
+        # Load
+        loaded = self.io.load("test_io.xml")
+        
+        # Check that the model is the same
+        # ONLY WORKS FOR DEFAULT PARAMETERS
+        # print combo.run(1)
+        self.assertEqual(combo.run(1), loaded.run(1))
+        return loaded
+        
+    def testSimple(self):
+        self.saveLoadAndCompare(self.sph)
+        
+    def testAdd(self):
+        combo = self.sph+self.cyl
+        self.saveLoadAndCompare(combo)
+
+    def testSub(self):
+        combo = self.sph-self.cyl
+        self.saveLoadAndCompare(combo)
+
+    def testMul(self):
+        combo = self.sph*self.cyl
+        self.saveLoadAndCompare(combo)
+
+    def testDiv(self):
+        combo = self.sph/self.cyl
+        self.saveLoadAndCompare(combo)
+        
+    def testDegree2(self):
+        combo = (self.sph+self.sph)/self.cyl
+        self.saveLoadAndCompare(combo)
+        
+    def testDegree3(self):
+        combo = self.sph*(self.cyl+self.sph/self.cyl)
+        self.saveLoadAndCompare(combo)
+
+    def testReSave(self):
+        combo = self.sph+self.cyl
+        loaded = self.saveLoadAndCompare(combo)
+        self.saveLoadAndCompare(loaded)
+
+class TestGetSet(unittest.TestCase):
+    def setUp(self):
+        from sans.models.ModelFactory import ModelFactory
+        self.factory = ModelFactory()
+        self.sph = self.factory.getModel("SphereModel")
+        
+    def testGet(self):
+        # The default value is 1.0e-6
+        self.assertEqual(self.sph.getParam("scale"), 1.0e-6)
+        
+    def testGetCaseSensitivity(self):
+        # The default value is 1.0e-6
+        self.assertEqual(self.sph.getParam("sCale"), 1.0e-6)
+        
+    def testGetException(self):
+        self.assertRaises(ValueError, self.sph.getParam, "scalee")
+        
+    def testSet(self):
+        value_0 = self.sph.run(1)
+        scale = 2.0e-6
+        self.sph.setParam("scale", scale)
+        self.assertEqual(self.sph.getParam("scale"), scale)
+        self.assertEqual(self.sph.run(1), 2*value_0)
+        
+    def testSetCaseSensitivity(self):
+        value_0 = self.sph.run(1)
+        scale = 2.0e-6
+        self.sph.setParam("sCale", scale)
+        self.assertEqual(self.sph.getParam("scale"), scale)
+        self.assertEqual(self.sph.run(1), 2*value_0)
+        
+    def testSetException(self):
+        self.assertRaises(ValueError, self.sph.setParam, "scalee", 2.0)
+        
+    def testCompositeAdd(self):
+        sph2 = self.factory.getModel("SphereModel")
+        sph2.setParam("scale", 2.0e-6)
+        model = sph2+self.sph  
+        self.assertEqual(model.getParam('Base.scale'), 2.0e-6)
+        self.assertEqual(model.getParam('add.scale'), 1.0e-6)
+        
+    def testCompositeSub(self):
+        sph2 = self.factory.getModel("SphereModel")
+        sph2.setParam("scale", 2.0e-6)
+        model = sph2-self.sph  
+        self.assertEqual(model.getParam('Base.scale'), 2.0e-6)
+        self.assertEqual(model.getParam('sub.scale'), 1.0e-6)
+        
+    def testCompositeMul(self):
+        sph2 = self.factory.getModel("SphereModel")
+        sph2.setParam("scale", 2.0e-6)
+        model = sph2*self.sph  
+        self.assertEqual(model.getParam('Base.scale'), 2.0e-6)
+        self.assertEqual(model.getParam('mul.scale'), 1.0e-6)
+        
+    def testCompositeDiv(self):
+        sph2 = self.factory.getModel("SphereModel")
+        sph2.setParam("scale", 2.0e-6)
+        model = sph2/self.sph  
+        self.assertEqual(model.getParam('Base.scale'), 2.0e-6)
+        self.assertEqual(model.getParam('div.scale'), 1.0e-6)
+        
+    def testSetCompositeAdd(self):
+        sph2 = self.factory.getModel("SphereModel")
+        #sph2.setParam("scale", 2.0e-6)
+        model = sph2+self.sph  
+        value = model.run(1)
+        scale = 2.0e-6
+        model.setParam("base.scale", scale)
+        model.setParam("add.scale", scale)
+       
+        self.assertEqual(model.getParam("base.scale"), scale)
+        self.assertEqual(model.getParam("add.scale"), scale)
+        self.assertEqual(model.run(1), 2*value)
+        
+    def testSetCompositeSub(self):
+        sph2 = self.factory.getModel("SphereModel")
+        #sph2.setParam("scale", 2.0e-6)
+        model = sph2-self.sph  
+        value = model.run(1)
+        scale = 2.0e-6
+        model.setParam("base.scale", scale)
+        model.setParam("sub.scale", scale)
+       
+        self.assertEqual(model.getParam("base.scale"), scale)
+        self.assertEqual(model.getParam("sub.scale"), scale)
+        self.assertEqual(model.run(1), 2*value)
+        
+    def testSetCompositeMul(self):
+        sph2 = self.factory.getModel("SphereModel")
+        #sph2.setParam("scale", 2.0e-6)
+        model = sph2*self.sph  
+        value = model.run(1)
+        scale = 2.0e-6
+        model.setParam("mul.scale", scale)
+       
+        self.assertEqual(model.getParam("mul.scale"), scale)
+        self.assertEqual(model.run(1), 2*value)
+        
+    def testSetCompositeDiv(self):
+        sph2 = self.factory.getModel("SphereModel")
+        #sph2.setParam("scale", 2.0e-6)
+        model = sph2/self.sph  
+        value = model.run(1)
+        scale = 2.0e-6
+        model.setParam("div.scale", scale)
+       
+        self.assertEqual(model.getParam("div.scale"), scale)
+        self.assertEqual(model.run(1), 0.5*value)
+        
+    def testParamList(self):
+        sph2 = self.factory.getModel("SphereModel")
+        model = sph2/self.sph  
+        p_list = model.getParamList()
+        #print p_list
+        check_list = ['base.scale', 'base.radius', 'base.background', 
+                      'base.contrast', 
+                      'div.scale', 'div.radius', 'div.contrast', 
+                      'div.background']
+        self.assertEqual(check_list, p_list)
+         
+if __name__ == '__main__':
+    unittest.main()
Index: sansmodels/src/sans/models/test/utest_disperser.py
===================================================================
--- sansmodels/src/sans/models/test/utest_disperser.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/utest_disperser.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,106 @@
+"""
+    Unit tests for specific models
+    @author: Mathieu Doucet / UTK
+"""
+
+import unittest
+from SmearList import Smear
+
+# Disable "missing docstring" complaint
+# pylint: disable-msg=C0111
+# Disable "too many methods" complaint 
+# pylint: disable-msg=R0904 
+# Disable "could be a function" complaint 
+# pylint: disable-msg=R0201
+
+from sans_extension.c_models import Disperser
+from sans.models.ModelFactory import ModelFactory
+from sans.models.DisperseModel import DisperseModel
+      
+class TestDisperser(unittest.TestCase):
+    """ Unit tests for sphere model """
+    
+    def setUp(self):
+        self.model = ModelFactory().getModel("CylinderModel")
+        self.model.setParam("cyl_theta", 1.57)
+        self.model.setParam("cyl_phi", 0.1)
+        
+        
+
+    def testNoDisp(self):
+        """ Test 1D model for a sphere """
+        q = 0.005
+        d = Disperser(self.model, [], [])
+        value = d.run([q, 0])
+        self.assertEqual(value, self.model.run([q, 0]))
+
+    def testComp(self):
+        q = 0.005
+        phi = 0.10
+        sigma = 0.3
+        value_0 = self.model.run([q, phi])
+        app = Smear(self.model, ['cyl_phi'], [sigma])
+        val_py = app.run([q, phi])
+        
+        # Check that the parameters were returned to original values
+        self.assertEqual(value_0, self.model.run([q, phi]))
+        d = Disperser(self.model, ["cyl_phi"], [sigma])
+        val_c = d.run([q, phi])
+        self.assertEqual(val_py, val_c)
+        
+    def test2Disp(self):
+        q = 0.005
+        phi = 0.10
+        sigma = 0.3
+        value_0 = self.model.run([q, phi])
+        app = Smear(self.model, ['cyl_phi', 'cyl_theta'], [sigma, sigma])
+        val_py = app.run([q, phi])
+        
+        # Check that the parameters were returned to original values
+        self.assertEqual(value_0, self.model.run([q, phi]))
+        d = Disperser(self.model, ["cyl_phi", "cyl_theta"], [sigma, sigma])
+        val_c = d.run([q, phi])
+        self.assertEqual(val_py, val_c)
+        
+    def test3Disp(self):
+        q = 0.005
+        phi = 0.10
+        sigma = 0.3
+        value_0 = self.model.run([q, phi])
+        app = Smear(self.model, 
+                    ['cyl_phi', 'cyl_theta', 'radius'], [sigma, sigma, 1.0])
+        val_py = app.run([q, phi])
+        
+        # Check that the parameters were returned to original values
+        self.assertEqual(value_0, self.model.run([q, phi]))
+        d = Disperser(self.model, 
+                      ["cyl_phi", "cyl_theta", 'radius'], [sigma, sigma, 1.0])
+        val_c = d.run([q, phi])
+        self.assertEqual(val_py, val_c)
+        
+        
+class TestDisperserModel(unittest.TestCase):
+    """ Unit tests for sphere model """
+    
+    def setUp(self):
+        self.model = ModelFactory().getModel("CylinderModel")
+        self.model.setParam("cyl_theta", 1.57)
+        self.model.setParam("cyl_phi", 0.1)
+        
+    def test2Disp(self):
+        q = 0.005
+        phi = 0.10
+        sigma = 0.3
+        value_0 = self.model.run([q, phi])
+        app = Smear(self.model, ['cyl_phi', 'cyl_theta'], [sigma, sigma])
+        val_py = app.run([q, phi])
+        
+        # Check that the parameters were returned to original values
+        self.assertEqual(value_0, self.model.run([q, phi]))
+        d = DisperseModel(self.model, ["cyl_phi", "cyl_theta"], [sigma, sigma])
+        val_c = d.run([q, phi])
+        self.assertEqual(val_py, val_c)
+
+
+if __name__ == '__main__':
+    unittest.main()
Index: sansmodels/src/sans/models/test/utest_models.py
===================================================================
--- sansmodels/src/sans/models/test/utest_models.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/utest_models.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,246 @@
+"""
+    Unit tests for specific models
+    @author: Mathieu Doucet / UTK
+"""
+
+import unittest, time, math
+
+# Disable "missing docstring" complaint
+# pylint: disable-msg=C0111
+# Disable "too many methods" complaint 
+# pylint: disable-msg=R0904 
+# Disable "could be a function" complaint 
+# pylint: disable-msg=R0201
+
+
+        
+class TestSphere(unittest.TestCase):
+    """ Unit tests for sphere model """
+    
+    def setUp(self):
+        from sans.models.SphereModel import SphereModel
+        self.comp = SphereModel()
+        
+    def test1D(self):
+        """ Test 1D model for a sphere """
+        self.assertAlmostEqual(self.comp.run(1.0), 56.3878, 4)
+        
+    def test1D_2(self):
+        """ Test 2D model for a sphere """
+        self.assertAlmostEqual(self.comp.run([1.0, 1.3]), 56.3878, 4)
+
+class TestCyl(unittest.TestCase):
+    """Unit tests for cylinder"""
+    
+    def setUp(self):
+
+        from sans.models.CylinderModel import CylinderModel
+        self.comp = CylinderModel()
+        
+    def test1D(self):
+        """ Test 1D model of a cylinder """ 
+        self.assertAlmostEqual(self.comp.run(0.2), 0.041761386790780453, 4)
+       
+    def testTime(self):
+        """ Time profiling """
+        self.comp.run(2.0)
+        t0 = time.clock()
+        self.assertTrue(time.clock()-t0<1e-5)
+     
+    def test2D(self):
+        """ Test 2D model of a cylinder """ 
+        self.assertAlmostEqual(self.comp.run([0.2, 2.5]), 
+                               0.038176446608393366, 4)
+       
+    def testIO(self):
+        from sans.models.ModelFactory import ModelFactory
+        from sans.models.ModelIO import ModelIO
+        factory = ModelFactory()
+        io = ModelIO(factory)
+        io.save(self.comp,"myModel.xml")
+        value = self.comp.run(1)
+        loaded = io.load("myModel.xml")
+        self.assertEqual(value, loaded.run(1))
+        
+    def testIO_add(self):
+        from sans.models.ModelFactory import ModelFactory
+        from sans.models.ModelIO import ModelIO
+        factory = ModelFactory()
+        io = ModelIO(factory)
+        sph = factory.getModel("SphereModel")
+        cyl = factory.getModel("CylinderModel")
+        combo = sph - cyl
+        io.save(combo,"myModel.xml")
+        value = combo.run(1)
+        loaded = io.load("myModel.xml")
+        self.assertEqual(value, loaded.run(1))
+        
+    def testIO_add2(self):
+        from sans.models.ModelFactory import ModelFactory
+        #from sans.models.ModelIO import ModelIO
+        factory = ModelFactory()
+        #io = ModelIO(factory)
+        sph = factory.getModel("SphereModel")
+        cyl = factory.getModel("CylinderModel")
+        sph2 = factory.getModel("SphereModel")
+        combo1 = cyl - sph
+        combo = combo1 / sph2
+        #combo1 = sph
+        #io.save(combo,"myModel.xml")
+        # Just check that we have some output
+        self.assertTrue(math.fabs(combo.run(1))>0)
+        #loaded = io.load("myModel.xml")
+        #self.assertEqual(value, loaded.run(1))
+        
+        
+      
+       
+class TestFactory(unittest.TestCase):
+    """Unit tests for Model Factory"""
+    
+    def setUp(self):
+        from sans.models.ModelFactory import ModelFactory
+        self.comp = ModelFactory().getModel('CylinderModel')
+        
+    def test1D(self):
+        """ Test 1D model of a cylinder """ 
+        self.assertAlmostEqual(self.comp.run(0.2), 0.041761386790780453, 4)
+        
+class TestAddition(unittest.TestCase):
+    """Unit tests for Model Factory"""
+    
+    def setUp(self):
+        from sans.models.ModelFactory import ModelFactory
+        self.comp1 = ModelFactory().getModel('CylinderModel')
+        self.comp2 = ModelFactory().getModel('CylinderModel')
+        self.comp3 = self.comp1+self.comp2
+        
+    def test1D(self):
+        """ Test 1D model of a cylinder """
+        self.assertAlmostEqual(self.comp3.run(0.2), 2*0.041761386790780453, 4)
+        
+ 
+class TestGaussian(unittest.TestCase):
+    """Unit tests for Gaussian function"""
+    
+    def setUp(self):
+        from sans.models.Gaussian import Gaussian
+        self.gauss= Gaussian()
+        
+    def test1D(self):
+        self.gauss.setParam('scale', 2.0)
+        self.gauss.setParam('center', 1.0)
+        self.gauss.setParam('sigma', 1.0)
+        self.assertEqual(self.gauss.run(1.0), 2.0)
+        value = math.exp(-0.5)
+        self.assertEqual(self.gauss.run(2.0), 2.0*value)
+        
+    def test2D(self):
+        self.gauss.setParam('scale', 2.0)
+        self.gauss.setParam('center', 1.0)
+        self.gauss.setParam('sigma', 1.0)
+        self.assertEqual(self.gauss.runXY([1.0,1.0]), 2.0*2.0)
+        value = math.exp(-0.5)
+        self.assertEqual(self.gauss.runXY([1.0,2.0]), 2.0*2.0*value)
+        self.assertEqual(self.gauss.runXY([2.0,2.0]), 2.0*2.0*value*value)
+        
+    def test2Dphi(self):
+        self.gauss.setParam('scale', 2.0)
+        self.gauss.setParam('center', 1.0)
+        self.gauss.setParam('sigma', 1.0)
+        value = math.exp(-0.5)
+        self.assertEqual(self.gauss.run([math.sqrt(8.0), math.pi/4.0]), 2.0*2.0*value*value)
+        
+class TestLorentzian(unittest.TestCase):
+    """Unit tests for Lorentzian function"""
+    
+    def setUp(self):
+        from sans.models.Lorentzian import Lorentzian
+        self.lor= Lorentzian()
+        
+    def test1D(self):
+        self.lor.setParam('scale', 2.0)
+        self.lor.setParam('center', 1.0)
+        self.lor.setParam('gamma', 1.0)
+        self.assertEqual(self.lor.run(1.0), 4.0/math.pi)
+        value = 1/math.pi*0.5/(1+.25)
+        self.assertEqual(self.lor.run(2.0), 2.0*value)
+        
+    def test2D(self):
+        self.lor.setParam('scale', 0.5*math.pi)
+        self.lor.setParam('center', 1.0)
+        self.lor.setParam('gamma', 1.0)
+        self.assertEqual(self.lor.run(1.0), 1.0)
+        value = 0.25/(1+.25)
+        self.assertEqual(self.lor.runXY([1.0,2.0]), value)
+        self.assertEqual(self.lor.runXY([2.0,2.0]), value*value)
+        
+    def test2Dphi(self):
+        self.lor.setParam('scale', 0.5*math.pi)
+        self.lor.setParam('center', 1.0)
+        self.lor.setParam('gamma', 1.0)
+        self.assertEqual(self.lor.run(1.0), 1.0)
+        value = 0.25/(1+.25)
+        self.assertEqual(self.lor.run([math.sqrt(8.0), math.pi/4.0]), value*value)
+        
+class TestSin(unittest.TestCase):
+    """Unit tests for Sin(x) function"""
+    
+    def setUp(self):
+        from sans.models.Sin import Sin
+        self.sin = Sin()
+
+    def test1D(self):
+        self.assertEqual(self.sin.run(1.13), math.sin(1.13))
+        
+    def test2D(self):
+        self.assertEqual(self.sin.run([1.13,0.56]), math.sin(1.13*math.cos(0.56))*math.sin(1.13*math.sin(0.56)))
+        self.assertEqual(self.sin.runXY([1.13,0.56]), math.sin(1.13)*math.sin(0.56))
+        
+class TestCos(unittest.TestCase):
+    """Unit tests for Cos(x) function"""
+    
+    def setUp(self):
+        from sans.models.Cos import Cos
+        self.cos = Cos()
+
+    def test1D(self):
+        self.assertEqual(self.cos.run(1.13), math.cos(1.13))
+        
+    def test2D(self):
+        self.assertEqual(self.cos.run([1.13,0.56]), math.cos(1.13*math.cos(0.56))*math.cos(1.13*math.sin(0.56)))
+        self.assertEqual(self.cos.runXY([1.13,0.56]), math.cos(1.13)*math.cos(0.56))
+        
+class TestConstant(unittest.TestCase):
+    """Unit tests for Cos(x) function"""
+    
+    def setUp(self):
+        from sans.models.Constant import Constant
+        self.const = Constant()
+        self.const.setParam('value',56.1)
+
+    def test1D(self):
+        self.assertEqual(self.const.run(1.13), 56.1)
+        
+    def test2D(self):
+        self.assertEqual(self.const.run([1.13,0.56]), 56.1)
+        self.assertEqual(self.const.runXY([1.13,0.56]), 56.1)
+        
+    def testFunction(self):
+        from sans.models.Sin import Sin
+        s = Sin()
+        A = self.const
+        A.setParam('Value',1.5)
+        B = self.const.clone()
+        B.setParam('value',2.0)
+        C = self.const.clone()
+        C.setParam('value',3.0)
+        
+        f = A+B*s*s+C
+        answer = 1.5+2.0*math.sin(1.1)**2+3.0
+        self.assertEqual(f.run(1.1), answer)
+    
+        
+
+if __name__ == '__main__':
+    unittest.main()
Index: sansmodels/src/sans/models/test/validate_1D_model.py
===================================================================
--- sansmodels/src/sans/models/test/validate_1D_model.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/validate_1D_model.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+""" 
+    Class to validate a given model by reading a test data set
+    generated from the IGOR SANS analysis tool from the NCNR
+"""
+import sys, math
+
+
+class Validate1D:
+    """ 
+        Class to validate a given model by reading a test data set
+        generated from the IGOR SANS analysis tool from the NCNR
+    """
+    
+    def __init__(self):
+        """ Initialization """
+        # Precision for the result comparison
+        self.precision = 0.0001
+        # Flag for end result
+        self.passed = True
+                
+    def __call__(self, filename):
+        """ 
+            Perform test on a data file
+            @param filename: name of the test data set
+        """
+        from sans.models.ModelFactory import ModelFactory
+        
+        # Read the data file
+        file_obj = open(filename,'r')
+        content = file_obj.read()
+        
+        # Flag to determine whether we are in the DATA section
+        started_data = False
+        # Model to test
+        model_object = None
+        
+        # Process each line of the file
+        for line in content.split('\n'):
+            if len(line)==0:
+                continue
+            try:
+                # Catch class name
+                if line.count("pythonclass")>0 and model_object==None:
+                    toks = line.split('=')
+                    print "Found class", toks[1]
+                    classname = toks[1].lstrip().rstrip()
+                    model_object = ModelFactory().getModel(classname)
+                
+                    # Output file for plotting
+                    file_out = open("%s_out.txt" % classname, 'w')
+                    file_out.write("<q>  <I_danse>  <I_igor>\n")
+                
+                # Process data
+                elif started_data:
+                    toks = line.split()
+                    q = float(toks[0])
+                    iq = float(toks[1])
+                    
+                    value = model_object.run(q)
+                    
+                    file_out.write("%g %g %g\n" % (q, value, iq))
+                    
+                    if math.fabs( (value - iq)/iq )>self.precision:
+                        self.passed = False
+                        print "ERROR q=%g: %g <> %g" % (q, model_object.run(q), iq)
+                        
+                # Catch DATA tag
+                elif line.count("DATA")>0:
+                    started_data = True
+                                
+                # Process parameters
+                elif started_data == False and not model_object==None:
+                    toks = line.split('=')
+                    if len(toks)==2:
+                        print "Setting parameter", line
+                        model_object.setParam(toks[0].lstrip().rstrip(), 
+                                              float(toks[1]))
+            except:
+                print "Could not parse line:\n %s" % line
+                print sys.exc_value
+            
+        file_obj.close()
+        file_out.close()
+        
+        print "Test passed = ", self.passed
+        return self.passed
+            
+if __name__ == '__main__':
+    validator = Validate1D()
+    all_pass = True
+    all_pass = all_pass and validator("sphere_testdata.txt")
+    print '\n'
+    all_pass = all_pass and validator("cylinder_testdata.txt")
+    print '\n'
+    all_pass = all_pass and validator("core_shell_cyl_testdata.txt")
+    print '\n'
+    all_pass = all_pass and validator("core_shell_testdata.txt")
+    print '\n'
+    all_pass = all_pass and validator("ellipsoid_testdata.txt")
+    print '\n'
+    all_pass = all_pass and validator("elliptical_cylinder_testdata.txt")
+
+    print '\nOverall result:', all_pass
+        
+        
+        
+  
+    
Index: sansmodels/src/sans/models/test/validate_2D_elliptical_cyl.py
===================================================================
--- sansmodels/src/sans/models/test/validate_2D_elliptical_cyl.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/validate_2D_elliptical_cyl.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,229 @@
+#!/usr/bin/env python
+""" 
+    Test for Elliptical Cylinder model.
+    Class to validate a given 2D model by averaging it
+    and comparing to 1D prediction.
+    
+"""
+import sys, math
+from sans.models.EllipticalCylinderModel import EllipticalCylinderModel
+
+
+class Validate2D:
+    """ 
+        Class to validate a given 2D model by averaging it
+        and comparing to 1D prediction.
+    """
+    
+    def __init__(self):
+        """ Initialization """
+        # Precision for the result comparison
+        self.precision = 0.000001
+        # Flag for end result
+        self.passed = True
+        # Verbose flag
+        self.verbose = True
+                
+    def __call__(self, npts = 101):
+        """ 
+            Perform test and produce output file
+            @param npts: number of points to average over
+            @return: True if the test passed, otherwise False
+        """
+        passed = True
+        
+        model = EllipticalCylinderModel()
+        
+        theta_label = 'cyl_theta'
+        if not model.params.has_key(theta_label):
+            theta_label = 'axis_theta'
+            
+        phi_label = 'cyl_phi'
+        if not model.params.has_key(phi_label):
+            phi_label = 'axis_phi'
+        
+        output_f = open("average_func.txt",'w')    
+        output_f.write("<q_average> <2d_average> <1d_average>\n")
+            
+        for i_q in range(1, 15):
+            q = 0.3/15.0*i_q
+            value = self.average_point_3D(model, q, npts)
+            ana = model.run(q)
+            if q<0.3 and (value-ana)/ana>0.05:
+                passed = False
+            output_f.write("%10g %10g %10g\n" % (q, value, ana))
+            if self.verbose:
+                print "Q=%g: %10g %10g %10g %10g" % (q, value, ana, value-ana, value/ana)
+        
+        output_f.close()
+        return passed
+    
+    def average_point_3D(self, model, q, npts):
+        """
+            Average intensity over all orientations
+            of the main cylinder axis and the 
+            rotation around that axis
+            
+            @param model: model to test
+            @param q: q-value
+            @param npts: number of points to average over
+            @return: average intensity
+        """
+        sum = 0.0
+        for i_theta in range(npts):
+            theta = math.pi/npts*i_theta
+            
+            model.setParam('cyl_theta', theta)
+            
+            for j in range(npts):
+                model.setParam('cyl_phi', math.pi * 2.0 / npts * j)
+                     
+                for k in range(npts):
+                    model.setParam("cyl_psi", math.pi * 2.0 / npts * k)
+                    
+                    if str(model.run([q, 0])).count("IN")>0:                        
+                        if self.verbose:
+                            #print "ERROR", q, theta, math.pi * 2.0 / npts * j
+                            pass
+                    else:
+                        sum += math.sin(theta)*model.run([q, 0])
+         
+        value = sum/npts/npts/npts
+        return value
+
+    def checkCylinder2D(self, phi):
+        """ 
+            Check that the 2D scattering intensity reduces
+            to a cylinder when r_ratio = 1.0
+            @param phi: angle of the vector q on the detector
+            @return: True if the test passed, otherwise False
+        """
+        from sans.models.CylinderModel import CylinderModel
+        
+        cyl = CylinderModel()
+        cyl.setParam("cyl_theta", 1.57)
+        cyl.setParam("cyl_phi", 0.0)
+        cyl.setParam("radius", 20)
+        cyl.setParam("length", 400)
+        cyl.setParam("contrast", 1.0e-6)
+
+        ell = EllipticalCylinderModel()
+        ell.setParam("r_ratio", 1.0)
+        ell.setParam("r_minor", 20)
+        ell.setParam("cyl_theta", 1.57)
+        ell.setParam("cyl_phi", 0.0)
+        ell.setParam("length", 400)
+        ell.setParam("contrast", 1.0e-6)
+        
+        passed = True
+        for i_q in range(1, 30):
+            q = 0.025*i_q
+            ell_val = ell.run([q, phi])
+            cyl_val = cyl.run([q, phi])
+            if self.verbose:
+                print "Q=%g    Ell=%g    Cyl=%g   R=%g" %(q, ell_val, cyl_val, ell_val/cyl_val)
+            if math.fabs(ell_val-cyl_val)/cyl_val>0.05:
+                passed= False
+                
+        return passed
+
+        
+    def checkCylinder(self, points):
+        """
+            Compare the average over all orientations
+            of the main cylinder axis for a cylinder
+            and the elliptical cylinder with r_ratio = 1
+            
+            @param points: number of points to average over
+            @return: True if the test passed, otherwise False
+        """
+        from sans.models.CylinderModel import CylinderModel
+        
+        passed = True
+        
+        npts =points
+        model = EllipticalCylinderModel()
+        model.setParam('r_ratio', 1.0)
+        model.setParam("r_minor", 20)
+        model.setParam("cyl_theta", 1.57)
+        model.setParam("cyl_phi", 0.0)
+        model.setParam("length", 400)
+        model.setParam("contrast", 1.0e-6)
+        
+        cyl = CylinderModel()
+        cyl.setParam("cyl_theta", 1.57)
+        cyl.setParam("cyl_phi", 0.0)
+        cyl.setParam("radius", 20)
+        cyl.setParam("length", 400)
+        cyl.setParam("contrast", 1.0e-6)
+        
+        output_f = open("average_func.txt",'w')    
+        output_f.write("<q_average> <2d_average> <1d_average>\n")
+            
+        for i_q in range(1, 15):
+            q = 0.3/15.0*i_q
+            value = self.average_point_2D(model, q, npts)
+            
+            ana = cyl.run(q)
+            if q<0.3 and math.fabs(value-ana)/ana>0.05:
+                passed = False
+            output_f.write("%10g %10g %10g\n" % (q, value, ana))
+            if self.verbose:
+                print "Q=%g: %10g %10g %10g %10g" % (q, value, ana, value-ana, value/ana)
+        
+        output_f.close()
+        return passed
+        
+        
+
+    def average_point_2D(self, model, q, npts):
+        """
+            Average intensity over all orientations
+            of the main cylinder axis
+            
+            @param model: model to test
+            @param q: q-value
+            @param npts: number of points to average over
+            @return: average intensity
+        """
+        sum = 0.0
+        
+        for i_theta in range(npts):
+            theta = math.pi/npts*i_theta            
+            model.setParam('cyl_theta', theta)
+            
+            for j in range(npts):
+                model.setParam('cyl_phi', math.pi * 2.0 / npts * j)
+                     
+                if str(model.run([q, 0])).count("IN")>0:                        
+                    if self.verbose:
+                        print "ERROR", q, theta, math.pi * 2.0 / npts * j
+                else:
+                    sum += math.sin(theta)*model.run([q, 0])
+        
+        value = sum/npts/npts*math.pi/2.0
+        return value
+
+    
+if __name__ == '__main__':
+    select = 0
+
+    validator = Validate2D()
+    validator.verbose = True
+    
+    print "Testing Elliptical cylinder to 5%\n"
+    
+    if select == 0 or select == 1:
+        # Check that the scat intensity reduces to a cylinder
+        # for r_ratio = 1
+        print "Comparing to Cyl I(q,theta):", validator.checkCylinder2D(1.5)
+    elif select == 0 or select == 2:
+        print "Comparing average to Cyl I(q):", validator.checkCylinder(100)
+    elif select == 0 or select == 3:
+        ellcyl_passed = validator(100)
+    
+        
+        
+        
+  
+    
Index: sansmodels/src/sans/models/test/validate_2D_model.py
===================================================================
--- sansmodels/src/sans/models/test/validate_2D_model.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
+++ sansmodels/src/sans/models/test/validate_2D_model.py	(revision ae3ce4eaa593202ebf49122e196897c0d45a25b6)
@@ -0,0 +1,206 @@
+#!/usr/bin/env python
+""" 
+    Class to validate a given 2D model by averaging it
+    and comparing to 1D prediction.
+    
+    The equation used for averaging is:
+    
+        (integral dphi from 0 to 2pi)(integral dtheta from 0 to pi)
+             p(theta, phi) I(q) sin(theta) dtheta
+             
+        = (1/N_phi) (1/N_theta) (pi/2) (sum over N_phi) (sum over N_theta)
+             p(theta_i, phi_i) I(q) sin(theta_i)
+             
+    where p(theta, phi) is the probability distribution normalized to 4pi.
+    In the current case, we put p(theta, phi) = 1.
+    
+    The normalization factor results from:
+          2pi/N_phi      for the phi sum
+        x pi/N_theta     for the theta sum
+        x 1/(4pi)        because p is normalized to 4pi
+        --------------
+        = (1/N_phi) (1/N_theta) (pi/2)
+    
+    Note: Ellipsoid
+        Averaging the 2D ellipsoid scattering intensity give a slightly
+        different output than the 1D function from the IGOR library 
+        at hight Q (Q>0.3). This is due to the way the IGOR library 
+        averages, taking only 76 points in alpha, the angle between
+        the axis of the ellipsoid and the q vector.
+        
+    Note: Core-shell and sphere models are symmetric around
+        all axes and don't need to be tested in the following way.
+"""
+import sys, math
+from sans.models.SphereModel import SphereModel
+from sans.models.CylinderModel import CylinderModel
+from sans.models.EllipsoidModel import EllipsoidModel
+from sans.models.CoreShellCylinderModel import CoreShellCylinderModel
+
+
+class Validate2D:
+    """ 
+        Class to validate a given 2D model by averaging it
+        and comparing to 1D prediction.
+    """
+    
+    def __init__(self):
+        """ Initialization """
+        # Precision for the result comparison
+        self.precision = 0.000001
+        # Flag for end result
+        self.passed = True
+                
+    def __call__(self, model_class=CylinderModel, points = 101):
+        """ 
+            Perform test and produce output file
+            @param model_class: python class of the model to test
+        """
+        print "Averaging %s" % model_class.__name__
+        passed = True
+        
+        npts =points
+        model = model_class()
+        #model.setParam('scale', 1.0)
+        #model.setParam('contrast', 1.0)
+        
+        theta_label = 'cyl_theta'
+        if not model.params.has_key(theta_label):
+            theta_label = 'axis_theta'
+            
+        phi_label = 'cyl_phi'
+        if not model.params.has_key(phi_label):
+            phi_label = 'axis_phi'
+        
+        output_f = open("%s_avg.txt" % model.__class__.__name__,'w')    
+        output_f.write("<q_average> <2d_average> <1d_average>\n")
+            
+        for i_q in range(1, 30):
+            q = 0.025*i_q
+            sum = 0.0
+            for i_theta in range(npts):
+                theta = math.pi/npts*i_theta
+                
+                model.setParam(theta_label, theta)
+                
+                for j in range(npts):
+                    model.setParam(phi_label, math.pi * 2.0 / npts * j)
+                    if str(model.run([q, 0])).count("INF")>0:                        
+                        print "ERROR", q, theta, math.pi * 2.0 / npts * j
+                    sum += math.sin(theta)*model.run([q, 0])
+                    #sum += model.run([q, 0])
+            
+            value = sum/npts/npts*math.pi/2.0
+            ana = model.run(q)
+            if q<0.3 and (value-ana)/ana>0.05:
+                passed = False
+            output_f.write("%10g %10g %10g\n" % (q, value, ana))
+            print "Q=%g: %10g %10g %10g %10g" % (q, value, ana, value-ana, value/ana)
+        
+        output_f.close()
+        return passed
+    
+    def average(self, model_class=CylinderModel, points = 101):
+        """ 
+            Perform test and produce output file
+            @param model_class: python class of the model to test
+        """
+        print "Averaging %s" % model_class.__name__
+        passed = True
+        
+        npts =points
+        model = model_class()
+        model.setParam('scale', 1.0)
+        model.setParam('contrast', 1.0)
+        
+        theta_label = 'cyl_theta'
+        if not model.params.has_key(theta_label):
+            theta_label = 'axis_theta'
+            
+        phi_label = 'cyl_phi'
+        if not model.params.has_key(phi_label):
+            phi_label = 'axis_phi'
+        
+        output_f = open("%s_avg.txt" % model.__class__.__name__,'w')    
+        output_f.write("<q_average> <2d_average> <1d_average>\n")
+            
+        for i_q in range(1, 30):
+            q = 0.025*i_q
+            sum = 0.0
+            theta = 1.57
+            
+            model.setParam(theta_label, theta)
+            
+            for j in range(npts):
+                model.setParam(phi_label, math.pi / 2.0 / npts * j)
+                if str(model.run([q, 0])).count("INF")>0:                        
+                    print "ERROR", q, theta, math.pi * 2.0 / npts * j
+                #sum += math.sin(theta)*model.run([q, 0])
+                sum += math.sin(math.pi / 2.0 / npts * j)*model.run([q, 0])
+                #sum += model.run([q, 0])
+            
+            value = sum/npts
+            ana = model.run(q)
+            if q<0.3 and (value-ana)/ana>0.05:
+                passed = False
+            output_f.write("%10g %10g %10g\n" % (q, value, ana))
+            print "Q=%g: %10g %10g %10g %10g" % (q, value, ana, value-ana, value/ana)
+        
+        output_f.close()
+        return passed
+        
+    def test_non_oriented(self, model_class=SphereModel, points = 101):
+        """ 
+            Perform test and produce output file
+            @param model_class: python class of the model to test
+        """
+        print "Averaging %s" % model_class.__name__
+        passed = True
+        
+        npts =points
+        model = model_class()
+        #model.setParam('scale', 1.0)
+        #model.setParam('contrast', 1.0)
+        
+        output_f = open("%s_avg.txt" % model.__class__.__name__,'w')    
+        output_f.write("<q_average> <2d_average> <1d_average>\n")
+            
+        for i_q in range(1, 30):
+            q = 0.025*i_q
+            sum = 0.0
+            for i_theta in range(npts):
+                theta = math.pi/npts*i_theta
+                
+                for j in range(npts):
+                    if str(model.run([q, 0])).count("INF")>0:                        
+                        print "ERROR", q, theta, math.pi * 2.0 / npts * j
+                    sum += math.sin(theta)*model.run([q, 0])
+            
+            value = sum/npts/npts*math.pi/2.0
+            ana = model.run(q)
+            if q<0.3 and (value-ana)/ana>0.05:
+                passed = False
+            output_f.write("%10g %10g %10g\n" % (q, value, ana))
+            print "Q=%g: %10g %10g %10g %10g" % (q, value, ana, value-ana, value/ana)
+        
+        output_f.close()
+        return passed
+            
+        
+if __name__ == '__main__':
+    validator = Validate2D()
+    cyl_passed = validator(CylinderModel, points=201)
+    ell_passed = validator(EllipsoidModel, points=501)
+    cylcorehsell_passed = validator(CoreShellCylinderModel, points=201) 
+    sph_passed = validator.test_non_oriented(SphereModel, points=211) 
+        
+    print ""
+    print "Model             Passed"
+    print "Cylinder          %s" % cyl_passed
+    print "Ellipsoid         %s" % ell_passed
+    print "Core-shell cyl    %s" % cylcorehsell_passed
+    print "Sphere            %s" % sph_passed
+        
+        
+  
+    
