Index: sasmodels/compare.py
===================================================================
--- sasmodels/compare.py	(revision 190fc2b167d3d89ca9c3b7d3128bdf8bde9f67d0)
+++ sasmodels/compare.py	(revision d5e650da432c58f8cd7c4d86e8ecb26828e17a21)
@@ -539,5 +539,5 @@
     if Ncomp > 0 and Nbase > 0:
         plt.subplot(133)
-        if '-abs' in opts:
+        if not opts['rel_err']:
             err, errstr, errview = resid, "abs err", "linear"
         else:
@@ -545,4 +545,6 @@
         #err,errstr = base/comp,"ratio"
         plot_theory(data, None, resid=err, view=errview, use_data=False)
+        if view == 'linear':
+            plt.xscale('linear')
         plt.title("max %s = %.3g"%(errstr, max(abs(err))))
         #cbar_title = errstr if errview=="linear" else "log "+errstr
Index: sasmodels/convert.py
===================================================================
--- sasmodels/convert.py	(revision 5054e805a7c53aa18f659a0c1613f527f05caab3)
+++ sasmodels/convert.py	(revision 3964f92c0d8ef50968fdc442ddace0815434fff6)
@@ -14,4 +14,5 @@
     'be_polyelectrolyte',
     'correlation_length',
+    'binary_hard_sphere'
 ]
 
Index: sasmodels/kernel_template.c
===================================================================
--- sasmodels/kernel_template.c	(revision caf768d5b2c191061248bcdc8e74f6e85385e393)
+++ sasmodels/kernel_template.c	(revision 840b85918586f724a23f78252f90ed33ac27fdf7)
@@ -16,4 +16,5 @@
      using namespace std;
      #if defined(_MSC_VER)
+         #include <limits>
          #include <float.h>
          #define kernel extern "C" __declspec( dllexport )
@@ -22,4 +23,29 @@
 	     inline double fmax(double x, double y) { return x<y ? y : x; }
 	     inline double isnan(double x) { return _isnan(x); }
+	     #define NAN (std::numeric_limits<double>::quiet_NaN()) // non-signalling NaN
+	     static double cephes_expm1(double x) {
+	         // Adapted from the cephes math library.
+	         // Copyright 1984 - 1992 by Stephen L. Moshier
+	         if (x != x || x == 0.0) {
+	             return x; // NaN and +/- 0
+	         } else if (x < -0.5 || x > 0.5) {
+	             return exp(x) - 1.0;
+	         } else {
+	             const double xsq = x*x;
+	             const double p = (((
+	                  +1.2617719307481059087798E-4)*xsq
+                      +3.0299440770744196129956E-2)*xsq
+                      +9.9999999999999999991025E-1);
+                 const double q = ((((
+                      +3.0019850513866445504159E-6)*xsq
+                      +2.5244834034968410419224E-3)*xsq
+                      +2.2726554820815502876593E-1)*xsq
+                      +2.0000000000000000000897E0);
+                 double r = x * p;
+	             r =  r / (q - r);
+	             return r+r;
+	         }
+	     }
+	     #define expm1 cephes_expm1
      #else
          #define kernel extern "C"
