Index: src/sas/sasgui/perspectives/fitting/media/plugin.rst
===================================================================
--- src/sas/sasgui/perspectives/fitting/media/plugin.rst	(revision 3e1c9e5091877d1c521b673e5810c0f363458e86)
+++ src/sas/sasgui/perspectives/fitting/media/plugin.rst	(revision af6de5094a825dbe7854084f228b45ef41928e80)
@@ -11,7 +11,11 @@
 There are essentially three ways to generate new fitting models for SasView:
 
-* Using the SasView :ref:`New_Plugin_Model` helper dialog (best for beginners and/or relatively simple models)
-* By copying/editing an existing model (this can include models generated by the *New Plugin Model* dialog) in the :ref:`Python_shell` or :ref:`Advanced_Plugin_Editor` as described below (suitable for all use cases)
-* By writing a model from scratch outside of SasView (only recommended for code monkeys!)
+* Using the SasView :ref:`New_Plugin_Model` helper dialog (best for beginners
+  and/or relatively simple models)
+* By copying/editing an existing model (this can include models generated by
+  the *New Plugin Model* dialog) in the :ref:`Python_shell` or
+  :ref:`Advanced_Plugin_Editor` as described below (suitable for all use cases)
+* By writing a model from scratch outside of SasView (only recommended for
+  code monkeys!)
 
 Overview
@@ -479,74 +483,21 @@
     }
 
-The C model operates on a single $q$ value at a time.  The code will be
-run in parallel across different $q$ values, either on the graphics card
-or the processor.
-
-Rather than returning NAN from Iq, you must define the *INVALID(v)*.  The
-*v* parameter lets you access all the parameters in the model using
-*v.par1*, *v.par2*, etc. For example::
-
-    #define INVALID(v) (v.bell_radius < v.radius)
-
 *Iqxy* is similar to *Iq*, except it uses parameters *qx, qy* instead of *q*,
-and it includes orientation parameters. As in python models, *form_volume*
-includes only the volume parameters.  *Iqxy* will default to
-*Iq(sqrt(qx**2 + qy**2), par1, ...)* and *form_volume* will default to 1.0.
-
-The C code follows the C99 standard, including the usual math functions,
-as defined in
-`OpenCL <https://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/mathFunctions.html>`_.
-
-The standard constants and functions include the following::
-
-    M_PI = pi
-    M_PI_2 = pi/2
-    M_PI_4 = pi/4
-    M_E = e
-    M_SQRT1_2 = 1/sqrt(2)
-    NAN = NaN
-    INFINITY = 1/0
-    erf(x) = error function
-    erfc(x) = 1-erf(x)
-    expm1(x) = exp(x) - 1
-    tgamma(x) = gamma function
-
-Some non-standard constants and functions are also provided::
-
-    M_PI_180 = pi/180
-    M_4PI_3 = 4pi/3
-    square(x) = x*x
-    cube(x) = x*x*x
-    sinc(x) = sin(x)/x, with sin(0)/0 -> 1
-    SINCOS(x, s, c) sets s=sin(angle) and c=cos(angle)
-    powr(x, y) = x^y for x >= 0
-    pown(x, n) = x^n for n integer
-
-**source=['lib/fn.c', ...]** includes the listed C source files in the
+and it includes orientation parameters.
+
+*form_volume* defines the volume of the shape. As in python models,
+includes only the volume parameters.
+
+*Iqxy* will default to *Iq(sqrt(qx**2 + qy**2), par1, ...)* and
+*form_volume* will default to 1.0.
+
+**source=['fn.c', ...]** includes the listed C source files in the
 program before *Iq* and *Iqxy* are defined. This allows you to extend the
