diff --git a/doc/source/examples/dem/dem.rst b/doc/source/examples/dem/dem.rst
index 4c6fa5e27b..240146d95e 100644
--- a/doc/source/examples/dem/dem.rst
+++ b/doc/source/examples/dem/dem.rst
@@ -21,6 +21,7 @@ We organize the DEM examples from the simplest to the most complicated example:
silo/silo
rectangular-hopper/rectangular-hopper
granular-dam-break/granular-dam-break
+ plate-discharge/plate-discharge
bunny-drill/bunny-drill
granular-mixer/granular-mixer
@@ -59,9 +60,11 @@ We organize the DEM examples from the simplest to the most complicated example:
dem_12 [label="Granular Dam-Break", href="https://chaos-polymtl.github.io/lethe/documentation/examples/dem/granular-dam-break/granular-dam-break.html"];
- dem_13 [label="Bunny Drill", href="https://chaos-polymtl.github.io/lethe/documentation/examples/dem/bunny-drill/bunny-drill.html"];
+ dem_13 [label="Plate Discharge", href="https://chaos-polymtl.github.io/lethe/documentation/examples/dem/plate-discharge/plate-discharge.html"];
- dem_14 [label="Granular Mixer", href="https://chaos-polymtl.github.io/lethe/documentation/examples/dem/granular-mixer/granular-mixer.html"];
+ dem_14 [label="Bunny Drill", href="https://chaos-polymtl.github.io/lethe/documentation/examples/dem/bunny-drill/bunny-drill.html"];
+
+ dem_15 [label="Granular Mixer", href="https://chaos-polymtl.github.io/lethe/documentation/examples/dem/granular-mixer/granular-mixer.html"];
@@ -79,4 +82,5 @@ We organize the DEM examples from the simplest to the most complicated example:
dem -> dem_12:w;
dem -> dem_13:w;
dem -> dem_14:w;
+ dem -> dem_15:w;
}
\ No newline at end of file
diff --git a/doc/source/examples/dem/plate-discharge/images/angle-areas.png b/doc/source/examples/dem/plate-discharge/images/angle-areas.png
new file mode 100644
index 0000000000..e0cb7009e2
Binary files /dev/null and b/doc/source/examples/dem/plate-discharge/images/angle-areas.png differ
diff --git a/doc/source/examples/dem/plate-discharge/images/angle-of-repose.png b/doc/source/examples/dem/plate-discharge/images/angle-of-repose.png
new file mode 100644
index 0000000000..1b73e913d4
Binary files /dev/null and b/doc/source/examples/dem/plate-discharge/images/angle-of-repose.png differ
diff --git a/doc/source/examples/dem/plate-discharge/images/performance.png b/doc/source/examples/dem/plate-discharge/images/performance.png
new file mode 100644
index 0000000000..5d195ddfef
Binary files /dev/null and b/doc/source/examples/dem/plate-discharge/images/performance.png differ
diff --git a/doc/source/examples/dem/plate-discharge/images/plate-discharge-diagram.png b/doc/source/examples/dem/plate-discharge/images/plate-discharge-diagram.png
new file mode 100644
index 0000000000..8a88834e51
Binary files /dev/null and b/doc/source/examples/dem/plate-discharge/images/plate-discharge-diagram.png differ
diff --git a/doc/source/examples/dem/plate-discharge/plate-discharge.rst b/doc/source/examples/dem/plate-discharge/plate-discharge.rst
new file mode 100644
index 0000000000..fb2c7bf858
--- /dev/null
+++ b/doc/source/examples/dem/plate-discharge/plate-discharge.rst
@@ -0,0 +1,494 @@
+==================================
+Plate Discharge
+==================================
+
+This example compares the angles of repose and performance results of a plate discharging particles with performance enhancement methods.
+
+----------------------------------
+Features
+----------------------------------
+
+- Solvers: ``lethe-particles``
+- Three-dimensional problem
+- Uses `adaptive sparse contacts (ASC) <../../../parameters/dem/model_parameters.html#adaptive-sparse-contacts-asc>`_
+- Uses `dynamic load balancing <../../../parameters/dem/model_parameters.html#load-balancing>`_
+- Post-processes results and compares them to the literature
+
+
+---------------------------
+Files Used in this Example
+---------------------------
+
+All the files mentioned below are located in the example folder ``examples/dem/3d-plate-discharge``.
+
+- There are 4 parameters files: a baseline case and three other cases with different features using one or a combination of performance enhancing methods. The parameters files are:
+
+ .. list-table::
+ :width: 100%
+ :widths: 30 30 30
+ :header-rows: 1
+ :align: center
+
+ * - Name of the .prm file
+ - `ASC <../../../parameters/dem/model_parameters.html#adaptive-sparse-contacts-asc>`_
+ - `Load Balancing <../../../parameters/dem/model_parameters.html#load-balancing>`_
+ * - ``plate-discharge_base.prm``
+ -
+ -
+ * - ``plate-discharge_asc.prm``
+ - ×
+ -
+ * - ``plate-discharge_lb.prm``
+ -
+ - ×
+ * - ``plate-discharge_asc-lb.prm``
+ - ×
+ - ×
+
+- These parameters files are ready for the simulations. We run 2 sets of simulation: performance and data. In the performance simulations, speedup is computed. Hence, the writing of solution files is deactivated. In the data simulations, those files are outputted to analyze the results. The performance analysis parameter files are in the folder ``performance/``, and the ones for the data analysis are in the folder ``data/``.
+
+-----------------------
+Description of the Case
+-----------------------
+
+This example simulates the discharge of particles at the sides of a plate in a rectangular container in order to get the angle of repose of the granular material as done by Zhou *et al*. [#zhou2002]_ The example compares the angles of repose and the performance of the simulations with the use of adaptive sparse contacts and load balancing methods. The angles are also compared to literature values.
+
+.. figure:: images/plate-discharge-diagram.png
+ :width: 50%
+ :alt: Plate discharge diagram
+ :align: center
+
+ Diagram of the container (black) with the plate (green) and the particles (blue).
+
+
+-------------------
+DEM Parameter files
+-------------------
+
+Baseline case simulation
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+In this section, we introduce the different sections of the parameter file ``plate-discharge_base.prm`` which do not use any performance enhancement methods.
+
+
+Simulation Control
+------------------
+
+The simulation lasts :math:`15 \ \text{s}` and the DEM time step is :math:`0.0001 \ \text{s}`. The output are generated every :math:`0.01 \ \text{s}` for the simulation for data analysis.
+
+.. code-block:: text
+
+ subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 100
+ set output path = ./output_base/
+ end
+
+Mesh
+----
+
+The rectangular container is a :math:`1.0 \times 1.0 \times 0.2 \ \text{m}` box.
+
+.. code-block:: text
+
+ subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+ end
+
+Solid Objects
+-------------
+
+The plate is a solid object with a simple mesh of 2 triangles placed at a height of :math:`0.4 \ \text{m}` in the container.
+
+.. code-block:: text
+
+ subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+ end
+
+Lagrangian Physical Properties
+------------------------------
+
+The lagrangian properties are relatively arbitrary. The simulation contains :math:`52000` particles with a diameter of :math:`0.01 \ \text{m}`, and a density of :math:`2400 \ \frac{\text{kg}}{\text{m}^3}`. Both properties of particle-particle and particle-wall interactions are the same.
+
+.. code-block:: text
+
+ subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+ end
+
+Insertion Info
+--------------
+
+The particles are inserted above the plate with the volume insertion method.
+
+.. code-block:: text
+
+ subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+ end
+
+Floating Walls
+--------------
+
+At the beginning of the simulation, floating walls are placed vertically at both extremities of the plate to keep all particles on the latter. The walls are removed suddenly after :math:`0.75 \ \text{s}` of simulation, starting the discharge.
+
+.. code-block:: text
+
+ subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ end
+
+Model Parameters
+----------------
+
+The model parameters are quite standard for a DEM simulation with the non-linear Hertz-Mindlin contact force model, a constant rolling resistance torque, and the velocity Verlet integration method. For the baseline case, we do not use any performance enhancement method.
+
+.. code-block:: text
+
+ subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ subsection load balancing
+ set load balance method = none
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = false
+ end
+ subsection load balancing
+ set load balance method = none
+ end
+ end
+
+
+Timer
+-------
+
+The timer is enabled since we want to profile the computational performance of the simulations. We print the total wallclock time elapsed since the start at every `log frequency` iteration.
+
+.. code-block:: text
+
+ subsection timer
+ set type = iteration
+ end
+
+
+ASC Simulation
+~~~~~~~~~~~~~~~~~~
+
+The only differences between ``plate-discharge_base.prm`` and ``plate-discharge_asc.prm`` are the enabling of the ASC and the name of the folder for outputs.
+
+Model Parameters
+----------------
+
+Here the ASC is enabled with a granular temperature threshold of :math:`0.0001 \ \frac{\text{m}^2}{\text{s}^2}` and a solid fraction threshold of :math:`0.4`. Those parameters have shown to be efficient in other DEM simulations with a good balance between performance gain and low impact on the simulation results. These parameters can be adjusted.
+
+.. code-block:: text
+
+ subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = true
+ set granular temperature threshold = 1e-4
+ set solid fraction threshold = 0.4
+ end
+ subsection load balancing
+ set load balance method = none
+ end
+ end
+
+
+Load Balancing Simulation
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The only differences between ``plate-discharge_base.prm`` and ``plate-discharge_lb.prm`` are the usage of the load balancing and the name of the folder for outputs.
+
+Model Parameters
+----------------
+
+Here, the dynamic load balancing checks if a load balancing is needed every :math:`2500` iterations with a load threshold of :math:`0.5`.
+
+.. code-block:: text
+
+ subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = false
+ end
+ subsection load balancing
+ set load balance method = dynamic
+ set threshold = 0.5
+ set dynamic check frequency = 2500
+ end
+ end
+
+
+ASC with Load Balancing Simulation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The only differences between ``plate-discharge_base.prm`` and ``plate-discharge_asc-lb.prm`` are the usage of the ASC method with the load balancing, and the name of the folder for outputs.
+
+Model Parameters
+----------------
+
+Here, we use the ASC with the dynamic load balancing, using the same load balancing parameters. In this case, the mobility status of the cells from the ASC will influence the weight, i.e. the computational contribution of the cell in the load balancing evaluation. The additional parameters for `active` cells `weight factor` is :math:`0.7`, and the `inactive` cells `weight factor` is :math:`0.5`, while the mobile cells always have a fixed weight factor of :math:`1`.
+
+.. code-block:: text
+
+ subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = true
+ set granular temperature threshold = 1e-4
+ set solid fraction threshold = 0.4
+ end
+ subsection load balancing
+ set load balance method = dynamic_with_sparse_contacts
+ set threshold = 0.5
+ set dynamic check frequency = 2500
+ set active weight factor = 0.7
+ set inactive weight factor = 0.5
+ end
+ end
+
+
+-----------------------
+Running the Simulations
+-----------------------
+
+Simulations can be launched individually with the executable ``lethe-particles`` and the parameter files, while saving the display in the terminal in a log file.
+To make things easier a script is provided to run all the simulations in a sequence from the ``dem/3d-plate-discharge/`` folder.
+
+In order to run the simulations for the performance analysis, you can use the following command:
+
+.. code-block:: text
+ :class: copy-button
+
+ bash run-performance-simulations.sh
+
+Which corresponds to:
+
+.. code-block:: bash
+
+ simulations=("base" "asc" "lb" "asc-lb")
+
+ cd performance/
+
+ for sim in "${simulations[@]}"
+ do
+ echo "Running the $sim simulation"
+ time mpirun -np 8 lethe-particles plate-discharge_$sim.prm | tee log_$sim.out
+ done
+
+Or you can run the simulations in the ``performance/`` folder with the following commands:
+
+.. code-block:: text
+ :class: copy-button
+
+ time mpirun -np 8 lethe-particles plate-discharge_base.prm | tee log_base.out
+ time mpirun -np 8 lethe-particles plate-discharge_asc.prm | tee log_asc.out
+ time mpirun -np 8 lethe-particles plate-discharge_lb.prm | tee log_lb.out
+ time mpirun -np 8 lethe-particles plate-discharge_asc-lb.prm | tee log_asc-lb.out
+
+In order to run the simulations for the data analysis, you can use the following script:
+
+.. code-block:: text
+ :class: copy-button
+
+ bash run-data-simulations.sh
+
+.. note::
+ Running the simulations for the performance analysis using 8 cores takes between 25 and 45 minutes per simulation, for a total of around 2 hours. Running the simulations for data analysis takes a few minutes longer per simulation.
+
+-------
+Results
+-------
+
+The simulations should look like the following video:
+
+.. raw:: html
+
+
+
+
+Post-Processing Code
+~~~~~~~~~~~~~~~~~~~~
+
+The data is extracted with the Lethe PyVista tool and post-processed with custom functions in the files ``pyvista_utilities.py`` and ``log_utilities.py``.
+Extraction, post-processing and plotting are automated in the script ``plate-discharge_post-processing.py``:
+
+.. code-block:: text
+ :class: copy-button
+
+ python3 plate-discharge_post-processing.py
+
+The script will generate the figures. If you want to modify the path or the filenames, you have to modify the script.
+
+Performance Analysis
+~~~~~~~~~~~~~~~~~~~~
+
+The log files (outputs displayed in the terminal) are read to extract the simulation and wall times.
+
+The speedup is calculated with the baseline case as the reference. The results are plotted in the following figure, where the solid lines show the walltime during the simulation, the dashed lines show the speedup, and the points show to total speedup.
+
+
+.. figure:: images/performance.png
+ :alt: Performance results
+ :align: center
+ :name: plate-discharge-performance-graph
+
+ The walltime during the simulations (solid line) and the speedup (dashed line) for the performance enhancement methods with the Adaptive Sparse Contacts (ASC) and the Load Balancing (LB) compared to the baseline case.
+
+.. note::
+ The slight oscillations of the speedup are caused by the scientific notation format of the walltime by the timer feature after :math:`1000 \ \text{s}`. The walltimes are attenuated by the moving average, but the division operation for the speedup accentuates the lack of time precision.
+
+The load balancing method helps the performance of the simulation from the start, since the particles move within the domain during the discharge. The load balancing allows to distribute the particles, and therefore all their related computations, more evenly between the cores. Once the discharge of the particles is mostly done and only a few particles are still falling from the top part, the performance gain brought by the load balancing stays constant since the load across the cores is already balanced.
+The adaptive sparse contacts method helps the performance of the simulation mostly when there are large areas of motionless particles. As it was showed in the video, those areas are located in the core of the pile at the top and at the corners of settled particles below the plate. This explains why the ASC gives a limited performance gain at the start of the simulation (only from the core of the pile) and an increasing gain through the simulation (accumulation of motionless particles at the bottom part). Given that both methods help the computation performance at different times, their combination gives the best performance as observed.
+
+
+Angle of Repose
+~~~~~~~~~~~~~~~
+
+The angles of repose are calculated from the data extracted from the VTU output files. The 2 angles of repose are calculated from the pile of particles on the plate for comparison with the literature, and from the piles formed by the discharge for curiosity.
+
+The configuration of the case gives a symmetrical formation of the piles, meaning that there are 2 angles of repose to calculate for the pile at the top of the plate and for the 2 piles at the bottom. The angles of repose are calculated by linear regressions from the highest particle positions in y-axis from :math:`-0.35 \ \text{m}` to :math:`-0.15 \ \text{m}` for the left angles and from :math:`0.15 \ \text{m}` to :math`0.35 \ \text{m}` for the right angles along the x-axis. The following figure shows the areas where the angles are calculated. The areas where the angle of repose is calculated for the left (blue) and right (red) sides of the piles.
+
+
+.. figure:: images/angle-areas.png
+ :alt: Angle of repose areas
+ :align: center
+ :name: plate-discharge-angle-areas
+
+
+In order to show how the results may fluctuate, we show the angle obtained from the particle positions from the left and the right sides of the top pile (left plot) and of the 2 piles at bottom (right plot) as solid lines.
+The given angles of repose are the linear regressions from the positions with absolute x coordinates.
+
+.. figure:: images/angle-of-repose.png
+ :alt: Angle of repose results
+ :align: center
+ :name: plate-discharge-angle-graph
+
+ The angles of repose calculated from the simulation data. The solid lines are the angles computed from the highest particles on both side, while the shaded areas represent the angles for the left and the right.
+
+According to Zhou *et al.* [#zhou2002]_, the angle of repose for this type of configuration is calculated with the following theoretical formula:
+
+.. math::
+ \phi = 68.61 \mu_{\text{f,pp}}^{0.27} \mu_{\text{f,pw}}^{0.22} \mu_{\text{r,pp}}^{0.06} \mu_{\text{r,pw}}^{0.12} d_p^{-0.2}
+
+
+where :math:`\mu_{\text{f,pp}}` and :math:`\mu_{\text{f,pw}}` are the friction coefficients of the particle-particle and particle-wall interactions, respectively, :math:`\mu_{\text{r,pp}}` and :math:`\mu_{\text{r,pw}}` are the rolling friction coefficients, and :math:`d_p` is the particle diameter.
+
+
+The meaning of the rolling friction coefficient :math:`\mu_{\text{r}}^{\text{eqt}}` by the authors [#zhou2002]_ is different than :math:`\mu_{\text{r}}^{\text{Lethe}}` found in Lethe. They express the coefficient as a length in the `rolling friction model <../../../theory/multiphase/cfd_dem/dem.html#rolling-friction-models>`_. However, they also use the constant torque, therefore the rolling friction coefficient in Lethe has to be multiplied by the effective radius of the particle for the results comparison:
+
+.. math::
+ \mu_{\text{r}}^{\text{eqt}} = \mu_{\text{r}}^{\text{Lethe}}d_p
+
+The theoretical angle of repose is :math:`19.7^\text{o}`. We did not compute the mean of the angles of repose in order to compare the results with the literature since, even after :math:`15 \ \text{s}` of simulation, some particles are still falling from the top. The angles are still not converging to a value. We can however state that the angles are close to the literature.
+
+Here we can see that the top angles from all simulations are in a range of around :math:`\pm 1.5^\text{o}` from the baseline case, which we consider as a good agreement. We can clearly see a trend in the bottom angles using the ASC. The angles of repose are about :math:`2^\text{o}` below the baseline and load balancing cases. It seems to be caused by the accumulation of particles at the bottom of the piles.
+
+
+----------
+References
+----------
+
+.. [#zhou2002] \Y.C. Zhou, B.H. Xu, A.B. Yu, P. Zulli, “An experimental and numerical study of the angle of repose of coarse spheres,” *Powder Technology*, vol. 125, pp. 45-54, 2002. doi: `10.1016/S0032-5910(01)00520-4 `_\.
diff --git a/doc/source/examples/unresolved-cfd-dem/dense-pneumatic-conveying/dense-pneumatic-conveying.rst b/doc/source/examples/unresolved-cfd-dem/dense-pneumatic-conveying/dense-pneumatic-conveying.rst
index 91aa20d6a1..ee0442b0a2 100644
--- a/doc/source/examples/unresolved-cfd-dem/dense-pneumatic-conveying/dense-pneumatic-conveying.rst
+++ b/doc/source/examples/unresolved-cfd-dem/dense-pneumatic-conveying/dense-pneumatic-conveying.rst
@@ -67,7 +67,7 @@ In this section we introduce the different sections of the parameter file ``load
Mesh
----
-In this example, we are simulating a horizontal cylindrical pipe. We use the `custom cylinder `_ of type balanced. We use this type of mesh in order to have uniform cell size in the radial direction. The length of a cell is about 2 times the diameter of the particles in each direction. The classical cylinder mesh of deal.II has smaller cells in the center which restrict the size of the particles. The length of the pipe is 1 m and the diameter is 0.084 m. The conveying is processed in the x-direction through periodic boundary conditions.
+In this example, we are simulating a horizontal cylindrical pipe. We use the `custom cylinder <../../../parameters/cfd/mesh.html>`_ of type balanced. We use this type of mesh in order to have uniform cell size in the radial direction. The length of a cell is about 2 times the diameter of the particles in each direction. The classical cylinder mesh of deal.II has smaller cells in the center which restrict the size of the particles. The length of the pipe is 1 m and the diameter is 0.084 m. The conveying is processed in the x-direction through periodic boundary conditions.
.. code-block:: text
@@ -234,7 +234,7 @@ The model parameters are quite standard for a DEM simulation with the non-linear
.. note::
- Here, we use the `Adaptive Sparse Contacts <../../../parameters/dem/model_parameters.html#adaptive-sparse-contacts-asc>`_ method to speedup the simulation. The method will disabled the contact computation in quasi-static areas which represents a significant part of the domain during the loading of the particles. Weight factor parameters for the ASC status are use in the load balancing method. No further explanation a given about the method, a future example will be added in order to detail it and to compare the performance gain.
+ Here, we use the `Adaptive Sparse Contacts (ASC) <../../../parameters/dem/model_parameters.html#adaptive-sparse-contacts-asc>`_ method to speedup the simulation. The method will disabled the contact computation in quasi-static areas which represents a significant part of the domain during the loading of the particles. Weight factor parameters for the ASC status are use in the load balancing method. The `discharge plate example <../../dem/plate-discharge/plate-discharge.html>`_ is a good example of the use of the ASC method with DEM.
.. code-block:: text
@@ -315,7 +315,7 @@ Here we allow a 2.5 seconds for the settling of the particles. Since this simula
end
Restart
-~~~~~~~~
+-------
This simulation reads the restart, meaning this option is set to true. Also, the checkpointing is reduce to 0.5 seconds.
diff --git a/examples/dem/3d-plate-discharge/data/output_asc-lb/dummy-file b/examples/dem/3d-plate-discharge/data/output_asc-lb/dummy-file
new file mode 100644
index 0000000000..482db144d3
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/output_asc-lb/dummy-file
@@ -0,0 +1,2 @@
+Dummy file to ensure folder exists
+
diff --git a/examples/dem/3d-plate-discharge/data/output_asc/dummy-file b/examples/dem/3d-plate-discharge/data/output_asc/dummy-file
new file mode 100644
index 0000000000..482db144d3
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/output_asc/dummy-file
@@ -0,0 +1,2 @@
+Dummy file to ensure folder exists
+
diff --git a/examples/dem/3d-plate-discharge/data/output_base/dummy-file b/examples/dem/3d-plate-discharge/data/output_base/dummy-file
new file mode 100644
index 0000000000..482db144d3
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/output_base/dummy-file
@@ -0,0 +1,2 @@
+Dummy file to ensure folder exists
+
diff --git a/examples/dem/3d-plate-discharge/data/output_lb/dummy-file b/examples/dem/3d-plate-discharge/data/output_lb/dummy-file
new file mode 100644
index 0000000000..482db144d3
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/output_lb/dummy-file
@@ -0,0 +1,2 @@
+Dummy file to ensure folder exists
+
diff --git a/examples/dem/3d-plate-discharge/data/plate-discharge_asc-lb.prm b/examples/dem/3d-plate-discharge/data/plate-discharge_asc-lb.prm
new file mode 100644
index 0000000000..fb8eefad53
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/plate-discharge_asc-lb.prm
@@ -0,0 +1,165 @@
+# Listing of Parameters
+#----------------------
+
+set dimension = 3
+
+#---------------------------------------------------
+# Simulation Control
+#---------------------------------------------------
+
+subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 100
+ set output path = ./output_asc-lb/
+end
+
+#---------------------------------------------------
+# Timer
+#---------------------------------------------------
+
+subsection timer
+ set type = iteration
+end
+
+#---------------------------------------------------
+# Model parameters
+#---------------------------------------------------
+
+subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = true
+ set granular temperature threshold = 1e-4
+ set solid fraction threshold = 0.4
+ end
+ subsection load balancing
+ set load balance method = dynamic_with_sparse_contacts
+ set threshold = 0.5
+ set dynamic check frequency = 2500
+ set active weight factor = 0.7
+ set inactive weight factor = 0.5
+ end
+end
+
+#---------------------------------------------------
+# Physical Properties
+#---------------------------------------------------
+
+subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+end
+
+#---------------------------------------------------
+# Insertion Info
+#---------------------------------------------------
+
+subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+end
+
+#---------------------------------------------------
+# Mesh
+#---------------------------------------------------
+
+subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+end
+
+#---------------------------------------------------
+# Solid objects
+#---------------------------------------------------
+
+subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+end
+
+#---------------------------------------------------
+# Floating walls
+#---------------------------------------------------
+
+subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+end
+
+#---------------------------------------------------
+# Lagrangian Post-processing
+#---------------------------------------------------
+
+subsection post-processing
+ set Lagrangian post-processing = true
+end
diff --git a/examples/dem/3d-plate-discharge/data/plate-discharge_asc.prm b/examples/dem/3d-plate-discharge/data/plate-discharge_asc.prm
new file mode 100644
index 0000000000..84b2d84f0a
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/plate-discharge_asc.prm
@@ -0,0 +1,161 @@
+# Listing of Parameters
+#----------------------
+
+set dimension = 3
+
+#---------------------------------------------------
+# Simulation Control
+#---------------------------------------------------
+
+subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 100
+ set output path = ./output_asc/
+end
+
+#---------------------------------------------------
+# Timer
+#---------------------------------------------------
+
+subsection timer
+ set type = iteration
+end
+
+#---------------------------------------------------
+# Model parameters
+#---------------------------------------------------
+
+subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = true
+ set granular temperature threshold = 1e-4
+ set solid fraction threshold = 0.4
+ end
+ subsection load balancing
+ set load balance method = none
+ end
+end
+
+#---------------------------------------------------
+# Physical Properties
+#---------------------------------------------------
+
+subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+end
+
+#---------------------------------------------------
+# Insertion Info
+#---------------------------------------------------
+
+subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+end
+
+#---------------------------------------------------
+# Mesh
+#---------------------------------------------------
+
+subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+end
+
+#---------------------------------------------------
+# Solid objects
+#---------------------------------------------------
+
+subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+end
+
+#---------------------------------------------------
+# Floating walls
+#---------------------------------------------------
+
+subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+end
+
+#---------------------------------------------------
+# Lagrangian Post-processing
+#---------------------------------------------------
+
+subsection post-processing
+ set Lagrangian post-processing = true
+end
diff --git a/examples/dem/3d-plate-discharge/data/plate-discharge_base.prm b/examples/dem/3d-plate-discharge/data/plate-discharge_base.prm
new file mode 100644
index 0000000000..0cb4093f9c
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/plate-discharge_base.prm
@@ -0,0 +1,159 @@
+# Listing of Parameters
+#----------------------
+
+set dimension = 3
+
+#---------------------------------------------------
+# Simulation Control
+#---------------------------------------------------
+
+subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 100
+ set output path = ./output_base/
+end
+
+#---------------------------------------------------
+# Timer
+#---------------------------------------------------
+
+subsection timer
+ set type = iteration
+end
+
+#---------------------------------------------------
+# Model parameters
+#---------------------------------------------------
+
+subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = false
+ end
+ subsection load balancing
+ set load balance method = none
+ end
+end
+
+#---------------------------------------------------
+# Physical Properties
+#---------------------------------------------------
+
+subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+end
+
+#---------------------------------------------------
+# Insertion Info
+#---------------------------------------------------
+
+subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+end
+
+#---------------------------------------------------
+# Mesh
+#---------------------------------------------------
+
+subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+end
+
+#---------------------------------------------------
+# Solid objects
+#---------------------------------------------------
+
+subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+end
+
+#---------------------------------------------------
+# Floating walls
+#---------------------------------------------------
+
+subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+end
+
+#---------------------------------------------------
+# Lagrangian Post-processing
+#---------------------------------------------------
+
+subsection post-processing
+ set Lagrangian post-processing = true
+end
diff --git a/examples/dem/3d-plate-discharge/data/plate-discharge_lb.prm b/examples/dem/3d-plate-discharge/data/plate-discharge_lb.prm
new file mode 100644
index 0000000000..dd79e740c6
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/plate-discharge_lb.prm
@@ -0,0 +1,161 @@
+# Listing of Parameters
+#----------------------
+
+set dimension = 3
+
+#---------------------------------------------------
+# Simulation Control
+#---------------------------------------------------
+
+subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 100
+ set output path = ./output_lb/
+end
+
+#---------------------------------------------------
+# Timer
+#---------------------------------------------------
+
+subsection timer
+ set type = iteration
+end
+
+#---------------------------------------------------
+# Model parameters
+#---------------------------------------------------
+
+subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = false
+ end
+ subsection load balancing
+ set load balance method = dynamic
+ set threshold = 0.5
+ set dynamic check frequency = 2500
+ end
+end
+
+#---------------------------------------------------
+# Physical Properties
+#---------------------------------------------------
+
+subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+end
+
+#---------------------------------------------------
+# Insertion Info
+#---------------------------------------------------
+
+subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+end
+
+#---------------------------------------------------
+# Mesh
+#---------------------------------------------------
+
+subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+end
+
+#---------------------------------------------------
+# Solid objects
+#---------------------------------------------------
+
+subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+end
+
+#---------------------------------------------------
+# Floating walls
+#---------------------------------------------------
+
+subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+end
+
+#---------------------------------------------------
+# Lagrangian Post-processing
+#---------------------------------------------------
+
+subsection post-processing
+ set Lagrangian post-processing = true
+end
diff --git a/examples/dem/3d-plate-discharge/data/plate.msh b/examples/dem/3d-plate-discharge/data/plate.msh
new file mode 100644
index 0000000000..3fcbf5df32
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/data/plate.msh
@@ -0,0 +1,41 @@
+$MeshFormat
+4.1 0 8
+$EndMeshFormat
+$Entities
+4 5 2 0
+0 -0.45 0 0 0
+1 -0.45 0 0.2 0
+2 0.45 0 0.2 0
+3 0.45 0 0 0
+0 -0.4500001 -1e-07 -1.000000000028756e-07 -0.4499999 1e-07 0.2000001 0 2 0 -1
+1 -0.4500001 -1e-07 0.1999999 0.4500001 1e-07 0.2000001 0 2 1 -2
+2 0.4499999 -1e-07 -1.000000000028756e-07 0.4500001 1e-07 0.2000001 0 2 2 -3
+3 -0.4500001 -1e-07 -1e-07 0.4500001 1e-07 1e-07 0 2 3 0
+100 -0.4500001 -1e-07 -1.000000000028756e-07 0.4500001 1e-07 0.2000001 0 2 2 0
+1 -0.4500001 -1e-07 -1.000000000028756e-07 0.4500001 1e-07 0.2000001 1 0 3 1 100 0
+100 -0.4500001 -1e-07 -1.000000000028756e-07 0.4500001 1e-07 0.2000001 1 0 3 3 -100 2
+$EndEntities
+$Nodes
+6 4 1 4
+0 0 0 1
+1
+-0.45 0 0
+0 1 0 1
+2
+-0.45 0 0.2
+0 2 0 1
+3
+0.45 0 0.2
+0 3 0 1
+4
+0.45 0 0
+2 1 0 0
+2 100 0 0
+$EndNodes
+$Elements
+2 2 1 2
+2 1 2 1
+1 1 2 3
+2 100 2 1
+2 1 3 4
+$EndElements
diff --git a/examples/dem/3d-plate-discharge/log_utilities.py b/examples/dem/3d-plate-discharge/log_utilities.py
new file mode 100644
index 0000000000..12cb8a2b67
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/log_utilities.py
@@ -0,0 +1,47 @@
+import numpy as np
+import pandas as pd
+
+class LogUtilities:
+ def __init__(self, log_filename):
+ with open(log_filename) as f:
+ self.lines = f.readlines()
+
+ log_data_type = ['time', 'dem_walltime']
+ self.log_dataframe = pd.DataFrame(columns=log_data_type)
+ self.log_dataframe['time'] = self.get_times()
+ self.total_walltime = []
+
+ def get_times(self):
+ # Find the time in the log file through the keyword "Transient" and some hardcoded indices
+ time_keyword = "Transient"
+ time = np.array([0])
+
+ for line in self.lines:
+ if time_keyword in line:
+ time_str = line[36:36 + 6].strip()
+ time = np.append(time, float(time_str))
+ return time
+
+ def get_log_data(self):
+ return self.log_dataframe
+
+ def dem_performance_monitoring(self, n_char_0=49, n_char=8):
+ walltime_keyword = "Total wallclock"
+ walltime = [0]
+ dof_time = True
+
+ for line in self.lines:
+ if walltime_keyword in line:
+ if dof_time == True:
+ dof_time = False
+ else:
+ if walltime[-1] > 999:
+
+ a = 1
+ walltime.append(float(line[n_char_0:n_char_0 + n_char].strip()))
+
+ self.log_dataframe['dem_walltime'] = walltime
+ self.total_walltime.append(walltime[-1])
+
+ print("DEM time monitoring done.")
+
diff --git a/examples/dem/3d-plate-discharge/performance/plate-discharge_asc-lb.prm b/examples/dem/3d-plate-discharge/performance/plate-discharge_asc-lb.prm
new file mode 100644
index 0000000000..ee9307bd61
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/performance/plate-discharge_asc-lb.prm
@@ -0,0 +1,165 @@
+# Listing of Parameters
+#----------------------
+
+set dimension = 3
+
+#---------------------------------------------------
+# Simulation Control
+#---------------------------------------------------
+
+subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 0
+ set output path = ./output_asc-lb/
+end
+
+#---------------------------------------------------
+# Timer
+#---------------------------------------------------
+
+subsection timer
+ set type = iteration
+end
+
+#---------------------------------------------------
+# Model parameters
+#---------------------------------------------------
+
+subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = true
+ set granular temperature threshold = 1e-4
+ set solid fraction threshold = 0.4
+ end
+ subsection load balancing
+ set load balance method = dynamic_with_sparse_contacts
+ set threshold = 0.5
+ set dynamic check frequency = 2500
+ set active weight factor = 0.7
+ set inactive weight factor = 0.5
+ end
+end
+
+#---------------------------------------------------
+# Physical Properties
+#---------------------------------------------------
+
+subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+end
+
+#---------------------------------------------------
+# Insertion Info
+#---------------------------------------------------
+
+subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+end
+
+#---------------------------------------------------
+# Mesh
+#---------------------------------------------------
+
+subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+end
+
+#---------------------------------------------------
+# Solid objects
+#---------------------------------------------------
+
+subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+end
+
+#---------------------------------------------------
+# Floating walls
+#---------------------------------------------------
+
+subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+end
+
+#---------------------------------------------------
+# Lagrangian Post-processing
+#---------------------------------------------------
+
+subsection post-processing
+ set Lagrangian post-processing = false
+end
diff --git a/examples/dem/3d-plate-discharge/performance/plate-discharge_asc.prm b/examples/dem/3d-plate-discharge/performance/plate-discharge_asc.prm
new file mode 100644
index 0000000000..8bb1a197e1
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/performance/plate-discharge_asc.prm
@@ -0,0 +1,161 @@
+# Listing of Parameters
+#----------------------
+
+set dimension = 3
+
+#---------------------------------------------------
+# Simulation Control
+#---------------------------------------------------
+
+subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 0
+ set output path = ./output_asc/
+end
+
+#---------------------------------------------------
+# Timer
+#---------------------------------------------------
+
+subsection timer
+ set type = iteration
+end
+
+#---------------------------------------------------
+# Model parameters
+#---------------------------------------------------
+
+subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = true
+ set granular temperature threshold = 1e-4
+ set solid fraction threshold = 0.4
+ end
+ subsection load balancing
+ set load balance method = none
+ end
+end
+
+#---------------------------------------------------
+# Physical Properties
+#---------------------------------------------------
+
+subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+end
+
+#---------------------------------------------------
+# Insertion Info
+#---------------------------------------------------
+
+subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+end
+
+#---------------------------------------------------
+# Mesh
+#---------------------------------------------------
+
+subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+end
+
+#---------------------------------------------------
+# Solid objects
+#---------------------------------------------------
+
+subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+end
+
+#---------------------------------------------------
+# Floating walls
+#---------------------------------------------------
+
+subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+end
+
+#---------------------------------------------------
+# Lagrangian Post-processing
+#---------------------------------------------------
+
+subsection post-processing
+ set Lagrangian post-processing = false
+end
diff --git a/examples/dem/3d-plate-discharge/performance/plate-discharge_base.prm b/examples/dem/3d-plate-discharge/performance/plate-discharge_base.prm
new file mode 100644
index 0000000000..fc3dc65650
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/performance/plate-discharge_base.prm
@@ -0,0 +1,159 @@
+# Listing of Parameters
+#----------------------
+
+set dimension = 3
+
+#---------------------------------------------------
+# Simulation Control
+#---------------------------------------------------
+
+subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 0
+ set output path = ./output_base/
+end
+
+#---------------------------------------------------
+# Timer
+#---------------------------------------------------
+
+subsection timer
+ set type = iteration
+end
+
+#---------------------------------------------------
+# Model parameters
+#---------------------------------------------------
+
+subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = false
+ end
+ subsection load balancing
+ set load balance method = none
+ end
+end
+
+#---------------------------------------------------
+# Physical Properties
+#---------------------------------------------------
+
+subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+end
+
+#---------------------------------------------------
+# Insertion Info
+#---------------------------------------------------
+
+subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+end
+
+#---------------------------------------------------
+# Mesh
+#---------------------------------------------------
+
+subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+end
+
+#---------------------------------------------------
+# Solid objects
+#---------------------------------------------------
+
+subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+end
+
+#---------------------------------------------------
+# Floating walls
+#---------------------------------------------------
+
+subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+end
+
+#---------------------------------------------------
+# Lagrangian Post-processing
+#---------------------------------------------------
+
+subsection post-processing
+ set Lagrangian post-processing = false
+end
diff --git a/examples/dem/3d-plate-discharge/performance/plate-discharge_lb.prm b/examples/dem/3d-plate-discharge/performance/plate-discharge_lb.prm
new file mode 100644
index 0000000000..f86e5aa303
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/performance/plate-discharge_lb.prm
@@ -0,0 +1,166 @@
+# Listing of Parameters
+#----------------------
+
+set dimension = 3
+
+#---------------------------------------------------
+# Simulation Control
+#---------------------------------------------------
+
+subsection simulation control
+ set time step = 1e-4
+ set time end = 15
+ set log frequency = 500
+ set output frequency = 0
+ set output path = ./output_lb/
+end
+
+#---------------------------------------------------
+# Timer
+#---------------------------------------------------
+
+subsection timer
+ set type = iteration
+end
+
+#---------------------------------------------------
+# Model parameters
+#---------------------------------------------------
+
+subsection model parameters
+ subsection contact detection
+ set contact detection method = dynamic
+ set dynamic contact search size coefficient = 0.9
+ set neighborhood threshold = 1.3
+ end
+ subsection load balancing
+ set load balance method = dynamic
+ set threshold = 0.5
+ set dynamic check frequency = 2500
+ end
+ set particle particle contact force method = hertz_mindlin_limit_overlap
+ set rolling resistance torque method = constant_resistance
+ set particle wall contact force method = nonlinear
+ set integration method = velocity_verlet
+ subsection adaptive sparse contacts
+ set enable adaptive sparse contacts = false
+ end
+ subsection load balancing
+ set load balance method = dynamic
+ set threshold = 0.5
+ set dynamic check frequency = 2500
+ end
+end
+
+#---------------------------------------------------
+# Physical Properties
+#---------------------------------------------------
+
+subsection lagrangian physical properties
+ set g = 0, -9.81, 0.0
+ set number of particle types = 1
+ subsection particle type 0
+ set size distribution type = uniform
+ set diameter = 0.01
+ set number of particles = 52000
+ set density particles = 2400
+ set young modulus particles = 1e6
+ set poisson ratio particles = 0.3
+ set restitution coefficient particles = 0.9
+ set friction coefficient particles = 0.2
+ set rolling friction particles = 0.1
+ end
+ set young modulus wall = 1e6
+ set poisson ratio wall = 0.3
+ set restitution coefficient wall = 0.9
+ set friction coefficient wall = 0.2
+ set rolling friction wall = 0.1
+end
+
+#---------------------------------------------------
+# Insertion Info
+#---------------------------------------------------
+
+subsection insertion info
+ set insertion method = volume
+ set inserted number of particles at each time step = 52000
+ set insertion frequency = 20000
+ set insertion box points coordinates = -0.45, 0.4, 0 : 0.45, 1.0, 0.2
+ set insertion distance threshold = 1.25
+ set insertion maximum offset = 0.1
+ set insertion prn seed = 20
+ set insertion direction sequence = 0, 2, 1
+end
+
+#---------------------------------------------------
+# Mesh
+#---------------------------------------------------
+
+subsection mesh
+ set type = dealii
+ set grid type = subdivided_hyper_rectangle
+ set grid arguments = 5,5,1 : -0.5, 0.0, 0.0 : 0.5, 1.0, 0.2 : true
+ set initial refinement = 3
+end
+
+#---------------------------------------------------
+# Solid objects
+#---------------------------------------------------
+
+subsection solid objects
+ subsection solid surfaces
+ set number of solids = 1
+ subsection solid object 0
+ subsection mesh
+ set type = gmsh
+ set file name = plate.msh
+ set simplex = true
+ set initial translation = 0, 0.4, 0
+ end
+ end
+ end
+end
+
+#---------------------------------------------------
+# Floating walls
+#---------------------------------------------------
+
+subsection floating walls
+ set number of floating walls = 2
+ subsection wall 0
+ subsection point on wall
+ set x = -0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+ subsection wall 1
+ subsection point on wall
+ set x = 0.45
+ set y = 0
+ set z = 0
+ end
+ subsection normal vector
+ set nx = 1
+ set ny = 0
+ set nz = 0
+ end
+ set start time = 0
+ set end time = 0.75
+ end
+end
+
+#---------------------------------------------------
+# Lagrangian Post-processing
+#---------------------------------------------------
+
+subsection post-processing
+ set Lagrangian post-processing = false
+end
diff --git a/examples/dem/3d-plate-discharge/performance/plate.msh b/examples/dem/3d-plate-discharge/performance/plate.msh
new file mode 100644
index 0000000000..3fcbf5df32
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/performance/plate.msh
@@ -0,0 +1,41 @@
+$MeshFormat
+4.1 0 8
+$EndMeshFormat
+$Entities
+4 5 2 0
+0 -0.45 0 0 0
+1 -0.45 0 0.2 0
+2 0.45 0 0.2 0
+3 0.45 0 0 0
+0 -0.4500001 -1e-07 -1.000000000028756e-07 -0.4499999 1e-07 0.2000001 0 2 0 -1
+1 -0.4500001 -1e-07 0.1999999 0.4500001 1e-07 0.2000001 0 2 1 -2
+2 0.4499999 -1e-07 -1.000000000028756e-07 0.4500001 1e-07 0.2000001 0 2 2 -3
+3 -0.4500001 -1e-07 -1e-07 0.4500001 1e-07 1e-07 0 2 3 0
+100 -0.4500001 -1e-07 -1.000000000028756e-07 0.4500001 1e-07 0.2000001 0 2 2 0
+1 -0.4500001 -1e-07 -1.000000000028756e-07 0.4500001 1e-07 0.2000001 1 0 3 1 100 0
+100 -0.4500001 -1e-07 -1.000000000028756e-07 0.4500001 1e-07 0.2000001 1 0 3 3 -100 2
+$EndEntities
+$Nodes
+6 4 1 4
+0 0 0 1
+1
+-0.45 0 0
+0 1 0 1
+2
+-0.45 0 0.2
+0 2 0 1
+3
+0.45 0 0.2
+0 3 0 1
+4
+0.45 0 0
+2 1 0 0
+2 100 0 0
+$EndNodes
+$Elements
+2 2 1 2
+2 1 2 1
+1 1 2 3
+2 100 2 1
+2 1 3 4
+$EndElements
diff --git a/examples/dem/3d-plate-discharge/plate-discharge_post-processing.py b/examples/dem/3d-plate-discharge/plate-discharge_post-processing.py
new file mode 100644
index 0000000000..ab644db349
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/plate-discharge_post-processing.py
@@ -0,0 +1,179 @@
+from lethe_pyvista_tools import *
+import matplotlib.pyplot as plt
+from pyvista_utilities import *
+from log_utilities import *
+
+#############################
+# Path and filenames
+#############################
+names = ["base", "asc", "lb", "asc-lb"]
+dict_names = {"base": "Baseline", "asc": "Adaptive Sparse Contacts", "lb": "Load Balancing", "asc-lb": "ASC-LB"}
+pvd_name = "out"
+
+#############################
+# Read data and process
+#############################
+# In this post-processing, we need the ID of the particles, the position, and the velocity, the other data is not
+# necessary and takes a lot of memory
+ignore_data = ['omega', 'type', 'diameter', 'mass', 'fem_force', 'fem_torque']
+
+# Data we want to process
+process_data = True
+process_log = True
+
+# All plots
+fig_mass = plt.figure(figsize=(7.5, 5))
+fig_angle = plt.figure(figsize=(10, 5))
+fig_performance = plt.figure(figsize=(7.5, 5))
+
+mass_plot = fig_mass.add_subplot(111)
+angle_top_plot = fig_angle.add_subplot(121)
+angle_bottom_plot = fig_angle.add_subplot(122)
+
+performance_plot = fig_performance.add_subplot(111)
+speedup_plot = plt.twinx(performance_plot)
+
+log_list = []
+data_list = []
+
+for k, name in enumerate(names):
+# Create the particle object
+ if process_data:
+ particles = lethe_pyvista_tools(f"data", f"plate-discharge_{name}.prm", f"{pvd_name}.pvd",
+ ignore_data=ignore_data, step=5)
+ prm_dict = particles.prm_dict
+ dataframe = []
+
+ # Some plate information
+ plate_norm = 'y'
+ plate_direction = 'x'
+
+ for i in range(len(particles.list_vtu)):
+ df = particles.get_df(i)
+ data = pd.DataFrame()
+ # Get the ID of the particles
+ data['ID'] = df['ID'].astype(int)
+
+ # Get the position of the particles
+ data['x'] = df.points[:, 0]
+ data['y'] = df.points[:, 1]
+ data['z'] = df.points[:, 2]
+ data.set_index('ID', inplace=True)
+
+ dataframe.append(data)
+
+ # Create the post-processing and log object
+ postprocessing = PlateDischargeUtilities(dataframe, prm_dict, particles.time_list,
+ plate_norm, plate_direction)
+
+ # Solid mass flow rate
+ postprocessing.calculate_solid_mass_flow_rate()
+
+ # Process the top left and right angle of repose
+ postprocessing.calculate_angle_of_repose(top=True, start_time=5, x_min=-0.35, x_max=-0.15)
+ postprocessing.calculate_angle_of_repose(top=True, start_time=5, x_min=0.15, x_max=0.35)
+ postprocessing.calculate_angle_from_symmetry(top=True, start=5)
+
+ # Process the bottom left and right angle of repose
+ postprocessing.calculate_angle_of_repose(top=False, start_time=5, x_min=-0.35, x_max=-0.15)
+ postprocessing.calculate_angle_of_repose(top=False, start_time=5, x_min=0.15, x_max=0.35)
+ postprocessing.calculate_angle_from_symmetry(top=False, start=5)
+
+ # Get theoretical angle of repose from Zhou et al. 2002
+ postprocessing.theoretical_angle_of_repose()
+
+ # Get the dataframes
+ postprocessing_dataframe = postprocessing.get_data()
+ data_list.append(postprocessing_dataframe)
+
+
+ if process_log:
+ log = LogUtilities(f"performance/log_{name}.out")
+ log.dem_performance_monitoring()
+ log_dataframe = log.get_log_data()
+ log_list.append(log_dataframe)
+
+if process_data:
+ # All simulations have the same time list
+ time = data_list[0]['time'].values
+
+ angle_top_plot.plot(time, postprocessing.theoritical_angle * np.ones(len(time)), '--', color='k', label='Theoretical angle', linewidth=1.00)
+
+ for k in range(0, len(names)):
+ time = data_list[k]['time'].values
+ mass_plot.plot(time, data_list[k]['mass'], '--', color='C' + str(k),
+ label=f'{dict_names[names[k]]}', linewidth=1.00)
+
+ angle_top_plot.plot(time, abs(data_list[k]['angle_top']), color='C' + str(k), label=f'{dict_names[names[k]]}', linewidth=1.00)
+ angle_top_plot.fill_between(time,
+ abs(data_list[k]['left_angle_top'].values).astype(float),
+ abs(data_list[k]['right_angle_top'].values).astype(float), color='C' + str(k), alpha=0.5)
+
+ angle_bottom_plot.plot(time, abs(data_list[k]['angle_bottom']),
+ color='C' + str(k), label=f'{dict_names[names[k]]}', linewidth=1.00)
+ angle_bottom_plot.fill_between(time,
+ abs(pd.to_numeric(data_list[k]['left_angle_bottom'].values,
+ errors='coerce')),
+ abs(pd.to_numeric(data_list[k]['right_angle_bottom'].values,
+ errors='coerce')), color='C' + str(k), alpha=0.5)
+
+ start = 10
+ end = 15
+ mass_plot.set(ylim=[0, 45], xlim=[0, end])
+ mass_plot.set_xlabel('Time (s)')
+ mass_plot.set_ylabel('Solid mass (kg)')
+ mass_plot.grid(color='k', linestyle='-', linewidth=0.4)
+ mass_plot.xaxis.set_major_locator(plt.MaxNLocator(11))
+ mass_plot.legend(edgecolor="k", fancybox=0,
+ facecolor="white", framealpha=1).get_frame().set_linewidth(0.75)
+
+ angle_top_plot.set(ylim=[16, 26], xlim=[start , end])
+ angle_top_plot.set_xlabel('Time (s)')
+ angle_top_plot.set_ylabel('Top angle of repose (°)')
+ angle_top_plot.grid(color='k', linestyle='-', linewidth=0.4)
+ angle_top_plot.xaxis.set_major_locator(plt.MaxNLocator(6))
+ angle_top_plot.axvspan(0, start, color='gray', alpha=0.15, lw=0)
+ angle_top_plot.legend(edgecolor="k", fancybox=0,
+ facecolor="white", framealpha=1).get_frame().set_linewidth(0.75)
+
+ angle_bottom_plot.set(ylim=[16, 26], xlim=[start, end])
+ angle_bottom_plot.set_xlabel('Time (s)')
+ angle_bottom_plot.set_ylabel('Bottom angle of repose (°)')
+ angle_bottom_plot.grid(color='k', linestyle='-', linewidth=0.4)
+ angle_bottom_plot.xaxis.set_major_locator(plt.MaxNLocator(6))
+ angle_bottom_plot.axvspan(0, start, color='gray', alpha=0.15, lw=0)
+ angle_bottom_plot.legend(edgecolor="k", fancybox=0,
+ facecolor="white", framealpha=1, loc='lower right').get_frame().set_linewidth(0.75)
+
+ fig_mass.savefig(f'./plate-discharge-data.png', bbox_inches='tight', dpi=300)
+ fig_angle.savefig(f'./plate-discharge-angle-data.png', bbox_inches='tight', dpi=300)
+
+if process_log:
+ time = log_list[0]['time'].values
+ for k in range(0, len(names)):
+ performance_plot.plot(time, log_list[k]['dem_walltime'].rolling(3).mean(),
+ color='C' + str(k), label=f'{dict_names[names[k]]}', linewidth=1.00)
+
+ if k != 0:
+ speedup = log_list[0]['dem_walltime']/log_list[k]['dem_walltime']
+ speedup[0] = 0.0
+ speedup_plot.plot(time, speedup.rolling(5).mean(), '--', color='C' + str(k), label=f'{dict_names[names[k]]}', linewidth=1.00)
+ speedup_plot.plot(time[-1], speedup.iat[-1], 'o', color='C' + str(k), markersize=5)
+ speedup_plot.annotate(f'{speedup.iat[-1]:.2f}x', (time[-1], speedup.iat[-1]), textcoords="offset points", xytext=(-31, 1.75), color='C' + str(k))
+
+ performance_plot.set(ylim=[0, 4000], xlim=[0, 15])
+ performance_plot.set_xlabel('Time (s)')
+ performance_plot.set_ylabel('Walltime (s)')
+ performance_plot.grid(color='k', linestyle='-', linewidth=0.4)
+ performance_plot.xaxis.set_major_locator(plt.MaxNLocator(11))
+ performance_plot.legend(edgecolor="k", fancybox=0,
+ facecolor="white", framealpha=1, loc='lower right').get_frame().set_linewidth(0.75)
+
+ speedup_plot.set_ylabel('Speedup')
+ speedup_plot.yaxis.set_major_locator(plt.MaxNLocator(9))
+ speedup_plot.set(ylim=[1.0, 1.8], xlim=[0, 15])
+ fig_performance.savefig(f'./plate-discharge-performance-data.png', bbox_inches='tight', dpi=300)
+
+
+plt.tight_layout()
+plt.show()
\ No newline at end of file
diff --git a/examples/dem/3d-plate-discharge/plate.geo b/examples/dem/3d-plate-discharge/plate.geo
new file mode 100644
index 0000000000..8362f1b4ba
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/plate.geo
@@ -0,0 +1,29 @@
+//+
+SetFactory("OpenCASCADE");
+lc = 1;
+W = 0.2;
+L = 0.45;
+H = 0.0;
+
+
+// Left side
+Point(0) = {-L, H, 0, lc};
+Point(1) = {-L, H, W, lc};
+Point(2) = {L, H, W, lc};
+Point(3) = {L, H, 0, lc};
+
+Line(0)={0,1};
+Line(1)={1,2};
+Line(2)={2,3};
+Line(3)={3,0};
+
+Line(100)={2,0};
+
+Line Loop(1) = {1,0,100};
+Line Loop(100) = {3,2,100};
+
+Plane Surface(1) = {1};
+Plane Surface(100) = {100};
+
+Physical Surface(0) = {1,100};
+
diff --git a/examples/dem/3d-plate-discharge/pyvista_utilities.py b/examples/dem/3d-plate-discharge/pyvista_utilities.py
new file mode 100644
index 0000000000..e683e4fe4e
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/pyvista_utilities.py
@@ -0,0 +1,242 @@
+import numpy as np
+import pandas as pd
+import scipy as sc
+
+def position_string_to_int(coord_str):
+ string_to_int = {'x': 0, 'y': 1, 'z': 2}
+ return string_to_int[coord_str]
+
+class PlateDischargeUtilities:
+ def __init__(self, dataframe, prm_dict, time_list, plate_norm, plate_direction):
+ # Storing the dataframe data with the particles information, the
+ # parameter for the prm file, and the time list
+ self.dataframe = dataframe
+ self.prm_dict = prm_dict
+ self.time_list = time_list
+
+ # Save the direction and the norm of the plate as number for string
+ self.direction = plate_direction
+ self.direction_str = plate_direction
+ self.norm = plate_norm
+ self.norm_str = position_string_to_int(plate_norm)
+
+ # Save the position, the height, and the length of the plate
+ p0 = prm_dict['initial translation'].split(', ')
+ self.H = float(p0[self.norm_str])
+ self.plate_extremities = prm_dict[plate_direction]
+ self.L = self.plate_extremities[1] - self.plate_extremities[0]
+
+ # Initialize the dataframe for the particles position
+ self.particles_x_top = [pd.Series()] * len(time_list)
+ self.particles_y_top = [pd.Series()] * len(time_list)
+ self.particles_x_bottom = [pd.Series()] * len(time_list)
+ self.particles_y_bottom = [pd.Series()] * len(time_list)
+
+ # Storing post-processing data
+ postprocessing_data_type = ['time',
+ 'number_of_particles', 'mass', 'mass_flow_rate',
+ 'left_angle_top', '_left_angle_top_rsqrt', 'left_angle_bottom', 'left_angle_bottom_rsqrt',
+ 'right_angle_top', '_right_angle_top_rsqrt', 'right_angle_bottom', 'right_angle_bottom_rsqrt',
+ 'angle_top', 'angle_bottom', 'angle_top_rsqrt', 'angle_bottom_rsqrt']
+ self.postprocessing_dataframe = pd.DataFrame(columns=postprocessing_data_type)
+ self.postprocessing_dataframe['time'] = time_list
+
+ # Useful variables for parameter file
+ self.compute_particle_info()
+
+ def compute_particle_info(self):
+ self.dp = self.prm_dict['diameter']
+ self.rhop = self.prm_dict['density particles']
+ self.vp = 4. / 3. * np.pi * (self.dp / 2.) ** 3
+
+ def get_data(self):
+ return self.postprocessing_dataframe
+
+ print("Solid mass flow rate computing done.")
+ def calculate_solid_mass_flow_rate(self):
+ # Virtual wall to limit the domain in the pipe for evaluation
+ wall = self.H
+
+ for i, df in enumerate(self.dataframe):
+ # Get the IDs and the positions of the particles
+ positions = df[self.norm].values
+
+ # Number of particles below and mass discharge
+ number_of_particles = (positions < wall).sum()
+ mass = number_of_particles * self.vp * self.rhop
+
+ self.postprocessing_dataframe.loc[i, 'number_of_particles'] = number_of_particles
+ self.postprocessing_dataframe.loc[i, 'mass'] = mass
+
+ self.postprocessing_dataframe.loc[i, 'mass_flow_rate'] = (mass / self.time_list[i])
+
+ print("Solid mass flow rate computing done.")
+
+ def calculate_angle_of_repose(self, x_min, x_max, f=1,
+ ignore_particles=False,
+ top=True, start_time=0.0):
+
+ def get_bands(x_min, x_max, size):
+ nx = round((x_max - x_min) / size)
+ bounds = np.linspace(x_min, x_max, nx + 1)
+ bands = [(bounds[i], bounds[i + 1]) for i in range(len(bounds) - 1)]
+ return bands
+
+ for i, df in enumerate(self.dataframe):
+ t = self.time_list[i]
+ if t < start_time:
+ if top:
+ if x_min > 0.0:
+ self.postprocessing_dataframe.loc[i, 'right_angle_top'] = 0.0
+ self.postprocessing_dataframe.loc[i, 'right_angle_top_rsqrt'] = 0.0
+ else:
+ self.postprocessing_dataframe.loc[i, 'left_angle_top'] = 0.0
+ self.postprocessing_dataframe.loc[i, 'left_angle_top_rsqrt'] = 0.0
+ else:
+ if x_min > 0.0:
+ self.postprocessing_dataframe.loc[i, 'right_angle_bottom'] = 0.0
+ self.postprocessing_dataframe.loc[i, 'right_angle_bottom_rsqrt'] = 0.0
+ else:
+ self.postprocessing_dataframe.loc[i, 'left_angle_bottom'] = 0.0
+ self.postprocessing_dataframe.loc[i, 'left_angle_bottom_rsqrt'] = 0.0
+ continue
+
+
+ bands = get_bands(x_min, x_max, f * self.dp)
+
+ # Get the particles in each band
+ particles_in_band = []
+ highest_particles = pd.DataFrame(columns=['x', 'y', 'z'])
+
+ # Get the IDs and the positions of the particles
+ positions_x = df[self.direction].values
+ positions_y = df[self.norm].values
+ if top:
+ particle_list = df[(positions_x > x_min) &
+ (positions_x < x_max) &
+ (positions_y > self.H)]
+ else:
+ particle_list = df[(positions_x > x_min) &
+ (positions_x < x_max) &
+ (positions_y < self.H)]
+
+ for band in bands:
+ # Get the bool vector of particles in the band and extract them
+ bool_vect = (band[0] <= particle_list[self.direction_str]) & (particle_list[self.direction_str] < band[1])
+ if any(bool_vect):
+ particles_in_band.append(particle_list[bool_vect])
+
+ # Get the highest particle in axis for each band
+ if ignore_particles is False:
+ max_particles = [particles[particles[self.norm] == particles[self.norm].max()] for particles in particles_in_band]
+ if len(max_particles) > 0:
+ highest_particles = pd.concat(max_particles, axis=0, ignore_index=True)
+ else:
+ continue
+ else:
+ max_particles = []
+ for particles in particles_in_band:
+ particles = particles.sort_values(by=[f'{self.norm}'], ascending=False)
+
+ if len(particles) > 1:
+ if (particles.iloc[0][self.norm_str] - particles.iloc[1][self.norm_str]) < 1.01 * self.dp:
+ max_particles.append(particles.iloc[[0]])
+ else:
+ max_particles.append(particles.iloc[[1]])
+ else:
+ if len(particles) == 0:
+ continue
+ else:
+ max_particles.append(particles.iloc[[0]])
+
+ highest_particles = pd.concat(max_particles, axis=0, ignore_index=True)
+
+ # Get the coordinates of the highest particles
+ particle_x = highest_particles[self.direction_str]
+ particle_y = highest_particles[self.norm]
+
+ self.particle_x = particle_x
+ self.particle_y = particle_y
+
+ a, b, r_value = self.linear_regression(particle_x, particle_y)
+ angle = np.arctan(a) * 180 / np.pi
+ y_model = a * particle_x + b
+ self.model = y_model
+ R_sq = r_value ** 2
+
+ if top:
+ self.particles_y_top[i] = pd.concat([self.particles_y_top[i], particle_y])
+ self.particles_x_top[i] = pd.concat([self.particles_x_top[i], particle_x])
+ if x_min > 0.0:
+ self.postprocessing_dataframe.loc[i, 'right_angle_top'] = angle
+ self.postprocessing_dataframe.loc[i, 'right_angle_top_rsqrt'] = R_sq
+ else:
+ self.postprocessing_dataframe.loc[i, 'left_angle_top'] = angle
+ self.postprocessing_dataframe.loc[i, 'left_angle_top_rsqrt'] = R_sq
+ else:
+ self.particles_y_bottom[i] = pd.concat([self.particles_y_bottom[i], particle_y])
+ self.particles_x_bottom[i] = pd.concat([self.particles_x_bottom[i], particle_x])
+ if x_min > 0.0:
+ self.postprocessing_dataframe.loc[i, 'right_angle_bottom'] = angle
+ self.postprocessing_dataframe.loc[i, 'right_angle_bottom_rsqrt'] = R_sq
+ else:
+ self.postprocessing_dataframe.loc[i, 'left_angle_bottom'] = angle
+ self.postprocessing_dataframe.loc[i, 'left_angle_bottom_rsqrt'] = R_sq
+
+ print("Angle of repose computing done.")
+
+
+ def calculate_angle_from_symmetry(self, top, start):
+ if top:
+ for i in range(len(self.dataframe)):
+ t = self.time_list[i]
+ if t < start:
+ self.postprocessing_dataframe.loc[i, 'angle_top'] = 0.0
+ self.postprocessing_dataframe.loc[i, 'angle_top_rsqrt'] = 0.0
+ continue
+
+ particle_x = abs(self.particles_x_top[i])
+ particle_y = self.particles_y_top[i]
+
+ a, b, r_value = self.linear_regression(particle_x, particle_y)
+ angle = np.arctan(a) * 180 / np.pi
+ R_sq = r_value ** 2
+
+ self.postprocessing_dataframe.loc[i, 'angle_top'] = angle
+ self.postprocessing_dataframe.loc[i, 'angle_top_rsqrt'] = R_sq
+ else:
+ for i in range(len(self.dataframe)):
+ t = self.time_list[i]
+ if t < start:
+ self.postprocessing_dataframe.loc[i, 'angle_bottom'] = 0.0
+ self.postprocessing_dataframe.loc[i, 'angle_bottom_rsqrt'] = 0.0
+ continue
+
+ particle_x = abs(self.particles_x_bottom[i])
+ particle_y = self.particles_y_bottom[i]
+
+ a, b, r_value = self.linear_regression(particle_x, particle_y)
+ angle = np.arctan(a) * 180 / np.pi
+ R_sq = r_value ** 2
+
+ self.postprocessing_dataframe.loc[i, 'angle_bottom'] = angle
+ self.postprocessing_dataframe.loc[i, 'angle_bottom_rsqrt'] = R_sq
+
+
+ def linear_regression(self, x, y):
+ slope, intercept, r_value, p_value, std_err = sc.stats.linregress(x, y)
+ return slope, intercept, r_value
+
+
+ def theoretical_angle_of_repose(self):
+ dp = self.dp * 1000 # mm
+ mu_f_pp = self.prm_dict['friction coefficient particles']
+ mu_f_pw = self.prm_dict['friction coefficient wall']
+ mu_r_pp = self.prm_dict['rolling friction particles'] * dp
+ mu_r_pw = self.prm_dict['rolling friction wall'] * dp
+
+ self.theoritical_angle = (68.61 * mu_f_pp ** 0.27 * mu_f_pw ** 0.22 *
+ mu_r_pp ** 0.06 * mu_r_pw ** 0.12 * dp ** -0.2)
+ print(self.theoritical_angle)
+
+ print(f"Theoretical angle of repose computing done.")
diff --git a/examples/dem/3d-plate-discharge/run-data-simulations.sh b/examples/dem/3d-plate-discharge/run-data-simulations.sh
new file mode 100644
index 0000000000..7496524de2
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/run-data-simulations.sh
@@ -0,0 +1,9 @@
+simulations=("base" "asc" "lb" "asc-lb")
+
+cd data/
+
+for sim in "${simulations[@]}"
+do
+ echo "Running the $sim simulation"
+ time mpirun -np 8 lethe-particles plate-discharge_$sim.prm | tee log2_$sim.out
+done
diff --git a/examples/dem/3d-plate-discharge/run-performance-simulations.sh b/examples/dem/3d-plate-discharge/run-performance-simulations.sh
new file mode 100644
index 0000000000..f56a3b6533
--- /dev/null
+++ b/examples/dem/3d-plate-discharge/run-performance-simulations.sh
@@ -0,0 +1,9 @@
+simulations=("base" "asc" "lb" "asc-lb")
+
+cd performance/
+
+for sim in "${simulations[@]}"
+do
+ echo "Running the $sim simulation"
+ time mpirun -np 8 lethe-particles plate-discharge_$sim.prm | tee log_$sim.out
+done