Index: example/weights/cyclic_gaussian.py
===================================================================
--- example/weights/cyclic_gaussian.py	(revision 36873d13d5ba83beb0b0f046deab7473e08aec25)
+++ example/weights/cyclic_gaussian.py	(revision 55e82f04d46e375f4f5aa858d1291b28da4d6219)
@@ -7,5 +7,5 @@
     r"""
     Cyclic gaussian dispersion on orientation.
-    
+
     .. math:
 
@@ -13,14 +13,11 @@
 
     This provides a close match to the gaussian distribution for
-    low angles (with $\sin \theta \approx \theta$), but the tails 
-    are limited to $\pm 90^\circ$.  For $\sigma$ large the
-    distribution is approximately uniform.  The usual polar coordinate
+    low angles, but the tails are limited to $\pm 90^\circ$.  For $\sigma$
+    large the distribution is approximately uniform.  The usual polar coordinate
     projection applies, with $\theta$ weights scaled by $\cos \theta$
     and $\phi$ weights unscaled.
 
-    This is closely related to a Maier-Saupe distribution with order
-    parameter $P_2$ and appropriate scaling constants, and changes
-    between $\sin$ and $\cos$ as appropriate for the coordinate system
-    representation.
+    This is eqivalent to a Maier-Saupe distribution with order
+    parameter $P_2 = 1/(2 \sigma^2)$, with $\sigma$ in radians.
     """
     type = "cyclic_gaussian"
@@ -29,14 +26,14 @@
     # Note: center is always zero for orientation distributions
     def _weights(self, center, sigma, lb, ub):
-        # Convert sigma in degrees to the approximately equivalent Maier-Saupe "a"
+        # Convert sigma in degrees to radians
         sigma = radians(sigma)
-        a = -0.5/sigma**2
 
-        # Limit width to +/-90 degrees; use an open interval since the
-        # pattern at +90 is the same as that at -90.
+        # Limit width to +/- 90 degrees
         width = min(self.nsigmas*sigma, pi/2)
-        x = np.linspace(-width, width, self.npts+2)[1:-1]
+        x = np.linspace(-width, width, self.npts)
+
+        # Truncate the distribution in case the parameter value is limited
+        x[(x >= radians(lb)) & (x <= radians(ub))]
 
         # Return orientation in degrees with Maier-Saupe weights
