Index: .gitignore
===================================================================
--- .gitignore	(revision 66ebdd668828275e87ada92bedafaa22888a51db)
+++ .gitignore	(revision 4a82d4dd41f712a03b7f365ffe63e370cf57b792)
@@ -1,4 +1,5 @@
 /build/
 /dist/
+/logs/
 /*.csv
 *.pyc
@@ -16,2 +17,4 @@
 /.project
 /.pydevproject
+/.idea
+/sasmodels.egg-info/
Index: doc/rst_prolog
===================================================================
--- doc/rst_prolog	(revision 19dcb93366741a275baa8ad855ce8a310dca78f8)
+++ doc/rst_prolog	(revision 0a4628d166f9d3d4ed9811ec8882eea0e9675a8e)
@@ -60,2 +60,3 @@
 .. |g/cm^3| replace:: g\ |cdot|\ cm\ :sup:`-3`
 .. |fm^2| replace:: fm\ :sup:`2`
+.. |Ang*cm^-1| replace:: |Ang|\ |cdot|\ cm\ :sup:`-1`
Index: multi_compare.sh
===================================================================
--- multi_compare.sh	(revision 5753e4ebc8ad092cc172a8a013acd92a6a19e86a)
+++ multi_compare.sh	(revision 0a4628d166f9d3d4ed9811ec8882eea0e9675a8e)
@@ -1,3 +1,3 @@
-#!/bin/sh
+#!/bin/bash
 
 sasview=( ../sasview/build/lib.* )
Index: sasmodels/convert.py
===================================================================
--- sasmodels/convert.py	(revision 3964f92c0d8ef50968fdc442ddace0815434fff6)
+++ sasmodels/convert.py	(revision 34d6cabda48bc12af0423cded9c76c0e4bf2572b)
@@ -10,4 +10,5 @@
     'broad_peak',
     'two_lorentzian',
+    "two_power_law",
     'gel_fit',
     'gauss_lorentz_gel',
@@ -151,4 +152,7 @@
         pars['string_thickness_pd_n'] = 0
         pars['number_of_pearls_pd_n'] = 0
+    elif name == 'line':
+        pars['scale'] = 1
+        pars['background'] = 0
     elif name == 'rpa':
         pars['case_num'] = int(pars['case_num'])
Index: sasmodels/generate.py
===================================================================
--- sasmodels/generate.py	(revision eafc9fa8b200a3b754e7dafb960c8891361eacef)
+++ sasmodels/generate.py	(revision 0a4628d166f9d3d4ed9811ec8882eea0e9675a8e)
@@ -233,4 +233,5 @@
     "degrees": "degree",
     "1/cm": "|cm^-1|",
+    "Ang/cm": "|Ang*cm^-1|",
     "": "None",
     }
Index: sasmodels/models/core_shell_sphere.c
===================================================================
--- sasmodels/models/core_shell_sphere.c	(revision edc9f8d2c5449514c0206c9ed35db99d865b35b6)
+++ sasmodels/models/core_shell_sphere.c	(revision edc9f8d2c5449514c0206c9ed35db99d865b35b6)
@@ -0,0 +1,31 @@
+double form_volume(double radius, double thickness);
+double Iq(double q, double radius, double thickness, double core_sld, double shell_sld, double solvent_sld);
+double Iqxy(double qx, double qy, double radius, double thickness, double core_sld, double shell_sld, double solvent_sld);
+
+
+double Iq(double q, double radius, double thickness, double core_sld, double shell_sld, double solvent_sld) {
+    // Core first, then add in shell
+    const double core_qr = q * radius;
+    const double core_contrast = core_sld - shell_sld;
+    const double core_bes = sph_j1c(core_qr);
+    const double core_volume = 4.0 * M_PI / 3.0 * radius * radius * radius;
+    double f = core_volume * core_bes * core_contrast;
+
+    // Now the shell
+    const double shell_qr = q * (radius + thickness);
+    const double shell_contrast = shell_sld - solvent_sld;
+    const double shell_bes = sph_j1c(shell_qr);
+    const double shell_volume = 4.0 * M_PI / 3.0 * pow((radius + thickness), 3);
+    f += shell_volume * shell_bes * shell_contrast;
+    return f * f * 1.0e-4;
+}
+
+double Iqxy(double qx, double qy, double radius, double thickness, double core_sld, double shell_sld, double solvent_sld) {
+    const double q = sqrt(qx*qx+qy*qy);
+    return Iq(q, radius, thickness, core_sld, shell_sld, solvent_sld);
+}
+
+double form_volume(double radius, double thickness)
+{
+    return 4.0 * M_PI / 3.0 * pow((radius + thickness), 3);
+}
Index: sasmodels/models/core_shell_sphere.py
===================================================================
--- sasmodels/models/core_shell_sphere.py	(revision 8c9dbc9161c033ba16aba0b54213deb314c953a0)
+++ sasmodels/models/core_shell_sphere.py	(revision 8c9dbc9161c033ba16aba0b54213deb314c953a0)
@@ -0,0 +1,110 @@
+r"""
+This model provides the form factor, $P(q)$, for a spherical particle with a core-shell structure.
+The form factor is normalized by the particle volume.
+
+Definition
+----------
+
+The 1D scattering intensity is calculated in the following way (Guinier, 1955)
+
+.. math::
+
+    P(q) = \frac{\text{scale}}{V} F^2(q) + \text{background}
+
+where
+
+.. math::
+    F^2(q)=\frac{3}{V_s}\left[V_c(\rho_c-\rho_s)\frac{\sin(qr_c)-qr_c\cos(qr_c)}{(qr_c)^3}+
+    V_s(\rho_s-\rho_{solv})\frac{\sin(qr_s)-qr_s\cos(qr_s)}{(qr_s)^3}\right]
+
+where $V_s$ is the volume of the outer shell, $V_c$ is
+the volume of the core, $r_s$ is the radius of the shell, $r_c$ is the radius of the
+core, $\rho_c$ is the scattering length density of the core, $\rho_s$ is the scattering length
+density of the shell, $\rho_{solv}$ is the scattering length density of the solvent.
+
+The 2D scattering intensity is the same as $P(q)$ above, regardless of the
+orientation of the $q$ vector.
+
+NB: The outer most radius (ie, = radius + thickness) is used as the
+effective radius for $S(Q)$ when $P(Q) \cdot S(Q)$ is applied.
+
+Reference
+---------
+
+A Guinier and G Fournet, *Small-Angle Scattering of X-Rays*, John Wiley and Sons, New York, (1955)
+
+Validation
+----------
+
+Validation of our code was done by comparing the output of the 1D model to the output of
+the software provided by NIST (Kline, 2006). Figure 1 shows a comparison of the output of
+our model and the output of the NIST software.
+
+.. image:: img/core_shell_sphere_1d.jpg
+
+    Figure 1: Comparison of the SasView scattering intensity for a core-shell sphere with
+    the output of the NIST SANS analysis software. The parameters were set to:
+    *scale* = 1.0, *radius* = 60 , *contrast* = 1e-6 |Ang^-2|, and
+    *background* = 0.001 |cm^-1|.
+"""
+
+from numpy import pi, inf
+
+name = "core_shell_sphere"
+title = "Form factor for a monodisperse spherical particle with particle with a core-shell structure."
+description = """
+    F^2(q) = 3/V_s [V_c (core_sld-shell_sld) (sin(q*radius)-q*radius*cos(q*radius))/(q*radius)^3 
+                   + V_s (shell_sld-solvent_sld) (sin(q*r_s)-q*r_s*cos(q*r_s))/(q*r_s)^3]
+
+            V_s: Volume of the sphere shell
+            V_c: Volume of the sphere core
+            r_s: Shell radius = radius + thickness
+"""
+category = "shape:sphere"
+
+# pylint: disable=bad-whitespace, line-too-long
+#             ["name", "units", default, [lower, upper], "type","description"],
+parameters = [["radius",      "Ang",        60.0, [0, inf],    "volume", "Sphere core radius"],
+              ["thickness",   "Ang",        10.0, [0, inf],    "volume", "Sphere shell thickness"],
+              ["core_sld",    "1e-6/Ang^2", 1.0,  [-inf, inf], "",       "Sphere core scattering length density"],
+              ["shell_sld",   "1e-6/Ang^2", 2.0,  [-inf, inf], "",       "Sphere shell scattering length density"],
+              ["solvent_sld", "1e-6/Ang^2", 3.0,  [-inf, inf],  "",      "Solvent scattering length density"]]
+# pylint: enable=bad-whitespace, line-too-long
+
+source = ["lib/sph_j1c.c", "core_shell_sphere.c"]
+
+demo = dict(scale=1, background=0, radius=60, thickness=10,
+            core_sld=1.0, shell_sld=2.0, solvent_sld=0.0)
+
+oldname = 'CoreShellModel'
+oldpars = {}
+
+def ER(radius, thickness):
+    """
+        Equivalent radius
+        @param radius: core radius
+        @param thickness: shell thickness
+    """
+    return radius + thickness
+
+def VR(radius, thickness):
+    """
+        Volume ratio
+        @param radius: core radius
+        @param thickness: shell thickness
+    """
+    whole = 4.0 * pi / 3.0 * pow((radius + thickness), 3)
+    core = 4.0 * pi / 3.0 * radius * radius * radius
+    return whole, whole - core
+
+tests = [[{'radius': 20.0, 'thickness': 10.0}, 'ER', 30.0],
+         [{'radius': 20.0, 'thickness': 10.0}, 'VR', 0.703703704],
+
+         # The SasView test result was 0.00169, with a background of 0.001
+         [{'radius': 60.0,
+           'thickness': 10.0,
+           'core_sld': 1.0,
+           'shell_sld':2.0,
+           'solvent_sld':3.0,
+           'background':0.0
+          }, 0.4, 0.000698838]]
Index: sasmodels/models/elliptical_cylinder.c
===================================================================
--- sasmodels/models/elliptical_cylinder.c	(revision a8b3cdb60df4f2e22c9b3c5c3110bb51b7dba2c6)
+++ sasmodels/models/elliptical_cylinder.c	(revision a8b3cdb60df4f2e22c9b3c5c3110bb51b7dba2c6)
@@ -0,0 +1,170 @@
+double form_volume(double r_minor, double r_ratio, double length);
+double Iq(double q, double r_minor, double r_ratio, double length,
+          double sld, double solvent_sld);
+double Iqxy(double qx, double qy, double r_minor, double r_ratio, double length,
+            double sld, double solvent_sld, double theta, double phi, double psi);
+
+
+double _elliptical_cylinder_kernel(double q, double r_minor, double r_ratio, double theta);
+
+double _elliptical_cylinder_kernel(double q, double r_minor, double r_ratio, double theta)
+{
+    // This is the function LAMBDA1^2 in Feigin's notation
+    // q is the q-value for the calculation (1/A)
+    // r_minor is the transformed radius"a" in Feigin's notation
+    // r_ratio is the ratio (major radius)/(minor radius) of the Ellipsoid [=] ---
+    // theta is the dummy variable of the integration
+
+    double retval,arg;
+
+    arg = q*r_minor*sqrt((1.0+r_ratio*r_ratio)/2+(1.0-r_ratio*r_ratio)*cos(theta)/2);
+    if (arg == 0.0){
+        retval = 1.0;
+    }else{
+        retval = 2.0*NR_BessJ1(arg)/arg;
+    }
+    return retval*retval ;
+}
+
+
+double form_volume(double r_minor, double r_ratio, double length)
+{
+    return M_PI * r_minor * r_minor * r_ratio * length;
+}
+
+double Iq(double q, double r_minor, double r_ratio, double length,
+          double sld, double solvent_sld) {
+
+    const int nordi=76; //order of integration
+    const int nordj=20;
+    double va,vb;       //upper and lower integration limits
+    double summ,zi,yyy,answer;         //running tally of integration
+    double summj,vaj,vbj,zij,arg,si;            //for the inner integration
+
+    // orientational average limits
+    va = 0.0;
+    vb = 1.0;
+    // inner integral limits
+    vaj=0.0;
+    vbj=M_PI;
+
+    //initialize integral
+    summ = 0.0;
+
+    const double delrho = sld - solvent_sld;
+
+    for(int i=0;i<nordi;i++) {
+        //setup inner integral over the ellipsoidal cross-section
+        summj=0;
+        zi = ( Gauss76Z[i]*(vb-va) + va + vb )/2.0;     //the "x" dummy
+        arg = r_minor*sqrt(1.0-zi*zi);
+        for(int j=0;j<nordj;j++) {
+            //20 gauss points for the inner integral
+            zij = ( Gauss20Z[j]*(vbj-vaj) + vaj + vbj )/2.0;        //the "y" dummy
+            yyy = Gauss20Wt[j] * _elliptical_cylinder_kernel(q, arg, r_ratio, zij);
+            summj += yyy;
+        }
+        //now calculate the value of the inner integral
+        answer = (vbj-vaj)/2.0*summj;
+        //divide integral by Pi
+        answer /= M_PI;
+
+        //now calculate outer integral
+        arg = q*length*zi/2.0;
+        if (arg == 0.0){
+            si = 1.0;
+        }else{
+            si = sin(arg) * sin(arg) / arg / arg;
+        }
+        yyy = Gauss76Wt[i] * answer * si;
+        summ += yyy;
+    }
+
+    answer = (vb-va)/2.0*summ;
+    // Multiply by contrast^2
+    answer *= delrho*delrho;
+
+    const double vol = form_volume(r_minor, r_ratio, length);
+    return answer*vol*vol*1e-4;
+}
+
+
+double Iqxy(double qx, double qy, double r_minor, double r_ratio, double length,
+            double sld, double solvent_sld, double theta, double phi, double psi) {
+    const double _theta = theta * M_PI / 180.0;
+    const double _phi = phi * M_PI / 180.0;
+    const double _psi = psi * M_PI / 180.0;
+    const double q = sqrt(qx*qx+qy*qy);
+    const double q_x = qx/q;
+    const double q_y = qy/q;
+
+    //Cylinder orientation
+    double cyl_x = cos(_theta) * cos(_phi);
+    double cyl_y = sin(_theta);
+
+    //cyl_z = -cos(_theta) * sin(_phi);
+
+    // q vector
+    //q_z = 0;
+
+    // Note: cos(alpha) = 0 and 1 will get an
+    // undefined value from CylKernel
+    //alpha = acos( cos_val );
+
+    //ellipse orientation:
+    // the elliptical corss section was transformed and projected
+    // into the detector plane already through sin(alpha)and furthermore psi remains as same
+    // on the detector plane.
+    // So, all we need is to calculate the angle (nu) of the minor axis of the ellipse wrt
+    // the wave vector q.
+
+    //x- y- component on the detector plane.
+    const double ella_x =  -cos(_phi)*sin(_psi) * sin(_theta)+sin(_phi)*cos(_psi);
+    const double ella_y =  sin(_psi)*cos(_theta);
+    const double ellb_x =  -sin(_theta)*cos(_psi)*cos(_phi)-sin(_psi)*sin(_phi);
+    const double ellb_y =  cos(_theta)*cos(_psi);
+
+    // Compute the angle btw vector q and the
+    // axis of the cylinder
+    double cos_val = cyl_x*q_x + cyl_y*q_y;// + cyl_z*q_z;
+
+    // calculate the axis of the ellipse wrt q-coord.
+    double cos_nu = ella_x*q_x + ella_y*q_y;
+    double cos_mu = ellb_x*q_x + ellb_y*q_y;
+
+    // The following test should always pass
+    if (fabs(cos_val)>1.0) {
+      //printf("cyl_ana_2D: Unexpected error: cos(alpha)>1\n");
+      cos_val = 1.0;
+    }
+    if (fabs(cos_nu)>1.0) {
+      //printf("cyl_ana_2D: Unexpected error: cos(nu)>1\n");
+      cos_nu = 1.0;
+    }
+    if (fabs(cos_mu)>1.0) {
+      //printf("cyl_ana_2D: Unexpected error: cos(nu)>1\n");
+      cos_mu = 1.0;
+    }
+
+    const double r_major = r_ratio * r_minor;
+    const double qr = q*sqrt( r_major*r_major*cos_nu*cos_nu + r_minor*r_minor*cos_mu*cos_mu );
+    const double qL = q*length*cos_val/2.0;
+
+    double Be;
+    if (qr==0){
+      Be = 0.5;
+    }else{
+      Be = NR_BessJ1(qr)/qr;
+    }
+
+    double Si;
+    if (qL==0){
+      Si = 1.0;
+    }else{
+      Si = sin(qL)/qL;
+    }
+
+    const double k = 2.0 * Be * Si;
+    const double vol = form_volume(r_minor, r_ratio, length);
+    return (sld - solvent_sld) * (sld - solvent_sld) * k * k *vol*vol*1.0e-4;
+}
Index: sasmodels/models/elliptical_cylinder.py
===================================================================
--- sasmodels/models/elliptical_cylinder.py	(revision b7c2fcea5b553bdc935287b253bc4a360bf6cf0c)
+++ sasmodels/models/elliptical_cylinder.py	(revision b7c2fcea5b553bdc935287b253bc4a360bf6cf0c)
@@ -0,0 +1,146 @@
+# pylint: disable=line-too-long
+r"""
+This function calculates the scattering from an elliptical cylinder.
+
+Definition for 2D (orientated system)
+-------------------------------------
+
+The angles |theta| and |phi| define the orientation of the axis of the cylinder. The angle |bigpsi| is defined as the
+orientation of the major axis of the ellipse with respect to the vector *Q*\ . A gaussian polydispersity can be added
+to any of the orientation angles, and also for the minor radius and the ratio of the ellipse radii.
+
+.. image:: img/elliptical_cylinder_geometry.gif
+
+    *Figure.* *a* = *r_minor* and |nu|\ :sub:`n` = $r_ratio$ (i.e., $r_major / r_minor$).
+
+The function calculated is
+
+.. math::
+    I(\mathbf{q})=\frac{1}{V_{cyl}}\int{d\psi}\int{d\phi}\int{p(\theta,\phi,\psi)F^2(\mathbf{q},\alpha,\psi)\sin(\theta)d\theta}
+
+with the functions
+
+.. math::
+    F(\mathbf{q},\alpha,\psi)=2\frac{J_1(a)\sin(b)}{ab}
+    \\
+    a = \mathbf{q}\sin(\alpha)\left[ r^2_{major}\sin^2(\psi)+r^2_{minor}\cos(\psi) \right]^{1/2}
+    \\
+    b=\mathbf{q}\frac{L}{2}\cos(\alpha)
+
+and the angle |bigpsi| is defined as the orientation of the major axis of the ellipse with respect to the vector $\vec q$ .
+The angle $\alpha$ is the angle between the axis of the cylinder and $\vec q$.
+
+
+Definition for 1D (no preferred orientation)
+--------------------------------------------
+
+The form factor is averaged over all possible orientation before normalized by the particle volume
+
+.. math::
+    P(q) = scale  <F^2> / V
+
+The returned value is scaled to units of |cm^-1|.
+
+To provide easy access to the orientation of the elliptical cylinder, we define the axis of the cylinder using two
+angles |theta|, |phi| and |bigpsi|. As for the case of the cylinder, the angles |theta| and |phi| are defined on
+Figure 2 of CylinderModel. The angle |bigpsi| is the rotational angle around its own long_c axis against the *q* plane.
+For example, |bigpsi| = 0 when the *r_minor* axis is parallel to the *x*\ -axis of the detector.
+
+All angle parameters are valid and given only for 2D calculation; ie, an oriented system.
+
+.. image:: img/elliptical_cylinder_geometry_2d.jpg
+
+    *Figure. Definition of angles for 2D*
+
+.. image:: img/core_shell_bicelle_fig2.jpg
+
+    *Figure. Examples of the angles for oriented elliptical cylinders against the detector plane.*
+
+NB: The 2nd virial coefficient of the cylinder is calculated based on the averaged radius (= sqrt(*r_minor*\ :sup:`2` \* *r_ratio*))
+and length values, and used as the effective radius for *S(Q)* when *P(Q)* \* *S(Q)* is applied.
+
+
+.. image:: img/elliptical_cylinder_comparison_1d.jpg
+
+    *Figure. 1D plot using the default values (w/1000 data point).*
+
+Validation
+----------
+
+Validation of our code was done by comparing the output of the 1D calculation to the angular average of the output of
+the 2D calculation over all possible angles. The figure below shows the comparison where the solid dot refers to
+averaged 2D values while the line represents the result of the 1D calculation (for the 2D averaging, values of 76, 180,
+and 76 degrees are taken for the angles of |theta|, |phi|, and |bigpsi| respectively).
+
+.. image:: img/elliptical_cylinder_validation_1d.gif
+
+    *Figure. Comparison between 1D and averaged 2D.*
+
+In the 2D average, more binning in the angle |phi| is necessary to get the proper result. The following figure shows
+the results of the averaging by varying the number of angular bins.
+
+.. image:: img/elliptical_cylinder_averaging.gif
+
+    *Figure. The intensities averaged from 2D over different numbers of bins and angles.*
+
+Reference
+---------
+
+L A Feigin and D I Svergun, *Structure Analysis by Small-Angle X-Ray and Neutron Scattering*, Plenum,
+New York, (1987)
+"""
+
+import math
+from numpy import pi, inf
+
+name = "elliptical_cylinder"
+title = "Form factor for an elliptical cylinder."
+description = """
+    Form factor for an elliptical cylinder.
+    See L A Feigin and D I Svergun, Structure Analysis by Small-Angle X-Ray and Neutron Scattering, Plenum, New York, (1987).
+"""
+category = "shape:cylinder"
+
+# pylint: disable=bad-whitespace, line-too-long
+#             ["name", "units", default, [lower, upper], "type","description"],
+parameters = [["r_minor",     "Ang",        20.0,  [0, inf],    "volume",      "Ellipse minor radius"],
+              ["r_ratio",     "",           1.5,   [1, inf],    "volume",      "Ratio of major radius over minor radius"],
+              ["length",      "Ang",        400.0, [1, inf],    "volume",      "Length of the cylinder"],
+              ["sld",         "1e-6/Ang^2", 4.0,   [-inf, inf], "",            "Cylinder scattering length density"],
+              ["solvent_sld", "1e-6/Ang^2", 1.0,   [-inf, inf], "",            "Solvent scattering length density"],
+              ["theta",       "degrees",    90.0,  [-360, 360], "orientation", "In plane angle"],
+              ["phi",         "degrees",    0,     [-360, 360], "orientation", "Out of plane angle"],
+              ["psi",         "degrees",    0,     [-360, 360], "orientation", "Major axis angle relative to Q"]]
+
+# pylint: enable=bad-whitespace, line-too-long
+
+source = ["lib/nr_bess_j1.c", "lib/gauss76.c", "lib/gauss20.c", "elliptical_cylinder.c"]
+
+demo = dict(scale=1, background=0, r_minor=100, r_ratio=1.5, length=400.0,
+            sld=4.0, solvent_sld=1.0, theta=10.0, phi=20, psi=30, theta_pd=10, phi_pd=2, psi_pd=3)
+
+oldname = 'EllipticalCylinderModel'
+oldpars = dict(theta='cyl_theta', phi='cyl_phi', psi='cyl_psi', sld='sldCyl', solvent_sld='sldSolv')
+
+def ER(r_minor, r_ratio, length):
+    """
+        Equivalent radius
+        @param r_minor: Ellipse minor radius
+        @param r_ratio: Ratio of major radius over minor radius
+        @param length: Length of the cylinder
+    """
+    radius = math.sqrt(r_minor * r_minor * r_ratio)
+    ddd = 0.75 * radius * (2 * radius * length + (length + radius) * (length + pi * radius))
+    return 0.5 * (ddd) ** (1. / 3.)
+
+tests = [[{'r_minor': 20.0, 'r_ratio': 1.5, 'length':400.0}, 'ER', 79.89245454155024],
+         [{'r_minor': 20.0, 'r_ratio': 1.2, 'length':300.0}, 'VR', 1],
+
+         # The SasView test result was 0.00169, with a background of 0.001
+         [{'r_minor': 20.0,
+           'r_ratio': 1.5,
+           'sld': 4.0,
+           'length':400.0,
+           'solvent_sld':1.0,
+           'background':0.0
+          }, 0.001, 675.504402]]
Index: sasmodels/models/hardsphere.py
===================================================================
--- sasmodels/models/hardsphere.py	(revision 13ed84c3c71257d2df9c2ed4b4e98c0bfa4d7d69)
+++ sasmodels/models/hardsphere.py	(revision 093f7545303dc8a4e5074424203e44456c438f42)
@@ -35,6 +35,6 @@
 from numpy import inf
 
-name = "hardsphere"
-title = "Hard sphere structure factor, with Percus-Yevick closure"
+name = "hardsphere_fish"
+title = "Hard sphere structure factor from FISH, with Percus-Yevick closure"
 description = """\
     [Hard sphere structure factor, with Percus-Yevick closure]
@@ -55,5 +55,4 @@
                "volume fraction of hard spheres"],
              ]
-single = False
 
 # No volume normalization despite having a volume parameter
@@ -64,33 +63,49 @@
 
 Iq = """
-    double denom,dnum,alpha,beta,gamm,a,asq,ath,afor,rca,rsa;
-    double calp,cbeta,cgam,prefac,c,vstruc;
-    double struc;
+      double D,A,B,G,X,X2,X4,S,C,FF,HARDSPH;
 
-    //  compute constants
-    denom = pow((1.0-volfraction),4);
-    dnum = pow((1.0 + 2.0*volfraction),2);
-    alpha = dnum/denom;
-    beta = -6.0*volfraction*pow((1.0 + volfraction/2.0),2)/denom;
-    gamm = 0.50*volfraction*dnum/denom;
-    //
-    //  calculate the structure factor
-    //
-    a = 2.0*q*effect_radius;
-    asq = a*a;
-    ath = asq*a;
-    afor = ath*a;
-    SINCOS(a,rsa,rca);
-    //rca = cos(a);
-    //rsa = sin(a);
-    calp = alpha*(rsa/asq - rca/a);
-    cbeta = beta*(2.0*rsa/asq - (asq - 2.0)*rca/ath - 2.0/ath);
-    cgam = gamm*(-rca/a + (4.0/a)*((3.0*asq - 6.0)*rca/afor + (asq - 6.0)*rsa/ath + 6.0/afor));
-    prefac = -24.0*volfraction/a;
-    c = prefac*(calp + cbeta + cgam);
-    vstruc = 1.0/(1.0-c);
-    struc = vstruc;
+      if(fabs(effect_radius) < 1.E-12) {
+               HARDSPH=1.0;
+               return(HARDSPH);
+      }
+      D=pow((1.-volfraction),2);
+      A=pow((1.+2.*volfraction)/D, 2);
+      X=fabs(q*effect_radius*2.0);
 
-    return(struc);
+      if(X < 5.E-06) {
+                 HARDSPH=1./A;
+                 return(HARDSPH);
+      }
+      X2=pow(X,2);
+      X4=pow(X2,2);
+      B=-6.*volfraction* pow((1.+0.5*volfraction)/D ,2);
+      G=0.5*volfraction*A;
+
+      if(X < 0.2) {
+      // use Taylor series expansion for small X, IT IS VERY PICKY ABOUT THE X CUT OFF VALUE, ought to be lower in double. 
+      // No obvious way to rearrange the equations to avoid needing a very high number of significant figures. 
+      // Series expansion found using Mathematica software. Numerical test in .xls showed terms to X^2 are sufficient 
+      // for 5 or 6 significant figures but I put the X^4 one in anyway 
+            FF = 8*A +6*B + 4*G - (0.8*A +2.0*B/3.0 +0.5*G)*X2 +(A/35. +B/40. +G/50.)*X4;
+            // combining the terms makes things worse at smallest Q in single precision
+            //FF = (8-0.8*X2)*A +(3.0-X2/3.)*2*B + (4+0.5*X2)*G +(A/35. +B/40. +G/50.)*X4;
+            // note that G = -volfraction*A/2, combining this makes no further difference at smallest Q
+            //FF = (8 +2.*volfraction + ( volfraction/4. -0.8 +(volfraction/100. -1./35.)*X2 )*X2 )*A  + (3.0 -X2/3. +X4/40)*2*B;
+            HARDSPH= 1./(1. + volfraction*FF );
+            return(HARDSPH);
+      }
+      SINCOS(X,S,C);
+
+// RKH Feb 2016, use version from FISH code as it is better than original sasview one at small Q in single precision
+      FF=A*(S-X*C)/X + B*(2.*X*S -(X2-2.)*C -2.)/X2 + G*( (4.*X2*X -24.*X)*S -(X4 -12.*X2 +24.)*C +24. )/X4;
+      HARDSPH= 1./(1. + 24.*volfraction*FF/X2 );
+
+// rearrange the terms, is now about same as sasmodels
+//     FF=A*(S/X-C) + B*(2.*S/X - C +2.0*(C-1.0)/X2) + G*( (4./X -24./X3)*S -(1.0 -12./X2 +24./X4)*C +24./X4 );
+//     HARDSPH= 1./(1. + 24.*volfraction*FF/X2 );
+// remove 1/X2 from final line, take more powers of X inside the brackets, stil bad
+//      FF=A*(S/X3-C/X2) + B*(2.*S/X3 - C/X2 +2.0*(C-1.0)/X4) + G*( (4./X -24./X3)*S -(1.0 -12./X2 +24./X4)*C +24./X4 )/X2;
+//      HARDSPH= 1./(1. + 24.*volfraction*FF );
+      return(HARDSPH);
    """
 
@@ -106,8 +121,8 @@
 oldname = 'HardsphereStructure'
 oldpars = dict()
-
+# Q=0.001 is in the Taylor series, low Q part, so add Q=0.1, assuming double precision sasview is correct
 tests = [
         [ {'scale': 1.0, 'background' : 0.0, 'effect_radius' : 50.0, 'volfraction' : 0.2,
-           'effect_radius_pd' : 0}, [0.001], [0.209128]]
+           'effect_radius_pd' : 0}, [0.001,0.1], [0.209128,0.930587]]
         ]
 
Index: sasmodels/models/lib/nr_bess_j1.c
===================================================================
--- sasmodels/models/lib/nr_bess_j1.c	(revision a8b3cdb60df4f2e22c9b3c5c3110bb51b7dba2c6)
+++ sasmodels/models/lib/nr_bess_j1.c	(revision a8b3cdb60df4f2e22c9b3c5c3110bb51b7dba2c6)
@@ -0,0 +1,28 @@
+double NR_BessJ1(double x);
+double NR_BessJ1(double x)
+{
+    double ax,z;
+    double xx,y,ans,ans1,ans2;
+
+    if ((ax=fabs(x)) < 8.0) {
+        y=x*x;
+        ans1=x*(72362614232.0+y*(-7895059235.0+y*(242396853.1
+                                                  +y*(-2972611.439+y*(15704.48260+y*(-30.16036606))))));
+        ans2=144725228442.0+y*(2300535178.0+y*(18583304.74
+                                               +y*(99447.43394+y*(376.9991397+y*1.0))));
+        ans=ans1/ans2;
+    } else {
+        z=8.0/ax;
+        y=z*z;
+        xx=ax-2.356194491;
+        ans1=1.0+y*(0.183105e-2+y*(-0.3516396496e-4
+                                   +y*(0.2457520174e-5+y*(-0.240337019e-6))));
+        ans2=0.04687499995+y*(-0.2002690873e-3
+                              +y*(0.8449199096e-5+y*(-0.88228987e-6
+                                                     +y*0.105787412e-6)));
+        ans=sqrt(0.636619772/ax)*(cos(xx)*ans1-z*sin(xx)*ans2);
+        if (x < 0.0) ans = -ans;
+    }
+
+    return(ans);
+}
Index: sasmodels/models/line.py
===================================================================
--- sasmodels/models/line.py	(revision e66075fa234147971eb4571c9a16e6f23eb35e29)
+++ sasmodels/models/line.py	(revision e66075fa234147971eb4571c9a16e6f23eb35e29)
@@ -0,0 +1,108 @@
+r"""
+This model calculates intensity using simple linear function
+
+Definition
+----------
+
+The scattering intensity $I(q)$ is calculated as
+
+.. math::
+
+    I(q) = A + B \cdot q
+
+.. note::
+    For 2D plots intensity has different definition than other shape independent models
+.. math::
+    I(q) = I(qx) \cdot I(qy)
+
+.. figure:: None
+
+References
+----------
+
+None.
+
+"""
+from numpy import inf
+
+name = "line"
+title = "Line model"
+description = """\
+      I(q) = A + B*q
+
+      List of default parameters:
+      A = intercept
+      B = slope
+      """
+category = "shape-independent"
+
+# pylint: disable=bad-whitespace, line-too-long
+#             ["name", "units", default, [lower, upper], "type", "description"],
+parameters = [["intercept", "1/cm",   1.0, [-inf, inf], "", "intercept in linear model"],
+              ["slope",     "Ang/cm", 1.0, [-inf, inf], "", "slope in linear model"],
+             ]
+# pylint: enable=bad-whitespace, line-too-long
+
+def Iq(q, intercept, slope):
+    """
+    :param q:           Input q-value
+    :param intercept:   Intrecept in linear model
+    :param slope:       Slope in linear model
+    :return:            Calculated Intensity
+    """
+    inten = intercept + slope*q
+    # TODO: In SasView code additional formula for list has been specified.
+    # if inten(x) = intercept + slope*x:
+    # then if q is a list, Iq=inten(x[0]*math.cos(x[1]))*inten(x[0]*math.sin(x[1]))
+    return inten
+
+Iq.vectorized = True # Iq accepts an array of q values
+
+def Iqxy(qx, qy, *args):
+    """
+    :param qx:   Input q_x-value
+    :param qy:   Input q_y-value
+    :param args: Remaining arguments
+    :return:     2D-Intensity
+    """
+    # TODO: SasView documention lists 2D intensity as Iq(qx)*Iq(qy) but code says:
+    # return self._line(x[1])
+    return Iq(qx, *args)*Iq(qy, *args)
+
+Iqxy.vectorized = True  # Iqxy accepts an array of qx, qy values
+
+demo = dict(scale=1.0, background=0, intercept=1.0, slope=1.0)
+
+oldname = "LineModel"
+oldpars = dict(intercept='A', slope='B', background=None, scale=None)
+
+tests = [
+
+    [{'intercept':   1.0,
+      'slope': 1.0,
+     }, 1.0, 2.0],
+
+    [{'intercept':   1.0,
+      'slope': 1.0,
+     }, 0.0, 1.0],
+
+    [{'intercept':   1.0,
+      'slope': 1.0,
+     }, 0.4, 1.4],
+
+    [{'intercept':   1.0,
+      'slope': 1.0,
+     }, 1.3, 2.3],
+
+    [{'intercept':   1.0,
+      'slope': 1.0,
+     }, 0.5, 1.5],
+
+    [{'intercept':   1.0,
+      'slope': 1.0,
+     }, [0.4, 0.5], [1.4, 1.5]],
+
+    [{'intercept':   1.0,
+      'slope': 1.0,
+     }, (1.3, 1.57), 5.911],
+]
Index: sasmodels/models/porod.py
===================================================================
--- sasmodels/models/porod.py	(revision 16bb4337d95ce5f999aec885781f06ccb80925c0)
+++ sasmodels/models/porod.py	(revision 16bb4337d95ce5f999aec885781f06ccb80925c0)
@@ -0,0 +1,58 @@
+r"""
+This model fits the Porod function
+
+.. math::
+
+    I(q) = C/q^4
+    \\
+    C = 2\pi (\Delta\rho)^2 S_v
+
+to the data directly without any need for linearisation (cf. Log I(q) vs Log q).
+
+Here $C$ is the scale factor and $S_v$ is the specific surface area (ie, surface area / volume)
+of the sample, and $\Delta\rho$ is the contrast factor.
+
+For 2D data: The 2D scattering intensity is calculated in the same way as 1D, 
+where the q vector is defined as
+
+.. math::
+    q = \sqrt{q_x^2+q_y^2}
+
+"""
+
+from numpy import sqrt, power
+
+name = "porod"
+title = "Porod function"
+description = """\
+          I(q) = scale/q^4 + background
+"""
+
+category = "shape-independent"
+
+parameters = []
+
+def Iq(q):
+    """
+    @param q: Input q-value
+    """
+    return 1.0/power(q, 4)
+
+Iq.vectorized = True  # Iq accepts an array of q values
+
+def Iqxy(qx, qy, *args):
+    """
+    @param qx:   Input q_x-value
+    @param qy:   Input q_y-value
+    @param args: Remaining arguments
+    """
+    return Iq(sqrt(qx ** 2 + qy ** 2), *args)
+
+Iqxy.vectorized = True # Iqxy accepts an array of qx, qy values
+
+demo = dict(scale=1.5, background=0.5)
+
+oldname = "PorodModel"
+oldpars = dict(scale='scale', background='background')
+
+tests = [[{'scale': 0.00001, 'background':0.01}, 0.04, 3.916250]]
Index: sasmodels/models/two_power_law.py
===================================================================
--- sasmodels/models/two_power_law.py	(revision 7e1d090d6f1579784a2df1a534eda3abdc9c2d5a)
+++ sasmodels/models/two_power_law.py	(revision 7e1d090d6f1579784a2df1a534eda3abdc9c2d5a)
@@ -0,0 +1,163 @@
+r"""
+This model calculates an empirical functional form for SAS data characterized
+by two power laws.
+
+Definition
+----------
+
+The scattering intensity $I(q)$ is calculated as
+
+.. math::
+
+    I(q) = \begin{cases}
+    A q^{-m1} + \text{background} & q <= qc \\
+    C q^{-m2} + \text{background} & q > qc
+    \end{cases}
+
+where $qc$ = the location of the crossover from one slope to the other,
+$A$ = the scaling coefficent that sets the overall intensity of the lower Q power law region,
+$m1$ = power law exponent at low Q,
+$m2$ = power law exponent at high Q
+
+The scaling of the second power law region (coefficent C) is then automatically scaled to
+match the first by following formula
+
+.. math::
+    C = \frac{A}{qc^{-m1} qc^{-m2}}
+
+.. note::
+    Be sure to enter the power law exponents as positive values!
+
+For 2D data the scattering intensity is calculated in the same way as 1D,
+where the $q$ vector is defined as
+
+.. math::
+
+    q = \sqrt{q_x^2 + q_y^2}
+
+
+.. figure:: img/two_power_law_1d.jpg
+
+    1D plot using the default values (with 500 data point).
+
+References
+----------
+
+None.
+
+"""
+
+from numpy import power
+from numpy import sqrt
+from numpy import inf
+from numpy import concatenate
+
+name = "two_power_law"
+title = "Two Power Law intensity calculation"
+description = """
+            I(q) = coef_A*pow(qval,-1.0*power1) + background for q<=qc
+            =C*pow(qval,-1.0*power2) + background for q>qc
+            where C=coef_A*pow(qc,-1.0*power1)/pow(qc,-1.0*power2).
+
+            coef_A = scaling coefficent
+            qc = crossover location [1/A]
+            power_1 (=m1) = power law exponent at low Q
+            power_2 (=m2) = power law exponent at high Q
+            background = Incoherent background [1/cm]
+        """
+category = "shape-independent"
+
+# pylint: disable=bad-whitespace, line-too-long
+#            ["name", "units", default, [lower, upper], "type", "description"],
+parameters = [["coefficent_1",  "",         1.0, [-inf, inf], "", "coefficent A in low Q region"],
+              ["crossover",     "1/Ang",    0.04,[0, inf],    "", "crossover location"],
+              ["power_1",       "",         1.0, [0, inf],    "", "power law exponent at low Q"],
+              ["power_2",       "",         4.0, [0, inf],    "", "power law exponent at high Q"],
+             ]
+# pylint: enable=bad-whitespace, line-too-long
+
+
+def Iq(q,
+       coefficent_1=1.0,
+       crossover=0.04,
+       power_1=1.0,
+       power_2=4.0,
+      ):
+
+    """
+    :param q:                   Input q-value (float or [float, float])
+    :param coefficent_1:        Scaling coefficent at low Q
+    :param crossover:           Crossover location
+    :param power_1:             Exponent of power law function at low Q
+    :param power_2:             Exponent of power law function at high Q
+    :return:                    Calculated intensity
+    """
+
+    #Two sub vectors are created to treat crossover values
+    q_lower = q[q <= crossover]
+    q_upper = q[q > crossover]
+    coefficent_2 = coefficent_1*power(crossover, -1.0*power_1)/power(crossover, -1.0*power_2)
+    intensity_lower = coefficent_1*power(q_lower, -1.0*power_1)
+    intensity_upper = coefficent_2*power(q_upper, -1.0*power_2)
+    intensity = concatenate((intensity_lower, intensity_upper), axis=0)
+
+    return intensity
+
+Iq.vectorized = True  # Iq accepts an array of q values
+
+def Iqxy(qx, qy, *args):
+    """
+    :param qx:   Input q_x-value
+    :param qy:   Input q_y-value
+    :param args: Remaining arguments
+    :return:     2D-Intensity
+    """
+
+    return Iq(sqrt(qx**2 + qy**2), *args)
+
+Iqxy.vectorized = True  # Iqxy accepts an array of qx, qy values
+
+demo = dict(scale=1, background=0.0,
+            coefficent_1=1.0,
+            crossover=0.04,
+            power_1=1.0,
+            power_2=4.0)
+
+oldname = "TwoPowerLawModel"
+oldpars = dict(coefficent_1='coef_A',
+               crossover='qc',
+               power_1='power1',
+               power_2='power2',
+               background='background')
+
+tests = [
+    # Accuracy tests based on content in test/utest_extra_models.py
+    [{'coefficent_1':     1.0,
+      'crossover':  0.04,
+      'power_1':    1.0,
+      'power_2':    4.0,
+      'background': 0.0,
+     }, 0.001, 1000],
+
+    [{'coefficent_1':     1.0,
+      'crossover':  0.04,
+      'power_1':    1.0,
+      'power_2':    4.0,
+      'background': 0.0,
+     }, 0.150141, 0.125945],
+
+    [{'coeffcent_1':    1.0,
+      'crossover':  0.04,
+      'power_1':    1.0,
+      'power_2':    4.0,
+      'background': 0.0,
+     }, 0.442528, 0.00166884],
+
+    [{'coeffcent_1':    1.0,
+      'crossover':  0.04,
+      'power_1':    1.0,
+      'power_2':    4.0,
+      'background': 0.0,
+     }, (0.442528, 0.00166884), 0.00166884],
+
+]
Index: setup.py
===================================================================
--- setup.py	(revision 040575fa2fdfdbba541ff27f43057fb6c1354010)
+++ setup.py	(revision 3eb331258188803a81fbe7efcc02852d2c6e3e39)
@@ -11,5 +11,5 @@
     version = "1.0.0a",
     description = "sasmodels package",
-    long_description=open('README.rst').read(),
+    long_description=open('README.md').read(),
     author = "SasView Collaboration",
     author_email = "management@sasview.org",
