Index: README.rst
===================================================================
--- README.rst	(revision e30d645b749d90d70af7bd103c010fae12fede8c)
+++ README.rst	(revision 2a64722f3e6a290ffdbe9f4084c5ba426347a275)
@@ -10,25 +10,79 @@
 is available.
 
-Example
+Install
 -------
+
+The easiest way to use sasmodels is from `SasView <http://www.sasview.org/>`_.
+
+You can also install sasmodels as a standalone package in python. Use
+`miniconda <https://docs.conda.io/en/latest/miniconda.html>`_
+or `anaconda <https://www.anaconda.com/>`_
+to create a python environment with the sasmodels dependencies::
+
+    $ conda create -n sasmodels -c conda-forge numpy scipy matplotlib pyopencl
+
+The option ``-n sasmodels`` names the environment sasmodels, and the option
+``-c conda-forge`` selects the conda-forge package channel because pyopencl
+is not part of the base anaconda distribution.
+
+Activate the environment and install sasmodels::
+
+    $ conda activate sasmodels
+    (sasmodels) $ pip install sasmodels
+
+Install `bumps <https://github.com/bumps/bumps>`_ if you want to use it to fit
+your data::
+
+    (sasmodels) $ pip install bumps
+
+Usage
+-----
+
+Check that the works::
+
+    (sasmodels) $ python -m sasmodels.compare cylinder
+
+To show the orientation explorer::
+
+    (sasmodels) $ python -m sasmodels.jitter
+
+Documentation is available online as part of the SasView
+`fitting perspective <http://www.sasview.org/docs/index.html>`_
+as well as separate pages for
+`individual models <http://www.sasview.org/docs/user/sasgui/perspectives/fitting/models/index.html>`_.
+Programming details for sasmodels are available in the
+`developer documentation <http://www.sasview.org/docs/dev/dev.html>`_.
+
+
+Fitting Example
+---------------
 
 The example directory contains a radial+tangential data set for an oriented
 rod-like shape.
 
-The data is loaded by sas.dataloader from the sasview package, so sasview
-is needed to run the example.
+To load the example data, you will need the SAS data loader from the sasview
+package. This is not yet available on PyPI, so you will need a copy of the
+SasView source code to run it.  Create a directory somewhere to hold the
+sasview and sasmodels source code, which we will refer to as $SOURCE.
 
-To run the example, you need sasview, sasmodels and bumps.  Assuming these
-repositories are installed side by side, change to the sasmodels/example
-directory and enter::
+Use the following to install sasview, and the sasmodels examples::
 
-    PYTHONPATH=..:../../sasview/src ../../bumps/run.py fit.py \
-        cylinder --preview
+    (sasmodels) $ cd $SOURCE
+    (sasmodels) $ conda install git
+    (sasmodels) $ git clone https://github.com/sasview/sasview.git
+    (sasmodels) $ git clone https://github.com/sasview/sasmodels.git
 
-See bumps documentation for instructions on running the fit.  With the
-python packages installed, e.g., into a virtual environment, then the
-python path need not be set, and the command would be::
+Set the path to the sasview source on your python path within the sasmodels
+environment.  On Windows, this will be::
 
-    bumps fit.py cylinder --preview
+    (sasmodels)> set PYTHONPATH="$SOURCE\sasview\src"
+    (sasmodels)> cd $SOURCE/sasmodels/example
+    (sasmodels)> python -m bumps.cli fit.py cylinder --preview
+
+On Mac/Linux with the standard shell this will be::
+
+    (sasmodels) $ export PYTHONPATH="$SOURCE/sasview/src"
+    (sasmodels) $ cd $SOURCE/sasmodels/example
+    (sasmodels) $ bumps fit.py cylinder --preview
 
 The fit.py model accepts up to two arguments.  The first argument is the
@@ -38,11 +92,6 @@
 both radial and tangential simultaneously, use the word "both".
 
-Notes
------
-
-cylinder.c + cylinder.py is the cylinder model with renamed variables and
-sld scaled by 1e6 so the numbers are nicer.  The model name is "cylinder"
-
-lamellar.py is an example of a single file model with embedded C code.
+See `bumps documentation <https://bumps.readthedocs.io/>`_ for detailed
+instructions on running the fit.
 
 |TravisStatus|_
