From d4631a06bdd2b813e69abfec931f0bbbfa2f0944 Mon Sep 17 00:00:00 2001
From: "Joachim Wuttke (o)" <j.wuttke@fz-juelich.de>
Date: Tue, 12 Apr 2016 16:13:41 +0200
Subject: [PATCH] Tetrahedron now using the generic Polyhedron

---
 Core/FormFactors/FormFactorCone6.h            |  25 +----
 Core/FormFactors/FormFactorTetrahedron.cpp    | 104 ++++++------------
 Core/FormFactors/FormFactorTetrahedron.h      |  42 ++-----
 Doc/UserManual/Assemblies.tex                 |  28 ++---
 Doc/UserManual/FormFactors.tex                |  51 +++------
 Doc/UserManual/Setup.tex                      |   3 +
 .../ref_FormFactors_Tetrahedron.int.gz        | Bin 5447 -> 5448 bytes
 7 files changed, 79 insertions(+), 174 deletions(-)

diff --git a/Core/FormFactors/FormFactorCone6.h b/Core/FormFactors/FormFactorCone6.h
index 1addfc03c85..1c64f3487e2 100644
--- a/Core/FormFactors/FormFactorCone6.h
+++ b/Core/FormFactors/FormFactorCone6.h
@@ -18,11 +18,6 @@
 
 #include "FormFactorPolyhedron.h"
 
-#include <memory>
-
-// Forward declaration to prevent IntegratorComplex.h to be parsed for Python API:
-template <class T> class IntegratorComplex;
-
 //! @class FormFactorCone6
 //! @ingroup formfactors
 //! @brief The formfactor of a cone6.
@@ -34,12 +29,11 @@ public:
     //! @param height of Cone6
     //! @param angle in radians between base and facet
     FormFactorCone6(double radius, double height,  double alpha);
+    virtual ~FormFactorCone6();
 
     static std::vector<PolyhedralFace> polyhedral_faces(
         double radius, double height,  double alpha);
 
-    virtual ~FormFactorCone6();
-
     virtual FormFactorCone6* clone() const;
 
     virtual void accept(ISampleVisitor *visitor) const;
@@ -59,19 +53,8 @@ private:
     mutable cvector_t m_q;
 };
 
-inline double FormFactorCone6::getHeight() const
-{
-    return m_height;
-}
-
-inline double FormFactorCone6::getRadius() const
-{
-    return m_radius;
-}
-
-inline double FormFactorCone6::getAlpha() const
-{
-    return m_alpha;
-}
+inline double FormFactorCone6::getHeight() const { return m_height; }
+inline double FormFactorCone6::getRadius() const { return m_radius; }
+inline double FormFactorCone6::getAlpha() const { return m_alpha; }
 
 #endif // FORMFACTORCONE6_H
diff --git a/Core/FormFactors/FormFactorTetrahedron.cpp b/Core/FormFactors/FormFactorTetrahedron.cpp
index f27a6587d53..86b134159e2 100644
--- a/Core/FormFactors/FormFactorTetrahedron.cpp
+++ b/Core/FormFactors/FormFactorTetrahedron.cpp
@@ -20,8 +20,8 @@
 
 using namespace  BornAgain;
 
