Skip to content

Commit 76cbbb2

Browse files
author
LasNikas
committed
second part
1 parent 185f9af commit 76cbbb2

3 files changed

Lines changed: 132 additions & 1 deletion

File tree

docs/literate/src/tut_packing.jl

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,124 @@ plot!(p5, Plots.Shape, geometry, linestyle=:dash, label=nothing, axis=false, gri
118118
# As shown in the plot, the interface of the geometry surface is not well resolved yet.
119119
# In other words, there is no body-fitted configuration.
120120
# This is where the particle packing will come into play.
121+
# If we want to assign the mass of each sampled partcle consistently with its density,
122+
# we can adjust it as follows:
123+
shape_sampled.mass .= density * TrixiParticles.volume(geometry) / nparticles(shape_sampled)
124+
125+
# # Particle packing
126+
127+
# In the following, we will essentially follow the same steps described in
128+
# "Setting up your simulation from scratch" (TODO: ref).
129+
# That means we will generate systems that are then passed to the [`Semidiscretization`](@ref Semidiscretization).
130+
# The difference from a typical physical simulation is that we use [`ParticlePackingSystem`](@ref ParticlePackingSystem),
131+
# which does not represent any physical law. Instead, we only use the simulation framework to time-integrate
132+
# the packing process.
133+
134+
# We first need to import [OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl).
135+
using OrdinaryDiffEq
136+
137+
# Next, we set a background pressure. This can be chosen arbitrarily.
138+
# A higher value results in smaller time steps, but the final packed state
139+
# will remain the same after running the same number of steps.
140+
background_pressure = 1.0
141+
142+
# For particle interaction, we select a [smoothing kernel](@ref smoothing_kernel)
143+
# with a suitable smoothing length. Empirically, a factor of `0.8` times the
144+
# particle spacing gives good results [neher2025robustefficientpreprocessingtechniques](@cite).
145+
smoothing_kernel = SchoenbergQuinticSplineKernel{ndims(geometry)}()
146+
smoothing_length = 0.8 * particle_spacing
147+
148+
# Now we can create the packing system. For learning purposes, let’s first try
149+
# passing no signed distance field (SDF) and see what happens.
150+
packing_system = ParticlePackingSystem(shape_sampled;
151+
smoothing_kernel=smoothing_kernel,
152+
smoothing_length=smoothing_length,
153+
signed_distance_field=nothing, background_pressure)
154+
155+
# We now proceed with the familiar steps
156+
# "Semidiscretization" and "Time integration" from the tutorial (TODO: ref)
157+
semi = Semidiscretization(packing_system)
158+
159+
## Use a high `tspan` to guarantee that the simulation runs for at least `maxiters`
160+
tspan = (0, 10000.0)
161+
ode = semidiscretize(semi, tspan)
162+
163+
maxiters = 100
164+
callbacks = CallbackSet(UpdateCallback())
165+
time_integrator = RDPK3SpFSAL35()
166+
167+
sol = solve(ode, time_integrator;
168+
abstol=1e-7, reltol=1e-4, save_everystep=false, maxiters=maxiters,
169+
callback=callbacks)
170+
171+
packed_ic = InitialCondition(sol, packing_system, semi)
172+
173+
p_not_constrained = plot(packed_ic, xlims=my_xlims, ylims=my_ylims)
174+
plot!(p_not_constrained, Plots.Shape, geometry, xlims=my_xlims, ylims=my_ylims)
175+
176+
# As we can see in the plot, the particles are not constrained to the
177+
# geometric surface.
178+
179+
# We therefore add an SDF for the geometry and repeat the same procedure.
180+
packing_system = ParticlePackingSystem(shape_sampled;
181+
smoothing_kernel=smoothing_kernel,
182+
smoothing_length=smoothing_length,
183+
signed_distance_field,
184+
background_pressure)
185+
186+
# Again, we follow the same steps for semidiscretization and time integration.
187+
semi = Semidiscretization(packing_system)
188+
189+
tspan = (0, 10000.0)
190+
ode = semidiscretize(semi, tspan)
191+
192+
maxiters = 1000
193+
callbacks = CallbackSet(UpdateCallback())
194+
time_integrator = RDPK3SpFSAL35()
195+
196+
sol = solve(ode, time_integrator;
197+
abstol=1e-7, reltol=1e-4, save_everystep=false, maxiters=maxiters,
198+
callback=callbacks)
199+
200+
packed_ic = InitialCondition(sol, packing_system, semi)
201+
202+
p = plot(packed_ic, xlims=my_xlims, ylims=my_ylims)
203+
plot!(p, Plots.Shape, geometry, xlims=my_xlims, ylims=my_ylims)
204+
205+
# We can see that the particles now stay inside the geometry,
206+
# but their distribution on the boundary is still not optimal.
207+
# Therefore, we set up a dedicated boundary packing system.
208+
# To do this, we set `is_boundary = true`.
209+
# For highly convex geometries, it is useful to slightly compress the
210+
# boundary layer thickness. The background for this is that the true
211+
# geometric volume is often larger than what was assumed when sampling
212+
# the boundary particles, because the mass was computed assuming an
213+
# interior particle volume.
214+
# A `boundary_compress_factor` of `0.8` or `0.9` works well for most shapes.
215+
# Since we have a relatively large particle spacing compared to the
216+
# geometry size in this example, we will choose `0.7`.
217+
boundary_system = ParticlePackingSystem(boundary_sampled;
218+
is_boundary=true,
219+
smoothing_kernel=smoothing_kernel,
220+
smoothing_length=smoothing_length,
221+
boundary_compress_factor=0.7,
222+
signed_distance_field, background_pressure)
223+
224+
# We can now couple the boundary system with the interior system:
225+
semi = Semidiscretization(packing_system, boundary_system)
226+
227+
tspan = (0, 10000.0)
228+
ode = semidiscretize(semi, tspan)
229+
230+
maxiters = 1000
231+
callbacks = CallbackSet(UpdateCallback())
232+
time_integrator = RDPK3SpFSAL35()
233+
234+
sol = solve(ode, time_integrator;
235+
abstol=1e-7, reltol=1e-4, save_everystep=false, maxiters=maxiters,
236+
callback=callbacks)
237+
238+
packed_ic = InitialCondition(sol, packing_system, semi)
239+
packed_boundary_ic = InitialCondition(sol, boundary_system, semi)
240+
p_final = plot(packed_ic, packed_boundary_ic, xlims=my_xlims, ylims=my_ylims)
241+
plot(p_final, Plots.Shape, geometry, xlims=my_xlims, ylims=my_ylims, linestyle=:dash)

docs/src/preprocessing/preprocessing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ Pages = [joinpath("preprocessing", "geometries", "io.jl")]
302302
```
303303

304304
# [Particle Packing](@id particle_packing)
305-
To obtain a body-fitted and isotropic particle distribution, an initial configuration (see [Sampling of Geometries](@ref sampling_of_geometries)) is first generated. This configuration is then packed using a [`ParticlePackingSystem`](@ref).
305+
To obtain a body-fitted and isotropic particle distribution, an initial configuration (see [Sampling of Geometries](@ref sampling_of_geometries)) is first generated. This configuration is then packed using a [`ParticlePackingSystem`](@ref) following the steps introduced in [neher2025robustefficientpreprocessingtechniques](@cite).
306306
The preprocessing pipeline consists of the following steps:
307307

308308
- Load geometry: Fig. 1, [`load_geometry`](@ref).

docs/src/refs.bib

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,16 @@ @Article{Negi2020
561561
publisher = {Elsevier BV},
562562
}
563563

564+
@misc{neher2025robustefficientpreprocessingtechniques,
565+
title={Robust and efficient pre-processing techniques for particle-based methods including dynamic boundary generation},
566+
author={Niklas S. Neher and Erik Faulhaber and Sven Berger and Christian Weißenfels and Gregor J. Gassner and Michael Schlottke-Lakemper},
567+
year={2025},
568+
eprint={2506.21206},
569+
archivePrefix={arXiv},
570+
primaryClass={math.NA},
571+
url={https://arxiv.org/abs/2506.21206},
572+
}
573+
564574
@Article{O’Connor2021,
565575
author = {O’Connor, Joseph and Rogers, Benedict D.},
566576
journal = {Journal of Fluids and Structures},

0 commit comments

Comments
 (0)