docs: regenerate html documentation - sphere - GPU-based 3D discrete element method algorithm with optional fluid coupling
HTML git clone git://src.adamsgaard.dk/sphere
DIR Log
DIR Files
DIR Refs
DIR LICENSE
---
DIR commit 8db43a4f44d5a40467db9fd613706de973506ece
DIR parent 3cf967ad1b0c4a985fa1a5c1fc3d99755b775b99
HTML Author: Anders Damsgaard <anders@adamsgaard.dk>
Date: Mon, 6 Jul 2026 09:20:27 +0000
docs: regenerate html documentation
Diffstat:
M doc/html/.buildinfo | 4 ++--
A doc/html/_sources/cfd.rst.txt | 587 +++++++++++++++++++++++++++++++
A doc/html/_sources/dem.rst.txt | 217 +++++++++++++++++++++++++++++++
A doc/html/_sources/index.rst.txt | 37 +++++++++++++++++++++++++++++++
A doc/html/_sources/introduction.rst… | 49 +++++++++++++++++++++++++++++++
A doc/html/_sources/python_api.rst.t… | 41 +++++++++++++++++++++++++++++++
A doc/html/_sources/quickstart.rst.t… | 77 +++++++++++++++++++++++++++++++
A doc/html/_sources/sphere_internals… | 81 ++++++++++++++++++++++++++++++
A doc/html/_static/base-stemmer.js | 476 +++++++++++++++++++++++++++++++
M doc/html/_static/basic.css | 326 ++++++++++++++++++++++---------
A doc/html/_static/classic.css | 263 +++++++++++++++++++++++++++++++
M doc/html/_static/doctools.js | 382 +++++++++----------------------
A doc/html/_static/documentation_opt… | 14 ++++++++++++++
A doc/html/_static/english-stemmer.js | 1066 ++++++++++++++++++++++++++++++
A doc/html/_static/language_data.js | 13 +++++++++++++
M doc/html/_static/pygments.css | 74 +++++++++++++++++--------------
M doc/html/_static/searchtools.js | 949 ++++++++++++++++++-------------
M doc/html/_static/sidebar.js | 163 +++++++------------------------
A doc/html/_static/sphinx_highlight.… | 159 +++++++++++++++++++++++++++++++
M doc/html/cfd.html | 116 ++++++++++++++++---------------
M doc/html/dem.html | 96 +++++++++++++++++--------------
M doc/html/genindex.html | 68 ++++++++++++++++++-------------
M doc/html/index.html | 112 ++++++++++++++++++-------------
M doc/html/introduction.html | 307 ++++++++-----------------------
M doc/html/objects.inv | 0
M doc/html/py-modindex.html | 45 ++++++++++++++++---------------
M doc/html/python_api.html | 1923 +++++++++++++++++--------------
A doc/html/quickstart.html | 200 +++++++++++++++++++++++++++++++
M doc/html/search.html | 65 +++++++++++++++++--------------
M doc/html/searchindex.js | 4 ++--
M doc/html/sphere_internals.html | 272 ++++++++++++-------------------
31 files changed, 5762 insertions(+), 2424 deletions(-)
---
DIR diff --git a/doc/html/.buildinfo b/doc/html/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
-# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: c4e84a3d2c04280d43617251d14cbd96
+# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
+config: 0da0288272aac7495bdc94873a9cae20
tags: 645f666f9bcd5a90fca523b33c5a78b7
DIR diff --git a/doc/html/_sources/cfd.rst.txt b/doc/html/_sources/cfd.rst.txt
@@ -0,0 +1,587 @@
+Fluid simulation and particle-fluid interaction
+===============================================
+``sphere`` can simulate mixtures of particles and a Newtonian fluid. The fluid
+solvers share the same source code across the CUDA and OpenMP CPU backends,
+using an Eulerian continuum approach coupled to the DEM particles.
+
+The following sections will describe the theoretical background, as well as the
+solution procedure and the numerical implementation.
+
+Derivation of the Navier Stokes equations with porosity
+-------------------------------------------------------
+Following the outline presented by `Limache and Idelsohn (2006)`_, the
+continuity equation for an incompressible fluid material is given by:
+
+.. math::
+ \nabla \cdot \boldsymbol{v} = 0
+
+and the momentum equation:
+
+.. math::
+ \rho \frac{\partial \boldsymbol{v}}{\partial t}
+ + \rho (\boldsymbol{v} \cdot \nabla \boldsymbol{v})
+ = \nabla \cdot \boldsymbol{\sigma}
+ - \boldsymbol{f}^i
+ + \rho \boldsymbol{g}
+
+Here, :math:`\boldsymbol{v}` is the fluid velocity, :math:`\rho` is the
+fluid density, :math:`\boldsymbol{\sigma}` is the `Cauchy stress tensor`_,
+:math:`\boldsymbol{f}^i` is the particle-fluid interaction vector and
+:math:`\boldsymbol{g}` is the gravitational acceleration. For incompressible
+Newtonian fluids, the Cauchy stress is given by:
+
+.. math::
+ \boldsymbol{\sigma} = -p \boldsymbol{I} + \boldsymbol{\tau}
+
+:math:`p` is the fluid pressure, :math:`\boldsymbol{I}` is the identity
+tensor, and :math:`\boldsymbol{\tau}` is the deviatoric stress tensor, given
+by:
+
+.. math::
+ \boldsymbol{\tau} =
+ \mu_f \nabla \boldsymbol{v}
+ + \mu_f (\nabla \boldsymbol{v})^T
+
+By using the following vector identities:
+
+.. math::
+ \nabla \cdot (p \boldsymbol{I}) = \nabla p
+
+ \nabla \cdot (\nabla \boldsymbol{v}) = \nabla^2 \boldsymbol{v}
+
+ \nabla \cdot (\nabla \boldsymbol{v})^T
+ = \nabla (\nabla \cdot \boldsymbol{v})
+
+the deviatoric component of the Cauchy stress tensor simplifies to the
+following, assuming that spatial variations in the viscosity can be neglected:
+
+.. math::
+ = -\nabla p
+ + \mu_f \nabla^2 \boldsymbol{v}
+
+Since we are dealing with fluid flow in a porous medium, additional terms are
+introduced to the equations for conservation of mass and momentum. In the
+following, the equations are derived for the first spatial component. The
+solution for the other components is trivial.
+
+The porosity value (in the saturated porous medium the volumetric fraction of
+the fluid phase) denoted :math:`\phi` is incorporated in the continuity and
+momentum equations. The continuity equation becomes:
+
+.. math::
+ \frac{\partial \phi}{\partial t}
+ + \nabla \cdot (\phi \boldsymbol{v}) = 0
+
+For the :math:`x` component, the Lagrangian formulation of the momentum equation
+with a body force :math:`\boldsymbol{f}` becomes:
+
+.. math::
+ \frac{D (\phi v_x)}{D t}
+ = \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\sigma}) \right]_x
+ - \frac{1}{\rho} f^i_x
+ + \phi g
+
+In the Eulerian formulation, an advection term is added, and the Cauchy stress
+tensor is represented as isotropic and deviatoric components individually:
+
+.. math::
+ \frac{\partial (\phi v_x)}{\partial t}
+ + \boldsymbol{v} \cdot \nabla (\phi v_x)
+ = \frac{1}{\rho} \left[ \nabla \cdot (-\phi p \boldsymbol{I})
+ + \phi \boldsymbol{\tau}) \right]_x
+ - \frac{1}{\rho} f^i_x
+ + \phi g_x
+
+Using vector identities to rewrite the advection term, and expanding the fluid
+stress tensor term:
+
+.. math::
+ \frac{\partial (\phi v_x)}{\partial t}
+ + \nabla \cdot (\phi v_x \boldsymbol{v})
+ - \phi v_x (\nabla \cdot \boldsymbol{v})
+ = \frac{1}{\rho} \left[ -\nabla \phi p \right]_x
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x
+ - \frac{1}{\rho} f^i_x
+ + \phi g_x
+
+Spatial variations in the porosity are neglected,
+
+.. math::
+ \nabla \phi := 0
+
+and the pressure is attributed to the fluid phase alone (model B in Zhu et al.
+2007 and Zhou et al. 2010). The divergence of fluid velocities is defined to be
+zero:
+
+.. math::
+ \nabla \cdot \boldsymbol{v} := 0
+
+With these assumptions, the momentum equation simplifies to:
+
+.. math::
+ \frac{\partial (\phi v_x)}{\partial t}
+ + \nabla \cdot (\phi v_x \boldsymbol{v})
+ = -\frac{1}{\rho} \frac{\partial p}{\partial x}
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x
+ - \frac{1}{\rho} f^i_x
+ + \phi g_x
+
+The remaining part of the advection term is for the :math:`x` component
+found as:
+
+.. math::
+ \nabla \cdot (\phi v_x \boldsymbol{v}) =
+ \left[
+ \frac{\partial}{\partial x},
+ \frac{\partial}{\partial y},
+ \frac{\partial}{\partial z}
+ \right]
+ \left[
+ \begin{array}{c}
+ \phi v_x v_x\\
+ \phi v_x v_y\\
+ \phi v_x v_z\\
+ \end{array}
+ \right]
+ =
+ \frac{\partial (\phi v_x v_x)}{\partial x} +
+ \frac{\partial (\phi v_x v_y)}{\partial y} +
+ \frac{\partial (\phi v_x v_z)}{\partial z}
+
+The deviatoric stress tensor is in this case symmetrical, i.e. :math:`\tau_{ij}
+= \tau_{ji}`, and is found by:
+
+.. math::
+ \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x
+ = \frac{1}{\rho}
+ \left[
+ \left[
+ \frac{\partial}{\partial x},
+ \frac{\partial}{\partial y},
+ \frac{\partial}{\partial z}
+ \right]
+ \phi
+ \left[
+ \begin{matrix}
+ \tau_{xx} & \tau_{xy} & \tau_{xz}\\
+ \tau_{yx} & \tau_{yy} & \tau_{yz}\\
+ \tau_{zx} & \tau_{zy} & \tau_{zz}\\
+ \end{matrix}
+ \right]
+ \right]_x
+
+ = \frac{1}{\rho}
+ \left[
+ \begin{array}{c}
+ \frac{\partial (\phi \tau_{xx})}{\partial x}
+ + \frac{\partial (\phi \tau_{xy})}{\partial y}
+ + \frac{\partial (\phi \tau_{xz})}{\partial z}\\
+ \frac{\partial (\phi \tau_{yx})}{\partial x}
+ + \frac{\partial (\phi \tau_{yy})}{\partial y}
+ + \frac{\partial (\phi \tau_{yz})}{\partial z}\\
+ \frac{\partial (\phi \tau_{zx})}{\partial x}
+ + \frac{\partial (\phi \tau_{zy})}{\partial y}
+ + \frac{\partial (\phi \tau_{zz})}{\partial z}\\
+ \end{array}
+ \right]_x
+ = \frac{1}{\rho}
+ \left(
+ \frac{\partial (\phi \tau_{xx})}{\partial x}
+ + \frac{\partial (\phi \tau_{xy})}{\partial y}
+ + \frac{\partial (\phi \tau_{xz})}{\partial z}
+ \right)
+
+In a linear viscous fluid, the stress and strain rate
+(:math:`\dot{\boldsymbol{\epsilon}}`) is linearly dependent, scaled by the
+viscosity parameter :math:`\mu_f`:
+
+.. math::
+ \tau_{ij} = 2 \mu_f \dot{\epsilon}_{ij}
+ = \mu_f \left(
+ \frac{\partial v_i}{\partial x_j} + \frac{\partial v_j}{\partial x_i}
+ \right)
+
+With this relationship, the deviatoric stress tensor components can be
+calculated as:
+
+.. math::
+ \tau_{xx} = 2 \mu_f \frac{\partial v_x}{\partial x} \qquad
+ \tau_{yy} = 2 \mu_f \frac{\partial v_y}{\partial y} \qquad
+ \tau_{zz} = 2 \mu_f \frac{\partial v_z}{\partial z}
+
+ \tau_{xy} = \mu_f \left(
+ \frac{\partial v_x}{\partial y} + \frac{\partial v_y}{\partial x} \right)
+
+ \tau_{xz} = \mu_f \left(
+ \frac{\partial v_x}{\partial z} + \frac{\partial v_z}{\partial x} \right)
+
+ \tau_{yz} = \mu_f \left(
+ \frac{\partial v_y}{\partial z} + \frac{\partial v_z}{\partial y} \right)
+
+where :math:`\mu_f` is the dynamic viscosity. The above formulation of the
+fluid rheology assumes identical bulk and shear viscosities. The derivation of
+the equations for the other spatial components is trivial.
+
+Porosity estimation
+-------------------
+The solid volume in each fluid cell is determined by the ratio of the
+a cell-centered spherical cell volume (:math:`V_c`) and the sum of intersecting
+particle volumes (:math:`V_s`). The spherical cell volume has a center at
+:math:`\boldsymbol{x}_i`, and a radius of :math:`R_i`, which is equal to half
+the fluid cell width. The nearby particles are characterized by position
+:math:`\boldsymbol{x}_j` and radius :math:`r_j`. The center distance is defined
+as:
+
+.. math::
+ d_{ij} = ||\boldsymbol{x}_i - \boldsymbol{x}_j||
+
+The common volume of the two intersecting spheres is zero if the volumes aren't
+intersecting, lens shaped if they are intersecting, and spherical if the
+particle is fully contained by the spherical cell volume:
+
+.. math::
+ V^s_{i} = \sum_j
+ \begin{cases}
+ 0 & \textit{if } R_i + r_j \leq d_{ij} \\
+ \frac{1}{12d_{ij}} \left[ \pi (R_i + r_j - d_{ij})^2
+ (d_{ij}^2 + 2d_{ij}r_j - 3r_j^2 + 2d_{ij} R_i + 6r_j R_i - 3R_i^2)
+ \right] & \textit{if } R_i - r_j < d_{ij} < R_i + r_j \\
+ \frac{4}{3} \pi r^3_j & \textit{if } d_{ij} \leq R_i - r_j
+ \end{cases}
+
+Using this method, the cell porosity values are continuous through time as
+particles enter and exit the cell volume. The rate of porosity change
+(:math:`d\phi/dt`) is estimated by the backwards Euler method
+by considering the previous and current porosity.
+
+Particle-fluid interaction
+--------------------------
+The momentum exchange of the granular and fluid phases follows the procedure
+outlined by Gidaspow 1992 and Shamy and Zhegal 2005. The fluid and particle
+interaction is based on the concept of drag, where the magnitude is based on
+semi-empirical relationships. The drag force scales linearly with the relative
+difference in velocity between the fluid and particle phase. On the base of
+Newton's third law, the resulting drag force is applied with opposite signs to
+the particle and fluid.
+
+For fluid cells with porosities (:math:`\phi`) less or equal to 0.8, the drag
+force is based on the Ergun (1952) equation:
+
+.. math::
+ \bar{\boldsymbol{f}}_d = \left(
+ 150 \frac{\mu_f (1-\phi)^2}{\phi\bar{d}^2}
+ + 1.75 \frac{(1-\phi)\rho_f
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||}{\bar{d}}
+ \right)
+ (\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p)
+
+here, :math:`\bar{d}` denotes the average particle diameter in the cell,
+:math:`\boldsymbol{v}_f` is the fluid flow velocity, and
+:math:`\bar{\boldsymbol{v}}_p` is the average particle velocity in the cell. All
+particles in contact with the previously mentioned cell-centered sphere for
+porosity estimation contribute to the average particle velocity and diameter in
+the fluid cell.
+
+If the porosity is greater than 0.8, the cell-averaged drag force
+(:math:`\bar{\boldsymbol{f}}_d` is found from the Wen and Yu (1966) equation,
+which considers the fluid flow situation:
+
+.. math::
+ \bar{\boldsymbol{f}}_d = \left(
+ \frac{3}{4}
+ \frac{C_d (1-\phi) \phi^{-2.65} \mu_f \rho_f
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||}{\bar{d}}
+ \right)
+ (\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p)
+
+The drag coefficient :math:`C_d` is evaluated depending on the magnitude of the
+Reynolds number :math:`Re`:
+
+.. math::
+ C_d =
+ \begin{cases}
+ \frac{24}{Re} (1+0.15 (Re)^{0.687} & \textit{if } Re < 1,000 \\
+ 0.44 & \textit{if } Re \geq 1,000
+ \end{cases}
+
+where the Reynold's number is found by:
+
+.. math::
+ Re = \frac{\phi\rho_f\bar{d}}{\mu_f}
+ ||\boldsymbol{v}_f - \bar{\boldsymbol{v}}_p||
+
+The interaction force is applied to the fluid with negative sign as a
+contribution to the body force :math:`\boldsymbol{f}`. The fluid interaction
+force applied particles in the fluid cell is:
+
+.. math::
+ \boldsymbol{f}_i = \frac{\bar{\boldsymbol{f}}_d V_p}{1-\phi}
+
+where :math:`V_p` denotes the particle volume. Optionally, the above
+interaction force could be expanded to include the force induced by the fluid
+pressure gradient:
+
+.. math::
+ \boldsymbol{f}_i = \left(
+ -\nabla p +
+ \frac{\bar{\boldsymbol{f}}_d}{1-\phi}
+ \right) V_p
+
+
+Fluid dynamics solution procedure by operator splitting
+-------------------------------------------------------
+The partial differential terms in the previously described equations are found
+using finite central differences. Modifying the operator splitting methodology
+presented by Langtangen et al. (2002), the predicted velocity
+:math:`\boldsymbol{v}^*` after a finite time step
+:math:`\Delta t` is found by explicit integration of the momentum equation.
+
+.. math::
+ \frac{\Delta (\phi v_x)}{\Delta t}
+ + \nabla \cdot (\phi v_x \boldsymbol{v})
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x}
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x
+ - \frac{1}{\rho} f^i_x
+ + \phi g_x
+
+ \Downarrow
+
+ \phi \frac{\Delta v_x}{\Delta t}
+ + v_x \frac{\Delta \phi}{\Delta t}
+ + \nabla \cdot (\phi v_x \boldsymbol{v})
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x}
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x
+ - \frac{1}{\rho} f^i_x
+ + \phi g_x
+
+We want to isolate :math:`\Delta v_x` in the above equation in order to project
+the new velocity.
+
+.. math::
+ \phi \frac{\Delta v_x}{\Delta t}
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x}
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x
+ - \frac{1}{\rho} f^i_x
+ + \phi g_x
+ - v_x \frac{\Delta \phi}{\Delta t}
+ - \nabla \cdot (\phi v_x \boldsymbol{v})
+
+ \Delta v_x
+ = - \frac{1}{\rho} \frac{\Delta p}{\Delta x} \frac{\Delta t}{\phi}
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi \boldsymbol{\tau}) \right]_x
+ \frac{\Delta t}{\phi}
+ - \frac{\Delta t}{\rho\phi} f^i_x
+ + \Delta t g_x
+ - v_x \frac{\Delta \phi}{\phi}
+ - \nabla \cdot (\phi v_x \boldsymbol{v}) \frac{\Delta t}{\phi}
+
+The term :math:`\beta` is introduced as an adjustable, dimensionless parameter
+in the range :math:`[0;1]`, and determines the importance of the old pressure
+values in the solution procedure (Langtangen et al. 2002). A value of 0
+corresponds to `Chorin's projection method`_ originally described
+in `Chorin (1968)`_.
+
+.. math::
+ v_x^* = v_x^t + \Delta v_x
+
+ v_x^* = v_x^t
+ - \frac{\beta}{\rho} \frac{\Delta p^t}{\Delta x} \frac{\Delta t}{\phi^t}
+ + \frac{1}{\rho} \left[ \nabla \cdot (\phi^t \boldsymbol{\tau}^t) \right]_x
+ \frac{\Delta t}{\phi}
+ - \frac{\Delta t}{\rho\phi} f^i_x
+ + \Delta t g_x
+ - v^t_x \frac{\Delta \phi}{\phi^t}
+ - \nabla \cdot (\phi^t v_x^t \boldsymbol{v}^t) \frac{\Delta t}{\phi^t}
+
+Here, :math:`\Delta x` denotes the cell spacing. The velocity found
+(:math:`v_x^*`) is only a prediction of the fluid velocity at time
+:math:`t+\Delta t`, since the estimate isn't constrained by the continuity
+equation:
+
+.. math::
+ \frac{\Delta \phi^t}{\Delta t} + \nabla \cdot (\phi^t
+ \boldsymbol{v}^{t+\Delta t}) = 0
+
+The divergence of a scalar and vector can be `split`_:
+
+.. math::
+ \phi^t \nabla \cdot \boldsymbol{v}^{t+\Delta t} +
+ \boldsymbol{v}^{t+\Delta t} \cdot \nabla \phi^t
+ + \frac{\Delta \phi^t}{\Delta t} = 0
+
+The predicted velocity is corrected using the new pressure (Langtangen et al.
+2002):
+
+.. math::
+ \boldsymbol{v}^{t+\Delta t} = \boldsymbol{v}^*
+ %- \frac{\Delta t}{\rho} \nabla \epsilon
+ - \frac{\Delta t}{\rho \phi^t} \nabla \epsilon
+ \quad \text{where} \quad
+ \epsilon = p^{t+\Delta t} - \beta p^t
+
+The above formulation of the future velocity is put into the continuity
+equation:
+
+.. math::
+ \Rightarrow
+ \phi^t \nabla \cdot
+ \left( \boldsymbol{v}^* - \frac{\Delta t}{\rho \phi^t} \nabla \epsilon \right)
+ +
+ \left( \boldsymbol{v}^* - \frac{\Delta t}{\rho \phi^t} \nabla \epsilon \right)
+ \cdot \nabla \phi^t + \frac{\Delta \phi^t}{\Delta t} = 0
+
+.. math::
+ \Rightarrow
+ \phi^t \nabla \cdot
+ \boldsymbol{v}^* - \frac{\Delta t}{\rho \phi^t} \phi^t \nabla^2 \epsilon
+ + \nabla \phi^t \cdot \boldsymbol{v}^*
+ - \nabla \phi^t \cdot \nabla \epsilon \frac{\Delta t}{\rho \phi^t}
+ + \frac{\Delta \phi^t}{\Delta t} = 0
+
+.. math::
+ \Rightarrow
+ \frac{\Delta t}{\rho} \nabla^2 \epsilon
+ = \phi^t \nabla \cdot \boldsymbol{v}^*
+ + \nabla \phi^t \cdot \boldsymbol{v}^*
+ - \nabla \phi^t \cdot \nabla \epsilon \frac{\Delta t}{\rho \phi^t}
+ + \frac{\Delta \phi^t}{\Delta t}
+
+The pressure difference in time becomes a `Poisson equation`_ with added terms:
+
+.. math::
+ \Rightarrow
+ \nabla^2 \epsilon
+ = \frac{\nabla \cdot \boldsymbol{v}^* \phi^t \rho}{\Delta t}
+ + \frac{\nabla \phi^t \cdot \boldsymbol{v}^* \rho}{\Delta t}
+ - \frac{\nabla \phi^t \cdot \nabla \epsilon}{\phi^t}
+ + \frac{\Delta \phi^t \rho}{\Delta t^2}
+
+The right hand side of the above equation is termed the *forcing function*
+:math:`f`, which is decomposed into two terms, :math:`f_1` and :math:`f_2`:
+
+.. math::
+ f_1
+ = \frac{\nabla \cdot \boldsymbol{v}^* \phi^t \rho}{\Delta t}
+ + \frac{\nabla \phi^t \cdot \boldsymbol{v}^* \rho}{\Delta t}
+ + \frac{\Delta \phi^t \rho}{\Delta t^2}
+
+ f_2 =
+ \frac{\nabla \phi^t \cdot \nabla \epsilon}{\phi^t}
+
+
+During the `Jacobi iterative solution procedure`_ :math:`f_1` remains constant,
+while :math:`f_2` changes value. For this reason, :math:`f_1` is found only
+during the first iteration, while :math:`f_2` is updated every time. The value
+of the forcing function is found as:
+
+.. math::
+ f = f_1 - f_2
+
+Using second-order finite difference approximations of the Laplace operator
+second-order partial derivatives, the differential equations become a system of
+equations that is solved using `iteratively`_ using Jacobi updates. The total
+number of unknowns is :math:`(n_x - 1)(n_y - 1)(n_z - 1)`.
+
+The discrete Laplacian (approximation of the Laplace operator) can be obtained
+by a finite-difference seven-point stencil in a three-dimensional, cubic
+grid with cell spacing :math:`\Delta x, \Delta y, \Delta z`, considering the six
+face neighbors:
+
+.. math::
+ \nabla^2 \epsilon_{i_x,i_y,i_z} \approx
+ \frac{\epsilon_{i_x-1,i_y,i_z} - 2 \epsilon_{i_x,i_y,i_z}
+ + \epsilon_{i_x+1,i_y,i_z}}{\Delta x^2}
+ + \frac{\epsilon_{i_x,i_y-1,i_z} - 2 \epsilon_{i_x,i_y,i_z}
+ + \epsilon_{i_x,i_y+1,i_z}}{\Delta y^2}
+
+ + \frac{\epsilon_{i_x,i_y,i_z-1} - 2 \epsilon_{i_x,i_y,i_z}
+ + \epsilon_{i_x,i_y,i_z+1}}{\Delta z^2}
+ \approx f_{i_x,i_y,i_z}
+
+Within a Jacobi iteration, the value of the unknowns (:math:`\epsilon^n`) is
+used to find an updated solution estimate (:math:`\epsilon^{n+1}`).
+The solution for the updated value takes the form:
+
+.. math::
+ \epsilon^{n+1}_{i_x,i_y,i_z}
+ = \frac{-\Delta x^2 \Delta y^2 \Delta z^2 f_{i_x,i_y,i_z}
+ + \Delta y^2 \Delta z^2 (\epsilon^n_{i_x-1,i_y,i_z} +
+ \epsilon^n_{i_x+1,i_y,i_z})
+ + \Delta x^2 \Delta z^2 (\epsilon^n_{i_x,i_y-1,i_z} +
+ \epsilon^n_{i_x,i_y+1,i_z})
+ + \Delta x^2 \Delta y^2 (\epsilon^n_{i_x,i_y,i_z-1} +
+ \epsilon^n_{i_x,i_y,i_z+1})}
+ {2 (\Delta x^2 \Delta y^2
+ + \Delta x^2 \Delta z^2
+ + \Delta y^2 \Delta z^2) }
+
+The difference between the current and updated value is termed the *normalized
+residual*:
+
+.. math::
+ r_{i_x,i_y,i_z} = \frac{(\epsilon^{n+1}_{i_x,i_y,i_z}
+ - \epsilon^n_{i_x,i_y,i_z})^2}{(\epsilon^{n+1}_{i_x,i_y,i_z})^2}
+
+Note that the :math:`\epsilon` values cannot be 0 due to the above normalization
+of the residual.
+
+The updated values are at the end of the iteration stored as the current values,
+and the maximal value of the normalized residual is found. If this value is
+larger than a tolerance criteria, the procedure is repeated. The iterative
+procedure is ended if the number of iterations exceeds a defined limit.
+
+After the values of :math:`\epsilon` are found, they are used to find the new
+pressures and velocities:
+
+.. math::
+ \bar{p}^{t+\Delta t} = \beta \bar{p}^t + \epsilon
+
+.. math::
+ \bar{\boldsymbol{v}}^{t+\Delta t} =
+ \bar{\boldsymbol{v}}^* - \frac{\Delta t}{\rho\phi} \nabla \epsilon
+
+
+Boundary conditions
+-------------------
+The lateral boundaries are periodic. This cannot be changed in the current
+version of ``sphere``. This means that the fluid properties at the paired,
+parallel lateral (:math:`x` and :math:`y`) boundaries are identical. A flow
+leaving through one side reappears on the opposite side.
+
+The top and bottom boundary conditions of the fluid grid can be either:
+prescribed pressure (Dirichlet), or prescribed velocity (Neumann). The
+(horizontal) velocities parallel to the boundaries are free to attain other
+values (free slip). The Dirichlet boundary condition is enforced by keeping the
+value of :math:`\epsilon` constant at the boundaries, e.g.:
+
+.. math::
+ \epsilon^{n+1}_{i_x,i_y,i_z = 1 \vee n_z}
+ =
+ \epsilon^{n}_{i_x,i_y,i_z = 1 \vee n_z}
+
+The Neumann boundary condition of no flow across the boundary is enforced by
+setting the gradient of :math:`\epsilon` perpendicular to the boundary to zero,
+e.g.:
+
+.. math::
+ \nabla_z \epsilon^{n+1}_{i_x,i_y,i_z = 1 \vee n_z} = 0
+
+
+Numerical implementation
+------------------------
+Ghost nodes
+
+---
+
+
+
+
+.. _Limache and Idelsohn (2006): http://www.cimec.org.ar/ojs/index.php/mc/article/view/486/464
+.. _Cauchy stress tensor: https://en.wikipedia.org/wiki/Cauchy_stress_tensor
+.. _`Chorin's projection method`: https://en.wikipedia.org/wiki/Projection_method_(fluid_dynamics)#Chorin.27s_projection_method
+.. _`Chorin (1968)`: http://www.ams.org/journals/mcom/1968-22-104/S0025-5718-1968-0242392-2/S0025-5718-1968-0242392-2.pdf
+.. _split: http://www.wolframalpha.com/input/?i=div(p+v)
+.. _Poisson equation: https://en.wikipedia.org/wiki/Poisson's_equation
+.. _`Jacobi iterative solution procedure`: http://www.rsmas.miami.edu/personal/miskandarani/Courses/MSC321/Projects/prjpoisson.pdf
+.. _iteratively: https://en.wikipedia.org/wiki/Relaxation_(iterative_method)
+
DIR diff --git a/doc/html/_sources/dem.rst.txt b/doc/html/_sources/dem.rst.txt
@@ -0,0 +1,217 @@
+Discrete element method
+=======================
+Granular material is a very common form of matter, both in nature and industry.
+It can be defined as material consisting of interacting, discrete particles.
+Common granular materials include gravels, sands and soils, ice bergs,
+asteroids, powders, seeds, and other foods. Over 75% of the raw materials that
+pass through industry are granular. This wide occurrence has driven the desire
+to understand the fundamental mechanics of the material.
+
+Contrary to other common materials such as gases, liquids and solids, a general
+mathematical formulation of it's behavior hasn't yet been found. Granular
+material can, however, display states that somewhat resemble gases, fluids and
+solids.
+
+.. The discrete element method (or distinct element method) was initially
+ formulated by Cundall and Strack (1979). It simulates the physical behavior and
+ interaction of discrete, unbreakable particles, with their own mass and inertia,
+ under the influence of e.g. gravity and boundary conditions such as moving
+ walls. By discretizing time into small time steps, explicit integration of
+ Newton's second law of motion is used to predict the new position and kinematic
+ values for each particle from the previous sums of forces. This Lagrangian
+ approach is ideal for simulating discontinuous materials, such as granular
+ matter.
+ The complexity of the computations is kept low by representing the particles as
+ spheres, which keeps contact-searching algorithms simple.
+
+The `Discrete Element Method
+<https://en.wikipedia.org/wiki/Discrete_element_method>`_ (DEM) is a numerical
+method that can be used to
+simulate the interaction of particles. Originally derived from
+`Molecular Dynamics <https://en.wikipedia.org/wiki/Molecular_dynamics>`_,
+it simulates particles as separate entities, and calculates their positions,
+velocities, and accelerations through time. See Cundall and Strack (1979) and
+`this blog post
+<http://anders-dc.github.io/2013/10/16/the-discrete-element-method/>`_ for
+general introduction to the DEM. The following sections will highlight the
+DEM implementation in ``sphere``. Some of the details are also described in
+Damsgaard et al. 2013. In the used notation, a bold symbol denotes a
+three-dimensional vector, and a dot denotes that the entity is a temporal
+derivative.
+
+Contact search
+--------------
+Homogeneous cubic grid.
+
+.. math::
+ \delta_n^{ij} = ||\boldsymbol{x}^i - \boldsymbol{x}^j|| - (r^i + r^j)
+
+where :math:`r` is the particle radius, and :math:`\boldsymbol{x}` denotes the
+positional vector of a particle, and :math:`i` and :math:`j` denote the indexes
+of two particles. Negative values of :math:`\delta_n` denote that the particles
+are overlapping.
+
+
+Contact interaction
+-------------------
+Now that the inter-particle contacts have been identified and characterized by
+their overlap, the resulting forces from the interaction can be resolved. The
+interaction is decomposed into normal and tangential components, relative to the
+contact interface orientation. The normal vector to the contact interface is
+found by:
+
+.. math::
+ \boldsymbol{n}^{ij} =
+ \frac{\boldsymbol{x}^i - \boldsymbol{x}^j}
+ {||\boldsymbol{x}^i - \boldsymbol{x}^j||}
+
+The contact velocity :math:`\dot{\boldsymbol{\delta}}` is found by:
+
+.. math::
+ \dot{\boldsymbol{\delta}}^{ij} =
+ (\boldsymbol{x}^i - \boldsymbol{x}^j)
+ + (r^i + \frac{\delta_n^{ij}}{2})
+ (\boldsymbol{n}^{ij} \times \boldsymbol{\omega}^{i})
+ + (r^j + \frac{\delta_n^{ij}}{2})
+ (\boldsymbol{n}^{ij} \times \boldsymbol{\omega}^{j})
+
+The contact velocity is decomposed into normal and tangential components,
+relative to the contact interface. The normal component is:
+
+.. math::
+ \dot{\delta}^{ij}_n =
+ -(\dot{\boldsymbol{\delta}}^{ij} \cdot \boldsymbol{n}^{ij})
+
+and the tangential velocity component is found as:
+
+.. math::
+ \dot{\boldsymbol{\delta}}^{ij}_t =
+ \dot{\boldsymbol{\delta}}^{ij}
+ - \boldsymbol{n}^{ij}
+ (\boldsymbol{n}^{ij} \cdot \dot{\boldsymbol{\delta}}^{ij})
+
+where :math:`\boldsymbol{\omega}` is the rotational velocity vector of a
+particle. The total tangential displacement on the contact plane is found
+incrementally:
+
+.. math::
+ \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij} =
+ \int_0^{t_c}
+ \dot{\boldsymbol{\delta}}^{ij}_t \Delta t
+
+where :math:`t_c` is the duration of the contact and :math:`\Delta t` is the
+computational time step length. The tangential contact interface displacement is
+set to zero when a contact pair no longer overlaps. At each time step, the value
+of :math:`\boldsymbol{\delta}_t` is corrected for rotation of the contact
+interface:
+
+.. math::
+ \boldsymbol{\delta}_t^{ij} = \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij}
+ - (\boldsymbol{n}
+ (\boldsymbol{n} \cdot \boldsymbol{\delta}_{t,\text{uncorrected}}^{ij})
+
+With all the geometrical and kinetic components determined, the resulting forces
+of the particle interaction can be determined using a contact model. ``sphere``
+features only one contact model in the normal direction to the contact; the
+linear-elastic-viscous (*Hookean* with viscous damping, or *Kelvin-Voigt*)
+contact model. The resulting force in the normal direction of the contact
+interface on particle :math:`i` is:
+
+.. math::
+ \boldsymbol{f}_n^{ij} = \left(
+ -k_n \delta_n^{ij} -\gamma_n \dot{\delta_n}^{ij}
+ \right) \boldsymbol{n}^{ij}
+
+The parameter :math:`k_n` is the defined `spring coefficient
+<https://en.wikipedia.org/wiki/Hooke's_law>`_ in the normal direction of the
+contact interface, and :math:`\gamma_n` is the defined contact interface
+viscosity, also in the normal direction. The loss of energy in this interaction
+due to the viscous component is for particle :math:`i` calculated as:
+
+.. math::
+ \dot{e}^i_v = \gamma_n (\dot{\delta}^{ij}_n)^2
+
+The tangential force is determined by either a viscous-frictional contact model,
+or a elastic-viscous-frictional contact model. The former contact model is very
+computationally efficient, but somewhat inaccurate relative to the mechanics of
+real materials. The latter contact model is therefore the default, even though
+it results in longer computational times. The tangential force in the
+visco-frictional contact model:
+
+.. math::
+ \boldsymbol{f}_t^{ij} = -\gamma_t \dot{\boldsymbol{\delta}_t}^{ij}
+
+:math:`\gamma_n` is the defined contact interface viscosity in the tangential
+direction. The tangential displacement along the contact interface
+(:math:`\boldsymbol{\delta}_t`) is not calculated and stored for this contact
+model. The tangential force in the more realistic elastic-viscous-frictional
+contact model:
+
+.. math::
+ \boldsymbol{f}_t^{ij} =
+ -k_t \boldsymbol{\delta}_t^{ij} -\gamma_t \dot{\boldsymbol{\delta}_t}^{ij}
+
+The parameter :math:`k_n` is the defined spring coefficient in the tangential
+direction of the contact interface. Note that the tangential force is only
+found if the tangential displacement (:math:`\delta_t`) or the tangential
+velocity (:math:`\dot{\delta}_t`) is non-zero, in order to avoid division by
+zero. Otherwise it is defined as being :math:`[0,0,0]`.
+
+For both types of contact model, the tangential force is limited by the Coulomb
+criterion of static and dynamic friction:
+
+.. math::
+ ||\boldsymbol{f}^{ij}_t|| \leq
+ \begin{cases}
+ \mu_s ||\boldsymbol{f}^{ij}_n|| &
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| = 0 \\
+ \mu_d ||\boldsymbol{f}^{ij}_n|| &
+ \text{if} \quad ||\boldsymbol{f}_t^{ij}|| > 0
+ \end{cases}
+
+If the elastic-viscous-frictional contact model is used and the Coulomb limit is
+reached, the tangential displacement along the contact interface is limited to
+this value:
+
+.. math::
+ \boldsymbol{\delta}_t^{ij} =
+ \frac{1}{k_t} \left(
+ \mu_d ||\boldsymbol{f}_n^{ij}||
+ \frac{\boldsymbol{f}^{ij}_t}{||\boldsymbol{f}^{ij}_t||}
+ + \gamma_t \dot{\boldsymbol{\delta}}_t^{ij} \right)
+
+If the tangential force reaches the Coulomb limit, the energy lost due to
+frictional dissipation is calculated as:
+
+.. math::
+ \dot{e}^i_s = \frac{||\boldsymbol{f}^{ij}_t
+ \dot{\boldsymbol{\delta}}_t^{ij} \Delta t||}{\Delta t}
+
+The loss of energy by viscous dissipation in the tangential direction is not
+found.
+
+
+Temporal integration
+--------------------
+In the DEM, the time is discretized into small steps (:math:`\Delta t`). For each time
+step, the entire network of contacts is resolved, and the resulting forces and
+torques for each particle are found. With these values at hand, the new
+linear and rotational accelerations can be found using
+`Newton's second law <https://en.wikipedia.org/wiki/Newton%27s_laws_of_motion>`_
+of the motion of solid bodies. If a particle with mass :math:`m` at a point in time
+experiences a sum of forces denoted :math:`\boldsymbol{F}`, the resultant acceleration
+(:math:`\boldsymbol{a}`) can be found by rearranging Newton's second law:
+
+.. math::
+ \boldsymbol{F} = m \boldsymbol{a} \Rightarrow \boldsymbol{a} = \frac{\boldsymbol{F}}{m}
+
+The new velocity and position is found by integrating the above equation
+with regards to time. The simplest integration scheme in this regard is the
+`Euler method <https://en.wikipedia.org/wiki/Euler_method>`_:
+
+.. math::
+ \boldsymbol{v} = \boldsymbol{v}_{old} + \boldsymbol{a} \Delta t
+
+.. math::
+ \boldsymbol{p} = \boldsymbol{p}_{old} + \boldsymbol{v} \Delta t
+
DIR diff --git a/doc/html/_sources/index.rst.txt b/doc/html/_sources/index.rst.txt
@@ -0,0 +1,37 @@
+The sphere documentation
+========================
+
+``sphere`` is a 3D DEM particle simulator with optional coupled fluid solvers.
+The same source tree can be built for CUDA GPUs or OpenMP CPUs.
+
+Start with :doc:`quickstart` for installation and build commands, then use the
+model and API chapters for setup, execution, and analysis details.
+
+``sphere`` is developed by Anders Damsgaard as part as his Ph.D. project, under
+supervision of David Lundbek Egholm and Jan A. Piotrowski, all of the Department
+of Geoscience, Aarhus University, Denmark. The author welcomes interested third
+party developers. This document is a work in progress.
+
+Contact: Anders Damsgaard, `<https://adamsgaard.dk>`_,
+`<anders.damsgaard@geo.au.dk>`_
+
+Contents
+--------
+
+.. toctree::
+ :maxdepth: 2
+
+ quickstart
+ introduction
+ dem
+ cfd
+ python_api
+ sphere_internals
+
+Indices and tables
+------------------
+
+.. * :ref:`modindex`
+
+* :ref:`genindex`
+* :ref:`search`
DIR diff --git a/doc/html/_sources/introduction.rst.txt b/doc/html/_sources/introduction.rst.txt
@@ -0,0 +1,49 @@
+Introduction
+============
+
+``sphere`` is used for three-dimensional DEM particle simulations with optional
+two-way coupled fluid flow. Simulation setup and post-processing are usually
+performed through the bundled Python API, while the compiled ``sphere`` binary
+executes the time integration.
+
+The source code can be built for either a CUDA GPU or an OpenMP CPU. See
+:doc:`quickstart` for current build requirements and commands.
+
+The ultimate aim of the ``sphere`` software is to simulate soft-bedded subglacial
+conditions, while retaining the flexibility to perform simulations of granular
+material in other environments.
+
+The purpose of this documentation is to provide the user with a walk-through of
+the workflow, data-analysis and visualization methods of ``sphere``. In
+addition, the ``sphere`` internals are exposed to provide a way to understand the
+numerical routines used by the discrete element method implementation.
+
+.. note:: Command examples in this document starting with the symbol ``$`` are
+ meant to be executed in the shell of the operating system, and ``>>>`` means
+ execution in Python. `IPython <http://ipython.org>`_ is an excellent,
+ interactive Python shell.
+
+All numerical values in this document, the source code, and the configuration
+files are typeset with strict respect to the SI unit system.
+
+Workflow
+--------
+
+A typical simulation workflow is:
+
+#. Create a ``sphere.sim`` object in Python and write an input binary.
+#. Run ``./sphere`` on the input file, optionally with ``--fluid``.
+#. Read output binaries in Python for analysis, visualization, or export.
+
+Updating sphere
+---------------
+
+To update your local version, type the following commands in the ``sphere`` root
+directory::
+
+ git pull
+ cmake -DSPHERE_GPU=OFF .
+ make
+
+Use the CUDA default build by omitting ``-DSPHERE_GPU=OFF`` when CUDA is the
+intended backend.
DIR diff --git a/doc/html/_sources/python_api.rst.txt b/doc/html/_sources/python_api.rst.txt
@@ -0,0 +1,41 @@
+Python API
+==========
+The Python module ``sphere`` is intended as the main interface to the ``sphere``
+application. It is recommended to use this module for simulation setup,
+simulation execution, and analysis of the simulation output data.
+
+Using the module
+----------------
+
+The package lives in ``python/sphere/``. Use it from the repository root with
+``PYTHONPATH=python``.
+
+.. code-block:: sh
+
+ PYTHONPATH=python python3 experiments/collision.py
+
+Most workflows follow this pattern: create :class:`sphere.sim`, initialize
+particles and parameters, call :meth:`sphere.sim.writebin`, run the compiled
+binary with :meth:`sphere.sim.run` or ``./sphere``, then read outputs with
+:meth:`sphere.sim.readlast` or related helpers.
+
+Sample usage
+------------
+Below is a simple, annotated example of how to setup, execute, and post-process
+a ``sphere`` simulation. The example is also found in the ``experiments/``
+folder as ``collision.py``.
+
+.. literalinclude:: ../../experiments/collision.py
+ :language: python
+ :linenos:
+
+The full documentation of the ``sphere`` Python API can be found below.
+
+
+The ``sphere`` module
+---------------------
+.. automodule:: sphere
+ :members:
+ :imported-members:
+ :inherited-members:
+
DIR diff --git a/doc/html/_sources/quickstart.rst.txt b/doc/html/_sources/quickstart.rst.txt
@@ -0,0 +1,77 @@
+Quick start
+===========
+
+Requirements
+------------
+
+Common requirements:
+
+* CMake 3.12 or newer
+* A C++ compiler supported by the selected backend
+* Python 3 with NumPy for simulation setup and analysis
+
+CUDA backend requirements:
+
+* Nvidia CUDA toolkit
+* Nvidia GPU with suitable double-precision support
+
+OpenMP CPU backend requirements:
+
+* OpenMP-capable compiler/runtime
+* On macOS with Apple clang: ``brew install libomp``
+
+Build
+-----
+
+CUDA backend (default)::
+
+ cmake .
+ make
+
+OpenMP CPU backend::
+
+ cmake -DSPHERE_GPU=OFF .
+ make
+
+Run checks
+----------
+
+::
+
+ ./sphere --version
+ ./sphere --help
+ ctest --output-on-failure
+
+Some CFD tests may expose known solver stability limitations on specific
+systems; review failing test output before relying on a solver configuration.
+
+Python workflow
+---------------
+
+Use the bundled Python package by setting ``PYTHONPATH`` from the repository
+root::
+
+ PYTHONPATH=python python3 experiments/collision.py
+
+Most workflows follow this pattern:
+
+#. Create a ``sphere.sim`` object in Python.
+#. Initialize particles, material properties, boundaries, and time settings.
+#. Write an input binary with ``writebin``.
+#. Run the compiled ``sphere`` binary, directly or through the Python API.
+#. Read output binaries for analysis, visualization, or export.
+
+Build documentation
+-------------------
+
+Install Python documentation dependencies::
+
+ python3 -m pip install -r doc/requirements.txt
+
+Full HTML documentation also requires Doxygen for the C++ reference::
+
+ make -C doc/sphinx html
+
+PDF documentation additionally requires a working TeX installation::
+
+ make -C doc/sphinx latexpdf
DIR diff --git a/doc/html/_sources/sphere_internals.rst.txt b/doc/html/_sources/sphere_internals.rst.txt
@@ -0,0 +1,81 @@
+Sphere internals
+================
+
+Command-line interface
+----------------------
+
+.. command-output:: ../../sphere --help
+
+The most common way to invoke ``sphere`` is through the Python API, for example
+with :py:meth:`sphere.sim.run` or :py:meth:`sphere.sim.render`.
+
+Execution pipeline
+------------------
+
+The usual execution path is:
+
+#. Parse command-line options in ``main.cpp``.
+#. Read the binary input file into a ``DEM`` object.
+#. Validate parameters unless ``--dont-check`` or render mode is selected.
+#. Initialize the selected backend and transfer or copy simulation state into
+ the backend arrays.
+#. Repeatedly sort particles into grid cells, resolve contacts, optionally run
+ the fluid solver, integrate particle and wall motion, and write output files
+ at the configured interval.
+#. Free backend and host memory before returning to the caller.
+
+Numerical algorithm
+-------------------
+
+Each simulation step uses the current particle positions to rebuild the grid
+cell index, sorts particles by cell, identifies neighbor contacts, evaluates
+contact and body forces, and integrates translational and rotational degrees of
+freedom. If a coupled fluid solver is enabled, the fluid state and
+particle-fluid interaction forces are updated as part of the loop.
+
+The length of the computational time step, ``time.dt``, is calculated from the
+particle mass and elastic stiffnesses:
+
+.. math::
+
+ \Delta t = 0.075 \min \left( m / \max(k_n, k_t) \right)
+
+where :math:`m` is the particle mass, and :math:`k_n` and :math:`k_t` are the
+normal and tangential elastic stiffnesses. The relationship resolves elastic
+waves several times while they travel through the smallest particle.
+
+Memory layout
+-------------
+
+Host arrays store input/output state and backend arrays use the ``dev_`` prefix
+for the working state used by kernels or emulated kernels. Constant simulation
+parameters use the ``devC_`` prefix.
+
+The internal floating-point precision is defined in ``datatypes.h``. Depending
+on the build it can be either single precision or double precision. Input and
+output data files are written in double precision and converted when necessary.
+
+Three-dimensional variables such as spatial vectors are stored as ``Float4``
+arrays in backend memory to preserve the memory layout expected by both backend
+implementations.
+
+Performance
+-----------
+
+Runtime depends on the number of particles, contact density, selected backend,
+thread or GPU configuration, output interval, and whether the fluid solver or
+raytracer is enabled. Use release builds and tune ``OMP_NUM_THREADS`` when using
+the OpenMP CPU backend.
+
+Compilation
+-----------
+
+The project is built from the repository root with CMake. The default build uses
+the CUDA backend. Pass ``-DSPHERE_GPU=OFF`` to build the OpenMP CPU backend. See
+:doc:`quickstart` for current requirements and build commands.
+
+C++ reference
+-------------
+
+.. doxygenclass:: DEM
+ :members:
DIR diff --git a/doc/html/_static/base-stemmer.js b/doc/html/_static/base-stemmer.js
@@ -0,0 +1,476 @@
+// @ts-check
+
+/**@constructor*/
+BaseStemmer = function() {
+ /** @protected */
+ this.current = '';
+ this.cursor = 0;
+ this.limit = 0;
+ this.limit_backward = 0;
+ this.bra = 0;
+ this.ket = 0;
+
+ /**
+ * @param {string} value
+ */
+ this.setCurrent = function(value) {
+ this.current = value;
+ this.cursor = 0;
+ this.limit = this.current.length;
+ this.limit_backward = 0;
+ this.bra = this.cursor;
+ this.ket = this.limit;
+ };
+
+ /**
+ * @return {string}
+ */
+ this.getCurrent = function() {
+ return this.current;
+ };
+
+ /**
+ * @param {BaseStemmer} other
+ */
+ this.copy_from = function(other) {
+ /** @protected */
+ this.current = other.current;
+ this.cursor = other.cursor;
+ this.limit = other.limit;
+ this.limit_backward = other.limit_backward;
+ this.bra = other.bra;
+ this.ket = other.ket;
+ };
+
+ /**
+ * @param {number[]} s
+ * @param {number} min
+ * @param {number} max
+ * @return {boolean}
+ */
+ this.in_grouping = function(s, min, max) {
+ /** @protected */
+ if (this.cursor >= this.limit) return false;
+ var ch = this.current.charCodeAt(this.cursor);
+ if (ch > max || ch < min) return false;
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
+ this.cursor++;
+ return true;
+ };
+
+ /**
+ * @param {number[]} s
+ * @param {number} min
+ * @param {number} max
+ * @return {boolean}
+ */
+ this.go_in_grouping = function(s, min, max) {
+ /** @protected */
+ while (this.cursor < this.limit) {
+ var ch = this.current.charCodeAt(this.cursor);
+ if (ch > max || ch < min)
+ return true;
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0)
+ return true;
+ this.cursor++;
+ }
+ return false;
+ };
+
+ /**
+ * @param {number[]} s
+ * @param {number} min
+ * @param {number} max
+ * @return {boolean}
+ */
+ this.in_grouping_b = function(s, min, max) {
+ /** @protected */
+ if (this.cursor <= this.limit_backward) return false;
+ var ch = this.current.charCodeAt(this.cursor - 1);
+ if (ch > max || ch < min) return false;
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
+ this.cursor--;
+ return true;
+ };
+
+ /**
+ * @param {number[]} s
+ * @param {number} min
+ * @param {number} max
+ * @return {boolean}
+ */
+ this.go_in_grouping_b = function(s, min, max) {
+ /** @protected */
+ while (this.cursor > this.limit_backward) {
+ var ch = this.current.charCodeAt(this.cursor - 1);
+ if (ch > max || ch < min) return true;
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return true;
+ this.cursor--;
+ }
+ return false;
+ };
+
+ /**
+ * @param {number[]} s
+ * @param {number} min
+ * @param {number} max
+ * @return {boolean}
+ */
+ this.out_grouping = function(s, min, max) {
+ /** @protected */
+ if (this.cursor >= this.limit) return false;
+ var ch = this.current.charCodeAt(this.cursor);
+ if (ch > max || ch < min) {
+ this.cursor++;
+ return true;
+ }
+ ch -= min;
+ if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) {
+ this.cursor++;
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * @param {number[]} s
+ * @param {number} min
+ * @param {number} max
+ * @return {boolean}
+ */
+ this.go_out_grouping = function(s, min, max) {
+ /** @protected */
+ while (this.cursor < this.limit) {
+ var ch = this.current.charCodeAt(this.cursor);
+ if (ch <= max && ch >= min) {
+ ch -= min;
+ if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) != 0) {
+ return true;
+ }
+ }
+ this.cursor++;
+ }
+ return false;
+ };
+
+ /**
+ * @param {number[]} s
+ * @param {number} min
+ * @param {number} max
+ * @return {boolean}
+ */
+ this.out_grouping_b = function(s, min, max) {
+ /** @protected */
+ if (this.cursor <= this.limit_backward) return false;
+ var ch = this.current.charCodeAt(this.cursor - 1);
+ if (ch > max || ch < min) {
+ this.cursor--;
+ return true;
+ }
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) {
+ this.cursor--;
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * @param {number[]} s
+ * @param {number} min
+ * @param {number} max
+ * @return {boolean}
+ */
+ this.go_out_grouping_b = function(s, min, max) {
+ /** @protected */
+ while (this.cursor > this.limit_backward) {
+ var ch = this.current.charCodeAt(this.cursor - 1);
+ if (ch <= max && ch >= min) {
+ ch -= min;
+ if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) != 0) {
+ return true;
+ }
+ }
+ this.cursor--;
+ }
+ return false;
+ };
+
+ /**
+ * @param {string} s
+ * @return {boolean}
+ */
+ this.eq_s = function(s)
+ {
+ /** @protected */
+ if (this.limit - this.cursor < s.length) return false;
+ if (this.current.slice(this.cursor, this.cursor + s.length) != s)
+ {
+ return false;
+ }
+ this.cursor += s.length;
+ return true;
+ };
+
+ /**
+ * @param {string} s
+ * @return {boolean}
+ */
+ this.eq_s_b = function(s)
+ {
+ /** @protected */
+ if (this.cursor - this.limit_backward < s.length) return false;
+ if (this.current.slice(this.cursor - s.length, this.cursor) != s)
+ {
+ return false;
+ }
+ this.cursor -= s.length;
+ return true;
+ };
+
+ /**
+ * @param {Among[]} v
+ * @return {number}
+ */
+ this.find_among = function(v)
+ {
+ /** @protected */
+ var i = 0;
+ var j = v.length;
+
+ var c = this.cursor;
+ var l = this.limit;
+
+ var common_i = 0;
+ var common_j = 0;
+
+ var first_key_inspected = false;
+
+ while (true)
+ {
+ var k = i + ((j - i) >>> 1);
+ var diff = 0;
+ var common = common_i < common_j ? common_i : common_j; // smaller
+ // w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional)
+ var w = v[k];
+ var i2;
+ for (i2 = common; i2 < w[0].length; i2++)
+ {
+ if (c + common == l)
+ {
+ diff = -1;
+ break;
+ }
+ diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2);
+ if (diff != 0) break;
+ common++;
+ }
+ if (diff < 0)
+ {
+ j = k;
+ common_j = common;
+ }
+ else
+ {
+ i = k;
+ common_i = common;
+ }
+ if (j - i <= 1)
+ {
+ if (i > 0) break; // v->s has been inspected
+ if (j == i) break; // only one item in v
+
+ // - but now we need to go round once more to get
+ // v->s inspected. This looks messy, but is actually
+ // the optimal approach.
+
+ if (first_key_inspected) break;
+ first_key_inspected = true;
+ }
+ }
+ do {
+ var w = v[i];
+ if (common_i >= w[0].length)
+ {
+ this.cursor = c + w[0].length;
+ if (w.length < 4) return w[2];
+ var res = w[3](this);
+ this.cursor = c + w[0].length;
+ if (res) return w[2];
+ }
+ i = w[1];
+ } while (i >= 0);
+ return 0;
+ };
+
+ // find_among_b is for backwards processing. Same comments apply
+ /**
+ * @param {Among[]} v
+ * @return {number}
+ */
+ this.find_among_b = function(v)
+ {
+ /** @protected */
+ var i = 0;
+ var j = v.length
+
+ var c = this.cursor;
+ var lb = this.limit_backward;
+
+ var common_i = 0;
+ var common_j = 0;
+
+ var first_key_inspected = false;
+
+ while (true)
+ {
+ var k = i + ((j - i) >> 1);
+ var diff = 0;
+ var common = common_i < common_j ? common_i : common_j;
+ var w = v[k];
+ var i2;
+ for (i2 = w[0].length - 1 - common; i2 >= 0; i2--)
+ {
+ if (c - common == lb)
+ {
+ diff = -1;
+ break;
+ }
+ diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2);
+ if (diff != 0) break;
+ common++;
+ }
+ if (diff < 0)
+ {
+ j = k;
+ common_j = common;
+ }
+ else
+ {
+ i = k;
+ common_i = common;
+ }
+ if (j - i <= 1)
+ {
+ if (i > 0) break;
+ if (j == i) break;
+ if (first_key_inspected) break;
+ first_key_inspected = true;
+ }
+ }
+ do {
+ var w = v[i];
+ if (common_i >= w[0].length)
+ {
+ this.cursor = c - w[0].length;
+ if (w.length < 4) return w[2];
+ var res = w[3](this);
+ this.cursor = c - w[0].length;
+ if (res) return w[2];
+ }
+ i = w[1];
+ } while (i >= 0);
+ return 0;
+ };
+
+ /* to replace chars between c_bra and c_ket in this.current by the
+ * chars in s.
+ */
+ /**
+ * @param {number} c_bra
+ * @param {number} c_ket
+ * @param {string} s
+ * @return {number}
+ */
+ this.replace_s = function(c_bra, c_ket, s)
+ {
+ /** @protected */
+ var adjustment = s.length - (c_ket - c_bra);
+ this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket);
+ this.limit += adjustment;
+ if (this.cursor >= c_ket) this.cursor += adjustment;
+ else if (this.cursor > c_bra) this.cursor = c_bra;
+ return adjustment;
+ };
+
+ /**
+ * @return {boolean}
+ */
+ this.slice_check = function()
+ {
+ /** @protected */
+ if (this.bra < 0 ||
+ this.bra > this.ket ||
+ this.ket > this.limit ||
+ this.limit > this.current.length)
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /**
+ * @param {number} c_bra
+ * @return {boolean}
+ */
+ this.slice_from = function(s)
+ {
+ /** @protected */
+ var result = false;
+ if (this.slice_check())
+ {
+ this.replace_s(this.bra, this.ket, s);
+ result = true;
+ }
+ return result;
+ };
+
+ /**
+ * @return {boolean}
+ */
+ this.slice_del = function()
+ {
+ /** @protected */
+ return this.slice_from("");
+ };
+
+ /**
+ * @param {number} c_bra
+ * @param {number} c_ket
+ * @param {string} s
+ */
+ this.insert = function(c_bra, c_ket, s)
+ {
+ /** @protected */
+ var adjustment = this.replace_s(c_bra, c_ket, s);
+ if (c_bra <= this.bra) this.bra += adjustment;
+ if (c_bra <= this.ket) this.ket += adjustment;
+ };
+
+ /**
+ * @return {string}
+ */
+ this.slice_to = function()
+ {
+ /** @protected */
+ var result = '';
+ if (this.slice_check())
+ {
+ result = this.current.slice(this.bra, this.ket);
+ }
+ return result;
+ };
+
+ /**
+ * @return {string}
+ */
+ this.assign_to = function()
+ {
+ /** @protected */
+ return this.current.slice(0, this.limit);
+ };
+};
DIR diff --git a/doc/html/_static/basic.css b/doc/html/_static/basic.css
@@ -1,12 +1,5 @@
/*
- * basic.css
- * ~~~~~~~~~
- *
* Sphinx stylesheet -- basic theme.
- *
- * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
*/
/* -- main layout ----------------------------------------------------------- */
@@ -15,6 +8,12 @@ div.clearer {
clear: both;
}
+div.section::after {
+ display: block;
+ content: '';
+ clear: left;
+}
+
/* -- relbar ---------------------------------------------------------------- */
div.related {
@@ -109,22 +108,18 @@ img {
/* -- search page ----------------------------------------------------------- */
ul.search {
- margin: 10px 0 0 20px;
- padding: 0;
+ margin-top: 10px;
}
ul.search li {
- padding: 5px 0 5px 20px;
- background-image: url(file.png);
- background-repeat: no-repeat;
- background-position: 0 7px;
+ padding: 5px 0;
}
ul.search li a {
font-weight: bold;
}
-ul.search li div.context {
+ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
@@ -216,7 +211,7 @@ table.modindextable td {
/* -- general body styles --------------------------------------------------- */
div.body {
- min-width: 450px;
+ min-width: 360px;
max-width: 800px;
}
@@ -231,14 +226,8 @@ a.headerlink {
visibility: hidden;
}
-a.brackets:before,
-span.brackets > a:before{
- content: "[";
-}
-
-a.brackets:after,
-span.brackets > a:after {
- content: "]";
+a:visited {
+ color: #551A8B;
}
h1:hover > a.headerlink,
@@ -271,25 +260,25 @@ p.rubric {
font-weight: bold;
}
-img.align-left, .figure.align-left, object.align-left {
+img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
-img.align-right, .figure.align-right, object.align-right {
+img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
-img.align-center, .figure.align-center, object.align-center {
+img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
-img.align-default, .figure.align-default {
+img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
@@ -313,24 +302,35 @@ img.align-default, .figure.align-default {
/* -- sidebars -------------------------------------------------------------- */
-div.sidebar {
+div.sidebar,
+aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
- padding: 7px 7px 0 7px;
+ padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
+ clear: right;
+ overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
+nav.contents,
+aside.topic,
+div.admonition, div.topic, blockquote {
+ clear: left;
+}
+
/* -- topics ---------------------------------------------------------------- */
+nav.contents,
+aside.topic,
div.topic {
border: 1px solid #ccc;
- padding: 7px 7px 0 7px;
+ padding: 7px;
margin: 10px 0 10px 0;
}
@@ -352,10 +352,6 @@ div.admonition dt {
font-weight: bold;
}
-div.admonition dl {
- margin-bottom: 0;
-}
-
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
@@ -366,9 +362,34 @@ div.body p.centered {
margin-top: 25px;
}
+/* -- content of sidebars/topics/admonitions -------------------------------- */
+
+div.sidebar > :last-child,
+aside.sidebar > :last-child,
+nav.contents > :last-child,
+aside.topic > :last-child,
+div.topic > :last-child,
+div.admonition > :last-child {
+ margin-bottom: 0;
+}
+
+div.sidebar::after,
+aside.sidebar::after,
+nav.contents::after,
+aside.topic::after,
+div.topic::after,
+div.admonition::after,
+blockquote::after {
+ display: block;
+ content: '';
+ clear: both;
+}
+
/* -- tables ---------------------------------------------------------------- */
table.docutils {
+ margin-top: 10px;
+ margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
@@ -398,10 +419,6 @@ table.docutils td, table.docutils th {
border-bottom: 1px solid #aaa;
}
-table.footnote td, table.footnote th {
- border: 0 !important;
-}
-
th {
text-align: left;
padding-right: 5px;
@@ -416,32 +433,34 @@ table.citation td {
border-bottom: none;
}
-th > p:first-child,
-td > p:first-child {
+th > :first-child,
+td > :first-child {
margin-top: 0px;
}
-th > p:last-child,
-td > p:last-child {
+th > :last-child,
+td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
-div.figure {
+div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
-div.figure p.caption {
+div.figure p.caption, figcaption {
padding: 0.3em;
}
-div.figure p.caption span.caption-number {
+div.figure p.caption span.caption-number,
+figcaption span.caption-number {
font-style: italic;
}
-div.figure p.caption span.caption-text {
+div.figure p.caption span.caption-text,
+figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
@@ -468,10 +487,71 @@ table.field-list td, table.field-list th {
/* -- hlist styles ---------------------------------------------------------- */
+table.hlist {
+ margin: 1em 0;
+}
+
table.hlist td {
vertical-align: top;
}
+/* -- object description styles --------------------------------------------- */
+
+.sig {
+ font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+}
+
+.sig-name, code.descname {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+.sig-name {
+ font-size: 1.1em;
+}
+
+code.descname {
+ font-size: 1.2em;
+}
+
+.sig-prename, code.descclassname {
+ background-color: transparent;
+}
+
+.optional {
+ font-size: 1.3em;
+}
+
+.sig-paren {
+ font-size: larger;
+}
+
+.sig-param.n {
+ font-style: italic;
+}
+
+/* C++ specific styling */
+
+.sig-inline.c-texpr,
+.sig-inline.cpp-texpr {
+ font-family: unset;
+}
+
+.sig.c .k, .sig.c .kt,
+.sig.cpp .k, .sig.cpp .kt {
+ color: #0033B3;
+}
+
+.sig.c .m,
+.sig.cpp .m {
+ color: #1750EB;
+}
+
+.sig.c .s, .sig.c .sc,
+.sig.cpp .s, .sig.cpp .sc {
+ color: #067D17;
+}
+
/* -- other body styles ----------------------------------------------------- */
@@ -495,26 +575,53 @@ ol.upperroman {
list-style: upper-roman;
}
-li > p:first-child {
+:not(li) > ol > li:first-child > :first-child,
+:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
-li > p:last-child {
+:not(li) > ol > li:last-child > :last-child,
+:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
-dl.footnote > dt,
-dl.citation > dt {
- float: left;
+ol.simple ol p,
+ol.simple ul p,
+ul.simple ol p,
+ul.simple ul p {
+ margin-top: 0;
}
-dl.footnote > dd,
-dl.citation > dd {
- margin-bottom: 0em;
+ol.simple > li:not(:first-child) > p,
+ul.simple > li:not(:first-child) > p {
+ margin-top: 0;
}
-dl.footnote > dd:after,
-dl.citation > dd:after {
+ol.simple p,
+ul.simple p {
+ margin-bottom: 0;
+}
+
+aside.footnote > span,
+div.citation > span {
+ float: left;
+}
+aside.footnote > span:last-of-type,
+div.citation > span:last-of-type {
+ padding-right: 0.5em;
+}
+aside.footnote > p {
+ margin-left: 2em;
+}
+div.citation > p {
+ margin-left: 4em;
+}
+aside.footnote > p:last-of-type,
+div.citation > p:last-of-type {
+ margin-bottom: 0em;
+}
+aside.footnote > p:last-of-type:after,
+div.citation > p:last-of-type:after {
content: "";
clear: both;
}
@@ -531,10 +638,6 @@ dl.field-list > dt {
padding-right: 5px;
}
-dl.field-list > dt:after {
- content: ":";
-}
-
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
@@ -546,7 +649,7 @@ dl {
margin-bottom: 15px;
}
-dd > p:first-child {
+dd > :first-child {
margin-top: 0px;
}
@@ -560,6 +663,21 @@ dd {
margin-left: 30px;
}
+.sig dd {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+.sig dl {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+dl > dd:last-child,
+dl > dd:last-child > :last-child {
+ margin-bottom: 0;
+}
+
dt:target, span.highlighted {
background-color: #fbe54e;
}
@@ -573,14 +691,6 @@ dl.glossary dt {
font-size: 1.1em;
}
-.optional {
- font-size: 1.3em;
-}
-
-.sig-paren {
- font-size: larger;
-}
-
.versionmodified {
font-style: italic;
}
@@ -621,8 +731,9 @@ dl.glossary dt {
.classifier:before {
font-style: normal;
- margin: 0.5em;
+ margin: 0 0.5em;
content: ":";
+ display: inline-block;
}
abbr, acronym {
@@ -637,29 +748,69 @@ pre {
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
+pre, div[class*="highlight-"] {
+ clear: both;
+}
+
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
+ white-space: nowrap;
+}
+
+div[class*="highlight-"] {
+ margin: 1em 0;
}
td.linenos pre {
- padding: 5px 0px;
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
- margin-left: 0.5em;
+ display: block;
+}
+
+table.highlighttable tbody {
+ display: block;
+}
+
+table.highlighttable tr {
+ display: flex;
}
table.highlighttable td {
- padding: 0 0.5em 0 0.5em;
+ margin: 0;
+ padding: 0;
+}
+
+table.highlighttable td.linenos {
+ padding-right: 0.5em;
+}
+
+table.highlighttable td.code {
+ flex: 1;
+ overflow: hidden;
+}
+
+.highlight .hll {
+ display: block;
+}
+
+div.highlight pre,
+table.highlighttable pre {
+ margin: 0;
+}
+
+div.code-block-caption + div {
+ margin-top: 0;
}
div.code-block-caption {
+ margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
@@ -668,8 +819,14 @@ div.code-block-caption code {
background-color: transparent;
}
-div.code-block-caption + div > div.highlight > pre {
- margin-top: 0;
+table.highlighttable td.linenos,
+span.linenos,
+div.highlight span.gp { /* gp: Generic.Prompt */
+ user-select: none;
+ -webkit-user-select: text; /* Safari fallback only */
+ -webkit-user-select: none; /* Chrome/Safari */
+ -moz-user-select: none; /* Firefox */
+ -ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
@@ -681,21 +838,7 @@ div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
- padding: 1em 1em 0;
-}
-
-div.literal-block-wrapper div.highlight {
- margin: 0;
-}
-
-code.descname {
- background-color: transparent;
- font-weight: bold;
- font-size: 1.2em;
-}
-
-code.descclassname {
- background-color: transparent;
+ margin: 1em 0;
}
code.xref, a code {
@@ -736,8 +879,7 @@ span.eqno {
}
span.eqno a.headerlink {
- position: relative;
- left: 0px;
+ position: absolute;
z-index: 1;
}
DIR diff --git a/doc/html/_static/classic.css b/doc/html/_static/classic.css
@@ -0,0 +1,262 @@
+/*
+ * Sphinx stylesheet -- classic theme.
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+html {
+ /* CSS hack for macOS's scrollbar (see #1125) */
+ background-color: #FFFFFF;
+}
+
+body {
+ font-family: sans-serif;
+ font-size: 100%;
+ background-color: #11303d;
+ color: #000;
+ margin: 0;
+ padding: 0;
+}
+
+div.document {
+ display: flex;
+ background-color: #1c4e63;
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 230px;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 0 20px 30px 20px;
+}
+
+div.footer {
+ color: #ffffff;
+ width: 100%;
+ padding: 9px 0 9px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: #ffffff;
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: #133f52;
+ line-height: 30px;
+ color: #ffffff;
+}
+
+div.related a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar {
+}
+
+div.sphinxsidebar h3 {
+ font-family: 'Trebuchet MS', sans-serif;
+ color: #ffffff;
+ font-size: 1.4em;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h3 a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar h4 {
+ font-family: 'Trebuchet MS', sans-serif;
+ color: #ffffff;
+ font-size: 1.3em;
+ font-weight: normal;
+ margin: 5px 0 0 0;
+ padding: 0;
+}
+
+div.sphinxsidebar p {
+ color: #ffffff;
+}
+
+div.sphinxsidebar p.topless {
+ margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px;
+ padding: 0;
+ color: #ffffff;
+}
+
+div.sphinxsidebar a {
+ color: #98dbcc;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+
+
+/* -- hyperlink styles ------------------------------------------------------ */
+
+a {
+ color: #355f7c;
+ text-decoration: none;
+}
+
+a:visited {
+ color: #551a8b;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+
+
+/* -- body styles ----------------------------------------------------------- */
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: 'Trebuchet MS', sans-serif;
+ background-color: #f2f2f2;
+ font-weight: normal;
+ color: #20435c;
+ border-bottom: 1px solid #ccc;
+ margin: 20px -20px 10px -20px;
+ padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 160%; }
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: #c60f0f;
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: #c60f0f;
+ color: white;
+}
+
+div.body p, div.body dd, div.body li, div.body blockquote {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.admonition p {
+ margin-bottom: 5px;
+}
+
+div.admonition pre {
+ margin-bottom: 5px;
+}
+
+div.admonition ul, div.admonition ol {
+ margin-bottom: 5px;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+nav.contents,
+aside.topic,
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ padding: 5px;
+ background-color: unset;
+ color: unset;
+ line-height: 120%;
+ border: 1px solid #ac9;
+ border-left: none;
+ border-right: none;
+}
+
+code {
+ background-color: #ecf0f3;
+ padding: 0 1px 0 1px;
+ font-size: 0.95em;
+}
+
+th, dl.field-list > dt {
+ background-color: #ede;
+}
+
+.warning code {
+ background: #efc2c2;
+}
+
+.note code {
+ background: #d6d6d6;
+}
+
+.viewcode-back {
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+}
+
+div.code-block-caption {
+ color: #efefef;
+ background-color: #1c4e63;
+}
+\ No newline at end of file
DIR diff --git a/doc/html/_static/doctools.js b/doc/html/_static/doctools.js
@@ -1,314 +1,150 @@
/*
- * doctools.js
- * ~~~~~~~~~~~
- *
- * Sphinx JavaScript utilities for all documentation.
- *
- * :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
+ * Base JavaScript utilities for all Sphinx HTML documentation.
*/
-
-/**
- * select a different prefix for underscore
- */
-$u = _.noConflict();
-
-/**
- * make the code below compatible with browsers without
- * an installed firebug like debugger
-if (!window.console || !console.firebug) {
- var names = ["log", "debug", "info", "warn", "error", "assert", "dir",
- "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace",
- "profile", "profileEnd"];
- window.console = {};
- for (var i = 0; i < names.length; ++i)
- window.console[names[i]] = function() {};
-}
- */
-
-/**
- * small helper function to urldecode strings
- */
-jQuery.urldecode = function(x) {
- return decodeURIComponent(x).replace(/\+/g, ' ');
-};
-
-/**
- * small helper function to urlencode strings
- */
-jQuery.urlencode = encodeURIComponent;
-
-/**
- * This function returns the parsed url parameters of the
- * current request. Multiple values per key are supported,
- * it will always return arrays of strings for the value parts.
- */
-jQuery.getQueryParameters = function(s) {
- if (typeof s === 'undefined')
- s = document.location.search;
- var parts = s.substr(s.indexOf('?') + 1).split('&');
- var result = {};
- for (var i = 0; i < parts.length; i++) {
- var tmp = parts[i].split('=', 2);
- var key = jQuery.urldecode(tmp[0]);
- var value = jQuery.urldecode(tmp[1]);
- if (key in result)
- result[key].push(value);
- else
- result[key] = [value];
+"use strict";
+
+const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
+ "TEXTAREA",
+ "INPUT",
+ "SELECT",
+ "BUTTON",
+]);
+
+const _ready = (callback) => {
+ if (document.readyState !== "loading") {
+ callback();
+ } else {
+ document.addEventListener("DOMContentLoaded", callback);
}
- return result;
};
/**
- * highlight a given string on a jquery object by wrapping it in
- * span elements with the given class name.
- */
-jQuery.fn.highlightText = function(text, className) {
- function highlight(node, addItems) {
- if (node.nodeType === 3) {
- var val = node.nodeValue;
- var pos = val.toLowerCase().indexOf(text);
- if (pos >= 0 &&
- !jQuery(node.parentNode).hasClass(className) &&
- !jQuery(node.parentNode).hasClass("nohighlight")) {
- var span;
- var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
- if (isInSVG) {
- span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
- } else {
- span = document.createElement("span");
- span.className = className;
- }
- span.appendChild(document.createTextNode(val.substr(pos, text.length)));
- node.parentNode.insertBefore(span, node.parentNode.insertBefore(
- document.createTextNode(val.substr(pos + text.length)),
- node.nextSibling));
- node.nodeValue = val.substr(0, pos);
- if (isInSVG) {
- var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
- var bbox = node.parentElement.getBBox();
- rect.x.baseVal.value = bbox.x;
- rect.y.baseVal.value = bbox.y;
- rect.width.baseVal.value = bbox.width;
- rect.height.baseVal.value = bbox.height;
- rect.setAttribute('class', className);
- addItems.push({
- "parent": node.parentNode,
- "target": rect});
- }
- }
- }
- else if (!jQuery(node).is("button, select, textarea")) {
- jQuery.each(node.childNodes, function() {
- highlight(this, addItems);
- });
- }
- }
- var addItems = [];
- var result = this.each(function() {
- highlight(this, addItems);
- });
- for (var i = 0; i < addItems.length; ++i) {
- jQuery(addItems[i].parent).before(addItems[i].target);
- }
- return result;
-};
-
-/*
- * backward compatibility for jQuery.browser
- * This will be supported until firefox bug is fixed.
- */
-if (!jQuery.browser) {
- jQuery.uaMatch = function(ua) {
- ua = ua.toLowerCase();
-
- var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
- /(webkit)[ \/]([\w.]+)/.exec(ua) ||
- /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
- /(msie) ([\w.]+)/.exec(ua) ||
- ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
- [];
-
- return {
- browser: match[ 1 ] || "",
- version: match[ 2 ] || "0"
- };
- };
- jQuery.browser = {};
- jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
-}
-
-/**
* Small JavaScript module for the documentation.
*/
-var Documentation = {
-
- init : function() {
- this.fixFirefoxAnchorBug();
- this.highlightSearchWords();
- this.initIndexTable();
- if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
- this.initOnKeyListeners();
- }
+const Documentation = {
+ init: () => {
+ Documentation.initDomainIndexTable();
+ Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
- TRANSLATIONS : {},
- PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; },
- LOCALE : 'unknown',
+ TRANSLATIONS: {},
+ PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
+ LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
- gettext : function(string) {
- var translated = Documentation.TRANSLATIONS[string];
- if (typeof translated === 'undefined')
- return string;
- return (typeof translated === 'string') ? translated : translated[0];
- },
-
- ngettext : function(singular, plural, n) {
- var translated = Documentation.TRANSLATIONS[singular];
- if (typeof translated === 'undefined')
- return (n == 1) ? singular : plural;
- return translated[Documentation.PLURALEXPR(n)];
+ gettext: (string) => {
+ const translated = Documentation.TRANSLATIONS[string];
+ switch (typeof translated) {
+ case "undefined":
+ return string; // no translation
+ case "string":
+ return translated; // translation exists
+ default:
+ return translated[0]; // (singular, plural) translation tuple exists
+ }
},
- addTranslations : function(catalog) {
- for (var key in catalog.messages)
- this.TRANSLATIONS[key] = catalog.messages[key];
- this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')');
- this.LOCALE = catalog.locale;
+ ngettext: (singular, plural, n) => {
+ const translated = Documentation.TRANSLATIONS[singular];
+ if (typeof translated !== "undefined")
+ return translated[Documentation.PLURAL_EXPR(n)];
+ return n === 1 ? singular : plural;
},
- /**
- * add context elements like header anchor links
- */
- addContextElements : function() {
- $('div[id] > :header:first').each(function() {
- $('<a class="headerlink">\u00B6</a>').
- attr('href', '#' + this.id).
- attr('title', _('Permalink to this headline')).
- appendTo(this);
- });
- $('dt[id]').each(function() {
- $('<a class="headerlink">\u00B6</a>').
- attr('href', '#' + this.id).
- attr('title', _('Permalink to this definition')).
- appendTo(this);
- });
+ addTranslations: (catalog) => {
+ Object.assign(Documentation.TRANSLATIONS, catalog.messages);
+ Documentation.PLURAL_EXPR = new Function(
+ "n",
+ `return (${catalog.plural_expr})`,
+ );
+ Documentation.LOCALE = catalog.locale;
},
/**
- * workaround a firefox stupidity
- * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
+ * helper function to focus on search bar
*/
- fixFirefoxAnchorBug : function() {
- if (document.location.hash && $.browser.mozilla)
- window.setTimeout(function() {
- document.location.href += '';
- }, 10);
+ focusSearchBar: () => {
+ document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
- * highlight the search words provided in the url in the text
+ * Initialise the domain index toggle buttons
*/
- highlightSearchWords : function() {
- var params = $.getQueryParameters();
- var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
- if (terms.length) {
- var body = $('div.body');
- if (!body.length) {
- body = $('body');
+ initDomainIndexTable: () => {
+ const toggler = (el) => {
+ const idNumber = el.id.substr(7);
+ const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
+ if (el.src.substr(-9) === "minus.png") {
+ el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
+ toggledRows.forEach((el) => (el.style.display = "none"));
+ } else {
+ el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
+ toggledRows.forEach((el) => (el.style.display = ""));
}
- window.setTimeout(function() {
- $.each(terms, function() {
- body.highlightText(this.toLowerCase(), 'highlighted');
- });
- }, 10);
- $('<p class="highlight-link"><a href="javascript:Documentation.' +
- 'hideSearchWords()">' + _('Hide Search Matches') + '</a></p>')
- .appendTo($('#searchbox'));
- }
- },
-
- /**
- * init the domain index toggle buttons
- */
- initIndexTable : function() {
- var togglers = $('img.toggler').click(function() {
- var src = $(this).attr('src');
- var idnum = $(this).attr('id').substr(7);
- $('tr.cg-' + idnum).toggle();
- if (src.substr(-9) === 'minus.png')
- $(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
- else
- $(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
- }).css('display', '');
- if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) {
- togglers.click();
- }
- },
-
- /**
- * helper function to hide the search marks again
- */
- hideSearchWords : function() {
- $('#searchbox .highlight-link').fadeOut(300);
- $('span.highlighted').removeClass('highlighted');
- },
-
- /**
- * make the url absolute
- */
- makeURL : function(relativeURL) {
- return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
- },
+ };
- /**
- * get the current relative url
- */
- getCurrentURL : function() {
- var path = document.location.pathname;
- var parts = path.split(/\//);
- $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
- if (this === '..')
- parts.pop();
- });
- var url = parts.join('/');
- return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
+ const togglerElements = document.querySelectorAll("img.toggler");
+ togglerElements.forEach((el) =>
+ el.addEventListener("click", (event) => toggler(event.currentTarget)),
+ );
+ togglerElements.forEach((el) => (el.style.display = ""));
+ if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
- initOnKeyListeners: function() {
- $(document).keyup(function(event) {
- var activeElementType = document.activeElement.tagName;
- // don't navigate when in search box or textarea
- if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') {
- switch (event.keyCode) {
- case 37: // left
- var prevHref = $('link[rel="prev"]').prop('href');
- if (prevHref) {
- window.location.href = prevHref;
- return false;
+ initOnKeyListeners: () => {
+ // only install a listener if it is really needed
+ if (
+ !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS
+ && !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
+ )
+ return;
+
+ document.addEventListener("keydown", (event) => {
+ // bail for input elements
+ if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName))
+ return;
+ // bail with special keys
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
+
+ if (!event.shiftKey) {
+ switch (event.key) {
+ case "ArrowLeft":
+ if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
+
+ const prevLink = document.querySelector('link[rel="prev"]');
+ if (prevLink && prevLink.href) {
+ window.location.href = prevLink.href;
+ event.preventDefault();
}
- case 39: // right
- var nextHref = $('link[rel="next"]').prop('href');
- if (nextHref) {
- window.location.href = nextHref;
- return false;
+ break;
+ case "ArrowRight":
+ if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
+
+ const nextLink = document.querySelector('link[rel="next"]');
+ if (nextLink && nextLink.href) {
+ window.location.href = nextLink.href;
+ event.preventDefault();
}
+ break;
}
}
+
+ // some keyboard layouts may need Shift to get /
+ switch (event.key) {
+ case "/":
+ if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
+ Documentation.focusSearchBar();
+ event.preventDefault();
+ }
});
- }
+ },
};
// quick alias for translations
-_ = Documentation.gettext;
+const _ = Documentation.gettext;
-$(document).ready(function() {
- Documentation.init();
-});
+_ready(Documentation.init);
DIR diff --git a/doc/html/_static/documentation_options.js b/doc/html/_static/documentation_options.js
@@ -0,0 +1,13 @@
+const DOCUMENTATION_OPTIONS = {
+ VERSION: '2.15',
+ LANGUAGE: 'en',
+ COLLAPSE_INDEX: false,
+ BUILDER: 'html',
+ FILE_SUFFIX: '.html',
+ LINK_SUFFIX: '.html',
+ HAS_SOURCE: true,
+ SOURCELINK_SUFFIX: '.txt',
+ NAVIGATION_WITH_KEYS: false,
+ SHOW_SEARCH_SUMMARY: true,
+ ENABLE_SEARCH_SHORTCUTS: true,
+};
+\ No newline at end of file
DIR diff --git a/doc/html/_static/english-stemmer.js b/doc/html/_static/english-stemmer.js
@@ -0,0 +1,1066 @@
+// Generated from english.sbl by Snowball 3.0.1 - https://snowballstem.org/
+
+/**@constructor*/
+var EnglishStemmer = function() {
+ var base = new BaseStemmer();
+
+ /** @const */ var a_0 = [
+ ["arsen", -1, -1],
+ ["commun", -1, -1],
+ ["emerg", -1, -1],
+ ["gener", -1, -1],
+ ["later", -1, -1],
+ ["organ", -1, -1],
+ ["past", -1, -1],
+ ["univers", -1, -1]
+ ];
+
+ /** @const */ var a_1 = [
+ ["'", -1, 1],
+ ["'s'", 0, 1],
+ ["'s", -1, 1]
+ ];
+
+ /** @const */ var a_2 = [
+ ["ied", -1, 2],
+ ["s", -1, 3],
+ ["ies", 1, 2],
+ ["sses", 1, 1],
+ ["ss", 1, -1],
+ ["us", 1, -1]
+ ];
+
+ /** @const */ var a_3 = [
+ ["succ", -1, 1],
+ ["proc", -1, 1],
+ ["exc", -1, 1]
+ ];
+
+ /** @const */ var a_4 = [
+ ["even", -1, 2],
+ ["cann", -1, 2],
+ ["inn", -1, 2],
+ ["earr", -1, 2],
+ ["herr", -1, 2],
+ ["out", -1, 2],
+ ["y", -1, 1]
+ ];
+
+ /** @const */ var a_5 = [
+ ["", -1, -1],
+ ["ed", 0, 2],
+ ["eed", 1, 1],
+ ["ing", 0, 3],
+ ["edly", 0, 2],
+ ["eedly", 4, 1],
+ ["ingly", 0, 2]
+ ];
+
+ /** @const */ var a_6 = [
+ ["", -1, 3],
+ ["bb", 0, 2],
+ ["dd", 0, 2],
+ ["ff", 0, 2],
+ ["gg", 0, 2],
+ ["bl", 0, 1],
+ ["mm", 0, 2],
+ ["nn", 0, 2],
+ ["pp", 0, 2],
+ ["rr", 0, 2],
+ ["at", 0, 1],
+ ["tt", 0, 2],
+ ["iz", 0, 1]
+ ];
+
+ /** @const */ var a_7 = [
+ ["anci", -1, 3],
+ ["enci", -1, 2],
+ ["ogi", -1, 14],
+ ["li", -1, 16],
+ ["bli", 3, 12],
+ ["abli", 4, 4],
+ ["alli", 3, 8],
+ ["fulli", 3, 9],
+ ["lessli", 3, 15],
+ ["ousli", 3, 10],
+ ["entli", 3, 5],
+ ["aliti", -1, 8],
+ ["biliti", -1, 12],
+ ["iviti", -1, 11],
+ ["tional", -1, 1],
+ ["ational", 14, 7],
+ ["alism", -1, 8],
+ ["ation", -1, 7],
+ ["ization", 17, 6],
+ ["izer", -1, 6],
+ ["ator", -1, 7],
+ ["iveness", -1, 11],
+ ["fulness", -1, 9],
+ ["ousness", -1, 10],
+ ["ogist", -1, 13]
+ ];
+
+ /** @const */ var a_8 = [
+ ["icate", -1, 4],
+ ["ative", -1, 6],
+ ["alize", -1, 3],
+ ["iciti", -1, 4],
+ ["ical", -1, 4],
+ ["tional", -1, 1],
+ ["ational", 5, 2],
+ ["ful", -1, 5],
+ ["ness", -1, 5]
+ ];
+
+ /** @const */ var a_9 = [
+ ["ic", -1, 1],
+ ["ance", -1, 1],
+ ["ence", -1, 1],
+ ["able", -1, 1],
+ ["ible", -1, 1],
+ ["ate", -1, 1],
+ ["ive", -1, 1],
+ ["ize", -1, 1],
+ ["iti", -1, 1],
+ ["al", -1, 1],
+ ["ism", -1, 1],
+ ["ion", -1, 2],
+ ["er", -1, 1],
+ ["ous", -1, 1],
+ ["ant", -1, 1],
+ ["ent", -1, 1],
+ ["ment", 15, 1],
+ ["ement", 16, 1]
+ ];
+
+ /** @const */ var a_10 = [
+ ["e", -1, 1],
+ ["l", -1, 2]
+ ];
+
+ /** @const */ var a_11 = [
+ ["andes", -1, -1],
+ ["atlas", -1, -1],
+ ["bias", -1, -1],
+ ["cosmos", -1, -1],
+ ["early", -1, 5],
+ ["gently", -1, 3],
+ ["howe", -1, -1],
+ ["idly", -1, 2],
+ ["news", -1, -1],
+ ["only", -1, 6],
+ ["singly", -1, 7],
+ ["skies", -1, 1],
+ ["sky", -1, -1],
+ ["ugly", -1, 4]
+ ];
+
+ /** @const */ var /** Array<int> */ g_aeo = [17, 64];
+
+ /** @const */ var /** Array<int> */ g_v = [17, 65, 16, 1];
+
+ /** @const */ var /** Array<int> */ g_v_WXY = [1, 17, 65, 208, 1];
+
+ /** @const */ var /** Array<int> */ g_valid_LI = [55, 141, 2];
+
+ var /** boolean */ B_Y_found = false;
+ var /** number */ I_p2 = 0;
+ var /** number */ I_p1 = 0;
+
+
+ /** @return {boolean} */
+ function r_prelude() {
+ B_Y_found = false;
+ /** @const */ var /** number */ v_1 = base.cursor;
+ lab0: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("'")))
+ {
+ break lab0;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ }
+ base.cursor = v_1;
+ /** @const */ var /** number */ v_2 = base.cursor;
+ lab1: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("y")))
+ {
+ break lab1;
+ }
+ base.ket = base.cursor;
+ if (!base.slice_from("Y"))
+ {
+ return false;
+ }
+ B_Y_found = true;
+ }
+ base.cursor = v_2;
+ /** @const */ var /** number */ v_3 = base.cursor;
+ lab2: {
+ while(true)
+ {
+ /** @const */ var /** number */ v_4 = base.cursor;
+ lab3: {
+ golab4: while(true)
+ {
+ /** @const */ var /** number */ v_5 = base.cursor;
+ lab5: {
+ if (!(base.in_grouping(g_v, 97, 121)))
+ {
+ break lab5;
+ }
+ base.bra = base.cursor;
+ if (!(base.eq_s("y")))
+ {
+ break lab5;
+ }
+ base.ket = base.cursor;
+ base.cursor = v_5;
+ break golab4;
+ }
+ base.cursor = v_5;
+ if (base.cursor >= base.limit)
+ {
+ break lab3;
+ }
+ base.cursor++;
+ }
+ if (!base.slice_from("Y"))
+ {
+ return false;
+ }
+ B_Y_found = true;
+ continue;
+ }
+ base.cursor = v_4;
+ break;
+ }
+ }
+ base.cursor = v_3;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_mark_regions() {
+ I_p1 = base.limit;
+ I_p2 = base.limit;
+ /** @const */ var /** number */ v_1 = base.cursor;
+ lab0: {
+ lab1: {
+ /** @const */ var /** number */ v_2 = base.cursor;
+ lab2: {
+ if (base.find_among(a_0) == 0)
+ {
+ break lab2;
+ }
+ break lab1;
+ }
+ base.cursor = v_2;
+ if (!base.go_out_grouping(g_v, 97, 121))
+ {
+ break lab0;
+ }
+ base.cursor++;
+ if (!base.go_in_grouping(g_v, 97, 121))
+ {
+ break lab0;
+ }
+ base.cursor++;
+ }
+ I_p1 = base.cursor;
+ if (!base.go_out_grouping(g_v, 97, 121))
+ {
+ break lab0;
+ }
+ base.cursor++;
+ if (!base.go_in_grouping(g_v, 97, 121))
+ {
+ break lab0;
+ }
+ base.cursor++;
+ I_p2 = base.cursor;
+ }
+ base.cursor = v_1;
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_shortv() {
+ lab0: {
+ /** @const */ var /** number */ v_1 = base.limit - base.cursor;
+ lab1: {
+ if (!(base.out_grouping_b(g_v_WXY, 89, 121)))
+ {
+ break lab1;
+ }
+ if (!(base.in_grouping_b(g_v, 97, 121)))
+ {
+ break lab1;
+ }
+ if (!(base.out_grouping_b(g_v, 97, 121)))
+ {
+ break lab1;
+ }
+ break lab0;
+ }
+ base.cursor = base.limit - v_1;
+ lab2: {
+ if (!(base.out_grouping_b(g_v, 97, 121)))
+ {
+ break lab2;
+ }
+ if (!(base.in_grouping_b(g_v, 97, 121)))
+ {
+ break lab2;
+ }
+ if (base.cursor > base.limit_backward)
+ {
+ break lab2;
+ }
+ break lab0;
+ }
+ base.cursor = base.limit - v_1;
+ if (!(base.eq_s_b("past")))
+ {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_R1() {
+ return I_p1 <= base.cursor;
+ };
+
+ /** @return {boolean} */
+ function r_R2() {
+ return I_p2 <= base.cursor;
+ };
+
+ /** @return {boolean} */
+ function r_Step_1a() {
+ var /** number */ among_var;
+ /** @const */ var /** number */ v_1 = base.limit - base.cursor;
+ lab0: {
+ base.ket = base.cursor;
+ if (base.find_among_b(a_1) == 0)
+ {
+ base.cursor = base.limit - v_1;
+ break lab0;
+ }
+ base.bra = base.cursor;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ }
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_2);
+ if (among_var == 0)
+ {
+ return false;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ if (!base.slice_from("ss"))
+ {
+ return false;
+ }
+ break;
+ case 2:
+ lab1: {
+ /** @const */ var /** number */ v_2 = base.limit - base.cursor;
+ lab2: {
+ {
+ /** @const */ var /** number */ c1 = base.cursor - 2;
+ if (c1 < base.limit_backward)
+ {
+ break lab2;
+ }
+ base.cursor = c1;
+ }
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ break lab1;
+ }
+ base.cursor = base.limit - v_2;
+ if (!base.slice_from("ie"))
+ {
+ return false;
+ }
+ }
+ break;
+ case 3:
+ if (base.cursor <= base.limit_backward)
+ {
+ return false;
+ }
+ base.cursor--;
+ if (!base.go_out_grouping_b(g_v, 97, 121))
+ {
+ return false;
+ }
+ base.cursor--;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_Step_1b() {
+ var /** number */ among_var;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_5);
+ base.bra = base.cursor;
+ lab0: {
+ /** @const */ var /** number */ v_1 = base.limit - base.cursor;
+ lab1: {
+ switch (among_var) {
+ case 1:
+ /** @const */ var /** number */ v_2 = base.limit - base.cursor;
+ lab2: {
+ lab3: {
+ /** @const */ var /** number */ v_3 = base.limit - base.cursor;
+ lab4: {
+ if (base.find_among_b(a_3) == 0)
+ {
+ break lab4;
+ }
+ if (base.cursor > base.limit_backward)
+ {
+ break lab4;
+ }
+ break lab3;
+ }
+ base.cursor = base.limit - v_3;
+ if (!r_R1())
+ {
+ break lab2;
+ }
+ if (!base.slice_from("ee"))
+ {
+ return false;
+ }
+ }
+ }
+ base.cursor = base.limit - v_2;
+ break;
+ case 2:
+ break lab1;
+ case 3:
+ among_var = base.find_among_b(a_4);
+ if (among_var == 0)
+ {
+ break lab1;
+ }
+ switch (among_var) {
+ case 1:
+ /** @const */ var /** number */ v_4 = base.limit - base.cursor;
+ if (!(base.out_grouping_b(g_v, 97, 121)))
+ {
+ break lab1;
+ }
+ if (base.cursor > base.limit_backward)
+ {
+ break lab1;
+ }
+ base.cursor = base.limit - v_4;
+ base.bra = base.cursor;
+ if (!base.slice_from("ie"))
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (base.cursor > base.limit_backward)
+ {
+ break lab1;
+ }
+ break;
+ }
+ break;
+ }
+ break lab0;
+ }
+ base.cursor = base.limit - v_1;
+ /** @const */ var /** number */ v_5 = base.limit - base.cursor;
+ if (!base.go_out_grouping_b(g_v, 97, 121))
+ {
+ return false;
+ }
+ base.cursor--;
+ base.cursor = base.limit - v_5;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ base.ket = base.cursor;
+ base.bra = base.cursor;
+ /** @const */ var /** number */ v_6 = base.limit - base.cursor;
+ among_var = base.find_among_b(a_6);
+ switch (among_var) {
+ case 1:
+ if (!base.slice_from("e"))
+ {
+ return false;
+ }
+ return false;
+ case 2:
+ {
+ /** @const */ var /** number */ v_7 = base.limit - base.cursor;
+ lab5: {
+ if (!(base.in_grouping_b(g_aeo, 97, 111)))
+ {
+ break lab5;
+ }
+ if (base.cursor > base.limit_backward)
+ {
+ break lab5;
+ }
+ return false;
+ }
+ base.cursor = base.limit - v_7;
+ }
+ break;
+ case 3:
+ if (base.cursor != I_p1)
+ {
+ return false;
+ }
+ /** @const */ var /** number */ v_8 = base.limit - base.cursor;
+ if (!r_shortv())
+ {
+ return false;
+ }
+ base.cursor = base.limit - v_8;
+ if (!base.slice_from("e"))
+ {
+ return false;
+ }
+ return false;
+ }
+ base.cursor = base.limit - v_6;
+ base.ket = base.cursor;
+ if (base.cursor <= base.limit_backward)
+ {
+ return false;
+ }
+ base.cursor--;
+ base.bra = base.cursor;
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_Step_1c() {
+ base.ket = base.cursor;
+ lab0: {
+ /** @const */ var /** number */ v_1 = base.limit - base.cursor;
+ lab1: {
+ if (!(base.eq_s_b("y")))
+ {
+ break lab1;
+ }
+ break lab0;
+ }
+ base.cursor = base.limit - v_1;
+ if (!(base.eq_s_b("Y")))
+ {
+ return false;
+ }
+ }
+ base.bra = base.cursor;
+ if (!(base.out_grouping_b(g_v, 97, 121)))
+ {
+ return false;
+ }
+ lab2: {
+ if (base.cursor > base.limit_backward)
+ {
+ break lab2;
+ }
+ return false;
+ }
+ if (!base.slice_from("i"))
+ {
+ return false;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_Step_2() {
+ var /** number */ among_var;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_7);
+ if (among_var == 0)
+ {
+ return false;
+ }
+ base.bra = base.cursor;
+ if (!r_R1())
+ {
+ return false;
+ }
+ switch (among_var) {
+ case 1:
+ if (!base.slice_from("tion"))
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_from("ence"))
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_from("ance"))
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!base.slice_from("able"))
+ {
+ return false;
+ }
+ break;
+ case 5:
+ if (!base.slice_from("ent"))
+ {
+ return false;
+ }
+ break;
+ case 6:
+ if (!base.slice_from("ize"))
+ {
+ return false;
+ }
+ break;
+ case 7:
+ if (!base.slice_from("ate"))
+ {
+ return false;
+ }
+ break;
+ case 8:
+ if (!base.slice_from("al"))
+ {
+ return false;
+ }
+ break;
+ case 9:
+ if (!base.slice_from("ful"))
+ {
+ return false;
+ }
+ break;
+ case 10:
+ if (!base.slice_from("ous"))
+ {
+ return false;
+ }
+ break;
+ case 11:
+ if (!base.slice_from("ive"))
+ {
+ return false;
+ }
+ break;
+ case 12:
+ if (!base.slice_from("ble"))
+ {
+ return false;
+ }
+ break;
+ case 13:
+ if (!base.slice_from("og"))
+ {
+ return false;
+ }
+ break;
+ case 14:
+ if (!(base.eq_s_b("l")))
+ {
+ return false;
+ }
+ if (!base.slice_from("og"))
+ {
+ return false;
+ }
+ break;
+ case 15:
+ if (!base.slice_from("less"))
+ {
+ return false;
+ }
+ break;
+ case 16:
+ if (!(base.in_grouping_b(g_valid_LI, 99, 116)))
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_Step_3() {
+ var /** number */ among_var;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_8);
+ if (among_var == 0)
+ {
+ return false;
+ }
+ base.bra = base.cursor;
+ if (!r_R1())
+ {
+ return false;
+ }
+ switch (among_var) {
+ case 1:
+ if (!base.slice_from("tion"))
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_from("ate"))
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_from("al"))
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!base.slice_from("ic"))
+ {
+ return false;
+ }
+ break;
+ case 5:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 6:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_Step_4() {
+ var /** number */ among_var;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_9);
+ if (among_var == 0)
+ {
+ return false;
+ }
+ base.bra = base.cursor;
+ if (!r_R2())
+ {
+ return false;
+ }
+ switch (among_var) {
+ case 1:
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ lab0: {
+ /** @const */ var /** number */ v_1 = base.limit - base.cursor;
+ lab1: {
+ if (!(base.eq_s_b("s")))
+ {
+ break lab1;
+ }
+ break lab0;
+ }
+ base.cursor = base.limit - v_1;
+ if (!(base.eq_s_b("t")))
+ {
+ return false;
+ }
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_Step_5() {
+ var /** number */ among_var;
+ base.ket = base.cursor;
+ among_var = base.find_among_b(a_10);
+ if (among_var == 0)
+ {
+ return false;
+ }
+ base.bra = base.cursor;
+ switch (among_var) {
+ case 1:
+ lab0: {
+ lab1: {
+ if (!r_R2())
+ {
+ break lab1;
+ }
+ break lab0;
+ }
+ if (!r_R1())
+ {
+ return false;
+ }
+ {
+ /** @const */ var /** number */ v_1 = base.limit - base.cursor;
+ lab2: {
+ if (!r_shortv())
+ {
+ break lab2;
+ }
+ return false;
+ }
+ base.cursor = base.limit - v_1;
+ }
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!r_R2())
+ {
+ return false;
+ }
+ if (!(base.eq_s_b("l")))
+ {
+ return false;
+ }
+ if (!base.slice_del())
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_exception1() {
+ var /** number */ among_var;
+ base.bra = base.cursor;
+ among_var = base.find_among(a_11);
+ if (among_var == 0)
+ {
+ return false;
+ }
+ base.ket = base.cursor;
+ if (base.cursor < base.limit)
+ {
+ return false;
+ }
+ switch (among_var) {
+ case 1:
+ if (!base.slice_from("sky"))
+ {
+ return false;
+ }
+ break;
+ case 2:
+ if (!base.slice_from("idl"))
+ {
+ return false;
+ }
+ break;
+ case 3:
+ if (!base.slice_from("gentl"))
+ {
+ return false;
+ }
+ break;
+ case 4:
+ if (!base.slice_from("ugli"))
+ {
+ return false;
+ }
+ break;
+ case 5:
+ if (!base.slice_from("earli"))
+ {
+ return false;
+ }
+ break;
+ case 6:
+ if (!base.slice_from("onli"))
+ {
+ return false;
+ }
+ break;
+ case 7:
+ if (!base.slice_from("singl"))
+ {
+ return false;
+ }
+ break;
+ }
+ return true;
+ };
+
+ /** @return {boolean} */
+ function r_postlude() {
+ if (!B_Y_found)
+ {
+ return false;
+ }
+ while(true)
+ {
+ /** @const */ var /** number */ v_1 = base.cursor;
+ lab0: {
+ golab1: while(true)
+ {
+ /** @const */ var /** number */ v_2 = base.cursor;
+ lab2: {
+ base.bra = base.cursor;
+ if (!(base.eq_s("Y")))
+ {
+ break lab2;
+ }
+ base.ket = base.cursor;
+ base.cursor = v_2;
+ break golab1;
+ }
+ base.cursor = v_2;
+ if (base.cursor >= base.limit)
+ {
+ break lab0;
+ }
+ base.cursor++;
+ }
+ if (!base.slice_from("y"))
+ {
+ return false;
+ }
+ continue;
+ }
+ base.cursor = v_1;
+ break;
+ }
+ return true;
+ };
+
+ this.stem = /** @return {boolean} */ function() {
+ lab0: {
+ /** @const */ var /** number */ v_1 = base.cursor;
+ lab1: {
+ if (!r_exception1())
+ {
+ break lab1;
+ }
+ break lab0;
+ }
+ base.cursor = v_1;
+ lab2: {
+ {
+ /** @const */ var /** number */ v_2 = base.cursor;
+ lab3: {
+ {
+ /** @const */ var /** number */ c1 = base.cursor + 3;
+ if (c1 > base.limit)
+ {
+ break lab3;
+ }
+ base.cursor = c1;
+ }
+ break lab2;
+ }
+ base.cursor = v_2;
+ }
+ break lab0;
+ }
+ base.cursor = v_1;
+ r_prelude();
+ r_mark_regions();
+ base.limit_backward = base.cursor; base.cursor = base.limit;
+ /** @const */ var /** number */ v_3 = base.limit - base.cursor;
+ r_Step_1a();
+ base.cursor = base.limit - v_3;
+ /** @const */ var /** number */ v_4 = base.limit - base.cursor;
+ r_Step_1b();
+ base.cursor = base.limit - v_4;
+ /** @const */ var /** number */ v_5 = base.limit - base.cursor;
+ r_Step_1c();
+ base.cursor = base.limit - v_5;
+ /** @const */ var /** number */ v_6 = base.limit - base.cursor;
+ r_Step_2();
+ base.cursor = base.limit - v_6;
+ /** @const */ var /** number */ v_7 = base.limit - base.cursor;
+ r_Step_3();
+ base.cursor = base.limit - v_7;
+ /** @const */ var /** number */ v_8 = base.limit - base.cursor;
+ r_Step_4();
+ base.cursor = base.limit - v_8;
+ /** @const */ var /** number */ v_9 = base.limit - base.cursor;
+ r_Step_5();
+ base.cursor = base.limit - v_9;
+ base.cursor = base.limit_backward;
+ /** @const */ var /** number */ v_10 = base.cursor;
+ r_postlude();
+ base.cursor = v_10;
+ }
+ return true;
+ };
+
+ /**@return{string}*/
+ this['stemWord'] = function(/**string*/word) {
+ base.setCurrent(word);
+ this.stem();
+ return base.getCurrent();
+ };
+};
DIR diff --git a/doc/html/_static/language_data.js b/doc/html/_static/language_data.js
@@ -0,0 +1,13 @@
+/*
+ * This script contains the language-specific data used by searchtools.js,
+ * namely the set of stopwords, stemmer, scorer and splitter.
+ */
+
mx1.adamsgaard.dk:70 /src/sphere/commit/8db43a4f44d5a40467db9fd613706de973506ece.gph:3995: line too long