-FormFactorTetrahedron::FormFactorTetrahedron(
-   double length, double height, double alpha)
+FormFactorTetrahedron::FormFactorTetrahedron(double length, double height, double alpha)
+    : FormFactorPolyhedron( polyhedral_faces( length, height, alpha ), 0. )
 {
     setName(FFTetrahedronType);
     m_height = height;
@@ -29,12 +29,39 @@ FormFactorTetrahedron::FormFactorTetrahedron(
     m_alpha = alpha;
     check_initialization();
     init_parameters();
-
-    mP_integrator = make_integrator_complex(this, &FormFactorTetrahedron::Integrand);
 }
 
-FormFactorTetrahedron::~FormFactorTetrahedron()
+FormFactorTetrahedron::~FormFactorTetrahedron() {}
+
+std::vector<PolyhedralFace> FormFactorTetrahedron::polyhedral_faces(
+    double length, double height, double alpha)
 {
+    double a = length;
+    double as = a/2;
+    double ac = a/sqrt(3)/2;
+    double ah = a/sqrt(3);
+    double b = a - 2*sqrt(3)*height/std::tan(alpha);
+    double bs = b/2;
+    double bc = b/sqrt(3)/2;
+    double bh = b/sqrt(3);
+
+    kvector_t V[6] = {
+        // base:
+        { -as, -ac, 0. },
+        {  as, -ac, 0. },
+        {  0.,  ah, 0. },
+        // top:
+        { -bs, -bc, height },
+        {  bs, -bc, height },
+        {  0.,  bh, height } };
+    std::vector<PolyhedralFace> faces;
+    faces.push_back( PolyhedralFace( { V[2], V[1], V[0] } ) );
+    faces.push_back( PolyhedralFace( { V[0], V[1], V[4], V[3] } ) );
+    faces.push_back( PolyhedralFace( { V[1], V[2], V[5], V[4] } ) );
+    faces.push_back( PolyhedralFace( { V[2], V[0], V[3], V[5] } ) );
+    faces.push_back( PolyhedralFace( { V[3], V[4], V[5] } ) );
+
+    return faces;
 }
 
 bool FormFactorTetrahedron::check_initialization() const
@@ -74,70 +101,3 @@ double FormFactorTetrahedron::getRadius() const
 {
     return m_length / 2;
 }
-
-complex_t FormFactorTetrahedron::Integrand(double Z) const
-{
-    static double root3 = std::sqrt(3.);
-    double Rz = m_length/2 -root3*Z/std::tan(m_alpha);
-
-    complex_t xy_part = 0;
-    if (m_q.x()==complex_t(0,0) && m_q.y()==complex_t(0,0)) {
-        xy_part = root3*Rz*Rz;
-    }
-    else {
-        complex_t r3qyRz = root3*m_q.y()*Rz;
-        complex_t expminiqyRdivr3 =
-            std::exp(-complex_t(0.0, 1.0)*m_q.y()*Rz/root3);
-        if (std::abs(m_q.x()*m_q.x()-3.*m_q.y()*m_q.y()) == 0) {
-            xy_part = complex_t(0.0, 1.0)*root3*expminiqyRdivr3*
-                   (std::sin(r3qyRz)-r3qyRz*std::exp(complex_t(0.0, 1.0)*r3qyRz))/
-                m_q.x()/m_q.x();
-        } else {
-            complex_t qxRz = m_q.x()*Rz;
-            xy_part = 2*root3*expminiqyRdivr3/
-                    (m_q.x()*m_q.x()-3.0*m_q.y()*m_q.y())*(
-                        std::exp(complex_t(0.0, 1.0)*r3qyRz) -
-                std::cos(qxRz)-complex_t(0.0, 1.0)*r3qyRz*
-                MathFunctions::sinc(qxRz));
-        }
-    }
-    return xy_part *std::exp(complex_t(0.0, 1.0)*m_q.z()*Z);
-}
-
-complex_t FormFactorTetrahedron::evaluate_for_q(const cvector_t& q) const
-{
-    static double root3 = std::sqrt(3.);
-    const complex_t im(0.0,1.0);
-    double H = m_height;
-    double R = m_length/2;
-    double tga = std::tan(m_alpha);
-    double L = 2*tga*R/root3-H;
-
-    if (std::abs(q.x()) <=  Numeric::double_epsilon ||
-        std::abs(q.y())<=  Numeric::double_epsilon ||
-        std::abs(q.z())<=  Numeric::double_epsilon ||
-        std::abs(q.x())*std::abs(q.x())
-        - 3*std::abs(q.y())*std::abs(q.y()) <=  Numeric::double_epsilon)
-    {
-        if ( std::abs(q.mag()) < Numeric::double_epsilon) {
-            double sqrt3HdivRtga = root3*H/R/tga;
-            return tga/3*R*R*R*(1 - (1-sqrt3HdivRtga)
-                                 *(1-sqrt3HdivRtga)
-                                 *(1-sqrt3HdivRtga));
-        } else {
-            m_q = q;
-            complex_t integral = mP_integrator->integrate(0., m_height);
-            return integral;
-        }
-    } else {
-        //general case
-        const complex_t q1=(1./2.)*((root3*q.x() - q.y())/tga - q.z());
-        const complex_t q2=(1./2.)*((root3*q.x() + q.y())/tga + q.z());
-        const complex_t q3 = (q.y()/tga - q.z()/2.);
-
-        return H*root3*std::exp(im*q.z()*R*tga/root3)/(q.x()*q.x()-3.*q.y()*q.y())*
-            (-(1.+root3*q.y()/q.x())*MathFunctions::sinc(q1*H)*std::exp(im*q1*L)
-             -(1.-root3*q.y()/q.x())*MathFunctions::sinc(q2*H)*std::exp(-im*q2*L) +
-             2.*MathFunctions::sinc(q3*H)*std::exp(im*q3*L));
-    }
-}
diff --git a/Core/FormFactors/FormFactorTetrahedron.h b/Core/FormFactors/FormFactorTetrahedron.h
index a13f5f60d48..6c60dd34141 100644
--- a/Core/FormFactors/FormFactorTetrahedron.h
+++ b/Core/FormFactors/FormFactorTetrahedron.h
@@ -16,17 +16,12 @@
 #ifndef FORMFACTORTETRAHEDRON_H
 #define FORMFACTORTETRAHEDRON_H
 
-#include "IFormFactorBorn.h"
-
-#include <memory>
-
-// Forward declaration to prevent IntegratorComplex.h to be parsed for Python API:
-template <class T> class IntegratorComplex;
+#include "FormFactorPolyhedron.h"
 
 //! @class FormFactorTetrahedron
 //! @ingroup formfactors
 //! @brief The formfactor of tetrahedron.
-class BA_CORE_API_ FormFactorTetrahedron : public IFormFactorBorn
+class BA_CORE_API_ FormFactorTetrahedron : public FormFactorPolyhedron
 {
 public:
     //! @brief Tetrahedron constructor
@@ -36,20 +31,18 @@ public:
     FormFactorTetrahedron(double length, double height, double alpha);
     virtual ~FormFactorTetrahedron();
 
+    static std::vector<PolyhedralFace> polyhedral_faces(
+        double length, double height,  double alpha);
+
     virtual FormFactorTetrahedron *clone() const;
 
     virtual void accept(ISampleVisitor *visitor) const;
 
     virtual double getRadius() const;
-
     double getHeight() const;
-
     double getLength() const;
-
     double getAlpha() const;
 
-    virtual complex_t evaluate_for_q(const cvector_t& q) const;
-
 protected:
     virtual bool check_initialization() const;
     virtual void init_parameters();
@@ -58,29 +51,10 @@ private:
     double m_height;
     double m_length;
     double m_alpha;
-
-    // addition integration
-    mutable cvector_t m_q;
-    complex_t Integrand(double Z) const;
-
-#ifndef GCCXML_SKIP_THIS
-    std::unique_ptr<IntegratorComplex<FormFactorTetrahedron>> mP_integrator;
-#endif
 };
 
-inline double FormFactorTetrahedron::getHeight() const
-{
-    return m_height;
-}
-
-inline double FormFactorTetrahedron::getLength() const
-{
-    return m_length;
-}
-
-inline double FormFactorTetrahedron::getAlpha() const
-{
-    return m_alpha;
-}
+inline double FormFactorTetrahedron::getHeight() const { return m_height; }
+inline double FormFactorTetrahedron::getLength() const { return m_length; }
+inline double FormFactorTetrahedron::getAlpha() const { return m_alpha; }
 
 #endif // FORMFACTORTETRAHEDRON_H
diff --git a/Doc/UserManual/Assemblies.tex b/Doc/UserManual/Assemblies.tex
index 7254543554b..60821c6e94d 100644
--- a/Doc/UserManual/Assemblies.tex
+++ b/Doc/UserManual/Assemblies.tex
@@ -614,7 +614,7 @@ For very diluted distributions of particles, the particles are too far apart fro
 %===============================================================================
 \subsection{The regular lattice and the ideal paracrystal}
 %===============================================================================
-The particles are positioned at regular intervals generating a layout characterized by its base vectors $\mathbf{a}$ and $\mathbf{b}$ (in direct space) and the angle between these two vectors.
+The particles are positioned at regular intervals generating a layout characterized by its base vectors $\v{a}$ and $\v{b}$ (in direct space) and the angle between these two vectors.
 This lattice can be two or one-dimensional depending on the characteristics of the particles. For example when they are infinitely long, the implementation can be simplified and reduced to a "pseudo" 1D system.
 
 \index{Paracrystal}
@@ -648,7 +648,7 @@ Figure~\ref{fig:1dparas_q} shows the evolution of $S(q)$ for different values of
 \label{fig:1dparas_q}
 \end{figure}
 
-In two dimensions, the paracrystal is constructed on a pseudo-regular lattice with base vectors $\mathbf{a}$ and $\mathbf{b}$ using the following conditions for the densities of probabilities:\\ $\int p_{\mathbf{a}}(\r)d^2r=\int p_{\mathbf{b}}(\r)d^2r=1$, $\int \r p_{\mathbf{a}}(\r)d^2r=\mathbf{a}$, $\int \r p_{\mathbf{b}}(\r)d^2r=\mathbf{b}$.\\
+In two dimensions, the paracrystal is constructed on a pseudo-regular lattice with base vectors $\v{a}$ and $\v{b}$ using the following conditions for the densities of probabilities:\\ $\int p_{\v{a}}(\r)d^2r=\int p_{\v{b}}(\r)d^2r=1$, $\int \r p_{\v{a}}(\r)d^2r=\v{a}$, $\int \r p_{\v{b}}(\r)d^2r=\v{b}$.\\
 In the ideal case the deformations along the two axes are decoupled and each unit cell should retain a parallelogram shape. The interference function is given by\\ $S(q_{\plll})=\prod_{k=a,b}\Re\left(\dfrac{1+P_k(q_{\plll})}{1-P_k(q_{\plll})} \right)$ with $P_k$ the Fourier transform of $p_k$, $k=a, b$.
 
 \paragraph{Probability distributions} \mbox{}\\
@@ -1092,7 +1092,7 @@ def get_sample():
 %-------------------------------------------------------------------------------
 \subsection{\Code{InterferenceFunction1DLattice(lattice\_length, xi)}}
 %-------------------------------------------------------------------------------
-where lattice\_length is the lattice constant and $\xi$ the angle in radian between the lattice unit vector and the $\mathbf{x}$-axis of the reference Cartesian frame as shown in fig.~\ref{fig:1dgrating}.
+where lattice\_length is the lattice constant and $\xi$ the angle in radian between the lattice unit vector and the $\v{x}$-axis of the reference Cartesian frame as shown in fig.~\ref{fig:1dgrating}.
 
 \begin{figure}[tb]
 \begin{center}
@@ -1193,8 +1193,8 @@ To illustrate the radial paracrystal interference function, we use the same samp
 where ($L_1$, $L_2$, $\alpha$, $\xi$) are shown in figure~\ref{fig:2dlattice} with
 \begin{itemize}
 \item[]$L_1$, $L_2$ the lengths of the lattice cell,
-\item[]$\alpha$ the angle between the lattice basis vectors $\mathbf{a}, \mathbf{b}$ in direct space,
-\item[] $\xi$ is the angle defining the lattice orientation (set to $0$ by default); it is taken as the angle between the $\mathbf{a}$ vector of the lattice basis and the $\mathbf{x}$ axis of the reference Cartesian frame (as shown in figure~\ref{fig:multil3d}).
+\item[]$\alpha$ the angle between the lattice basis vectors $\v{a}, \v{b}$ in direct space,
+\item[] $\xi$ is the angle defining the lattice orientation (set to $0$ by default); it is taken as the angle between the $\v{a}$ vector of the lattice basis and the $\v{x}$ axis of the reference Cartesian frame (as shown in figure~\ref{fig:multil3d}).
 \end{itemize}
 
 \begin{figure}[tb]
@@ -1207,7 +1207,7 @@ where ($L_1$, $L_2$, $\alpha$, $\xi$) are shown in figure~\ref{fig:2dlattice} wi
 
 Like for the one-dimensional case, a probability distribution function \Code{pdf} has to be defined. One can choose between those listed in Sec.~\ref{baftd} and implements it using \Code{setProbabilityDistribution(pdf)}.
 
-\paragraph{Example} The sample used to run the simulation is made of half-spheres deposited on a substrate. The interference function is "2Dlattice" and the particles are located at the nodes of a square lattice with $L_1=L_2=20$~nm, $\mathbf{a}\equiv \mathbf{b}$ and the probability distribution function is Gaussian. We also use the Decoupling Approximation.
+\paragraph{Example} The sample used to run the simulation is made of half-spheres deposited on a substrate. The interference function is "2Dlattice" and the particles are located at the nodes of a square lattice with $L_1=L_2=20$~nm, $\v{a}\equiv \v{b}$ and the probability distribution function is Gaussian. We also use the Decoupling Approximation.
 
 \begin{lstlisting}[language=python, style=eclipseboxed,numbers=none,nolol,caption={\Code{Python} script to define a 2DLattice interference function between hemi-spherical particles as well as the Decoupling Approximation in \Code{getSimulation()}.  The part specific to the interferences is marked in a red italic font.},label={lst:2dlatticeinterf}]
     #collection of particles
@@ -1252,7 +1252,7 @@ Like for the one-dimensional case, a probability distribution function \Code{pdf
 %-------------------------------------------------------------------------------
 \begin{itemize}
 \item[where] $L_1$, $L_2$ are the lengths of the lattice cell,
-\item[] lattice\_angle the angle between the lattice basis vectors $\mathbf{a}, \mathbf{b}$ in direct space,
+\item[] lattice\_angle the angle between the lattice basis vectors $\v{a}, \v{b}$ in direct space,
 \item[] $\xi$ is the angle defining the lattice orientation (set to $0$ by default).
 \item[] \Code{damping\_length} is used to introduce finite size effects by applying a multiplicative coefficient equal to  $\exp$(-\Code{peak\_distance/damping\_length}) to the Fourier transform of the probability densities. \Code{damping\_length} is equal to 0 by default and, in this case, no correction is applied.
 \end{itemize}
@@ -1265,7 +1265,7 @@ it creates a squared lattice,
 where the angle between the base vectors of the lattice is set to $2\pi/3$ ,
 \end{itemize}
 where
-\Code{domain\_size1, 2} are the dimensions of coherent domains of the paracrystal along the main axes,\\ \Code{peak\_distance} is the same in both directions and $\mathbf{a}\equiv \mathbf{x}$.\\
+\Code{domain\_size1, 2} are the dimensions of coherent domains of the paracrystal along the main axes,\\ \Code{peak\_distance} is the same in both directions and $\v{a}\equiv \v{x}$.\\
 
 Probability distribution functions have to be defined. As the two-dimensional paracrystal is defined from two independent one-dimensional paracrystals, we need two of these functions, using\\ \Code{setProbabilityDistributions(pdf\_1, pdf\_2)}, with \Code{pdf\_{1,2}} related to each main axis of the paracrystal (see figure~\ref{fig:2dparaschematic}).
 
@@ -1312,23 +1312,23 @@ Function  & Parameters & Comments\\
 \Code{InterferenceFunctionNone}  & None & disordered distribution \\
 \hline
 \Code{InterferenceFunction1DLattice} & \Code{lattice\_length} & use only with infinitely long/wide particles \\
-  & $\xi=\widehat{(\mathbf{x},\mathbf{a})}$ & pdf=(Cauchy, Gauss or Voigt)  to be defined\\
+  & $\xi=\widehat{(\v{x},\v{a})}$ & pdf=(Cauchy, Gauss or Voigt)  to be defined\\
 \hline
  \Code{InterferenceFunctionRadialParaCrystal}  & peak\_distance of pdf & pdf=(Cauchy, Gauss or Voigt) to be defined \\
 & damping\_length (optional) & \\
 \hline
  \Code{InterferenceFunction2DLattice}  & L\_1, L\_2: lattice lengths & pdf=(Cauchy, Gauss or Voigt) to be defined\\
-                        & lattice\_angle=$\widehat{(\mathbf{a},\mathbf{b})}$ & \\
-                                                            & $\xi =\widehat{(\mathbf{x},\mathbf{a})}$ & \\
+                        & lattice\_angle=$\widehat{(\v{a},\v{b})}$ & \\
+                                                            & $\xi =\widehat{(\v{x},\v{a})}$ & \\
 \hline
 \Code{InterferenceFunction2DParaCrystal}  & L\_1, L\_2: lattice lengths & 2D pdf=(Cauchy, Gauss or Voigt) to be defined \\
-                          & lattice\_angle=$\widehat{(\mathbf{a},\mathbf{b})}$ & (1 pdf per axis) \\
-& $\xi=\widehat{(\mathbf{x},\mathbf{a})}$ & \\
+                          & lattice\_angle=$\widehat{(\v{a},\v{b})}$ & (1 pdf per axis) \\
+& $\xi=\widehat{(\v{x},\v{a})}$ & \\
 & damping\_length (optional)  &  same for both axes\\
 \hline
 \hline
 \end{tabular}
-\caption{List of interference functions implemented in \BornAgain. pdf : probability distribution function, $\mathbf{a}, \mathbf{b}$ are the lattice base vectors, and $\mathbf{x}$ is the axis vector perpendicular to the detector plane.}
+\caption{List of interference functions implemented in \BornAgain. pdf : probability distribution function, $\v{a}, \v{b}$ are the lattice base vectors, and $\v{x}$ is the axis vector perpendicular to the detector plane.}
 \end{table}
 
 \index{Particle assemblies)}%
diff --git a/Doc/UserManual/FormFactors.tex b/Doc/UserManual/FormFactors.tex
index 163a8fda186..07532feb820 100644
--- a/Doc/UserManual/FormFactors.tex
+++ b/Doc/UserManual/FormFactors.tex
@@ -513,20 +513,12 @@ The parameters must fulfill
 \end{displaymath}
 
 \paragraph{Form factor, volume, horizontal section}\strut\\
-Notation:
-\begin{equation*}
-  R_H \coloneqq R-\frac{H}{\tan\beta},\quad
-  \tilde{q}_x \coloneqq \frac{1}{2}q_y,\quad
-  \tilde{q}_y \coloneqq \frac{\sqrt{3}}{2}q_y,\quad
-  \tilde{q}_z \coloneqq (\tan\beta) q_z.
-\end{equation*}
-Results:
 \begin{equation*}
   F \text{~: computed algebraically,
           using the generic polyhedron form factor~\cite{ba:ffp},}
 \end{equation*}
 \begin{equation*}
-  V = \tan\beta  \left( R^3- R_H^3 \right),
+  V = \tan\beta  \left( R^3- \left(R-\frac{H}{\tan\beta}\right)^3 \right),
 \end{equation*}
 \begin{equation*}
   S =\dfrac{3\sqrt{3}R^2}{2}.
@@ -544,13 +536,13 @@ for four different angles~$\omega$ of rotation around the $z$ axis.}
 \end{figure}
 
 \paragraph{References and History}\strut\\
