Index: sasmodels/models/superball.c
===================================================================
--- sasmodels/models/superball.c	(revision 8b7ce55594a7b4ce4a870634a4c91734f8434426)
+++ sasmodels/models/superball.c	(revision 8b7ce55594a7b4ce4a870634a4c91734f8434426)
@@ -0,0 +1,137 @@
+static double
+form_volume(double length_a, double exponent_p)
+{
+  double g1 = sas_gamma(1.0 / (2.0 * exponent_p));
+  double g3 = sas_gamma(3.0 / (2.0 * exponent_p));
+  return cube(length_a) / 12.0 / square(exponent_p) * cube(g1) / g3;
+}
+
+static double
+radius_from_excluded_volume(double length_a, double exponent_p)
+{
+  double g1 = sas_gamma(1.0 / (2.0 * exponent_p));
+  double g3 = sas_gamma(3.0 / (2.0 * exponent_p));
+  double g5 = sas_gamma(5.0 / (2.0 * exponent_p));
+
+  return length_a * g3 * sqrt(3.0 / 10.0 / g1 / g5);
+}
+
+static double
+effective_radius(int mode, double length_a, double exponent_p)
+{
+  switch (mode)
+  {
+  default:
+  case 1: // radius of gyration
+    return radius_from_excluded_volume(length_a, exponent_p);
+  case 2: // equivalent volume sphere
+    return cbrt(form_volume(length_a, exponent_p) / M_4PI_3);
+  case 3: // half length_a
+    return 0.5 * length_a;
+  }
+}
+
+static double oriented_superball(
+    double qx,
+    double qy,
+    double qz,
+    double length_a,
+    double exponent_p)
+{
+  // oriented superball form factor
+
+  // outer integral for x
+  const double radius = length_a / 2.0; // superball radius
+  const double inverse_2p = 1.0 / (2.0 * exponent_p);
+
+  double outer_integral = 0.0; //initialize integral
+
+  for (int i_x = 0; i_x < GAUSS_N; i_x++)
+  {
+    const double x = 0.5 * (GAUSS_Z[i_x] + 1.0); // integrate 0, 1
+    const double x2p = pow(x, 2.0 * exponent_p);
+    const double gamma = pow(1.0 - x2p, inverse_2p);
+
+    // inner integral for y
+    double inner_integral = 0.0; //initialize integral
+    for (int i_y = 0; i_y < GAUSS_N; i_y++)
+    {
+      const double y = 0.5 * gamma * (GAUSS_Z[i_y] + 1.0); // integrate 0, gamma
+      const double y2p = pow(y, 2.0 * exponent_p);
+      const double zeta = pow(1.0 - x2p - y2p, inverse_2p);
+      const double cos1 = cos(radius * qy * y);
+      const double sinc2 = qz == 0 ? radius * zeta : sin(radius * qz * zeta) / qz;
+      const double fq = cos1 * sinc2;
+      inner_integral += GAUSS_W[i_y] * fq;
+    }
+
+    const double co = cos(radius * qx * x);
+
+    // integration factor for -1,1 quadrature to 0, gamma: gamma/2
+    const double integration_factor = 0.5 * gamma;
+    outer_integral += GAUSS_W[i_x] * integration_factor * inner_integral * co;
+  }
+  // integration factor for -1,1 quadrature to 0, 1: 1/2
+  return 0.5 * outer_integral;
+}
+
+static void
+Fq(double q,
+   double *F1,
+   double *F2,
+   double sld,
+   double solvent_sld,
+   double length_a,
+   double exponent_p)
+{
+  double orient_averaged_outer_total_F1 = 0.0; //initialize integral
+  double orient_averaged_outer_total_F2 = 0.0; //initialize integral
+  // phi integral
+  for (int i_phi = 0; i_phi < GAUSS_N; i_phi++)
+  {
+    const double phi = (GAUSS_Z[i_phi] + 1.0) * M_PI_4; // integrate 0 .. pi/2
+    double sin_phi, cos_phi;
+    SINCOS(phi, sin_phi, cos_phi);
+
+    double orient_averaged_inner_total_F1 = 0.0; //initialize integral
+    double orient_averaged_inner_total_F2 = 0.0; //initialize integral
+    // theta integral
+    for (int i_theta = 0; i_theta < GAUSS_N; i_theta++)
+    {
+      const double theta = (GAUSS_Z[i_theta] + 1.0) * M_PI_4; // integrate 0, pi/2
+      double sin_theta, cos_theta;
+      SINCOS(theta, sin_theta, cos_theta);
+      const double qx = q * cos_phi * sin_theta;
+      const double qy = q * sin_phi * sin_theta;
+      const double qz = q * cos_theta;
+
+      const double f_oriented = oriented_superball(qx, qy, qz, length_a, exponent_p);
+
+      orient_averaged_inner_total_F1 += GAUSS_W[i_theta] * f_oriented * sin_theta;
+      orient_averaged_inner_total_F2 += GAUSS_W[i_theta] * square(f_oriented) * sin_theta;
+    }
+    orient_averaged_outer_total_F1 += GAUSS_W[i_phi] * orient_averaged_inner_total_F1;
+    orient_averaged_outer_total_F2 += GAUSS_W[i_phi] * orient_averaged_inner_total_F2;
+  }
+  // Multiply by contrast^2 and convert from [1e-12 A-1] to [cm-1]
+  const double contrast = (sld - solvent_sld);
+  const double s = 2.0 * contrast * square(length_a);
+  *F1 = 1.0e-2 * s * orient_averaged_outer_total_F1 * M_PI_4 * 0.5;
+  *F2 = 1.0e-4 * s * s * orient_averaged_outer_total_F2 * M_PI_4 * 0.5;
+}
+
+static double
+Iqabc(double qa, double qb, double qc,
+      double sld,
+      double solvent_sld,
+      double length_a,
+      double exponent_p)
+{
+  const double f_oriented = oriented_superball(qa, qb, qc, length_a, exponent_p);
+  const double contrast = (sld - solvent_sld);
+  const double s = 2.0 * contrast * square(length_a);
+
+  const double form = square(s * f_oriented);
+  // Square and convert from [1e-12 A-1] to [cm-1]
+  return 1.0e-4 * form;
+}
Index: sasmodels/models/superball.py
===================================================================
--- sasmodels/models/superball.py	(revision 8b7ce55594a7b4ce4a870634a4c91734f8434426)
+++ sasmodels/models/superball.py	(revision 8b7ce55594a7b4ce4a870634a4c91734f8434426)
@@ -0,0 +1,193 @@
+# superball model
+# Note: model title and parameter table are inserted automatically
+r"""
+Definition
+----------
+
+.. figure:: img/superball_realSpace.png
+
+   Superball visualisation for varied values of the parameter p.
+
+This model calculates the scattering of a superball, which represents a cube
+with rounded edges. It can be used to describe nanoparticles that deviate from
+the perfect cube shape as it is often observed experimentally
+[#WetterskogSuperball]_. The shape is described by
+
+.. math::
+
+    x^{2p} + y^{2p} + z^{2p} \leq \biggl( \frac{a}{2} \biggr)^{2p}
+
+with $a$ the cube edge length of the superball and $p$ a parameter that
+describes the roundness of the edges. In the limiting cases $p=1$ the superball
+corresponds to a sphere with radius $R = a/2$ and for $p = \infty$ to a cube
+with edge length $a$. The exponent p is related to $a$ and the face diagonal
+$d$ via
+
+.. math::
+    p = \frac{1}{1 + 2 \mathrm{log}_2 (a/d)}.
+
+.. figure:: img/superball_geometry2d.png
+
+    Cross-sectional view of a superball showing the principal axis length $a$,
+    the face-diagonal $d$ and the superball radius $R$.
+
+The oriented form factor is determined by solving
+
+.. math::
+    p_o(\vec{q}) =& \int_{V} \mathrm{d} \vec{r} e^{i \vec{q} \cdot \vec{r}}\\
+        =& \frac{a^3}{8} \int_{-1}^{1} \mathrm{d} x \int_{-\gamma}^{\gamma}
+            \mathrm{d} y \int_{-\zeta}^{\zeta} \mathrm{d} z
+            e^{i a (q_x x + q_y y + q_z z) / 2}\\
+        =& \frac{a^2}{2 q_z} \int_{-1}^{1} \mathrm{d} x \int_{-\gamma}^{\gamma}
+            \mathrm{d} y  e^{i a(q_x x + q_y y)/2}
+            \mathrm{sin}(q_z a \zeta / 2),\\
+with
+
+.. math::
+    \gamma =& \sqrt[2p]{1-x^{2p}}, \\
+    \zeta =& \sqrt[2p]{1-x^{2p} -y^{2p}}.
+
+The integral can be transformed to
+
+.. math::
+    p_o(\vec{q}) = \frac{2 a^2}{q_z} \int_{0}^{1} \mathrm{d} x \, \mathrm{cos}
+        \biggl(\frac{a q_x x}{2} \biggr) \int_{0}^{\gamma} \mathrm{d} y \,
+        \mathrm{cos} \biggl( \frac{a q_y y}{2} \biggr) \mathrm{sin}
+        \biggl( \frac{a q_z \zeta}{2} \biggr),
+
+which can be solved numerically.
+
+The orientational average is then obtained by calculating
+
+.. math::
+    P(q) = \int_0^{\tfrac{\pi}{2}} \mathrm{d} \varphi \int_0^{\tfrac{\pi}{2}}
+        \mathrm{d} \theta \, \mathrm{sin} (\theta) | p_o(\vec{q}) |^2
+
+with
+
+.. math::
+    \vec{q} &= q \begin{pmatrix} \mathrm{cos} (\varphi) \mathrm{sin} (\theta)\\
+    \mathrm{sin} (\varphi) \mathrm{sin} (\theta)\\
+    \mathrm{cos} (\theta)\end{pmatrix}
+
+The implemented orientationally averaged superball model is then fully given by
+[#DresenSuperball]_
+
+.. math::
+    I(q) = \mathrm{scale} (\Delta \rho)^2 P(q) + \mathrm{background}.
+
+
+FITTING NOTES
+~~~~~~~~~~~~~
+
+Validation
+----------
+
+    The code is validated by reproducing the spherical form factor implemented
+    in SasView for p = 1 and the parallelepiped form factor with a = b = c for
+    p = 1000. The form factors match in the first order oscillation with a
+    precision in the order of $10^{-4}$. The agreement worsens for higher order
+    oscillations and beyond the third order oscillations a higher order Gauss
+    quadrature rule needs to be used to keep the agreement below $10^{-3}$.
+    This is however avoided in this implementation to keep the computation time
+    fast.
+
+References
+----------
+
+.. [#WetterskogSuperball] E. Wetterskog, A. Klapper, S. Disch, E. Josten, R. P. Hermann, U. RÃŒcker, T. BrÃŒckel, L. BergstrÃ¶m and G. Salazar-Alvarez, *Nanoscale*, 8 (2016) 15571
+
+.. [#DresenSuperball] A paper on the superball form factor is in preparation.
+
+Source
+------
+
+`superball.py <https://github.com/SasView/sasmodels/blob/master/sasmodels/models/superball.py>`_
+
+`superball.c <https://github.com/SasView/sasmodels/blob/master/sasmodels/models/superball.c>`_
+
+Authorship and Verification
+----------------------------
+
+* **Author:** Dominique Dresen **Date:** March 27, 2019
+* **Last Modified by:**  Dominique Dresen **Date:** March 27, 2019
+* **Last Reviewed by:**  **Date:**
+* **Source added by :** Dominique Dresen **Date:** March 27, 2019"""
+
+import numpy as np
+from numpy import inf
+
+name = "superball"
+title = "Superball with uniform scattering length density."
+description = """
+    I(q)= scale*V*(sld - sld_solvent)^2*P(q)+background
+        P(q) = (2/pi) * double integral from 0 to pi/2 of ...
+           AP^2(q)*sin(theta)*dtheta*dphi
+        AP = integral from -1 to 1 integral from -g to g of ...
+        cos(R qx x) cos(R qy y]) sin(R qz Ze)
+        g = (1 - x^(2p))^(1/(2p))
+        Ze = (1 - x^(2p) - y^(2p))^(1/(2p))
+"""
+category = "shape:superball"
+
+#             ["name", "units", default, [lower, upper], "type","description"],
+parameters = [["sld", "1e-6/Ang^2", 4, [-inf, inf], "sld",
+               "Superball scattering length density"],
+              ["sld_solvent", "1e-6/Ang^2", 1, [-inf, inf], "sld",
+               "Solvent scattering length density"],
+              ["length_a", "Ang", 50, [0, inf], "volume",
+               "Cube edge length of the superball"],
+              ["exponent_p", "", 2.5, [0, inf], "volume",
+               "Exponent describing the roundness of the superball"],
+              ["theta", "degrees", 0, [-360, 360], "orientation",
+               "c axis to beam angle"],
+              ["phi", "degrees", 0, [-360, 360], "orientation",
+               "rotation about beam"],
+              ["psi", "degrees", 0, [-360, 360], "orientation",
+               "rotation about c axis"],
+              ]
+# lib/gauss76.c
+# lib/gauss20.c
+source = ["lib/gauss20.c", "lib/sas_gamma.c", "superball.c"]
+
+have_Fq = True
+effective_radius_type = [
+    "radius of gyration",
+    "equivalent volume sphere",
+    "half length_a",
+]
+
+
+def random():
+    """Return a random parameter set for the model."""
+    length = np.random.uniform(10, 500)
+    exponent = np.random.uniform(1.5, 5)
+    pars = dict(
+        length_a=length,
+        exponent_p=exponent)
+    return pars
+
+
+# parameters for demo
+demo = dict(scale=1, background=0,
+            sld=6.3, sld_solvent=1.0,
+            length_a=100, exponent_p=2.5,
+            theta=45, phi=30, psi=15,
+            length_a_pd=0.1, length_a_pd_n=10,
+            theta_pd=10, theta_pd_n=1,
+            phi_pd=10, phi_pd_n=1,
+            psi_pd=10, psi_pd_n=1)
+
+
+tests = [
+    [{}, 0.2, 0.7683],
+    [{"length_a": 100., "exponent_p": 1, "sld": 6., "sld_solvent": 1.},
+     0.2, 0.7263],
+    [{"length_a": 100., "exponent_p": 1000, "sld": 6., "sld_solvent": 1.},
+     0.2, 0.2714],
+    [{"length_a": 100., "exponent_p": 2.5, "sld": 6., "sld_solvent": 1.},
+     0.2, 0.2810],
+    [{"length_a": 100., "exponent_p": 2.5, "sld": 6., "sld_solvent": 1.,
+      "length_a_pd": 0.1, "length_a_pd_n": 10},
+     0.2, 0.4955],
+]