Index: explore/precision.py
===================================================================
--- explore/precision.py	(revision fba9ca0239129ed28284a0b435ba2a9524913f36)
+++ explore/precision.py	(revision cd289474f05b7a39cccd795f21f84befdf2a8788)
@@ -207,7 +207,13 @@
     return model_info
 
-# Hack to allow second parameter A in two parameter functions
+# Hack to allow second parameter A in the gammainc and gammaincc functions.
+# Create a 2-D variant of the precision test if we need to handle other two
+# parameter functions.
 A = 1
 def parse_extra_pars():
+    """
+    Parse the command line looking for the second parameter "A=..." for the
+    gammainc/gammaincc functions.
+    """
     global A
 
@@ -333,4 +339,5 @@
 )
 add_function(
+    # Note: "a" is given as A=... on the command line via parse_extra_pars
     name="gammainc(x)",
     mp_function=lambda x, a=A: mp.gammainc(a, a=0, b=x)/mp.gamma(a),
@@ -339,4 +346,5 @@
 )
 add_function(
+    # Note: "a" is given as A=... on the command line via parse_extra_pars
     name="gammaincc(x)",
     mp_function=lambda x, a=A: mp.gammainc(a, a=x, b=mp.inf)/mp.gamma(a),
Index: sasmodels/compare.py
===================================================================
--- sasmodels/compare.py	(revision 4de145843b2267c4f1e8be16517c48de601151b6)
+++ sasmodels/compare.py	(revision 4de145843b2267c4f1e8be16517c48de601151b6)
@@ -1152,5 +1152,5 @@
         'rel_err'   : True,
         'explore'   : False,
-        'use_demo'  : True,
+        'use_demo'  : False,
         'zero'      : False,
         'html'      : False,
Index: sasmodels/direct_model.py
===================================================================
--- sasmodels/direct_model.py	(revision 5024a563075fb86e48bd868acf2f39ea4af2e90b)
+++ sasmodels/direct_model.py	(revision 5024a563075fb86e48bd868acf2f39ea4af2e90b)
@@ -332,5 +332,6 @@
 
         # Need to pull background out of resolution for multiple scattering
-        background = pars.get('background', DEFAULT_BACKGROUND)
+        default_background = self._model.info.parameters.common_parameters[1].default
+        background = pars.get('background', default_background)
         pars = pars.copy()
         pars['background'] = 0.
Index: sasmodels/generate.py
===================================================================
--- sasmodels/generate.py	(revision 39a06c989ba87dcedc6e5270d59fc4c9dd47ffe5)
+++ sasmodels/generate.py	(revision cd289474f05b7a39cccd795f21f84befdf2a8788)
@@ -703,6 +703,5 @@
     """
     for code in source:
-        m = _FQ_PATTERN.search(code)
-        if m is not None:
+        if _FQ_PATTERN.search(code) is not None:
             return True
     return False
@@ -712,9 +711,8 @@
     # type: (List[str]) -> bool
     """
-    Return True if C source defines "void Fq(".
+    Return True if C source defines "double shell_volume(".
     """
     for code in source:
-        m = _SHELL_VOLUME_PATTERN.search(code)
-        if m is not None:
+        if _SHELL_VOLUME_PATTERN.search(code) is not None:
             return True
     return False
@@ -1008,5 +1006,6 @@
         pars = model_info.parameters.kernel_parameters
     else:
-        pars = model_info.parameters.COMMON + model_info.parameters.kernel_parameters
+        pars = (model_info.parameters.common_parameters
+                + model_info.parameters.kernel_parameters)
     partable = make_partable(pars)
     subst = dict(id=model_info.id.replace('_', '-'),
Index: sasmodels/kernel.py
===================================================================
--- sasmodels/kernel.py	(revision e44432d7890080c5b9a440696903718a52c60eba)
+++ sasmodels/kernel.py	(revision cd289474f05b7a39cccd795f21f84befdf2a8788)
@@ -133,4 +133,7 @@
         nout = 2 if self.info.have_Fq and self.dim == '1d' else 1
         total_weight = self.result[nout*self.q_input.nq + 0]
+        # Note: total_weight = sum(weight > cutoff), with cutoff >= 0, so it
+        # is okay to test directly against zero.  If weight is zero then I(q),
+        # etc. must also be zero.
         if total_weight == 0.:
             total_weight = 1.
Index: sasmodels/modelinfo.py
===================================================================
--- sasmodels/modelinfo.py	(revision 39a06c989ba87dcedc6e5270d59fc4c9dd47ffe5)
+++ sasmodels/modelinfo.py	(revision 39a06c989ba87dcedc6e5270d59fc4c9dd47ffe5)
@@ -404,4 +404,8 @@
       parameters counted as n individual parameters p1, p2, ...
 
+    * *common_parameters* is the list of common parameters, with a unique
+      copy for each model so that structure factors can have a default
+      background of 0.0.
+
     * *call_parameters* is the complete list of parameters to the kernel,
       including scale and background, with vector parameters recorded as
@@ -416,9 +420,12 @@
     parameters don't use vector notation, and instead use p1, p2, ...
     """
-    # scale and background are implicit parameters
-    COMMON = [Parameter(*p) for p in COMMON_PARAMETERS]
-
     def __init__(self, parameters):
         # type: (List[Parameter]) -> None
+
+        # scale and background are implicit parameters
+        # Need them to be unique to each model in case they have different
+        # properties, such as default=0.0 for structure factor backgrounds.
+        self.common_parameters = [Parameter(*p) for p in COMMON_PARAMETERS]
+
         self.kernel_parameters = parameters
         self._set_vector_lengths()
@@ -468,4 +475,15 @@
                          if p.polydisperse and p.type not in ('orientation', 'magnetic'))
         self.pd_2d = set(p.name for p in self.call_parameters if p.polydisperse)