-Originally computed through numeric integration,
-as the (differently parametrized) form factor \E{Cone6}  of \IsGISAXS\
+Our parametrization deviates from the form factor \E{Cone6} of \IsGISAXS
 \cite[Eq.~2.32]{Laz08} \cite[Eq.~222]{ReLL09}.
-Algebraic implementation
-based on the generic polyhedron form factor \cite{ba:ffp}
-introduced in \BornAgain-1.6.
 
+Up to \BornAgain-1.5 computed by numeric integration, as in \IsGISAXS.
+Since \BornAgain-1.6 higher speed and accuracy are achieved
+by using the generic polyhedron form factor \cite{ba:ffp},
+with series expansions near singularities.
 
 %===============================================================================
 \ffsection{Cuboctahedron} \label{SCuboctahedron}
@@ -1305,6 +1297,8 @@ Agrees with the \E{Ripple2} form factor of \FitGISAXS\ \cite{Bab13}.
 \noindent
 Incorrectly named so, since it actually has five, not four surfaces.
 
+\Work{will soon be rotated by 30$^\circ$}
+
 \begin{figure}[H]
 \hfill
 \subfigure[Perspective]{\includegraphics[width=.24\textwidth]{fig/blue/Tetrahedron3d.png}}
@@ -1337,22 +1331,10 @@ but the angle~$\beta$ between the base and a side edge.
 They are related through $\tan \alpha = 2 \tan \beta$.
 
 \paragraph{Form factor, volume, horizontal section}\strut\\
