Index: sansmodels/src/sans/models/UnifiedPowerRgModel.py
===================================================================
--- sansmodels/src/sans/models/UnifiedPowerRgModel.py	(revision 29100cbd8615290396bfd34101d92623b01517ff)
+++ sansmodels/src/sans/models/UnifiedPowerRgModel.py	(revision 29100cbd8615290396bfd34101d92623b01517ff)
@@ -0,0 +1,251 @@
+   
+from sans.models.BaseComponent import BaseComponent
+from math import pow,exp, sqrt
+from scipy.special import erf
+max_level_n = 7
+class UnifiedPowerRgModel(BaseComponent):
+    """
+    This model is based on Exponential/Power-law fit method developed 
+    by G. Beaucage
+    """
+    def __init__(self, multfactor=1):
+        BaseComponent.__init__(self)
+        """
+        :param multfactor: number of levels in the model, assumes 0<= level# <=5.
+        """
+
+        ## Setting  model name model description
+        self.name = "UnifiedPowerRg"
+        self.description="""Multiple Levels of Unified Exponential/Power-law Method.
+        Up to Level 6 is provided.
+        Note; the additional Level 0 is an inverse linear function, 
+        i.e., y = scale/x + background.
+        The Level N is defined as
+        y = background + scale * Sum(1..N)[G_i*exp(-x^2*Rg_i^2/3)
+        + B_i/x^(power_i)*(erf(x*Rg_i/sqrt(6))^(3*power_i))].
+        Ref:
+        G. Beaucage (1995).  J. Appl. Cryst., vol. 28, p717-728.
+        G. Beaucage (1996).  J. Appl. Cryst., vol. 29, p134-146.
+        """
+        self.level_num = multfactor
+        ## Define parameters
+        self.params = {}
+
+        ## Parameter details [units, min, max]
+        self.details = {}
+        
+        # non-fittable parameters
+        self.non_fittable = []
+
+        # list of function in order of the function number 
+        self.fun_list = self._get_func_list()
+        ## dispersion
+        self._set_dispersion()
+        ## Define parameters
+        self._set_params()
+        
+        ## Parameter details [units, min, max]
+        self._set_details()
+        
+        #list of parameter that can be fitted
+        self._set_fixed_params()  
+        
+        ## functional multiplicity of the model
+        self.multiplicity_info = [max_level_n,"Level No.:",[],[]]
+    
+    def _unifiedpowerrg(self,x):
+        """
+        Scattering function
+        
+        :param x: q value(s)
+        :return answer: output of the function
+        """
+        # common parameters for the model functions
+        bkg = self.params['background'] 
+        scale = self.params['scale']
+        l_num = self.level_num
+        # set default output
+        answer = 0.0
+        # Set constant on lebel zero (special case)
+        if l_num == 0:
+            answer = scale / x + bkg
+            return answer
+        # rearrange the parameters for the given label no.
+        for ind in range(1,l_num+1):
+            # get exp term
+            exp_now = exp(-pow(x*self.params['Rg%s'% ind],2)/3.0)
+            # get erf term
+            erf_now = erf(x*self.params['Rg%s'% ind]/sqrt(6.0))
+            # get power term
+            pow_now = pow((erf_now*erf_now*erf_now/x),self.params['power%s'% ind])
+            # get next exp term only if it exists
+            try:
+                exp_next = exp(-pow(x*self.params['Rg%s'% (ind+1)],2)/3.0)
+            except:
+                exp_next = 1.0
+            # get to the calculation
+            answer += self.params['G%s'% ind]*exp_now + self.params['B%s'% ind]* \
+                            exp_next * pow_now
+        # take care of the singular point
+        if x == 0.0:
+            answer = 0.0
+            for ind in range(1,l_num+1):
+                answer += self.params['G%s'% ind]
+        # get scaled
+        answer *= scale
+        # add background
+        answer += bkg
+        return answer
+        
+    def _set_dispersion(self):
+        """
+        model dispersions
+        """ 
+        ##set dispersion from model 
+        self.dispersion = {}
+                    
+
+    def _set_params(self):
+        """
+        Concatenate the parameters of the model to create
+        this model parameters 
+        """
+        # common parameters for the model functions
+        self.params['background'] = 0.0
+        self.params['scale'] = 1.0
+        l_num = self.level_num
+        # rearrange the parameters for the given label no.
+        for ind in range(0,l_num+1):
+            if ind == 0:
+                continue
+            # multiple factor for higher labels
+            mult = 1.0
+            mul_pow = 1.0
+            if ind != l_num:
+                mult = 10.0 * 4.0/3.0
+                mul_pow = 2.0
+            # Set reasonably define default values that consistent 
+            # w/NIST for label #1
+            self.params['G%s'% ind] = 0.3*mult*pow(10, \
+                            (l_num+1 - float('%s'% ind)))
+            self.params['Rg%s'% ind] = 21.0/mult*pow(10, \
+                              (l_num - float('%s'% ind)))
+            self.params['B%s'% ind] = 6e-03/mult*pow(10, \
+                           -(l_num+1 - float('%s'% ind)))
+            self.params['power%s'% ind] = 2.0 * mul_pow
+            
+
+    def _set_details(self):
+        """
+        Concatenate details of the original model to create
+        this model details 
+        """
+        # common parameters for the model functions
+        self.details['background'] = ['[1/cm]', None, None]
+        self.details['scale'] = ['', None, None]
+        # rearrange the parameters for the given label no.
+        for ind in range(0,self.level_num+1):
+            if ind == 0:
+                continue
+            self.details['G%s'% ind] = ['[1/(cm.sr)]', None, None]
+            self.details['Rg%s'% ind] = ['[A]', None, None]
+            self.details['B%s'% ind] = ['[1/(cm.sr)]', None, None]
+            self.details['power%s'% ind] = ['', None, None]
+
+    
+    def _get_func_list(self):
+        """
+        Get the list of functions in each cases 
+        """
+        func_list = {}
+        return func_list
+        
+    def getProfile(self):
+        """
+        Get SLD profile 
+        
+        : return: None, No SLD profile supporting for this model
+        """
+        return None
+        
+    def setParam(self, name, value):
+        """ 
+        Set the value of a model parameter
+    
+        : param name: name of the parameter
+        : param value: value of the parameter
+        """
+        # set param to new model
+        self._setParamHelper(name, value)
+
+    def _setParamHelper(self, name, value):
+        """
+        Helper function to setParam
+        """
+
+        # Look for standard parameter
+        for item in self.params.keys():
+            if item.lower()==name.lower():
+                self.params[item] = value
+                return
+        
+        raise ValueError, "Model does not contain parameter %s" % name
+             
+   
+    def _set_fixed_params(self):
+        """
+        Fill the self.fixed list with the model fixed list
+        """
+        pass         
+
+                
+    def run(self, x = 0.0):
+        """ 
+        Evaluate the model
+        
+        : param x: input q-value (float or [float, float] as [r, theta])
+        : return: (DAB value)
+        """
+        if x.__class__.__name__ == 'list':
+            # Take absolute value of Q, since this model is really meant to
+            # be defined in 1D for a given length of Q
+            #qx = math.fabs(x[0]*math.cos(x[1]))
+            #qy = math.fabs(x[0]*math.sin(x[1]))
+            return self._unifiedpowerrg(x)
+        elif x.__class__.__name__ == 'tuple':
+            raise ValueError, "Tuples are not allowed as input to BaseComponent models"
+        else:
+            return self._unifiedpowerrg(x)
+
+
+        return self._unifiedpowerrg(x)
+
+    def runXY(self, x = 0.0):
+        """ 
+        Evaluate the model
+        
+        : param x: input q-value (float or [float, float] as [qx, qy])
+        : return: DAB value
+        """  
+        if x.__class__.__name__ == 'list':
+            q = math.sqrt(x[0]**2 + x[1]**2)
+            return self._unifiedpowerrg(x)
+        elif x.__class__.__name__ == 'tuple':
+            raise ValueError, "Tuples are not allowed as input to BaseComponent models"
+        else:
+            return self._unifiedpowerrg(x)
+
+    def calculate_ER(self):
+        """
+        """
+        # Not implemented!!!
+        pass
+    
+    def set_dispersion(self, parameter, dispersion):
+        """
+        Set the dispersion object for a model parameter
+        
+        : param parameter: name of the parameter [string]
+        :dispersion: dispersion object of type DispersionModel
+        """
+        pass
Index: sansmodels/src/sans/models/test/utest_extra_models.py
===================================================================
--- sansmodels/src/sans/models/test/utest_extra_models.py	(revision 8f20419d5be1d467bd17c7dfade28c96f90656ca)
+++ sansmodels/src/sans/models/test/utest_extra_models.py	(revision 29100cbd8615290396bfd34101d92623b01517ff)
@@ -287,6 +287,48 @@
         self.assertAlmostEqual(self.model.run(0.001), 273.742, 3)
         self.assertAlmostEqual(self.model.run(0.1501412), 0.040079, 6)