+
+    def set_zero_background(self):
+        """
+        Set the default background to zero for this model.  This is done for
+        structure factor models.
+        """
+        # type: () -> None
+        # Make sure background is the second common parameter.
+        assert self.common_parameters[1].id == "background"
+        self.common_parameters[1].default = 0.0
+        self.defaults = self._get_defaults()
 
     def check_angles(self):
@@ -567,5 +585,5 @@
     def _get_call_parameters(self):
         # type: () -> List[Parameter]
-        full_list = self.COMMON[:]
+        full_list = self.common_parameters[:]
         for p in self.kernel_parameters:
             if p.length == 1:
@@ -670,5 +688,5 @@
 
         # Gather the user parameters in order
-        result = control + self.COMMON
+        result = control + self.common_parameters
         for p in self.kernel_parameters:
             if not is2d and p.type in ('orientation', 'magnetic'):
@@ -770,7 +788,20 @@
 
     info = ModelInfo()
+
+    # Build the parameter table
     #print("make parameter table", kernel_module.parameters)
     parameters = make_parameter_table(getattr(kernel_module, 'parameters', []))
+
+    # background defaults to zero for structure factor models
+    structure_factor = getattr(kernel_module, 'structure_factor', False)
+    if structure_factor:
+        parameters.set_zero_background()
+
+    # TODO: remove demo parameters
+    # The plots in the docs are generated from the model default values.
+    # Sascomp set parameters from the command line, and so doesn't need
+    # demo values for testing.
     demo = expand_pars(parameters, getattr(kernel_module, 'demo', None))
+
     filename = abspath(kernel_module.__file__).replace('.pyc', '.py')
     kernel_id = splitext(basename(filename))[0]
Index: sasmodels/models/flexible_cylinder.c
===================================================================
--- sasmodels/models/flexible_cylinder.c	(revision 71b751d6a3a4ef4164f3b322841ddf3875c025c5)
+++ sasmodels/models/flexible_cylinder.c	(revision e5bbe64af768352c08de083d6f49653ad4da834e)
Index: sasmodels/models/hardsphere.py
===================================================================
--- sasmodels/models/hardsphere.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
+++ sasmodels/models/hardsphere.py	(revision 304c775207a9dbbadb23ad414848454d66b556d1)
@@ -162,6 +162,4 @@
     return pars
 
-demo = dict(radius_effective=200, volfraction=0.2,
-            radius_effective_pd=0.1, radius_effective_pd_n=40)
 # Q=0.001 is in the Taylor series, low Q part, so add Q=0.1,
 # assuming double precision sasview is correct