-Notation:
 \begin{equation*}
-q_1  \coloneqq \frac{1}{2}\left[\frac{q_x\sqrt{3} -q_y}{\tan \alpha}-q_z \right],
-\quad q_2 \coloneqq \frac{1}{2}\left[\frac{q_x\sqrt{3} +q_y}{\tan \alpha}+q_z
-\right], \quad
-q_3 \coloneqq \frac{q_y}{\tan \alpha} -\frac{q_z}{2}, \quad
-D \coloneqq \frac{L \tan \alpha}{\sqrt{3}} -H.
+  F\text{~: computed algebraically,
+          using the generic polyhedron form factor~\cite{ba:ffp},}
 \end{equation*}
-Results:
-\begin{align*}
-&F=\frac{\sqrt{3}H}{q_x (q_x^2-3q_y^2)}
-\exp\left(i\frac{q_z L\tan (\alpha)}{2\sqrt{3}}\right) \times \\
-&\Big\{2q_x \exp(iq_3 D)\sinc(q_3 H) - (q_x +\sqrt{3}q_y)
-\exp(iq_1 D)\sinc(q_1 H)\\
-&-(q_x-\sqrt{3}q_y)\exp(-iq_2 D)\sinc(q_2 H) \Big\},
-\end{align*}
 \begin{equation*}
   V= \dfrac{\tan(\alpha) L^3}{24} \left[1- \left(1 -
   \dfrac{2\sqrt{3} H}{L \tan(\alpha)} \right)^3\right],