-        self.assertAlmostEqual(self.model.runXY(0.4425284), 0.00141167, 6)                             
-                                                                                                               
+        self.assertAlmostEqual(self.model.runXY(0.4425284), 0.00141167, 8)                             
+
+class TestUnifiedPowerRg(unittest.TestCase):
+    """
+    Unit tests for FractalCoreShell Model function
+    """
+    def setUp(self):
+        from sans.models.UnifiedPowerRgModel import UnifiedPowerRgModel
+        self.model1= UnifiedPowerRgModel(1)
+        self.model4= UnifiedPowerRgModel(4)
+        
+    def test1DLabel1(self):        
+        # Label #1
+        # the values are from Igor pro calculation  
+        # the different digits are due to precision of q values  
+        self.assertAlmostEqual(self.model1.run(0.001), 2.99956, 5)
+        self.assertAlmostEqual(self.model1.run(0.1501412), 0.126506, 6)
+        self.assertAlmostEqual(self.model1.runXY(0.4425284), 0.00306386, 8)                             
+ 
+    def test1DLabel4(self):        
+        # Label #4
+        # Set params consistent w/NIST
+        # No. 4 already same
+        # No. 3
+        self.model4.setParam('Rg3', 200)
+        self.model4.setParam('B3', 5e-06)
+        self.model4.setParam('G3', 400)
+        # No. 2
+        self.model4.setParam('Rg2', 600)
+        self.model4.setParam('B2', 2e-07)
+        self.model4.setParam('G2', 4000)
+        # No. 1
+        self.model4.setParam('Rg1', 2000)
+        self.model4.setParam('B1', 1e-08)
+        self.model4.setParam('G1', 40000)
+        
+        # the values are from Igor pro calculation  
+        # the different digits are due to precision of q values  
+        self.assertAlmostEqual(self.model4.run(0.001), 14778.4, 1)
+        self.assertAlmostEqual(self.model4.run(0.0301614), 7.88115, 4)
+        self.assertAlmostEqual(self.model4.run(0.1501412), 0.126864, 6)
+        self.assertAlmostEqual(self.model4.runXY(0.4425284), 0.00306386, 8)  
+                                                                                  
+                                                                                                                                                                                 
 if __name__ == '__main__':
     unittest.main()