Index: sasmodels/models/vesicle.c
===================================================================
--- sasmodels/models/vesicle.c	(revision 321736f226b3c435616dc00b9de43863e55a24fe)
+++ sasmodels/models/vesicle.c	(revision 321736f226b3c435616dc00b9de43863e55a24fe)
@@ -0,0 +1,72 @@
+double form_volume(double radius, double thickness);
+
+double Iq(double q, 
+          double sld, double solvent_sld,
+          double radius, double thickness);
+
+double Iqxy(double qx, double qy,
+          double sld, double solvent_sld,
+          double radius, double thickness);
+
+double form_volume(double radius, double thickness)
+{
+    //note that for the vesicle model, the volume is ONLY the shell volume
+    double volume;
+    volume =4.*M_PI*(radius+thickness)*(radius+thickness)*(radius+thickness)/3;
+    volume -=4.*M_PI*radius*radius*radius/3.;
+    return volume;
+}
+
+double Iq(double q,
+    double sld,
+    double solvent_sld,
+    double radius,
+    double thickness)
+
+/*
+   scattering from a unilamellar vesicle.
+   same functional form as the core-shell sphere, but more intuitive for
+   a vesicle
+*/
+
+/*
+   note that the sph_j1c we are using has been optimized for precision over
+   SasView's original implementation. HOWEVER at q==0 that implementation
+   set bes=1.0 rather than 0.0 (correct value) on the grounds I believe that 
+   bes=0.00 causes Iq to have a divide by 0 error (mostly encountered when
+   doing a theory curve in 2D?  We should verify this and if necessary fix
+     -PDB Feb 7, 2016 
+*/
+{
+    double bes,vol,contrast,f,f2;
+
+    // core first, then add in shell
+    contrast = solvent_sld-sld;
+    bes = sph_j1c(q*radius);
+    vol = 4.0*M_PI/3.0*radius*radius*radius;
+    f = vol*bes*contrast;
+ 
+    //now the shell
+    contrast = sld-solvent_sld;
+    bes = sph_j1c(q*(radius+thickness));
+    vol = 4.0*M_PI/3.0*(radius+thickness)*(radius+thickness)*(radius+thickness);
+    f += vol*bes*contrast;
+
+    //rescale to [cm-1]. No volume normalization as this is done by the caller
+    f2 = f*f*1.0e-4;
+    
+    return(f2);
+}
+
+
+double Iqxy(double qx, double qy,
+          double sld, double solvent_sld,
+          double radius, double thickness)
+          
+{
+    double q = sqrt(qx*qx + qy*qy);
+    return Iq(q,
+        sld, solvent_sld,
+        radius,thickness);
+
+}
Index: sasmodels/models/vesicle.py
===================================================================
--- sasmodels/models/vesicle.py	(revision 216fa6d00e42770a4a028c2953a73319ad05e924)
+++ sasmodels/models/vesicle.py	(revision 216fa6d00e42770a4a028c2953a73319ad05e924)
@@ -0,0 +1,136 @@
+r"""
+Definition
+----------
+
+The 1D scattering intensity is calculated in the following way (Guinier, 1955)
+
+.. math::
+
+    P(q) = \frac{\text{scale}}{V_\text{shell}} \left[
+           \frac{3V_{\text{core}}({\rho_{\text{solvent}}
+           - \rho_{\text{shell}})j_1(qR_{\text{core}})}}{qR_{\text{core}}}
+           + \frac{3V_{\text{tot}}(\rho_{\text{shell}}
+           - \rho_{\text{solvent}}) j_1(qR_{\text{tot}})}{qR_{\text{tot}}}
+           \right]^2 + \text{background}
+
+
+where scale is a scale factor equivalent to the volume fraction of shell
+material if the data is on an absolute scale, $V_{shell}$ is the volume of the
+shell, $V_{\text{cor}}$ is the volume of the core, $V_{\text{tot}}$ is the
+total volume, $R_{\text{core}}$ is the radius of the core, $R_{\text{tot}}$ is
+the outer radius of the shell, $\rho_{\text{solvent}}$ is the scattering length
+density of the solvent (which is the same as for the core in this case),
+$\rho_{\text{scale}}$ is the scattering length density of the shell, background
+is a flat background level (due for example to incoherent scattering in the
+case of neutrons), and $j_1$ is the spherical bessel function
+$j_1 = (sin(x) - x cos(x))/ x^2$.
+
+The functional form is identical to a "typical" core-shell structure, except
+that the scattering is normalized by the volume that is contributing to the
+scattering, namely the volume of the shell alone, the scattering length density
+of the core is fixed the same as that of the solvent, the scale factor when the
+data are on an absolute scale is equivalent to the volume fraction of material
+in the shell rather than the entire core+shell sphere, and the parameterization
+is done in terms of the core radius = $R_{\text{core}}$ and the shell
+thickness = $R_{\text{tot}} - R_{\text{core}}$.
+
+.. figure: img/vesicle_geometry.jpg
+
+The 2D scattering intensity is the same as *P(q)* above, regardless of the
+orientation of the *q* vector which is defined as
+
+.. math::
+
+    q = \sqrt{q_x^2 + q_y^2}
+
+
+NB: The outer most radius (= *radius* + *thickness*) is used as the effective
+radius for *S(Q)* when *P(Q)* \* *S(Q)* is applied.
+
+.. image:: img/vesicle_1d.jpg
+
+*Figure. 1D plot using the default values given in the table
+(w/200 data point). Polydispersity and instrumental resolution normally
+will smear out most of the rapidly oscillating features.*
+
+REFERENCE
+
+A Guinier and G. Fournet, *Small-Angle Scattering of X-Rays*, John Wiley and
+Sons, New York, (1955)
+"""
+
+import numpy as np
+from numpy import pi, inf
+
+name = "vesicle"
+title = "This model provides the form factor, *P(q)*, for an unilamellar \
+    vesicle. This is model is effectively identical to the hollow sphere \
+    reparameterized to be more intuitive for a vesicle and normalizing the \
+    form factor by the volume of the shell."
+description = """
+    Model parameters:
+        radius : the core radius of the vesicle
+        thickness: the shell thickness
+        sld: the shell SLD
+        solvent_sld: the solvent (and core) SLD
+        background: incoherent background
+        scale : scale factor = shell volume fraction if on absolute scale"""
+category = "shape:sphere"
+
+#             [ "name", "units", default, [lower, upper], "type", "description"],
+parameters = [["sld", "1e-6/Ang^2", 0.5, [-inf, inf], "",
+               "vesicle shell scattering length density"],
+              ["solvent_sld", "1e-6/Ang^2", 6.36, [-inf, inf], "",
+               "solvent scattering length density"],
+              ["radius", "Ang", 100, [0, inf], "volume",
+               "vesicle core radius"],
+              ["thickness", "Ang", 30, [0, inf], "volume",
+               "vesicle shell thickness"],
+             ]
+
+source = ["lib/sph_j1c.c", "vesicle.c"]
+
+def ER(radius, thickness):
+    '''
+    returns the effective radius used in the S*P calculation
+
+    :param radius: core radius
+    :param thickness: shell thickness
+    '''
+    return radius + thickness
+
+def VR(radius, thickness):
+    '''
+    returns the volumes of the shell and of the whole sphere including the
+    core plus shell - is used to normalize when including polydispersity.
+
+    :param radius: core radius
+    :param thickness: shell thickness
+    :return whole: volume of core and shell
+    :return whole-core: volume of the shell
+    '''
+
+    whole = 4. * pi * (radius + thickness) ** 3. / 3.
+    core = 4. * pi * radius ** 3. / 3.
+    return whole, whole - core
+
+
+# parameters for demo
+demo = dict(scale=1, background=0,
+            sld=0.5, solvent_sld=6.36,
+            radius=100, thickness=30,
+            radius_pd=.2, radius_pd_n=10,
+            thickness_pd=.2, thickness_pd_n=10)
+
+# For testing against the old sasview models, include the converted parameter
+# names and the target sasview model name.
+oldname = 'VesicleModel'
+oldpars = dict(sld='shell_sld', solvent_sld='solv_sld')
+
+
+# NOTE: test results taken from values returned by SasView 3.1.2
+tests = [[{}, 0.0010005303255, 17139.8268799],
+         [{}, 0.200027832249, 0.130387268704 ],
+         [{}, 'ER', 130.],
+         [{}, 'VR', 0.54483386436],
+        ]
