Skip to content

Commit

Permalink
Adding DryGranular
Browse files Browse the repository at this point in the history
  • Loading branch information
wallytutor committed Mar 30, 2024
1 parent 4b26e15 commit 9dbce9f
Show file tree
Hide file tree
Showing 8 changed files with 977 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

julia_version = "1.10.1"
manifest_format = "2.0"
project_hash = "63593c2b584fbc32a0e5777ac3429c279bcc4c17"
project_hash = "aa40df391343477c67256f2b12be80ad030257dd"

[[deps.ADTypes]]
git-tree-sha1 = "016833eb52ba2d6bea9fcb50ca295980e728ee24"
Expand Down
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ authors = ["Walter Dal'Maz Silva <[email protected]>"]
version = "0.1.0"

[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Expand Down
2 changes: 1 addition & 1 deletion docs/Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

julia_version = "1.10.1"
manifest_format = "2.0"
project_hash = "ae56c51e2accfbdfa2d99de62643dc58ae6de2bf"
project_hash = "86498443abd203753402feebd36f4237b78f6ae6"

[[deps.ADTypes]]
git-tree-sha1 = "016833eb52ba2d6bea9fcb50ca295980e728ee24"
Expand Down
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[deps]
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Expand Down
3 changes: 3 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ using WallyToolbox
using DryAbstract
using DryConstants
using DryFlowsheet
using DryGranular
using DryMaterials
using DryUtilities
using OpenFOAM
Expand All @@ -40,6 +41,7 @@ modules = [
DryAbstract,
DryConstants,
DryFlowsheet,
DryGranular,
DryMaterials,
DryUtilities,
OpenFOAM,
Expand Down Expand Up @@ -73,6 +75,7 @@ pages = [

"Dry Packages" => [
"DryFlowsheet" => "DryFlowsheet/index.md",
"DryGranular" => "DryGranular/index.md",
"DryMaterials" => "DryMaterials/index.md",
"Helpers" => "helpers.md"
],
Expand Down
220 changes: 220 additions & 0 deletions docs/src/DryGranular/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# DryGranular

```@meta
CurrentModule = DryGranular
DocTestSetup = quote
using Statistics
using DryGranular
end
```

## General porous media

Modeling of geometrical characteristics of porous beds is required for including
both their thermal effect or role over chemistry in chemical reactors. A
classical approach used in several commercial and open source tools is that of
Gunn [Gunn1978](@cite). In what follows we develop the ideas that lead to an
analogous model which is implemented by this structure.

To build the model we will assume a reactor of constant rectangular
cross-section ``{A}_{r}={b}{w}`` and volume ``{V}_{R}={b}{w}{h}``. Its
cross-section perimeter is then ``{P}_{R}=2({b}+{w})``. Inside this reactor we
randomly pack cubic particles ``\beta`` of surface area
``{A}_{\beta}=6{l}_{\beta}^2`` and volume ``{V}_{\beta}={l}_{\beta}^3`` at a
porosity level ``{\phi}``. Thus the total volume of solids inside the reactor is
``{V}_{S}=(1-{\phi}){V}_{R}`` and the approximate number of particles
``{N}=\frac{{V}_{S}}{{V}_{\beta}}``. Following a similar reasoning the total
surface area of particles is ``{A}_{S}={N}{A}_{\beta}``. Performing all the
substitutions so far one finds the following expression

```math
{A}_{S}=\frac{6(1-{\phi}){b}{w}{h}}{{l}_{\beta}}
```

Since the differential ``d{A}={P}d{l}`` holds for the surface of a body over its
length ``{l}``, one can divide the above expression by the reactor length to get
the perimeter of particles in a cross-section. We can further divide by the
cross-section area itself and find the *perimeter density* which is a more
general result, and find the expression proposed by Gunn [Gunn1978](@cite). This
result is summarized in the next equation where the subscript of particle size
was dropped for generality.

```math
{P} = \frac{6(1-{\phi})}{{l}}
```

An estimator of the number of channels per unit cross-section of reactor ``{N}``
can be related to the porosity through ``{N}\pi{R}^2={\phi}``. Because the above
perimeter is shared between the fluid volume and solids, it holds that
``{N}2\pi{R}=P``. Using these expressions one can solve for the porosity
channels characteristic *radius* ``{R}`` as given below, which is also a result
reported by Gunn [Gunn1978](@cite).

```math
{R}=\frac{{\phi}{l}}{3(1-{\phi})}
```

This model is probided in [`PackedBedPorosityDescriptor`](@ref).

```@docs
DryGranular.PackedBedPorosityDescriptor
```

[`PackedBedPorosityDescriptor`](@ref) can be used to describe the geometry of
exchange section of a packed bed for a single set of arguments.

```jldoctest
julia> PackedBedPorosityDescriptor(; ϕ = 0.65, l = 0.10, area = 1.0)
PackedBedPorosityDescriptor(P = 21.000000 m, D = 0.123810 m)
```

It can also be used to describe randomly varying reactors, what is a more
realistic thing to do when using this structure to simulate real world systems.

```jldoctest
julia> PackedBedPorosityDescriptor(;
ϕ = 0.65, l = 0.10,
σϕ = 0.03, σl = 0.01,
N = 2,
ϕlims = (0.4, 0.8),
llims = (0.0, 0.3),
seed = 42,
area = 1.0
)
PackedBedPorosityDescriptor(
P from 21.455749 m to 24.370742 m
D from 0.125589 m to 0.102353 m
)
```

## Rotary kiln models

In a rotary kiln as proposed by Kramers and Croockewite (1952)
[Kramers1952](@cite). Its goal is to be used as a process support tool or to
integrate more complex models requiring integration of the bed profile. In its
classical statement, the bed height profile ``h(z)`` can be evaluated from
*volume* of flowing material conservation through the following equations.
Coordinate ``z=0`` represents the discharge position where initial condition
must be applied. This is given by the dam height, if any, or particle size.

```math
\begin{aligned}
\dfrac{dh}{dz} &= C₁ \left[\frac{h}{R}\left(2 - \frac{h}{R}\right)\right]^{-\frac{3}{2}} - C₂\\[6pt]
C₁ &= \frac{3}{4}\dfrac{Φ\tan{γ}}{π R^3 ω}\\[6pt]
C₂ &= \dfrac{\tan{β}}{\cos{γ}}
\end{aligned}
```

The structure [`SymbolicLinearKramersModel`](@ref) implements the Kramers'
ordinary differential equation for prediction of bed height profile in a rotary
kiln. This equation is implemented under the formalism of `ModelingToolkit`.

```@docs
DryGranular.SymbolicLinearKramersModel
```


For integration of this model we implement [`RotaryKilnBedSolution`](@ref). It
provides the solved description of a rotary kiln bed geometry computed from the
solution of bed height along the kiln length. The main goal of the quantities
computed here is their use with heat and mass transfer models for the simulation
of rotary kiln process. A simple post-processing utilitiy
[`plotlinearkramersmodel`](@ref) is also provided.

```@docs
DryGranular.RotaryKilnBedSolution
DryGranular.plotlinearkramersmodel
```

Data in next example is an SI conversion of an example from Kramers and
Croockewite (1952) [Kramers1952](@cite).

```jldoctest
julia> L = 13.715999999999998; # Kiln length [m]
julia> D = 1.8897599999999999; # Kiln diameter [m]
julia> β = 2.3859440303888126; # Kiln slope [°]
julia> γ = 45.0; # Repose angle [°]
julia> d = 1.0; # Particle/dam size [mm]
julia> Φ = 10.363965852671996; # Feed rate [m³/h]
julia> ω = 3.0300000000000002; # Rotation rate [rev/min]
julia> bed = RotaryKilnBedSolution(;
model = SymbolicLinearKramersModel(),
L = L,
R = D / 2.0,
Φ = Φ / 3600.0,
ω = ω / 60.0,
β = deg2rad(β),
γ = deg2rad(γ),
d = d / 1000.0
);
julia> bed
RotaryKilnBedSolution(τ = 13.169938 min, ηₘ = 5.913271 %)
julia> bed.τ
790.1963002204403
```

In the following dummy example we force a very thick *analytical* bed solution,
filling the radius of the rotary drum.

```jldoctest dummy-1
julia> R = 1.0e+00;
julia> Φ = 1.0e-02;
julia> z = collect(0.0:0.1:10.0);
julia> h = R * ones(size(z));
julia> Aₐ = π * R^2 / 2;
julia> Vₐ = Aₐ * z[end];
julia> bed = RotaryKilnBedSolution(z, h, 0, R, Φ)
RotaryKilnBedSolution(τ = 26.179939 min, ηₘ = 50.000000 %)
```

Next we confirm the *internal* evaluations of the model match the expected *analytical* values.

```jldoctest dummy-1
julia> mean(bed.θ) ≈ π
true
julia> mean(bed.l) ≈ 2R
true
julia> mean(bed.A) ≈ Aₐ
true
julia> mean(bed.η) ≈ 0.5
true
julia> bed.ηₘ ≈ 50.0
true
julia> bed.V ≈ Vₐ
true
julia> bed.τ ≈ Vₐ / Φ
true
```

Validation of Kramers' model is provided [here](kramers.md).

Finally a set of basic equations provided for process analysis.

```@docs
DryGranular.sullivansηₘ
DryGranular.dimlessNΦ
DryGranular.dimlessNₖ
DryGranular.perrayresidence
DryGranular.kramersnlapprox
```
Loading

0 comments on commit 9dbce9f

Please sign in to comment.