-library of available C functions. Additional special functions and
-scattering calculations are defined in
-`sasmodels/models/lib <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib>`_,
-including::
-
-    sph_j1c(x) = 3 j1(x)/x = 3 (sin(x) - x cos(x))/x^3  [spherical bessel function]
-    sas_J1c(x) = 2 J1(x)/x  [bessel function of the first kind]
-    sas_gamma(x) = gamma function  [tgamma is unstable below 1]
-    sas_erf(x) = error function [erf is broken on some Intel OpenCL drivers]
-    sas_erfc(x) = 1-erf(x)
-    sas_J0(x) = J0(x)
-    sas_J1(x) = J1(x)
-    sas_JN(x) = JN(x)
-    Si(x) = integral sin(z)/z from 0 to x
-    Gauss76Wt = gaussian quadrature weights for 76 point integral
-    Gauss76Z = gaussian quadrature values for 76 point integral
-
-These functions have been tuned to be fast and numerically stable down
-to $q=0$ even in single precision.  In some cases they work around bugs
-which appear on some platforms but not others. So use them where needed!!!
+library of C functions available to your model.
 
 Models are defined using double precision declarations for the
-parameters and return values.  Declarations and constants will be converted
-to float or long double depending on the precision requested.
+parameters and return values.  When a model is run using single
+precision or long double precision, each variable is converted
+to the target type, depending on the precision requested.
 
 **Floating point constants must include the decimal point.**  This allows us
@@ -558,17 +509,207 @@
 use the builtin constant M_PI rather than 4*atan(1); it is faster and smaller!
 