-        return degrees(x), exp(a*sin(x)**2)
-
+        return degrees(x), exp(-0.5*sin(x)**2/sigma**2)
Index: example/weights/gaussian_eq.py
===================================================================
--- example/weights/gaussian_eq.py	(revision 55e82f04d46e375f4f5aa858d1291b28da4d6219)
+++ example/weights/gaussian_eq.py	(revision 55e82f04d46e375f4f5aa858d1291b28da4d6219)
@@ -0,0 +1,42 @@
+import numpy as np
+import scipy.stats
+
+from sasmodels.weights import Dispersion as BaseDispersion
+
+class Dispersion(BaseDispersion):
+    r"""
+    Gaussian dispersion, with 1-$\sigma$ width.
+
+    .. math::
+
+        w = \exp\left(-\tfrac12 (x - c)^2/\sigma^2\right)
+
+    $x$ points are chosen such that each interval has equal weight.
+
+    This works surprisingly poorly.  Try::
+
+        $ sascomp cylinder -2d theta=45 phi=20 phi_pd_type=gaussian_eq \
+          phi_pd_n=100,1000 radius=50 length=2*radius -midq phi_pd=5
+
+    Leaving it here for others to improve.
+    """
+    type = "gaussian_eq"
+    default = dict(npts=35, width=0, nsigmas=3)
+
+    def _weights(self, center, sigma, lb, ub):
+        # Use the gaussian distribution from scipy.stats
+        dist = scipy.stats.norm(center, sigma)
+
+        # Find the mid-points of the cdf intervals
+        cdf = np.linspace(0, 1, self.npts+2)[1:-1]
+        x = dist.ppf(cdf)
+
+        # Since we are equally spaced in cdf, all weights are the same
+        wx = np.ones_like(x)
+
+        # Truncate the distribution in case the parameter value is limited
+        index = (x >= lb) & (x <= ub)
+        x, wx = x[index], wx[index]
+
+        return x, wx
+
Index: example/weights/maier_saupe.py
===================================================================
--- example/weights/maier_saupe.py	(revision 55e82f04d46e375f4f5aa858d1291b28da4d6219)
+++ example/weights/maier_saupe.py	(revision 55e82f04d46e375f4f5aa858d1291b28da4d6219)
@@ -0,0 +1,45 @@
+from __future__ import print_function
+import numpy as np
+from numpy import exp, sin, degrees, radians, pi, sqrt
+
+from sasmodels.weights import Dispersion as BaseDispersion
+
+class Dispersion(BaseDispersion):
+    r"""
+    Maier-Saupe dispersion on orientation.
+
+    .. math:
+
+        w(\theta) = e^{P_2{\cos^2 \theta}}
+
+    This provides a close match to the gaussian distribution for
+    low angles, but the tails are limited to $\pm 90^\circ$.  For $P_2 \ll 1$
+    the distribution is approximately uniform.  The usual polar coordinate
+    projection applies, with $\theta$ weights scaled by $\cos \theta$
+    and $\phi$ weights unscaled.
+
+    This is equivalent to a cyclic gaussian distribution
+    $w(\theta) = e^{-sin^2(\theta)/(2\P_2^2)}.
+    """
+    type = "maier_saupe"
+    default = dict(npts=35, width=1, nsigmas=None)
+
+    # Note: center is always zero for orientation distributions
+    def _weights(self, center, sigma, lb, ub):
+        # use the width parameter as the value for Maier-Saupe P_2
+        # and find the equivalent width sigma
+        P2 = sigma
+        sigma = 1./sqrt(2.*P2)
+
+        # Limit width to +/- 90 degrees
+        width = min(self.nsigmas*sigma, pi/2)
+        x = np.linspace(-width, width, self.npts)
+
+        # Truncate the distribution in case the parameter value is limited
+        x[(x >= radians(lb)) & (x <= radians(ub))]
+
+        # Return orientation in degrees with Maier-Saupe weights
+        # Note: weights are normalized to sum to 1, so we can scale
+        # by an arbitrary scale factor c = exp(m) to get:
+        #     w = exp(m*cos(x)**2)/c = exp(-m*sin(x)**2)
+        return degrees(x), exp(-P2*sin(x)**2)
Index: example/weights/maier_saupe_eq.py
===================================================================
--- example/weights/maier_saupe_eq.py	(revision 55e82f04d46e375f4f5aa858d1291b28da4d6219)
+++ example/weights/maier_saupe_eq.py	(revision 55e82f04d46e375f4f5aa858d1291b28da4d6219)
@@ -0,0 +1,63 @@
+import numpy as np
+from numpy import exp, sin, degrees, radians, pi, sqrt
+
+from sasmodels.weights import Dispersion as BaseDispersion
+
+class Dispersion(BaseDispersion):
+    r"""
+    Maier-Saupe dispersion on orientation (equal weights).
+
+    .. math:
+
+        w(\theta) = e^{P_2{\cos^2 \theta}}
+
+    This provides a close match to the gaussian distribution for
+    low angles, but the tails are limited to $\pm 90^\circ$.  For $P_2 \ll 1$
+    the distribution is approximately uniform.  The usual polar coordinate
+    projection applies, with $\theta$ weights scaled by $\cos \theta$
+    and $\phi$ weights unscaled.
+
+    This is equivalent to a cyclic gaussian distribution
+    $w(\theta) = e^{-sin^2(\theta)/(2\sigma^2)}.
+
+    The $\theta$ points are spaced such that each interval has an
+    equal contribution to the distribution.
+
+    This works surprisingly poorly.  Try::
+
+        $ sascomp cylinder -2d theta=45 phi=20 phi_pd_type=maier_saupe_eq \
+          phi_pd_n=100,1000 radius=50 length=2*radius -midq phi_pd=5
+
+    Leaving it here for others to improve.
+    """
+    type = "maier_saupe_eq"
+    default = dict(npts=35, width=1, nsigmas=None)
+
+    # Note: center is always zero for orientation distributions
+    def _weights(self, center, sigma, lb, ub):
+        # use the width parameter as the value for Maier-Saupe P_2
+        P2 = sigma
+        sigma = 1./sqrt(2.*P2)
+
+        # Create a lookup table for finding n points equally spaced
+        # in the cumulative density function.
+        # Limit width to +/-90 degrees.
+        width = min(self.nsigmas*sigma, pi/2)
+        xp = np.linspace(-width, width, max(self.npts*10, 100))
+
+        # Compute CDF. Since we normalized the sum of the weights to 1,
+        # we can scale by an arbitrary scale factor c = exp(m) to get:
+        #     w = exp(m*cos(x)**2)/c = exp(-m*sin(x)**2)
+        yp = np.cumsum(exp(-P2*sin(xp)**2))
+        yp /= yp[-1]
+
+        # Find the mid-points of the equal-weighted intervals in the CDF
+        y = np.linspace(0, 1, self.npts+2)[1:-1]
+        x = np.interp(y, yp, xp)
+        wx = np.ones(self.npts)
+
+        # Truncate the distribution in case the parameter value is limited
+        index = (x >= lb) & (x <= ub)
+        x, wx = x[index], wx[index]
+
+        return degrees(x), wx
