diff --git a/docs/literate/src/tut_2d_geometry.jl b/docs/literate/src/tut_2d_geometry.jl new file mode 100644 index 0000000000..d591d0f6d7 --- /dev/null +++ b/docs/literate/src/tut_2d_geometry.jl @@ -0,0 +1,155 @@ +# # [Setting up a 2D simulation from geometry files](@id tut_2d_geometry) + +# In this tutorial, we build two genuine 2D setups from geometry files: +# 1. a curved pipe, where one geometry file defines the outer wall envelope and a second +# one defines the empty channel cut out of it, +# 2. a dam-break basin with a coastline profile, where one geometry file defines the +# filled coastline wall together with the seawall on the right. +# +# For a real 2D setup, we use 2D geometry formats such as `.asc` or `.dxf`. +# STL files are surface meshes and therefore naturally lead to thin 3D setups instead. + +# First, we import TrixiParticles.jl together with +# [OrdinaryDiffEq.jl](https://github.com/SciML/OrdinaryDiffEq.jl) +# and [Plots.jl](https://docs.juliaplots.org/stable/). +using TrixiParticles +using OrdinaryDiffEq +using Plots + +# ## Resolution + +# We use the same particle spacing for the fluid and for the wall geometries. +particle_spacing = 0.03 +fluid_density = 1000.0 +gravity = 9.81 +sound_speed = 10.0 +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=7) +nothing # hide + +# ## Loading 2D geometry files + +# The following helper loads a closed 2D geometry file and samples particles in its interior: +# 1. load the polygon with [`load_geometry`](@ref), +# 2. fill the polygon with [`ComplexShape`](@ref). +# +# This creates a true 2D solid region instead of a hollow shell around the polygon edges. +function solid_from_geometry_file(file; particle_spacing, density) + geometry = load_geometry(file) + solid = ComplexShape(geometry; particle_spacing, density, + grid_offset=0.5particle_spacing) + + return (; geometry, solid) +end + +# ## A curved pipe from two filled geometries + +# The pipe wall is a solid L-shaped region with a channel cut out of it: +# 1. one geometry file describes the outer pipe envelope, +# 2. one geometry file describes the empty channel, +# 3. the `setdiff` operation subtracts the channel from the solid envelope. +pipe_outer_file = pkgdir(TrixiParticles, "examples", "preprocessing", "data", + "curved_pipe_outer_2d.asc") +pipe_channel_file = pkgdir(TrixiParticles, "examples", "preprocessing", "data", + "curved_pipe_channel_2d.asc") + +pipe_outer = solid_from_geometry_file(pipe_outer_file; particle_spacing, + density=fluid_density) +pipe_channel = load_geometry(pipe_channel_file) + +pipe_setup = (; wall=setdiff(pipe_outer.solid, pipe_channel), + outer_geometry=pipe_outer.geometry, + channel_geometry=pipe_channel) + +# ## A dam-break basin with a coastline profile + +# In the second setup, a single 2D geometry file defines a filled coastline wall: +# the beach profile on top, a finite wall thickness below it, and the seawall on the right. +coast_file = pkgdir(TrixiParticles, "examples", "preprocessing", "data", + "coastline_profile_2d.asc") +coast = solid_from_geometry_file(coast_file; particle_spacing, density=fluid_density) + +# The geometry file gives the coastline bed and the right wall as a solid region. +# We add the left wall explicitly as a rectangular particle block and place a +# 1.5x taller rectangular dam-break water column next to it. +left_wall = RectangularShape(particle_spacing, (5, 50), (0.0, -0.12), + density=fluid_density) +reservoir = RectangularShape(particle_spacing, (28, 42), (0.15, 0.03), + acceleration=(0.0, -gravity), + state_equation=state_equation) +coast_setup = (; geometry=coast.geometry, + wall=union(coast.solid, left_wall), + fluid=setdiff(reservoir, coast.geometry)) + +p_pipe = plot(pipe_setup.wall, label="wall", title="Curved pipe", + markerstrokewidth=0, markersize=4) +plot!(p_pipe, showaxis=false, aspect_ratio=:equal, + xlims=(-0.03, 1.23), ylims=(-0.03, 1.23)) + +p_coast = plot(coast_setup.fluid, coast_setup.wall, + labels=["fluid" "wall"], title="Coastline dam break", + markerstrokewidth=0, markersize=3) +plot!(p_coast, showaxis=false, aspect_ratio=:equal, + xlims=(0.0, 2.75), ylims=(-0.15, 1.35)) + +plot(p_pipe, p_coast, layout=(1, 2), size=(900, 360)) +savefig("tut_2d_geometry_plot.png"); # hide +# ![2D geometry based initial conditions](tut_2d_geometry_plot.png) + +# ## Building the simulation systems + +# To keep the example focused, we continue with the coastline setup. +# From this point on, the simulation setup is the same as in other 2D simulation files. +setup = coast_setup +tspan = (0.0, 0.03) +nothing # hide + +# We define the state equation, smoothing kernel, and viscosity for a +# weakly compressible SPH simulation. +smoothing_length = 1.2 * particle_spacing +smoothing_kernel = SchoenbergCubicSplineKernel{2}() +viscosity = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) + +fluid_density_calculator = ContinuityDensity() +density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) + +fluid_system = WeaklyCompressibleSPHSystem(setup.fluid, fluid_density_calculator, + state_equation, smoothing_kernel, + smoothing_length, viscosity=viscosity, + density_diffusion=density_diffusion, + acceleration=(0.0, -gravity)) +nothing # hide + +# For the wall, we reuse the combined solid wall particles created above. +boundary_model = BoundaryModelDummyParticles(setup.wall.density, setup.wall.mass, + state_equation=state_equation, + AdamiPressureExtrapolation(), + smoothing_kernel, smoothing_length) +boundary_system = WallBoundarySystem(setup.wall, boundary_model) +nothing # hide + +# ## Semidiscretization + +# With fluid and wall particles defined, we can build the +# [`Semidiscretization`](@ref TrixiParticles.Semidiscretization) exactly as in other tutorials. +semi = Semidiscretization(fluid_system, boundary_system) +ode = semidiscretize(semi, tspan) +nothing # hide + +# ## Time integration + +# The setup is now complete. +# To start the simulation, run for example +# ```julia +# callbacks = CallbackSet(InfoCallback(interval=10)) +# sol = solve(ode, RDPK3SpFSAL35(), save_everystep=false, callback=callbacks) +# ``` +# This is the same final step as in [the basic setup tutorial](@ref tut_setup). +callbacks = CallbackSet(InfoCallback(interval=10)) +nothing # hide + +sol = solve(ode, RDPK3SpFSAL35(), save_everystep=false, callback=callbacks) #!md + +# For more accurate body-fitted particles around sharper features, you can also +# apply the [particle packing workflow](@ref tut_packing) to the 2D geometry files +# before starting the simulation. diff --git a/docs/make.jl b/docs/make.jl index ddffab849a..4ac1ae3709 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -40,6 +40,8 @@ Literate.markdown(joinpath("docs", "literate", "src", "tut_custom_kernel.jl"), joinpath("docs", "src", "tutorials")) Literate.markdown(joinpath("docs", "literate", "src", "tut_packing.jl"), joinpath("docs", "src", "tutorials")) +Literate.markdown(joinpath("docs", "literate", "src", "tut_2d_geometry.jl"), + joinpath("docs", "src", "tutorials")) copy_file("AUTHORS.md", "in the [LICENSE.md](LICENSE.md) file" => "under [License](@ref)") @@ -87,6 +89,8 @@ makedocs(sitename="TrixiParticles.jl", "tut_custom_kernel.md") ], "Preprocessing" => [ + "Setting up a 2D simulation from geometry files" => joinpath("tutorials", + "tut_2d_geometry.md"), "Particle packing tutorial" => joinpath("tutorials", "tut_packing.md") ] diff --git a/docs/src/tutorial.md b/docs/src/tutorial.md index 1166010291..b62dbb642c 100644 --- a/docs/src/tutorial.md +++ b/docs/src/tutorial.md @@ -1,8 +1,68 @@ # Tutorials -## General -- [Setting up your simulation from scratch](tutorials/tut_setup.md) -- [Modifying or extending components of TrixiParticles.jl within a simulation file](tutorials/tut_custom_kernel.md) +Choose a tutorial based on the task in front of you. -## Preprocessing -- [Particle packing tutorial](tutorials/tut_packing.md) +> New to TrixiParticles.jl? Start with [Setting up your simulation from scratch](tutorials/tut_setup.md). + +## Recommended Path + +1. [Setting up your simulation from scratch](tutorials/tut_setup.md): learn the structure of a simulation file and run a complete WCSPH example. +2. [Modifying or extending components of TrixiParticles.jl within a simulation file](tutorials/tut_custom_kernel.md): replace selected parts of an existing setup without cloning the package. +3. [Setting up a 2D simulation from geometry files](tutorials/tut_2d_geometry.md): load 2D geometry files, turn them into filled wall regions, and combine them with standard 2D fluid blocks. +4. [Particle packing tutorial](tutorials/tut_packing.md): build a body-fitted particle configuration for complex geometries. + +## Tutorials + +### [Setting up your simulation from scratch](tutorials/tut_setup.md) + +```@raw html +Rectangular tank setup used in the first tutorial +``` + +Build a complete weakly compressible SPH dam break setup from particle spacing through semidiscretization, callbacks, and time integration. + +- Focus: initial conditions, systems, semidiscretization, callbacks +- Choose this if: you want the full workflow from a minimal example + +### [Modifying or extending components of TrixiParticles.jl within a simulation file](tutorials/tut_custom_kernel.md) + +```@raw html +Kernel comparison plot from the custom kernel tutorial +``` + +Start from an existing simulation and replace pieces such as the smoothing kernel directly in the file you run. + +- Focus: `trixi_include`, custom kernels, rapid iteration +- Choose this if: you want to prototype changes without rewriting a full setup + +### [Setting up a 2D simulation from geometry files](tutorials/tut_2d_geometry.md) + +```@raw html +2D pipe and coastline geometries converted to wall and fluid particles +``` + +Load 2D geometry files, fill them with particles using `ComplexShape`, and build genuine 2D setups such as a curved pipe and a coastline dam break. + +- Focus: `load_geometry`, `ComplexShape`, `setdiff`, 2D `Polygon`s +- Choose this if: you want a true 2D setup from line-based geometry data + +### [Particle packing tutorial](tutorials/tut_packing.md) + +```@raw html +Packed particle configuration for a complex geometry +``` + +Go from a geometry file to a packed particle distribution using signed distance fields together with boundary and interior sampling. + +- Focus: geometry import, signed distance fields, boundary sampling, `ParticlePackingSystem` +- Choose this if: you need body-fitted particles for complex shapes + +See also [Getting started](getting_started.md) and [Examples](examples.md). diff --git a/examples/preprocessing/data/coastline_profile_2d.asc b/examples/preprocessing/data/coastline_profile_2d.asc new file mode 100644 index 0000000000..7986e07261 --- /dev/null +++ b/examples/preprocessing/data/coastline_profile_2d.asc @@ -0,0 +1,20 @@ +# ASCII +0.18 -0.12 0 +2.68 -0.12 0 +2.68 1.08 0 +2.62 0.66 0 +2.53 0.52 0 +2.42 0.40 0 +2.30 0.42 0 +2.18 0.33 0 +2.05 0.24 0 +1.92 0.26 0 +1.78 0.18 0 +1.62 0.11 0 +1.46 0.14 0 +1.28 0.06 0 +1.05 0.02 0 +0.82 0.05 0 +0.55 0.03 0 +0.18 0.03 0 +0.18 -0.12 0 diff --git a/examples/preprocessing/data/curved_pipe_channel_2d.asc b/examples/preprocessing/data/curved_pipe_channel_2d.asc new file mode 100644 index 0000000000..76a1d7b8fe --- /dev/null +++ b/examples/preprocessing/data/curved_pipe_channel_2d.asc @@ -0,0 +1,20 @@ +# ASCII +0.00 0.12 0 +0.60 0.12 0 +0.72423 0.13646 0 +0.84000 0.18431 0 +0.93941 0.26059 0 +1.01569 0.36000 0 +1.06354 0.47577 0 +1.08 0.60 0 +1.08 1.20 0 +0.72 1.20 0 +0.72 0.60 0 +0.71591 0.56894 0 +0.70392 0.54000 0 +0.68485 0.51515 0 +0.66000 0.49608 0 +0.63106 0.48409 0 +0.60 0.48 0 +0.00 0.48 0 +0.00 0.12 0 diff --git a/examples/preprocessing/data/curved_pipe_outer_2d.asc b/examples/preprocessing/data/curved_pipe_outer_2d.asc new file mode 100644 index 0000000000..1b26afac92 --- /dev/null +++ b/examples/preprocessing/data/curved_pipe_outer_2d.asc @@ -0,0 +1,14 @@ +# ASCII +0.00 0.00 0 +0.60 0.00 0 +0.75529 0.02044 0 +0.90000 0.08038 0 +1.02426 0.17574 0 +1.11962 0.30000 0 +1.17956 0.44471 0 +1.20 0.60 0 +1.20 1.20 0 +0.60 1.20 0 +0.60 0.60 0 +0.00 0.60 0 +0.00 0.00 0