-FLOAT_SIZE is the number of bytes in the converted variables. If your
-algorithm depends on precision (which is not uncommon for numerical
-algorithms), use the following::
-
-    #if FLOAT_SIZE>4
-    ... code for double precision ...
-    #else
-    ... code for single precision ...
-    #endif
-
-A value defined as SAS_DOUBLE will stay double precision; this should
-not be used since some graphics cards do not support double precision.
-
+The C model operates on a single $q$ value at a time.  The code will be
+run in parallel across different $q$ values, either on the graphics card
+or the processor.
+
+Rather than returning NAN from Iq, you must define the *INVALID(v)*.  The
+*v* parameter lets you access all the parameters in the model using
+*v.par1*, *v.par2*, etc. For example::
+
+    #define INVALID(v) (v.bell_radius < v.radius)
+
+Special Functions
+.................
+
+The C code follows the C99 standard, with the usual math functions,
+as defined in
+`OpenCL <https://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/mathFunctions.html>`_.
+This includes the following:
+
+    M_PI, M_PI_2, M_PI_4, M_SQRT1_2, M_E:
+        $\pi$, $\pi/2$, $\pi/4$, $1/\sqrt{2}$ and Euler's constant $e$
+    exp, log, pow(x,y), expm1, sqrt:
+        Power functions $e^x$, $\ln x$, $x^y$, $e^x - 1$, $\sqrt{x}$.
+        The function expm1(x) is accurate across all $x$, including $x$
+        very close to zero.
+    sin, cos, tan, asin, acos, atan:
+        Trigonometry functions and inverses, operating on radians.
+    sinh, cos, tanh, asinh, acosh, atanh:
+        Hyperbolic trigonometry functions.
+    atan2(y,x):
+        Angle from the $x$\ -axis to the point $(x,y)$, which is equal to
+        $\tan^{-1}(y/x)$ corrected for quadrant.  That is, if $x$ and $y$ are
+        both negative, then atan2(y,x) returns a value in quadrant III where
+        atan(y/x) would return a value in quadrant I. Similarly for
+        quadrants II and IV when $x$ and $y$ have opposite sign.
+    fmin(x,y), fmax(x,y), trunc, rint:
+        Floating point functions.  rint(x) returns the nearest integer.
+    NAN:
+        NaN, Not a Number, $0/0$.  Use isnan(x) to test for NaN.  Note that
+        you cannot use :code:`x == NAN` to test for NaN values since that
+        will always return false.  NAN does not equal NAN!
+    INFINITY:
+        $\infty, 1/0$.  Use isinf(x) to test for infinity, or isfinite(x)
+        to test for finite and not NaN.
+    erf, erfc, tgamma, lgamma:  **do not use**
+        Special functions that should be part of the standard, but are missing
+        or inaccurate on some platforms. Use sas_erf, sas_erfc and sas_gamma
+        instead (see below). Note: lgamma(x) has not yet been tested.
+
+Some non-standard constants and functions are also provided:
+
+    M_PI_180, M_4PI_3:
+        $\pi/{180}$, $\tfrac{4}{3}\pi$
+    SINCOS(x, s, c):
+        Macro which sets s=sin(x) and c=cos(x). The variables *c* and *s*
+        must be declared first.
+    square(x):
+        $x^2$
+    cube(x):
+        $x^3$
+    sinc(x):
+        $\sin(x)/x$, with limit $\sin(0)/0 = 1$.
+    powr(x, y):
+        $x^y$ for $x \ge 0$; this is faster than general $x^y$ on some GPUs.
+    pown(x, n):
+        $x^n$ for $n$ integer; this is faster than general $x^n$ on some GPUs.
+    FLOAT_SIZE:
+        The number of bytes in a floating point value.  Even though all
+        variables are declared double, they may be converted to single
+        precision float before running. If your algorithm depends on
+        precision (which is not uncommon for numerical algorithms), use
+        the following::
+
+            #if FLOAT_SIZE>4
+            ... code for double precision ...
+            #else
+            ... code for single precision ...
+            #endif
+    SAS_DOUBLE:
+        A replacement for :code:`double` so that the declared variable will
+        stay double precision; this should generally not be used since some
+        graphics cards do not support double precision.  There is no provision
+        for forcing a constant to stay double precision.
+
+The following special functions and scattering calculations are defined in
+`sasmodels/models/lib <https://github.com/SasView/sasmodels/tree/master/sasmodels/models/lib>`_.
+These functions have been tuned to be fast and numerically stable down
+to $q=0$ even in single precision.  In some cases they work around bugs
+which appear on some platforms but not others. So use them where needed!!!
+
+    polevl(x, c, n):
+        Polynomial evaluation $p(x) = \sum_{i=0}^n c_i x^{n-i}$ using Horner's
+        method so it is faster and more accurate.
+
+        :code:`source = ["lib/polevl.c", ...]`
+
+    sas_gamma:
+        Gamma function $\text{sas_gamma}(x) = \Gamma(x)$.  The standard math
+        library gamma function, tgamma(x) is unstable below 1 on some platforms.
+
+        :code:`source = ["lib/sasgamma.c", ...]`
+
+    erf, erfc:
+        Error function
+        $\text{erf}(x) = \frac{1}{\sqrt\pi}\int_0^x e^{-t^2}\,dt$
+        and complementary error function
+        $\text{erfc}(x) = \frac{1}{\sqrt\pi}\int_x^\inf e^{-t^2}\,dt$.
+        The standard manth library erf and erfc are slower and broken
+        on some platforms.
+
+        :code:`source = ["lib/polevl.c", "lib/sas_erf.c", ...]`
+
+    sas_J0:
+        Bessel functions of the first kind where
+        $J_0(x) = \frac{1}{\pi}\int_0^\pi \cos(x\sin(\tau))\,d\tau$.
+
+        :code:`source = ["lib/polevl.c", "lib/sas_J0.c", ...]`
+
+    sas_J1:
+        Bessel functions of the first kind where
+        $J_1(x) = \frac{1}{\pi}\int_0^\pi \cos(\tau - x\sin(\tau))\,d\tau$.
+
+        :code:`source = ["lib/polevl.c", "lib/sas_J1.c", ...]`
+
+    sas_JN:
+        Bessel functions of the first kind where
+        $J_n(x) = \frac{1}{\pi}\int_0^\pi \cos(n\tau - x\sin(\tau))\,d\tau$.
+
+        :code:`source = ["lib/polevl.c", "lib/sas_J0.c", "lib/sas_J1.c", "lib/sas_JN.c", ...]`
+
+    Si:
+        Sine integral $\text{Si}(x) = \int_0^x \tfrac{\sin t}{t}\,dt$.
+
+        :code:`soure = ["lib/Si.c", ...]`
+
+    sph_j1c(qr):
+        Spherical Bessel form
+        $F(qr) = 3 j_1(qr)/(qr) = 3 (\sin(qr) - qr \cos(qr))/{(qr)^3}$,
+        with a limiting value of 1 at $qr=0$.  This function uses a Taylor
+        series for small $qr$ for numerical accuracy.
+
+        :code:`source = ["lib/sph_j1c.c", ...]`
+
+    sas_J1c(qr):
+        Bessel form $F(qr) = 2 J_1(qr)/{(qr)}$, with a limiting value of 1 at $qr=0$.
+
+        :code:`source = ["lib/polevl.c", "lib/sas_J1c.c", ...]`
+
+    Gauss76z[i], Gauss76Wt[i]:
+        Points $z_i$ and weights $w_i$ for 76-point Gaussian quadrature,
+        computing $\int_{-1}^1 f(z)\,dz \approx \sum_{i=1}^{76} w_i f(z_i)$.
+        Similar arrays are available in :code:`gauss20.c` for 20 point
+        quadrature and in :code:`gauss150.c` for 150 point quadrature.
+
+        :code:`source = ["gauss76.c", ...]`
+
+Problems with C models
+......................
+
+The graphics processor (GPU) in your computer is a specialized computer tuned
+for certain kinds of problems.  This leads to strange restrictions that you
+need to be aware of.  Your code may work fine on some platforms or for some
+models, but then return bad values on other platforms.  Some examples of
+particular problems:
+
+  (1) Code is too complex, or uses too much memory.  GPU devices only have a
+  limited amount of memory available for each processor.  If you run programs
+  which take too much memory, then rather than running multiple values in parallel
+  as it usually does, the GPU may only run a single version of the code at a
+  time, making it slower than running on the CPU.  It may fail to run on
+  some platforms, or worse, cause the screen to go blank or the system to reboot.
+
+  (2) Code takes too long.  Because GPU devices are used for the computer
+  display, the OpenCL drivers are very careful about the amount of time they
+  will allow any code to run.  For example, on OS X, the model will stop running
+  after 5 seconds regardless if the computation is complete.  You may end up
+  with only some of your 2-D array defined, with the rest containing random
+  data. Or it may cause the screen to go blank or the system to reboot.
+
+  (3) Memory is not *aligned*.  The GPU hardware is specialized to operate on
+  multiple values simultaneously.  To keep the GPU simpler the values in memory
+  must be aligned with the different GPU compute engines.  Not following these
+  rules can lead to unexpected values being loaded into memory, and wrong answers
+  computed.  The conclusion from a very long and strange debugging session was
+  that any arrays that you declare in your model should be a multiple of four.
+  For example
+
+      double Iq(q, p1, p2, ...)
+      {
+          double vector[8];  // Only going to use seven slots, but declare 8
+          ...
+      }
+
+The first step when your model is behaving strangely is to set **single=False**.
+This automatically restricts the model to only run on the CPU, or on high end
+GPU cards.  There can still be problems even on high end cards, so you can force
+the model off the GPU by setting **opencl=False**.  This runs the model
+as a normal C program without any GPU restrictions so you know that
+strange results are probably from your code rather than the environment.  Once
+the code is debugged, you can compare your output to the output on the GPU.
+
+Although it can be difficult to get your model to work on the GPU, the reward
+can be a model that runs 1000x faster on a good card.  Even your laptop may
+show a 50x improvement or more over the equivalent pure python model.
 
 External C Models
@@ -577,5 +718,5 @@
 External C models are very much like embedded C models, except that
 *Iq*, *Iqxy* and *form_volume* are defined in an external source file
-loaded using the *source=[...]*  method. You need to supply the function
+loaded using the *source=[...]* statement. You need to supply the function
 declarations for each of these that you need instead of building them
 automatically from the parameter table.