Index: sasmodels/sasview_model.py
===================================================================
--- sasmodels/sasview_model.py	(revision 5024a563075fb86e48bd868acf2f39ea4af2e90b)
+++ sasmodels/sasview_model.py	(revision 3a1afed813c02d7a1016d5e4fe9bfc3cb41c1ae7)
@@ -382,5 +382,4 @@
             hidden.add('scale')
             hidden.add('background')
-            self._model_info.parameters.defaults['background'] = 0.
 
         # Update the parameter lists to exclude any hidden parameters
@@ -695,5 +694,5 @@
             return self._calculate_Iq(qx, qy)
 
-    def _calculate_Iq(self, qx, qy=None, Fq=False, effective_radius_type=1):
+    def _calculate_Iq(self, qx, qy=None):
         if self._model is None:
             self._model = core.build_model(self._model_info)
@@ -715,8 +714,4 @@
         #print("values", values)
         #print("is_mag", is_magnetic)
-        if Fq:
-            result = calculator.Fq(call_details, values, cutoff=self.cutoff,
-                                   magnetic=is_magnetic,
-                                   effective_radius_type=effective_radius_type)
         result = calculator(call_details, values, cutoff=self.cutoff,
                             magnetic=is_magnetic)
@@ -736,8 +731,29 @@
         Calculate the effective radius for P(q)*S(q)
 
+        *mode* is the R_eff type, which defaults to 1 to match the ER
+        calculation for sasview models from version 3.x.
+
         :return: the value of the effective radius
         """
-        Fq = self._calculate_Iq([0.1], True, mode)
-        return Fq[2]
+        # ER and VR are only needed for old multiplication models, based on
+        # sas.sascalc.fit.MultiplicationModel.  Fail for now.  If we want to
+        # continue supporting them then add some test cases so that the code
+        # is exercised.  We can access ER/VR using the kernel Fq function by
+        # extending _calculate_Iq so that it calls:
+        #    if er_mode > 0:
+        #        res = calculator.Fq(call_details, values, cutoff=self.cutoff,
+        #                            magnetic=False, effective_radius_type=mode)
+        #        R_eff, form_shell_ratio = res[2], res[4]
+        #        return R_eff, form_shell_ratio
+        # Then use the following in calculate_ER:
+        #    ER, VR = self._calculate_Iq(q=[0.1], er_mode=mode)
+        #    return ER
+        # Similarly, for calculate_VR:
+        #    ER, VR = self._calculate_Iq(q=[0.1], er_mode=1)
+        #    return VR
+        # Obviously a combined calculate_ER_VR method would be better, but
+        # we only need them to support very old models, so ignore the 2x
+        # performance hit.
+        raise NotImplementedError("ER function is no longer available.")
 
     def calculate_VR(self):
@@ -748,6 +764,6 @@
         :return: the value of the form:shell volume ratio
         """
-        Fq = self._calculate_Iq([0.1], True, mode)
-        return Fq[4]
+        # See comments in calculate_ER.
+        raise NotImplementedError("VR function is no longer available.")
 
     def set_dispersion(self, parameter, dispersion):
@@ -914,4 +930,32 @@
     CylinderModel().evalDistribution([0.1, 0.1])
 
+def test_structure_factor_background():
+    # type: () -> None
+    """
+    Check that sasview model and direct model match, with background=0.
+    """
+    from .data import empty_data1D
+    from .core import load_model_info, build_model
+    from .direct_model import DirectModel
+
+    model_name = "hardsphere"
+    q = [0.0]
+
+    sasview_model = _make_standard_model(model_name)()
+    sasview_value = sasview_model.evalDistribution(np.array(q))[0]
+
+    data = empty_data1D(q)
+    model_info = load_model_info(model_name)
+    model = build_model(model_info)
+    direct_model = DirectModel(data, model)
+    direct_value_zero_background = direct_model(background=0.0)
+
+    assert sasview_value == direct_value_zero_background
+
+    # Additionally check that direct value background defaults to zero
+    direct_value_default = direct_model()
+    assert sasview_value == direct_value_default
+
+
 def magnetic_demo():
     Model = _make_standard_model('sphere')
@@ -934,2 +978,3 @@
     #print("rpa:", test_rpa())
     #test_empty_distribution()
+    #test_structure_factor_background()