@@ -1373,11 +1355,14 @@ for four different angles~$\omega$ of rotation around the $z$ axis.
 The low symmetry requires other angular ranges than used in most other figures.}
 \end{figure}
 
-\paragraph{References}\strut\\
-Agrees with the \E{Tetrahedron} form factor of \IsGISAXS\
-\cite[Eq.~2.30]{Laz08} \cite[Eq.~220]{ReLL09}.
-In \FitGISAXS\ correctly called \E{Truncated tetrahedron} \cite{Bab13}.
-
+\paragraph{References and History}\strut\\
+Previous implementations as \E{Tetrahedron} in \IsGISAXS\
+\cite[Eq.~2.30]{Laz08} \cite[Eq.~220]{ReLL09},
+and as  \E{Truncated tetrahedron} in \FitGISAXS\ \cite{Bab13}.
+Up to \BornAgain-1.5, we computed it by numeric integration, as in \IsGISAXS.
+Since \BornAgain-1.6 higher speed and accuracy are achieved
+by using the generic polyhedron form factor \cite{ba:ffp},
+with series expansions near singularities.
 
 %===============================================================================
 \ffsection{TruncatedCube} \label{STruncatedCube}
diff --git a/Doc/UserManual/Setup.tex b/Doc/UserManual/Setup.tex
index cb8be89ff06..ced26f148d8 100644
--- a/Doc/UserManual/Setup.tex
+++ b/Doc/UserManual/Setup.tex
@@ -54,6 +54,9 @@
 \usepackage{amssymb}
 
 \usepackage[bold-style=ISO]{unicode-math} % must come after ams and symbols
+% from unicode-0.8, use \symbf instead of \mathbf
+% see https://github.com/wspr/unicode-math/issues/340
+%     http://apps.jcns.fz-juelich.de/redmine/issues/1293
 \newif\ifolducm
 \makeatletter
 \@ifpackagelater{unicode-math}{2014/07/01}{\olducmfalse}{\olducmtrue}
diff --git a/Tests/ReferenceData/BornAgain/ref_FormFactors_Tetrahedron.int.gz b/Tests/ReferenceData/BornAgain/ref_FormFactors_Tetrahedron.int.gz
index cd612fb7e8d8b2e33bfe22f7ae765294f4226cea..375b0aa6b471a841b6af46b883974ff3b68ea534 100644
GIT binary patch
delta 5187
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
Q$Mp=Kl;httn=vo|08PvNX#fBK

delta 5186
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
L(NfK3GX@3#6z1x&

-- 
GitLab