From 30a7a92fd170568a5f6255328617bbbbfdd6757c Mon Sep 17 00:00:00 2001 From: Hendrik Ranocha Date: Wed, 27 Mar 2024 10:48:31 +0100 Subject: [PATCH 01/49] Create SECURITY.md (#477) --- SECURITY.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..008cae1aa --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +# Security Policy + +The Trixi.jl development team takes security issues seriously. We appreciate +all efforts to responsibly disclose any security issues and will make every +effort to acknowledge contributions. + + +## Supported Versions + +The current stable release following the interpretation of +[semantic versioning (SemVer)](https://julialang.github.io/Pkg.jl/dev/compatibility/#Version-specifier-format-1) +used in the Julia ecosystem is supported with security updates. + + +## Reporting a Vulnerability + +To report a security issue, please use the GitHub Security Advisory +["Report a Vulnerability"](https://github.com/trixi-framework/TrixiParticles.jl/security/advisories/new) +tab. + +We will send a response indicating the next steps in handling your report. +After the initial reply to your report, we will keep you informed of the +progress towards a fix and full announcement, and may ask for additional +information or guidance. + +Please report security bugs in third-party modules directly to the person +or team maintaining the module. + +Public notifications of vulnerabilities will be shared in community channels +such as Slack. From 4a6e3343937c3af35fac6485450170b40bfb2f02 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Thu, 28 Mar 2024 19:15:42 +0100 Subject: [PATCH 02/49] The grand reopening Part 2: Add overview page (#432) * implement * Revert "implement" This reverts commit 171505a4e078c7271e838c35f98f3df7f4cf77de. * some improvements to the documentation * some more fixes * add install documentation * add more info * update * Update get_started.md * add tutorial * fix * add stubs * update * include the example files * update * update * format * add more info to error message * fix path * add overview page * add diagram * add text * Update overview.md * add development page * refine text * [skip ci] * update * fix * fix * rm get_started * update URL of diagram * fix error * update * update * fix * update * update * rename * rename * update * change to img tags * remove minimal * update images * update * remove old stuff * fix images * fix * make wider * format * format * Update docs/src/development.md Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> * review * rm css * add new picture * update time * move back --------- Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Co-authored-by: Niklas Neher <73897120+LasNikas@users.noreply.github.com> --- docs/make.jl | 6 +- docs/src/development.md | 23 +++++++ docs/src/examples.md | 64 +++++++++++++++++-- docs/src/index.md | 27 +++++++- docs/src/install.md | 2 +- docs/src/overview.md | 23 +++++++ examples/fluid/periodic_channel_2d.jl | 7 +- ...{dam_break_2d.jl => dam_break_plate_2d.jl} | 8 +-- examples/fsi/falling_sphere_2d.jl | 2 +- examples/fsi/falling_sphere_3d.jl | 2 +- examples/fsi/falling_spheres_2d.jl | 2 +- src/general/file_system.jl | 15 ++++- test/examples/examples.jl | 20 +++--- 13 files changed, 170 insertions(+), 31 deletions(-) create mode 100644 docs/src/development.md create mode 100644 docs/src/overview.md rename examples/fsi/{dam_break_2d.jl => dam_break_plate_2d.jl} (97%) diff --git a/docs/make.jl b/docs/make.jl index ac3f26ba9..5fb9ea27b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -92,16 +92,19 @@ DocMeta.setdocmeta!(TrixiParticles, :DocTestSetup, :(using TrixiParticles); recu makedocs(sitename="TrixiParticles.jl", # Run doctests and check docs for the following modules modules=[TrixiParticles], + format=Documenter.HTML(), # Explicitly specify documentation structure pages=[ "Home" => "index.md", "News" => "news.md", "Installation" => "install.md", "Getting started" => "getting_started.md", + "Development" => "development.md", "Tutorial" => "tutorial.md", "Examples" => "examples.md", "Visualization" => "visualization.md", "Components" => [ + "Overview" => "overview.md", "General" => [ "Semidiscretization" => joinpath("general", "semidiscretization.md"), "Initial Condition and Setups" => joinpath("general", @@ -132,5 +135,4 @@ makedocs(sitename="TrixiParticles.jl", ]) deploydocs(repo="github.com/trixi-framework/TrixiParticles.jl", - devbranch="main", - push_preview=true) + devbranch="main", push_preview=true) diff --git a/docs/src/development.md b/docs/src/development.md new file mode 100644 index 000000000..94f65d540 --- /dev/null +++ b/docs/src/development.md @@ -0,0 +1,23 @@ +# [Development](@id development) + + +## Preview of the documentation + +To generate the Documentation, first instantiate the `docs` environment +by executing the following command from the TrixiParticles.jl root directory: +```bash +julia --project=docs -e "using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()" +``` +This command only has to be run once. After that, maintain the `docs` environment +as described under [Installation](@ref installation-issues). + +With an instantiated `docs` environment, generate the docs with the following command (again from the TrixiParticles.jl root directory): +```bash +julia --project=docs --color=yes docs/make.jl +``` +You can then open the generated files in `docs/build` with your webbrowser. +Alternatively, run +```bash +python3 -m http.server -d docs/build +``` +and open `localhost:8000` in your webbrowser. \ No newline at end of file diff --git a/docs/src/examples.md b/docs/src/examples.md index 32107240c..57ac2eb7c 100644 --- a/docs/src/examples.md +++ b/docs/src/examples.md @@ -1,14 +1,68 @@ # Examples - ## Fluid +### Accelerated Tank 2D (`fluid/accelerated_tank_2d.jl`) +```@raw html + +``` -## Structure Mechanics - +### Dam Break 2D (`fluid/dam_break_2d.jl`) +```@raw html + +``` + +### Dam Break 3D (`fluid/dam_break_3d.jl`) +```@raw html + +``` + +### Falling Water Column (`fluid/falling_water_column_2d.jl`) +```@raw html + +``` + +### Hydrostatic Water Column (`fluid/hydrostatic_water_column_*.jl`) +```@raw html + +``` +### Moving Wall (`fluid/moving_wall_2d.jl`) +```@raw html + +``` + +### Oscillating Drop (`fluid/oscillating_drop_2d.jl`) +```@raw html + +``` + +### Periodic Channel (`fluid/periodic_channel_2d.jl`) +```@raw html + +``` + ## Fluid Structure Interaction +### Dam Break with Elastic Plate (`fsi/dam_break_plate_2d.jl`) +```@raw html + +``` + +### Falling Sphere 2D (`fsi/falling_sphere_2d.jl`) +```@raw html + +``` + +### Falling Spheres 2D (`fsi/falling_spheres_2d.jl`) +```@raw html + +``` + +## Structure Mechanics -## Postprocessing - +### Oscillating Beam (`solid/oscillating_beam_2d.jl`) +```@raw html + +``` + \ No newline at end of file diff --git a/docs/src/index.md b/docs/src/index.md index 41930cf37..a3b41a417 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -12,13 +12,36 @@ TrixiParticles.jl is a numerical simulation framework designed for particle-base - VTK ## Examples - +```@raw html + + + + + + + + + +
+
2D Dam Break
+
+
Moving Wall
+
+
Oscillating Beam
+
+
Dam Break with Elastic Plate
+
+``` ## Quickstart 1. [Installation](@ref installation) 2. [Getting started](@ref getting_started) +If you have any questions concerning **TrixiParticles.jl** you can join our community [on Slack](https://join.slack.com/t/trixi-framework/shared_invite/zt-sgkc6ppw-6OXJqZAD5SPjBYqLd8MU~g) or open an issue with your question. ## Start with development +To get started with development have a look at these pages: + 1. [Installation](@ref installation) -2. [Contributing](@ref) +2. [Development](@ref development) +3. [Contributing](@ref) diff --git a/docs/src/install.md b/docs/src/install.md index 09ad164c5..f88a10121 100644 --- a/docs/src/install.md +++ b/docs/src/install.md @@ -45,7 +45,7 @@ and always have a reproducible environment at hand to share with others. - [PythonPlot.jl](https://github.com/JuliaPy/PythonPlot.jl) -- Plotting library that can be used instead of Plots.jl - [ParaView](https://www.paraview.org/) -- Software that can be used for visualization of results -## Common issues +## [Common issues](@id installation-issues) If you followed the [installation instructions for developers](@ref for-developers) and you run into any problems with packages when pulling the latest version of TrixiParticles.jl, diff --git a/docs/src/overview.md b/docs/src/overview.md new file mode 100644 index 000000000..a113d1f9f --- /dev/null +++ b/docs/src/overview.md @@ -0,0 +1,23 @@ +# Overview +The following page gives a rough overview of important parts of the code. + +## Program flow + +To initiate a simulation, the goal is to solve an ordinary differential equation, for example, +by employing the time integration schemes provided by OrdinaryDiffEq.jl. These schemes are then +utilized to integrate ``\mathrm{d}u/\mathrm{d}t`` and ``\mathrm{d}v/\mathrm{d}t``, where ``u`` +represents the particles' positions and ``v`` their properties such as velocity and density. +During a single time step or an intermediate step of the time integration scheme, the functions +`drift!` and `kick!` are invoked, followed by the functions depicted in this diagram +(with key parts highlighted in orange/yellow). + +![Main Program Flow](https://github.com/trixi-framework/TrixiParticles.jl/assets/10238714/d7c6eedd-7173-4879-b62e-3e6d4bc5091f) + + +## Structure +What we refer to as schemes are various models such as Weakly Compressible Smoothed Particle Hydrodynamics (WCSPH) +or Total Lagrangian Smoothed Particle Hydrodynamics (TLSPH). These schemes are categorized based on the applicable +physical regimes, namely fluid, solid, gas, and others. Each scheme comprises at least two files: a `system.jl` file +and an `rhs.jl` file. The `system.jl` file provides the data structure holding the particles of this scheme and some +routines, particularly those for allocation and the main update routines, excluding system interactions. +The interactions between particles of this scheme (and with particles of other schemes) are handled in the `rhs.jl` file. diff --git a/examples/fluid/periodic_channel_2d.jl b/examples/fluid/periodic_channel_2d.jl index cd033717a..55899df44 100644 --- a/examples/fluid/periodic_channel_2d.jl +++ b/examples/fluid/periodic_channel_2d.jl @@ -42,10 +42,15 @@ fluid_system = WeaklyCompressibleSPHSystem(tank.fluid, fluid_density_calculator, # ========================================================================================== # ==== Boundary boundary_density_calculator = AdamiPressureExtrapolation() +viscosity_wall = nothing +# Activate to switch to no-slip walls +#viscosity_wall = ViscosityAdami(nu=0.0025 * smoothing_length * sound_speed / 8) + boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, state_equation=state_equation, boundary_density_calculator, - smoothing_kernel, smoothing_length) + smoothing_kernel, smoothing_length, + viscosity=viscosity_wall) boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) diff --git a/examples/fsi/dam_break_2d.jl b/examples/fsi/dam_break_plate_2d.jl similarity index 97% rename from examples/fsi/dam_break_2d.jl rename to examples/fsi/dam_break_plate_2d.jl index 8126e9759..4db77b44a 100644 --- a/examples/fsi/dam_break_2d.jl +++ b/examples/fsi/dam_break_plate_2d.jl @@ -14,7 +14,7 @@ fluid_particle_spacing = 0.01 n_particles_x = 5 # Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model -boundary_layers = 3 +boundary_layers = 4 spacing_ratio = 1 # ========================================================================================== @@ -29,7 +29,7 @@ tank_size = 4 .* initial_fluid_size fluid_density = 1000.0 sound_speed = 20 * sqrt(gravity * initial_fluid_size[2]) state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, - exponent=7) + exponent=1) tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, n_layers=boundary_layers, spacing_ratio=spacing_ratio, @@ -65,8 +65,8 @@ solid = union(plate, fixed_particles) # ========================================================================================== # ==== Fluid -smoothing_length = 1.2 * fluid_particle_spacing -smoothing_kernel = SchoenbergCubicSplineKernel{2}() +smoothing_length = 3.5 * fluid_particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() fluid_density_calculator = ContinuityDensity() viscosity = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) diff --git a/examples/fsi/falling_sphere_2d.jl b/examples/fsi/falling_sphere_2d.jl index be775ea69..99a69bc2c 100644 --- a/examples/fsi/falling_sphere_2d.jl +++ b/examples/fsi/falling_sphere_2d.jl @@ -4,4 +4,4 @@ trixi_include(@__MODULE__, joinpath(examples_dir(), "fsi", "falling_spheres_2d.jl"), solid_system_2=nothing, fluid_particle_spacing=0.02, initial_fluid_size=(1.0, 0.9), tank_size=(1.0, 1.0), - tspan=(0.0, 2.0), abstol=1e-6, reltol=1e-3) + tspan=(0.0, 1.0), abstol=1e-6, reltol=1e-3) diff --git a/examples/fsi/falling_sphere_3d.jl b/examples/fsi/falling_sphere_3d.jl index 239836c9d..d44911409 100644 --- a/examples/fsi/falling_sphere_3d.jl +++ b/examples/fsi/falling_sphere_3d.jl @@ -11,4 +11,4 @@ trixi_include(@__MODULE__, sphere_type=RoundSphere(), output_directory="out", prefix="", write_meta_data=false, # Files with meta data can't be read by meshio - tspan=(0.0, 2.0), abstol=1e-6, reltol=1e-3) + tspan=(0.0, 1.0), abstol=1e-6, reltol=1e-3) diff --git a/examples/fsi/falling_spheres_2d.jl b/examples/fsi/falling_spheres_2d.jl index 66f769308..6a2397a4b 100644 --- a/examples/fsi/falling_spheres_2d.jl +++ b/examples/fsi/falling_spheres_2d.jl @@ -13,7 +13,7 @@ spacing_ratio = 1 # ========================================================================================== # ==== Experiment Setup gravity = 9.81 -tspan = (0.0, 2.0) +tspan = (0.0, 1.0) # Boundary geometry and initial fluid particle positions initial_fluid_size = (2.0, 0.9) diff --git a/src/general/file_system.jl b/src/general/file_system.jl index 39d37394b..0cc4cc56d 100644 --- a/src/general/file_system.jl +++ b/src/general/file_system.jl @@ -13,10 +13,19 @@ function system_names(systems) end function get_git_hash() + pkg_directory = pkgdir(@__MODULE__) + git_directory = joinpath(pkg_directory, ".git") + + # Check if the .git directory exists + if !isdir(git_directory) + return "UnknownVersion" + end + try - return string(readchomp(Cmd(`git describe --tags --always --first-parent --dirty`, - dir=pkgdir(@__MODULE__)))) + git_cmd = Cmd(`git describe --tags --always --first-parent --dirty`, + dir=pkg_directory) + return string(readchomp(git_cmd)) catch e - return "Git is not installed or not accessible" + return "UnknownVersion" end end diff --git a/test/examples/examples.jl b/test/examples/examples.jl index 3611cf093..15c90c672 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -76,14 +76,6 @@ @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "fluid/moving_wall_2d.jl" begin - @test_nowarn_mod trixi_include(@__MODULE__, tspan=(0.0, 0.5), - joinpath(examples_dir(), "fluid", - "moving_wall_2d.jl")) - @test sol.retcode == ReturnCode.Success - @test count_rhs_allocations(sol, semi) == 0 - end - @trixi_testset "fluid/dam_break_2d.jl" begin @test_nowarn_mod trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", @@ -122,6 +114,14 @@ @test count_rhs_allocations(sol, semi) == 0 end + @trixi_testset "fluid/moving_wall_2d.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, tspan=(0.0, 0.5), + joinpath(examples_dir(), "fluid", + "moving_wall_2d.jl")) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + include("dam_break_2d_corrections.jl") end @@ -146,11 +146,11 @@ @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "fsi/dam_break_2d.jl" begin + @trixi_testset "fsi/dam_break_plate_2d.jl" begin # Use rounded dimensions to avoid warnings @test_nowarn_mod trixi_include(@__MODULE__, joinpath(examples_dir(), "fsi", - "dam_break_2d.jl"), + "dam_break_plate_2d.jl"), initial_fluid_size=(0.15, 0.29), tspan=(0.0, 0.4), dtmax=1e-3) From 955d60cd128ab095c8a9ef6f7324b8061d2d543f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:05:47 +0200 Subject: [PATCH 03/49] Bump crate-ci/typos from 1.18.2 to 1.19.0 (#480) Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.18.2 to 1.19.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/v1.18.2...v1.19.0) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/SpellCheck.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml index 87e34cb50..e8f2271f8 100644 --- a/.github/workflows/SpellCheck.yml +++ b/.github/workflows/SpellCheck.yml @@ -10,4 +10,4 @@ jobs: - name: Checkout Actions Repository uses: actions/checkout@v4 - name: Check spelling - uses: crate-ci/typos@v1.18.2 + uses: crate-ci/typos@v1.19.0 From fc78207616873f145af69e10c315c82f8e88dce8 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 2 Apr 2024 20:45:12 +0200 Subject: [PATCH 04/49] Fix another closures error on macOS-ARM (#489) --- .../solid/total_lagrangian_sph/system.jl | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/schemes/solid/total_lagrangian_sph/system.jl b/src/schemes/solid/total_lagrangian_sph/system.jl index 452a74506..cdaf8dba4 100644 --- a/src/schemes/solid/total_lagrangian_sph/system.jl +++ b/src/schemes/solid/total_lagrangian_sph/system.jl @@ -390,21 +390,29 @@ end # The von-Mises stress is one form of equivalent stress, where sigma is the deviatoric stress. # See pages 32 and 123. function von_mises_stress(system::TotalLagrangianSPHSystem) - von_mises_stress = zeros(eltype(system.pk1_corrected), nparticles(system)) + von_mises_stress_vector = zeros(eltype(system.pk1_corrected), nparticles(system)) @threaded for particle in each_moving_particle(system) - F = deformation_gradient(system, particle) - J = det(F) - P = pk1_corrected(system, particle) - sigma = (1.0 / J) * P * F' + von_mises_stress_vector[particle] = von_mises_stress(system, particle) + end + + return von_mises_stress_vector +end - # Calculate deviatoric stress tensor - s = sigma - (1.0 / 3.0) * tr(sigma) * I +# Use this function barrier and unpack inside to avoid passing closures to Polyester.jl +# with `@batch` (`@threaded`). +# Otherwise, `@threaded` does not work here with Julia ARM on macOS. +# See https://github.com/JuliaSIMD/Polyester.jl/issues/88. +@inline function von_mises_stress(system, particle) + F = deformation_gradient(system, particle) + J = det(F) + P = pk1_corrected(system, particle) + sigma = (1.0 / J) * P * F' - von_mises_stress[particle] = sqrt(3.0 / 2.0 * sum(s .^ 2)) - end + # Calculate deviatoric stress tensor + s = sigma - (1.0 / 3.0) * tr(sigma) * I - return von_mises_stress + return sqrt(3.0 / 2.0 * sum(s .^ 2)) end # An explanation of these equation can be found in From 60d42d421b185ebe7b11d6dab828734fae789628 Mon Sep 17 00:00:00 2001 From: Niklas Neher <73897120+LasNikas@users.noreply.github.com> Date: Wed, 3 Apr 2024 15:10:10 +0200 Subject: [PATCH 05/49] Add wrapper for internal use of interpolation functions (#466) * add wrapper * fix tests * fix tests * implement suggestions * quick dirty fix --------- Co-authored-by: Sven Berger --- src/general/interpolation.jl | 130 +++++++++++++----- test/general/interpolation.jl | 42 +++--- .../dam_break_2d/validation_dam_break_2d.jl | 3 +- 3 files changed, 115 insertions(+), 60 deletions(-) diff --git a/src/general/interpolation.jl b/src/general/interpolation.jl index 51b13c681..eb04b4e61 100644 --- a/src/general/interpolation.jl +++ b/src/general/interpolation.jl @@ -51,14 +51,32 @@ results = interpolate_plane_2d([0.0, 0.0], [1.0, 1.0], 0.2, semi, ref_system, so (density = ...) ``` """ -function interpolate_plane_2d(min_corner, max_corner, resolution, semi, ref_system, sol; +function interpolate_plane_2d(min_corner, max_corner, resolution, semi, ref_system, + sol::ODESolution; smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) # Filter out particles without neighbors filter_no_neighbors = true + v_ode, u_ode = sol.u[end].x + + results, _, _ = interpolate_plane_2d(min_corner, max_corner, resolution, + semi, ref_system, v_ode, u_ode, + filter_no_neighbors, smoothing_length, cut_off_bnd, + clip_negative_pressure) + + return results +end + +function interpolate_plane_2d(min_corner, max_corner, resolution, semi, ref_system, + v_ode, u_ode; + smoothing_length=ref_system.smoothing_length, + cut_off_bnd=true, clip_negative_pressure=false) + # Filter out particles without neighbors + filter_no_neighbors = true + results, _, _ = interpolate_plane_2d(min_corner, max_corner, resolution, - semi, ref_system, sol, filter_no_neighbors, - smoothing_length, cut_off_bnd, + semi, ref_system, v_ode, u_ode, + filter_no_neighbors, smoothing_length, cut_off_bnd, clip_negative_pressure) return results @@ -112,14 +130,28 @@ See also: [`interpolate_plane_2d`](@ref), [`interpolate_plane_3d`](@ref), results = interpolate_plane_2d([0.0, 0.0], [1.0, 1.0], 0.2, semi, ref_system, sol) ``` """ -function interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_system, sol; +function interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_system, + sol::ODESolution; clip_negative_pressure=false, + smoothing_length=ref_system.smoothing_length, + cut_off_bnd=true, + output_directory="out", filename="plane") + v_ode = sol.u[end].x[1] + u_ode = sol.u[end].x[2] + + interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_system, + v_ode, u_ode; clip_negative_pressure, + smoothing_length, cut_off_bnd, output_directory, filename) +end + +function interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_system, + v_ode, u_ode; smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false, output_directory="out", filename="plane") # Don't filter out particles without neighbors to keep 2D grid structure filter_no_neighbors = false results, x_range, y_range = interpolate_plane_2d(min_corner, max_corner, resolution, - semi, ref_system, sol, + semi, ref_system, v_ode, u_ode, filter_no_neighbors, smoothing_length, cut_off_bnd, clip_negative_pressure) @@ -135,9 +167,9 @@ function interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_ end end -function interpolate_plane_2d(min_corner, max_corner, resolution, semi, ref_system, sol, - filter_no_neighbors, smoothing_length, cut_off_bnd, - clip_negative_pressure) +function interpolate_plane_2d(min_corner, max_corner, resolution, semi, ref_system, + v_ode, u_ode, filter_no_neighbors, smoothing_length, + cut_off_bnd, clip_negative_pressure) dims = length(min_corner) if dims != 2 || length(max_corner) != 2 throw(ArgumentError("function is intended for 2D coordinates only")) @@ -157,7 +189,7 @@ function interpolate_plane_2d(min_corner, max_corner, resolution, semi, ref_syst # Generate points within the plane points_coords = [SVector(x, y) for x in x_range, y in y_range] - results = interpolate_point(points_coords, semi, ref_system, sol, + results = interpolate_point(points_coords, semi, ref_system, v_ode, u_ode, smoothing_length=smoothing_length, cut_off_bnd=cut_off_bnd, clip_negative_pressure=clip_negative_pressure) @@ -226,9 +258,21 @@ results = interpolate_plane_3d([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0] (density = ...) ``` """ -function interpolate_plane_3d(point1, point2, point3, resolution, semi, ref_system, sol; +function interpolate_plane_3d(point1, point2, point3, resolution, semi, ref_system, + sol::ODESolution; smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) + v_ode = sol.u[end].x[1] + u_ode = sol.u[end].x[2] + + interpolate_plane_3d(point1, point2, point3, resolution, semi, ref_system, + v_ode, u_ode; smoothing_length, cut_off_bnd, + clip_negative_pressure) +end + +function interpolate_plane_3d(point1, point2, point3, resolution, semi, ref_system, + v_ode, u_ode; smoothing_length=ref_system.smoothing_length, + cut_off_bnd=true, clip_negative_pressure=false) # Verify that points are in 3D space if length(point1) != 3 || length(point2) != 3 || length(point3) != 3 throw(ArgumentError("all points must be 3D coordinates")) @@ -270,7 +314,7 @@ function interpolate_plane_3d(point1, point2, point3, resolution, semi, ref_syst end # Interpolate using the generated points - results = interpolate_point(points_coords, semi, ref_system, sol, + results = interpolate_point(points_coords, semi, ref_system, v_ode, u_ode, smoothing_length=smoothing_length, cut_off_bnd=cut_off_bnd, clip_negative_pressure=clip_negative_pressure) @@ -335,8 +379,17 @@ results = interpolate_line([1.0, 0.0], [1.0, 1.0], 5, semi, ref_system, sol) (density = ...) ``` """ -function interpolate_line(start, end_, n_points, semi, ref_system, sol; endpoint=true, - smoothing_length=ref_system.smoothing_length, +function interpolate_line(start, end_, n_points, semi, ref_system, sol::ODESolution; + endpoint=true, smoothing_length=ref_system.smoothing_length, + cut_off_bnd=true, clip_negative_pressure=false) + v_ode = sol.u[end].x[1] + u_ode = sol.u[end].x[2] + + interpolate_line(start, end_, n_points, semi, ref_system, v_ode, u_ode; + endpoint, smoothing_length, cut_off_bnd, clip_negative_pressure) +end +function interpolate_line(start, end_, n_points, semi, ref_system, v_ode, u_ode; + endpoint=true, smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) start_svector = SVector{ndims(ref_system)}(start) end_svector = SVector{ndims(ref_system)}(end_) @@ -346,10 +399,9 @@ function interpolate_line(start, end_, n_points, semi, ref_system, sol; endpoint points_coords = points_coords[2:(end - 1)] end - return interpolate_point(points_coords, semi, ref_system, sol, + return interpolate_point(points_coords, semi, ref_system, v_ode, u_ode; smoothing_length=smoothing_length, - cut_off_bnd=cut_off_bnd, - clip_negative_pressure=clip_negative_pressure) + cut_off_bnd=cut_off_bnd, clip_negative_pressure) end @doc raw""" @@ -411,9 +463,19 @@ results = interpolate_point(points, semi, ref_system, sol) - With `cut_off_bnd`, a density-based estimation of the surface is used which is not as accurate as a real surface reconstruction. """ -function interpolate_point(points_coords::AbstractArray{<:AbstractArray}, semi, ref_system, - sol; smoothing_length=ref_system.smoothing_length, - cut_off_bnd=true, clip_negative_pressure=false) +@inline function interpolate_point(point_coords, semi, ref_system, sol::ODESolution; + smoothing_length=ref_system.smoothing_length, + cut_off_bnd=true, clip_negative_pressure=false) + v_ode = sol.u[end].x[1] + u_ode = sol.u[end].x[2] + interpolate_point(point_coords, semi, ref_system, v_ode, u_ode; + smoothing_length, cut_off_bnd, clip_negative_pressure) +end + +@inline function interpolate_point(points_coords::AbstractArray{<:AbstractArray}, semi, + ref_system, v_ode, u_ode; + smoothing_length=ref_system.smoothing_length, + cut_off_bnd=true, clip_negative_pressure=false) num_points = length(points_coords) coords = similar(points_coords) velocities = similar(points_coords) @@ -421,14 +483,13 @@ function interpolate_point(points_coords::AbstractArray{<:AbstractArray}, semi, pressures = Vector{Float64}(undef, num_points) neighbor_counts = Vector{Int}(undef, num_points) - neighborhood_searches = process_neighborhood_searches(semi, sol, ref_system, + neighborhood_searches = process_neighborhood_searches(semi, u_ode, ref_system, smoothing_length) for (i, point) in enumerate(points_coords) - result = interpolate_point(SVector{ndims(ref_system)}(point), semi, ref_system, sol, - neighborhood_searches, smoothing_length=smoothing_length, - cut_off_bnd=cut_off_bnd, - clip_negative_pressure=clip_negative_pressure) + result = interpolate_point(SVector{ndims(ref_system)}(point), semi, ref_system, + v_ode, u_ode, neighborhood_searches; + smoothing_length, cut_off_bnd, clip_negative_pressure) densities[i] = result.density neighbor_counts[i] = result.neighbor_count coords[i] = result.coord @@ -440,28 +501,27 @@ function interpolate_point(points_coords::AbstractArray{<:AbstractArray}, semi, velocity=velocities, pressure=pressures) end -function interpolate_point(point_coords, semi, ref_system, sol; +function interpolate_point(point_coords, semi, ref_system, v_ode, u_ode; smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) - neighborhood_searches = process_neighborhood_searches(semi, sol, ref_system, + neighborhood_searches = process_neighborhood_searches(semi, u_ode, ref_system, smoothing_length) return interpolate_point(SVector{ndims(ref_system)}(point_coords), semi, ref_system, - sol, neighborhood_searches, smoothing_length=smoothing_length, - cut_off_bnd=cut_off_bnd, - clip_negative_pressure=clip_negative_pressure) + v_ode, u_ode, neighborhood_searches; + smoothing_length, cut_off_bnd, clip_negative_pressure) end -function process_neighborhood_searches(semi, sol, ref_system, smoothing_length) +function process_neighborhood_searches(semi, u_ode, ref_system, smoothing_length) if isapprox(smoothing_length, ref_system.smoothing_length) # Update existing NHS - update_nhs(sol.u[end].x[2], semi) + update_nhs(u_ode, semi) neighborhood_searches = semi.neighborhood_searches[system_indices(ref_system, semi)] else ref_smoothing_kernel = ref_system.smoothing_kernel search_radius = compact_support(ref_smoothing_kernel, smoothing_length) neighborhood_searches = map(semi.systems) do system - u = wrap_u(sol.u[end].x[2], system, semi) + u = wrap_u(u_ode, system, semi) system_coords = current_coordinates(u, system) old_nhs = get_neighborhood_search(ref_system, system, semi) nhs = copy_neighborhood_search(old_nhs, search_radius, system_coords) @@ -472,7 +532,7 @@ function process_neighborhood_searches(semi, sol, ref_system, smoothing_length) return neighborhood_searches end -@inline function interpolate_point(point_coords, semi, ref_system, sol, +@inline function interpolate_point(point_coords, semi, ref_system, v_ode, u_ode, neighborhood_searches; smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) @@ -498,8 +558,8 @@ end nhs = neighborhood_searches[system_id] (; search_radius, periodic_box) = nhs - v = wrap_v(sol.u[end].x[1], system, semi) - u = wrap_u(sol.u[end].x[2], system, semi) + v = wrap_v(v_ode, system, semi) + u = wrap_u(u_ode, system, semi) system_coords = current_coordinates(u, system) diff --git a/test/general/interpolation.jl b/test/general/interpolation.jl index f45534f16..899e60528 100644 --- a/test/general/interpolation.jl +++ b/test/general/interpolation.jl @@ -115,16 +115,12 @@ # Density is integrated with `ContinuityDensity` v_no_bnd = vcat(fluid.velocity, fluid.density') - sol_no_boundary = (; u=[(; x=(v_no_bnd, u_no_bnd))]) - u_bnd = hcat(fluid.coordinates, bnd.coordinates) v_bnd_velocity = hcat(fluid.velocity, bnd.velocity) v_bnd_density = vcat(fluid.density, bnd.density) v_bnd = vcat(v_bnd_velocity, v_bnd_density') - sol_boundary = (; u=[(; x=(v_bnd, u_bnd))]) - semi_no_boundary = Semidiscretization(fluid_system, neighborhood_search=GridNeighborhoodSearch) semi_boundary = Semidiscretization(fluid_system, boundary_system, @@ -139,7 +135,8 @@ interpolation_walldistance(y) = TrixiParticles.interpolate_point([0.0, y], semi_no_boundary, fluid_system, - sol_no_boundary, + v_no_bnd, + u_no_bnd, cut_off_bnd=cut_off_bnd) # top outside @@ -205,7 +202,7 @@ result_multipoint = TrixiParticles.interpolate_point(multi_point_coords, semi_no_boundary, fluid_system, - sol_no_boundary, + v_no_bnd, u_no_bnd, cut_off_bnd=cut_off_bnd) expected_multi = (density=[666.0, 666.0000000000001, 666.0], @@ -224,13 +221,13 @@ result_endpoint = TrixiParticles.interpolate_line([1.0, -0.05], [1.0, 1.0], 5, semi_no_boundary, fluid_system, - sol_no_boundary, + v_no_bnd, u_no_bnd, endpoint=true, cut_off_bnd=cut_off_bnd) result = TrixiParticles.interpolate_line([1.0, -0.05], [1.0, 1.0], 5, semi_no_boundary, - fluid_system, sol_no_boundary, + fluid_system, v_no_bnd, u_no_bnd, endpoint=false, cut_off_bnd=cut_off_bnd) @@ -282,7 +279,7 @@ result = interpolate_plane_2d(interpolation_start, interpolation_end, resolution, semi_no_boundary, - fluid_system, sol_no_boundary) + fluid_system, v_no_bnd, u_no_bnd) expected_res = (density=[ 666.0, 666.0, 666.0, 666.0, 666.0, 666.0, 666.0, @@ -403,7 +400,7 @@ result = interpolate_plane_2d(interpolation_start, interpolation_end, resolution, semi_no_boundary, fluid_system, - sol_no_boundary, + v_no_bnd, u_no_bnd, smoothing_length=0.5 * smoothing_length) expected_res = (density=[ 666.0, 666.0, 666.0, 666.0, 666.0, 666.0, 666.0, 666.0, @@ -490,7 +487,8 @@ interpolation_walldistance(y) = TrixiParticles.interpolate_point([0.0, y], semi_boundary, fluid_system, - sol_boundary, + v_bnd, + u_bnd, cut_off_bnd=cut_off_bnd) # top outside @@ -578,7 +576,7 @@ result_multipoint = TrixiParticles.interpolate_point(multi_point_coords, semi_boundary, fluid_system, - sol_boundary, + v_bnd, u_bnd, cut_off_bnd=cut_off_bnd) if cut_off_bnd expected_multi = (density=[666.0, 666.0000000000001, 666.0], @@ -618,13 +616,13 @@ result_endpoint = TrixiParticles.interpolate_line([1.0, -0.05], [1.0, 1.0], 5, semi_boundary, fluid_system, - sol_boundary, + v_bnd, u_bnd, endpoint=true, cut_off_bnd=cut_off_bnd) result = TrixiParticles.interpolate_line([1.0, -0.05], [1.0, 1.0], 5, semi_no_boundary, - fluid_system, sol_no_boundary, + fluid_system, v_no_bnd, u_no_bnd, endpoint=false, cut_off_bnd=cut_off_bnd) if cut_off_bnd @@ -823,16 +821,12 @@ # Density is integrated with `ContinuityDensity` v_no_bnd = vcat(fluid.velocity, fluid.density') - sol_no_boundary = (; u=[(; x=(v_no_bnd, u_no_bnd))]) - u_bnd = hcat(fluid.coordinates, bnd.coordinates) v_bnd_velocity = hcat(fluid.velocity, bnd.velocity) v_bnd_density = vcat(fluid.density, bnd.density) v_bnd = vcat(v_bnd_velocity, v_bnd_density') - sol_boundary = (; u=[(; x=(v_bnd, u_bnd))]) - semi_no_boundary = Semidiscretization(fluid_system, neighborhood_search=GridNeighborhoodSearch) semi_boundary = Semidiscretization(fluid_system, boundary_system, @@ -851,7 +845,8 @@ ], semi_no_boundary, fluid_system, - sol_no_boundary, + v_no_bnd, + u_no_bnd, cut_off_bnd=cut_off_bnd) # top outside @@ -920,7 +915,7 @@ result_multipoint = TrixiParticles.interpolate_point(multi_point_coords, semi_no_boundary, fluid_system, - sol_no_boundary, + v_no_bnd, u_no_bnd, cut_off_bnd=cut_off_bnd) expected_multi = (density=[666.0, 666.0, 666.0], neighbor_count=[4, 4, 9], @@ -944,7 +939,8 @@ ], semi_boundary, fluid_system, - sol_boundary, + v_no_bnd, + u_no_bnd, cut_off_bnd=cut_off_bnd) # top outside @@ -1031,7 +1027,7 @@ result_multipoint = TrixiParticles.interpolate_point(multi_point_coords, semi_no_boundary, fluid_system, - sol_no_boundary, + v_no_bnd, u_no_bnd, cut_off_bnd=cut_off_bnd) expected_multi = (density=[666.0, 666.0, 666.0], neighbor_count=[4, 4, 9], @@ -1053,7 +1049,7 @@ result = interpolate_plane_3d(p1, p2, p3, resolution, semi_no_boundary, - fluid_system, sol_no_boundary) + fluid_system, v_no_bnd, u_no_bnd) expected_res = (density=[ 666.0, diff --git a/validation/dam_break_2d/validation_dam_break_2d.jl b/validation/dam_break_2d/validation_dam_break_2d.jl index baf2c41b4..bfe216fde 100644 --- a/validation/dam_break_2d/validation_dam_break_2d.jl +++ b/validation/dam_break_2d/validation_dam_break_2d.jl @@ -65,10 +65,9 @@ function max_x_coord(v, u, t, system) end function interpolated_pressure(coord_top, coord_bottom, v, u, t, system) - sol = (; u=[(; x=(v, u))]) n_interpolation_points = 10 interpolated_values = interpolate_line(coord_top, coord_bottom, - n_interpolation_points, semi, system, sol, + n_interpolation_points, semi, system, v, u, smoothing_length=2.0 * system.smoothing_length, clip_negative_pressure=true) return sum(map(x -> isnan(x) ? 0.0 : x, interpolated_values.pressure)) / From 2bff6c482efd6baf77e6320c9e6e02665eb10dc4 Mon Sep 17 00:00:00 2001 From: Niklas Neher <73897120+LasNikas@users.noreply.github.com> Date: Fri, 5 Apr 2024 11:55:59 +0200 Subject: [PATCH 06/49] Reopen #382: Extrude shapes/geometry (#437) * implement `ExtrudeFace` * use `sample_face` in `interpolate_plane_3d` * add docs * add tests * fix bug * fix bug * add `tlsph=true` * fix tuple bug * fix typo * fix again * fix layers * calculate particle spacing differently * change `floor` to `ceil` * change `round` to `isapprox` * adapt docs * implement suggestions * add doctests * try to fix doctest * implement suggestions * implement suggestion --- src/TrixiParticles.jl | 2 +- src/general/interpolation.jl | 50 ++----- src/setups/extrude_geometry.jl | 257 ++++++++++++++++++++++++++++++++ src/setups/rectangular_shape.jl | 2 +- src/setups/setups.jl | 1 + test/setups/extrude_geometry.jl | 110 ++++++++++++++ test/setups/setups.jl | 1 + 7 files changed, 381 insertions(+), 42 deletions(-) create mode 100644 src/setups/extrude_geometry.jl create mode 100644 test/setups/extrude_geometry.jl diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index d623cb9f7..b7c0c5fe7 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -61,7 +61,7 @@ export GridNeighborhoodSearch, TrivialNeighborhoodSearch export examples_dir, validation_dir, trixi_include export trixi2vtk export RectangularTank, RectangularShape, SphereShape -export VoxelSphere, RoundSphere, reset_wall! +export VoxelSphere, RoundSphere, reset_wall!, extrude_geometry export SourceTermDamping export ShepardKernelCorrection, KernelCorrection, AkinciFreeSurfaceCorrection, GradientCorrection, BlendedGradientCorrection, MixedKernelGradientCorrection diff --git a/src/general/interpolation.jl b/src/general/interpolation.jl index eb04b4e61..fb917f91a 100644 --- a/src/general/interpolation.jl +++ b/src/general/interpolation.jl @@ -135,8 +135,7 @@ function interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_ smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, output_directory="out", filename="plane") - v_ode = sol.u[end].x[1] - u_ode = sol.u[end].x[2] + v_ode, u_ode = sol.u[end].x interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_system, v_ode, u_ode; clip_negative_pressure, @@ -262,8 +261,7 @@ function interpolate_plane_3d(point1, point2, point3, resolution, semi, ref_syst sol::ODESolution; smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) - v_ode = sol.u[end].x[1] - u_ode = sol.u[end].x[2] + v_ode, u_ode = sol.u[end].x interpolate_plane_3d(point1, point2, point3, resolution, semi, ref_system, v_ode, u_ode; smoothing_length, cut_off_bnd, @@ -273,45 +271,18 @@ end function interpolate_plane_3d(point1, point2, point3, resolution, semi, ref_system, v_ode, u_ode; smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) - # Verify that points are in 3D space - if length(point1) != 3 || length(point2) != 3 || length(point3) != 3 - throw(ArgumentError("all points must be 3D coordinates")) - end - if ndims(ref_system) != 3 throw(ArgumentError("`interpolate_plane_3d` requires a 3D simulation")) end - point1_ = SVector{3}(point1) - point2_ = SVector{3}(point2) - point3_ = SVector{3}(point3) - - # Vectors defining the edges of the parallelogram - edge1 = point2_ - point1_ - edge2 = point3_ - point1_ + coords, resolution_ = sample_plane((point1, point2, point3), resolution) - # Check if the points are collinear - if norm(cross(edge1, edge2)) == 0 - throw(ArgumentError("the points must not be collinear")) + if !isapprox(resolution, resolution_, rtol=5e-2) + @info "The desired plane size is not a multiple of the resolution $resolution." * + "\nNew resolution is set to $resolution_." end - # Determine the number of points along each edge - num_points_edge1 = ceil(Int, norm(edge1) / resolution) - num_points_edge2 = ceil(Int, norm(edge2) / resolution) - - # Create a set of points on the plane - points_coords = Vector{SVector{3, Float64}}(undef, - (num_points_edge1 + 1) * - (num_points_edge2 + 1)) - index = 1 - for i in 0:num_points_edge1 - for j in 0:num_points_edge2 - point_on_plane = point1 + (i / num_points_edge1) * edge1 + - (j / num_points_edge2) * edge2 - points_coords[index] = point_on_plane - index += 1 - end - end + points_coords = reinterpret(reshape, SVector{3, Float64}, coords) # Interpolate using the generated points results = interpolate_point(points_coords, semi, ref_system, v_ode, u_ode, @@ -382,8 +353,7 @@ results = interpolate_line([1.0, 0.0], [1.0, 1.0], 5, semi, ref_system, sol) function interpolate_line(start, end_, n_points, semi, ref_system, sol::ODESolution; endpoint=true, smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) - v_ode = sol.u[end].x[1] - u_ode = sol.u[end].x[2] + v_ode, u_ode = sol.u[end].x interpolate_line(start, end_, n_points, semi, ref_system, v_ode, u_ode; endpoint, smoothing_length, cut_off_bnd, clip_negative_pressure) @@ -466,8 +436,8 @@ results = interpolate_point(points, semi, ref_system, sol) @inline function interpolate_point(point_coords, semi, ref_system, sol::ODESolution; smoothing_length=ref_system.smoothing_length, cut_off_bnd=true, clip_negative_pressure=false) - v_ode = sol.u[end].x[1] - u_ode = sol.u[end].x[2] + v_ode, u_ode = sol.u[end].x + interpolate_point(point_coords, semi, ref_system, v_ode, u_ode; smoothing_length, cut_off_bnd, clip_negative_pressure) end diff --git a/src/setups/extrude_geometry.jl b/src/setups/extrude_geometry.jl new file mode 100644 index 000000000..9b7a62401 --- /dev/null +++ b/src/setups/extrude_geometry.jl @@ -0,0 +1,257 @@ +@doc raw""" + extrude_geometry(geometry; particle_spacing, direction, n_extrude::Integer, + velocity=zeros(length(direction)), + mass=nothing, density=nothing, pressure=0.0) + +Extrude either a line, a plane or a shape along a specific direction. + +# Arguments +- `geometry`: Either particle coordinates or an [`InitialCondition`](@ref) + defining a 2D shape to extrude to a 3D volume, or two 2D points + defining a line to extrude to a plane in 2D, or three 3D points defining + a parallelogram to extrude to a parallelepiped. + +# Keywords +- `particle_spacing`: Spacing between the particles. Can be omitted when `geometry` is an + `InitialCondition` (unless `geometry.particle_spacing == -1`). +- `direction`: A vector that specifies the direction in which to extrude. +- `n_extrude`: Number of layers of particles created in the direction of extrusion. +- `velocity`: Either a function mapping each particle's coordinates to its velocity, + or, for a constant fluid velocity, a vector holding this velocity. + Velocity is constant zero by default. +- `mass`: Either `nothing` (default) to automatically compute particle mass from particle + density and spacing, or a function mapping each particle's coordinates to its mass, + or a scalar for a constant mass over all particles. +- `density`: Either a function mapping each particle's coordinates to its density, + or a scalar for a constant density over all particles. + Obligatory when not using a state equation. Cannot be used together with + `state_equation`. +- `pressure`: Scalar to set the pressure of all particles to this value. + This is only used by the [`EntropicallyDampedSPHSystem`](@ref) and + will be overwritten when using an initial pressure function in the system. + Cannot be used together with hydrostatic pressure gradient. +- `tlsph`: With the [`TotalLagrangianSPHSystem`](@ref), particles need to be placed + on the boundary of the shape and not one particle radius away, as for fluids. + When `tlsph=true`, particles will be placed on the boundary of the shape. + +# Examples +```jldoctest; output = false +# Extrude a line in 2D to a plane in 2D +p1 = [0.0, 0.0] +p2 = [1.0, 1.0] + +direction = [-1.0, 1.0] + +shape = extrude_geometry((p1, p2); direction, particle_spacing=0.1, n_extrude=4, density=1000.0) + +# Extrude a parallelogram in 3D to a parallelepiped in 3D +p1 = [0.0, 0.0, 0.0] +p2 = [0.5, 1.0, 0.0] +p3 = [1.0, 0.2, 0.0] + +direction = [0.0, 0.0, 1.0] + +shape = extrude_geometry((p1, p2, p3); direction, particle_spacing=0.1, n_extrude=4, density=1000.0) + +# Extrude a 2D shape (here: a disc) to a 3D shape (here: a cylinder) +shape = SphereShape(0.1, 0.5, (0.2, 0.4), 1000.0, n_layers=3, + sphere_type=RoundSphere(end_angle=pi)) + +direction = [0.0, 0.0, 1.0] + +shape = extrude_geometry(shape; direction, particle_spacing=0.1, n_extrude=4, density=1000.0) + +# output +┌ Info: The desired size is not a multiple of the particle spacing 0.1. +└ New particle spacing is set to 0.09387239731236392. +┌ Info: The desired size is not a multiple of the particle spacing 0.1. +└ New particle spacing is set to 0.09198039027185569. +InitialCondition{Float64}(0.1, [0.44999999999999996 0.43096988312782164 … -0.23871756048182058 -0.24999999999999994; 0.4 0.4956708580912724 … 0.5001344202803415 0.4000000000000001; 0.05 0.05 … 0.35000000000000003 0.35000000000000003], [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0], [1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002 … 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002, 1.0000000000000002], [1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0 … 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 … 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) +``` + +!!! warning "Experimental Implementation" + This is an experimental feature and may change in any future releases. +""" +function extrude_geometry(geometry; particle_spacing=-1, direction, n_extrude::Integer, + velocity=zeros(length(direction)), tlsph=false, + mass=nothing, density=nothing, pressure=0.0) + direction_ = normalize(direction) + NDIMS = length(direction_) + + if geometry isa InitialCondition && geometry.particle_spacing > 0 + if particle_spacing > 0 && particle_spacing != geometry.particle_spacing + throw(ArgumentError("`particle_spacing` must be -1 when using an `InitialCondition`")) + end + particle_spacing = geometry.particle_spacing + end + + if particle_spacing <= 0 + throw(ArgumentError("`particle_spacing` must be specified when not extruding an `InitialCondition`")) + end + + geometry = shift_plane_corners(geometry, direction_, particle_spacing, tlsph) + + face_coords, particle_spacing_ = sample_plane(geometry, particle_spacing; tlsph=tlsph) + + if !isapprox(particle_spacing, particle_spacing_, rtol=5e-2) + @info "The desired size is not a multiple of the particle spacing $particle_spacing." * + "\nNew particle spacing is set to $particle_spacing_." + end + + coords = (face_coords .+ i * particle_spacing_ * direction_ for i in 0:(n_extrude - 1)) + + # In this context, `stack` is faster than `hcat(coords...)` + coordinates = reshape(stack(coords), (NDIMS, size(face_coords, 2) * n_extrude)) + + if geometry isa InitialCondition + density = vcat(geometry.density, (geometry.density for i in 1:(n_extrude - 1))...) + end + + return InitialCondition(; coordinates, velocity, density, mass, pressure, + particle_spacing=particle_spacing_) +end + +# For corners/endpoints of a plane/line, sample the plane/line with particles. +# For 2D coordinates or an `InitialCondition`, add a third dimension. +function sample_plane(geometry::AbstractMatrix, particle_spacing; tlsph) + if size(geometry, 1) == 2 + # Extruding a 2D shape results in a 3D shape + + # When `tlsph=true`, particles will be placed on the x-y plane + coords = vcat(geometry, fill(tlsph ? 0.0 : 0.5particle_spacing, size(geometry, 2))') + + # TODO: 2D shapes not only in x-y plane but in any user-defined plane + return coords, particle_spacing + end + + return geometry, particle_spacing +end + +function sample_plane(shape::InitialCondition, particle_spacing; tlsph) + if ndims(shape) == 2 + # Extruding a 2D shape results in a 3D shape + + # When `tlsph=true`, particles will be placed on the x-y plane + coords = vcat(shape.coordinates, + fill(tlsph ? 0.0 : 0.5particle_spacing, size(shape.coordinates, 2))') + + # TODO: 2D shapes not only in x-y plane but in any user-defined plane + return coords, particle_spacing + end + + return shape.coordinates, particle_spacing +end + +function sample_plane(plane_points, particle_spacing; tlsph=nothing) + + # Convert to tuple + return sample_plane(tuple(plane_points...), particle_spacing; tlsph=nothing) +end + +function sample_plane(plane_points::NTuple{2}, particle_spacing; tlsph=nothing) + # Verify that points are in 2D space + if any(length.(plane_points) .!= 2) + throw(ArgumentError("all points must be 2D coordinates")) + end + + n_points = ceil(Int, norm(plane_points[2] - plane_points[1]) / particle_spacing) + 1 + + coords = stack(range(plane_points[1], plane_points[2], length=n_points)) + particle_spacing_new = norm(coords[:, 1] - coords[:, 2]) + + return coords, particle_spacing_new +end + +function sample_plane(plane_points::NTuple{3}, particle_spacing; tlsph=nothing) + # Verify that points are in 3D space + if any(length.(plane_points) .!= 3) + throw(ArgumentError("all points must be 3D coordinates")) + end + + point1_ = SVector{3}(plane_points[1]) + point2_ = SVector{3}(plane_points[2]) + point3_ = SVector{3}(plane_points[3]) + + # Vectors defining the edges of the parallelogram + edge1 = point2_ - point1_ + edge2 = point3_ - point1_ + + # Check if the points are collinear + if norm(cross(edge1, edge2)) == 0 + throw(ArgumentError("the points must not be collinear")) + end + + # Determine the number of points along each edge + num_points_edge1 = ceil(Int, norm(edge1) / particle_spacing) + num_points_edge2 = ceil(Int, norm(edge2) / particle_spacing) + + coords = zeros(3, (num_points_edge1 + 1) * (num_points_edge2 + 1)) + + index = 1 + for i in 0:num_points_edge1 + for j in 0:num_points_edge2 + point_on_plane = point1_ + (i / num_points_edge1) * edge1 + + (j / num_points_edge2) * edge2 + coords[:, index] = point_on_plane + index += 1 + end + end + + particle_spacing_new = min(norm(edge1 / num_points_edge1), + norm(edge2 / num_points_edge2)) + + return coords, particle_spacing_new +end + +# Shift corners of the plane/line inwards by half a particle spacing with `tlsph=false` +# because fluid particles need to be half a particle spacing away from the boundary of the shape. +function shift_plane_corners(geometry::Union{AbstractMatrix, InitialCondition}, + direction, particle_spacing, tlsph) + return geometry +end + +function shift_plane_corners(plane_points, direction, particle_spacing, tlsph) + shift_plane_corners(tuple(plane_points...), direction, particle_spacing, tlsph) +end + +function shift_plane_corners(plane_points::NTuple{2}, direction, particle_spacing, tlsph) + # With TLSPH, particles need to be AT the min coordinates and not half a particle + # spacing away from it. + (tlsph) && (return plane_points) + + plane_point1 = copy(plane_points[1]) + plane_point2 = copy(plane_points[2]) + + # Vectors shifting the points in the corresponding direction + dir1 = 0.5 * particle_spacing * direction + dir2 = 0.5 * particle_spacing * normalize(plane_point2 - plane_point1) + + plane_point1 .+= dir1 + dir2 + plane_point2 .+= dir1 - dir2 + + return (plane_point1, plane_point2) +end + +function shift_plane_corners(plane_points::NTuple{3}, direction, particle_spacing, tlsph) + # With TLSPH, particles need to be AT the min coordinates and not half a particle + # spacing away from it. + (tlsph) && (return plane_points) + + plane_point1 = copy(plane_points[1]) + plane_point2 = copy(plane_points[2]) + plane_point3 = copy(plane_points[3]) + + edge1 = normalize(plane_point2 - plane_point1) + edge2 = normalize(plane_point3 - plane_point1) + + # Vectors shifting the points in the corresponding direction + dir1 = 0.5 * particle_spacing * direction + dir2 = 0.5 * particle_spacing * edge1 + dir3 = 0.5 * particle_spacing * edge2 + + plane_point1 .+= dir1 + dir2 + dir3 + plane_point2 .+= dir1 - dir2 + dir3 + plane_point3 .+= dir1 + dir2 - dir3 + + return (plane_point1, plane_point2, plane_point3) +end diff --git a/src/setups/rectangular_shape.jl b/src/setups/rectangular_shape.jl index 5e79ebbe9..d520bbfa5 100644 --- a/src/setups/rectangular_shape.jl +++ b/src/setups/rectangular_shape.jl @@ -77,7 +77,7 @@ function RectangularShape(particle_spacing, n_particles_per_dimension, min_coord throw(ArgumentError("`min_coordinates` must be of length $NDIMS for a $(NDIMS)D problem")) end - if density !== nothing && density < eps() + if density !== nothing && any(density .< eps()) throw(ArgumentError("`density` needs to be positive and larger than $(eps())")) end diff --git a/src/setups/setups.jl b/src/setups/setups.jl index a4754edb5..f8c625874 100644 --- a/src/setups/setups.jl +++ b/src/setups/setups.jl @@ -1,3 +1,4 @@ include("sphere_shape.jl") include("rectangular_shape.jl") include("rectangular_tank.jl") +include("extrude_geometry.jl") diff --git a/test/setups/extrude_geometry.jl b/test/setups/extrude_geometry.jl new file mode 100644 index 000000000..7e0c8be4b --- /dev/null +++ b/test/setups/extrude_geometry.jl @@ -0,0 +1,110 @@ +# 2D +@testset verbose=true "Extrude Geometry 2D" begin + point1 = [0.0, 0.0] + point2 = [0.5, 1.0] + directions = [ + [-1.0, 2.0], + -[-1.0, 2.0], + [-2.0, 1.0], + -[-2.0, 1.0], + [1.0, 0.0], + [0.0, 1.0], + ] + + @testset verbose=true "Extrude Line-Points" begin + expected_coords = [ + [0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 -0.0625 0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 -0.125 -0.0625 0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 -0.1875 -0.125 -0.0625 0.0 0.0625 0.125 0.1875 0.25 0.3125 -0.25 -0.1875 -0.125 -0.0625 0.0 0.0625 0.125 0.1875 0.25; + 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 1.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 1.125 1.25 0.375 0.5 0.625 0.75 0.875 1.0 1.125 1.25 1.375 0.5 0.625 0.75 0.875 1.0 1.125 1.25 1.375 1.5], + [0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.5625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.5625 0.625 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.5625 0.625 0.6875 0.25 0.3125 0.375 0.4375 0.5 0.5625 0.625 0.6875 0.75; + 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 -0.125 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 -0.25 -0.125 0.0 0.125 0.25 0.375 0.5 0.625 0.75 -0.375 -0.25 -0.125 0.0 0.125 0.25 0.375 0.5 0.625 -0.5 -0.375 -0.25 -0.125 0.0 0.125 0.25 0.375 0.5], + [0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 -0.125 -0.0625 0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 -0.25 -0.1875 -0.125 -0.0625 0.0 0.0625 0.125 0.1875 0.25 -0.375 -0.3125 -0.25 -0.1875 -0.125 -0.0625 0.0 0.0625 0.125 -0.5 -0.4375 -0.375 -0.3125 -0.25 -0.1875 -0.125 -0.0625 0.0; + 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 0.0625 0.1875 0.3125 0.4375 0.5625 0.6875 0.8125 0.9375 1.0625 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 1.125 0.1875 0.3125 0.4375 0.5625 0.6875 0.8125 0.9375 1.0625 1.1875 0.25 0.375 0.5 0.625 0.75 0.875 1.0 1.125 1.25], + [0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.5625 0.625 0.25 0.3125 0.375 0.4375 0.5 0.5625 0.625 0.6875 0.75 0.375 0.4375 0.5 0.5625 0.625 0.6875 0.75 0.8125 0.875 0.5 0.5625 0.625 0.6875 0.75 0.8125 0.875 0.9375 1.0; + 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 -0.0625 0.0625 0.1875 0.3125 0.4375 0.5625 0.6875 0.8125 0.9375 -0.125 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 -0.1875 -0.0625 0.0625 0.1875 0.3125 0.4375 0.5625 0.6875 0.8125 -0.25 -0.125 0.0 0.125 0.25 0.375 0.5 0.625 0.75], + [0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.13975424859373686 0.20225424859373686 0.26475424859373686 0.32725424859373686 0.38975424859373686 0.45225424859373686 0.5147542485937369 0.5772542485937369 0.6397542485937369 0.2795084971874737 0.3420084971874737 0.4045084971874737 0.4670084971874737 0.5295084971874737 0.5920084971874737 0.6545084971874737 0.7170084971874737 0.7795084971874737 0.4192627457812106 0.4817627457812106 0.5442627457812106 0.6067627457812106 0.6692627457812106 0.7317627457812106 0.7942627457812106 0.8567627457812106 0.9192627457812106 0.5590169943749475 0.6215169943749475 0.6840169943749475 0.7465169943749475 0.8090169943749475 0.8715169943749475 0.9340169943749475 0.9965169943749475 1.0590169943749475; + 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0], + [0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5 0.0 0.0625 0.125 0.1875 0.25 0.3125 0.375 0.4375 0.5; + 0.0 0.125 0.25 0.375 0.5 0.625 0.75 0.875 1.0 0.13975424859373686 0.26475424859373686 0.38975424859373686 0.5147542485937369 0.6397542485937369 0.7647542485937369 0.8897542485937369 1.0147542485937369 1.1397542485937369 0.2795084971874737 0.4045084971874737 0.5295084971874737 0.6545084971874737 0.7795084971874737 0.9045084971874737 1.0295084971874737 1.1545084971874737 1.2795084971874737 0.4192627457812106 0.5442627457812106 0.6692627457812106 0.7942627457812106 0.9192627457812106 1.0442627457812106 1.1692627457812106 1.2942627457812106 1.4192627457812106 0.5590169943749475 0.6840169943749475 0.8090169943749475 0.9340169943749475 1.0590169943749475 1.1840169943749475 1.3090169943749475 1.4340169943749475 1.5590169943749475], + ] + + @testset "Direction $i" for i in eachindex(directions) + shape = extrude_geometry((point1, point2); direction=directions[i], tlsph=true, + particle_spacing=0.15, n_extrude=5, density=1.0) + + @test shape.coordinates ≈ expected_coords[i] + end + end + + @testset verbose=true "Shifted Particles" begin + point1 = [0.0, 0.0] + point2 = [0.0, 1.0] + + expected_coords = [-0.05 -0.05 -0.05 -0.05 -0.05 -0.05 -0.05 -0.05 -0.05 -0.05 -0.15 -0.15 -0.15 -0.15 -0.15 -0.15 -0.15 -0.15 -0.15 -0.15 -0.25 -0.25 -0.25 -0.25 -0.25 -0.25 -0.25 -0.25 -0.25 -0.25 -0.35 -0.35 -0.35 -0.35 -0.35 -0.35 -0.35 -0.35 -0.35 -0.35 -0.44999999999999996 -0.44999999999999996 -0.44999999999999996 -0.44999999999999996 -0.44999999999999996 -0.44999999999999996 -0.44999999999999996 -0.44999999999999996 -0.44999999999999996 -0.44999999999999996; + 0.05 0.15 0.24999999999999997 0.35 0.44999999999999996 0.55 0.65 0.7499999999999999 0.8499999999999999 0.95 0.05 0.15 0.24999999999999997 0.35 0.44999999999999996 0.55 0.65 0.7499999999999999 0.8499999999999999 0.95 0.05 0.15 0.24999999999999997 0.35 0.44999999999999996 0.55 0.65 0.7499999999999999 0.8499999999999999 0.95 0.05 0.15 0.24999999999999997 0.35 0.44999999999999996 0.55 0.65 0.7499999999999999 0.8499999999999999 0.95 0.05 0.15 0.24999999999999997 0.35 0.44999999999999996 0.55 0.65 0.7499999999999999 0.8499999999999999 0.95] + shape = extrude_geometry((point1, point2); direction=[-1.0, 0.0], + particle_spacing=0.1, n_extrude=5, density=1.0) + + @test shape.coordinates ≈ expected_coords + end +end + +# 3D +@testset verbose=true "Extrude Geometry 3D" begin + @testset verbose=true "Extrude 2D Shape" begin + geometry = SphereShape(0.1, 0.5, (0.2, 0.4), 1000.0, n_layers=3, + sphere_type=RoundSphere(end_angle=pi)) + particle_spacing = geometry.particle_spacing + + directions = [[0.0, 0.0, 1.0], [0.0, -1.0, 1.0]] + + expected_coords = [ + [0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994 0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994 0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994 0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994 0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994; + 0.4 0.4956708580912724 0.5767766952966369 0.6309698831278217 0.6499999999999999 0.6309698831278217 0.5767766952966369 0.4956708580912725 0.4000000000000001 0.4 0.4986063948945004 0.5892242861094592 0.6645123510239904 0.7183711983740815 0.7464375046583265 0.7464375046583265 0.7183711983740815 0.6645123510239904 0.5892242861094591 0.49860639489450054 0.4000000000000001 0.4 0.5001344202803415 0.5952476826029012 0.6805704108364301 0.7518241671106134 0.8054359905560886 0.8387175604818207 0.85 0.8387175604818207 0.8054359905560886 0.7518241671106134 0.6805704108364301 0.5952476826029012 0.5001344202803415 0.4000000000000001 0.4 0.4956708580912724 0.5767766952966369 0.6309698831278217 0.6499999999999999 0.6309698831278217 0.5767766952966369 0.4956708580912725 0.4000000000000001 0.4 0.4986063948945004 0.5892242861094592 0.6645123510239904 0.7183711983740815 0.7464375046583265 0.7464375046583265 0.7183711983740815 0.6645123510239904 0.5892242861094591 0.49860639489450054 0.4000000000000001 0.4 0.5001344202803415 0.5952476826029012 0.6805704108364301 0.7518241671106134 0.8054359905560886 0.8387175604818207 0.85 0.8387175604818207 0.8054359905560886 0.7518241671106134 0.6805704108364301 0.5952476826029012 0.5001344202803415 0.4000000000000001 0.4 0.4956708580912724 0.5767766952966369 0.6309698831278217 0.6499999999999999 0.6309698831278217 0.5767766952966369 0.4956708580912725 0.4000000000000001 0.4 0.4986063948945004 0.5892242861094592 0.6645123510239904 0.7183711983740815 0.7464375046583265 0.7464375046583265 0.7183711983740815 0.6645123510239904 0.5892242861094591 0.49860639489450054 0.4000000000000001 0.4 0.5001344202803415 0.5952476826029012 0.6805704108364301 0.7518241671106134 0.8054359905560886 0.8387175604818207 0.85 0.8387175604818207 0.8054359905560886 0.7518241671106134 0.6805704108364301 0.5952476826029012 0.5001344202803415 0.4000000000000001 0.4 0.4956708580912724 0.5767766952966369 0.6309698831278217 0.6499999999999999 0.6309698831278217 0.5767766952966369 0.4956708580912725 0.4000000000000001 0.4 0.4986063948945004 0.5892242861094592 0.6645123510239904 0.7183711983740815 0.7464375046583265 0.7464375046583265 0.7183711983740815 0.6645123510239904 0.5892242861094591 0.49860639489450054 0.4000000000000001 0.4 0.5001344202803415 0.5952476826029012 0.6805704108364301 0.7518241671106134 0.8054359905560886 0.8387175604818207 0.85 0.8387175604818207 0.8054359905560886 0.7518241671106134 0.6805704108364301 0.5952476826029012 0.5001344202803415 0.4000000000000001 0.4 0.4956708580912724 0.5767766952966369 0.6309698831278217 0.6499999999999999 0.6309698831278217 0.5767766952966369 0.4956708580912725 0.4000000000000001 0.4 0.4986063948945004 0.5892242861094592 0.6645123510239904 0.7183711983740815 0.7464375046583265 0.7464375046583265 0.7183711983740815 0.6645123510239904 0.5892242861094591 0.49860639489450054 0.4000000000000001 0.4 0.5001344202803415 0.5952476826029012 0.6805704108364301 0.7518241671106134 0.8054359905560886 0.8387175604818207 0.85 0.8387175604818207 0.8054359905560886 0.7518241671106134 0.6805704108364301 0.5952476826029012 0.5001344202803415 0.4000000000000001; + 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.30000000000000004 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4], + [0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994 0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994 0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994 0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994 0.44999999999999996 0.43096988312782164 0.3767766952966369 0.29567085809127247 0.20000000000000004 0.10432914190872761 0.023223304703363173 -0.030969883127821618 -0.04999999999999993 0.55 0.5358225407650741 0.4944387364909134 0.42920125688084976 0.3453952545506602 0.24981019339564986 0.15018980660435027 0.0546047454493398 -0.029201256880849707 -0.09443873649091344 -0.135822540765074 -0.14999999999999997 0.6499999999999999 0.6387175604818206 0.6054359905560887 0.5518241671106134 0.4805704108364301 0.39524768260290116 0.3001344202803415 0.20000000000000004 0.09986557971965856 0.0047523173970988875 -0.08057041083643002 -0.1518241671106133 -0.20543599055608852 -0.23871756048182058 -0.24999999999999994; + 0.4 0.4956708580912724 0.5767766952966369 0.6309698831278217 0.6499999999999999 0.6309698831278217 0.5767766952966369 0.4956708580912725 0.4000000000000001 0.4 0.4986063948945004 0.5892242861094592 0.6645123510239904 0.7183711983740815 0.7464375046583265 0.7464375046583265 0.7183711983740815 0.6645123510239904 0.5892242861094591 0.49860639489450054 0.4000000000000001 0.4 0.5001344202803415 0.5952476826029012 0.6805704108364301 0.7518241671106134 0.8054359905560886 0.8387175604818207 0.85 0.8387175604818207 0.8054359905560886 0.7518241671106134 0.6805704108364301 0.5952476826029012 0.5001344202803415 0.4000000000000001 0.3292893218813453 0.4249601799726177 0.5060660171779822 0.5602592050091669 0.5792893218813452 0.5602592050091669 0.5060660171779822 0.4249601799726177 0.3292893218813453 0.3292893218813453 0.4278957167758457 0.5185136079908045 0.5938016729053357 0.6476605202554268 0.6757268265396718 0.6757268265396718 0.6476605202554268 0.5938016729053357 0.5185136079908044 0.4278957167758458 0.3292893218813453 0.3292893218813453 0.4294237421616868 0.5245370044842464 0.6098597327177754 0.6811134889919587 0.7347253124374339 0.7680068823631659 0.7792893218813453 0.7680068823631659 0.7347253124374339 0.6811134889919587 0.6098597327177754 0.5245370044842464 0.4294237421616868 0.3292893218813453 0.2585786437626905 0.3542495018539629 0.4353553390593274 0.48954852689051215 0.5085786437626905 0.48954852689051215 0.4353553390593274 0.354249501853963 0.2585786437626906 0.2585786437626905 0.3571850386571909 0.4478029298721497 0.5230909947866809 0.576949842136772 0.605016148421017 0.605016148421017 0.576949842136772 0.5230909947866809 0.4478029298721496 0.35718503865719103 0.2585786437626906 0.2585786437626905 0.358713064043032 0.45382632636559167 0.5391490545991207 0.610402810873304 0.6640146343187792 0.6972962042445112 0.7085786437626904 0.6972962042445112 0.6640146343187792 0.610402810873304 0.5391490545991207 0.45382632636559167 0.358713064043032 0.2585786437626906 0.18786796564403574 0.28353882373530814 0.3646446609406726 0.41883784877185737 0.43786796564403563 0.41883784877185737 0.3646446609406726 0.2835388237353082 0.1878679656440358 0.18786796564403574 0.28647436053853614 0.3770922517534949 0.45238031666802614 0.5062391640181172 0.5343054703023622 0.5343054703023622 0.5062391640181172 0.45238031666802614 0.3770922517534948 0.28647436053853625 0.1878679656440358 0.18786796564403574 0.2880023859243772 0.3831156482469369 0.4684383764804658 0.5396921327546491 0.5933039562001243 0.6265855261258564 0.6378679656440357 0.6265855261258564 0.5933039562001243 0.5396921327546491 0.4684383764804658 0.3831156482469369 0.2880023859243772 0.1878679656440358 0.11715728752538102 0.21282814561665342 0.2939339828220179 0.34812717065320264 0.3671572875253809 0.34812717065320264 0.2939339828220179 0.21282814561665347 0.11715728752538107 0.11715728752538102 0.21576368241988142 0.3063815736348402 0.3816696385493714 0.43552848589946247 0.46359479218370747 0.46359479218370747 0.43552848589946247 0.3816696385493714 0.3063815736348401 0.21576368241988153 0.11715728752538107 0.11715728752538102 0.2172917078057225 0.31240497012828217 0.3977276983618111 0.4689814546359944 0.5225932780814696 0.5558748480072017 0.567157287525381 0.5558748480072017 0.5225932780814696 0.4689814546359944 0.3977276983618111 0.31240497012828217 0.2172917078057225 0.11715728752538107; + 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.07071067811865475 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.1414213562373095 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.21213203435596428 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619 0.282842712474619], + ] + + @testset "Direction $i" for i in eachindex(directions) + shape = extrude_geometry(geometry; direction=directions[i], particle_spacing, + n_extrude=5, tlsph=true, density=1.0) + + @test shape.coordinates ≈ expected_coords[i] + end + end + + @testset verbose=true "Extrude 3D Plane" begin + p1 = [0.0, 0.0, 0.0] + p2 = [0.5, 1.0, 1.0] + p3 = [1.0, 0.2, 0.0] + + direction = [0.0, 0.0, 1.0] + + expected_coords = [0.0 0.09090909090909091 0.18181818181818182 0.2727272727272727 0.36363636363636365 0.45454545454545453 0.5454545454545454 0.6363636363636364 0.7272727272727273 0.8181818181818182 0.9090909090909091 1.0 0.03333333333333333 0.12424242424242424 0.21515151515151515 0.30606060606060603 0.396969696969697 0.48787878787878786 0.5787878787878787 0.6696969696969697 0.7606060606060606 0.8515151515151516 0.9424242424242424 1.0333333333333334 0.06666666666666667 0.1575757575757576 0.24848484848484848 0.33939393939393936 0.4303030303030303 0.5212121212121212 0.6121212121212121 0.703030303030303 0.793939393939394 0.8848484848484849 0.9757575757575757 1.0666666666666667 0.1 0.19090909090909092 0.28181818181818186 0.3727272727272727 0.4636363636363636 0.5545454545454546 0.6454545454545454 0.7363636363636363 0.8272727272727273 0.9181818181818182 1.009090909090909 1.1 0.13333333333333333 0.22424242424242424 0.3151515151515152 0.406060606060606 0.49696969696969695 0.5878787878787879 0.6787878787878787 0.7696969696969697 0.8606060606060606 0.9515151515151515 1.0424242424242425 1.1333333333333333 0.16666666666666666 0.25757575757575757 0.3484848484848485 0.43939393939393934 0.5303030303030303 0.6212121212121212 0.712121212121212 0.803030303030303 0.8939393939393939 0.9848484848484849 1.0757575757575757 1.1666666666666667 0.2 0.2909090909090909 0.38181818181818183 0.4727272727272727 0.5636363636363637 0.6545454545454545 0.7454545454545454 0.8363636363636364 0.9272727272727272 1.0181818181818183 1.1090909090909091 1.2 0.23333333333333334 0.3242424242424242 0.41515151515151516 0.5060606060606061 0.5969696969696969 0.6878787878787879 0.7787878787878788 0.8696969696969696 0.9606060606060607 1.0515151515151515 1.1424242424242423 1.2333333333333334 0.26666666666666666 0.35757575757575755 0.4484848484848485 0.5393939393939393 0.6303030303030304 0.7212121212121212 0.812121212121212 0.9030303030303031 0.9939393939393939 1.084848484848485 1.1757575757575758 1.2666666666666666 0.3 0.3909090909090909 0.4818181818181818 0.5727272727272728 0.6636363636363636 0.7545454545454545 0.8454545454545455 0.9363636363636363 1.0272727272727273 1.1181818181818182 1.209090909090909 1.3 0.3333333333333333 0.4242424242424242 0.5151515151515151 0.606060606060606 0.696969696969697 0.7878787878787878 0.8787878787878787 0.9696969696969697 1.0606060606060606 1.1515151515151516 1.2424242424242424 1.3333333333333333 0.36666666666666664 0.4575757575757575 0.5484848484848485 0.6393939393939394 0.7303030303030302 0.8212121212121212 0.9121212121212121 1.003030303030303 1.093939393939394 1.1848484848484848 1.2757575757575756 1.3666666666666667 0.4 0.49090909090909096 0.5818181818181818 0.6727272727272727 0.7636363636363637 0.8545454545454545 0.9454545454545454 1.0363636363636364 1.1272727272727274 1.2181818181818183 1.309090909090909 1.4 0.43333333333333335 0.5242424242424243 0.6151515151515152 0.706060606060606 0.796969696969697 0.8878787878787879 0.9787878787878788 1.0696969696969698 1.1606060606060606 1.2515151515151515 1.3424242424242423 1.4333333333333333 0.4666666666666667 0.5575757575757576 0.6484848484848484 0.7393939393939394 0.8303030303030303 0.9212121212121211 1.0121212121212122 1.103030303030303 1.1939393939393939 1.284848484848485 1.3757575757575757 1.4666666666666668 0.5 0.5909090909090909 0.6818181818181819 0.7727272727272727 0.8636363636363636 0.9545454545454546 1.0454545454545454 1.1363636363636362 1.2272727272727273 1.3181818181818183 1.4090909090909092 1.5 0.0 0.09090909090909091 0.18181818181818182 0.2727272727272727 0.36363636363636365 0.45454545454545453 0.5454545454545454 0.6363636363636364 0.7272727272727273 0.8181818181818182 0.9090909090909091 1.0 0.03333333333333333 0.12424242424242424 0.21515151515151515 0.30606060606060603 0.396969696969697 0.48787878787878786 0.5787878787878787 0.6696969696969697 0.7606060606060606 0.8515151515151516 0.9424242424242424 1.0333333333333334 0.06666666666666667 0.1575757575757576 0.24848484848484848 0.33939393939393936 0.4303030303030303 0.5212121212121212 0.6121212121212121 0.703030303030303 0.793939393939394 0.8848484848484849 0.9757575757575757 1.0666666666666667 0.1 0.19090909090909092 0.28181818181818186 0.3727272727272727 0.4636363636363636 0.5545454545454546 0.6454545454545454 0.7363636363636363 0.8272727272727273 0.9181818181818182 1.009090909090909 1.1 0.13333333333333333 0.22424242424242424 0.3151515151515152 0.406060606060606 0.49696969696969695 0.5878787878787879 0.6787878787878787 0.7696969696969697 0.8606060606060606 0.9515151515151515 1.0424242424242425 1.1333333333333333 0.16666666666666666 0.25757575757575757 0.3484848484848485 0.43939393939393934 0.5303030303030303 0.6212121212121212 0.712121212121212 0.803030303030303 0.8939393939393939 0.9848484848484849 1.0757575757575757 1.1666666666666667 0.2 0.2909090909090909 0.38181818181818183 0.4727272727272727 0.5636363636363637 0.6545454545454545 0.7454545454545454 0.8363636363636364 0.9272727272727272 1.0181818181818183 1.1090909090909091 1.2 0.23333333333333334 0.3242424242424242 0.41515151515151516 0.5060606060606061 0.5969696969696969 0.6878787878787879 0.7787878787878788 0.8696969696969696 0.9606060606060607 1.0515151515151515 1.1424242424242423 1.2333333333333334 0.26666666666666666 0.35757575757575755 0.4484848484848485 0.5393939393939393 0.6303030303030304 0.7212121212121212 0.812121212121212 0.9030303030303031 0.9939393939393939 1.084848484848485 1.1757575757575758 1.2666666666666666 0.3 0.3909090909090909 0.4818181818181818 0.5727272727272728 0.6636363636363636 0.7545454545454545 0.8454545454545455 0.9363636363636363 1.0272727272727273 1.1181818181818182 1.209090909090909 1.3 0.3333333333333333 0.4242424242424242 0.5151515151515151 0.606060606060606 0.696969696969697 0.7878787878787878 0.8787878787878787 0.9696969696969697 1.0606060606060606 1.1515151515151516 1.2424242424242424 1.3333333333333333 0.36666666666666664 0.4575757575757575 0.5484848484848485 0.6393939393939394 0.7303030303030302 0.8212121212121212 0.9121212121212121 1.003030303030303 1.093939393939394 1.1848484848484848 1.2757575757575756 1.3666666666666667 0.4 0.49090909090909096 0.5818181818181818 0.6727272727272727 0.7636363636363637 0.8545454545454545 0.9454545454545454 1.0363636363636364 1.1272727272727274 1.2181818181818183 1.309090909090909 1.4 0.43333333333333335 0.5242424242424243 0.6151515151515152 0.706060606060606 0.796969696969697 0.8878787878787879 0.9787878787878788 1.0696969696969698 1.1606060606060606 1.2515151515151515 1.3424242424242423 1.4333333333333333 0.4666666666666667 0.5575757575757576 0.6484848484848484 0.7393939393939394 0.8303030303030303 0.9212121212121211 1.0121212121212122 1.103030303030303 1.1939393939393939 1.284848484848485 1.3757575757575757 1.4666666666666668 0.5 0.5909090909090909 0.6818181818181819 0.7727272727272727 0.8636363636363636 0.9545454545454546 1.0454545454545454 1.1363636363636362 1.2272727272727273 1.3181818181818183 1.4090909090909092 1.5 0.0 0.09090909090909091 0.18181818181818182 0.2727272727272727 0.36363636363636365 0.45454545454545453 0.5454545454545454 0.6363636363636364 0.7272727272727273 0.8181818181818182 0.9090909090909091 1.0 0.03333333333333333 0.12424242424242424 0.21515151515151515 0.30606060606060603 0.396969696969697 0.48787878787878786 0.5787878787878787 0.6696969696969697 0.7606060606060606 0.8515151515151516 0.9424242424242424 1.0333333333333334 0.06666666666666667 0.1575757575757576 0.24848484848484848 0.33939393939393936 0.4303030303030303 0.5212121212121212 0.6121212121212121 0.703030303030303 0.793939393939394 0.8848484848484849 0.9757575757575757 1.0666666666666667 0.1 0.19090909090909092 0.28181818181818186 0.3727272727272727 0.4636363636363636 0.5545454545454546 0.6454545454545454 0.7363636363636363 0.8272727272727273 0.9181818181818182 1.009090909090909 1.1 0.13333333333333333 0.22424242424242424 0.3151515151515152 0.406060606060606 0.49696969696969695 0.5878787878787879 0.6787878787878787 0.7696969696969697 0.8606060606060606 0.9515151515151515 1.0424242424242425 1.1333333333333333 0.16666666666666666 0.25757575757575757 0.3484848484848485 0.43939393939393934 0.5303030303030303 0.6212121212121212 0.712121212121212 0.803030303030303 0.8939393939393939 0.9848484848484849 1.0757575757575757 1.1666666666666667 0.2 0.2909090909090909 0.38181818181818183 0.4727272727272727 0.5636363636363637 0.6545454545454545 0.7454545454545454 0.8363636363636364 0.9272727272727272 1.0181818181818183 1.1090909090909091 1.2 0.23333333333333334 0.3242424242424242 0.41515151515151516 0.5060606060606061 0.5969696969696969 0.6878787878787879 0.7787878787878788 0.8696969696969696 0.9606060606060607 1.0515151515151515 1.1424242424242423 1.2333333333333334 0.26666666666666666 0.35757575757575755 0.4484848484848485 0.5393939393939393 0.6303030303030304 0.7212121212121212 0.812121212121212 0.9030303030303031 0.9939393939393939 1.084848484848485 1.1757575757575758 1.2666666666666666 0.3 0.3909090909090909 0.4818181818181818 0.5727272727272728 0.6636363636363636 0.7545454545454545 0.8454545454545455 0.9363636363636363 1.0272727272727273 1.1181818181818182 1.209090909090909 1.3 0.3333333333333333 0.4242424242424242 0.5151515151515151 0.606060606060606 0.696969696969697 0.7878787878787878 0.8787878787878787 0.9696969696969697 1.0606060606060606 1.1515151515151516 1.2424242424242424 1.3333333333333333 0.36666666666666664 0.4575757575757575 0.5484848484848485 0.6393939393939394 0.7303030303030302 0.8212121212121212 0.9121212121212121 1.003030303030303 1.093939393939394 1.1848484848484848 1.2757575757575756 1.3666666666666667 0.4 0.49090909090909096 0.5818181818181818 0.6727272727272727 0.7636363636363637 0.8545454545454545 0.9454545454545454 1.0363636363636364 1.1272727272727274 1.2181818181818183 1.309090909090909 1.4 0.43333333333333335 0.5242424242424243 0.6151515151515152 0.706060606060606 0.796969696969697 0.8878787878787879 0.9787878787878788 1.0696969696969698 1.1606060606060606 1.2515151515151515 1.3424242424242423 1.4333333333333333 0.4666666666666667 0.5575757575757576 0.6484848484848484 0.7393939393939394 0.8303030303030303 0.9212121212121211 1.0121212121212122 1.103030303030303 1.1939393939393939 1.284848484848485 1.3757575757575757 1.4666666666666668 0.5 0.5909090909090909 0.6818181818181819 0.7727272727272727 0.8636363636363636 0.9545454545454546 1.0454545454545454 1.1363636363636362 1.2272727272727273 1.3181818181818183 1.4090909090909092 1.5 0.0 0.09090909090909091 0.18181818181818182 0.2727272727272727 0.36363636363636365 0.45454545454545453 0.5454545454545454 0.6363636363636364 0.7272727272727273 0.8181818181818182 0.9090909090909091 1.0 0.03333333333333333 0.12424242424242424 0.21515151515151515 0.30606060606060603 0.396969696969697 0.48787878787878786 0.5787878787878787 0.6696969696969697 0.7606060606060606 0.8515151515151516 0.9424242424242424 1.0333333333333334 0.06666666666666667 0.1575757575757576 0.24848484848484848 0.33939393939393936 0.4303030303030303 0.5212121212121212 0.6121212121212121 0.703030303030303 0.793939393939394 0.8848484848484849 0.9757575757575757 1.0666666666666667 0.1 0.19090909090909092 0.28181818181818186 0.3727272727272727 0.4636363636363636 0.5545454545454546 0.6454545454545454 0.7363636363636363 0.8272727272727273 0.9181818181818182 1.009090909090909 1.1 0.13333333333333333 0.22424242424242424 0.3151515151515152 0.406060606060606 0.49696969696969695 0.5878787878787879 0.6787878787878787 0.7696969696969697 0.8606060606060606 0.9515151515151515 1.0424242424242425 1.1333333333333333 0.16666666666666666 0.25757575757575757 0.3484848484848485 0.43939393939393934 0.5303030303030303 0.6212121212121212 0.712121212121212 0.803030303030303 0.8939393939393939 0.9848484848484849 1.0757575757575757 1.1666666666666667 0.2 0.2909090909090909 0.38181818181818183 0.4727272727272727 0.5636363636363637 0.6545454545454545 0.7454545454545454 0.8363636363636364 0.9272727272727272 1.0181818181818183 1.1090909090909091 1.2 0.23333333333333334 0.3242424242424242 0.41515151515151516 0.5060606060606061 0.5969696969696969 0.6878787878787879 0.7787878787878788 0.8696969696969696 0.9606060606060607 1.0515151515151515 1.1424242424242423 1.2333333333333334 0.26666666666666666 0.35757575757575755 0.4484848484848485 0.5393939393939393 0.6303030303030304 0.7212121212121212 0.812121212121212 0.9030303030303031 0.9939393939393939 1.084848484848485 1.1757575757575758 1.2666666666666666 0.3 0.3909090909090909 0.4818181818181818 0.5727272727272728 0.6636363636363636 0.7545454545454545 0.8454545454545455 0.9363636363636363 1.0272727272727273 1.1181818181818182 1.209090909090909 1.3 0.3333333333333333 0.4242424242424242 0.5151515151515151 0.606060606060606 0.696969696969697 0.7878787878787878 0.8787878787878787 0.9696969696969697 1.0606060606060606 1.1515151515151516 1.2424242424242424 1.3333333333333333 0.36666666666666664 0.4575757575757575 0.5484848484848485 0.6393939393939394 0.7303030303030302 0.8212121212121212 0.9121212121212121 1.003030303030303 1.093939393939394 1.1848484848484848 1.2757575757575756 1.3666666666666667 0.4 0.49090909090909096 0.5818181818181818 0.6727272727272727 0.7636363636363637 0.8545454545454545 0.9454545454545454 1.0363636363636364 1.1272727272727274 1.2181818181818183 1.309090909090909 1.4 0.43333333333333335 0.5242424242424243 0.6151515151515152 0.706060606060606 0.796969696969697 0.8878787878787879 0.9787878787878788 1.0696969696969698 1.1606060606060606 1.2515151515151515 1.3424242424242423 1.4333333333333333 0.4666666666666667 0.5575757575757576 0.6484848484848484 0.7393939393939394 0.8303030303030303 0.9212121212121211 1.0121212121212122 1.103030303030303 1.1939393939393939 1.284848484848485 1.3757575757575757 1.4666666666666668 0.5 0.5909090909090909 0.6818181818181819 0.7727272727272727 0.8636363636363636 0.9545454545454546 1.0454545454545454 1.1363636363636362 1.2272727272727273 1.3181818181818183 1.4090909090909092 1.5; + 0.0 0.018181818181818184 0.03636363636363637 0.05454545454545454 0.07272727272727274 0.09090909090909091 0.10909090909090909 0.1272727272727273 0.14545454545454548 0.16363636363636366 0.18181818181818182 0.2 0.06666666666666667 0.08484848484848485 0.10303030303030303 0.12121212121212122 0.1393939393939394 0.1575757575757576 0.17575757575757575 0.19393939393939397 0.21212121212121215 0.23030303030303034 0.24848484848484848 0.26666666666666666 0.13333333333333333 0.15151515151515152 0.1696969696969697 0.18787878787878787 0.20606060606060606 0.22424242424242424 0.24242424242424243 0.2606060606060606 0.2787878787878788 0.296969696969697 0.3151515151515152 0.33333333333333337 0.2 0.2181818181818182 0.2363636363636364 0.2545454545454546 0.27272727272727276 0.2909090909090909 0.3090909090909091 0.32727272727272727 0.34545454545454546 0.36363636363636365 0.38181818181818183 0.4 0.26666666666666666 0.28484848484848485 0.30303030303030304 0.3212121212121212 0.3393939393939394 0.35757575757575755 0.37575757575757573 0.3939393939393939 0.4121212121212121 0.4303030303030303 0.4484848484848485 0.4666666666666667 0.3333333333333333 0.3515151515151515 0.3696969696969697 0.3878787878787879 0.40606060606060607 0.4242424242424242 0.4424242424242424 0.4606060606060606 0.47878787878787876 0.49696969696969695 0.5151515151515151 0.5333333333333333 0.4 0.4181818181818182 0.4363636363636364 0.4545454545454546 0.4727272727272728 0.49090909090909096 0.5090909090909091 0.5272727272727273 0.5454545454545455 0.5636363636363637 0.5818181818181818 0.6000000000000001 0.4666666666666667 0.48484848484848486 0.503030303030303 0.5212121212121212 0.5393939393939394 0.5575757575757576 0.5757575757575758 0.593939393939394 0.6121212121212122 0.6303030303030304 0.6484848484848484 0.6666666666666667 0.5333333333333333 0.5515151515151515 0.5696969696969697 0.5878787878787879 0.6060606060606061 0.6242424242424243 0.6424242424242425 0.6606060606060606 0.6787878787878788 0.696969696969697 0.7151515151515151 0.7333333333333334 0.6 0.6181818181818182 0.6363636363636364 0.6545454545454545 0.6727272727272727 0.6909090909090909 0.7090909090909091 0.7272727272727273 0.7454545454545455 0.7636363636363637 0.7818181818181817 0.8 0.6666666666666666 0.6848484848484848 0.703030303030303 0.7212121212121212 0.7393939393939394 0.7575757575757576 0.7757575757575758 0.793939393939394 0.8121212121212121 0.8303030303030303 0.8484848484848484 0.8666666666666667 0.7333333333333333 0.7515151515151515 0.7696969696969697 0.7878787878787878 0.806060606060606 0.8242424242424242 0.8424242424242424 0.8606060606060606 0.8787878787878788 0.896969696969697 0.915151515151515 0.9333333333333333 0.8 0.8181818181818182 0.8363636363636364 0.8545454545454546 0.8727272727272728 0.890909090909091 0.9090909090909092 0.9272727272727274 0.9454545454545455 0.9636363636363637 0.9818181818181819 1.0 0.8666666666666667 0.8848484848484849 0.9030303030303031 0.9212121212121213 0.9393939393939394 0.9575757575757576 0.9757575757575758 0.993939393939394 1.0121212121212122 1.0303030303030303 1.0484848484848486 1.0666666666666667 0.9333333333333333 0.9515151515151515 0.9696969696969697 0.9878787878787879 1.006060606060606 1.0242424242424242 1.0424242424242425 1.0606060606060606 1.0787878787878789 1.096969696969697 1.1151515151515152 1.1333333333333333 1.0 1.018181818181818 1.0363636363636364 1.0545454545454545 1.0727272727272728 1.0909090909090908 1.1090909090909091 1.1272727272727272 1.1454545454545455 1.1636363636363636 1.1818181818181819 1.2 0.0 0.018181818181818184 0.03636363636363637 0.05454545454545454 0.07272727272727274 0.09090909090909091 0.10909090909090909 0.1272727272727273 0.14545454545454548 0.16363636363636366 0.18181818181818182 0.2 0.06666666666666667 0.08484848484848485 0.10303030303030303 0.12121212121212122 0.1393939393939394 0.1575757575757576 0.17575757575757575 0.19393939393939397 0.21212121212121215 0.23030303030303034 0.24848484848484848 0.26666666666666666 0.13333333333333333 0.15151515151515152 0.1696969696969697 0.18787878787878787 0.20606060606060606 0.22424242424242424 0.24242424242424243 0.2606060606060606 0.2787878787878788 0.296969696969697 0.3151515151515152 0.33333333333333337 0.2 0.2181818181818182 0.2363636363636364 0.2545454545454546 0.27272727272727276 0.2909090909090909 0.3090909090909091 0.32727272727272727 0.34545454545454546 0.36363636363636365 0.38181818181818183 0.4 0.26666666666666666 0.28484848484848485 0.30303030303030304 0.3212121212121212 0.3393939393939394 0.35757575757575755 0.37575757575757573 0.3939393939393939 0.4121212121212121 0.4303030303030303 0.4484848484848485 0.4666666666666667 0.3333333333333333 0.3515151515151515 0.3696969696969697 0.3878787878787879 0.40606060606060607 0.4242424242424242 0.4424242424242424 0.4606060606060606 0.47878787878787876 0.49696969696969695 0.5151515151515151 0.5333333333333333 0.4 0.4181818181818182 0.4363636363636364 0.4545454545454546 0.4727272727272728 0.49090909090909096 0.5090909090909091 0.5272727272727273 0.5454545454545455 0.5636363636363637 0.5818181818181818 0.6000000000000001 0.4666666666666667 0.48484848484848486 0.503030303030303 0.5212121212121212 0.5393939393939394 0.5575757575757576 0.5757575757575758 0.593939393939394 0.6121212121212122 0.6303030303030304 0.6484848484848484 0.6666666666666667 0.5333333333333333 0.5515151515151515 0.5696969696969697 0.5878787878787879 0.6060606060606061 0.6242424242424243 0.6424242424242425 0.6606060606060606 0.6787878787878788 0.696969696969697 0.7151515151515151 0.7333333333333334 0.6 0.6181818181818182 0.6363636363636364 0.6545454545454545 0.6727272727272727 0.6909090909090909 0.7090909090909091 0.7272727272727273 0.7454545454545455 0.7636363636363637 0.7818181818181817 0.8 0.6666666666666666 0.6848484848484848 0.703030303030303 0.7212121212121212 0.7393939393939394 0.7575757575757576 0.7757575757575758 0.793939393939394 0.8121212121212121 0.8303030303030303 0.8484848484848484 0.8666666666666667 0.7333333333333333 0.7515151515151515 0.7696969696969697 0.7878787878787878 0.806060606060606 0.8242424242424242 0.8424242424242424 0.8606060606060606 0.8787878787878788 0.896969696969697 0.915151515151515 0.9333333333333333 0.8 0.8181818181818182 0.8363636363636364 0.8545454545454546 0.8727272727272728 0.890909090909091 0.9090909090909092 0.9272727272727274 0.9454545454545455 0.9636363636363637 0.9818181818181819 1.0 0.8666666666666667 0.8848484848484849 0.9030303030303031 0.9212121212121213 0.9393939393939394 0.9575757575757576 0.9757575757575758 0.993939393939394 1.0121212121212122 1.0303030303030303 1.0484848484848486 1.0666666666666667 0.9333333333333333 0.9515151515151515 0.9696969696969697 0.9878787878787879 1.006060606060606 1.0242424242424242 1.0424242424242425 1.0606060606060606 1.0787878787878789 1.096969696969697 1.1151515151515152 1.1333333333333333 1.0 1.018181818181818 1.0363636363636364 1.0545454545454545 1.0727272727272728 1.0909090909090908 1.1090909090909091 1.1272727272727272 1.1454545454545455 1.1636363636363636 1.1818181818181819 1.2 0.0 0.018181818181818184 0.03636363636363637 0.05454545454545454 0.07272727272727274 0.09090909090909091 0.10909090909090909 0.1272727272727273 0.14545454545454548 0.16363636363636366 0.18181818181818182 0.2 0.06666666666666667 0.08484848484848485 0.10303030303030303 0.12121212121212122 0.1393939393939394 0.1575757575757576 0.17575757575757575 0.19393939393939397 0.21212121212121215 0.23030303030303034 0.24848484848484848 0.26666666666666666 0.13333333333333333 0.15151515151515152 0.1696969696969697 0.18787878787878787 0.20606060606060606 0.22424242424242424 0.24242424242424243 0.2606060606060606 0.2787878787878788 0.296969696969697 0.3151515151515152 0.33333333333333337 0.2 0.2181818181818182 0.2363636363636364 0.2545454545454546 0.27272727272727276 0.2909090909090909 0.3090909090909091 0.32727272727272727 0.34545454545454546 0.36363636363636365 0.38181818181818183 0.4 0.26666666666666666 0.28484848484848485 0.30303030303030304 0.3212121212121212 0.3393939393939394 0.35757575757575755 0.37575757575757573 0.3939393939393939 0.4121212121212121 0.4303030303030303 0.4484848484848485 0.4666666666666667 0.3333333333333333 0.3515151515151515 0.3696969696969697 0.3878787878787879 0.40606060606060607 0.4242424242424242 0.4424242424242424 0.4606060606060606 0.47878787878787876 0.49696969696969695 0.5151515151515151 0.5333333333333333 0.4 0.4181818181818182 0.4363636363636364 0.4545454545454546 0.4727272727272728 0.49090909090909096 0.5090909090909091 0.5272727272727273 0.5454545454545455 0.5636363636363637 0.5818181818181818 0.6000000000000001 0.4666666666666667 0.48484848484848486 0.503030303030303 0.5212121212121212 0.5393939393939394 0.5575757575757576 0.5757575757575758 0.593939393939394 0.6121212121212122 0.6303030303030304 0.6484848484848484 0.6666666666666667 0.5333333333333333 0.5515151515151515 0.5696969696969697 0.5878787878787879 0.6060606060606061 0.6242424242424243 0.6424242424242425 0.6606060606060606 0.6787878787878788 0.696969696969697 0.7151515151515151 0.7333333333333334 0.6 0.6181818181818182 0.6363636363636364 0.6545454545454545 0.6727272727272727 0.6909090909090909 0.7090909090909091 0.7272727272727273 0.7454545454545455 0.7636363636363637 0.7818181818181817 0.8 0.6666666666666666 0.6848484848484848 0.703030303030303 0.7212121212121212 0.7393939393939394 0.7575757575757576 0.7757575757575758 0.793939393939394 0.8121212121212121 0.8303030303030303 0.8484848484848484 0.8666666666666667 0.7333333333333333 0.7515151515151515 0.7696969696969697 0.7878787878787878 0.806060606060606 0.8242424242424242 0.8424242424242424 0.8606060606060606 0.8787878787878788 0.896969696969697 0.915151515151515 0.9333333333333333 0.8 0.8181818181818182 0.8363636363636364 0.8545454545454546 0.8727272727272728 0.890909090909091 0.9090909090909092 0.9272727272727274 0.9454545454545455 0.9636363636363637 0.9818181818181819 1.0 0.8666666666666667 0.8848484848484849 0.9030303030303031 0.9212121212121213 0.9393939393939394 0.9575757575757576 0.9757575757575758 0.993939393939394 1.0121212121212122 1.0303030303030303 1.0484848484848486 1.0666666666666667 0.9333333333333333 0.9515151515151515 0.9696969696969697 0.9878787878787879 1.006060606060606 1.0242424242424242 1.0424242424242425 1.0606060606060606 1.0787878787878789 1.096969696969697 1.1151515151515152 1.1333333333333333 1.0 1.018181818181818 1.0363636363636364 1.0545454545454545 1.0727272727272728 1.0909090909090908 1.1090909090909091 1.1272727272727272 1.1454545454545455 1.1636363636363636 1.1818181818181819 1.2 0.0 0.018181818181818184 0.03636363636363637 0.05454545454545454 0.07272727272727274 0.09090909090909091 0.10909090909090909 0.1272727272727273 0.14545454545454548 0.16363636363636366 0.18181818181818182 0.2 0.06666666666666667 0.08484848484848485 0.10303030303030303 0.12121212121212122 0.1393939393939394 0.1575757575757576 0.17575757575757575 0.19393939393939397 0.21212121212121215 0.23030303030303034 0.24848484848484848 0.26666666666666666 0.13333333333333333 0.15151515151515152 0.1696969696969697 0.18787878787878787 0.20606060606060606 0.22424242424242424 0.24242424242424243 0.2606060606060606 0.2787878787878788 0.296969696969697 0.3151515151515152 0.33333333333333337 0.2 0.2181818181818182 0.2363636363636364 0.2545454545454546 0.27272727272727276 0.2909090909090909 0.3090909090909091 0.32727272727272727 0.34545454545454546 0.36363636363636365 0.38181818181818183 0.4 0.26666666666666666 0.28484848484848485 0.30303030303030304 0.3212121212121212 0.3393939393939394 0.35757575757575755 0.37575757575757573 0.3939393939393939 0.4121212121212121 0.4303030303030303 0.4484848484848485 0.4666666666666667 0.3333333333333333 0.3515151515151515 0.3696969696969697 0.3878787878787879 0.40606060606060607 0.4242424242424242 0.4424242424242424 0.4606060606060606 0.47878787878787876 0.49696969696969695 0.5151515151515151 0.5333333333333333 0.4 0.4181818181818182 0.4363636363636364 0.4545454545454546 0.4727272727272728 0.49090909090909096 0.5090909090909091 0.5272727272727273 0.5454545454545455 0.5636363636363637 0.5818181818181818 0.6000000000000001 0.4666666666666667 0.48484848484848486 0.503030303030303 0.5212121212121212 0.5393939393939394 0.5575757575757576 0.5757575757575758 0.593939393939394 0.6121212121212122 0.6303030303030304 0.6484848484848484 0.6666666666666667 0.5333333333333333 0.5515151515151515 0.5696969696969697 0.5878787878787879 0.6060606060606061 0.6242424242424243 0.6424242424242425 0.6606060606060606 0.6787878787878788 0.696969696969697 0.7151515151515151 0.7333333333333334 0.6 0.6181818181818182 0.6363636363636364 0.6545454545454545 0.6727272727272727 0.6909090909090909 0.7090909090909091 0.7272727272727273 0.7454545454545455 0.7636363636363637 0.7818181818181817 0.8 0.6666666666666666 0.6848484848484848 0.703030303030303 0.7212121212121212 0.7393939393939394 0.7575757575757576 0.7757575757575758 0.793939393939394 0.8121212121212121 0.8303030303030303 0.8484848484848484 0.8666666666666667 0.7333333333333333 0.7515151515151515 0.7696969696969697 0.7878787878787878 0.806060606060606 0.8242424242424242 0.8424242424242424 0.8606060606060606 0.8787878787878788 0.896969696969697 0.915151515151515 0.9333333333333333 0.8 0.8181818181818182 0.8363636363636364 0.8545454545454546 0.8727272727272728 0.890909090909091 0.9090909090909092 0.9272727272727274 0.9454545454545455 0.9636363636363637 0.9818181818181819 1.0 0.8666666666666667 0.8848484848484849 0.9030303030303031 0.9212121212121213 0.9393939393939394 0.9575757575757576 0.9757575757575758 0.993939393939394 1.0121212121212122 1.0303030303030303 1.0484848484848486 1.0666666666666667 0.9333333333333333 0.9515151515151515 0.9696969696969697 0.9878787878787879 1.006060606060606 1.0242424242424242 1.0424242424242425 1.0606060606060606 1.0787878787878789 1.096969696969697 1.1151515151515152 1.1333333333333333 1.0 1.018181818181818 1.0363636363636364 1.0545454545454545 1.0727272727272728 1.0909090909090908 1.1090909090909091 1.1272727272727272 1.1454545454545455 1.1636363636363636 1.1818181818181819 1.2; + 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.06666666666666667 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.13333333333333333 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.2 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.26666666666666666 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.3333333333333333 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.4666666666666667 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.5333333333333333 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.6666666666666666 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.7333333333333333 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.8666666666666667 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 0.9333333333333333 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.092709445701687 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.15937611236835367 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.22604277903502035 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.292709445701687 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.35937611236835365 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.4260427790350203 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.492709445701687 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.5593761123683537 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.6260427790350204 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.692709445701687 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.7593761123683537 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8260427790350203 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.8927094457016871 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 0.9593761123683537 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.0260427790350204 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 1.092709445701687 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.185418891403374 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.2520855580700407 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.31875222473670733 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.38541889140337404 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.4520855580700407 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.5187522247367073 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.585418891403374 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.6520855580700406 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.7187522247367073 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.785418891403374 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.8520855580700406 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.9187522247367073 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 0.985418891403374 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.0520855580700408 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.1187522247367074 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 1.185418891403374 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.278128337105061 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.34479500377172767 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.4114616704383943 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.47812833710506103 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.5447950037717277 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.6114616704383944 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.678128337105061 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.7447950037717277 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.8114616704383943 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.878128337105061 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 0.9447950037717276 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0114616704383943 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.0781283371050612 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.1447950037717276 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.2114616704383945 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061 1.278128337105061] + + shape = extrude_geometry((p1, p2, p3); direction, particle_spacing=0.1, n_extrude=4, + density=1000.0, tlsph=true) + + @test shape.coordinates ≈ expected_coords + end + + @testset verbose=true "Shifted Particles" begin + p1 = [0.0, 0.0, 0.0] + p2 = [0.5, 1.0, 0.0] + p3 = [1.0, 0.2, 0.0] + + direction = [0.0, 0.0, 1.0] + + expected_coords = [0.0713897135595439 0.1615839068026347 0.25177810004572554 0.3419722932888163 0.43216648653190715 0.5223606797749979 0.6125548730180886 0.7027490662611794 0.7929432595042704 0.8831374527473611 0.9733316459904519 0.11277868087318066 0.20297287411627146 0.29316706735936227 0.383361260602453 0.47355545384554387 0.5637496470886346 0.6539438403317254 0.7441380335748161 0.8343322268179071 0.9245264200609978 1.0147206133040885 0.15416764818681739 0.2443618414299082 0.334556034672999 0.42475022791608974 0.5149444211591806 0.6051386144022713 0.6953328076453621 0.7855270008884528 0.8757211941315438 0.9659153873746346 1.0561095806177252 0.19555661550045414 0.28575080874354497 0.3759450019866357 0.46613919522972647 0.5563333884728173 0.6465275817159081 0.7367217749589988 0.8269159682020896 0.9171101614451805 1.0073043546882714 1.0974985479313621 0.2369455828140909 0.3271397760571817 0.4173339693002725 0.5075281625433632 0.5977223557864542 0.6879165490295449 0.7781107422726357 0.8683049355157264 0.9584991287588174 1.0486933220019081 1.1388875152449989 0.2783345501277276 0.3685287433708184 0.4587229366139092 0.5489171298569999 0.6391113231000909 0.7293055163431816 0.8194997095862724 0.9096939028293631 0.9998880960724541 1.0900822893155449 1.1802764825586356 0.3197235174413644 0.4099177106844552 0.500111903927546 0.5903060971706368 0.6805002904137276 0.7706944836568184 0.8608886768999091 0.9510828701429999 1.0412770633860908 1.1314712566291816 1.2216654498722723 0.36111248475500113 0.45130667799809193 0.5415008712411827 0.6316950644842735 0.7218892577273643 0.8120834509704551 0.9022776442135458 0.9924718374566366 1.0826660306997276 1.1728602239428183 1.263054417185909 0.40250145206863785 0.49269564531172866 0.5828898385548195 0.6730840317979102 0.7632782250410011 0.8534724182840918 0.9436666115271826 1.0338608047702733 1.1240549980133643 1.214249191256455 1.3044433844995458 0.4438904193822747 0.5340846126253656 0.6242788058684563 0.714472999111547 0.8046671923546379 0.8948613855977287 0.9850555788408194 1.07524977208391 1.1654439653270012 1.255638158570092 1.3458323518131827 0.4852793866959113 0.5754735799390021 0.6656677731820929 0.7558619664251837 0.8460561596682745 0.9362503529113653 1.026444546154456 1.1166387393975468 1.2068329326406377 1.2970271258837285 1.3872213191268192 0.5266683540095481 0.616862547252639 0.7070567404957298 0.7972509337388205 0.8874451269819114 0.9776393202250021 1.0678335134680927 1.1580277067111835 1.2482218999542747 1.3384160931973654 1.4286102864404562 0.0713897135595439 0.1615839068026347 0.25177810004572554 0.3419722932888163 0.43216648653190715 0.5223606797749979 0.6125548730180886 0.7027490662611794 0.7929432595042704 0.8831374527473611 0.9733316459904519 0.11277868087318066 0.20297287411627146 0.29316706735936227 0.383361260602453 0.47355545384554387 0.5637496470886346 0.6539438403317254 0.7441380335748161 0.8343322268179071 0.9245264200609978 1.0147206133040885 0.15416764818681739 0.2443618414299082 0.334556034672999 0.42475022791608974 0.5149444211591806 0.6051386144022713 0.6953328076453621 0.7855270008884528 0.8757211941315438 0.9659153873746346 1.0561095806177252 0.19555661550045414 0.28575080874354497 0.3759450019866357 0.46613919522972647 0.5563333884728173 0.6465275817159081 0.7367217749589988 0.8269159682020896 0.9171101614451805 1.0073043546882714 1.0974985479313621 0.2369455828140909 0.3271397760571817 0.4173339693002725 0.5075281625433632 0.5977223557864542 0.6879165490295449 0.7781107422726357 0.8683049355157264 0.9584991287588174 1.0486933220019081 1.1388875152449989 0.2783345501277276 0.3685287433708184 0.4587229366139092 0.5489171298569999 0.6391113231000909 0.7293055163431816 0.8194997095862724 0.9096939028293631 0.9998880960724541 1.0900822893155449 1.1802764825586356 0.3197235174413644 0.4099177106844552 0.500111903927546 0.5903060971706368 0.6805002904137276 0.7706944836568184 0.8608886768999091 0.9510828701429999 1.0412770633860908 1.1314712566291816 1.2216654498722723 0.36111248475500113 0.45130667799809193 0.5415008712411827 0.6316950644842735 0.7218892577273643 0.8120834509704551 0.9022776442135458 0.9924718374566366 1.0826660306997276 1.1728602239428183 1.263054417185909 0.40250145206863785 0.49269564531172866 0.5828898385548195 0.6730840317979102 0.7632782250410011 0.8534724182840918 0.9436666115271826 1.0338608047702733 1.1240549980133643 1.214249191256455 1.3044433844995458 0.4438904193822747 0.5340846126253656 0.6242788058684563 0.714472999111547 0.8046671923546379 0.8948613855977287 0.9850555788408194 1.07524977208391 1.1654439653270012 1.255638158570092 1.3458323518131827 0.4852793866959113 0.5754735799390021 0.6656677731820929 0.7558619664251837 0.8460561596682745 0.9362503529113653 1.026444546154456 1.1166387393975468 1.2068329326406377 1.2970271258837285 1.3872213191268192 0.5266683540095481 0.616862547252639 0.7070567404957298 0.7972509337388205 0.8874451269819114 0.9776393202250021 1.0678335134680927 1.1580277067111835 1.2482218999542747 1.3384160931973654 1.4286102864404562 0.0713897135595439 0.1615839068026347 0.25177810004572554 0.3419722932888163 0.43216648653190715 0.5223606797749979 0.6125548730180886 0.7027490662611794 0.7929432595042704 0.8831374527473611 0.9733316459904519 0.11277868087318066 0.20297287411627146 0.29316706735936227 0.383361260602453 0.47355545384554387 0.5637496470886346 0.6539438403317254 0.7441380335748161 0.8343322268179071 0.9245264200609978 1.0147206133040885 0.15416764818681739 0.2443618414299082 0.334556034672999 0.42475022791608974 0.5149444211591806 0.6051386144022713 0.6953328076453621 0.7855270008884528 0.8757211941315438 0.9659153873746346 1.0561095806177252 0.19555661550045414 0.28575080874354497 0.3759450019866357 0.46613919522972647 0.5563333884728173 0.6465275817159081 0.7367217749589988 0.8269159682020896 0.9171101614451805 1.0073043546882714 1.0974985479313621 0.2369455828140909 0.3271397760571817 0.4173339693002725 0.5075281625433632 0.5977223557864542 0.6879165490295449 0.7781107422726357 0.8683049355157264 0.9584991287588174 1.0486933220019081 1.1388875152449989 0.2783345501277276 0.3685287433708184 0.4587229366139092 0.5489171298569999 0.6391113231000909 0.7293055163431816 0.8194997095862724 0.9096939028293631 0.9998880960724541 1.0900822893155449 1.1802764825586356 0.3197235174413644 0.4099177106844552 0.500111903927546 0.5903060971706368 0.6805002904137276 0.7706944836568184 0.8608886768999091 0.9510828701429999 1.0412770633860908 1.1314712566291816 1.2216654498722723 0.36111248475500113 0.45130667799809193 0.5415008712411827 0.6316950644842735 0.7218892577273643 0.8120834509704551 0.9022776442135458 0.9924718374566366 1.0826660306997276 1.1728602239428183 1.263054417185909 0.40250145206863785 0.49269564531172866 0.5828898385548195 0.6730840317979102 0.7632782250410011 0.8534724182840918 0.9436666115271826 1.0338608047702733 1.1240549980133643 1.214249191256455 1.3044433844995458 0.4438904193822747 0.5340846126253656 0.6242788058684563 0.714472999111547 0.8046671923546379 0.8948613855977287 0.9850555788408194 1.07524977208391 1.1654439653270012 1.255638158570092 1.3458323518131827 0.4852793866959113 0.5754735799390021 0.6656677731820929 0.7558619664251837 0.8460561596682745 0.9362503529113653 1.026444546154456 1.1166387393975468 1.2068329326406377 1.2970271258837285 1.3872213191268192 0.5266683540095481 0.616862547252639 0.7070567404957298 0.7972509337388205 0.8874451269819114 0.9776393202250021 1.0678335134680927 1.1580277067111835 1.2482218999542747 1.3384160931973654 1.4286102864404562 0.0713897135595439 0.1615839068026347 0.25177810004572554 0.3419722932888163 0.43216648653190715 0.5223606797749979 0.6125548730180886 0.7027490662611794 0.7929432595042704 0.8831374527473611 0.9733316459904519 0.11277868087318066 0.20297287411627146 0.29316706735936227 0.383361260602453 0.47355545384554387 0.5637496470886346 0.6539438403317254 0.7441380335748161 0.8343322268179071 0.9245264200609978 1.0147206133040885 0.15416764818681739 0.2443618414299082 0.334556034672999 0.42475022791608974 0.5149444211591806 0.6051386144022713 0.6953328076453621 0.7855270008884528 0.8757211941315438 0.9659153873746346 1.0561095806177252 0.19555661550045414 0.28575080874354497 0.3759450019866357 0.46613919522972647 0.5563333884728173 0.6465275817159081 0.7367217749589988 0.8269159682020896 0.9171101614451805 1.0073043546882714 1.0974985479313621 0.2369455828140909 0.3271397760571817 0.4173339693002725 0.5075281625433632 0.5977223557864542 0.6879165490295449 0.7781107422726357 0.8683049355157264 0.9584991287588174 1.0486933220019081 1.1388875152449989 0.2783345501277276 0.3685287433708184 0.4587229366139092 0.5489171298569999 0.6391113231000909 0.7293055163431816 0.8194997095862724 0.9096939028293631 0.9998880960724541 1.0900822893155449 1.1802764825586356 0.3197235174413644 0.4099177106844552 0.500111903927546 0.5903060971706368 0.6805002904137276 0.7706944836568184 0.8608886768999091 0.9510828701429999 1.0412770633860908 1.1314712566291816 1.2216654498722723 0.36111248475500113 0.45130667799809193 0.5415008712411827 0.6316950644842735 0.7218892577273643 0.8120834509704551 0.9022776442135458 0.9924718374566366 1.0826660306997276 1.1728602239428183 1.263054417185909 0.40250145206863785 0.49269564531172866 0.5828898385548195 0.6730840317979102 0.7632782250410011 0.8534724182840918 0.9436666115271826 1.0338608047702733 1.1240549980133643 1.214249191256455 1.3044433844995458 0.4438904193822747 0.5340846126253656 0.6242788058684563 0.714472999111547 0.8046671923546379 0.8948613855977287 0.9850555788408194 1.07524977208391 1.1654439653270012 1.255638158570092 1.3458323518131827 0.4852793866959113 0.5754735799390021 0.6656677731820929 0.7558619664251837 0.8460561596682745 0.9362503529113653 1.026444546154456 1.1166387393975468 1.2068329326406377 1.2970271258837285 1.3872213191268192 0.5266683540095481 0.616862547252639 0.7070567404957298 0.7972509337388205 0.8874451269819114 0.9776393202250021 1.0678335134680927 1.1580277067111835 1.2482218999542747 1.3384160931973654 1.4286102864404562; + 0.054527166306904996 0.07256600495552315 0.09060484360414131 0.10864368225275947 0.12668252090137763 0.1447213595499958 0.16276019819861395 0.18079903684723211 0.19883787549585027 0.21687671414446844 0.2349155527930866 0.1373051009341785 0.15534393958279666 0.17338277823141482 0.19142161688003298 0.20946045552865114 0.2274992941772693 0.24553813282588746 0.26357697147450565 0.28161581012312376 0.29965464877174197 0.3176934874203601 0.22008303556145198 0.23812187421007014 0.2561607128586883 0.27419955150730646 0.2922383901559246 0.3102772288045428 0.32831606745316094 0.3463549061017791 0.36439374475039726 0.3824325833990154 0.4004714220476336 0.3028609701887255 0.32089980883734365 0.3389386474859618 0.35697748613457997 0.37501632478319813 0.3930551634318163 0.41109400208043445 0.4291328407290526 0.44717167937767077 0.46521051802628893 0.4832493566749071 0.385638904815999 0.40367774346461716 0.4217165821132353 0.4397554207618535 0.45779425941047164 0.4758330980590898 0.49387193670770796 0.5119107753563261 0.5299496140049442 0.5479884526535624 0.5660272913021807 0.46841683944327245 0.4864556780918906 0.5044945167405088 0.5225333553891269 0.540572194037745 0.5586110326863633 0.5766498713349815 0.5946887099835996 0.6127275486322177 0.6307663872808359 0.6488052259294541 0.551194774070546 0.5692336127191642 0.5872724513677823 0.6053112900164004 0.6233501286650186 0.6413889673136368 0.6594278059622549 0.677466644610873 0.6955054832594912 0.7135443219081095 0.7315831605567276 0.6339727086978194 0.6520115473464376 0.6700503859950557 0.6880892246436738 0.706128063292292 0.7241669019409103 0.7422057405895284 0.7602445792381465 0.7782834178867647 0.7963222565353829 0.814361095184001 0.716750643325093 0.7347894819737111 0.7528283206223293 0.7708671592709475 0.7889059979195656 0.8069448365681837 0.8249836752168019 0.8430225138654202 0.8610613525140383 0.8791001911626564 0.8971390298112746 0.7995285779523665 0.8175674166009848 0.8356062552496029 0.853645093898221 0.8716839325468392 0.8897227711954574 0.9077616098440755 0.9258004484926936 0.9438392871413118 0.96187812578993 0.9799169644385481 0.8823065125796399 0.900345351228258 0.9183841898768762 0.9364230285254944 0.9544618671741125 0.9725007058227306 0.9905395444713488 1.008578383119967 1.0266172217685852 1.0446560604172033 1.0626948990658214 0.9650844472069134 0.9831232858555317 1.0011621245041498 1.0192009631527679 1.0372398018013862 1.0552786404500043 1.0733174790986224 1.0913563177472405 1.1093951563958586 1.127433995044477 1.145472833693095 0.054527166306904996 0.07256600495552315 0.09060484360414131 0.10864368225275947 0.12668252090137763 0.1447213595499958 0.16276019819861395 0.18079903684723211 0.19883787549585027 0.21687671414446844 0.2349155527930866 0.1373051009341785 0.15534393958279666 0.17338277823141482 0.19142161688003298 0.20946045552865114 0.2274992941772693 0.24553813282588746 0.26357697147450565 0.28161581012312376 0.29965464877174197 0.3176934874203601 0.22008303556145198 0.23812187421007014 0.2561607128586883 0.27419955150730646 0.2922383901559246 0.3102772288045428 0.32831606745316094 0.3463549061017791 0.36439374475039726 0.3824325833990154 0.4004714220476336 0.3028609701887255 0.32089980883734365 0.3389386474859618 0.35697748613457997 0.37501632478319813 0.3930551634318163 0.41109400208043445 0.4291328407290526 0.44717167937767077 0.46521051802628893 0.4832493566749071 0.385638904815999 0.40367774346461716 0.4217165821132353 0.4397554207618535 0.45779425941047164 0.4758330980590898 0.49387193670770796 0.5119107753563261 0.5299496140049442 0.5479884526535624 0.5660272913021807 0.46841683944327245 0.4864556780918906 0.5044945167405088 0.5225333553891269 0.540572194037745 0.5586110326863633 0.5766498713349815 0.5946887099835996 0.6127275486322177 0.6307663872808359 0.6488052259294541 0.551194774070546 0.5692336127191642 0.5872724513677823 0.6053112900164004 0.6233501286650186 0.6413889673136368 0.6594278059622549 0.677466644610873 0.6955054832594912 0.7135443219081095 0.7315831605567276 0.6339727086978194 0.6520115473464376 0.6700503859950557 0.6880892246436738 0.706128063292292 0.7241669019409103 0.7422057405895284 0.7602445792381465 0.7782834178867647 0.7963222565353829 0.814361095184001 0.716750643325093 0.7347894819737111 0.7528283206223293 0.7708671592709475 0.7889059979195656 0.8069448365681837 0.8249836752168019 0.8430225138654202 0.8610613525140383 0.8791001911626564 0.8971390298112746 0.7995285779523665 0.8175674166009848 0.8356062552496029 0.853645093898221 0.8716839325468392 0.8897227711954574 0.9077616098440755 0.9258004484926936 0.9438392871413118 0.96187812578993 0.9799169644385481 0.8823065125796399 0.900345351228258 0.9183841898768762 0.9364230285254944 0.9544618671741125 0.9725007058227306 0.9905395444713488 1.008578383119967 1.0266172217685852 1.0446560604172033 1.0626948990658214 0.9650844472069134 0.9831232858555317 1.0011621245041498 1.0192009631527679 1.0372398018013862 1.0552786404500043 1.0733174790986224 1.0913563177472405 1.1093951563958586 1.127433995044477 1.145472833693095 0.054527166306904996 0.07256600495552315 0.09060484360414131 0.10864368225275947 0.12668252090137763 0.1447213595499958 0.16276019819861395 0.18079903684723211 0.19883787549585027 0.21687671414446844 0.2349155527930866 0.1373051009341785 0.15534393958279666 0.17338277823141482 0.19142161688003298 0.20946045552865114 0.2274992941772693 0.24553813282588746 0.26357697147450565 0.28161581012312376 0.29965464877174197 0.3176934874203601 0.22008303556145198 0.23812187421007014 0.2561607128586883 0.27419955150730646 0.2922383901559246 0.3102772288045428 0.32831606745316094 0.3463549061017791 0.36439374475039726 0.3824325833990154 0.4004714220476336 0.3028609701887255 0.32089980883734365 0.3389386474859618 0.35697748613457997 0.37501632478319813 0.3930551634318163 0.41109400208043445 0.4291328407290526 0.44717167937767077 0.46521051802628893 0.4832493566749071 0.385638904815999 0.40367774346461716 0.4217165821132353 0.4397554207618535 0.45779425941047164 0.4758330980590898 0.49387193670770796 0.5119107753563261 0.5299496140049442 0.5479884526535624 0.5660272913021807 0.46841683944327245 0.4864556780918906 0.5044945167405088 0.5225333553891269 0.540572194037745 0.5586110326863633 0.5766498713349815 0.5946887099835996 0.6127275486322177 0.6307663872808359 0.6488052259294541 0.551194774070546 0.5692336127191642 0.5872724513677823 0.6053112900164004 0.6233501286650186 0.6413889673136368 0.6594278059622549 0.677466644610873 0.6955054832594912 0.7135443219081095 0.7315831605567276 0.6339727086978194 0.6520115473464376 0.6700503859950557 0.6880892246436738 0.706128063292292 0.7241669019409103 0.7422057405895284 0.7602445792381465 0.7782834178867647 0.7963222565353829 0.814361095184001 0.716750643325093 0.7347894819737111 0.7528283206223293 0.7708671592709475 0.7889059979195656 0.8069448365681837 0.8249836752168019 0.8430225138654202 0.8610613525140383 0.8791001911626564 0.8971390298112746 0.7995285779523665 0.8175674166009848 0.8356062552496029 0.853645093898221 0.8716839325468392 0.8897227711954574 0.9077616098440755 0.9258004484926936 0.9438392871413118 0.96187812578993 0.9799169644385481 0.8823065125796399 0.900345351228258 0.9183841898768762 0.9364230285254944 0.9544618671741125 0.9725007058227306 0.9905395444713488 1.008578383119967 1.0266172217685852 1.0446560604172033 1.0626948990658214 0.9650844472069134 0.9831232858555317 1.0011621245041498 1.0192009631527679 1.0372398018013862 1.0552786404500043 1.0733174790986224 1.0913563177472405 1.1093951563958586 1.127433995044477 1.145472833693095 0.054527166306904996 0.07256600495552315 0.09060484360414131 0.10864368225275947 0.12668252090137763 0.1447213595499958 0.16276019819861395 0.18079903684723211 0.19883787549585027 0.21687671414446844 0.2349155527930866 0.1373051009341785 0.15534393958279666 0.17338277823141482 0.19142161688003298 0.20946045552865114 0.2274992941772693 0.24553813282588746 0.26357697147450565 0.28161581012312376 0.29965464877174197 0.3176934874203601 0.22008303556145198 0.23812187421007014 0.2561607128586883 0.27419955150730646 0.2922383901559246 0.3102772288045428 0.32831606745316094 0.3463549061017791 0.36439374475039726 0.3824325833990154 0.4004714220476336 0.3028609701887255 0.32089980883734365 0.3389386474859618 0.35697748613457997 0.37501632478319813 0.3930551634318163 0.41109400208043445 0.4291328407290526 0.44717167937767077 0.46521051802628893 0.4832493566749071 0.385638904815999 0.40367774346461716 0.4217165821132353 0.4397554207618535 0.45779425941047164 0.4758330980590898 0.49387193670770796 0.5119107753563261 0.5299496140049442 0.5479884526535624 0.5660272913021807 0.46841683944327245 0.4864556780918906 0.5044945167405088 0.5225333553891269 0.540572194037745 0.5586110326863633 0.5766498713349815 0.5946887099835996 0.6127275486322177 0.6307663872808359 0.6488052259294541 0.551194774070546 0.5692336127191642 0.5872724513677823 0.6053112900164004 0.6233501286650186 0.6413889673136368 0.6594278059622549 0.677466644610873 0.6955054832594912 0.7135443219081095 0.7315831605567276 0.6339727086978194 0.6520115473464376 0.6700503859950557 0.6880892246436738 0.706128063292292 0.7241669019409103 0.7422057405895284 0.7602445792381465 0.7782834178867647 0.7963222565353829 0.814361095184001 0.716750643325093 0.7347894819737111 0.7528283206223293 0.7708671592709475 0.7889059979195656 0.8069448365681837 0.8249836752168019 0.8430225138654202 0.8610613525140383 0.8791001911626564 0.8971390298112746 0.7995285779523665 0.8175674166009848 0.8356062552496029 0.853645093898221 0.8716839325468392 0.8897227711954574 0.9077616098440755 0.9258004484926936 0.9438392871413118 0.96187812578993 0.9799169644385481 0.8823065125796399 0.900345351228258 0.9183841898768762 0.9364230285254944 0.9544618671741125 0.9725007058227306 0.9905395444713488 1.008578383119967 1.0266172217685852 1.0446560604172033 1.0626948990658214 0.9650844472069134 0.9831232858555317 1.0011621245041498 1.0192009631527679 1.0372398018013862 1.0552786404500043 1.0733174790986224 1.0913563177472405 1.1093951563958586 1.127433995044477 1.145472833693095; + 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.05 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.14198039027185572 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.2339607805437114 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671 0.3259411708155671] + + shape = extrude_geometry((p1, p2, p3); direction, particle_spacing=0.1, n_extrude=4, + density=1000.0) + + @test shape.coordinates ≈ expected_coords + end +end diff --git a/test/setups/setups.jl b/test/setups/setups.jl index eaffd6761..0a72c9410 100644 --- a/test/setups/setups.jl +++ b/test/setups/setups.jl @@ -1,3 +1,4 @@ include("rectangular_shape.jl") include("sphere_shape.jl") include("rectangular_tank.jl") +include("extrude_geometry.jl") From 307a3d84831ec83ce8019731baeada0197a84848 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Thu, 18 Apr 2024 11:59:37 +0200 Subject: [PATCH 07/49] Optimization bnd pressure computation (#475) * optimization * optimization * fix calc for 1 thread * format * update describtion * remove +1 * update reference files * update error bounds * fix * Update src/schemes/boundary/dummy_particles/dummy_particles.jl Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> * Update src/schemes/boundary/dummy_particles/dummy_particles.jl Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> * Update src/schemes/boundary/dummy_particles/dummy_particles.jl Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> * Update src/schemes/boundary/dummy_particles/dummy_particles.jl Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> * review --------- Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> --- .../dummy_particles/dummy_particles.jl | 107 +++-- test/validation/validation.jl | 6 +- .../validation_reference_edac_0015.json | 412 ++++++++--------- .../validation_reference_wcsph_0015.json | 428 +++++++++--------- 4 files changed, 504 insertions(+), 449 deletions(-) diff --git a/src/schemes/boundary/dummy_particles/dummy_particles.jl b/src/schemes/boundary/dummy_particles/dummy_particles.jl index 27c7f924d..76da93cf0 100644 --- a/src/schemes/boundary/dummy_particles/dummy_particles.jl +++ b/src/schemes/boundary/dummy_particles/dummy_particles.jl @@ -324,13 +324,34 @@ function compute_pressure!(boundary_model, ::AdamiPressureExtrapolation, v_neighbor_system = wrap_v(v_ode, neighbor_system, semi) u_neighbor_system = wrap_u(u_ode, neighbor_system, semi) - nhs = get_neighborhood_search(system, neighbor_system, semi) - neighbor_coords = current_coordinates(u_neighbor_system, neighbor_system) - adami_pressure_extrapolation!(boundary_model, system, neighbor_system, - system_coords, neighbor_coords, - v_neighbor_system, nhs) + # This is an optimization for simulations with large and complex boundaries. + # Especially, in 3D simulations with large and/or complex structures outside + # of areas with permanent flow. + # Note: The version iterating neighbors first is not thread parallelizable. + # The factor is based on the achievable speed-up of the thread parallelizable version. + if nparticles(system) > + ceil(Int, 0.5 * Threads.nthreads()) * nparticles(neighbor_system) + nhs = get_neighborhood_search(neighbor_system, system, semi) + + # Loop over fluid particles and then the neighboring boundary particles to extrapolate fluid pressure to the boundaries + adami_pressure_extrapolation_neighbor!(boundary_model, system, neighbor_system, + system_coords, neighbor_coords, + v_neighbor_system, nhs) + else + nhs = get_neighborhood_search(system, neighbor_system, semi) + + # Loop over boundary particles and then the neighboring fluid particles to extrapolate fluid pressure to the boundaries + adami_pressure_extrapolation!(boundary_model, system, neighbor_system, + system_coords, neighbor_coords, + v_neighbor_system, nhs) + end + for particle in eachparticle(system) + # Limit pressure to be non-negative to avoid attractive forces between fluid and + # boundary particles at free surfaces (sticking artifacts). + pressure[particle] = max(pressure[particle], 0.0) + end end @trixi_timeit timer() "inverse state equation" @threaded for particle in eachparticle(system) @@ -365,6 +386,35 @@ function compute_pressure!(boundary_model, ::Union{PressureMirroring, PressureZe return boundary_model end +@inline function adami_pressure_extrapolation_neighbor!(boundary_model, system, + neighbor_system::FluidSystem, + system_coords, neighbor_coords, + v_neighbor_system, + neighborhood_search) + (; pressure, cache, viscosity) = boundary_model + + for_particle_neighbor(neighbor_system, system, + neighbor_coords, system_coords, + neighborhood_search; + particles=eachparticle(neighbor_system), + parallel=false) do neighbor, particle, + pos_diff, distance + # Since neighbor and particle are switched + pos_diff = -pos_diff + adami_pressure_inner!(boundary_model, system, neighbor_system::FluidSystem, + v_neighbor_system, particle, neighbor, pos_diff, + distance, viscosity, cache, pressure) + end +end + +@inline function adami_pressure_extrapolation_neighbor!(boundary_model, system, + neighbor_system, + system_coords, neighbor_coords, + v_neighbor_system, + neighborhood_search) + return boundary_model +end + @inline function adami_pressure_extrapolation!(boundary_model, system, neighbor_system::FluidSystem, system_coords, neighbor_coords, @@ -377,28 +427,9 @@ end neighborhood_search; particles=eachparticle(system)) do particle, neighbor, pos_diff, distance - density_neighbor = particle_density(v_neighbor_system, neighbor_system, neighbor) - - resulting_acc = neighbor_system.acceleration - - current_acceleration(system, particle) - - kernel_weight = smoothing_kernel(boundary_model, distance) - - pressure[particle] += (particle_pressure(v_neighbor_system, neighbor_system, - neighbor) + - dot(resulting_acc, density_neighbor * pos_diff)) * - kernel_weight - - cache.volume[particle] += kernel_weight - - compute_smoothed_velocity!(cache, viscosity, neighbor_system, v_neighbor_system, - kernel_weight, particle, neighbor) - end - - for particle in eachparticle(system) - # Limit pressure to be non-negative to avoid attractive forces between fluid and - # boundary particles at free surfaces (sticking artifacts). - pressure[particle] = max(pressure[particle], 0.0) + adami_pressure_inner!(boundary_model, system, neighbor_system, + v_neighbor_system, particle, neighbor, pos_diff, + distance, viscosity, cache, pressure) end end @@ -408,6 +439,28 @@ end return boundary_model end +@inline function adami_pressure_inner!(boundary_model, system, + neighbor_system::FluidSystem, + v_neighbor_system, particle, neighbor, pos_diff, + distance, viscosity, cache, pressure) + density_neighbor = particle_density(v_neighbor_system, neighbor_system, neighbor) + + resulting_acc = neighbor_system.acceleration - + current_acceleration(system, particle) + + kernel_weight = smoothing_kernel(boundary_model, distance) + + pressure[particle] += (particle_pressure(v_neighbor_system, neighbor_system, + neighbor) + + dot(resulting_acc, density_neighbor * pos_diff)) * + kernel_weight + + cache.volume[particle] += kernel_weight + + compute_smoothed_velocity!(cache, viscosity, neighbor_system, v_neighbor_system, + kernel_weight, particle, neighbor) +end + function compute_smoothed_velocity!(cache, viscosity, neighbor_system, v_neighbor_system, kernel_weight, particle, neighbor) return cache diff --git a/test/validation/validation.jl b/test/validation/validation.jl index 95dcfe1bd..1fd67ac28 100644 --- a/test/validation/validation.jl +++ b/test/validation/validation.jl @@ -46,14 +46,16 @@ ] @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 - @test isapprox(error_edac_P1, 0, atol=1e-9) - @test isapprox(error_edac_P2, 0, atol=1e-11) if VERSION >= v"1.10" + @test isapprox(error_edac_P1, 0, atol=1e-9) + @test isapprox(error_edac_P2, 0, atol=6e-12) @test isapprox(error_wcsph_P1, 0, atol=eps()) @test isapprox(error_wcsph_P2, 0, atol=eps()) else # 1.9 causes a large difference in the solution + @test isapprox(error_edac_P1, 0, atol=1e-8) + @test isapprox(error_edac_P2, 0, atol=1e-10) @test isapprox(error_wcsph_P1, 0, atol=10) @test isapprox(error_wcsph_P2, 0, atol=1e-3) end diff --git a/validation/dam_break_2d/validation_reference_edac_0015.json b/validation/dam_break_2d/validation_reference_edac_0015.json index 24757b07f..fb8ead957 100644 --- a/validation/dam_break_2d/validation_reference_edac_0015.json +++ b/validation/dam_break_2d/validation_reference_edac_0015.json @@ -139,79 +139,79 @@ 0.0, 0.0, 0.0, - 604.3957582215899, - 854.3064249696633, - 806.7433554643078, - 863.2834525307577, - 1172.7188760376416, - 1185.7354823999622, - 1029.8194599143999, - 1220.8424591423286, - 1129.1130583244467, - 1681.2917027879928, - 1541.8470498978272, - 1941.6817449062, - 1703.3428527373653, - 1820.7752456908524, - 2273.61907013479, - 2059.549527721113, - 2492.4265793163177, - 2431.891436605787, - 2341.4127018992012, - 2380.505662332444, - 2476.6304162505776, - 2377.2152352341263, - 2407.6934010063724, - 2621.6411341900084, - 2604.6982173831625, - 2546.399445810557, - 2689.684321946729, - 2654.6394300204142, - 2815.3615097824277, - 2642.321188486082, - 2862.2882426968963, - 2811.52377372663, - 2800.9092542116946, - 2952.956412783148, - 2844.263845334785, - 2874.303528967565, - 2947.9713667109695, - 2981.7363562084547, - 3022.715296426873, - 3095.8959184057803, - 3223.2834166674697, - 3438.1549404217467, - 3461.6157573942837, - 3889.5328111053327, - 4764.835081898881, - 6691.330625903637, - 5937.573914006671, - 6470.044187005868, - 5114.121150798657, - 3634.979008335772, - 3802.039385890102, - 2751.9598060963713, - 2027.890808792743, - 1718.7065304962896, - 1535.5876413862059, - 1382.3526450110292, - 1479.4637909212727, - 1636.5432932362105, - 1580.8409259586185, - 1362.2791052129037, - 1307.4981915271765, - 1483.094125847357, - 1582.6083889647803, - 3052.2515396988983, - 34536.296819296316, - 37.65056335694383 + 604.3957582210057, + 854.3064249701162, + 806.7433554645238, + 863.2834525323564, + 1172.7188760362817, + 1185.7354823957364, + 1029.8194599051196, + 1220.8424591289627, + 1129.1130583250354, + 1681.2917027821925, + 1541.84704989596, + 1941.681744886013, + 1703.342852837277, + 1820.7752456709127, + 2273.6190702267386, + 2059.549528549632, + 2492.42657872154, + 2431.891435051085, + 2341.412705521953, + 2380.5056651594596, + 2476.6304064185897, + 2377.215248491543, + 2407.6934046918395, + 2621.6411290967458, + 2604.698214284965, + 2546.399443630783, + 2689.6843234734833, + 2654.639435738155, + 2815.3615123943055, + 2642.321187463903, + 2862.2882422981534, + 2811.523774221353, + 2800.9092531867764, + 2952.9564218334053, + 2844.26385035426, + 2874.3035321104517, + 2947.9713669965477, + 2981.736356294694, + 3022.7152925706846, + 3095.8959107824994, + 3223.283421806281, + 3438.1549422956728, + 3461.615757497162, + 3889.532826771071, + 4764.8350876461145, + 6691.330618320588, + 5937.57389697787, + 6470.044184482702, + 5114.121178496523, + 3634.978964640017, + 3802.0393876762696, + 2751.9597721194446, + 2027.8908055573243, + 1718.7065075201758, + 1535.5875986343772, + 1382.352588739524, + 1479.4637838388012, + 1636.543321007025, + 1580.840904760441, + 1362.2790951007676, + 1307.4981559335906, + 1483.0941976925808, + 1582.6084515968898, + 3052.2514672201683, + 34536.29685017654, + 37.65095815197997 ], "datatype": "Float64", "type": "series" }, "meta": { "julia_version": "1.10.2", - "solver_version": "dcd1d2b1-dirty", + "solver_version": "v0.1.0-22-g4d428a23-dirty", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { @@ -360,43 +360,43 @@ 0.0, 0.0, 0.0, - 1.4586145070841525, - 35.23970220674348, - 37.54572391444544, - 13.731385167742658, - 18.846457975737682, - 19.8224841977086, - 16.230770674916435, - 56.876420772446885, - 37.33671454315734, - 78.42964736040615, - 75.04969119915044, - 166.61484088498904, - 162.7352955160456, - 281.7282268014637, - 314.52179459013166, - 297.23412867557687, - 357.6400910860976, - 448.6558207559398, - 465.072910798512, - 612.2401011562348, - 751.9411583776679, - 889.9451285845244, - 1033.4736483837655, - 1058.202206530216, - 1247.2730240894725, - 1414.0754260543076, - 1588.386535262655, - 1665.7826025761042, - 1545.8294662391331, - 1673.9628009744847, - 1683.2228289778777, - 1629.4962992741189, - 1359.6993719827465, - 887.91187263999, - 1629.9983580422795, - 1234.4717693503253, - 2004.3258881084407, + 1.4586145071541898, + 35.23970220674639, + 37.545723914493706, + 13.731385167730085, + 18.84645797568068, + 19.822484197824288, + 16.23077067519315, + 56.87642077158365, + 37.33671454346889, + 78.42964734337639, + 75.04969120367215, + 166.6148408881951, + 162.73529547483014, + 281.72822678093445, + 314.5217962647108, + 297.23412415145486, + 357.6400963258884, + 448.65581898807187, + 465.0729072685026, + 612.2400999708409, + 751.9411576661471, + 889.945132047711, + 1033.4736519596122, + 1058.2021923062105, + 1247.2730353264728, + 1414.0754183693182, + 1588.3865333590663, + 1665.7825902521158, + 1545.8294821241884, + 1673.9628180445059, + 1683.2228111190875, + 1629.4962936654842, + 1359.6993745648126, + 887.9118718296219, + 1629.9983551561381, + 1234.4717618954708, + 2004.325882177016, 0.0, 0.0, 0.0, @@ -538,98 +538,98 @@ 1.302960573952499, 1.3475843440024642, 1.3976361698571822, - 1.4522199811561385, - 1.510520947475266, - 1.57214293886014, - 1.6367982265986314, - 1.7042518338466675, - 1.7742937998709938, - 1.8467352027968718, - 1.9213758913869927, - 1.9980351614993204, - 2.076544579209514, - 2.1566383641391065, - 2.238003134643121, - 2.3206381687170157, - 2.4044919394042736, - 2.489540630902278, - 2.5757273488305, - 2.6627522454913684, - 2.7501950551381626, - 2.837835825711921, - 2.9257131544255777, - 3.013897557214555, - 3.102445768668488, - 3.1914847216832074, - 3.2105882666919, - 3.2106025664774633, - 3.210407075423367, - 3.210274207277424, - 3.210140068534914, - 3.210439644792984, - 3.2112884336091394, - 3.2121208254868923, - 3.2125141820856236, - 3.212599029590728, - 3.2119451344617342, - 3.210893702477008, - 3.2095320951690165, - 3.208866042695388, - 3.20871345061095, - 3.208573760872467, - 3.2083568996870384, - 3.208389043560661, - 3.208031877949919, - 3.2079261547153943, - 3.207855507755997, - 3.207774119588862, - 3.207706002731428, - 3.2076706691657413, + 1.452219981156139, + 1.5105209474752668, + 1.572142938860141, + 1.6367982265986325, + 1.7042518338466686, + 1.774293799870995, + 1.8467352027968729, + 1.9213758913869938, + 1.9980351614993215, + 2.0765445792095147, + 2.1566383641391074, + 2.238003134643122, + 2.3206381687170166, + 2.404491939404275, + 2.4895406309022796, + 2.575727348830502, + 2.662752245491369, + 2.7501950551381618, + 2.837835825711919, + 2.925713154425575, + 3.0138975572145523, + 3.102445768668486, + 3.191484721683206, + 3.2105882666919006, + 3.210602566477464, + 3.2104070754233653, + 3.2102742072774215, + 3.210140068534911, + 3.210439644793008, + 3.211288433609166, + 3.21212082548689, + 3.2125141820856595, + 3.212599029590776, + 3.211945134461789, + 3.210893702477077, + 3.2095320951690947, + 3.2088660426953854, + 3.2087134506109494, + 3.20857376087247, + 3.2083568996870397, + 3.2083890435617195, + 3.2080318779499213, + 3.2079261547153983, + 3.207855507756003, + 3.2077741195888656, + 3.2077060027314293, + 3.207670669165742, 3.2076288978754195, - 3.2075941374177184, + 3.2075941374177193, 3.2075586504257148, - 3.2075368319522997, - 3.2075265213627904, - 3.210218793361137, - 3.21269507520042, - 3.2119664146378195, - 3.2074630303747567, - 3.2074452874588837, - 3.2074252902484437, - 3.2074042323916974, - 3.207389539186995, - 3.207376753579591, - 3.2073732467888747, - 3.207348313206412, - 3.2076648180581375, - 3.2073154618525073, - 3.2078540055088123, - 3.2085800521157415, - 3.2072818774036778, - 3.2072679757668348, - 3.207248958752789, - 3.2072243341066833, - 3.2072195892028104, - 3.2072071351452363, - 3.207191617668054, - 3.207167720999114, - 3.207148223076407, - 3.207125308504274, - 3.2071076627463984, - 3.2070796885516786, - 3.2070631437453265, - 3.207023980424339, - 3.207002203054955, - 3.2069578815542887, - 3.206882240708725, - 3.206818992733726, - 3.2067228411656616, - 3.2066412586740536, - 3.206517043308112, - 3.2063950957148974, - 3.206329455325393, - 3.206229289248847, - 3.2061593054711017 + 3.207536831952301, + 3.2075265213627917, + 3.2102187933610122, + 3.2126950751999495, + 3.21196641463669, + 3.2074630303747598, + 3.207445287458887, + 3.2074252902484464, + 3.2074042323917, + 3.2073895391869978, + 3.207376753579594, + 3.2073732467888774, + 3.2073483132064147, + 3.207664818066874, + 3.20731546185251, + 3.207854005492601, + 3.208580052098399, + 3.2072818774036804, + 3.207267975766838, + 3.2072489587527926, + 3.2072243341066873, + 3.207219589202814, + 3.207207135145238, + 3.207191617668057, + 3.207167720999116, + 3.2071482230764086, + 3.2071253085042755, + 3.2071076627464006, + 3.207079688551678, + 3.2070631437453248, + 3.207023980424338, + 3.2070022030549534, + 3.2069578815542865, + 3.206882240708722, + 3.206818992733722, + 3.206722841165657, + 3.206641258674049, + 3.20651704330811, + 3.2063950957148926, + 3.206329455325389, + 3.206229289248844, + 3.2061593054711004 ], "datatype": "Float64", "type": "series" @@ -792,16 +792,16 @@ 0.0, 0.0, 0.0, - 3.9648485713304686, - 5.688115787444037, - 5.021169068151417, - 3.4690420734565928, - 3.558737480081865, - 3.1571728411890927, - 3.002903034691488, - 4.056831531943184, - 6.550226833801881, - 4.5706557048191865, + 3.9648485713534356, + 5.688115787337868, + 5.021169068061549, + 3.4690420744982595, + 3.5587374771061193, + 3.1571728370072245, + 3.002903045034488, + 4.056831537710453, + 6.550226835881946, + 4.570655703611319, 0.0, 0.0, 0.0, @@ -809,7 +809,7 @@ 0.0, 0.0, 0.0, - 13.988872277177816, + 13.98887227719292, 0.0, 0.0, 0.0, @@ -844,4 +844,4 @@ "datatype": "Float64", "type": "series" } -} \ No newline at end of file +} diff --git a/validation/dam_break_2d/validation_reference_wcsph_0015.json b/validation/dam_break_2d/validation_reference_wcsph_0015.json index 3a3d2221e..3b0f95b9f 100644 --- a/validation/dam_break_2d/validation_reference_wcsph_0015.json +++ b/validation/dam_break_2d/validation_reference_wcsph_0015.json @@ -139,79 +139,79 @@ 0.0, 0.0, 0.0, - 741.9939338248116, - 1074.8234704502822, - 780.1667963911615, - 750.2648649048765, - 1867.0876734094586, - 2289.2233790766354, - 1854.778595920126, - 1898.4536968839027, - 1467.7442216200166, - 1634.062582456003, - 1051.0175372038652, - 1792.992475880798, - 2224.4495761272037, - 2072.49744240541, - 1759.7942677800675, - 2213.446522638474, - 2195.8824872685705, - 2296.936590368152, - 2110.2619949482696, - 2199.2839820790064, - 2970.3733860698267, - 2306.9379237499984, - 2708.549145872953, - 3000.178544197365, - 2269.792696086254, - 2084.4626138591966, - 2584.8924415020083, - 2922.9580586157217, - 2700.961322015944, - 2912.960202391661, - 2333.093486494811, - 2272.6997429718494, - 2482.9264961596828, - 2818.1473417252982, - 2995.432486972956, - 2909.3768298891655, - 3100.77021995143, - 3025.9649439094655, - 3235.1990309725757, - 3257.7483119248636, - 2988.7945918465675, - 3115.2094695758724, - 3505.9426755013787, - 3713.8656343932016, - 4586.51617966487, - 4512.604003095575, - 6079.787376393051, - 6651.042779454037, - 5998.169882646548, - 3485.069906443089, - 810.5465351054376, - 1498.1154610674191, - 1280.9451536023262, - 6123.104036824773, - 1207.454673045513, - 1872.428135603674, - 345.5216355544876, - 1190.3488951150066, - 1160.696857170737, - 1552.7750026162616, - 2007.8328589553835, - 1229.3505526363801, - 941.7174210262225, - 1420.6537905276475, - 4080.222555513338, - 774.1134490223096 + 741.993933814252, + 1074.8234704492293, + 780.1667963845828, + 750.264864820256, + 1867.0876722351645, + 2289.2233808791984, + 1854.7785895125223, + 1898.453697957532, + 1467.7442227383228, + 1634.0625899997626, + 1051.017537937822, + 1792.9924495715281, + 2224.4495834290674, + 2072.497434035885, + 1759.7943013144645, + 2213.4464461574053, + 2195.8826026854586, + 2296.9363606455972, + 2110.2618680074606, + 2199.283897208698, + 2970.3734738765897, + 2306.9378427287784, + 2708.5493847934304, + 3000.1784782069417, + 2269.7929805781623, + 2084.4623649717955, + 2584.892142305768, + 2922.95823696328, + 2700.9623996934047, + 2912.9611466447036, + 2333.0917554209454, + 2272.7000065438688, + 2482.9258774761342, + 2818.1463732338643, + 2995.433781965522, + 2909.3736079646987, + 3100.7712408135812, + 3025.9641406700944, + 3235.2029348584956, + 3257.7410461563277, + 2988.8181504113973, + 3115.194724294498, + 3505.92247124294, + 3713.8459250716937, + 4586.526088795744, + 4512.65880424846, + 6079.986577464039, + 6651.130159524973, + 5997.838678899889, + 3484.6443631044467, + 810.4039689089993, + 1497.7738917876206, + 1281.226457082536, + 6127.035664299017, + 1199.4753269710066, + 1869.222563929746, + 345.0423599469749, + 1192.8963322296063, + 1164.7884692609991, + 1552.9964308420076, + 2007.227306493361, + 1229.1139656462394, + 940.9388974307137, + 1420.324488576742, + 4081.141427086134, + 778.984800800608 ], "datatype": "Float64", "type": "series" }, "meta": { "julia_version": "1.10.2", - "solver_version": "dcd1d2b1", + "solver_version": "v0.1.0-22-g4d428a23-dirty", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { @@ -360,41 +360,41 @@ 0.0, 0.0, 0.0, - 47.78581694313901, - 44.38574442306553, - 30.473857197402413, - 8.477074724627595, - 33.57951875059658, - 1.8632501617021102, - 22.444845391614827, - 5.229751210510513, - 12.452368822405496, - 113.28031750326882, - 16.872533929605122, - 183.53117184170725, - 124.91979697908702, - 119.72035918066965, - 157.18678733719997, - 274.41224922147677, - 366.928403397206, - 436.7982051917158, - 656.8646154705689, - 693.6804508905344, - 709.8624537628132, - 940.2761580309083, - 713.5961440910972, - 1152.7121152728616, - 1175.0856672128707, - 1117.0740820240865, - 1473.3118337985009, - 1398.3788581542326, - 1426.7186616370407, - 1724.6680215045349, - 1689.582829806212, - 1716.4125370027475, - 2313.2602797513496, - 1548.2978703765393, - 872.8096099036991, + 47.78581701120133, + 44.38574448700575, + 30.47385719369803, + 8.477074777805942, + 33.57951839840989, + 1.8632502205569363, + 22.444846119437738, + 5.229750916569136, + 12.452367852533479, + 113.28032802403966, + 16.872523440014806, + 183.53111225036926, + 124.91979335190122, + 119.72020754524598, + 157.186868843168, + 274.4122531283022, + 366.92834558395305, + 436.79833075915656, + 656.8648337902086, + 693.6806185748894, + 709.8624031802605, + 940.2755903496779, + 713.5958561086439, + 1152.7132323277215, + 1175.0858756074435, + 1117.0732832554718, + 1473.3114994251675, + 1398.3800311373202, + 1426.7187322271566, + 1724.6666661737895, + 1689.5894191979883, + 1716.4136340314021, + 2313.2505906487645, + 1548.2452467272121, + 872.8944564735984, 0.0, 0.0, 0.0, @@ -531,105 +531,105 @@ "system_name": "fluid", "values": [ 1.1925, - 1.1979096218668643, - 1.2114046382489774, - 1.233642069008528, - 1.2644066208331137, - 1.3028362471750408, - 1.347675181367617, - 1.3979265161874306, - 1.4526822092129996, - 1.5111470319630962, - 1.5729272010563125, - 1.6376619970447728, - 1.7050782695517812, - 1.774922235352398, - 1.8470381017706874, - 1.9214486425691797, - 1.9977114877634126, - 2.0756036806364517, + 1.1979096218668641, + 1.2114046382489772, + 1.2336420690085275, + 1.264406620833113, + 1.30283624717504, + 1.3476751813676164, + 1.3979265161874295, + 1.4526822092129983, + 1.5111470319630946, + 1.572927201056311, + 1.6376619970447712, + 1.7050782695517803, + 1.7749222353523972, + 1.8470381017706896, + 1.9214486425691817, + 1.9977114877634146, + 2.075603680636452, 2.1548845384780377, - 2.235402446374903, - 2.317146330306623, - 2.4000829664557464, - 2.484138084380929, - 2.5693227139746573, - 2.6555845619869327, - 2.7426282187297657, - 2.830107717849038, - 2.9177936457239837, + 2.2354024463749025, + 2.317146330306622, + 2.400082966455745, + 2.4841380843809278, + 2.569322713974655, + 2.6555845619869305, + 2.742628218729764, + 2.8301077178490366, + 2.917793645723983, 3.0055781193133417, 3.0934656910674185, - 3.1819553106957805, - 3.2108247664460037, + 3.1819553106957756, + 3.210824766445999, 3.2113869376507562, 3.2114127786783255, - 3.2114161066075466, - 3.2113893826012587, - 3.211426416730024, - 3.2113609775292216, - 3.21134242815772, - 3.2113732404825783, - 3.2114163514270784, - 3.2115424540866804, - 3.2114240008532415, - 3.21133982171087, - 3.211435764568252, - 3.2114307576517303, - 3.2113884470340004, - 3.211430796900016, - 3.21138905570089, - 3.211385392690142, - 3.211391389367187, - 3.2113867220811616, - 3.211314609628206, - 3.2112229705963546, - 3.211144904724678, - 3.2110067641757443, - 3.210911971600086, - 3.2107740015910333, - 3.2105999166679235, - 3.210409429662026, - 3.2101913618122015, - 3.209950737465752, - 3.2096723630110047, - 3.209388535442989, - 3.209054807974806, - 3.2087189035854937, - 3.2084002150370896, - 3.2092529975678827, - 3.210961614417827, - 3.2087142145674608, - 3.2078771017362766, - 3.2099811740892332, - 3.2099475931241868, - 3.2057948825772176, - 3.2053841685407347, - 3.206091263909073, - 3.205519339413853, - 3.2060202862612424, - 3.208319007154801, - 3.2093695706812637, - 3.2093274365942297, - 3.2077697929876843, - 3.2052776717641263, - 3.204666763480492, - 3.204935394911624, - 3.205533549952792, - 3.206955192729286, - 3.205966890849804, - 3.205813036921348, - 3.2054758271429558, - 3.2056931166230225, - 3.2057049469317778, - 3.205688541125392, - 3.2056979581817227, - 3.205641713595731, - 3.207203044444701, - 3.2078958675858784, - 3.2077053880890443, - 3.2064670429651616, - 3.2020423230291524 + 3.211416106607547, + 3.2113893826012614, + 3.211426416730049, + 3.2113609775292393, + 3.2113424281577405, + 3.2113732404827138, + 3.2114163514266383, + 3.2115424540877786, + 3.2114240008557804, + 3.2113398217104034, + 3.2114357645677893, + 3.2114307576516063, + 3.2113884470339853, + 3.211430796900003, + 3.211389055700846, + 3.2113853926900813, + 3.21139138936708, + 3.211386722081144, + 3.2113146096279768, + 3.2112229705960287, + 3.2111449047243386, + 3.2110067641753868, + 3.2109119715996353, + 3.210774001590455, + 3.210599916667371, + 3.210409429661165, + 3.2101913618115168, + 3.20995073746505, + 3.209672363010088, + 3.209388535442117, + 3.2090548079737142, + 3.2087189035843537, + 3.2084002150359723, + 3.209253007341981, + 3.210961634193927, + 3.208714219483822, + 3.2078770543055657, + 3.2099811750510763, + 3.2099476105422085, + 3.205794882576252, + 3.205384168539671, + 3.20609132730366, + 3.2055193740331265, + 3.206017846407191, + 3.208320022840661, + 3.209364084855916, + 3.2093323815411345, + 3.2077746632044515, + 3.2052718199939743, + 3.204666769872325, + 3.2049353274677914, + 3.205556387328138, + 3.2069851034663706, + 3.2059668637455325, + 3.205811839035014, + 3.205474317245427, + 3.205693324445893, + 3.205705272070128, + 3.2056899532532865, + 3.205701459845998, + 3.2056470262047125, + 3.2071535188963542, + 3.207886027964543, + 3.207631231852247, + 3.2062764850292496, + 3.202043659162054 ], "datatype": "Float64", "type": "series" @@ -789,29 +789,29 @@ 0.0, 0.0, 0.0, - 7.496013435689593, - 4.405923582489511, - 3.863763140478915, - 5.608197870347884, - 5.873367887336063, - 8.085513458105599, - 5.478876868208552, - 4.599394355614402, - 4.4993063713207775, - 4.593781791458133, - 4.183019826563516, - 1.4104621904133992, - 3.2046660479543077, - 11.008713759804175, - 5.743853671430627, - 4.599785421280336, + 7.496013434489482, + 4.4059235799132725, + 3.863763129703675, + 5.608197865483601, + 5.873368216011654, + 8.085513300597075, + 5.478876262291463, + 4.599394758694961, + 4.499307194633306, + 4.593780734393691, + 4.18301483779327, + 1.4104607407747796, + 3.2046657016293922, + 11.008712667545892, + 5.743854966354466, + 4.599781912734178, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 6.279733295239109, + 6.279727857215189, 0.0, 0.0, 0.0, @@ -844,4 +844,4 @@ "datatype": "Float64", "type": "series" } -} \ No newline at end of file +} From 0e0a9dbaab28194a251caac8d2413e64c430a9ea Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:56:40 +0200 Subject: [PATCH 08/49] Add `show` for `SolutionSavingCallback` with `save_times` (#485) * Add hacky `show` for solution saving callback with `save_times` * Unify comments * Add tests for saving callback show * Reformat --- src/callbacks/post_process.jl | 8 ++-- src/callbacks/solution_saving.jl | 67 ++++++++++++++++++++++++++---- test/callbacks/callbacks.jl | 1 + test/callbacks/solution_saving.jl | 68 +++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 test/callbacks/solution_saving.jl diff --git a/src/callbacks/post_process.jl b/src/callbacks/post_process.jl index 243789433..25a8d051b 100644 --- a/src/callbacks/post_process.jl +++ b/src/callbacks/post_process.jl @@ -96,12 +96,12 @@ function PostprocessCallback(; interval::Integer=0, dt=0.0, exclude_boundary=tru exclude_boundary, funcs, filename, output_directory, append_timestamp, write_csv, write_json) if dt > 0 - # Add a `tstop` every `dt`, and save the final solution. + # Add a `tstop` every `dt`, and save the final solution return PeriodicCallback(post_callback, dt, initialize=initialize_postprocess_callback!, save_positions=(false, false), final_affect=true) else - # The first one is the condition, the second the affect! + # The first one is the `condition`, the second the `affect!` return DiscreteCallback(post_callback, post_callback, save_positions=(false, false), initialize=initialize_postprocess_callback!) @@ -214,7 +214,7 @@ function initialize_postprocess_callback!(cb::PostprocessCallback, u, t, integra return nothing end -# condition with interval +# `condition` with interval function (pp::PostprocessCallback)(u, t, integrator) (; interval) = pp @@ -260,7 +260,7 @@ function (pp::PostprocessCallback)(integrator) write_postprocess_callback(pp) end - # Tell OrdinaryDiffEq that u has not been modified + # Tell OrdinaryDiffEq that `u` has not been modified u_modified!(integrator, false) end diff --git a/src/callbacks/solution_saving.jl b/src/callbacks/solution_saving.jl index 119f80e80..b83e9d4ef 100644 --- a/src/callbacks/solution_saving.jl +++ b/src/callbacks/solution_saving.jl @@ -104,15 +104,16 @@ function SolutionSavingCallback(; interval::Integer=0, dt=0.0, -1) if length(save_times) > 0 - return PresetTimeCallback(save_times, solution_callback) + # See the large comment below for an explanation why we use `finalize` here + return PresetTimeCallback(save_times, solution_callback, finalize=solution_callback) elseif dt > 0 - # Add a `tstop` every `dt`, and save the final solution. + # Add a `tstop` every `dt`, and save the final solution return PeriodicCallback(solution_callback, dt, initialize=initialize_save_cb!, save_positions=(false, false), final_affect=save_final_solution) else - # The first one is the condition, the second the affect! + # The first one is the `condition`, the second the `affect!` return DiscreteCallback(solution_callback, solution_callback, save_positions=(false, false), initialize=initialize_save_cb!) @@ -132,19 +133,19 @@ function initialize_save_cb!(solution_callback::SolutionSavingCallback, u, t, in # Save initial solution if solution_callback.save_initial_solution - # Update systems to compute quantities like density and pressure. + # Update systems to compute quantities like density and pressure semi = integrator.p v_ode, u_ode = u.x update_systems_and_nhs(v_ode, u_ode, semi, t) - # Apply the callback. + # Apply the callback solution_callback(integrator) end return nothing end -# condition +# `condition` function (solution_callback::SolutionSavingCallback)(u, t, integrator) (; interval, save_final_solution) = solution_callback @@ -152,7 +153,7 @@ function (solution_callback::SolutionSavingCallback)(u, t, integrator) save_final_solution=save_final_solution) end -# affect! +# `affect!` function (solution_callback::SolutionSavingCallback)(integrator) (; interval, output_directory, custom_quantities, write_meta_data, verbose, prefix, latest_saved_iter, max_coordinates) = solution_callback @@ -183,12 +184,30 @@ function (solution_callback::SolutionSavingCallback)(integrator) max_coordinates=max_coordinates, custom_quantities...) - # Tell OrdinaryDiffEq that u has not been modified + # Tell OrdinaryDiffEq that `u` has not been modified u_modified!(integrator, false) return nothing end +# `finalize` +# This is a hack to make dispatch on a `PresetTimeCallback` possible. +# +# The type of the returned `DiscreteCallback` is +# `DiscreteCallback{typeof(condition), typeof(affect!), typeof(initialize), typeof(finalize)}`. +# For the `PeriodicCallback`, `typeof(affect!)` contains the type of the +# `SolutionSavingCallback`. The `PresetTimeCallback` uses anonymous functions as `condition` +# and `affect!`, so this doesn't work here. +# +# This hacky workaround makes use of the type parameter `typeof(finalize)` above. +# It's set to `FINALIZE_DEFAULT` by default in the `PresetTimeCallback`, which is a function +# that just returns `nothing`. +# Instead, we pass the `SolutionSavingCallback` as `finalize`, and define it to also just +# return `nothing` when called as `initialize`. +function (finalize::SolutionSavingCallback)(c, u, t, integrator) + return nothing +end + function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:SolutionSavingCallback}) @nospecialize cb # reduce precompilation time @@ -205,6 +224,14 @@ function Base.show(io::IO, print(io, "SolutionSavingCallback(dt=", solution_saving.interval, ")") end +function Base.show(io::IO, + cb::DiscreteCallback{<:Any, <:Any, <:Any, <:SolutionSavingCallback}) + @nospecialize cb # reduce precompilation time + + solution_saving = cb.finalize + print(io, "SolutionSavingCallback(save_times=", solution_saving.save_times, ")") +end + function Base.show(io::IO, ::MIME"text/plain", cb::DiscreteCallback{<:Any, <:SolutionSavingCallback}) @nospecialize cb # reduce precompilation time @@ -229,6 +256,30 @@ function Base.show(io::IO, ::MIME"text/plain", end end +function Base.show(io::IO, ::MIME"text/plain", + cb::DiscreteCallback{<:Any, <:Any, <:Any, <:SolutionSavingCallback}) + @nospecialize cb # reduce precompilation time + + if get(io, :compact, false) + show(io, cb) + else + solution_saving = cb.finalize + cq = collect(solution_saving.custom_quantities) + + setup = [ + "save_times" => solution_saving.save_times, + "custom quantities" => isempty(cq) ? nothing : cq, + "save initial solution" => solution_saving.save_initial_solution ? + "yes" : "no", + "save final solution" => solution_saving.save_final_solution ? "yes" : + "no", + "output directory" => abspath(solution_saving.output_directory), + "prefix" => solution_saving.prefix, + ] + summary_box(io, "SolutionSavingCallback", setup) + end +end + function Base.show(io::IO, ::MIME"text/plain", cb::DiscreteCallback{<:Any, <:PeriodicCallbackAffect{<:SolutionSavingCallback}}) diff --git a/test/callbacks/callbacks.jl b/test/callbacks/callbacks.jl index 3076b9274..0a97850bd 100644 --- a/test/callbacks/callbacks.jl +++ b/test/callbacks/callbacks.jl @@ -2,4 +2,5 @@ include("info.jl") include("stepsize.jl") include("postprocess.jl") + include("solution_saving.jl") end diff --git a/test/callbacks/solution_saving.jl b/test/callbacks/solution_saving.jl new file mode 100644 index 000000000..3cc765b65 --- /dev/null +++ b/test/callbacks/solution_saving.jl @@ -0,0 +1,68 @@ +@testset verbose=true "SolutionSavingCallback" begin + @testset verbose=true "show" begin + out = joinpath(pkgdir(TrixiParticles), "out") + output_directory_padded = out * " "^(65 - length(out)) + + @testset verbose=true "dt" begin + callback = SolutionSavingCallback(dt=0.02, prefix="test", output_directory=out) + + show_compact = "SolutionSavingCallback(dt=0.02)" + @test repr(callback) == show_compact + + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ SolutionSavingCallback │ + │ ══════════════════════ │ + │ dt: ……………………………………………………………………… 0.02 │ + │ custom quantities: ……………………………… nothing │ + │ save initial solution: …………………… yes │ + │ save final solution: ………………………… yes │ + │ output directory: ………………………………… $(output_directory_padded)│ + │ prefix: …………………………………………………………… test │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", callback) == show_box + end + + @testset verbose=true "interval" begin + callback = SolutionSavingCallback(interval=100, prefix="test", + output_directory=out) + + show_compact = "SolutionSavingCallback(interval=100)" + @test repr(callback) == show_compact + + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ SolutionSavingCallback │ + │ ══════════════════════ │ + │ interval: ……………………………………………………… 100 │ + │ custom quantities: ……………………………… nothing │ + │ save initial solution: …………………… yes │ + │ save final solution: ………………………… yes │ + │ output directory: ………………………………… $(output_directory_padded)│ + │ prefix: …………………………………………………………… test │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", callback) == show_box + end + + @testset verbose=true "interval" begin + callback = SolutionSavingCallback(save_times=[1.0, 2.0, 3.0], prefix="test", + output_directory=out) + + show_compact = "SolutionSavingCallback(save_times=[1.0, 2.0, 3.0])" + @test repr(callback) == show_compact + + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ SolutionSavingCallback │ + │ ══════════════════════ │ + │ save_times: ………………………………………………… [1.0, 2.0, 3.0] │ + │ custom quantities: ……………………………… nothing │ + │ save initial solution: …………………… yes │ + │ save final solution: ………………………… yes │ + │ output directory: ………………………………… $(output_directory_padded)│ + │ prefix: …………………………………………………………… test │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", callback) == show_box + end + end +end From d52b8352720dbc5fa9f3ef15cc090c057df878f5 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:23:31 +0200 Subject: [PATCH 09/49] Remove TrixiParticles dependency in docs environment (#501) --- docs/Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/Project.toml b/docs/Project.toml index b6b5e7993..7ada97297 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,7 +2,6 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" -TrixiParticles = "66699cd8-9c01-4e9d-a059-b96c86d16b3a" [compat] Documenter = "1" From 9c1ebff2e4bf54c72b7d1d2f27cdfc9cfd78d647 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Tue, 23 Apr 2024 16:11:32 +0200 Subject: [PATCH 10/49] Fix doc string of solution_saving.jl (#499) * fix doc string * missing comma * Update solution_saving.jl Fix indent --------- Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> --- src/callbacks/solution_saving.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/callbacks/solution_saving.jl b/src/callbacks/solution_saving.jl index b83e9d4ef..5163199d6 100644 --- a/src/callbacks/solution_saving.jl +++ b/src/callbacks/solution_saving.jl @@ -1,7 +1,8 @@ @doc raw""" SolutionSavingCallback(; interval::Integer=0, dt=0.0, save_times=Array{Float64, 1}([]), save_initial_solution=true, save_final_solution=true, - output_directory="out", append_timestamp=false, max_coordinates=2^15, + output_directory="out", append_timestamp=false, prefix="", + verbose=false, write_meta_data=true, max_coordinates=2^15, custom_quantities...) @@ -25,9 +26,9 @@ To ignore a custom quantity for a specific system, return `nothing`. - `save_final_solution=true`: Save the final solution. - `output_directory="out"`: Directory to save the VTK files. - `append_timestamp=false`: Append current timestamp to the output directory. -- 'prefix': Prefix added to the filename. +- 'prefix=""': Prefix added to the filename. - `custom_quantities...`: Additional user-defined quantities. -- `write_meta_data`: Write meta data. +- `write_meta_data=true`: Write meta data. - `verbose=false`: Print to standard IO when a file is written. - `max_coordinates=2^15`: The coordinates of particles will be clipped if their absolute values exceed this threshold. From fe72dd5263e80b630bea7b4182ca43c6f601ff95 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 30 Apr 2024 11:48:50 +0200 Subject: [PATCH 11/49] Make array types in structs generic (#486) * Make array types in structs generic * Use `Ref{Bool}` instead of `Vector{Bool}` * Make array types of EDAC system generic * Reformat * Fix tests * Make `DensityDiffusionAntuono` generic * Fix tests * Implement suggestions --- src/general/semidiscretization.jl | 4 ++-- .../dummy_particles/dummy_particles.jl | 19 +++++++-------- .../monaghan_kajtar/monaghan_kajtar.jl | 9 ++++---- src/schemes/boundary/system.jl | 23 ++++++++++--------- .../fluid/entropically_damped_sph/system.jl | 9 ++++---- .../density_diffusion.jl | 11 +++++---- .../fluid/weakly_compressible_sph/system.jl | 12 ++++++---- .../solid/total_lagrangian_sph/system.jl | 23 +++++++++++-------- test/systems/boundary_system.jl | 3 ++- 9 files changed, 63 insertions(+), 50 deletions(-) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index ada9275e4..d942f7397 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -590,7 +590,7 @@ end function nhs_coords(system::FluidSystem, neighbor::BoundarySPHSystem, u) - if neighbor.ismoving[1] + if neighbor.ismoving[] return current_coordinates(u, neighbor) end @@ -611,7 +611,7 @@ end function nhs_coords(system::TotalLagrangianSPHSystem, neighbor::BoundarySPHSystem, u) - if neighbor.ismoving[1] + if neighbor.ismoving[] return current_coordinates(u, neighbor) end diff --git a/src/schemes/boundary/dummy_particles/dummy_particles.jl b/src/schemes/boundary/dummy_particles/dummy_particles.jl index 76da93cf0..86ffe678c 100644 --- a/src/schemes/boundary/dummy_particles/dummy_particles.jl +++ b/src/schemes/boundary/dummy_particles/dummy_particles.jl @@ -37,9 +37,9 @@ boundary_model = BoundaryModelDummyParticles(densities, masses, AdamiPressureExt BoundaryModelDummyParticles(AdamiPressureExtrapolation, ViscosityAdami) ``` """ -struct BoundaryModelDummyParticles{DC, ELTYPE <: Real, SE, K, V, COR, C} - pressure :: Vector{ELTYPE} - hydrodynamic_mass :: Vector{ELTYPE} +struct BoundaryModelDummyParticles{DC, ELTYPE <: Real, VECTOR, SE, K, V, COR, C} + pressure :: VECTOR # Vector{ELTYPE} + hydrodynamic_mass :: VECTOR # Vector{ELTYPE} state_equation :: SE density_calculator :: DC smoothing_kernel :: K @@ -64,12 +64,13 @@ struct BoundaryModelDummyParticles{DC, ELTYPE <: Real, SE, K, V, COR, C} create_cache_model(correction, initial_density, NDIMS, n_particles)..., cache...) - new{typeof(density_calculator), eltype(initial_density), - typeof(state_equation), typeof(smoothing_kernel), typeof(viscosity), - typeof(correction), typeof(cache)}(pressure, hydrodynamic_mass, state_equation, - density_calculator, - smoothing_kernel, smoothing_length, - viscosity, correction, cache) + return new{typeof(density_calculator), eltype(initial_density), + typeof(pressure), typeof(state_equation), + typeof(smoothing_kernel), typeof(viscosity), + typeof(correction), typeof(cache)}(pressure, hydrodynamic_mass, + state_equation, density_calculator, + smoothing_kernel, smoothing_length, + viscosity, correction, cache) end end diff --git a/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl b/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl index e80740cf5..ff45c7986 100644 --- a/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl +++ b/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl @@ -14,17 +14,18 @@ - `viscosity`: Free-slip (default) or no-slip condition. See description above for further information. """ -struct BoundaryModelMonaghanKajtar{ELTYPE <: Real, V} +struct BoundaryModelMonaghanKajtar{ELTYPE <: Real, VECTOR, V} K :: ELTYPE beta :: ELTYPE boundary_particle_spacing :: ELTYPE - hydrodynamic_mass :: Vector{ELTYPE} + hydrodynamic_mass :: VECTOR # Vector{ELTYPE} viscosity :: V function BoundaryModelMonaghanKajtar(K, beta, boundary_particle_spacing, mass; viscosity=nothing) - return new{typeof(K), typeof(viscosity)}(K, beta, boundary_particle_spacing, mass, - viscosity) + return new{typeof(K), typeof(mass), + typeof(viscosity)}(K, beta, boundary_particle_spacing, + mass, viscosity) end end diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl index b30d8fc41..3e2e04772 100644 --- a/src/schemes/boundary/system.jl +++ b/src/schemes/boundary/system.jl @@ -11,18 +11,18 @@ The interaction between fluid and boundary particles is specified by the boundar # Keyword Arguments - `movement`: For moving boundaries, a [`BoundaryMovement`](@ref) can be passed. """ -struct BoundarySPHSystem{BM, NDIMS, ELTYPE <: Real, M, C} <: BoundarySystem{NDIMS} - initial_condition :: InitialCondition{ELTYPE} - coordinates :: Array{ELTYPE, 2} +struct BoundarySPHSystem{BM, NDIMS, IC, CO, M, IM, CA} <: BoundarySystem{NDIMS} + initial_condition :: IC + coordinates :: CO # Array{ELTYPE, 2} boundary_model :: BM movement :: M - ismoving :: Vector{Bool} - cache :: C + ismoving :: IM # Ref{Bool} (to make a mutable field compatible with GPUs) + cache :: CA function BoundarySPHSystem(initial_condition, model; movement=nothing) coordinates = copy(initial_condition.coordinates) NDIMS = size(coordinates, 1) - ismoving = zeros(Bool, 1) + ismoving = Ref(!isnothing(movement)) cache = create_cache_boundary(movement, initial_condition) @@ -33,7 +33,8 @@ struct BoundarySPHSystem{BM, NDIMS, ELTYPE <: Real, M, C} <: BoundarySystem{NDIM movement.moving_particles .= collect(1:nparticles(initial_condition)) end - return new{typeof(model), NDIMS, eltype(coordinates), typeof(movement), + return new{typeof(model), NDIMS, typeof(initial_condition), + typeof(coordinates), typeof(movement), typeof(ismoving), typeof(cache)}(initial_condition, coordinates, model, movement, ismoving, cache) end @@ -125,7 +126,7 @@ function (movement::BoundaryMovement)(system, t) (; movement_function, is_moving, moving_particles) = movement (; acceleration, velocity) = cache - system.ismoving[1] = is_moving(t) + system.ismoving[] = is_moving(t) is_moving(t) || return system @@ -145,7 +146,7 @@ function (movement::BoundaryMovement)(system, t) end function (movement::Nothing)(system, t) - system.ismoving[1] = false + system.ismoving[] = false return system end @@ -177,7 +178,7 @@ end @inline function current_velocity(v, system::BoundarySPHSystem, particle) (; cache, ismoving) = system - if ismoving[1] + if ismoving[] return extract_svector(cache.velocity, system, particle) end @@ -187,7 +188,7 @@ end @inline function current_acceleration(system::BoundarySPHSystem, particle) (; cache, ismoving) = system - if ismoving[1] + if ismoving[] return extract_svector(cache.acceleration, system, particle) end diff --git a/src/schemes/fluid/entropically_damped_sph/system.jl b/src/schemes/fluid/entropically_damped_sph/system.jl index bfa07c051..15c765040 100644 --- a/src/schemes/fluid/entropically_damped_sph/system.jl +++ b/src/schemes/fluid/entropically_damped_sph/system.jl @@ -39,10 +39,10 @@ See [Entropically Damped Artificial Compressibility for SPH](@ref edac) for more The keyword argument `acceleration` should be used instead for gravity-like source terms. """ -struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, DC, K, V, +struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, IC, M, DC, K, V, PF, ST, C} <: FluidSystem{NDIMS} - initial_condition :: InitialCondition{ELTYPE} - mass :: Array{ELTYPE, 1} # [particle] + initial_condition :: IC + mass :: M # Vector{ELTYPE}: [particle] density_calculator :: DC smoothing_kernel :: K smoothing_length :: ELTYPE @@ -86,7 +86,8 @@ struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, DC, K, V, cache = create_cache_density(initial_condition, density_calculator) - new{NDIMS, ELTYPE, typeof(density_calculator), typeof(smoothing_kernel), + new{NDIMS, ELTYPE, typeof(initial_condition), typeof(mass), + typeof(density_calculator), typeof(smoothing_kernel), typeof(viscosity), typeof(pressure_acceleration), typeof(source_terms), typeof(cache)}(initial_condition, mass, density_calculator, smoothing_kernel, smoothing_length, sound_speed, viscosity, nu_edac, acceleration_, diff --git a/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl b/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl index 117383b11..21c81f991 100644 --- a/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl +++ b/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl @@ -134,10 +134,10 @@ diffusion terms. In: Computer Physics Communications 180.6 (2009), pages 861--872. [doi: 10.1016/j.cpc.2008.12.004](https://doi.org/10.1016/j.cpc.2008.12.004) """ -struct DensityDiffusionAntuono{NDIMS, ELTYPE} <: DensityDiffusion +struct DensityDiffusionAntuono{NDIMS, ELTYPE, ARRAY2D, ARRAY3D} <: DensityDiffusion delta :: ELTYPE - correction_matrix :: Array{ELTYPE, 3} # [i, j, particle] - normalized_density_gradient :: Array{ELTYPE, 2} # [i, particle] + correction_matrix :: ARRAY3D # Array{ELTYPE, 3}: [i, j, particle] + normalized_density_gradient :: ARRAY2D # Array{ELTYPE, 2}: [i, particle] function DensityDiffusionAntuono(initial_condition; delta) NDIMS = ndims(initial_condition) @@ -148,7 +148,10 @@ struct DensityDiffusionAntuono{NDIMS, ELTYPE} <: DensityDiffusion normalized_density_gradient = Array{ELTYPE, 2}(undef, NDIMS, nparticles(initial_condition)) - new{NDIMS, ELTYPE}(delta, correction_matrix, normalized_density_gradient) + new{NDIMS, ELTYPE, + typeof(normalized_density_gradient), + typeof(correction_matrix)}(delta, correction_matrix, + normalized_density_gradient) end end diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index 8e98e3a13..708873cf6 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -39,11 +39,11 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method. gravity-like source terms. """ -struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, DC, SE, K, +struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, V, DD, COR, PF, ST, C} <: FluidSystem{NDIMS} - initial_condition :: InitialCondition{ELTYPE} - mass :: Array{ELTYPE, 1} # [particle] - pressure :: Array{ELTYPE, 1} # [particle] + initial_condition :: IC + mass :: MA # Array{ELTYPE, 1} + pressure :: P # Array{ELTYPE, 1} density_calculator :: DC state_equation :: SE smoothing_kernel :: K @@ -96,7 +96,9 @@ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, DC, SE, K, create_cache_wcsph(correction, initial_condition.density, NDIMS, n_particles)..., cache...) - return new{NDIMS, ELTYPE, typeof(density_calculator), + return new{NDIMS, ELTYPE, typeof(initial_condition), + typeof(mass), typeof(pressure), + typeof(density_calculator), typeof(state_equation), typeof(smoothing_kernel), typeof(viscosity), typeof(density_diffusion), typeof(correction), typeof(pressure_acceleration), diff --git a/src/schemes/solid/total_lagrangian_sph/system.jl b/src/schemes/solid/total_lagrangian_sph/system.jl index cdaf8dba4..a8632885d 100644 --- a/src/schemes/solid/total_lagrangian_sph/system.jl +++ b/src/schemes/solid/total_lagrangian_sph/system.jl @@ -48,15 +48,16 @@ See [Total Lagrangian SPH](@ref tlsph) for more details on the method. ``` where `beam` and `fixed_particles` are of type `InitialCondition`. """ -struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, K, PF, ST} <: SolidSystem{NDIMS} - initial_condition :: InitialCondition{ELTYPE} - initial_coordinates :: Array{ELTYPE, 2} # [dimension, particle] - current_coordinates :: Array{ELTYPE, 2} # [dimension, particle] - mass :: Array{ELTYPE, 1} # [particle] - correction_matrix :: Array{ELTYPE, 3} # [i, j, particle] - pk1_corrected :: Array{ELTYPE, 3} # [i, j, particle] - deformation_grad :: Array{ELTYPE, 3} # [i, j, particle] - material_density :: Array{ELTYPE, 1} # [particle] +struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D, ARRAY3D, + K, PF, ST} <: SolidSystem{NDIMS} + initial_condition :: IC + initial_coordinates :: ARRAY2D # Array{ELTYPE, 2}: [dimension, particle] + current_coordinates :: ARRAY2D # Array{ELTYPE, 2}: [dimension, particle] + mass :: ARRAY1D # Array{ELTYPE, 1}: [particle] + correction_matrix :: ARRAY3D # Array{ELTYPE, 3}: [i, j, particle] + pk1_corrected :: ARRAY3D # Array{ELTYPE, 3}: [i, j, particle] + deformation_grad :: ARRAY3D # Array{ELTYPE, 3}: [i, j, particle] + material_density :: ARRAY1D # Array{ELTYPE, 1}: [particle] n_moving_particles :: Int64 young_modulus :: ELTYPE poisson_ratio :: ELTYPE @@ -105,7 +106,9 @@ struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, K, PF, ST} <: SolidSy lame_mu = 0.5 * young_modulus / (1 + poisson_ratio) return new{typeof(boundary_model), NDIMS, ELTYPE, - typeof(smoothing_kernel), + typeof(initial_condition), + typeof(mass), typeof(initial_coordinates), + typeof(deformation_grad), typeof(smoothing_kernel), typeof(penalty_force), typeof(source_terms)}(initial_condition, initial_coordinates, current_coordinates, mass, correction_matrix, diff --git a/test/systems/boundary_system.jl b/test/systems/boundary_system.jl index 3876040cd..c47d278bd 100644 --- a/test/systems/boundary_system.jl +++ b/test/systems/boundary_system.jl @@ -17,13 +17,14 @@ model = Val(:boundary_model) system = BoundarySPHSystem(initial_condition, model) + TrixiParticles.update_positions!(system, 0, 0, 0, 0, 0, 0.0) @test system isa BoundarySPHSystem @test ndims(system) == NDIMS @test system.coordinates == coordinates @test system.boundary_model == model @test system.movement === nothing - @test system.ismoving == [false] + @test system.ismoving[] == false end end From 478a46c00aebaed4be88e8ed3c77d9b95b07f7c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 08:50:59 +0200 Subject: [PATCH 12/49] Bump crate-ci/typos from 1.19.0 to 1.21.0 (#507) * Bump crate-ci/typos from 1.19.0 to 1.21.0 Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.19.0 to 1.21.0. - [Release notes](https://github.com/crate-ci/typos/releases) - [Changelog](https://github.com/crate-ci/typos/blob/master/CHANGELOG.md) - [Commits](https://github.com/crate-ci/typos/compare/v1.19.0...v1.21.0) --- updated-dependencies: - dependency-name: crate-ci/typos dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Fix/ignore typos --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Michael Schlottke-Lakemper --- .github/workflows/SpellCheck.yml | 2 +- .typos.toml | 1 + src/schemes/solid/total_lagrangian_sph/rhs.jl | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/SpellCheck.yml b/.github/workflows/SpellCheck.yml index e8f2271f8..10bcc2278 100644 --- a/.github/workflows/SpellCheck.yml +++ b/.github/workflows/SpellCheck.yml @@ -10,4 +10,4 @@ jobs: - name: Checkout Actions Repository uses: actions/checkout@v4 - name: Check spelling - uses: crate-ci/typos@v1.19.0 + uses: crate-ci/typos@v1.21.0 diff --git a/.typos.toml b/.typos.toml index 35fc77631..9feb75bab 100644 --- a/.typos.toml +++ b/.typos.toml @@ -2,3 +2,4 @@ ba = "ba" Shepard = "Shepard" shepard = "shepard" +Lok = "Lok" diff --git a/src/schemes/solid/total_lagrangian_sph/rhs.jl b/src/schemes/solid/total_lagrangian_sph/rhs.jl index deb9b9112..36c8fd286 100644 --- a/src/schemes/solid/total_lagrangian_sph/rhs.jl +++ b/src/schemes/solid/total_lagrangian_sph/rhs.jl @@ -75,7 +75,7 @@ function interact!(dv, v_particle_system, u_particle_system, distance < sqrt(eps()) && return # Apply the same force to the solid particle - # that the fluid particle experiences due to the soild particle. + # that the fluid particle experiences due to the solid particle. # Note that the same arguments are passed here as in fluid-solid interact!, # except that pos_diff has a flipped sign. # From 691dd9d3bef331bd7b76bd374ba3cd9e25a621c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 09:02:57 +0200 Subject: [PATCH 13/49] Bump julia-actions/setup-julia from 1 to 2 (#506) Bumps [julia-actions/setup-julia](https://github.com/julia-actions/setup-julia) from 1 to 2. - [Release notes](https://github.com/julia-actions/setup-julia/releases) - [Commits](https://github.com/julia-actions/setup-julia/compare/v1...v2) --- updated-dependencies: - dependency-name: julia-actions/setup-julia dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/Documenter.yml | 2 +- .github/workflows/FormatCheck.yml | 2 +- .github/workflows/ci.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Documenter.yml b/.github/workflows/Documenter.yml index 7d66c333b..941fef735 100644 --- a/.github/workflows/Documenter.yml +++ b/.github/workflows/Documenter.yml @@ -28,7 +28,7 @@ jobs: - name: Check out project uses: actions/checkout@v4 - name: Set up Julia - uses: julia-actions/setup-julia@v1 + uses: julia-actions/setup-julia@v2 with: version: '1' show-versioninfo: true diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index 9a61f2131..5000913d4 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -15,7 +15,7 @@ jobs: - name: Check out project uses: actions/checkout@v4 - name: Set up Julia - uses: julia-actions/setup-julia@v1 + uses: julia-actions/setup-julia@v2 with: version: '1' - run: julia -e 'using InteractiveUtils; versioninfo(verbose=true)' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 698222792..a5832a20f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: - name: Check out project uses: actions/checkout@v4 - name: Set up Julia - uses: julia-actions/setup-julia@v1 + uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} - uses: julia-actions/cache@v1 From 5a55a25718ebebfb98f0913209f84f12a9d37fa2 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Fri, 3 May 2024 08:55:53 +0200 Subject: [PATCH 14/49] The grand reopening Part 3: Add basic DEM (#433) * implement basic dem * fix * fix bnd * get basic simulation working * fix * format * fix spellcheck * add docs * update readme and news * format * add to examples run list * add basic test * ignore author name * remove * most of the review stuff * format * forgot to remove one thing * one more * rework interaction * format * update * typo * fix doctest * fix test * fix doc * fix tests again * fix doc and test * fix test * fix incorrect test * review * format * typo * review * review * format * realign line * fix test * update --- .typos.toml | 1 + NEWS.md | 26 ++-- README.md | 3 +- docs/make.jl | 2 + docs/src/systems/boundary.md | 4 + docs/src/systems/dem.md | 33 +++++ examples/dem/rectangular_tank_2d.jl | 50 ++++++++ src/TrixiParticles.jl | 2 +- src/general/semidiscretization.jl | 23 ++++ .../dummy_particles/dummy_particles.jl | 1 + src/schemes/boundary/rhs.jl | 3 +- src/schemes/boundary/system.jl | 76 ++++++++++-- src/schemes/schemes.jl | 2 + .../discrete_element_method.jl | 1 + .../solid/discrete_element_method/rhs.jl | 110 +++++++++++++++++ .../solid/discrete_element_method/system.jl | 116 ++++++++++++++++++ src/visualization/write2vtk.jl | 7 ++ test/examples/examples.jl | 11 ++ test/systems/boundary_system.jl | 4 +- test/systems/dem_system.jl | 26 ++++ test/systems/systems.jl | 1 + 21 files changed, 482 insertions(+), 20 deletions(-) create mode 100644 docs/src/systems/dem.md create mode 100644 examples/dem/rectangular_tank_2d.jl create mode 100644 src/schemes/solid/discrete_element_method/discrete_element_method.jl create mode 100644 src/schemes/solid/discrete_element_method/rhs.jl create mode 100644 src/schemes/solid/discrete_element_method/system.jl create mode 100644 test/systems/dem_system.jl diff --git a/.typos.toml b/.typos.toml index 9feb75bab..ce360954c 100644 --- a/.typos.toml +++ b/.typos.toml @@ -2,4 +2,5 @@ ba = "ba" Shepard = "Shepard" shepard = "shepard" +Strack = "Strack" Lok = "Lok" diff --git a/NEWS.md b/NEWS.md index 4282613f2..3541c9d68 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,8 +4,7 @@ TrixiParticles.jl follows the interpretation of [semantic versioning (semver)](h used in the Julia ecosystem. Notable changes will be documented in this file for human readability. We aim at 3 to 4 month between major release versions and about 2 weeks between minor versions. - -## Version 0.1.x +## Version 0.2.x ### Highlights @@ -16,15 +15,28 @@ We aim at 3 to 4 month between major release versions and about 2 weeks between ### Deprecated -## Pre Initial Release (v0.1.0) -This section summarizes the initial features that TrixiParticles.jl was released with. +## Version 0.1.2 ### Highlights -#### EDAC + +#### Discrete Element Method +A basic implementation of the discrete element method was added. + + +## Version 0.1.1 + +### Added +A surface tension and adhesion model based on the work by Akinci et al., "Versatile Surface Tension and Adhesion for SPH Fluids", 2013 was added to WCSPH + +# Pre Initial Release (v0.1.0) +This section summarizes the initial features that TrixiParticles.jl was released with. + +## Highlights +### EDAC An implementation of EDAC (Entropically Damped Artificial Compressibility) was added, which allows for more stable simulations compared to basic WCSPH and reduces spurious pressure oscillations. -#### WCSPH +### WCSPH An implementation of WCSPH (Weakly Compressible Smoothed Particle Hydrodynamics), which is the classical SPH approach. Features: @@ -36,5 +48,5 @@ Features: - Density diffusion based on the models by Molteni & Colagrossi (2009), Ferrari et al. (2009) and Antuono et al. (2010). -#### TLSPH +### TLSPH An implementation of TLSPH (Total Lagrangian Smoothed Particle Hydrodynamics) for solid bodies enabling FSI (Fluid Structure Interactions). diff --git a/README.md b/README.md index 2cc50bbcb..9f5defe1a 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ Its features include: ## Features - Incompressible Navier-Stokes - Methods: Weakly Compressible Smoothed Particle Hydrodynamics (WCSPH), Entropically Damped Artificial Compressibility (EDAC) + - Models: Surface Tension - Solid-body mechanics - - Methods: Total Lagrangian SPH (TLSPH) + - Methods: Total Lagrangian SPH (TLSPH), Discrete Element Method (DEM) - Fluid-Structure Interaction - Output formats: - VTK diff --git a/docs/make.jl b/docs/make.jl index 5fb9ea27b..3fcf40ece 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -116,6 +116,8 @@ makedocs(sitename="TrixiParticles.jl", "Util" => joinpath("general", "util.md"), ], "Systems" => [ + "Discrete Element Method (Solid)" => joinpath("systems", + "dem.md"), "Weakly Compressible SPH (Fluid)" => joinpath("systems", "weakly_compressible_sph.md"), "Entropically Damped Artificial Compressibility for SPH (Fluid)" => joinpath("systems", diff --git a/docs/src/systems/boundary.md b/docs/src/systems/boundary.md index d11a50583..8b374a71e 100644 --- a/docs/src/systems/boundary.md +++ b/docs/src/systems/boundary.md @@ -4,6 +4,10 @@ BoundarySPHSystem ``` +```@docs + BoundaryDEMSystem +``` + ```@docs BoundaryMovement ``` diff --git a/docs/src/systems/dem.md b/docs/src/systems/dem.md new file mode 100644 index 000000000..a593db01e --- /dev/null +++ b/docs/src/systems/dem.md @@ -0,0 +1,33 @@ +# [Discrete Element Method](@id dem) +The Discrete Element Method (DEM) is a computational technique widely used in physics, engineering, +and applied mathematics for simulating the mechanical behavior of granular materials, such as powders, +sand, soil, or rock, as well as other discontinua. Unlike continuum mechanics that treats materials as +continuous, DEM considers individual particles or elements and their interactions. This approach provides +detailed insights into the micro-mechanical behavior of materials, making it particularly valuable +in fields such as geomechanics, material science, and mechanical engineering. + +## Fundamental Principles +The core idea behind DEM is the discretization of a material system into a finite set of distinct, +interacting mass elements (particles). These elements (particles) can vary in shape, size, and properties, and +they interact with each other and possibly with their boundaries through contact forces and potential fields. +The motion and behavior of each mass element are governed by Newton's laws of motion, accounting for the forces +and moments acting upon them. + +```@autodocs +Modules = [TrixiParticles] +Pages = [joinpath("schemes", "solid", "discrete_element_method", "system.jl")] +``` + +## References +- N. Bićanić. "Discrete element methods". + In: Encyclopedia of Computational Mechanics (2007). + [doi: 10.1002/0470091355.ecm006.pub2](https://doi.org/10.1002/0470091355.ecm006.pub2) + +- P. Cundall and O. Strack. "A discrete numerical model for granular assemblies". + In: Géotechnique 29.1 (1979), pages 47--65. + [doi: 10.1680/geot.1979.29.1.47](https://doi.org/10.1680/geot.1979.29.1.47) + +- A. Renzo and F. Maio. "Comparison of contact-force models for the simulation of collisions in DEM-based granular flow codes" + In: Chemical Engineering Science 59.3 (2004), pages 525--541. + [doi: 10.1016/j.ces.2003.09.037](https://doi.org/10.1016/j.ces.2003.09.037) + diff --git a/examples/dem/rectangular_tank_2d.jl b/examples/dem/rectangular_tank_2d.jl new file mode 100644 index 000000000..c7daa31e2 --- /dev/null +++ b/examples/dem/rectangular_tank_2d.jl @@ -0,0 +1,50 @@ +using TrixiParticles +using OrdinaryDiffEq + +gravity = -9.81 + +# ========================================================================================== +# ==== Falling rocks + +particle_spacing = 0.1 + +rock_width = 2.0 +rock_height = 2.0 +rock_density = 3000.0 + +tank_width = 2.0 +tank_height = 4.0 + +tank = RectangularTank(particle_spacing, (rock_width, rock_height), + (tank_width, tank_height), rock_density, + n_layers=2) + +# ========================================================================================== +# ==== Systems + +# Move the rocks up to let them fall +tank.fluid.coordinates[2, :] .+= 0.5 +rock_system = DEMSystem(tank.fluid, 2 * 10e5, 10e9, 0.3, acceleration=(0.0, gravity)) +boundary_system = BoundaryDEMSystem(tank.boundary, 10e7) + +# ========================================================================================== +# ==== Simulation + +semi = Semidiscretization(rock_system, boundary_system, + neighborhood_search=GridNeighborhoodSearch) + +tspan = (0.0, 5.0) +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=5000) +saving_callback = SolutionSavingCallback(dt=0.02) + +callbacks = CallbackSet(info_callback, saving_callback) + +# Use a Runge-Kutta method with automatic (error based) time step size control +sol = solve(ode, RDPK3SpFSAL49(), + abstol=1e-5, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) + reltol=1e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) + dtmax=1e-3, # Limit stepsize to prevent crashing + dt=1e-7, # Initial step size + save_everystep=false, callback=callbacks); diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index b7c0c5fe7..c254e90d9 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -42,7 +42,7 @@ include("visualization/recipes_plots.jl") export Semidiscretization, semidiscretize, restart_with! export InitialCondition export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangianSPHSystem, - BoundarySPHSystem + BoundarySPHSystem, DEMSystem, BoundaryDEMSystem export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback, PostprocessCallback, StepsizeCallback export ContinuityDensity, SummationDensity diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index d942f7397..77ba28ebf 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -150,6 +150,15 @@ end return compact_support(smoothing_kernel, smoothing_length) end +@inline function compact_support(system::BoundaryDEMSystem, neighbor::BoundaryDEMSystem) + return 0.0 +end + +@inline function compact_support(system::BoundaryDEMSystem, neighbor::DEMSystem) + # Use the compact support of the DEMSystem + return compact_support(neighbor, system) +end + @inline function compact_support(system::TotalLagrangianSPHSystem, neighbor::TotalLagrangianSPHSystem) (; smoothing_kernel, smoothing_length) = system @@ -578,6 +587,20 @@ end end # NHS updates +# To prevent hard to spot errors there is not default version + +function nhs_coords(system::DEMSystem, neighbor::DEMSystem, u) + return current_coordinates(u, neighbor) +end + +function nhs_coords(system::BoundaryDEMSystem, + neighbor::Union{BoundaryDEMSystem, DEMSystem}, u) + return nothing +end +function nhs_coords(system::DEMSystem, neighbor::BoundaryDEMSystem, u) + return nothing +end + function nhs_coords(system::FluidSystem, neighbor::FluidSystem, u) return current_coordinates(u, neighbor) diff --git a/src/schemes/boundary/dummy_particles/dummy_particles.jl b/src/schemes/boundary/dummy_particles/dummy_particles.jl index 86ffe678c..059f25cba 100644 --- a/src/schemes/boundary/dummy_particles/dummy_particles.jl +++ b/src/schemes/boundary/dummy_particles/dummy_particles.jl @@ -52,6 +52,7 @@ struct BoundaryModelDummyParticles{DC, ELTYPE <: Real, VECTOR, SE, K, V, COR, C} density_calculator, smoothing_kernel, smoothing_length; viscosity=nothing, state_equation=nothing, correction=nothing) + ELTYPE = eltype(initial_density) pressure = initial_boundary_pressure(initial_density, density_calculator, state_equation) NDIMS = ndims(smoothing_kernel) diff --git a/src/schemes/boundary/rhs.jl b/src/schemes/boundary/rhs.jl index 965e55062..201a3bbb9 100644 --- a/src/schemes/boundary/rhs.jl +++ b/src/schemes/boundary/rhs.jl @@ -1,7 +1,8 @@ # Interaction of boundary with other systems function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, neighborhood_search, - particle_system::BoundarySPHSystem, neighbor_system) + particle_system::BoundarySystem, + neighbor_system) # TODO Solids and moving boundaries should be considered in the continuity equation return dv end diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl index 3e2e04772..b38e94c14 100644 --- a/src/schemes/boundary/system.jl +++ b/src/schemes/boundary/system.jl @@ -40,6 +40,53 @@ struct BoundarySPHSystem{BM, NDIMS, IC, CO, M, IM, CA} <: BoundarySystem{NDIMS} end end +""" + BoundaryDEMSystem(initial_condition, normal_stiffness) + +System for boundaries modeled by boundary particles. +The interaction between fluid and boundary particles is specified by the boundary model. + +!!! warning "Experimental Implementation" + This is an experimental feature and may change in a future releases. + +""" +struct BoundaryDEMSystem{NDIMS, ELTYPE <: Real, ARRAY1D, ARRAY2D} <: BoundarySystem{NDIMS} + coordinates :: ARRAY2D # [dimension, particle] + radius :: ARRAY1D # [particle] + normal_stiffness :: ELTYPE + + function BoundaryDEMSystem(initial_condition, normal_stiffness) + coordinates = initial_condition.coordinates + radius = 0.5 * initial_condition.particle_spacing * + ones(length(initial_condition.mass)) + NDIMS = size(coordinates, 1) + + return new{NDIMS, eltype(coordinates), typeof(radius), typeof(coordinates)}(coordinates, + radius, + normal_stiffness) + end +end + +function Base.show(io::IO, system::BoundaryDEMSystem) + @nospecialize system # reduce precompilation time + + print(io, "BoundaryDEMSystem{", ndims(system), "}(") + print(io, system.boundary_model) + print(io, ") with ", nparticles(system), " particles") +end + +function Base.show(io::IO, ::MIME"text/plain", system::BoundaryDEMSystem) + @nospecialize system # reduce precompilation time + + if get(io, :compact, false) + show(io, system) + else + summary_header(io, "BoundaryDEMSystem{$(ndims(system))}") + summary_line(io, "#particles", nparticles(system)) + summary_footer(io) + end +end + """ BoundaryMovement(movement_function, is_moving; moving_particles=nothing) @@ -119,7 +166,17 @@ function Base.show(io::IO, ::MIME"text/plain", system::BoundarySPHSystem) end end -timer_name(::BoundarySPHSystem) = "boundary" +timer_name(::Union{BoundarySPHSystem, BoundaryDEMSystem}) = "boundary" + +@inline function Base.eltype(system::Union{BoundarySPHSystem, BoundaryDEMSystem}) + eltype(system.coordinates) +end + +# This does not account for moving boundaries, but it's only used to initialize the +# neighborhood search, anyway. +@inline function initial_coordinates(system::Union{BoundarySPHSystem, BoundaryDEMSystem}) + system.coordinates +end function (movement::BoundaryMovement)(system, t) (; coordinates, cache) = system @@ -151,13 +208,13 @@ function (movement::Nothing)(system, t) return system end -@inline function nparticles(system::BoundarySPHSystem) - length(system.boundary_model.hydrodynamic_mass) +@inline function nparticles(system::Union{BoundaryDEMSystem, BoundarySPHSystem}) + size(system.coordinates, 2) end # No particle positions are advanced for boundary systems, # except when using `BoundaryModelDummyParticles` with `ContinuityDensity`. -@inline function n_moving_particles(system::BoundarySPHSystem) +@inline function n_moving_particles(system::Union{BoundarySPHSystem, BoundaryDEMSystem}) return 0 end @@ -165,13 +222,14 @@ end return nparticles(system) end -@inline u_nvariables(system::BoundarySPHSystem) = 0 +@inline u_nvariables(system::Union{BoundarySPHSystem, BoundaryDEMSystem}) = 0 # For BoundaryModelDummyParticles with ContinuityDensity, this needs to be 1. # For all other models and density calculators, it's irrelevant. @inline v_nvariables(system::BoundarySPHSystem) = 1 +@inline v_nvariables(system::BoundaryDEMSystem) = 0 -@inline function current_coordinates(u, system::BoundarySPHSystem) +@inline function current_coordinates(u, system::Union{BoundarySPHSystem, BoundaryDEMSystem}) return system.coordinates end @@ -248,11 +306,13 @@ function update_final!(system::BoundarySPHSystem, v, u, v_ode, u_ode, semi, t) return system end -function write_u0!(u0, system::BoundarySPHSystem) +function write_u0!(u0, system::Union{BoundarySPHSystem, BoundaryDEMSystem}) return u0 end -function write_v0!(v0, system::BoundarySPHSystem) +function write_v0!(v0, + system::Union{BoundarySPHSystem, + BoundaryDEMSystem}) return v0 end diff --git a/src/schemes/schemes.jl b/src/schemes/schemes.jl index 5eabef80e..b81a7768b 100644 --- a/src/schemes/schemes.jl +++ b/src/schemes/schemes.jl @@ -3,6 +3,7 @@ include("fluid/fluid.jl") include("boundary/boundary.jl") include("solid/total_lagrangian_sph/total_lagrangian_sph.jl") +include("solid/discrete_element_method/discrete_element_method.jl") # Monaghan-Kajtar repulsive boundary particles require the `BoundarySPHSystem` # and the `TotalLagrangianSPHSystem`. include("boundary/monaghan_kajtar/monaghan_kajtar.jl") @@ -12,3 +13,4 @@ include("fluid/weakly_compressible_sph/rhs.jl") include("fluid/entropically_damped_sph/rhs.jl") include("boundary/rhs.jl") include("solid/total_lagrangian_sph/rhs.jl") +include("solid/discrete_element_method/rhs.jl") diff --git a/src/schemes/solid/discrete_element_method/discrete_element_method.jl b/src/schemes/solid/discrete_element_method/discrete_element_method.jl new file mode 100644 index 000000000..c67930ab8 --- /dev/null +++ b/src/schemes/solid/discrete_element_method/discrete_element_method.jl @@ -0,0 +1 @@ +include("system.jl") diff --git a/src/schemes/solid/discrete_element_method/rhs.jl b/src/schemes/solid/discrete_element_method/rhs.jl new file mode 100644 index 000000000..0362c3a04 --- /dev/null +++ b/src/schemes/solid/discrete_element_method/rhs.jl @@ -0,0 +1,110 @@ +# Calculate the interaction forces between particles in a Discrete Element Method (DEM) system. +# +# This function loops over all pairs of particles and their neighbors within a set distance. +# When particles overlap (i.e., they come into contact), a normal force is applied to resolve the overlap. +# This force is computed based on Hertzian contact mechanics typical for DEM simulations. +# The force is proportional to the amount of overlap and is directed along the normal between the particle centers. +# The magnitude of the force is determined by the stiffness constant `normal_stiffness` and the overlap distance. +function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, + u_neighbor_system, neighborhood_search, particle_system::DEMSystem, + neighbor_system::Union{BoundaryDEMSystem, DEMSystem}) + (; damping_coefficient) = particle_system + + E_a = particle_system.elastic_modulus + nu_a = particle_system.poissons_ratio + + system_coords = current_coordinates(u_particle_system, particle_system) + neighbor_coords = current_coordinates(u_neighbor_system, neighbor_system) + + for_particle_neighbor(particle_system, neighbor_system, system_coords, neighbor_coords, + neighborhood_search) do particle, neighbor, pos_diff, distance + m_a = particle_system.mass[particle] + + r_a = particle_system.radius[particle] + r_b = neighbor_system.radius[neighbor] + + # Only consider particles with a distance > 0 + distance < sqrt(eps()) && return + + # Calculate the overlap (penetration depth) between the two particles + overlap = r_a + r_b - distance + + # If there's no overlap, no force needs to be applied + overlap <= 0 && return + + # Normal direction from neighbor to particle + normal = pos_diff / distance + + interaction_force = collision_force(particle_system, neighbor_system, overlap, + normal, v_particle_system, + v_neighbor_system, E_a, + nu_a, r_a, r_b, m_a, + damping_coefficient, particle, neighbor) + + # Update the acceleration of the particle based on the force and its mass + for i in 1:ndims(particle_system) + dv[i, particle] += interaction_force[i] / m_a + end + + # TODO: use update callback + position_correction!(neighbor_system, u_particle_system, overlap, normal, particle) + end + + return dv +end + +@inline function collision_force(particle_system, neighbor_system::BoundaryDEMSystem, + overlap, normal, v_particle_system, + v_neighbor_system, E_a, nu_a, + r_a, r_b, m_a, damping_coefficient, + particle, neighbor) + return neighbor_system.normal_stiffness * overlap * normal +end + +@inline function collision_force(particle_system, neighbor_system::DEMSystem, overlap, + normal, v_particle_system, v_neighbor_system, + E_a, nu_a, r_a, r_b, + m_a, damping_coefficient, particle, neighbor) + m_b = neighbor_system.mass[neighbor] + E_b = neighbor_system.elastic_modulus + nu_b = neighbor_system.poissons_ratio + + v_a = current_velocity(v_particle_system, particle_system, particle) + v_b = current_velocity(v_neighbor_system, neighbor_system, neighbor) + + v_ab = v_a - v_b + rel_vel_normal = dot(v_ab, normal) + + # Compute effective modulus for both systems + E_star = 1 / ((1 - nu_a^2) / E_a + (1 - nu_b^2) / E_b) + + # Compute effective radius for the interaction + r_star = (r_a * r_b) / (r_a + r_b) + + # Compute stiffness constant for the interaction + normal_stiffness = (4 / 3) * E_star * sqrt(r_star * overlap) + + # Calculate effective mass for the interaction + m_star = (m_a * m_b) / (m_a + m_b) + + # Calculate critical damping coefficient + gamma_c = 2 * sqrt(m_star * normal_stiffness) + + # Compute the force magnitude using Hertzian contact mechanics with damping + force_magnitude = normal_stiffness * overlap + + damping_coefficient * gamma_c * rel_vel_normal + + return force_magnitude * normal +end + +@inline function position_correction!(neighbor_system, u_particle_system, overlap, normal, + particle) +end + +@inline function position_correction!(neighbor_system::BoundaryDEMSystem, u_particle_system, + overlap, normal, particle) + for i in 1:ndims(neighbor_system) + # Position correction to prevent penetration + u_particle_system[i, particle] -= 0.5 * overlap * normal[i] + end +end diff --git a/src/schemes/solid/discrete_element_method/system.jl b/src/schemes/solid/discrete_element_method/system.jl new file mode 100644 index 000000000..a7cd2868f --- /dev/null +++ b/src/schemes/solid/discrete_element_method/system.jl @@ -0,0 +1,116 @@ +""" + DEMSystem(initial_condition, normal_stiffness, elastic_modulus, poissons_ratio; + damping_coefficient=0.0001, acceleration=ntuple(_ -> 0.0, NDIMS), source_terms=nothing) + +Constructs a Discrete Element Method (DEM) system for numerically simulating the dynamics of +granular and particulate matter. DEM is employed to simulate and analyze the motion, +interactions, and collective behavior of assemblies of discrete, solid particles, typically +under mechanical loading. The model accounts for individual particle characteristics +and implements interaction laws that govern contact forces (normal and tangential), based on +specified material properties and contact mechanics. + +# Arguments + - `initial_condition`: Initial condition of the system, encapsulating the initial positions, + velocities, masses, and radii of particles. + - `normal_stiffness`: Normal stiffness coefficient for particle-particle and particle-wall contacts. + - `elastic_modulus`: Elastic modulus for this particle system. + - `poissons_ratio`: Poisson ratio for this particle system. + +# Keywords + - `acceleration`: Global acceleration vector applied to the system, such as gravity. Specified as + an `SVector` of length `NDIMS`, with a default of zero in each dimension. + - `source_terms`: Optional; additional forces or modifications to particle dynamics not + captured by standard DEM interactions, such as electromagnetic forces or user-defined perturbations. + - `damping_coefficient=0.0001`: Set a damping coefficient for the collision interactions. + + !!! warning "Experimental Implementation" + This is an experimental feature and may change in a future releases. +""" +struct DEMSystem{NDIMS, ELTYPE <: Real, ARRAY1D, ST} <: SolidSystem{NDIMS} + initial_condition :: InitialCondition{ELTYPE} + mass :: ARRAY1D # [particle] + radius :: ARRAY1D # [particle] + elastic_modulus :: ELTYPE + poissons_ratio :: ELTYPE + normal_stiffness :: ELTYPE + damping_coefficient :: ELTYPE + acceleration :: SVector{NDIMS, ELTYPE} + source_terms :: ST + + function DEMSystem(initial_condition, normal_stiffness, elastic_modulus, poissons_ratio; + damping_coefficient=0.0001, + acceleration=ntuple(_ -> 0.0, + ndims(initial_condition)), source_terms=nothing) + NDIMS = ndims(initial_condition) + ELTYPE = eltype(initial_condition) + + mass = copy(initial_condition.mass) + radius = 0.5 * initial_condition.particle_spacing * ones(length(mass)) + + # Make acceleration an SVector + acceleration_ = SVector(acceleration...) + if length(acceleration_) != NDIMS + throw(ArgumentError("`acceleration` must be of length $NDIMS for a $(NDIMS)D problem")) + end + + return new{NDIMS, ELTYPE, typeof(mass), + typeof(source_terms)}(initial_condition, mass, radius, elastic_modulus, + poissons_ratio, normal_stiffness, + damping_coefficient, acceleration_, source_terms) + end +end + +function Base.show(io::IO, system::DEMSystem) + @nospecialize system # reduce precompilation time + + print(io, "DEMSystem{", ndims(system), "}(") + print(io, system.initial_condition) + print(io, ", ", system.elastic_modulus) + print(io, ", ", system.poissons_ratio) + print(io, ", ", system.normal_stiffness) + print(io, ", ", system.damping_coefficient) + print(io, ") with ", TrixiParticles.nparticles(system), " particles") +end + +function Base.show(io::IO, ::MIME"text/plain", system::DEMSystem) + @nospecialize system # reduce precompilation time + + if get(io, :compact, false) + show(io, system) + else + TrixiParticles.summary_header(io, "DEMSystem{$(ndims(system))}") + TrixiParticles.summary_line(io, "#particles", TrixiParticles.nparticles(system)) + TrixiParticles.summary_line(io, "elastic_modulus", system.elastic_modulus) + TrixiParticles.summary_line(io, "poissons_ratio", system.poissons_ratio) + TrixiParticles.summary_line(io, "normal_stiffness", system.normal_stiffness) + TrixiParticles.summary_line(io, "damping_coefficient", system.damping_coefficient) + TrixiParticles.summary_footer(io) + end +end + +timer_name(::DEMSystem) = "solid" + +function TrixiParticles.write_u0!(u0, system::DEMSystem) + u0 .= system.initial_condition.coordinates + return u0 +end + +function TrixiParticles.write_v0!(v0, system::DEMSystem) + v0 .= system.initial_condition.velocity + return v0 +end + +# Nothing to initialize for this system +initialize!(system::DEMSystem, neighborhood_search) = system + +function compact_support(system::DEMSystem, neighbor::DEMSystem) + # we for now assume that the compact support is 3 * radius + # todo: needs to be changed for more complex simulations + return 3 * max(maximum(system.radius), maximum(neighbor.radius)) +end + +function compact_support(system::DEMSystem, neighbor) + # we for now assume that the compact support is 3 * radius + # todo: needs to be changed for more complex simulations + return 3 * maximum(system.radius) +end diff --git a/src/visualization/write2vtk.jl b/src/visualization/write2vtk.jl index 9db300269..81581494e 100644 --- a/src/visualization/write2vtk.jl +++ b/src/visualization/write2vtk.jl @@ -98,6 +98,7 @@ function trixi2vtk(v, u, t, system, periodic_box; output_directory="out", prefix end vtk_grid(file, points, cells) do vtk + # dispatches based on the different system types e.g. FluidSystem, TotalLagrangianSPHSystem write2vtk!(vtk, v, u, t, system, write_meta_data=write_meta_data) # Store particle index @@ -172,6 +173,12 @@ function trixi2vtk(coordinates; output_directory="out", prefix="", filename="coo return file end +function write2vtk!(vtk, v, u, t, system; write_meta_data=true) + vtk["velocity"] = view(v, 1:ndims(system), :) + + return vtk +end + function write2vtk!(vtk, v, u, t, system::FluidSystem; write_meta_data=true) vtk["velocity"] = view(v, 1:ndims(system), :) vtk["density"] = [particle_density(v, system, particle) diff --git a/test/examples/examples.jl b/test/examples/examples.jl index 15c90c672..6a41db723 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -235,3 +235,14 @@ end end end + +@testset verbose=true "DEM" begin + @trixi_testset "dem/rectangular_tank_2d.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, + joinpath(examples_dir(), "dem", + "rectangular_tank_2d.jl"), + tspan=(0.0, 0.1)) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end +end diff --git a/test/systems/boundary_system.jl b/test/systems/boundary_system.jl index c47d278bd..dad30e71c 100644 --- a/test/systems/boundary_system.jl +++ b/test/systems/boundary_system.jl @@ -111,14 +111,14 @@ system = BoundarySPHSystem(initial_condition, model) - show_compact = "BoundarySPHSystem{2}((hydrodynamic_mass = 3,), nothing) with 1 particles" + show_compact = "BoundarySPHSystem{2}((hydrodynamic_mass = 3,), nothing) with 2 particles" @test repr(system) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ │ BoundarySPHSystem{2} │ │ ════════════════════ │ - │ #particles: ………………………………………………… 1 │ + │ #particles: ………………………………………………… 2 │ │ boundary model: ……………………………………… (hydrodynamic_mass = 3,) │ │ movement function: ……………………………… nothing │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" diff --git a/test/systems/dem_system.jl b/test/systems/dem_system.jl new file mode 100644 index 000000000..c7ae18bcb --- /dev/null +++ b/test/systems/dem_system.jl @@ -0,0 +1,26 @@ +include("../test_util.jl") +@testset verbose=true "DEMSystem" begin + @trixi_testset "show" begin + coordinates = [1.0 2.0 + 1.0 2.0] + mass = [1.25, 1.5] + density = [990.0, 1000.0] + + initial_condition = InitialCondition(; coordinates, mass, density) + system = DEMSystem(initial_condition, 2 * 10^5, 10e9, 0.3, acceleration=(0.0, 10.0)) + + show_compact = "DEMSystem{2}(InitialCondition{Float64}(-1.0, [1.0 2.0; 1.0 2.0], [0.0 0.0; 0.0 0.0], [1.25, 1.5], [990.0, 1000.0], [0.0, 0.0]), 1.0e10, 0.3, 200000.0, 0.0001) with 2 particles" + @test repr(system) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ DEMSystem{2} │ + │ ════════════ │ + │ #particles: ………………………………………………… 2 │ + │ elastic_modulus: …………………………………… 1.0e10 │ + │ poissons_ratio: ……………………………………… 0.3 │ + │ normal_stiffness: ………………………………… 200000.0 │ + │ damping_coefficient: ………………………… 0.0001 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", system) == show_box + end +end diff --git a/test/systems/systems.jl b/test/systems/systems.jl index 6fe2ac7fb..6aff417b3 100644 --- a/test/systems/systems.jl +++ b/test/systems/systems.jl @@ -2,3 +2,4 @@ include("wcsph_system.jl") include("edac_system.jl") include("solid_system.jl") include("boundary_system.jl") +include("dem_system.jl") From 84138a571e43d9ceaf6147f5acc7a936e51a1894 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 3 May 2024 09:53:32 +0200 Subject: [PATCH 15/49] Document proper release management (#508) --- Project.toml | 2 +- docs/src/development.md | 47 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index e05dfae01..f31196a7f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TrixiParticles" uuid = "66699cd8-9c01-4e9d-a059-b96c86d16b3a" authors = ["erik.faulhaber <44124897+efaulhaber@users.noreply.github.com>"] -version = "0.1.0" +version = "0.1.1-dev" [deps] CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" diff --git a/docs/src/development.md b/docs/src/development.md index 94f65d540..8ffb75af5 100644 --- a/docs/src/development.md +++ b/docs/src/development.md @@ -20,4 +20,49 @@ Alternatively, run ```bash python3 -m http.server -d docs/build ``` -and open `localhost:8000` in your webbrowser. \ No newline at end of file +and open `localhost:8000` in your webbrowser. + + +## Release management + +To create a new release for TrixiParticles.jl, perform the following steps: +1) Make sure that all PRs and changes that you want to go into the release are merged to + `main` and that the latest commit on `main` has passed all CI tests. +2) Determine the currently released version of TrixiParticles.jl, e.g., on the + [release page](https://github.com/trixi-framework/TrixiParticles.jl/releases). For this manual, + we will assume that the latest release was `v0.2.3`. +3) Decide on the next version number. We follow [semantic versioning](https://semver.org/), + thus each version is of the form `vX.Y.Z` where `X` is the major version, `Y` the minor + version, and `Z` the patch version. In this manual, we assume that the major version is + always `0`, thus the decision process on the new version is as follows: + * If the new release contains *breaking changes* (i.e., user code might not work as + before without modifications), increase the *minor* version by one and set the + *patch* version to zero. In our example, the new version should thus be `v0.3.0`. + * If the new release only contains minor modifications and/or bug fixes, the *minor* + version is kept as-is and the *patch* version is increased by one. In our example, the + new version should thus be `v0.2.4`. +4) Edit the `version` string in the + [`Project.toml`](https://github.com/trixi-framework/TrixiParticles.jl/blob/main/Project.toml) + and set it to the new version. Push/merge this change to `main`. +5) Go to GitHub and add a comment to the commit that you would like to become the new + release (typically this will be the commit where you just updated the version). You can + comment on a commit by going to the + [commit overview](https://github.com/trixi-framework/TrixiParticles.jl/commits/main/) and clicking + on the title of the commit. The comment should contain the following text: + ``` + @JuliaRegistrator register + ``` +6) Wait for the magic to happen! Specifically, JuliaRegistrator will create a new PR to the + Julia registry with the new release information. After a grace period of ~15 minutes, + this PR will be merged automatically. A short while after, + [TagBot](https://github.com/trixi-framework/TrixiParticles.jl/blob/main/.github/workflows/TagBot.yml) + will create a new release of TrixiParticles.jl in our GitHub repository. +7) Once the new release has been created, the new version can be obtained through the Julia + package manager as usual. +8) To make sure people do not mistake the latest state of `main` as the latest release, we + set the version in the `Project.toml` to a *development* version. The development version + should be the latest released version, with the patch version incremented by one, and the + `-dev` suffix added. For example, if you just released `v0.3.0`, the new development + version should be `v0.3.1-dev`. If you just released `v0.2.4`, the new development + version should be `v0.2.5-dev`. + From b30bbbf2f1e7306befcfaf720af7e890811dd0e3 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Fri, 3 May 2024 10:23:36 +0200 Subject: [PATCH 16/49] Mark Release V0.1.1 (#510) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f31196a7f..8a9753c62 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TrixiParticles" uuid = "66699cd8-9c01-4e9d-a059-b96c86d16b3a" authors = ["erik.faulhaber <44124897+efaulhaber@users.noreply.github.com>"] -version = "0.1.1-dev" +version = "0.1.1" [deps] CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" From 107f05381852b31c87cad406f792437c6f1cc03e Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Fri, 3 May 2024 11:01:53 +0200 Subject: [PATCH 17/49] Set to development version (#511) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8a9753c62..b1155e96f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TrixiParticles" uuid = "66699cd8-9c01-4e9d-a059-b96c86d16b3a" authors = ["erik.faulhaber <44124897+efaulhaber@users.noreply.github.com>"] -version = "0.1.1" +version = "0.1.2-dev" [deps] CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" From e3bddb9c00d912642a391cf195cc6c20a44c9238 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 3 May 2024 15:40:50 +0200 Subject: [PATCH 18/49] Recursively convert (adapt) `Semidiscretization` object to GPU types with Adapt.jl (#492) * Adapt `Semidiscretization` before running the simulation * Modify constructors to be compatible with Adapt.jl * Only adapt the semidiscretization when using an explicit data type * Adapt `DensityDiffusionAntuono` * Adapt `BoundaryModelMonaghanKajtar` to GPUs * Add comments * Remove unnecessary types --- Project.toml | 1 + src/TrixiParticles.jl | 4 +- src/general/gpu.jl | 34 ++++++ src/general/semidiscretization.jl | 23 +++- .../dummy_particles/dummy_particles.jl | 47 ++++---- .../monaghan_kajtar/monaghan_kajtar.jl | 14 +-- src/schemes/boundary/system.jl | 39 ++++--- .../density_diffusion.jl | 24 +++-- .../fluid/weakly_compressible_sph/system.jl | 102 +++++++++--------- 9 files changed, 175 insertions(+), 113 deletions(-) create mode 100644 src/general/gpu.jl diff --git a/Project.toml b/Project.toml index b1155e96f..12ffee051 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ authors = ["erik.faulhaber <44124897+efaulhaber@users.noreply.github.com>"] version = "0.1.2-dev" [deps] +Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index c254e90d9..64d62ea21 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -2,6 +2,7 @@ module TrixiParticles using Reexport: @reexport +using Adapt: Adapt using CSV: CSV using Dates using DataFrames: DataFrame @@ -34,8 +35,9 @@ include("setups/setups.jl") include("schemes/schemes.jl") # Note that `semidiscretization.jl` depends on the system types and has to be -# included separately. +# included separately. `gpu.jl` in turn depends on the semidiscretization type. include("general/semidiscretization.jl") +include("general/gpu.jl") include("visualization/write2vtk.jl") include("visualization/recipes_plots.jl") diff --git a/src/general/gpu.jl b/src/general/gpu.jl new file mode 100644 index 000000000..377ea8b72 --- /dev/null +++ b/src/general/gpu.jl @@ -0,0 +1,34 @@ +# Adapt.jl provides a function `adapt(to, x)`, which adapts a value `x` to `to`. +# In practice, this means that we can use `adapt(CuArray, system)` to adapt a system to +# the `CuArray` type. +# What this does is that it converts all `Array`s inside this system to `CuArray`s, +# therefore copying them to the GPU. +# In order to run a simulation on a GPU, we want to call `adapt(T, semi)` to adapt the +# `Semidiscretization` `semi` to the GPU array type `T` (e.g. `CuArray`). +# +# `Adapt.@adapt_structure` automatically generates the `adapt` function for our custom types. +Adapt.@adapt_structure Semidiscretization +Adapt.@adapt_structure WeaklyCompressibleSPHSystem +Adapt.@adapt_structure DensityDiffusionAntuono +Adapt.@adapt_structure BoundarySPHSystem +Adapt.@adapt_structure BoundaryModelDummyParticles +Adapt.@adapt_structure BoundaryModelMonaghanKajtar + +# The initial conditions are only used for initialization, which happens before `adapt`ing +# the semidiscretization, so we don't need to store `InitialCondition`s on the GPU. +# To save precious GPU memory, we replace initial conditions by `nothing`. +function Adapt.adapt_structure(to, ic::InitialCondition) + return nothing +end + +# `adapt(CuArray, ::SVector)::SVector`, but `adapt(Array, ::SVector)::Vector`. +# We don't want to change the type of the `SVector` here. +function Adapt.adapt_structure(to::typeof(Array), svector::SVector) + return svector +end + +# `adapt(CuArray, ::UnitRange)::UnitRange`, but `adapt(Array, ::UnitRange)::Vector`. +# We don't want to change the type of the `UnitRange` here. +function Adapt.adapt_structure(to::typeof(Array), range::UnitRange) + return range +end diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 77ba28ebf..36eb54766 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -257,7 +257,7 @@ timespan: (0.0, 1.0) u0: ([...], [...]) *this line is ignored by filter* ``` """ -function semidiscretize(semi, tspan; reset_threads=true) +function semidiscretize(semi, tspan; reset_threads=true, data_type=nothing) (; systems) = semi @assert all(system -> eltype(system) === eltype(systems[1]), systems) @@ -283,8 +283,16 @@ function semidiscretize(semi, tspan; reset_threads=true) sizes_u = (u_nvariables(system) * n_moving_particles(system) for system in systems) sizes_v = (v_nvariables(system) * n_moving_particles(system) for system in systems) - u0_ode = Vector{ELTYPE}(undef, sum(sizes_u)) - v0_ode = Vector{ELTYPE}(undef, sum(sizes_v)) + + if isnothing(data_type) + # Use CPU vectors and the optimized CPU code + u0_ode = Vector{ELTYPE}(undef, sum(sizes_u)) + v0_ode = Vector{ELTYPE}(undef, sum(sizes_v)) + else + # Use the specified data type, e.g., `CuArray` or `ROCArray` + u0_ode = data_type{ELTYPE}(undef, sum(sizes_u)) + v0_ode = data_type{ELTYPE}(undef, sum(sizes_v)) + end # Set initial condition foreach_system(semi) do system @@ -295,6 +303,15 @@ function semidiscretize(semi, tspan; reset_threads=true) write_v0!(v0_system, system) end + if !isnothing(data_type) + # Convert all arrays to the correct array type. When e.g. `data_type=CuArray`, + # this will convert all `Array`s to `CuArray`s, moving data to the GPU. + # See the comments in general/gpu.jl for more details. + semi_adapted = Adapt.adapt(data_type, semi) + + return DynamicalODEProblem(kick!, drift!, v0_ode, u0_ode, tspan, semi_adapted) + end + return DynamicalODEProblem(kick!, drift!, v0_ode, u0_ode, tspan, semi) end diff --git a/src/schemes/boundary/dummy_particles/dummy_particles.jl b/src/schemes/boundary/dummy_particles/dummy_particles.jl index 059f25cba..0f7b42696 100644 --- a/src/schemes/boundary/dummy_particles/dummy_particles.jl +++ b/src/schemes/boundary/dummy_particles/dummy_particles.jl @@ -47,32 +47,29 @@ struct BoundaryModelDummyParticles{DC, ELTYPE <: Real, VECTOR, SE, K, V, COR, C} viscosity :: V correction :: COR cache :: C +end - function BoundaryModelDummyParticles(initial_density, hydrodynamic_mass, - density_calculator, smoothing_kernel, - smoothing_length; viscosity=nothing, - state_equation=nothing, correction=nothing) - ELTYPE = eltype(initial_density) - pressure = initial_boundary_pressure(initial_density, density_calculator, - state_equation) - NDIMS = ndims(smoothing_kernel) - - n_particles = length(initial_density) - - cache = (; create_cache_model(viscosity, n_particles, NDIMS)..., - create_cache_model(initial_density, density_calculator)...) - cache = (; - create_cache_model(correction, initial_density, NDIMS, - n_particles)..., cache...) - - return new{typeof(density_calculator), eltype(initial_density), - typeof(pressure), typeof(state_equation), - typeof(smoothing_kernel), typeof(viscosity), - typeof(correction), typeof(cache)}(pressure, hydrodynamic_mass, - state_equation, density_calculator, - smoothing_kernel, smoothing_length, - viscosity, correction, cache) - end +# The default constructor needs to be accessible for Adapt.jl to work with this struct. +# See the comments in general/gpu.jl for more details. +function BoundaryModelDummyParticles(initial_density, hydrodynamic_mass, + density_calculator, smoothing_kernel, + smoothing_length; viscosity=nothing, + state_equation=nothing, correction=nothing) + pressure = initial_boundary_pressure(initial_density, density_calculator, + state_equation) + NDIMS = ndims(smoothing_kernel) + + n_particles = length(initial_density) + + cache = (; create_cache_model(viscosity, n_particles, NDIMS)..., + create_cache_model(initial_density, density_calculator)...) + cache = (; create_cache_model(correction, initial_density, NDIMS, + n_particles)..., cache...) + + return BoundaryModelDummyParticles(pressure, hydrodynamic_mass, + state_equation, density_calculator, + smoothing_kernel, smoothing_length, + viscosity, correction, cache) end @doc raw""" diff --git a/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl b/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl index ff45c7986..6af5c4aab 100644 --- a/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl +++ b/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl @@ -20,13 +20,15 @@ struct BoundaryModelMonaghanKajtar{ELTYPE <: Real, VECTOR, V} boundary_particle_spacing :: ELTYPE hydrodynamic_mass :: VECTOR # Vector{ELTYPE} viscosity :: V +end - function BoundaryModelMonaghanKajtar(K, beta, boundary_particle_spacing, mass; - viscosity=nothing) - return new{typeof(K), typeof(mass), - typeof(viscosity)}(K, beta, boundary_particle_spacing, - mass, viscosity) - end +# The default constructor needs to be accessible for Adapt.jl to work with this struct. +# See the comments in general/gpu.jl for more details. +function BoundaryModelMonaghanKajtar(K, beta, boundary_particle_spacing, mass; + viscosity=nothing) + return BoundaryModelMonaghanKajtar(K, convert(typeof(K), beta), + boundary_particle_spacing, + mass, viscosity) end function Base.show(io::IO, model::BoundaryModelMonaghanKajtar) diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl index b38e94c14..334f2d30d 100644 --- a/src/schemes/boundary/system.jl +++ b/src/schemes/boundary/system.jl @@ -19,25 +19,34 @@ struct BoundarySPHSystem{BM, NDIMS, IC, CO, M, IM, CA} <: BoundarySystem{NDIMS} ismoving :: IM # Ref{Bool} (to make a mutable field compatible with GPUs) cache :: CA - function BoundarySPHSystem(initial_condition, model; movement=nothing) - coordinates = copy(initial_condition.coordinates) - NDIMS = size(coordinates, 1) - ismoving = Ref(!isnothing(movement)) + # This constructor is necessary for Adapt.jl to work with this struct. + # See the comments in general/gpu.jl for more details. + function BoundarySPHSystem(initial_condition, coordinates, boundary_model, movement, + ismoving, cache) + new{typeof(boundary_model), size(coordinates, 1), + typeof(initial_condition), typeof(coordinates), + typeof(movement), typeof(ismoving), typeof(cache)}(initial_condition, + coordinates, boundary_model, + movement, + ismoving, cache) + end +end - cache = create_cache_boundary(movement, initial_condition) +function BoundarySPHSystem(initial_condition, model; movement=nothing) + coordinates = copy(initial_condition.coordinates) + ismoving = Ref(!isnothing(movement)) - if movement !== nothing && isempty(movement.moving_particles) - # Default is an empty vector, since the number of particles is not known when - # instantiating `BoundaryMovement`. - resize!(movement.moving_particles, nparticles(initial_condition)) - movement.moving_particles .= collect(1:nparticles(initial_condition)) - end + cache = create_cache_boundary(movement, initial_condition) - return new{typeof(model), NDIMS, typeof(initial_condition), - typeof(coordinates), typeof(movement), typeof(ismoving), - typeof(cache)}(initial_condition, coordinates, model, movement, - ismoving, cache) + if movement !== nothing && isempty(movement.moving_particles) + # Default is an empty vector, since the number of particles is not known when + # instantiating `BoundaryMovement`. + resize!(movement.moving_particles, nparticles(initial_condition)) + movement.moving_particles .= collect(1:nparticles(initial_condition)) end + + return BoundarySPHSystem(initial_condition, coordinates, model, + movement, ismoving, cache) end """ diff --git a/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl b/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl index 21c81f991..a1710052d 100644 --- a/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl +++ b/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl @@ -139,22 +139,26 @@ struct DensityDiffusionAntuono{NDIMS, ELTYPE, ARRAY2D, ARRAY3D} <: DensityDiffus correction_matrix :: ARRAY3D # Array{ELTYPE, 3}: [i, j, particle] normalized_density_gradient :: ARRAY2D # Array{ELTYPE, 2}: [i, particle] - function DensityDiffusionAntuono(initial_condition; delta) - NDIMS = ndims(initial_condition) - ELTYPE = eltype(initial_condition) - correction_matrix = Array{ELTYPE, 3}(undef, NDIMS, NDIMS, - nparticles(initial_condition)) - - normalized_density_gradient = Array{ELTYPE, 2}(undef, NDIMS, - nparticles(initial_condition)) - - new{NDIMS, ELTYPE, + function DensityDiffusionAntuono(delta, correction_matrix, normalized_density_gradient) + new{size(correction_matrix, 1), typeof(delta), typeof(normalized_density_gradient), typeof(correction_matrix)}(delta, correction_matrix, normalized_density_gradient) end end +function DensityDiffusionAntuono(initial_condition; delta) + NDIMS = ndims(initial_condition) + ELTYPE = eltype(initial_condition) + correction_matrix = Array{ELTYPE, 3}(undef, NDIMS, NDIMS, + nparticles(initial_condition)) + + normalized_density_gradient = Array{ELTYPE, 2}(undef, NDIMS, + nparticles(initial_condition)) + + return DensityDiffusionAntuono(delta, correction_matrix, normalized_density_gradient) +end + @inline Base.ndims(::DensityDiffusionAntuono{NDIMS}) where {NDIMS} = NDIMS function Base.show(io::IO, density_diffusion::DensityDiffusionAntuono) diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index 708873cf6..ff9ec3c95 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -55,61 +55,57 @@ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, pressure_acceleration_formulation :: PF source_terms :: ST cache :: C +end + +# The default constructor needs to be accessible for Adapt.jl to work with this struct. +# See the comments in general/gpu.jl for more details. +function WeaklyCompressibleSPHSystem(initial_condition, + density_calculator, state_equation, + smoothing_kernel, smoothing_length; + pressure_acceleration=nothing, + viscosity=nothing, density_diffusion=nothing, + acceleration=ntuple(_ -> 0.0, + ndims(smoothing_kernel)), + correction=nothing, source_terms=nothing) + NDIMS = ndims(initial_condition) + ELTYPE = eltype(initial_condition) + n_particles = nparticles(initial_condition) + + mass = copy(initial_condition.mass) + pressure = similar(initial_condition.pressure) + + if ndims(smoothing_kernel) != NDIMS + throw(ArgumentError("smoothing kernel dimensionality must be $NDIMS for a $(NDIMS)D problem")) + end - function WeaklyCompressibleSPHSystem(initial_condition, - density_calculator, state_equation, - smoothing_kernel, smoothing_length; - pressure_acceleration=nothing, - viscosity=nothing, density_diffusion=nothing, - acceleration=ntuple(_ -> 0.0, - ndims(smoothing_kernel)), - correction=nothing, source_terms=nothing) - NDIMS = ndims(initial_condition) - ELTYPE = eltype(initial_condition) - n_particles = nparticles(initial_condition) - - mass = copy(initial_condition.mass) - pressure = similar(initial_condition.pressure) - - if ndims(smoothing_kernel) != NDIMS - throw(ArgumentError("smoothing kernel dimensionality must be $NDIMS for a $(NDIMS)D problem")) - end - - # Make acceleration an SVector - acceleration_ = SVector(acceleration...) - if length(acceleration_) != NDIMS - throw(ArgumentError("`acceleration` must be of length $NDIMS for a $(NDIMS)D problem")) - end - - if correction isa ShepardKernelCorrection && - density_calculator isa ContinuityDensity - throw(ArgumentError("`ShepardKernelCorrection` cannot be used with `ContinuityDensity`")) - end - - pressure_acceleration = choose_pressure_acceleration_formulation(pressure_acceleration, - density_calculator, - NDIMS, ELTYPE, - correction) - - cache = create_cache_density(initial_condition, density_calculator) - cache = (; - create_cache_wcsph(correction, initial_condition.density, NDIMS, - n_particles)..., cache...) - - return new{NDIMS, ELTYPE, typeof(initial_condition), - typeof(mass), typeof(pressure), - typeof(density_calculator), - typeof(state_equation), typeof(smoothing_kernel), - typeof(viscosity), typeof(density_diffusion), - typeof(correction), typeof(pressure_acceleration), - typeof(source_terms), typeof(cache)}(initial_condition, mass, pressure, - density_calculator, state_equation, - smoothing_kernel, smoothing_length, - acceleration_, viscosity, - density_diffusion, correction, - pressure_acceleration, - source_terms, cache) + # Make acceleration an SVector + acceleration_ = SVector(acceleration...) + if length(acceleration_) != NDIMS + throw(ArgumentError("`acceleration` must be of length $NDIMS for a $(NDIMS)D problem")) end + + if correction isa ShepardKernelCorrection && + density_calculator isa ContinuityDensity + throw(ArgumentError("`ShepardKernelCorrection` cannot be used with `ContinuityDensity`")) + end + + pressure_acceleration = choose_pressure_acceleration_formulation(pressure_acceleration, + density_calculator, + NDIMS, ELTYPE, + correction) + + cache = create_cache_density(initial_condition, density_calculator) + cache = (; + create_cache_wcsph(correction, initial_condition.density, NDIMS, + n_particles)..., cache...) + + return WeaklyCompressibleSPHSystem(initial_condition, mass, pressure, + density_calculator, state_equation, + smoothing_kernel, smoothing_length, + acceleration_, viscosity, + density_diffusion, correction, + pressure_acceleration, + source_terms, cache) end create_cache_wcsph(correction, density, NDIMS, nparticles) = (;) From f8ff1957f3b679497fb3a7cd776a6ee4deaba0cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 4 May 2024 15:51:54 +0200 Subject: [PATCH 19/49] CompatHelper: add new compat entry for Adapt at version 4, (keep existing compat) (#515) Co-authored-by: CompatHelper Julia --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 12ffee051..0cd815aa0 100644 --- a/Project.toml +++ b/Project.toml @@ -28,6 +28,7 @@ TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" [compat] +Adapt = "4" CSV = "0.10" DataFrames = "1.6" DiffEqCallbacks = "2.25, 3" From 1ccfa7dbc47373bb338499b115f84485624102c5 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 6 May 2024 11:50:28 +0200 Subject: [PATCH 20/49] Change compat of Adapt to 3 (#517) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 0cd815aa0..ecc2186c9 100644 --- a/Project.toml +++ b/Project.toml @@ -28,7 +28,7 @@ TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" [compat] -Adapt = "4" +Adapt = "3" CSV = "0.10" DataFrames = "1.6" DiffEqCallbacks = "2.25, 3" From 3445bd0046f40505acd0ef91e06f8a2210f1f828 Mon Sep 17 00:00:00 2001 From: Niklas Neher <73897120+LasNikas@users.noreply.github.com> Date: Mon, 6 May 2024 12:26:13 +0200 Subject: [PATCH 21/49] Reopen #390: Update callback (#440) * add `UpdateCallback` * fix typo * apply formatter * remove update bool * adapt docstring * implement suggestions * remove redundant comment --- src/TrixiParticles.jl | 2 +- src/callbacks/callbacks.jl | 1 + src/callbacks/update.jl | 130 ++++++++++++++++++++++++++++++++++++ test/callbacks/callbacks.jl | 1 + test/callbacks/update.jl | 48 +++++++++++++ 5 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/callbacks/update.jl create mode 100644 test/callbacks/update.jl diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index 64d62ea21..a7ca8e274 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -46,7 +46,7 @@ export InitialCondition export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangianSPHSystem, BoundarySPHSystem, DEMSystem, BoundaryDEMSystem export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback, - PostprocessCallback, StepsizeCallback + PostprocessCallback, StepsizeCallback, UpdateCallback export ContinuityDensity, SummationDensity export PenaltyForceGanzenmueller export SchoenbergCubicSplineKernel, SchoenbergQuarticSplineKernel, diff --git a/src/callbacks/callbacks.jl b/src/callbacks/callbacks.jl index 08b28187a..f86f9a586 100644 --- a/src/callbacks/callbacks.jl +++ b/src/callbacks/callbacks.jl @@ -31,3 +31,4 @@ include("solution_saving.jl") include("density_reinit.jl") include("post_process.jl") include("stepsize.jl") +include("update.jl") diff --git a/src/callbacks/update.jl b/src/callbacks/update.jl new file mode 100644 index 000000000..ca3b5f114 --- /dev/null +++ b/src/callbacks/update.jl @@ -0,0 +1,130 @@ +struct UpdateCallback{I} + interval::I +end + +""" + UpdateCallback(; interval::Integer, dt=0.0) + +Callback to update quantities either at the end of every `interval` time steps or +in intervals of `dt` in terms of integration time by adding additional `tstops` +(note that this may change the solution). + +# Keywords +- `interval=1`: Update quantities at the end of every `interval` time steps. +- `dt`: Update quantities in regular intervals of `dt` in terms of integration time + by adding additional `tstops` (note that this may change the solution). +""" +function UpdateCallback(; interval::Integer=-1, dt=0.0) + if dt > 0 && interval !== -1 + throw(ArgumentError("Setting both interval and dt is not supported!")) + end + + # Update in intervals in terms of simulation time + if dt > 0 + interval = Float64(dt) + + # Update every time step (default) + elseif interval == -1 + interval = 1 + end + + update_callback! = UpdateCallback(interval) + + if dt > 0 + # Add a `tstop` every `dt`, and save the final solution. + return PeriodicCallback(update_callback!, dt, + initialize=initial_update!, + save_positions=(false, false)) + else + # The first one is the `condition`, the second the `affect!` + return DiscreteCallback(update_callback!, update_callback!, + initialize=initial_update!, + save_positions=(false, false)) + end +end + +# `initialize` +function initial_update!(cb, u, t, integrator) + # The `UpdateCallback` is either `cb.affect!` (with `DiscreteCallback`) + # or `cb.affect!.affect!` (with `PeriodicCallback`). + # Let recursive dispatch handle this. + + initial_update!(cb.affect!, u, t, integrator) +end + +initial_update!(cb::UpdateCallback, u, t, integrator) = cb(integrator) + +# `condition` +function (update_callback!::UpdateCallback)(u, t, integrator) + (; interval) = update_callback! + + return condition_integrator_interval(integrator, interval) +end + +# `affect!` +function (update_callback!::UpdateCallback)(integrator) + t = integrator.t + semi = integrator.p + v_ode, u_ode = integrator.u.x + + # Update quantities that are stored in the systems. These quantities (e.g. pressure) + # still have the values from the last stage of the previous step if not updated here. + update_systems_and_nhs(v_ode, u_ode, semi, t) + + # Other updates might be added here later (e.g. Transport Velocity Formulation). + # @trixi_timeit timer() "update open boundary" foreach_system(semi) do system + # update_open_boundary_eachstep!(system, v_ode, u_ode, semi, t) + # end + # + # @trixi_timeit timer() "update TVF" foreach_system(semi) do system + # update_transport_velocity_eachstep!(system, v_ode, u_ode, semi, t) + # end + + # Tell OrdinaryDiffEq that `u` has been modified + u_modified!(integrator, true) + + return integrator +end + +function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:UpdateCallback}) + @nospecialize cb # reduce precompilation time + print(io, "UpdateCallback(interval=", cb.affect!.interval, ")") +end + +function Base.show(io::IO, + cb::DiscreteCallback{<:Any, + <:PeriodicCallbackAffect{<:UpdateCallback}}) + @nospecialize cb # reduce precompilation time + print(io, "UpdateCallback(dt=", cb.affect!.affect!.interval, ")") +end + +function Base.show(io::IO, ::MIME"text/plain", + cb::DiscreteCallback{<:Any, <:UpdateCallback}) + @nospecialize cb # reduce precompilation time + + if get(io, :compact, false) + show(io, cb) + else + update_cb = cb.affect! + setup = [ + "interval" => update_cb.interval, + ] + summary_box(io, "UpdateCallback", setup) + end +end + +function Base.show(io::IO, ::MIME"text/plain", + cb::DiscreteCallback{<:Any, + <:PeriodicCallbackAffect{<:UpdateCallback}}) + @nospecialize cb # reduce precompilation time + + if get(io, :compact, false) + show(io, cb) + else + update_cb = cb.affect!.affect! + setup = [ + "dt" => update_cb.interval, + ] + summary_box(io, "UpdateCallback", setup) + end +end diff --git a/test/callbacks/callbacks.jl b/test/callbacks/callbacks.jl index 0a97850bd..274fde65d 100644 --- a/test/callbacks/callbacks.jl +++ b/test/callbacks/callbacks.jl @@ -2,5 +2,6 @@ include("info.jl") include("stepsize.jl") include("postprocess.jl") + include("update.jl") include("solution_saving.jl") end diff --git a/test/callbacks/update.jl b/test/callbacks/update.jl new file mode 100644 index 000000000..180f0bb97 --- /dev/null +++ b/test/callbacks/update.jl @@ -0,0 +1,48 @@ +@testset verbose=true "UpdateCallback" begin + @testset verbose=true "show" begin + # Default + callback0 = UpdateCallback() + + show_compact = "UpdateCallback(interval=1)" + @test repr(callback0) == show_compact + + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ UpdateCallback │ + │ ══════════════ │ + │ interval: ……………………………………………………… 1 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", callback0) == show_box + + callback1 = UpdateCallback(interval=11) + + show_compact = "UpdateCallback(interval=11)" + @test repr(callback1) == show_compact + + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ UpdateCallback │ + │ ══════════════ │ + │ interval: ……………………………………………………… 11 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", callback1) == show_box + + callback2 = UpdateCallback(dt=1.2) + + show_compact = "UpdateCallback(dt=1.2)" + @test repr(callback2) == show_compact + + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ UpdateCallback │ + │ ══════════════ │ + │ dt: ……………………………………………………………………… 1.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", callback2) == show_box + end + + @testset "Illegal Input" begin + error_str = "Setting both interval and dt is not supported!" + @test_throws ArgumentError(error_str) UpdateCallback(dt=0.1, interval=1) + end +end From 06bd864a0950f9e5d6e169b26ff2f4f65d354ce8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 May 2024 11:37:13 +0200 Subject: [PATCH 22/49] CompatHelper: bump compat for Adapt to 4, (keep existing compat) (#519) Co-authored-by: CompatHelper Julia --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index ecc2186c9..f47a5b2c6 100644 --- a/Project.toml +++ b/Project.toml @@ -28,7 +28,7 @@ TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" [compat] -Adapt = "3" +Adapt = "3, 4" CSV = "0.10" DataFrames = "1.6" DiffEqCallbacks = "2.25, 3" From e92c9fcc89768f994d7df04455f6024decd1a4d7 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 15 May 2024 15:50:01 +0200 Subject: [PATCH 23/49] Create output directory for post process callback (#527) --- src/callbacks/post_process.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/callbacks/post_process.jl b/src/callbacks/post_process.jl index 25a8d051b..e2e98a2a0 100644 --- a/src/callbacks/post_process.jl +++ b/src/callbacks/post_process.jl @@ -276,9 +276,9 @@ end # After the simulation has finished, this function is called to write the data to a JSON file function write_postprocess_callback(pp::PostprocessCallback) - if isempty(pp.data) - return - end + isempty(pp.data) && return + + mkpath(pp.output_directory) data = Dict{String, Any}() write_meta_data!(data) From 657c284694dc5d69e73eb6a0e83eb0893236e245 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Wed, 15 May 2024 16:19:32 +0200 Subject: [PATCH 24/49] Manually increase CairoMakie Version (#528) --- test/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Project.toml b/test/Project.toml index a591c7415..ef78271e3 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -16,7 +16,7 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] CSV = "0.10" -CairoMakie = "0.11" +CairoMakie = "0.12" DataFrames = "1.6" GLM = "1.9" Glob = "1.3" From 228b86dc642e0a844e2f6ba722d01de1071e78fa Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Thu, 16 May 2024 15:35:14 +0200 Subject: [PATCH 25/49] Move neighborhood search to a new standalone package (#516) * Move neighborhood search to a new standalone package * Remove Morton.jl dependency * Remove tests for neighborhood search * Remove ThreadingUtilities.jl dependency * Fix interpolation * Add docs for neighborhood search * Dispatch `update_nhs!` instead of `nhs_coords` * Reformat code * Update NHS with coordinates of both systems * Initialize NHS with coordinates of both systems * Fix broken functions * Rename package to PointNeighbors.jl * Reformat code * Resort Project.toml * Change PointNeighbors compat to 0.2 * Fix `InitialCondition` set operations * Fix interpolation and tests * Fix docs * Fix example tests * Fix doctests * Fix n-body * Add more comments --- .github/workflows/Documenter.yml | 2 + Project.toml | 6 +- docs/src/general/neighborhood_search.md | 46 +- examples/n_body/n_body_system.jl | 9 +- src/TrixiParticles.jl | 6 +- src/general/general.jl | 1 + src/general/initial_condition.jl | 5 +- src/general/interpolation.jl | 13 +- src/general/neighborhood_search.jl | 11 + src/general/semidiscretization.jl | 205 +++++---- src/neighborhood_search/grid_nhs.jl | 423 ------------------ .../neighborhood_search.jl | 110 ----- src/neighborhood_search/trivial_nhs.jl | 97 ---- src/visualization/write2vtk.jl | 3 +- test/count_allocations.jl | 6 +- test/neighborhood_search/grid_nhs.jl | 272 ----------- .../neighborhood_search.jl | 2 - test/neighborhood_search/trivial_nhs.jl | 10 - .../schemes/solid/total_lagrangian_sph/rhs.jl | 2 +- test/systems/solid_system.jl | 2 +- test/unittest.jl | 1 - 21 files changed, 208 insertions(+), 1024 deletions(-) create mode 100644 src/general/neighborhood_search.jl delete mode 100644 src/neighborhood_search/grid_nhs.jl delete mode 100644 src/neighborhood_search/neighborhood_search.jl delete mode 100644 src/neighborhood_search/trivial_nhs.jl delete mode 100644 test/neighborhood_search/grid_nhs.jl delete mode 100644 test/neighborhood_search/neighborhood_search.jl delete mode 100644 test/neighborhood_search/trivial_nhs.jl diff --git a/.github/workflows/Documenter.yml b/.github/workflows/Documenter.yml index 941fef735..4cc31a611 100644 --- a/.github/workflows/Documenter.yml +++ b/.github/workflows/Documenter.yml @@ -8,10 +8,12 @@ on: paths-ignore: - '.github/workflows/ci.yml' - '.github/workflows/CompatHelper.yml' + - '.github/workflows/TagBot.yml' pull_request: paths-ignore: - '.github/workflows/ci.yml' - '.github/workflows/CompatHelper.yml' + - '.github/workflows/TagBot.yml' workflow_dispatch: concurrency: diff --git a/Project.toml b/Project.toml index f47a5b2c6..20b40306c 100644 --- a/Project.toml +++ b/Project.toml @@ -13,8 +13,8 @@ FastPow = "c0e83750-1142-43a8-81cf-6c956b72b4d1" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -Morton = "2a6d852e-3fac-5a38-885c-fe708af2d09e" MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" +PointNeighbors = "1c4d5385-0a27-49de-8e2c-43b175c8985c" Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" @@ -22,7 +22,6 @@ Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StrideArrays = "d1fa6d79-ef01-42a6-86c9-f7c551f8593b" -ThreadingUtilities = "8290d209-cae3-49c0-8002-c8c24d57dab5" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" @@ -35,7 +34,6 @@ DiffEqCallbacks = "2.25, 3" FastPow = "0.1" ForwardDiff = "0.10" JSON = "0.21" -Morton = "0.1" MuladdMacro = "0.2" Polyester = "0.7.5" RecipesBase = "1" @@ -43,8 +41,8 @@ Reexport = "1" SciMLBase = "1, 2" StaticArrays = "1" StrideArrays = "0.1" -ThreadingUtilities = "0.5" TimerOutputs = "0.5" TrixiBase = "0.1" +PointNeighbors = "0.2" WriteVTK = "1" julia = "1.9" diff --git a/docs/src/general/neighborhood_search.md b/docs/src/general/neighborhood_search.md index 5a1512b6d..e611f5350 100644 --- a/docs/src/general/neighborhood_search.md +++ b/docs/src/general/neighborhood_search.md @@ -1,6 +1,44 @@ # Neighborhood Search -```@autodocs -Modules = [TrixiParticles] -Pages = map(file -> joinpath("neighborhood_search", file), readdir(joinpath("..", "src", "neighborhood_search"))) -``` +The neighborhood search is the most essential component for performance. +We provide several implementations in the package +[PointNeighbors.jl](https://github.com/trixi-framework/PointNeighbors.jl). +See the docs of this package for an overview and a comparison of different implementations. + +!!! note "Usage" + To run a simulation with a neighborhood search implementation, just pass the type + to the constructor of the [`Semidiscretization`](@ref): + ```jldoctest semi_example; output=false, setup = :(using TrixiParticles; trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "hydrostatic_water_column_2d.jl"), sol=nothing); system1 = fluid_system; system2 = boundary_system) + semi = Semidiscretization(system1, system2, + neighborhood_search=GridNeighborhoodSearch) + + # output + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Semidiscretization │ + │ ══════════════════ │ + │ #spatial dimensions: ………………………… 2 │ + │ #systems: ……………………………………………………… 2 │ + │ neighborhood search: ………………………… GridNeighborhoodSearch │ + │ total #particles: ………………………………… 636 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘ + ``` + The keyword arguments `periodic_box_min_corner` and `periodic_box_max_corner` mentioned + in the PointNeighbors.jl docs can also be passed to the + [`Semidiscretization`](@ref) and will internally be forwarded to the neighborhood search. + See the docs of [`Semidiscretization`](@ref) for more details. + ```jldoctest semi_example; output = false + semi = Semidiscretization(system1, system2, + neighborhood_search=GridNeighborhoodSearch, + periodic_box_min_corner=[0.0, -0.25], + periodic_box_max_corner=[1.0, 0.75]) + + # output + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Semidiscretization │ + │ ══════════════════ │ + │ #spatial dimensions: ………………………… 2 │ + │ #systems: ……………………………………………………… 2 │ + │ neighborhood search: ………………………… GridNeighborhoodSearch │ + │ total #particles: ………………………………… 636 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘ + ``` diff --git a/examples/n_body/n_body_system.jl b/examples/n_body/n_body_system.jl index 068eae953..96b6cdebf 100644 --- a/examples/n_body/n_body_system.jl +++ b/examples/n_body/n_body_system.jl @@ -31,9 +31,12 @@ function TrixiParticles.write_v0!(v0, system::NBodySystem) end # NHS update -function TrixiParticles.nhs_coords(system::NBodySystem, - neighbor::NBodySystem, u) - return u +function TrixiParticles.update_nhs!(neighborhood_search, + system::NBodySystem, neighbor::NBodySystem, + u_system, u_neighbor) + TrixiParticles.PointNeighbors.update!(neighborhood_search, + u_system, u_neighbor, + particles_moving=(true, true)) end function TrixiParticles.compact_support(system::NBodySystem, diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index a7ca8e274..c95451a25 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -11,7 +11,6 @@ using FastPow: @fastpow using ForwardDiff: ForwardDiff using JSON: JSON using LinearAlgebra: norm, dot, I, tr, inv, pinv, det -using Morton: cartesian2morton using MuladdMacro: @muladd using Polyester: Polyester, @batch using Printf: @printf, @sprintf @@ -21,16 +20,16 @@ using SciMLBase: CallbackSet, DiscreteCallback, DynamicalODEProblem, u_modified! @reexport using StaticArrays: SVector using StaticArrays: @SMatrix, SMatrix, setindex using StrideArrays: PtrArray, StaticInt -using ThreadingUtilities using TimerOutputs: TimerOutput, TimerOutputs, print_timer, reset_timer! using TrixiBase: trixi_include +@reexport using PointNeighbors: TrivialNeighborhoodSearch, GridNeighborhoodSearch +using PointNeighbors: PointNeighbors, for_particle_neighbor using WriteVTK: vtk_grid, MeshCell, VTKCellTypes, paraview_collection, vtk_save # util needs to be first because of macro @trixi_timeit include("util.jl") include("callbacks/callbacks.jl") include("general/general.jl") -include("neighborhood_search/neighborhood_search.jl") include("setups/setups.jl") include("schemes/schemes.jl") @@ -59,7 +58,6 @@ export DensityDiffusion, DensityDiffusionMolteniColagrossi, DensityDiffusionFerr export BoundaryModelMonaghanKajtar, BoundaryModelDummyParticles, AdamiPressureExtrapolation, PressureMirroring, PressureZeroing export BoundaryMovement -export GridNeighborhoodSearch, TrivialNeighborhoodSearch export examples_dir, validation_dir, trixi_include export trixi2vtk export RectangularTank, RectangularShape, SphereShape diff --git a/src/general/general.jl b/src/general/general.jl index 6651c03c5..4c9531683 100644 --- a/src/general/general.jl +++ b/src/general/general.jl @@ -26,3 +26,4 @@ include("system.jl") include("interpolation.jl") include("file_system.jl") include("custom_quantities.jl") +include("neighborhood_search.jl") diff --git a/src/general/initial_condition.jl b/src/general/initial_condition.jl index 9ede75648..8e94b1615 100644 --- a/src/general/initial_condition.jl +++ b/src/general/initial_condition.jl @@ -291,11 +291,10 @@ function find_too_close_particles(coords1, coords2, max_distance) result = Int[] nhs = GridNeighborhoodSearch{NDIMS}(max_distance, size(coords2, 2)) - TrixiParticles.initialize!(nhs, coords2) + PointNeighbors.initialize!(nhs, coords1, coords2) # We are modifying the vector `result`, so this cannot be parallel - TrixiParticles.for_particle_neighbor(coords1, coords2, nhs, - parallel=false) do particle, _, _, _ + for_particle_neighbor(coords1, coords2, nhs, parallel=false) do particle, _, _, _ if !(particle in result) append!(result, particle) end diff --git a/src/general/interpolation.jl b/src/general/interpolation.jl index fb917f91a..80116a605 100644 --- a/src/general/interpolation.jl +++ b/src/general/interpolation.jl @@ -485,7 +485,7 @@ end function process_neighborhood_searches(semi, u_ode, ref_system, smoothing_length) if isapprox(smoothing_length, ref_system.smoothing_length) # Update existing NHS - update_nhs(u_ode, semi) + update_nhs!(semi, u_ode) neighborhood_searches = semi.neighborhood_searches[system_indices(ref_system, semi)] else ref_smoothing_kernel = ref_system.smoothing_kernel @@ -494,7 +494,8 @@ function process_neighborhood_searches(semi, u_ode, ref_system, smoothing_length u = wrap_u(u_ode, system, semi) system_coords = current_coordinates(u, system) old_nhs = get_neighborhood_search(ref_system, system, semi) - nhs = copy_neighborhood_search(old_nhs, search_radius, system_coords) + nhs = PointNeighbors.copy_neighborhood_search(old_nhs, search_radius, + system_coords, system_coords) return nhs end end @@ -534,13 +535,15 @@ end system_coords = current_coordinates(u, system) # This is basically `for_particle_neighbor` unrolled - for particle in eachneighbor(point_coords, nhs) + for particle in PointNeighbors.eachneighbor(point_coords, nhs) coords = extract_svector(system_coords, Val(ndims(system)), particle) pos_diff = point_coords - coords distance2 = dot(pos_diff, pos_diff) - pos_diff, distance2 = compute_periodic_distance(pos_diff, distance2, - search_radius, periodic_box) + pos_diff, distance2 = PointNeighbors.compute_periodic_distance(pos_diff, + distance2, + search_radius, + periodic_box) if distance2 > search_radius^2 continue end diff --git a/src/general/neighborhood_search.jl b/src/general/neighborhood_search.jl new file mode 100644 index 000000000..0713f153d --- /dev/null +++ b/src/general/neighborhood_search.jl @@ -0,0 +1,11 @@ +# Loop over all pairs of particles and neighbors within the kernel cutoff. +# `f(particle, neighbor, pos_diff, distance)` is called for every particle-neighbor pair. +# By default, loop over `each_moving_particle(system)`. +function PointNeighbors.for_particle_neighbor(f, system, neighbor_system, + system_coords, neighbor_coords, + neighborhood_search; + particles=each_moving_particle(system), + parallel=true) + for_particle_neighbor(f, system_coords, neighbor_coords, neighborhood_search, + particles=particles, parallel=parallel) +end diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 36eb54766..e699f4bf0 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -13,8 +13,8 @@ the keyword argument `neighborhood_search`. A value of `nothing` means no neighb # Keywords - `neighborhood_search`: The type of neighborhood search to be used in the simulation. - By default, the [`GridNeighborhoodSearch`](@ref) is used. - Use [`TrivialNeighborhoodSearch`](@ref) or `nothing` to loop + By default, the `GridNeighborhoodSearch` is used. + Use `TrivialNeighborhoodSearch` or `nothing` to loop over all particles (no neighborhood search). - `periodic_box_min_corner`: In order to use a (rectangular) periodic domain, pass the coordinates of the domain corner in negative coordinate @@ -140,7 +140,8 @@ function create_neighborhood_search(system, neighbor, ::Val{GridNeighborhoodSear periodic_box_max_corner=periodic_box_max_corner, threaded_nhs_update=threaded_nhs_update) # Initialize neighborhood search - initialize!(search, initial_coordinates(neighbor)) + PointNeighbors.initialize!(search, initial_coordinates(system), + initial_coordinates(neighbor)) return search end @@ -437,7 +438,7 @@ function update_systems_and_nhs(v_ode, u_ode, semi, t) end # Update NHS - @trixi_timeit timer() "update nhs" update_nhs(u_ode, semi) + @trixi_timeit timer() "update nhs" update_nhs!(semi, u_ode) # Second update step. # This is used to calculate density and pressure of the fluid systems @@ -467,14 +468,16 @@ function update_systems_and_nhs(v_ode, u_ode, semi, t) end end -function update_nhs(u_ode, semi) +function update_nhs!(semi, u_ode) # Update NHS for each pair of systems foreach_system(semi) do system + u_system = wrap_u(u_ode, system, semi) + foreach_system(semi) do neighbor u_neighbor = wrap_u(u_ode, neighbor, semi) neighborhood_search = get_neighborhood_search(system, neighbor, semi) - update!(neighborhood_search, nhs_coords(system, neighbor, u_neighbor)) + update_nhs!(neighborhood_search, system, neighbor, u_system, u_neighbor) end end end @@ -604,82 +607,120 @@ end end # NHS updates -# To prevent hard to spot errors there is not default version - -function nhs_coords(system::DEMSystem, neighbor::DEMSystem, u) - return current_coordinates(u, neighbor) -end - -function nhs_coords(system::BoundaryDEMSystem, - neighbor::Union{BoundaryDEMSystem, DEMSystem}, u) - return nothing -end -function nhs_coords(system::DEMSystem, neighbor::BoundaryDEMSystem, u) - return nothing -end - -function nhs_coords(system::FluidSystem, - neighbor::FluidSystem, u) - return current_coordinates(u, neighbor) -end - -function nhs_coords(system::FluidSystem, - neighbor::TotalLagrangianSPHSystem, u) - return current_coordinates(u, neighbor) -end - -function nhs_coords(system::FluidSystem, - neighbor::BoundarySPHSystem, u) - if neighbor.ismoving[] - return current_coordinates(u, neighbor) - end - - # Don't update - return nothing -end - -function nhs_coords(system::TotalLagrangianSPHSystem, - neighbor::FluidSystem, u) - return current_coordinates(u, neighbor) -end - -function nhs_coords(system::TotalLagrangianSPHSystem, - neighbor::TotalLagrangianSPHSystem, u) - # Don't update - return nothing -end - -function nhs_coords(system::TotalLagrangianSPHSystem, - neighbor::BoundarySPHSystem, u) - if neighbor.ismoving[] - return current_coordinates(u, neighbor) - end - - # Don't update - return nothing -end - -function nhs_coords(system::BoundarySPHSystem, - neighbor::FluidSystem, u) - # Don't update - return nothing -end - -function nhs_coords(system::BoundarySPHSystem{<:BoundaryModelDummyParticles}, - neighbor::FluidSystem, u) - return current_coordinates(u, neighbor) -end - -function nhs_coords(system::BoundarySPHSystem, - neighbor::TotalLagrangianSPHSystem, u) - # Don't update - return nothing -end - -function nhs_coords(system::BoundarySPHSystem, - neighbor::BoundarySPHSystem, u) - # Don't update - return nothing +# To prevent hard-to-find bugs, there is not default version +function update_nhs!(neighborhood_search, + system::FluidSystem, + neighbor::Union{FluidSystem, TotalLagrangianSPHSystem}, + u_system, u_neighbor) + # The current coordinates of fluids and solids change over time + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(true, true)) +end + +function update_nhs!(neighborhood_search, + system::FluidSystem, neighbor::BoundarySPHSystem, + u_system, u_neighbor) + # Boundary coordinates only change over time when `neighbor.ismoving[]` + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(true, neighbor.ismoving[])) +end + +function update_nhs!(neighborhood_search, + system::TotalLagrangianSPHSystem, neighbor::FluidSystem, + u_system, u_neighbor) + # The current coordinates of fluids and solids change over time + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(true, true)) +end + +function update_nhs!(neighborhood_search, + system::TotalLagrangianSPHSystem, neighbor::TotalLagrangianSPHSystem, + u_system, u_neighbor) + # Don't update. Neighborhood search works on the initial coordinates, which don't change. + return neighborhood_search +end + +function update_nhs!(neighborhood_search, + system::TotalLagrangianSPHSystem, neighbor::BoundarySPHSystem, + u_system, u_neighbor) + # The current coordinates of solids change over time. + # Boundary coordinates only change over time when `neighbor.ismoving[]`. + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(true, neighbor.ismoving[])) +end + +function update_nhs!(neighborhood_search, + system::BoundarySPHSystem, + neighbor::Union{FluidSystem, TotalLagrangianSPHSystem, + BoundarySPHSystem}, + u_system, u_neighbor) + # Don't update. This NHS is never used. + return neighborhood_search +end + +function update_nhs!(neighborhood_search, + system::BoundarySPHSystem{<:BoundaryModelDummyParticles}, + neighbor::Union{FluidSystem, TotalLagrangianSPHSystem}, + u_system, u_neighbor) + # Depending on the density calculator of the boundary model, this NHS is used for + # - kernel summation (`SummationDensity`) + # - continuity equation (`ContinuityDensity`) + # - pressure extrapolation (`AdamiPressureExtrapolation`) + # + # Boundary coordinates only change over time when `neighbor.ismoving[]`. + # The current coordinates of fluids and solids change over time. + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(system.ismoving[], true)) +end + +function update_nhs!(neighborhood_search, + system::BoundarySPHSystem{<:BoundaryModelDummyParticles}, + neighbor::BoundarySPHSystem, + u_system, u_neighbor) + # `system` coordinates only change over time when `system.ismoving[]`. + # `neighbor` coordinates only change over time when `neighbor.ismoving[]`. + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(system.ismoving[], neighbor.ismoving[])) +end + +function update_nhs!(neighborhood_search, + system::DEMSystem, neighbor::DEMSystem, + u_system, u_neighbor) + # Both coordinates change over time + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(true, true)) +end + +function update_nhs!(neighborhood_search, + system::DEMSystem, neighbor::BoundaryDEMSystem, + u_system, u_neighbor) + # DEM coordinates change over time, the boundary coordinates don't + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(true, false)) +end + +function update_nhs!(neighborhood_search, + system::BoundaryDEMSystem, + neighbor::Union{DEMSystem, BoundaryDEMSystem}, + u_system, u_neighbor) + # Don't update. This NHS is never used. + return neighborhood_search end function check_configuration(systems) diff --git a/src/neighborhood_search/grid_nhs.jl b/src/neighborhood_search/grid_nhs.jl deleted file mode 100644 index 82526a6b4..000000000 --- a/src/neighborhood_search/grid_nhs.jl +++ /dev/null @@ -1,423 +0,0 @@ -@doc raw""" - GridNeighborhoodSearch{NDIMS}(search_radius, n_particles; periodic_box_min_corner=nothing, - periodic_box_max_corner=nothing, threaded_nhs_update=true) - -Simple grid-based neighborhood search with uniform search radius. -The domain is divided into a regular grid. -For each (non-empty) grid cell, a list of particles in this cell is stored. -Instead of representing a finite domain by an array of cells, a potentially infinite domain -is represented by storing cell lists in a hash table (using Julia's `Dict` data structure), -indexed by the cell index tuple -```math -\left( \left\lfloor \frac{x}{d} \right\rfloor, \left\lfloor \frac{y}{d} \right\rfloor \right) \quad \text{or} \quad -\left( \left\lfloor \frac{x}{d} \right\rfloor, \left\lfloor \frac{y}{d} \right\rfloor, \left\lfloor \frac{z}{d} \right\rfloor \right), -``` -where ``x, y, z`` are the space coordinates and ``d`` is the search radius. - -To find particles within the search radius around a point, only particles in the neighboring -cells are considered. - -See also (Chalela et al., 2021), (Ihmsen et al. 2011, Section 4.4). - -As opposed to (Ihmsen et al. 2011), we do not sort the particles in any way, -since not sorting makes our implementation a lot faster (although less parallelizable). - -# Arguments -- `NDIMS`: Number of dimensions. -- `search_radius`: The uniform search radius. -- `n_particles`: Total number of particles. - -# Keywords -- `periodic_box_min_corner`: In order to use a (rectangular) periodic domain, pass the - coordinates of the domain corner in negative coordinate - directions. -- `periodic_box_max_corner`: In order to use a (rectangular) periodic domain, pass the - coordinates of the domain corner in positive coordinate - directions. -- `threaded_nhs_update=true`: Can be used to deactivate thread parallelization in the neighborhood search update. - This can be one of the largest sources of variations between simulations - with different thread numbers due to particle ordering changes. - -!!! warning "Internal use only" - Please note that this constructor is intended for internal use only. It is *not* part of - the public API of TrixiParticles.jl, and it thus can altered (or be removed) at any time - without it being considered a breaking change. - - To run a simulation with this neighborhood search, just pass the type to the constructor - of [`Semidiscretization`](@ref): - ```jldoctest semi_example; output=false, setup = :(trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "hydrostatic_water_column_2d.jl"), sol=nothing); system1 = fluid_system; system2 = boundary_system) - semi = Semidiscretization(system1, system2, - neighborhood_search=GridNeighborhoodSearch) - - # output - ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ Semidiscretization │ - │ ══════════════════ │ - │ #spatial dimensions: ………………………… 2 │ - │ #systems: ……………………………………………………… 2 │ - │ neighborhood search: ………………………… GridNeighborhoodSearch │ - │ total #particles: ………………………………… 636 │ - └──────────────────────────────────────────────────────────────────────────────────────────────────┘ - ``` - The keyword arguments `periodic_box_min_corner` and `periodic_box_max_corner` explained - above can also be passed to the [`Semidiscretization`](@ref) and will internally be - forwarded to the neighborhood search: - ```jldoctest semi_example; output = false - semi = Semidiscretization(system1, system2, - neighborhood_search=GridNeighborhoodSearch, - periodic_box_min_corner=[0.0, -0.25], - periodic_box_max_corner=[1.0, 0.75]) - - # output - ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ Semidiscretization │ - │ ══════════════════ │ - │ #spatial dimensions: ………………………… 2 │ - │ #systems: ……………………………………………………… 2 │ - │ neighborhood search: ………………………… GridNeighborhoodSearch │ - │ total #particles: ………………………………… 636 │ - └──────────────────────────────────────────────────────────────────────────────────────────────────┘ - ``` - -## References -- M. Chalela, E. Sillero, L. Pereyra, M.A. Garcia, J.B. Cabral, M. Lares, M. Merchán. - "GriSPy: A Python package for fixed-radius nearest neighbors search". - In: Astronomy and Computing 34 (2021). - [doi: 10.1016/j.ascom.2020.100443](https://doi.org/10.1016/j.ascom.2020.100443) -- Markus Ihmsen, Nadir Akinci, Markus Becker, Matthias Teschner. - "A Parallel SPH Implementation on Multi-Core CPUs". - In: Computer Graphics Forum 30.1 (2011), pages 99–112. - [doi: 10.1111/J.1467-8659.2010.01832.X](https://doi.org/10.1111/J.1467-8659.2010.01832.X) -""" -struct GridNeighborhoodSearch{NDIMS, ELTYPE, PB} - hashtable :: Dict{NTuple{NDIMS, Int}, Vector{Int}} - search_radius :: ELTYPE - empty_vector :: Vector{Int} # Just an empty vector (used in `eachneighbor`) - cell_buffer :: Array{NTuple{NDIMS, Int}, 2} # Multithreaded buffer for `update!` - cell_buffer_indices :: Vector{Int} # Store which entries of `cell_buffer` are initialized - periodic_box :: PB - n_cells :: NTuple{NDIMS, Int} - cell_size :: NTuple{NDIMS, ELTYPE} - threaded_nhs_update :: Bool - - function GridNeighborhoodSearch{NDIMS}(search_radius, n_particles; - periodic_box_min_corner=nothing, - periodic_box_max_corner=nothing, - threaded_nhs_update=true) where {NDIMS} - ELTYPE = typeof(search_radius) - - hashtable = Dict{NTuple{NDIMS, Int}, Vector{Int}}() - empty_vector = Int[] - cell_buffer = Array{NTuple{NDIMS, Int}, 2}(undef, n_particles, Threads.nthreads()) - cell_buffer_indices = zeros(Int, Threads.nthreads()) - - if search_radius < eps() || - (periodic_box_min_corner === nothing && periodic_box_max_corner === nothing) - - # No periodicity - periodic_box = nothing - n_cells = ntuple(_ -> -1, Val(NDIMS)) - cell_size = ntuple(_ -> search_radius, Val(NDIMS)) - elseif periodic_box_min_corner !== nothing && periodic_box_max_corner !== nothing - periodic_box = PeriodicBox(periodic_box_min_corner, periodic_box_max_corner) - - # Round up search radius so that the grid fits exactly into the domain without - # splitting any cells. This might impact performance slightly, since larger - # cells mean that more potential neighbors are considered than necessary. - # Allow small tolerance to avoid inefficient larger cells due to machine - # rounding errors. - n_cells = Tuple(floor.(Int, (periodic_box.size .+ 10eps()) / search_radius)) - cell_size = Tuple(periodic_box.size ./ n_cells) - - if any(i -> i < 3, n_cells) - throw(ArgumentError("the `GridNeighborhoodSearch` needs at least 3 cells " * - "in each dimension when used with periodicity. " * - "Please use no NHS for very small problems.")) - end - else - throw(ArgumentError("`periodic_box_min_corner` and `periodic_box_max_corner` " * - "must either be both `nothing` or both an array or tuple")) - end - - new{NDIMS, ELTYPE, - typeof(periodic_box)}(hashtable, search_radius, empty_vector, - cell_buffer, cell_buffer_indices, - periodic_box, n_cells, cell_size, threaded_nhs_update) - end -end - -@inline Base.ndims(neighborhood_search::GridNeighborhoodSearch{NDIMS}) where {NDIMS} = NDIMS - -@inline function nparticles(neighborhood_search::GridNeighborhoodSearch) - return size(neighborhood_search.cell_buffer, 1) -end - -function initialize!(neighborhood_search::GridNeighborhoodSearch, ::Nothing) - # No particle coordinates function -> don't initialize. - return neighborhood_search -end - -function initialize!(neighborhood_search::GridNeighborhoodSearch{NDIMS}, - x::AbstractArray) where {NDIMS} - initialize!(neighborhood_search, i -> extract_svector(x, Val(NDIMS), i)) -end - -function initialize!(neighborhood_search::GridNeighborhoodSearch, coords_fun) - (; hashtable) = neighborhood_search - - empty!(hashtable) - - # This is needed to prevent lagging on macOS ARM. - # See https://github.com/JuliaSIMD/Polyester.jl/issues/89 - ThreadingUtilities.sleep_all_tasks() - - for particle in 1:nparticles(neighborhood_search) - # Get cell index of the particle's cell - cell = cell_coords(coords_fun(particle), neighborhood_search) - - # Add particle to corresponding cell or create cell if it does not exist - if haskey(hashtable, cell) - append!(hashtable[cell], particle) - else - hashtable[cell] = [particle] - end - end - - return neighborhood_search -end - -function update!(neighborhood_search::GridNeighborhoodSearch, ::Nothing) - # No particle coordinates function -> don't update. - return neighborhood_search -end - -function update!(neighborhood_search::GridNeighborhoodSearch{NDIMS}, - x::AbstractArray) where {NDIMS} - update!(neighborhood_search, i -> extract_svector(x, Val(NDIMS), i)) -end - -# Modify the existing hash table by moving particles into their new cells -function update!(neighborhood_search::GridNeighborhoodSearch, coords_fun) - (; hashtable, cell_buffer, cell_buffer_indices, threaded_nhs_update) = neighborhood_search - - # Reset `cell_buffer` by moving all pointers to the beginning. - cell_buffer_indices .= 0 - - # Find all cells containing particles that now belong to another cell. - # `collect` the keyset to be able to loop over it with `@threaded`. - mark_changed_cell!(neighborhood_search, hashtable, coords_fun, Val(threaded_nhs_update)) - - # This is needed to prevent lagging on macOS ARM. - # See https://github.com/JuliaSIMD/Polyester.jl/issues/89 - ThreadingUtilities.sleep_all_tasks() - - # Iterate over all marked cells and move the particles into their new cells. - for thread in 1:Threads.nthreads() - # Only the entries `1:cell_buffer_indices[thread]` are initialized for `thread`. - for i in 1:cell_buffer_indices[thread] - cell = cell_buffer[i, thread] - particles = hashtable[cell] - - # Find all particles whose coordinates do not match this cell - moved_particle_indices = (i for i in eachindex(particles) - if cell_coords(coords_fun(particles[i]), - neighborhood_search) != cell) - - # Add moved particles to new cell - for i in moved_particle_indices - particle = particles[i] - new_cell_coords = cell_coords(coords_fun(particle), neighborhood_search) - - # Add particle to corresponding cell or create cell if it does not exist - if haskey(hashtable, new_cell_coords) - append!(hashtable[new_cell_coords], particle) - else - hashtable[new_cell_coords] = [particle] - end - end - - # Remove moved particles from this cell or delete the cell if it is now empty - if count(_ -> true, moved_particle_indices) == length(particles) - delete!(hashtable, cell) - else - deleteat!(particles, moved_particle_indices) - end - end - end - - return neighborhood_search -end - -@inline function mark_changed_cell!(neighborhood_search, hashtable, coords_fun, - threaded_nhs_update::Val{true}) - @threaded for cell in collect(keys(hashtable)) - mark_changed_cell!(neighborhood_search, cell, coords_fun) - end -end - -@inline function mark_changed_cell!(neighborhood_search, hashtable, coords_fun, - threaded_nhs_update::Val{false}) - for cell in collect(keys(hashtable)) - mark_changed_cell!(neighborhood_search, cell, coords_fun) - end -end - -# Use this function barrier and unpack inside to avoid passing closures to Polyester.jl -# with `@batch` (`@threaded`). -# Otherwise, `@threaded` does not work here with Julia ARM on macOS. -# See https://github.com/JuliaSIMD/Polyester.jl/issues/88. -@inline function mark_changed_cell!(neighborhood_search, cell, coords_fun) - (; hashtable, cell_buffer, cell_buffer_indices) = neighborhood_search - - for particle in hashtable[cell] - if cell_coords(coords_fun(particle), neighborhood_search) != cell - # Mark this cell and continue with the next one. - # - # `cell_buffer` is preallocated, - # but only the entries 1:i are used for this thread. - i = cell_buffer_indices[Threads.threadid()] += 1 - cell_buffer[i, Threads.threadid()] = cell - break - end - end -end - -# 1D -@inline function eachneighbor(coords, neighborhood_search::GridNeighborhoodSearch{1}) - cell = cell_coords(coords, neighborhood_search) - x = cell[1] - # Generator of all neighboring cells to consider - neighboring_cells = ((x + i) for i in -1:1) - - # Merge all lists of particles in the neighboring cells into one iterator - Iterators.flatten(particles_in_cell(cell, neighborhood_search) - for cell in neighboring_cells) -end - -# 2D -@inline function eachneighbor(coords, neighborhood_search::GridNeighborhoodSearch{2}) - cell = cell_coords(coords, neighborhood_search) - x, y = cell - # Generator of all neighboring cells to consider - neighboring_cells = ((x + i, y + j) for i in -1:1, j in -1:1) - - # Merge all lists of particles in the neighboring cells into one iterator - Iterators.flatten(particles_in_cell(cell, neighborhood_search) - for cell in neighboring_cells) -end - -# 3D -@inline function eachneighbor(coords, neighborhood_search::GridNeighborhoodSearch{3}) - cell = cell_coords(coords, neighborhood_search) - x, y, z = cell - # Generator of all neighboring cells to consider - neighboring_cells = ((x + i, y + j, z + k) for i in -1:1, j in -1:1, k in -1:1) - - # Merge all lists of particles in the neighboring cells into one iterator - Iterators.flatten(particles_in_cell(cell, neighborhood_search) - for cell in neighboring_cells) -end - -@inline function particles_in_cell(cell_index, neighborhood_search) - (; hashtable, empty_vector) = neighborhood_search - - # Return an empty vector when `cell_index` is not a key of `hashtable` and - # reuse the empty vector to avoid allocations - return get(hashtable, periodic_cell_index(cell_index, neighborhood_search), - empty_vector) -end - -@inline function periodic_cell_index(cell_index, neighborhood_search) - (; n_cells, periodic_box) = neighborhood_search - - periodic_cell_index(cell_index, periodic_box, n_cells) -end - -@inline periodic_cell_index(cell_index, ::Nothing, n_cells) = cell_index - -@inline function periodic_cell_index(cell_index, periodic_box, n_cells) - return rem.(cell_index, n_cells, RoundDown) -end - -@inline function cell_coords(coords, neighborhood_search) - (; periodic_box, cell_size) = neighborhood_search - - return cell_coords(coords, periodic_box, cell_size) -end - -@inline function cell_coords(coords, periodic_box::Nothing, cell_size) - return Tuple(floor_to_int.(coords ./ cell_size)) -end - -@inline function cell_coords(coords, periodic_box, cell_size) - # Subtract `min_corner` to offset coordinates so that the min corner of the periodic - # box corresponds to the (0, 0) cell of the NHS. - # This way, there are no partial cells in the domain if the domain size is an integer - # multiple of the cell size (which is required, see the constructor). - offset_coords = periodic_coords(coords, periodic_box) .- periodic_box.min_corner - - return Tuple(floor_to_int.(offset_coords ./ cell_size)) -end - -# When particles end up with coordinates so big that the cell coordinates -# exceed the range of Int, then `floor(Int, i)` will fail with an InexactError. -# In this case, we can just use typemax(Int), since we can assume that particles -# that far away will not interact with anything, anyway. -# This usually indicates an instability, but we don't want the simulation to crash, -# since adaptive time integration methods may detect the instability and reject the -# time step. -# If we threw an error here, we would prevent the time integration method from -# retrying with a smaller time step, and we would thus crash perfectly fine simulations. -@inline function floor_to_int(i) - if isnan(i) || i > typemax(Int) - return typemax(Int) - elseif i < typemin(Int) - return typemin(Int) - end - - return floor(Int, i) -end - -# Sorting only really makes sense in longer simulations where particles -# end up very unordered. -# WARNING: This is currently unmaintained. -function z_index_sort!(coordinates, system) - (; mass, pressure, neighborhood_search) = system - - perm = sortperm(eachparticle(system), - by=(i -> cell_z_index(extract_svector(coordinates, system, i), - neighborhood_search))) - - permute!(mass, perm) - permute!(pressure, perm) - Base.permutecols!!(u, perm) - - return nothing -end - -@inline function cell_z_index(coords, neighborhood_search) - cell = cell_coords(coords, neighborhood_search.search_radius) .+ 1 - - return cartesian2morton(SVector(cell)) -end - -# Create a copy of a neighborhood search but with a different search radius -function copy_neighborhood_search(nhs::GridNeighborhoodSearch, search_radius, u) - if nhs.periodic_box === nothing - search = GridNeighborhoodSearch{ndims(nhs)}(search_radius, nparticles(nhs)) - else - search = GridNeighborhoodSearch{ndims(nhs)}(search_radius, nparticles(nhs), - periodic_box_min_corner=nhs.periodic_box.min_corner, - periodic_box_max_corner=nhs.periodic_box.max_corner) - end - - # Initialize neighborhood search - initialize!(search, u) - - return search -end - -# Create a copy of a neighborhood search but with a different search radius -function copy_neighborhood_search(nhs::TrivialNeighborhoodSearch, search_radius, u) - return nhs -end diff --git a/src/neighborhood_search/neighborhood_search.jl b/src/neighborhood_search/neighborhood_search.jl deleted file mode 100644 index e9062c5db..000000000 --- a/src/neighborhood_search/neighborhood_search.jl +++ /dev/null @@ -1,110 +0,0 @@ -struct PeriodicBox{NDIMS, ELTYPE} - min_corner :: SVector{NDIMS, ELTYPE} - max_corner :: SVector{NDIMS, ELTYPE} - size :: SVector{NDIMS, ELTYPE} - - function PeriodicBox(min_corner, max_corner) - new{length(min_corner), eltype(min_corner)}(min_corner, max_corner, - max_corner - min_corner) - end -end - -# Loop over all pairs of particles and neighbors within the kernel cutoff. -# `f(particle, neighbor, pos_diff, distance)` is called for every particle-neighbor pair. -# By default, loop over `each_moving_particle(system)`. -@inline function for_particle_neighbor(f, system, neighbor_system, - system_coords, neighbor_coords, neighborhood_search; - particles=each_moving_particle(system), - parallel=true) - for_particle_neighbor(f, system_coords, neighbor_coords, neighborhood_search, - particles=particles, parallel=parallel) -end - -@inline function for_particle_neighbor(f, system_coords, neighbor_coords, - neighborhood_search; - particles=axes(system_coords, 2), parallel=true) - for_particle_neighbor(f, system_coords, neighbor_coords, neighborhood_search, particles, - Val(parallel)) -end - -@inline function for_particle_neighbor(f, system_coords, neighbor_coords, - neighborhood_search, particles, parallel::Val{true}) - @threaded for particle in particles - for_particle_neighbor_inner(f, system_coords, neighbor_coords, neighborhood_search, - particle) - end - - return nothing -end - -@inline function for_particle_neighbor(f, system_coords, neighbor_coords, - neighborhood_search, particles, parallel::Val{false}) - for particle in particles - for_particle_neighbor_inner(f, system_coords, neighbor_coords, neighborhood_search, - particle) - end - - return nothing -end - -# Use this function barrier and unpack inside to avoid passing closures to Polyester.jl -# with `@batch` (`@threaded`). -# Otherwise, `@threaded` does not work here with Julia ARM on macOS. -# See https://github.com/JuliaSIMD/Polyester.jl/issues/88. -@inline function for_particle_neighbor_inner(f, system_coords, neighbor_system_coords, - neighborhood_search, particle) - (; search_radius, periodic_box) = neighborhood_search - - particle_coords = extract_svector(system_coords, Val(ndims(neighborhood_search)), - particle) - for neighbor in eachneighbor(particle_coords, neighborhood_search) - neighbor_coords = extract_svector(neighbor_system_coords, - Val(ndims(neighborhood_search)), neighbor) - - pos_diff = particle_coords - neighbor_coords - distance2 = dot(pos_diff, pos_diff) - - pos_diff, distance2 = compute_periodic_distance(pos_diff, distance2, search_radius, - periodic_box) - - if distance2 <= search_radius^2 - distance = sqrt(distance2) - - # Inline to avoid loss of performance - # compared to not using `for_particle_neighbor`. - @inline f(particle, neighbor, pos_diff, distance) - end - end -end - -@inline function compute_periodic_distance(pos_diff, distance2, search_radius, - periodic_box::Nothing) - return pos_diff, distance2 -end - -@inline function compute_periodic_distance(pos_diff, distance2, search_radius, - periodic_box) - if distance2 > search_radius^2 - # Use periodic `pos_diff` - pos_diff -= periodic_box.size .* round.(pos_diff ./ periodic_box.size) - distance2 = dot(pos_diff, pos_diff) - end - - return pos_diff, distance2 -end - -@inline function periodic_coords(coords, periodic_box) - (; min_corner, size) = periodic_box - - # Move coordinates into the periodic box - box_offset = floor.((coords .- min_corner) ./ size) - - return coords - box_offset .* size -end - -@inline function periodic_coords(coords, periodic_box::Nothing) - return coords -end - -include("trivial_nhs.jl") -include("grid_nhs.jl") diff --git a/src/neighborhood_search/trivial_nhs.jl b/src/neighborhood_search/trivial_nhs.jl deleted file mode 100644 index 3902f1d3f..000000000 --- a/src/neighborhood_search/trivial_nhs.jl +++ /dev/null @@ -1,97 +0,0 @@ -@doc raw""" - TrivialNeighborhoodSearch{NDIMS}(search_radius, eachparticle) - -Trivial neighborhood search that simply loops over all particles. -The search radius still needs to be passed in order to sort out particles outside the -search radius in the internal function `for_particle_neighbor`, but it's not used in the -internal function `eachneighbor`. - -# Arguments -- `NDIMS`: Number of dimensions. -- `search_radius`: The uniform search radius. -- `eachparticle`: `UnitRange` of all particle indices. Usually just `1:n_particles`. - -# Keywords -- `periodic_box_min_corner`: In order to use a (rectangular) periodic domain, pass the - coordinates of the domain corner in negative coordinate - directions. -- `periodic_box_max_corner`: In order to use a (rectangular) periodic domain, pass the - coordinates of the domain corner in positive coordinate - directions. - -!!! warning "Internal use only" - Please note that this constructor is intended for internal use only. It is *not* part of - the public API of TrixiParticles.jl, and it thus can altered (or be removed) at any time - without it being considered a breaking change. - - To run a simulation with this neighborhood search, just pass the type to the constructor - of [`Semidiscretization`](@ref): - ```jldoctest semi_example; output=false, setup = :(trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "hydrostatic_water_column_2d.jl"), sol=nothing); system1 = fluid_system; system2 = boundary_system) - semi = Semidiscretization(system1, system2, - neighborhood_search=TrivialNeighborhoodSearch) - - # output - ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ Semidiscretization │ - │ ══════════════════ │ - │ #spatial dimensions: ………………………… 2 │ - │ #systems: ……………………………………………………… 2 │ - │ neighborhood search: ………………………… TrivialNeighborhoodSearch │ - │ total #particles: ………………………………… 636 │ - └──────────────────────────────────────────────────────────────────────────────────────────────────┘ - ``` - The keyword arguments `periodic_box_min_corner` and `periodic_box_max_corner` explained - above can also be passed to the [`Semidiscretization`](@ref) and will internally be - forwarded to the neighborhood search: - ```jldoctest semi_example; output = false - semi = Semidiscretization(system1, system2, - neighborhood_search=TrivialNeighborhoodSearch, - periodic_box_min_corner=[0.0, -0.25], - periodic_box_max_corner=[1.0, 0.75]) - - # output - ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ Semidiscretization │ - │ ══════════════════ │ - │ #spatial dimensions: ………………………… 2 │ - │ #systems: ……………………………………………………… 2 │ - │ neighborhood search: ………………………… TrivialNeighborhoodSearch │ - │ total #particles: ………………………………… 636 │ - └──────────────────────────────────────────────────────────────────────────────────────────────────┘ - ``` -""" -struct TrivialNeighborhoodSearch{NDIMS, ELTYPE, EP, PB} - search_radius :: ELTYPE - eachparticle :: EP - periodic_box :: PB - - function TrivialNeighborhoodSearch{NDIMS}(search_radius, eachparticle; - periodic_box_min_corner=nothing, - periodic_box_max_corner=nothing) where {NDIMS} - if search_radius < eps() || - (periodic_box_min_corner === nothing && periodic_box_max_corner === nothing) - - # No periodicity - periodic_box = nothing - elseif periodic_box_min_corner !== nothing && periodic_box_max_corner !== nothing - periodic_box = PeriodicBox(periodic_box_min_corner, periodic_box_max_corner) - else - throw(ArgumentError("`periodic_box_min_corner` and `periodic_box_max_corner` " * - "must either be both `nothing` or both an array or tuple")) - end - - new{NDIMS, typeof(search_radius), - typeof(eachparticle), typeof(periodic_box)}(search_radius, eachparticle, - periodic_box) - end -end - -@inline function Base.ndims(neighborhood_search::TrivialNeighborhoodSearch{NDIMS}) where { - NDIMS - } - return NDIMS -end - -@inline initialize!(search::TrivialNeighborhoodSearch, coords_fun) = search -@inline update!(search::TrivialNeighborhoodSearch, coords_fun) = search -@inline eachneighbor(coords, search::TrivialNeighborhoodSearch) = search.eachparticle diff --git a/src/visualization/write2vtk.jl b/src/visualization/write2vtk.jl index 81581494e..bd1658e32 100644 --- a/src/visualization/write2vtk.jl +++ b/src/visualization/write2vtk.jl @@ -85,7 +85,8 @@ function trixi2vtk(v, u, t, system, periodic_box; output_directory="out", prefix # Reset the collection when the iteration is 0 pvd = paraview_collection(collection_file; append=iter > 0) - points = periodic_coords(current_coordinates(u, system), periodic_box) + points = PointNeighbors.periodic_coords(current_coordinates(u, system), + periodic_box) cells = [MeshCell(VTKCellTypes.VTK_VERTEX, (i,)) for i in axes(points, 2)] if abs(maximum(points)) > max_coordinates || abs(minimum(points)) > max_coordinates diff --git a/test/count_allocations.jl b/test/count_allocations.jl index 6a87e9868..3b5110ba1 100644 --- a/test/count_allocations.jl +++ b/test/count_allocations.jl @@ -27,7 +27,11 @@ end end # No update -@inline TrixiParticles.update!(search::NoUpdateNeighborhoodSearch, coords_fun) = search +@inline function TrixiParticles.PointNeighbors.update!(search::NoUpdateNeighborhoodSearch, + x, y; + particles_moving=(true, true)) + return search +end # Count allocations of one call to the right-hand side (`kick!` + `drift!`) function count_rhs_allocations(sol, semi) diff --git a/test/neighborhood_search/grid_nhs.jl b/test/neighborhood_search/grid_nhs.jl deleted file mode 100644 index d686efc5b..000000000 --- a/test/neighborhood_search/grid_nhs.jl +++ /dev/null @@ -1,272 +0,0 @@ -@testset verbose=true "GridNeighborhoodSearch" begin - @testset "Coordinate Limits" begin - # Test the threshold for very large and very small coordinates. - coords1 = [Inf, -Inf] - coords2 = [NaN, 0] - coords3 = [typemax(Int) + 1.0, -typemax(Int) - 1.0] - - @test TrixiParticles.cell_coords(coords1, nothing, (1.0, 1.0)) == - (typemax(Int), typemin(Int)) - @test TrixiParticles.cell_coords(coords2, nothing, (1.0, 1.0)) == (typemax(Int), 0) - @test TrixiParticles.cell_coords(coords3, nothing, (1.0, 1.0)) == - (typemax(Int), typemin(Int)) - end - - @testset "Rectangular Point Cloud 2D" begin - #### Setup - # Rectangular filled with equidistant spaced particles - # from (x, y) = (-0.25, -0.25) to (x, y) = (0.35, 0.35) - range = -0.25:0.1:0.35 - coordinates1 = hcat(collect.(Iterators.product(range, range))...) - nparticles = size(coordinates1, 2) - - particle_position1 = [0.05, 0.05] - particle_spacing = 0.1 - radius = particle_spacing - - # Create neighborhood search - nhs1 = GridNeighborhoodSearch{2}(radius, nparticles) - - coords_fun(i) = coordinates1[:, i] - TrixiParticles.initialize!(nhs1, coords_fun) - - # Get each neighbor for `particle_position1` - neighbors1 = sort(collect(TrixiParticles.eachneighbor(particle_position1, nhs1))) - - # Move particles - coordinates2 = coordinates1 .+ [1.4, -3.5] - - # Update neighborhood search - coords_fun2(i) = coordinates2[:, i] - TrixiParticles.update!(nhs1, coords_fun2) - - # Get each neighbor for updated NHS - neighbors2 = sort(collect(TrixiParticles.eachneighbor(particle_position1, nhs1))) - - # Change position - particle_position2 = particle_position1 .+ [1.4, -3.5] - - # Get each neighbor for `particle_position2` - neighbors3 = sort(collect(TrixiParticles.eachneighbor(particle_position2, nhs1))) - - # Double search radius - nhs2 = GridNeighborhoodSearch{2}(2 * radius, size(coordinates1, 2)) - TrixiParticles.initialize!(nhs2, coords_fun) - - # Get each neighbor in double search radius - neighbors4 = sort(collect(TrixiParticles.eachneighbor(particle_position1, nhs2))) - - # Move particles - coordinates2 = coordinates1 .+ [0.4, -0.4] - - # Update neighborhood search - TrixiParticles.update!(nhs2, coords_fun2) - - # Get each neighbor in double search radius - neighbors5 = sort(collect(TrixiParticles.eachneighbor(particle_position1, nhs2))) - - #### Verification - @test neighbors1 == [17, 18, 19, 24, 25, 26, 31, 32, 33] - - @test neighbors2 == Int[] - - @test neighbors3 == [17, 18, 19, 24, 25, 26, 31, 32, 33] - - @test neighbors4 == [9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 23, 24, 25, - 26, 27, 28, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 44, 45, 46, 47, - 48, 49] - - @test neighbors5 == [36, 37, 38, 43, 44, 45] - end - - @testset "Rectangular Point Cloud 3D" begin - #### Setup - # Rectangular filled with equidistant spaced particles - # from (x, y, z) = (-0.25, -0.25, -0.25) to (x, y) = (0.35, 0.35, 0.35) - range = -0.25:0.1:0.35 - coordinates1 = hcat(collect.(Iterators.product(range, range, range))...) - nparticles = size(coordinates1, 2) - - particle_position1 = [0.05, 0.05, 0.05] - particle_spacing = 0.1 - radius = particle_spacing - - # Create neighborhood search - nhs1 = GridNeighborhoodSearch{3}(radius, nparticles) - - coords_fun(i) = coordinates1[:, i] - TrixiParticles.initialize!(nhs1, coords_fun) - - # Get each neighbor for `particle_position1` - neighbors1 = sort(collect(TrixiParticles.eachneighbor(particle_position1, nhs1))) - - # Move particles - coordinates2 = coordinates1 .+ [1.4, -3.5, 0.8] - - # Update neighborhood search - coords_fun2(i) = coordinates2[:, i] - TrixiParticles.update!(nhs1, coords_fun2) - - # Get each neighbor for updated NHS - neighbors2 = sort(collect(TrixiParticles.eachneighbor(particle_position1, nhs1))) - - # Change position - particle_position2 = particle_position1 .+ [1.4, -3.5, 0.8] - - # Get each neighbor for `particle_position2` - neighbors3 = sort(collect(TrixiParticles.eachneighbor(particle_position2, nhs1))) - - #### Verification - @test neighbors1 == - [115, 116, 117, 122, 123, 124, 129, 130, 131, 164, 165, 166, 171, 172, - 173, 178, 179, 180, 213, 214, 215, 220, 221, 222, 227, 228, 229] - - @test neighbors2 == Int[] - - @test neighbors3 == - [115, 116, 117, 122, 123, 124, 129, 130, 131, 164, 165, 166, 171, 172, - 173, 178, 179, 180, 213, 214, 215, 220, 221, 222, 227, 228, 229] - end - - @testset verbose=true "Periodicity 2D" begin - @testset "Simple Example" begin - coords = [-0.08 0.0 0.18 0.1 -0.08 - -0.12 -0.05 -0.09 0.15 0.39] - - # 3 x 6 cells - nhs = GridNeighborhoodSearch{2}(0.1, size(coords, 2), - periodic_box_min_corner=[-0.1, -0.2], - periodic_box_max_corner=[0.2, 0.4]) - - TrixiParticles.initialize!(nhs, coords) - - neighbors = [sort(collect(TrixiParticles.eachneighbor(coords[:, i], nhs))) - for i in 1:5] - - # Note that (1, 2) and (2, 3) are not neighbors, but they are in neighboring cells - @test neighbors[1] == [1, 2, 3, 5] - @test neighbors[2] == [1, 2, 3] - @test neighbors[3] == [1, 2, 3] - @test neighbors[4] == [4] - @test neighbors[5] == [1, 5] - - neighbors_loop = [Int[] for _ in axes(coords, 2)] - - TrixiParticles.for_particle_neighbor(nothing, nothing, - coords, coords, nhs, - particles=axes(coords, 2)) do particle, - neighbor, - pos_diff, - distance - append!(neighbors_loop[particle], neighbor) - end - - @test sort(neighbors_loop[1]) == [1, 3, 5] - @test sort(neighbors_loop[2]) == [2] - @test sort(neighbors_loop[3]) == [1, 3] - @test sort(neighbors_loop[4]) == [4] - @test sort(neighbors_loop[5]) == [1, 5] - end - - @testset "Rounding Up Cell Sizes" begin - coords = [-0.08 0.0 0.18 0.1 -0.08 - -0.12 -0.05 -0.09 0.15 0.42] - - # 3 x 6 cells - nhs = GridNeighborhoodSearch{2}(0.1, size(coords, 2), - periodic_box_min_corner=[-0.1, -0.2], - periodic_box_max_corner=[0.205, 0.43]) - - TrixiParticles.initialize!(nhs, coords) - - neighbors = [sort(collect(TrixiParticles.eachneighbor(coords[:, i], nhs))) - for i in 1:5] - - # Note that (1, 2) and (2, 3) are not neighbors, but they are in neighboring cells - @test neighbors[1] == [1, 2, 3, 5] - @test neighbors[2] == [1, 2, 3] - @test neighbors[3] == [1, 2, 3] - @test neighbors[4] == [4] - @test neighbors[5] == [1, 5] - - neighbors_loop = [Int[] for _ in axes(coords, 2)] - - TrixiParticles.for_particle_neighbor(nothing, nothing, - coords, coords, nhs, - particles=axes(coords, 2)) do particle, - neighbor, - pos_diff, - distance - append!(neighbors_loop[particle], neighbor) - end - - @test sort(neighbors_loop[1]) == [1, 3, 5] - @test sort(neighbors_loop[2]) == [2] - @test sort(neighbors_loop[3]) == [1, 3] - @test sort(neighbors_loop[4]) == [4] - @test sort(neighbors_loop[5]) == [1, 5] - end - - @testset "Offset Domain Triggering Split Cells" begin - # This used to trigger a "split cell bug", where the left and right boundary - # cells were only partially contained in the domain. - # The left particle was placed inside a ghost cells, which causes it to not - # see the right particle, even though it is within the search distance. - # The domain size is an integer multiple of the cell size, but the NHS did not - # offset the grid based on the domain position. - # See https://github.com/trixi-framework/TrixiParticles.jl/pull/211 for a more - # detailed explanation. - coords = [-1.4 1.9 - 0.0 0.0] - - # 5 x 1 cells - nhs = GridNeighborhoodSearch{2}(1.0, size(coords, 2), - periodic_box_min_corner=[-1.5, 0.0], - periodic_box_max_corner=[2.5, 3.0]) - - TrixiParticles.initialize!(nhs, coords) - - neighbors = [sort(unique(collect(TrixiParticles.eachneighbor(coords[:, i], nhs)))) - for i in 1:2] - - @test neighbors[1] == [1, 2] - @test neighbors[2] == [1, 2] - end - end - - @testset verbose=true "Periodicity 3D" begin - coords = [-0.08 0.0 0.18 0.1 -0.08 - -0.12 -0.05 -0.09 0.15 0.39 - 0.14 0.34 0.12 0.06 0.13] - - # 3 x 6 x 3 cells - nhs = GridNeighborhoodSearch{3}(0.1, size(coords, 2), - periodic_box_min_corner=[-0.1, -0.2, 0.05], - periodic_box_max_corner=[0.2, 0.4, 0.35]) - - TrixiParticles.initialize!(nhs, coords) - - neighbors = [sort(collect(TrixiParticles.eachneighbor(coords[:, i], nhs))) - for i in 1:5] - - # Note that (1, 2) and (2, 3) are not neighbors, but they are in neighboring cells - @test neighbors[1] == [1, 2, 3, 5] - @test neighbors[2] == [1, 2, 3] - @test neighbors[3] == [1, 2, 3] - @test neighbors[4] == [4] - @test neighbors[5] == [1, 5] - - neighbors_loop = [Int[] for _ in axes(coords, 2)] - - TrixiParticles.for_particle_neighbor(coords, coords, - nhs) do particle, neighbor, pos_diff, distance - append!(neighbors_loop[particle], neighbor) - end - - @test sort(neighbors_loop[1]) == [1, 3, 5] - @test sort(neighbors_loop[2]) == [2] - @test sort(neighbors_loop[3]) == [1, 3] - @test sort(neighbors_loop[4]) == [4] - @test sort(neighbors_loop[5]) == [1, 5] - end -end diff --git a/test/neighborhood_search/neighborhood_search.jl b/test/neighborhood_search/neighborhood_search.jl deleted file mode 100644 index 0c7270a7e..000000000 --- a/test/neighborhood_search/neighborhood_search.jl +++ /dev/null @@ -1,2 +0,0 @@ -include("trivial_nhs.jl") -include("grid_nhs.jl") diff --git a/test/neighborhood_search/trivial_nhs.jl b/test/neighborhood_search/trivial_nhs.jl deleted file mode 100644 index aa0b21846..000000000 --- a/test/neighborhood_search/trivial_nhs.jl +++ /dev/null @@ -1,10 +0,0 @@ -@testset verbose=true "TrivialNeighborhoodSearch" begin - # Setup with 5 particles - nhs = TrixiParticles.TrivialNeighborhoodSearch{2}(1.0, Base.OneTo(5)) - - # Get each neighbor for arbitrary coordinates - neighbors = collect(TrixiParticles.eachneighbor([1.0, 2.0], nhs)) - - #### Verification - @test neighbors == [1, 2, 3, 4, 5] -end diff --git a/test/schemes/solid/total_lagrangian_sph/rhs.jl b/test/schemes/solid/total_lagrangian_sph/rhs.jl index 6a4f6bce3..dfc9fc234 100644 --- a/test/schemes/solid/total_lagrangian_sph/rhs.jl +++ b/test/schemes/solid/total_lagrangian_sph/rhs.jl @@ -85,7 +85,7 @@ each_moving_particle end TrixiParticles.eachparticle(::Val{:mock_system_interact}) = eachparticle - TrixiParticles.eachneighbor(_, ::Val{:nhs}) = eachneighbor + TrixiParticles.PointNeighbors.eachneighbor(_, ::Val{:nhs}) = eachneighbor function Base.getproperty(::Val{:nhs}, f::Symbol) if f === :search_radius diff --git a/test/systems/solid_system.jl b/test/systems/solid_system.jl index 812493fa8..369022273 100644 --- a/test/systems/solid_system.jl +++ b/test/systems/solid_system.jl @@ -153,7 +153,7 @@ return Val(Symbol("mock_" * string(f))) end - TrixiParticles.eachneighbor(_, ::Val{:nhs}) = neighbors + TrixiParticles.PointNeighbors.eachneighbor(_, ::Val{:nhs}) = neighbors function Base.getproperty(::Val{:nhs}, f::Symbol) if f === :search_radius diff --git a/test/unittest.jl b/test/unittest.jl index 01308bd44..5c05924e1 100644 --- a/test/unittest.jl +++ b/test/unittest.jl @@ -3,7 +3,6 @@ @testset verbose=true "Unit Tests" begin include("callbacks/callbacks.jl") include("general/general.jl") - include("neighborhood_search/neighborhood_search.jl") include("setups/setups.jl") include("systems/systems.jl") include("schemes/schemes.jl") From 3e14679b9c09210a152481de788bd2d71f2be54d Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 17 May 2024 18:01:21 +0200 Subject: [PATCH 26/49] Remove `pinv` in correction matrix inversion step (#526) * Remove `pinv` in correction matrix inversion step * Improve comment * Update validation files for dam break * Revert "Update validation files for dam break" This reverts commit d08c06d277fe6cfc2d5364c81d8243f520ce8bad. * Update WCSPH validation files * Update WCSPH validation files * Add single-threaded validation files (see #481) --- src/general/corrections.jl | 47 +- .../validation_reference_wcsph_0001875.json | 58 +-- .../validation_reference_wcsph_00075.json | 428 +++++++++--------- .../validation_reference_wcsph_0015.json | 374 +++++++-------- 4 files changed, 454 insertions(+), 453 deletions(-) diff --git a/src/general/corrections.jl b/src/general/corrections.jl index 53b925f48..cf64f6d16 100644 --- a/src/general/corrections.jl +++ b/src/general/corrections.jl @@ -442,32 +442,33 @@ end function correction_matrix_inversion_step!(corr_matrix, system) @threaded for particle in eachparticle(system) L = extract_smatrix(corr_matrix, system, particle) - norm_ = norm(L) - # The norm value is quasi-zero, so there are probably no neighbors for this particle - if norm_ < sqrt(eps()) - # The correction matrix is set to an identity matrix, which effectively disables - # the correction for this particle. - @inbounds for j in 1:ndims(system), i in 1:ndims(system) - corr_matrix[i, j, particle] = 0.0 - end - @inbounds for i in 1:ndims(system) - corr_matrix[i, i, particle] = 1.0 - end - continue - end - - det_ = abs(det(L)) - @fastmath if det_ < 1e-6 * norm_ - L_inv = pinv(L) - @inbounds for j in 1:ndims(system), i in 1:ndims(system) - corr_matrix[i, j, particle] = L_inv[i, j] - end + # The matrix `L` only becomes singular when the particle and all neighbors + # are collinear (in 2D) or lie all in the same plane (in 3D). + # This happens only when two (in 2D) or three (in 3D) particles are isolated, + # or in cases where there is only one layer of fluid particles on a wall. + # In these edge cases, we just disable the correction and set the corrected + # gradient to be the uncorrected one by setting `L` to the identity matrix. + # + # Proof: `L` is just a sum of tensor products of relative positions X_ab with + # themselves. According to + # https://en.wikipedia.org/wiki/Outer_product#Connection_with_the_matrix_product + # the sum of tensor products can be rewritten as A A^T, where the columns of A + # are the relative positions X_ab. The rank of A A^T is equal to the rank of A, + # so `L` is singular if and only if the position vectors X_ab don't span the + # full space, i.e., particle a and all neighbors lie on the same line (in 2D) + # or plane (in 3D). + if abs(det(L)) < 1e-9 + L_inv = I else L_inv = inv(L) - @inbounds for j in 1:ndims(system), i in 1:ndims(system) - corr_matrix[i, j, particle] = L_inv[i, j] - end + end + + # Write inverse back to `corr_matrix` + for j in 1:ndims(system), i in 1:ndims(system) + @inbounds corr_matrix[i, j, particle] = L_inv[i, j] end end + + return corr_matrix end diff --git a/validation/dam_break_2d/validation_reference_wcsph_0001875.json b/validation/dam_break_2d/validation_reference_wcsph_0001875.json index b65acf655..143fa2d58 100644 --- a/validation/dam_break_2d/validation_reference_wcsph_0001875.json +++ b/validation/dam_break_2d/validation_reference_wcsph_0001875.json @@ -192,26 +192,26 @@ 4810.974638781969, 4057.474362834096, 4982.9085809799635, - 4326.893681724326, - 2365.579225741909, - 794.454920623942, - 128.518422948347, - 3802.146008868988, - 0.0, - 193.6041251795611, - 155.5982729768126, - 0.0, - 0.0, - 280.0329234725871, - 2691.975566958315, - 587.3786496564874 + 4326.893681723601, + 2365.5792257312305, + 794.455434481232, + 115.03811223063278, + 1595.8828694583096, + 48.06627062092718, + 142.2684567691535, + 426.8228505863073, + 0.0, + 706.4392919333849, + 0.0, + 150.25642381312346, + 2277.737279445718 ], "datatype": "Float64", "type": "series" }, "meta": { - "julia_version": "1.10.2", - "solver_version": "98fe4b85-dirty", + "julia_version": "1.10.3", + "solver_version": "v0.1.1-14-g16ba4ad3-dirty", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { @@ -617,19 +617,19 @@ 3.21912366161124, 3.2186659672343008, 3.220265299799684, - 3.2189520703755012, - 3.2207158761914387, - 3.22006342847907, - 3.2216900634986714, - 3.221863805412857, - 3.21992639788017, - 3.22035809693605, - 3.219768897432158, - 3.220162312109093, - 3.2199596621054885, - 3.219830044932556, - 3.219676592514716, - 3.2200324616308778 + 3.218952070255997, + 3.220715876032403, + 3.220063428269404, + 3.221695913083556, + 3.221860930773287, + 3.2203023876287897, + 3.2202986314807363, + 3.219811690478277, + 3.219840248409694, + 3.2199399387860748, + 3.220772411716257, + 3.219926013023057, + 3.221087670404958 ], "datatype": "Float64", "type": "series" @@ -844,4 +844,4 @@ "datatype": "Float64", "type": "series" } -} \ No newline at end of file +} diff --git a/validation/dam_break_2d/validation_reference_wcsph_00075.json b/validation/dam_break_2d/validation_reference_wcsph_00075.json index 2b415c07b..fed5edbce 100644 --- a/validation/dam_break_2d/validation_reference_wcsph_00075.json +++ b/validation/dam_break_2d/validation_reference_wcsph_00075.json @@ -137,81 +137,81 @@ 0.0, 0.0, 0.0, - 58.34607670780366, - 138.30780730090584, - 422.4439168767831, - 916.8364120975024, - 419.06398575736984, - 1347.417827563866, - 2349.6255616253516, - 1064.5393348074151, - 27.317068325912476, - 101.40177005737137, - 781.1469021132997, - 1397.141799265406, - 1379.759513694514, - 1194.2325124227586, - 577.1087108938541, - 835.985544221772, - 3084.814615378419, - 3315.6976314538865, - 832.4717664353399, - 2897.005749304349, - 3002.164449326629, - 1361.257691488331, - 3857.591385230775, - 1670.062910230366, - 3151.308834774854, - 3129.2187304687695, - 979.7764644576539, - 4547.292898341513, - 2359.9991553519276, - 926.182837379232, - 3935.8020396511865, - 4379.52352100003, - 1722.4646619986313, - 239.70093766679906, - 2089.466442476288, - 4483.323370640237, - 5364.48776205749, - 4475.9885720309485, - 2627.998878339486, - 1181.8674467133405, - 649.6636140991379, - 330.1142981716199, - 345.0782726299165, - 192.3883424307605, - 179.4013719150344, - 1136.3039269688265, - 3826.440939852382, - 7363.986736552375, - 7195.6667823247935, - 7394.382064105072, - 6254.669285089546, - 4209.599045337116, - 6960.72634285375, - 4403.324013800456, - 1981.9578327450497, - 766.6754384823071, - 4585.4210989388175, - 8579.806145402657, - 1303.9255807328104, - 850.3314028904712, - 2149.0574314606965, - 1119.63353117133, - 1025.4664409249024, - 2062.434608152032, - 10.626963343280085, - 2866.5242641333803, - 1210.0747178019974, - 1997.7373282796998 + 58.344991574597245, + 138.3079819297823, + 422.44543275991543, + 916.8379090327911, + 419.0542812820178, + 1347.4169400618655, + 2349.6352547704764, + 1064.5188397369652, + 27.28474677852581, + 101.40272350576802, + 781.2699036849094, + 1397.375646820365, + 1380.062731506342, + 1194.529392969989, + 577.3455172987748, + 837.930707986937, + 3081.161976053418, + 3318.476728313081, + 840.9237550556791, + 2892.0625372919735, + 3033.1045842317258, + 1259.1306900753145, + 3817.228202280832, + 1635.2988377088582, + 3229.66356315531, + 3186.684524742882, + 985.5939469237841, + 4559.785446597643, + 2323.036294969968, + 870.2389644974486, + 3913.740475400634, + 4345.220860174271, + 1727.481685909364, + 317.0817954906028, + 2136.9635058026806, + 4477.765360075153, + 5281.26500304141, + 4350.273753361081, + 2546.9182615226355, + 1165.5955609655477, + 734.9000832904637, + 461.3215749633864, + 519.663763497804, + 387.3458428471696, + 419.23300905557824, + 1315.620427554483, + 3777.79723772579, + 7077.795941168353, + 6982.577978305283, + 7332.201328785472, + 6519.795708535393, + 4443.747044691211, + 7050.928045952259, + 2588.3483986830647, + 1964.994261825345, + 1933.4846460072567, + 5032.453646266516, + 4272.017659452722, + 2275.8426396788036, + 879.224598485221, + 1907.8934504222573, + 1363.6979297710648, + 595.0588557689321, + 2553.1879813651676, + 341.1912981329048, + 1605.7780612629278, + 524.6937275195312, + 2009.0164472926597 ], "datatype": "Float64", "type": "series" }, "meta": { - "julia_version": "1.10.2", - "solver_version": "98fe4b85-dirty", + "julia_version": "1.10.3", + "solver_version": "v0.1.1-12-g81063e0c-dirty", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { @@ -357,52 +357,52 @@ 0.0, 0.0, 0.0, - 20.31323871163639, - 45.50619636076011, - 6.167957730082405, - 23.884709125644008, - 4.43255918517901, - 8.774110436930735, - 8.963620141583254, - 40.731562580934956, - 13.241833317657427, - 7.200450403202941, - 35.88196312189998, - 21.541443478575296, - 3.65452867132935, - 75.4427754124238, - 201.09695963130483, - 49.816202634048224, - 255.57387598753877, - 289.5116797564693, - 267.79845001041633, - 421.33924738849765, - 518.9202572071249, - 443.37050099785904, - 640.6791984196514, - 729.3057886088931, - 708.2134106342477, - 1025.235951022832, - 1026.3999971246153, - 1096.9087688994634, - 1008.4260873388821, - 1269.2640692741431, - 1625.4501394880863, - 1844.8915331962003, - 1937.2541977002384, - 1719.9451731647855, - 1374.1398026925883, - 1600.1725709745563, - 1400.2678834762628, - 1661.0115314501156, - 1371.158183140062, - 646.0660269386094, - 168.90325590861573, - 17.13050116673967, + 20.313232646831917, + 45.50619773676554, + 6.167987928920181, + 23.887316107055206, + 4.430146943903635, + 8.768108012133887, + 8.968265715355544, + 40.80561634950723, + 13.266294706155914, + 7.219760928606955, + 35.837355015060986, + 21.21805714727239, + 3.4965726828218804, + 85.24808274134794, + 198.65072969366972, + 84.55703995068527, + 222.2918851291387, + 255.16931211945393, + 287.2663679859116, + 390.13505695583376, + 462.164671744731, + 478.8093301950632, + 667.5463321442569, + 708.8526827117113, + 644.7813417154648, + 1006.6892456341614, + 1022.7891602511103, + 1088.3173425223758, + 974.9347449872591, + 1302.0652060904554, + 1561.291264087963, + 1894.1485159976914, + 1935.3743926330087, + 1664.0454616980896, + 1417.613710039965, + 1572.8675261801695, + 1469.2600241416771, + 1576.6503876078307, + 1357.7509207067628, + 627.6625285260745, + 171.3536996335749, + 16.555442318313464, 0.0, 0.0, 0.0, - 6.928117091969661, + 6.927952002970758, 0.0, 0.0, 0.0, @@ -561,75 +561,75 @@ 3.0557396096262015, 3.148099574918203, 3.217883381034535, - 3.2182524076050694, - 3.219035833332324, - 3.2196638215078255, - 3.219258579483189, - 3.2183916128026855, - 3.2182369934034982, - 3.218223208202275, - 3.2182163705155977, - 3.2182383904949226, - 3.2182285980567626, - 3.218238344389985, - 3.2182077455893454, - 3.2182204186323973, - 3.218170605256553, - 3.218151520915598, - 3.2181831831840575, - 3.2182493485634134, - 3.2181785938759218, - 3.218188358844037, - 3.2182324626442353, - 3.2182142215753884, - 3.2182264340719717, - 3.2182064295006336, - 3.218213596467143, - 3.2182008193381617, - 3.2182039410427405, - 3.2182025004491424, - 3.2181926479696, - 3.2182003300356925, - 3.218207646497285, - 3.218209138192204, - 3.218196212105352, - 3.2182009245598135, - 3.2181952111642214, - 3.218212351556307, - 3.2182122930292048, - 3.218334624700235, - 3.218585621943171, - 3.2188865560695596, - 3.2192019897484103, - 3.219574328223231, - 3.219582766932132, - 3.218936057751625, - 3.218203739651142, - 3.218195663545464, - 3.218195596720888, - 3.2182126696027797, - 3.2182138825029765, - 3.2182016914355787, - 3.2182146627037733, - 3.2182120968881103, - 3.2182004332757366, - 3.218473417648065, - 3.2182117733109865, - 3.218198251815128, - 3.218197262453762, - 3.218223980447437, - 3.2227801092805124, - 3.2207859044320903, - 3.218230016750645, - 3.2182184991110523, - 3.218208917822917, - 3.2182016690447384, - 3.221967175753277, - 3.2219266041450023, - 3.221972452578923, - 3.2219639418722528, - 3.221909307915254, - 3.221972546583524 + 3.2182521991925985, + 3.2190358988544725, + 3.219663973025696, + 3.2192588413421133, + 3.2183918503396116, + 3.218238394517403, + 3.218217349335105, + 3.2182135276397252, + 3.2182331098537738, + 3.2182123548665516, + 3.2182351451319717, + 3.2181860314410704, + 3.2181826027766878, + 3.218261609150298, + 3.218290039492455, + 3.2181013332172035, + 3.2181807074655313, + 3.2182413760911555, + 3.218236604272375, + 3.2181908383681304, + 3.218206894438717, + 3.218192216494783, + 3.218218304785117, + 3.218209781428932, + 3.2182014695397645, + 3.2181983905211684, + 3.2182177816258646, + 3.2182083303461964, + 3.2182141488580966, + 3.2181940144865506, + 3.2181949194681048, + 3.218204747679178, + 3.2181938551074114, + 3.218197842690469, + 3.218205426948579, + 3.2182070153293743, + 3.218334642138954, + 3.218585647160275, + 3.218886605377294, + 3.2192020710208524, + 3.219574419943034, + 3.2195825815053456, + 3.2189357478307796, + 3.2181998457861263, + 3.2181963889480194, + 3.2181940755753153, + 3.218211667716726, + 3.2182091551294123, + 3.218208689192436, + 3.2182110246884466, + 3.2182138808799348, + 3.2181947548040326, + 3.21908316441958, + 3.218192847601368, + 3.2182125261578234, + 3.2182085567456062, + 3.218202507636954, + 3.2181962807514024, + 3.2182248069249417, + 3.2181984126618266, + 3.2182146801522773, + 3.219360754040278, + 3.2206149671663638, + 3.220484842692027, + 3.219420656013077, + 3.2194090912602475, + 3.2194099228296955, + 3.219378813315998, + 3.2195112418230374 ], "datatype": "Float64", "type": "series" @@ -782,42 +782,42 @@ 0.0, 0.0, 0.0, - 48.777453004578454, - 10.358152612197825, - 15.049716723438971, - 8.912878348810716, - 3.1038473116339116, - 9.08029215572343, - 11.001309062044069, - 4.389568308532413, - 9.34597656525185, - 8.215815969414672, - 8.921424560673799, - 11.548495006236983, - 8.100275261058105, - 6.463798001100874, - 10.126510322875083, - 3.5656525366383485, - 9.936641893186898, - 8.36706783034966, - 6.531183179219712, - 3.397637976308925, - 4.301526347537932, - 3.1626566693065383, - 13.957194239542332, - 2.355736919532164, - 9.467443984066906, - 15.268611565230216, - 4.803212788857185, - 17.17166030016114, - 16.481158545172544, - 35.126053887486044, + 48.77745540928442, + 10.358164540228152, + 15.049608310077256, + 8.912918416371017, + 3.1038547498987126, + 9.080093846498611, + 11.001862497415107, + 4.389149064297083, + 9.327454050543402, + 8.079108270395704, + 8.873144682478369, + 11.70989304656271, + 9.778468385332287, + 10.139553590994097, + 7.951623034613833, + 3.903962005507205, + 7.599910342535063, + 8.624094009131955, + 8.186379762767322, + 4.029544470513885, + 3.983062631983286, + 3.2253097131812596, + 13.985451999478542, + 2.350909911211228, + 9.466000090043943, + 15.270276355793007, + 4.803286926676352, + 17.17179251061254, + 16.4819672034308, + 35.117451725247186, 0.0, 0.0, 0.0, 0.0, 0.0, - 6.527472931535234, + 6.527361143284767, 0.0, 0.0, 0.0, @@ -844,4 +844,4 @@ "datatype": "Float64", "type": "series" } -} \ No newline at end of file +} diff --git a/validation/dam_break_2d/validation_reference_wcsph_0015.json b/validation/dam_break_2d/validation_reference_wcsph_0015.json index 3b0f95b9f..670a13f7a 100644 --- a/validation/dam_break_2d/validation_reference_wcsph_0015.json +++ b/validation/dam_break_2d/validation_reference_wcsph_0015.json @@ -139,79 +139,79 @@ 0.0, 0.0, 0.0, - 741.993933814252, - 1074.8234704492293, - 780.1667963845828, - 750.264864820256, - 1867.0876722351645, - 2289.2233808791984, - 1854.7785895125223, - 1898.453697957532, - 1467.7442227383228, - 1634.0625899997626, - 1051.017537937822, - 1792.9924495715281, - 2224.4495834290674, - 2072.497434035885, - 1759.7943013144645, - 2213.4464461574053, - 2195.8826026854586, - 2296.9363606455972, - 2110.2618680074606, - 2199.283897208698, - 2970.3734738765897, - 2306.9378427287784, - 2708.5493847934304, - 3000.1784782069417, - 2269.7929805781623, - 2084.4623649717955, - 2584.892142305768, - 2922.95823696328, - 2700.9623996934047, - 2912.9611466447036, - 2333.0917554209454, - 2272.7000065438688, - 2482.9258774761342, - 2818.1463732338643, - 2995.433781965522, - 2909.3736079646987, - 3100.7712408135812, - 3025.9641406700944, - 3235.2029348584956, - 3257.7410461563277, - 2988.8181504113973, - 3115.194724294498, - 3505.92247124294, - 3713.8459250716937, - 4586.526088795744, - 4512.65880424846, - 6079.986577464039, - 6651.130159524973, - 5997.838678899889, - 3484.6443631044467, - 810.4039689089993, - 1497.7738917876206, - 1281.226457082536, - 6127.035664299017, - 1199.4753269710066, - 1869.222563929746, - 345.0423599469749, - 1192.8963322296063, - 1164.7884692609991, - 1552.9964308420076, - 2007.227306493361, - 1229.1139656462394, - 940.9388974307137, - 1420.324488576742, - 4081.141427086134, - 778.984800800608 + 741.993989499188, + 1074.8234774705008, + 780.1667978016657, + 750.2646819009395, + 1867.0876175708631, + 2289.2233812476165, + 1854.7785819902779, + 1898.4536989266617, + 1467.7442261288045, + 1634.0625951725135, + 1051.0175330299053, + 1792.9924178916986, + 2224.449593047438, + 2072.497420987851, + 1759.794387609291, + 2213.4462548625984, + 2195.882761495324, + 2296.9359071622694, + 2110.261628995037, + 2199.283711290986, + 2970.3736472205856, + 2306.9377872024497, + 2708.5496860356457, + 3000.1783021126685, + 2269.7935071889146, + 2084.462044237944, + 2584.891574798109, + 2922.9584610302077, + 2700.9638330280814, + 2912.962403023275, + 2333.0896174441677, + 2272.70021772166, + 2482.9249133633994, + 2818.1450177572733, + 2995.435467727107, + 2909.3691069629745, + 3100.77267069721, + 3025.9643852018985, + 3235.2107438665453, + 3257.726890859399, + 2988.8564114485575, + 3115.182446969521, + 3505.8949466499425, + 3713.810524900102, + 4590.058796083151, + 4355.887013844826, + 6158.300373074685, + 6758.121068681193, + 5923.416082268637, + 3778.132671948576, + 853.0072739525398, + 1654.167440624306, + 1293.1342883693856, + 5038.477782149967, + 2514.857909325529, + 2194.8799832441873, + 670.5436529503403, + 1266.7144427699454, + 637.4225382331412, + 1349.35969801679, + 2013.3006836641252, + 1472.444123400624, + 1092.6572808458293, + 1240.5339171417113, + 3713.2055968317923, + 794.5971054101079 ], "datatype": "Float64", "type": "series" }, "meta": { - "julia_version": "1.10.2", - "solver_version": "v0.1.0-22-g4d428a23-dirty", + "julia_version": "1.10.3", + "solver_version": "v0.1.1-12-g81063e0c", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { @@ -360,41 +360,41 @@ 0.0, 0.0, 0.0, - 47.78581701120133, - 44.38574448700575, - 30.47385719369803, - 8.477074777805942, - 33.57951839840989, - 1.8632502205569363, - 22.444846119437738, - 5.229750916569136, - 12.452367852533479, - 113.28032802403966, - 16.872523440014806, - 183.53111225036926, - 124.91979335190122, - 119.72020754524598, - 157.186868843168, - 274.4122531283022, - 366.92834558395305, - 436.79833075915656, - 656.8648337902086, - 693.6806185748894, - 709.8624031802605, - 940.2755903496779, - 713.5958561086439, - 1152.7132323277215, - 1175.0858756074435, - 1117.0732832554718, - 1473.3114994251675, - 1398.3800311373202, - 1426.7187322271566, - 1724.6666661737895, - 1689.5894191979883, - 1716.4136340314021, - 2313.2505906487645, - 1548.2452467272121, - 872.8944564735984, + 47.77942633833136, + 44.375511308801165, + 30.473913165821585, + 8.476970647743345, + 33.57949027495734, + 1.8632530695075122, + 22.444832138896185, + 5.229749466466734, + 12.45236780256954, + 113.28034507259024, + 16.872509526872864, + 183.5309631867931, + 124.91977274284777, + 119.71991124316344, + 157.1870298933421, + 274.4122702419585, + 366.9283073596733, + 436.7984767499305, + 656.8651152221494, + 693.6807559949332, + 709.8622378950579, + 940.2747009417171, + 713.595404833571, + 1152.7154288766947, + 1175.086016427977, + 1117.072092779762, + 1473.3110894037666, + 1398.3815929624138, + 1426.719077735046, + 1724.6637655473126, + 1689.600981475803, + 1716.4157506396168, + 2313.2400917892637, + 1548.166884927166, + 873.0003498783823, 0.0, 0.0, 0.0, @@ -563,73 +563,73 @@ 3.1819553106957756, 3.210824766445999, 3.2113869376507562, - 3.2114127786783255, - 3.211416106607547, - 3.2113893826012614, - 3.211426416730049, - 3.2113609775292393, - 3.2113424281577405, - 3.2113732404827138, - 3.2114163514266383, - 3.2115424540877786, - 3.2114240008557804, - 3.2113398217104034, - 3.2114357645677893, - 3.2114307576516063, - 3.2113884470339853, - 3.211430796900003, - 3.211389055700846, - 3.2113853926900813, - 3.21139138936708, - 3.211386722081144, - 3.2113146096279768, - 3.2112229705960287, - 3.2111449047243386, - 3.2110067641753868, - 3.2109119715996353, - 3.210774001590455, - 3.210599916667371, - 3.210409429661165, - 3.2101913618115168, - 3.20995073746505, - 3.209672363010088, - 3.209388535442117, - 3.2090548079737142, - 3.2087189035843537, - 3.2084002150359723, - 3.209253007341981, - 3.210961634193927, - 3.208714219483822, - 3.2078770543055657, - 3.2099811750510763, - 3.2099476105422085, - 3.205794882576252, - 3.205384168539671, - 3.20609132730366, - 3.2055193740331265, - 3.206017846407191, - 3.208320022840661, - 3.209364084855916, - 3.2093323815411345, - 3.2077746632044515, - 3.2052718199939743, - 3.204666769872325, - 3.2049353274677914, - 3.205556387328138, - 3.2069851034663706, - 3.2059668637455325, - 3.205811839035014, - 3.205474317245427, - 3.205693324445893, - 3.205705272070128, - 3.2056899532532865, - 3.205701459845998, - 3.2056470262047125, - 3.2071535188963542, - 3.207886027964543, - 3.207631231852247, - 3.2062764850292496, - 3.202043659162054 + 3.211412733613088, + 3.211416086243693, + 3.211389363109834, + 3.211426369650999, + 3.2113609390638658, + 3.2113423890613904, + 3.211373063658337, + 3.211416879532908, + 3.2115410232676393, + 3.211420449026326, + 3.211340414954566, + 3.2114367116108946, + 3.2114314964286104, + 3.211388714333208, + 3.211430070547672, + 3.211389214185582, + 3.211385960112267, + 3.2113923720865305, + 3.2113927640902724, + 3.2113232302750965, + 3.2112382778326545, + 3.2111709478497157, + 3.211044813629002, + 3.2109619319113025, + 3.210835775367167, + 3.210676328655387, + 3.210494708667366, + 3.2102932552996304, + 3.210065134623158, + 3.2097946508076096, + 3.2095209162914307, + 3.2091908718152014, + 3.208858073378806, + 3.208540190789524, + 3.2092530229321037, + 3.21096166446629, + 3.2087142239418385, + 3.207876985261815, + 3.209981082122852, + 3.2099475857031297, + 3.2059177379588446, + 3.205500472975577, + 3.2060914486021095, + 3.205519414840442, + 3.206014703356543, + 3.208394467358398, + 3.2096207184298824, + 3.2099349143153155, + 3.209121978170608, + 3.2060689626565755, + 3.2046673031145434, + 3.204929136720981, + 3.205524292587136, + 3.2054895995204817, + 3.206946403159766, + 3.205982786489014, + 3.2056724075416114, + 3.205688076719014, + 3.2056000742724207, + 3.205427784285096, + 3.205636810334264, + 3.2082911860239833, + 3.210702471698039, + 3.210541425755314, + 3.2095944307240436, + 3.208960536226681, + 3.2047017324364746 ], "datatype": "Float64", "type": "series" @@ -789,29 +789,29 @@ 0.0, 0.0, 0.0, - 7.496013434489482, - 4.4059235799132725, - 3.863763129703675, - 5.608197865483601, - 5.873368216011654, - 8.085513300597075, - 5.478876262291463, - 4.599394758694961, - 4.499307194633306, - 4.593780734393691, - 4.18301483779327, - 1.4104607407747796, - 3.2046657016293922, - 11.008712667545892, - 5.743854966354466, - 4.599781912734178, + 7.495670456119567, + 4.405996200470255, + 3.8637687267462297, + 5.608251475045593, + 5.873394653044703, + 8.085513883298564, + 5.478878616770271, + 4.599392627242936, + 4.4993047636542105, + 4.593772701085671, + 4.183024838311256, + 1.4104710124483635, + 3.204733460221206, + 11.008683815731874, + 5.7439955641918825, + 4.599460338927322, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 6.279727857215189, + 6.936334864725218, 0.0, 0.0, 0.0, From e4585177c92b1a5949854a2eda5c73ba0d619844 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Thu, 23 May 2024 12:40:09 +0200 Subject: [PATCH 27/49] Adapt some code to GPUs (#533) * Statically return zero verlocity and acceleration without `movement` * Make `write_v0!` and `write_u0!` GPU-compatible * Remove hardcoded float type in rectangular tank setup * Replace more hardcoded `Float64` types * Simplify zero SVector even more --- src/general/system.jl | 2 +- src/schemes/boundary/system.jl | 20 +++++++++++++++++-- src/schemes/fluid/fluid.jl | 18 ++++++----------- src/schemes/fluid/viscosity.jl | 4 ++-- .../fluid/weakly_compressible_sph/system.jl | 6 ++---- .../solid/total_lagrangian_sph/system.jl | 2 +- src/setups/rectangular_tank.jl | 4 ++-- 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/general/system.jl b/src/general/system.jl index c201bf3a5..a4c7754c4 100644 --- a/src/general/system.jl +++ b/src/general/system.jl @@ -61,7 +61,7 @@ end @inline function current_acceleration(system, particle) # TODO: Return `dv` of solid particles - return SVector(ntuple(_ -> 0.0, Val(ndims(system)))) + return zero(SVector{ndims(system), eltype(system)}) end @inline function smoothing_kernel(system, distance) diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl index 334f2d30d..843dfa4df 100644 --- a/src/schemes/boundary/system.jl +++ b/src/schemes/boundary/system.jl @@ -243,23 +243,39 @@ end end @inline function current_velocity(v, system::BoundarySPHSystem, particle) + return current_velocity(v, system, system.movement, particle) +end + +@inline function current_velocity(v, system, movement, particle) (; cache, ismoving) = system if ismoving[] return extract_svector(cache.velocity, system, particle) end - return SVector(ntuple(_ -> 0.0, Val(ndims(system)))) + return zero(SVector{ndims(system), eltype(system)}) +end + +@inline function current_velocity(v, system, movement::Nothing, particle) + return zero(SVector{ndims(system), eltype(system)}) end @inline function current_acceleration(system::BoundarySPHSystem, particle) + return current_acceleration(system, system.movement, particle) +end + +@inline function current_acceleration(system, movement, particle) (; cache, ismoving) = system if ismoving[] return extract_svector(cache.acceleration, system, particle) end - return SVector(ntuple(_ -> 0.0, Val(ndims(system)))) + return zero(SVector{ndims(system), eltype(system)}) +end + +@inline function current_acceleration(system, movement::Nothing, particle) + return zero(SVector{ndims(system), eltype(system)}) end @inline function viscous_velocity(v, system::BoundarySPHSystem, particle) diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index e2f08444d..04e29888a 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -14,23 +14,17 @@ end function write_u0!(u0, system::FluidSystem) (; initial_condition) = system - for particle in eachparticle(system) - # Write particle coordinates - for dim in 1:ndims(system) - u0[dim, particle] = initial_condition.coordinates[dim, particle] - end - end + # This is as fast as a loop with `@inbounds`, but it's GPU-compatible + indices = CartesianIndices(initial_condition.coordinates) + copyto!(u0, indices, initial_condition.coordinates, indices) return u0 end function write_v0!(v0, system::FluidSystem) - for particle in eachparticle(system) - # Write particle velocities - for dim in 1:ndims(system) - v0[dim, particle] = system.initial_condition.velocity[dim, particle] - end - end + # This is as fast as a loop with `@inbounds`, but it's GPU-compatible + indices = CartesianIndices(system.initial_condition.velocity) + copyto!(v0, indices, system.initial_condition.velocity, indices) write_v0!(v0, system, system.density_calculator) diff --git a/src/schemes/fluid/viscosity.jl b/src/schemes/fluid/viscosity.jl index 3c0c517a1..6a34692f0 100644 --- a/src/schemes/fluid/viscosity.jl +++ b/src/schemes/fluid/viscosity.jl @@ -25,7 +25,7 @@ function dv_viscosity(viscosity::Nothing, particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, sound_speed, m_a, m_b, rho_mean) - return SVector(ntuple(_ -> 0.0, Val(ndims(particle_system)))) + return zero(pos_diff) end @doc raw""" @@ -100,7 +100,7 @@ end pi_ab = viscosity(sound_speed, v_diff, pos_diff, distance, rho_mean, smoothing_length) if pi_ab < eps() - return SVector(ntuple(_ -> 0.0, Val(ndims(particle_system)))) + return zero(pos_diff) end return -m_b * pi_ab * smoothing_kernel_grad(particle_system, pos_diff, distance) diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index ff9ec3c95..7df899868 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -295,10 +295,8 @@ end end function write_v0!(v0, system::WeaklyCompressibleSPHSystem, ::ContinuityDensity) - for particle in eachparticle(system) - # Set particle densities - v0[end, particle] = system.initial_condition.density[particle] - end + # Note that `.=` is very slightly faster, but not GPU-compatible + v0[end, :] = system.initial_condition.density return v0 end diff --git a/src/schemes/solid/total_lagrangian_sph/system.jl b/src/schemes/solid/total_lagrangian_sph/system.jl index a8632885d..fe949b9f1 100644 --- a/src/schemes/solid/total_lagrangian_sph/system.jl +++ b/src/schemes/solid/total_lagrangian_sph/system.jl @@ -181,7 +181,7 @@ end @inline function current_velocity(v, system::TotalLagrangianSPHSystem, particle) if particle > n_moving_particles(system) - return SVector(ntuple(_ -> 0.0, Val(ndims(system)))) + return zero(SVector{ndims(system), eltype(system)}) end return extract_svector(v, system, particle) diff --git a/src/setups/rectangular_tank.jl b/src/setups/rectangular_tank.jl index f3d260191..3aa0af76d 100644 --- a/src/setups/rectangular_tank.jl +++ b/src/setups/rectangular_tank.jl @@ -302,7 +302,7 @@ function initialize_boundaries(particle_spacing, tank_size::NTuple{2}, face_indices_4 = Array{Int, 2}(undef, n_layers, n_particles_x) # Create empty array to extend later depending on faces and corners to build - boundary_coordinates = Array{Float64, 2}(undef, 2, 0) + boundary_coordinates = Array{typeof(particle_spacing), 2}(undef, 2, 0) # Counts the global index of the particles index = 0 @@ -433,7 +433,7 @@ function initialize_boundaries(particle_spacing, tank_size::NTuple{3}, face_indices_6 = Array{Int, 2}(undef, n_layers, n_particles_x * n_particles_y) # Create empty array to extend later depending on faces and corners to build - boundary_coordinates = Array{Float64, 2}(undef, 3, 0) + boundary_coordinates = Array{typeof(particle_spacing), 2}(undef, 3, 0) # Counts the global index of the particles index = 0 From c36e13dd1250008dddb2cb17a2ed2d6c9eeaaff1 Mon Sep 17 00:00:00 2001 From: Niklas Neher <73897120+LasNikas@users.noreply.github.com> Date: Fri, 24 May 2024 10:43:48 +0200 Subject: [PATCH 28/49] Modify error message (#531) * change message * fix tests --------- Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> --- src/general/semidiscretization.jl | 2 +- test/general/semidiscretization.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index e699f4bf0..a3ce1e056 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -739,7 +739,7 @@ function check_configuration(boundary_system::BoundarySPHSystem, systems) boundary_model isa BoundaryModelDummyParticles && isnothing(boundary_model.state_equation) throw(ArgumentError("`WeaklyCompressibleSPHSystem` cannot be used without " * - "setting a `state_equation` for all boundary systems")) + "setting a `state_equation` for all boundary models")) end end end diff --git a/test/general/semidiscretization.jl b/test/general/semidiscretization.jl index ac0f34300..8a74de0ab 100644 --- a/test/general/semidiscretization.jl +++ b/test/general/semidiscretization.jl @@ -90,7 +90,7 @@ kernel, 1.0) error_str = "`WeaklyCompressibleSPHSystem` cannot be used without setting a " * - "`state_equation` for all boundary systems" + "`state_equation` for all boundary models" @test_throws ArgumentError(error_str) Semidiscretization(fluid_system, boundary_system) end From 6f83043d314d75e22c075728b9e2559dfa089621 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Tue, 28 May 2024 17:44:12 +0200 Subject: [PATCH 29/49] The Grand ReReReReReopening the final straw part 9: Intra-Particle-Force Surface Tension model (#434) * merge by hand * fix merge problems * fix merge * fix merge * fix merge * fix merge * fix merge * more merge fixes * format * fix * fix * fix * fix * fix * more fixes * fix * fix * fix * more fixes * fix * fix * format * running * update * update * optimization * update * update * update * update * format * up * update * remove unused code * switch to support radius * update * fix doc * add adhesion coefficient * update_broken * update * format * add to examples * fix tests * fix merge * fix typos * add basic test * format * rename * format * rename container system * reduce example run time * update news and readme * format * fix test * reduce run time * format * fix * correct merge * update doc test * revert * fix tests * fix * fix * review comments * fix * fix * correct some stuff * init with empty initial condition * review update * rename function * remove unnecessary if * docs * fix doc * revert one change * fix typo * update * try to avoid allocs * fix mem allocs * review update * update docs * update * review * fix * format * make examples smaller * reduce resolution * review * rename * use trixi_include * format * use trixi_include * update * update * review update * format * fix test errors * update * fix surface normal calculation * adjust testcases and review comments * update examples * remove saving interval * fix comment * remove dt --- NEWS.md | 11 +- docs/src/index.md | 1 + docs/src/systems/weakly_compressible_sph.md | 62 ++++++ examples/fluid/dam_break_2d.jl | 6 +- .../fluid/dam_break_2d_surface_tension.jl | 31 +++ examples/fluid/falling_water_spheres_2d.jl | 95 ++++++++ examples/fluid/falling_water_spheres_3d.jl | 37 ++++ examples/fluid/sphere_surface_tension_2d.jl | 59 +++++ examples/fluid/sphere_surface_tension_3d.jl | 25 +++ .../fluid/sphere_surface_tension_wall_2d.jl | 44 ++++ src/TrixiParticles.jl | 1 + src/schemes/boundary/system.jl | 41 ++-- src/schemes/fluid/fluid.jl | 1 + src/schemes/fluid/surface_tension.jl | 206 ++++++++++++++++++ src/schemes/fluid/viscosity.jl | 1 + .../density_diffusion.jl | 32 +++ .../fluid/weakly_compressible_sph/rhs.jl | 56 ++--- .../fluid/weakly_compressible_sph/system.jl | 66 +++++- src/setups/rectangular_tank.jl | 34 +-- test/examples/examples.jl | 54 +++++ test/schemes/fluid/fluid.jl | 1 + test/schemes/fluid/surface_tension.jl | 92 ++++++++ test/systems/boundary_system.jl | 3 +- test/systems/wcsph_system.jl | 3 +- 24 files changed, 877 insertions(+), 85 deletions(-) create mode 100644 examples/fluid/dam_break_2d_surface_tension.jl create mode 100644 examples/fluid/falling_water_spheres_2d.jl create mode 100644 examples/fluid/falling_water_spheres_3d.jl create mode 100644 examples/fluid/sphere_surface_tension_2d.jl create mode 100644 examples/fluid/sphere_surface_tension_3d.jl create mode 100644 examples/fluid/sphere_surface_tension_wall_2d.jl create mode 100644 src/schemes/fluid/surface_tension.jl create mode 100644 test/schemes/fluid/surface_tension.jl diff --git a/NEWS.md b/NEWS.md index 3541c9d68..ffe272e8a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,17 +17,16 @@ We aim at 3 to 4 month between major release versions and about 2 weeks between ## Version 0.1.2 +### Added +A surface tension and adhesion model based on the work by Akinci et al., "Versatile Surface Tension and Adhesion for SPH Fluids" (2013) was added to WCSPH + +## Version 0.1.1 + ### Highlights #### Discrete Element Method A basic implementation of the discrete element method was added. - -## Version 0.1.1 - -### Added -A surface tension and adhesion model based on the work by Akinci et al., "Versatile Surface Tension and Adhesion for SPH Fluids", 2013 was added to WCSPH - # Pre Initial Release (v0.1.0) This section summarizes the initial features that TrixiParticles.jl was released with. diff --git a/docs/src/index.md b/docs/src/index.md index a3b41a417..42c2c42c6 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -5,6 +5,7 @@ TrixiParticles.jl is a numerical simulation framework designed for particle-base ## Features - Incompressible Navier-Stokes - Methods: Weakly Compressible Smoothed Particle Hydrodynamics (WCSPH), Entropically Damped Artificial Compressibility (EDAC) + - Models: Surface Tension - Solid-body mechanics - Methods: Total Lagrangian SPH (TLSPH) - Fluid-Structure Interaction diff --git a/docs/src/systems/weakly_compressible_sph.md b/docs/src/systems/weakly_compressible_sph.md index 69b514950..353b6b5cb 100644 --- a/docs/src/systems/weakly_compressible_sph.md +++ b/docs/src/systems/weakly_compressible_sph.md @@ -147,3 +147,65 @@ Pages = [joinpath("schemes", "fluid", "weakly_compressible_sph", "density_diffus Modules = [TrixiParticles] Pages = [joinpath("general", "corrections.jl")] ``` + +## [Surface Tension](@id surface_tension) + +### Akinci-based intra-particle force surface tension and wall adhesion model +The work by Akinci proposes three forces: +- a cohesion force +- a surface area minimization force +- a wall adhesion force + +The classical model is composed of the curvature minimization and cohesion force. + +#### Cohesion force +The model calculates the cohesion force based on the distance between particles and the support radius ``h_c``. +This force is determined using two distinct regimes within the support radius: +- For particles closer than half the support radius, + a repulsive force is calculated to prevent particle clustering too tightly, + enhancing the simulation's stability and realism. +- Beyond half the support radius and within the full support radius, + an attractive force is computed, simulating the effects of surface tension that draw particles together. +The cohesion force, ``F_{\text{cohesion}}``, for a pair of particles is given by: +```math +F_{\text{cohesion}} = -\sigma m_b C(r) \frac{r}{\Vert r \Vert}, +``` +where: +- ``\sigma`` represents the surface tension coefficient, adjusting the overall strength of the cohesion effect. +- ``C`` is a scalar function of the distance between particles. + +The cohesion kernel ``C`` is defined as +```math +C(r)=\frac{32}{\pi h_c^9} +\begin{cases} +(h_c-r)^3 r^3, & \text{if } 2r > h_c \\ +2(h_c-r)^3 r^3 - \frac{h^6}{64}, & \text{if } r > 0 \text{ and } 2r \leq h_c \\ +0, & \text{otherwise} +\end{cases} +``` + +#### Surface area minimization force +To model the minimization of the surface area and curvature of the fluid, a curvature force is used, which is calculated as +```math +F_{\text{curvature}} = -\sigma (n_a - n_b) +``` + +#### Wall adhesion force +The wall adhesion model proposed by Akinci et al. is based on a kernel function which is 0 from 0.0 to 0.5 support radiia with a maximum at 0.75. +With the force calculated with an adhesion coefficient ``\beta`` as +```math +F_{\text{adhesion}} = -\beta m_b A(r) \frac{r}{\Vert r \Vert}, +``` +with ``A`` being the adhesion kernel defined as +```math +A(r)= \frac{0.007}{h_c^{3.25}} +\begin{cases} +\sqrt[4]{- \frac{4r^2}{h_c} + 6r - 2h_c}, & \text{if } 2r > h_c \text{ and } r \leq h_c \\ +0, & \text{otherwise.} +\end{cases} +``` + +```@autodocs +Modules = [TrixiParticles] +Pages = [joinpath("schemes", "fluid", "surface_tension.jl")] +``` diff --git a/examples/fluid/dam_break_2d.jl b/examples/fluid/dam_break_2d.jl index 40176927c..e58a7cc06 100644 --- a/examples/fluid/dam_break_2d.jl +++ b/examples/fluid/dam_break_2d.jl @@ -57,8 +57,8 @@ fluid_system = WeaklyCompressibleSPHSystem(tank.fluid, fluid_density_calculator, state_equation, smoothing_kernel, smoothing_length, viscosity=viscosity, density_diffusion=density_diffusion, - acceleration=(0.0, -gravity), - correction=nothing) + acceleration=(0.0, -gravity), correction=nothing, + surface_tension=nothing) # ========================================================================================== # ==== Boundary @@ -69,7 +69,7 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar smoothing_kernel, smoothing_length, correction=nothing) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) +boundary_system = BoundarySPHSystem(tank.boundary, boundary_model, adhesion_coefficient=0.0) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/dam_break_2d_surface_tension.jl b/examples/fluid/dam_break_2d_surface_tension.jl new file mode 100644 index 000000000..d83eb9865 --- /dev/null +++ b/examples/fluid/dam_break_2d_surface_tension.jl @@ -0,0 +1,31 @@ +# This example shows how surface tension can be applied to existing cases +# and which parameters have to be changed! +using TrixiParticles + +fluid_density = 1000.0 + +H = 0.6 +fluid_particle_spacing = H / 60 + +# Set the surface tension to a value that is accurate in your case. +# Note: This usually requires calibration to be physically accurate! +surface_tension = SurfaceTensionAkinci(surface_tension_coefficient=0.025) + +# `density_diffusion` is deactivated since the interaction with the surface tension model can +# cause stability problems. +# `adhesion_coefficient` needs to be set to a value so that the fluid doesn't separate +# from the boundary. +# Note: The surface tension model leads to an increase in compressibility of the fluid, +# which needs to be rectified by an increase of the `sound_speed`. +# Note: The Wendland Kernels don't work very well here since the `SurfaceTensionAkinci` +# model is optimized for a compact support of `2 * particle_spacing`, which would result +# in a too small `smoothing_length` for the Wendland Kernel functions. +# Note: Adhesion will result in additional friction at the boundary. +trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "dam_break_2d.jl"), + surface_tension=surface_tension, + fluid_particle_spacing=fluid_particle_spacing, + smoothing_kernel=SchoenbergCubicSplineKernel{2}(), + smoothing_length=1.0 * fluid_particle_spacing, + correction=AkinciFreeSurfaceCorrection(fluid_density), + density_diffusion=nothing, adhesion_coefficient=0.05, + sound_speed=100.0, tspan=(0.0, 2.0)) diff --git a/examples/fluid/falling_water_spheres_2d.jl b/examples/fluid/falling_water_spheres_2d.jl new file mode 100644 index 000000000..37132d70b --- /dev/null +++ b/examples/fluid/falling_water_spheres_2d.jl @@ -0,0 +1,95 @@ +# In this example two circles of water drop to the floor demonstrating the difference +# between the behavior with and without surface tension modelling. +using TrixiParticles +using OrdinaryDiffEq + +# ========================================================================================== +# ==== Resolution +fluid_particle_spacing = 0.005 + +boundary_layers = 3 +spacing_ratio = 1 + +# ========================================================================================== +# ==== Experiment Setup +gravity = 9.81 +tspan = (0.0, 0.3) + +# Boundary geometry and initial fluid particle positions +initial_fluid_size = (0.0, 0.0) +tank_size = (2.0, 0.5) + +fluid_density = 1000.0 +sound_speed = 100 +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=1) + +tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, + n_layers=boundary_layers, spacing_ratio=spacing_ratio, + faces=(true, true, true, false), + acceleration=(0.0, -gravity), state_equation=state_equation) + +sphere_radius = 0.05 + +sphere1_center = (0.5, 0.2) +sphere2_center = (1.5, 0.2) +sphere1 = SphereShape(fluid_particle_spacing, sphere_radius, sphere1_center, + fluid_density, sphere_type=VoxelSphere(), velocity=(0.0, -3.0)) +sphere2 = SphereShape(fluid_particle_spacing, sphere_radius, sphere2_center, + fluid_density, sphere_type=VoxelSphere(), velocity=(0.0, -3.0)) + +# ========================================================================================== +# ==== Fluid +fluid_smoothing_length = 1.0 * fluid_particle_spacing +fluid_smoothing_kernel = SchoenbergCubicSplineKernel{2}() + +fluid_density_calculator = ContinuityDensity() + +nu = 0.005 +alpha = 8 * nu / (fluid_smoothing_length * sound_speed) +viscosity = ArtificialViscosityMonaghan(alpha=alpha, beta=0.0) +density_diffusion = DensityDiffusionAntuono(sphere2, delta=0.1) + +sphere_surface_tension = WeaklyCompressibleSPHSystem(sphere1, fluid_density_calculator, + state_equation, fluid_smoothing_kernel, + fluid_smoothing_length, + viscosity=viscosity, + acceleration=(0.0, -gravity), + surface_tension=SurfaceTensionAkinci(surface_tension_coefficient=0.05), + correction=AkinciFreeSurfaceCorrection(fluid_density)) + +sphere = WeaklyCompressibleSPHSystem(sphere2, fluid_density_calculator, + state_equation, fluid_smoothing_kernel, + fluid_smoothing_length, viscosity=viscosity, + density_diffusion=density_diffusion, + acceleration=(0.0, -gravity)) + +# ========================================================================================== +# ==== Boundary +boundary_density_calculator = AdamiPressureExtrapolation() +wall_viscosity = nu +boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, + state_equation=state_equation, + boundary_density_calculator, + fluid_smoothing_kernel, fluid_smoothing_length, + viscosity=ViscosityAdami(nu=wall_viscosity)) + +boundary_system = BoundarySPHSystem(tank.boundary, boundary_model, + adhesion_coefficient=0.001) + +# ========================================================================================== +# ==== Simulation +semi = Semidiscretization(boundary_system, sphere_surface_tension, sphere) +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=50) +saving_callback = SolutionSavingCallback(dt=0.01, output_directory="out", prefix="", + write_meta_data=true) + +callbacks = CallbackSet(info_callback, saving_callback) + +# Use a Runge-Kutta method with automatic (error based) time step size control. +sol = solve(ode, RDPK3SpFSAL35(), + abstol=1e-6, # Default abstol is 1e-6 + reltol=1e-4, # Default reltol is 1e-3 + save_everystep=false, callback=callbacks); diff --git a/examples/fluid/falling_water_spheres_3d.jl b/examples/fluid/falling_water_spheres_3d.jl new file mode 100644 index 000000000..a4ebb799e --- /dev/null +++ b/examples/fluid/falling_water_spheres_3d.jl @@ -0,0 +1,37 @@ +using TrixiParticles +using OrdinaryDiffEq + +# ========================================================================================== +# ==== Resolution +fluid_particle_spacing = 0.008 + +# ========================================================================================== +# ==== Experiment Setup +gravity = 9.81 +nu = 0.01 +fluid_density = 1000.0 +sound_speed = 50 + +sphere1_radius = 0.05 + +sphere1_center = (0.5, 0.5, 0.2) +sphere2_center = (1.5, 0.5, 0.2) +sphere1 = SphereShape(fluid_particle_spacing, sphere1_radius, sphere1_center, + fluid_density, sphere_type=VoxelSphere(), velocity=(0.0, 0.0, -2.0)) +sphere2 = SphereShape(fluid_particle_spacing, sphere1_radius, sphere2_center, + fluid_density, sphere_type=VoxelSphere(), velocity=(0.0, 0.0, -2.0)) + +# `compact_support` needs to be `2.0 * particle_spacing` to be correct +fluid_smoothing_length = 1.0 * fluid_particle_spacing + +trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "falling_water_spheres_2d.jl"), + fluid_particle_spacing=fluid_particle_spacing, tspan=(0.0, 0.2), + initial_fluid_size=(0.0, 0.0, 0.0), + tank_size=(2.0, 1.0, 0.1), sound_speed=sound_speed, + faces=(true, true, true, true, true, false), + acceleration=(0.0, 0.0, -gravity), sphere1=sphere1, sphere2=sphere2, + fluid_smoothing_length=fluid_smoothing_length, + fluid_smoothing_kernel=SchoenbergCubicSplineKernel{3}(), + nu=nu, alpha=10 * nu / (fluid_smoothing_length * sound_speed), + surface_tension_coefficient=1.5, adhesion_coefficient=0.5) diff --git a/examples/fluid/sphere_surface_tension_2d.jl b/examples/fluid/sphere_surface_tension_2d.jl new file mode 100644 index 000000000..9006ebf0e --- /dev/null +++ b/examples/fluid/sphere_surface_tension_2d.jl @@ -0,0 +1,59 @@ +# In this example we can observe that the `SurfaceTensionAkinci` surface tension model correctly leads to a +# surface minimization of the water square and approaches a sphere. +using TrixiParticles +using OrdinaryDiffEq + +fluid_density = 1000.0 + +particle_spacing = 0.1 + +# Note: Only square shapes will result in a sphere. +# Furthermore, changes of the coefficients might be necessary for higher resolutions or larger squares. +fluid_size = (0.5, 0.5) + +sound_speed = 20.0 +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=7, clip_negative_pressure=true) + +# For all surface tension simulations, we need a compact support of `2 * particle_spacing` +# smoothing_length = 2.0 * particle_spacing +# smoothing_kernel = WendlandC2Kernel{2}() +# nu = 0.01 + +smoothing_length = 1.0 * particle_spacing +smoothing_kernel = SchoenbergCubicSplineKernel{2}() +nu = 0.025 + +fluid = RectangularShape(particle_spacing, round.(Int, fluid_size ./ particle_spacing), + zeros(length(fluid_size)), density=fluid_density) + +alpha = 8 * nu / (smoothing_length * sound_speed) +source_terms = SourceTermDamping(; damping_coefficient=0.5) +fluid_system = WeaklyCompressibleSPHSystem(fluid, SummationDensity(), + state_equation, smoothing_kernel, + smoothing_length, + viscosity=ArtificialViscosityMonaghan(alpha=alpha, + beta=0.0), + surface_tension=SurfaceTensionAkinci(surface_tension_coefficient=0.02), + correction=AkinciFreeSurfaceCorrection(fluid_density), + source_terms=source_terms) + +# ========================================================================================== +# ==== Simulation +semi = Semidiscretization(fluid_system) + +tspan = (0.0, 3.0) +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=100) + +# For overwriting via `trixi_include` +saving_callback = SolutionSavingCallback(dt=0.02) + +stepsize_callback = StepsizeCallback(cfl=1.0) + +callbacks = CallbackSet(info_callback, saving_callback, stepsize_callback) + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # This is overwritten by the stepsize callback + save_everystep=false, callback=callbacks); diff --git a/examples/fluid/sphere_surface_tension_3d.jl b/examples/fluid/sphere_surface_tension_3d.jl new file mode 100644 index 000000000..58a506294 --- /dev/null +++ b/examples/fluid/sphere_surface_tension_3d.jl @@ -0,0 +1,25 @@ +# In this example we can observe that the `SurfaceTensionAkinci` surface tension model correctly leads to a +# surface minimization of the water cube and approaches a sphere. +using TrixiParticles +using OrdinaryDiffEq + +fluid_density = 1000.0 + +particle_spacing = 0.1 +fluid_size = (0.9, 0.9, 0.9) + +sound_speed = 20.0 + +# For all surface tension simulations, we need a compact support of `2 * particle_spacing` +smoothing_length = 1.0 * particle_spacing + +nu = 0.01 + +trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "sphere_surface_tension_2d.jl"), + dt=0.1, cfl=1.2, surface_tension_coefficient=0.1, + tspan=(0.0, 10.0), nu=nu, + alpha=10 * nu / (smoothing_length * sound_speed), + smoothing_kernel=SchoenbergCubicSplineKernel{3}(), + particle_spacing=particle_spacing, sound_speed=sound_speed, + fluid_density=fluid_density, fluid_size=fluid_size) diff --git a/examples/fluid/sphere_surface_tension_wall_2d.jl b/examples/fluid/sphere_surface_tension_wall_2d.jl new file mode 100644 index 000000000..3e1cbdb80 --- /dev/null +++ b/examples/fluid/sphere_surface_tension_wall_2d.jl @@ -0,0 +1,44 @@ +# In this example we try to approach the static shape of a water droplet on a horizontal plane. +# The shape of a static droplet can be calculated from the Young-Laplace equation. +using TrixiParticles +using OrdinaryDiffEq + +# ========================================================================================== +# ==== Resolution +fluid_particle_spacing = 0.0025 + +# ========================================================================================== +# ==== Experiment Setup +tspan = (0.0, 0.5) + +# Boundary geometry and initial fluid particle positions +tank_size = (0.5, 0.1) + +fluid_density = 1000.0 +sound_speed = 120.0 + +sphere_radius = 0.05 + +sphere1_center = (0.25, sphere_radius) +sphere1 = SphereShape(fluid_particle_spacing, sphere_radius, sphere1_center, + fluid_density, sphere_type=VoxelSphere()) + +# ========================================================================================== +# ==== Fluid +fluid_smoothing_length = 1.0 * fluid_particle_spacing + +# For perfect wetting +# nu = 0.0005 +# For no wetting +nu = 0.001 + +alpha = 8 * nu / (fluid_smoothing_length * sound_speed) +# `adhesion_coefficient = 1.0` and `surface_tension_coefficient = 0.01` for perfect wetting +# `adhesion_coefficient = 0.001` and `surface_tension_coefficient = 2.0` for no wetting +trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "falling_water_spheres_2d.jl"), + sphere=nothing, sphere1=sphere1, adhesion_coefficient=0.001, + wall_viscosity=4.0 * nu, surface_tension_coefficient=2.0, alpha=alpha, + sound_speed=sound_speed, fluid_density=fluid_density, nu=nu, + fluid_particle_spacing=fluid_particle_spacing, tspan=tspan, + tank_size=tank_size) diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index c95451a25..f170e08d8 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -70,5 +70,6 @@ export kinetic_energy, total_mass, max_pressure, min_pressure, avg_pressure, max_density, min_density, avg_density export interpolate_line, interpolate_point, interpolate_plane_3d, interpolate_plane_2d, interpolate_plane_2d_vtk +export SurfaceTensionAkinci, CohesionForceAkinci end # module diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl index 843dfa4df..a1bec02e9 100644 --- a/src/schemes/boundary/system.jl +++ b/src/schemes/boundary/system.jl @@ -1,5 +1,5 @@ """ - BoundarySPHSystem(initial_condition, boundary_model; movement=nothing) + BoundarySPHSystem(initial_condition, boundary_model; movement=nothing, adhesion_coefficient=0.0) System for boundaries modeled by boundary particles. The interaction between fluid and boundary particles is specified by the boundary model. @@ -10,30 +10,38 @@ The interaction between fluid and boundary particles is specified by the boundar # Keyword Arguments - `movement`: For moving boundaries, a [`BoundaryMovement`](@ref) can be passed. +- `adhesion_coefficient`: Coefficient specifying the adhesion of a fluid to the surface. + Note: currently it is assumed that all fluids have the same adhesion coefficient. """ -struct BoundarySPHSystem{BM, NDIMS, IC, CO, M, IM, CA} <: BoundarySystem{NDIMS} - initial_condition :: IC - coordinates :: CO # Array{ELTYPE, 2} - boundary_model :: BM - movement :: M - ismoving :: IM # Ref{Bool} (to make a mutable field compatible with GPUs) - cache :: CA +struct BoundarySPHSystem{BM, NDIMS, ELTYPE <: Real, IC, CO, M, IM, CA} <: + BoundarySystem{NDIMS} + initial_condition :: IC + coordinates :: CO # Array{ELTYPE, 2} + boundary_model :: BM + movement :: M + ismoving :: IM # Ref{Bool} (to make a mutable field compatible with GPUs) + adhesion_coefficient :: ELTYPE + cache :: CA # This constructor is necessary for Adapt.jl to work with this struct. # See the comments in general/gpu.jl for more details. function BoundarySPHSystem(initial_condition, coordinates, boundary_model, movement, - ismoving, cache) - new{typeof(boundary_model), size(coordinates, 1), + ismoving, adhesion_coefficient, cache) + ELTYPE = eltype(coordinates) + + new{typeof(boundary_model), size(coordinates, 1), ELTYPE, typeof(initial_condition), typeof(coordinates), typeof(movement), typeof(ismoving), typeof(cache)}(initial_condition, coordinates, boundary_model, - movement, - ismoving, cache) + movement, ismoving, + adhesion_coefficient, cache) end end -function BoundarySPHSystem(initial_condition, model; movement=nothing) +function BoundarySPHSystem(initial_condition, model; movement=nothing, + adhesion_coefficient=0.0) coordinates = copy(initial_condition.coordinates) + ismoving = Ref(!isnothing(movement)) cache = create_cache_boundary(movement, initial_condition) @@ -45,8 +53,9 @@ function BoundarySPHSystem(initial_condition, model; movement=nothing) movement.moving_particles .= collect(1:nparticles(initial_condition)) end - return BoundarySPHSystem(initial_condition, coordinates, model, - movement, ismoving, cache) + # Because of dispatches boundary model needs to be first! + return BoundarySPHSystem(initial_condition, coordinates, model, movement, + ismoving, adhesion_coefficient, cache) end """ @@ -156,6 +165,7 @@ function Base.show(io::IO, system::BoundarySPHSystem) print(io, "BoundarySPHSystem{", ndims(system), "}(") print(io, system.boundary_model) print(io, ", ", system.movement) + print(io, ", ", system.adhesion_coefficient) print(io, ") with ", nparticles(system), " particles") end @@ -171,6 +181,7 @@ function Base.show(io::IO, ::MIME"text/plain", system::BoundarySPHSystem) summary_line(io, "movement function", isnothing(system.movement) ? "nothing" : string(system.movement.movement_function)) + summary_line(io, "adhesion coefficient", system.adhesion_coefficient) summary_footer(io) end end diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index 04e29888a..056662bbc 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -70,5 +70,6 @@ end include("pressure_acceleration.jl") include("viscosity.jl") +include("surface_tension.jl") include("weakly_compressible_sph/weakly_compressible_sph.jl") include("entropically_damped_sph/entropically_damped_sph.jl") diff --git a/src/schemes/fluid/surface_tension.jl b/src/schemes/fluid/surface_tension.jl new file mode 100644 index 000000000..69e468cd7 --- /dev/null +++ b/src/schemes/fluid/surface_tension.jl @@ -0,0 +1,206 @@ +abstract type AkinciTypeSurfaceTension end + +@doc raw""" + CohesionForceAkinci(surface_tension_coefficient=1.0) + +This model only implements the cohesion force of the Akinci surface tension model. + +# Keywords +- `surface_tension_coefficient=1.0`: Modifies the intensity of the surface tension-induced force, + enabling the tuning of the fluid's surface tension properties within the simulation. + +# References +- Nadir Akinci, Gizem Akinci, Matthias Teschner. + "Versatile Surface Tension and Adhesion for SPH Fluids". + In: ACM Transactions on Graphics 32.6 (2013). + [doi: 10.1145/2508363.2508395](https://doi.org/10.1145/2508363.2508395) +""" +struct CohesionForceAkinci{ELTYPE} <: AkinciTypeSurfaceTension + surface_tension_coefficient::ELTYPE + + function CohesionForceAkinci(; surface_tension_coefficient=1.0) + new{typeof(surface_tension_coefficient)}(surface_tension_coefficient) + end +end + +@doc raw""" + SurfaceTensionAkinci(surface_tension_coefficient=1.0) + +Implements a model for surface tension and adhesion effects drawing upon the +principles outlined by Akinci et al. This model is instrumental in capturing the nuanced +behaviors of fluid surfaces, such as droplet formation and the dynamics of merging or +separation, by utilizing intra-particle forces. + +# Keywords +- `surface_tension_coefficient=1.0`: A parameter to adjust the magnitude of + surface tension forces, facilitating the fine-tuning of how surface tension phenomena + are represented in the simulation. + +# References +- Nadir Akinci, Gizem Akinci, Matthias Teschner. + "Versatile Surface Tension and Adhesion for SPH Fluids". + In: ACM Transactions on Graphics 32.6 (2013). + [doi: 10.1145/2508363.2508395](https://doi.org/10.1145/2508363.2508395) +""" +struct SurfaceTensionAkinci{ELTYPE} <: AkinciTypeSurfaceTension + surface_tension_coefficient::ELTYPE + + function SurfaceTensionAkinci(; surface_tension_coefficient=1.0) + new{typeof(surface_tension_coefficient)}(surface_tension_coefficient) + end +end + +# Note that `floating_point_number^integer_literal` is lowered to `Base.literal_pow`. +# Currently, specializations reducing this to simple multiplications exist only up +# to a power of three, see +# https://github.com/JuliaLang/julia/blob/34934736fa4dcb30697ac1b23d11d5ad394d6a4d/base/intfuncs.jl#L327-L339 +# By using the `@fastpow` macro, we are consciously trading off some precision in the result +# for enhanced computational speed. This is especially useful in scenarios where performance +# is a higher priority than exact precision. +@fastpow @inline function cohesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, distance) + (; surface_tension_coefficient) = surface_tension + + # Eq. 2 + # We only reach this function when `sqrt(eps()) < distance <= support_radius` + if distance > 0.5 * support_radius + # Attractive force + C = (support_radius - distance)^3 * distance^3 + else + # `distance < 0.5 * support_radius` + # Repulsive force + C = 2 * (support_radius - distance)^3 * distance^3 - support_radius^6 / 64.0 + end + C *= 32.0 / (pi * support_radius^9) + + # Eq. 1 in acceleration form + cohesion_force = -surface_tension_coefficient * m_b * C * pos_diff / distance + + return cohesion_force +end + +@inline function adhesion_force_akinci(surface_tension, support_radius, m_b, pos_diff, + distance, adhesion_coefficient) + + # The neighborhood search has an `<=` check, but for `distance == support_radius` + # the term inside the parentheses might be very slightly negative, causing an error with `^0.25`. + # TODO Change this in the neighborhood search? + # See https://github.com/trixi-framework/PointNeighbors.jl/issues/19 + distance >= support_radius && return zero(pos_diff) + + distance <= 0.5 * support_radius && return zero(pos_diff) + + # Eq. 7 + A = 0.007 / support_radius^3.25 * + (-4 * distance^2 / support_radius + 6 * distance - 2 * support_radius)^0.25 + + # Eq. 6 in acceleration form with `m_b` being the boundary mass calculated as + # `m_b = rho_0 * volume` (Akinci boundary condition treatment) + adhesion_force = -adhesion_coefficient * m_b * A * pos_diff / distance + + return adhesion_force +end + +# Section 2.2 in Akinci et al. 2013 "Versatile Surface Tension and Adhesion for SPH Fluids" +# Note: most of the time this only leads to an approximation of the surface normal + +function calc_normal_akinci!(system, neighbor_system::FluidSystem, + surface_tension::SurfaceTensionAkinci, u_system, + v_neighbor_system, u_neighbor_system, + neighborhood_search) + (; smoothing_length, cache) = system + + system_coords = current_coordinates(u_system, system) + neighbor_system_coords = current_coordinates(u_neighbor_system, neighbor_system) + + for_particle_neighbor(system, neighbor_system, + system_coords, neighbor_system_coords, + neighborhood_search) do particle, neighbor, pos_diff, distance + m_b = hydrodynamic_mass(neighbor_system, neighbor) + density_neighbor = particle_density(v_neighbor_system, + neighbor_system, neighbor) + grad_kernel = smoothing_kernel_grad(system, pos_diff, distance, + particle) + for i in 1:ndims(system) + cache.surface_normal[i, particle] += m_b / density_neighbor * + grad_kernel[i] * smoothing_length + end + end + + return system +end + +function calc_normal_akinci!(system, neighbor_system, surface_tension, u_system, + v_neighbor_system, u_neighbor_system, + neighborhood_search) + # Normal not needed + return system +end + +@inline function surface_tension_force(surface_tension_a::CohesionForceAkinci, + surface_tension_b::CohesionForceAkinci, + particle_system::FluidSystem, + neighbor_system::FluidSystem, particle, neighbor, + pos_diff, distance) + (; smoothing_length) = particle_system + # No cohesion with oneself + distance < sqrt(eps()) && return zero(pos_diff) + + m_b = hydrodynamic_mass(neighbor_system, neighbor) + support_radius = compact_support(smoothing_kernel, smoothing_length) + + return cohesion_force_akinci(surface_tension_a, support_radius, m_b, pos_diff, distance) +end + +@inline function surface_tension_force(surface_tension_a::SurfaceTensionAkinci, + surface_tension_b::SurfaceTensionAkinci, + particle_system::FluidSystem, + neighbor_system::FluidSystem, particle, neighbor, + pos_diff, distance) + (; smoothing_length, smoothing_kernel) = particle_system + (; surface_tension_coefficient) = surface_tension_a + + # No surface tension with oneself + distance < sqrt(eps()) && return zero(pos_diff) + + m_b = hydrodynamic_mass(neighbor_system, neighbor) + n_a = surface_normal(surface_tension_a, particle_system, particle) + n_b = surface_normal(surface_tension_b, neighbor_system, neighbor) + support_radius = compact_support(smoothing_kernel, smoothing_length) + + return cohesion_force_akinci(surface_tension_a, support_radius, m_b, + pos_diff, distance) .- + (surface_tension_coefficient * (n_a - n_b)) +end + +# Skip +@inline function surface_tension_force(surface_tension_a, surface_tension_b, + particle_system, neighbor_system, particle, neighbor, + pos_diff, distance) + return zero(pos_diff) +end + +@inline function adhesion_force(surface_tension::AkinciTypeSurfaceTension, + particle_system::FluidSystem, + neighbor_system::BoundarySystem, particle, neighbor, + pos_diff, distance) + (; smoothing_length, smoothing_kernel) = particle_system + (; adhesion_coefficient, boundary_model) = neighbor_system + + # No adhesion with oneself + distance < sqrt(eps()) && return zero(pos_diff) + + # No reason to calculate the adhesion force if adhesion coefficient is near zero + abs(adhesion_coefficient) < eps() && return zero(pos_diff) + + m_b = hydrodynamic_mass(neighbor_system, neighbor) + + support_radius = compact_support(smoothing_kernel, smoothing_length) + return adhesion_force_akinci(surface_tension, support_radius, m_b, pos_diff, distance, + adhesion_coefficient) +end + +@inline function adhesion_force(surface_tension, particle_system, neighbor_system, particle, + neighbor, pos_diff, distance) + return zero(pos_diff) +end diff --git a/src/schemes/fluid/viscosity.jl b/src/schemes/fluid/viscosity.jl index 6a34692f0..554f41c14 100644 --- a/src/schemes/fluid/viscosity.jl +++ b/src/schemes/fluid/viscosity.jl @@ -190,6 +190,7 @@ end rho_a = particle_density(v_particle_system, particle_system, particle) rho_b = particle_density(v_neighbor_system, neighbor_system, neighbor) + # TODO This is not correct for two different fluids. It should be `nu_a` and `nu_b`. eta_a = nu * rho_a eta_b = nu * rho_b diff --git a/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl b/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl index a1710052d..716245be8 100644 --- a/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl +++ b/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl @@ -224,3 +224,35 @@ function update!(density_diffusion::DensityDiffusionAntuono, neighborhood_search return density_diffusion end + +@inline function density_diffusion!(dv, density_diffusion::DensityDiffusion, + v_particle_system, v_neighbor_system, + particle, neighbor, pos_diff, distance, + m_b, rho_a, rho_b, + particle_system::FluidSystem, + neighbor_system::FluidSystem, + grad_kernel) + # Density diffusion terms are all zero for distance zero + distance < sqrt(eps()) && return + + (; delta) = density_diffusion + (; smoothing_length, state_equation) = particle_system + (; sound_speed) = state_equation + + volume_b = m_b / rho_b + + psi = density_diffusion_psi(density_diffusion, rho_a, rho_b, pos_diff, distance, + particle_system, particle, neighbor) + density_diffusion_term = dot(psi, grad_kernel) * volume_b + + dv[end, particle] += delta * smoothing_length * sound_speed * density_diffusion_term +end + +# Density diffusion `nothing` or interaction other than fluid-fluid +@inline function density_diffusion!(dv, density_diffusion, + v_particle_system, v_neighbor_system, + particle, neighbor, pos_diff, distance, + m_b, rho_a, rho_b, + particle_system, neighbor_system, grad_kernel) + return dv +end diff --git a/src/schemes/fluid/weakly_compressible_sph/rhs.jl b/src/schemes/fluid/weakly_compressible_sph/rhs.jl index 0b913bc23..5f1c41912 100644 --- a/src/schemes/fluid/weakly_compressible_sph/rhs.jl +++ b/src/schemes/fluid/weakly_compressible_sph/rhs.jl @@ -6,9 +6,12 @@ function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, neighborhood_search, particle_system::WeaklyCompressibleSPHSystem, neighbor_system) - (; density_calculator, state_equation, correction) = particle_system + (; density_calculator, state_equation, correction, surface_tension) = particle_system (; sound_speed) = state_equation + surface_tension_a = surface_tension_model(particle_system) + surface_tension_b = surface_tension_model(neighbor_system) + system_coords = current_coordinates(u_particle_system, particle_system) neighbor_system_coords = current_coordinates(u_neighbor_system, neighbor_system) @@ -25,9 +28,9 @@ function interact!(dv, v_particle_system, u_particle_system, rho_mean = 0.5 * (rho_a + rho_b) # Determine correction values - viscosity_correction, pressure_correction = free_surface_correction(correction, - particle_system, - rho_mean) + viscosity_correction, pressure_correction, surface_tension_correction = free_surface_correction(correction, + particle_system, + rho_mean) grad_kernel = smoothing_kernel_grad(particle_system, pos_diff, distance, particle) @@ -55,8 +58,17 @@ function interact!(dv, v_particle_system, u_particle_system, particle, neighbor, pos_diff, distance, sound_speed, m_a, m_b, rho_mean) - for i in 1:ndims(particle_system) - dv[i, particle] += dv_pressure[i] + dv_viscosity_[i] + dv_surface_tension = surface_tension_correction * + surface_tension_force(surface_tension_a, surface_tension_b, + particle_system, neighbor_system, + particle, neighbor, pos_diff, distance) + + dv_adhesion = adhesion_force(surface_tension, particle_system, neighbor_system, + particle, neighbor, pos_diff, distance) + + @inbounds for i in 1:ndims(particle_system) + dv[i, particle] += dv_pressure[i] + dv_viscosity_[i] + dv_surface_tension[i] + + dv_adhesion[i] # Debug example # debug_array[i, particle] += dv_pressure[i] end @@ -104,38 +116,6 @@ end particle_system, neighbor_system, grad_kernel) end -@inline function density_diffusion!(dv, density_diffusion::DensityDiffusion, - v_particle_system, v_neighbor_system, - particle, neighbor, pos_diff, distance, - m_b, rho_a, rho_b, - particle_system::WeaklyCompressibleSPHSystem, - neighbor_system::WeaklyCompressibleSPHSystem, - grad_kernel) - # Density diffusion terms are all zero for distance zero - distance < sqrt(eps()) && return - - (; delta) = density_diffusion - (; smoothing_length, state_equation) = particle_system - (; sound_speed) = state_equation - - volume_b = m_b / rho_b - - psi = density_diffusion_psi(density_diffusion, rho_a, rho_b, pos_diff, distance, - particle_system, particle, neighbor) - density_diffusion_term = dot(psi, grad_kernel) * volume_b - - dv[end, particle] += delta * smoothing_length * sound_speed * density_diffusion_term -end - -# Density diffusion `nothing` or interaction other than fluid-fluid -@inline function density_diffusion!(dv, density_diffusion, - v_particle_system, v_neighbor_system, - particle, neighbor, pos_diff, distance, - m_b, rho_a, rho_b, - particle_system, neighbor_system, grad_kernel) - return dv -end - @inline function particle_neighbor_pressure(v_particle_system, v_neighbor_system, particle_system, neighbor_system, particle, neighbor) diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index 7df899868..63a32e6db 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -37,10 +37,12 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method. [`BoundaryModelDummyParticles`](@ref) and [`AdamiPressureExtrapolation`](@ref). The keyword argument `acceleration` should be used instead for gravity-like source terms. +- `surface_tension`: Surface tension model used for this SPH system. (default: no surface tension) + """ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, - V, DD, COR, PF, ST, C} <: FluidSystem{NDIMS} + V, DD, COR, PF, ST, SRFT, C} <: FluidSystem{NDIMS} initial_condition :: IC mass :: MA # Array{ELTYPE, 1} pressure :: P # Array{ELTYPE, 1} @@ -54,6 +56,7 @@ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, correction :: COR pressure_acceleration_formulation :: PF source_terms :: ST + surface_tension :: SRFT cache :: C end @@ -66,7 +69,8 @@ function WeaklyCompressibleSPHSystem(initial_condition, viscosity=nothing, density_diffusion=nothing, acceleration=ntuple(_ -> 0.0, ndims(smoothing_kernel)), - correction=nothing, source_terms=nothing) + correction=nothing, source_terms=nothing, + surface_tension=nothing) NDIMS = ndims(initial_condition) ELTYPE = eltype(initial_condition) n_particles = nparticles(initial_condition) @@ -98,14 +102,16 @@ function WeaklyCompressibleSPHSystem(initial_condition, cache = (; create_cache_wcsph(correction, initial_condition.density, NDIMS, n_particles)..., cache...) + cache = (; + create_cache_wcsph(surface_tension, ELTYPE, NDIMS, n_particles)..., + cache...) return WeaklyCompressibleSPHSystem(initial_condition, mass, pressure, density_calculator, state_equation, - smoothing_kernel, smoothing_length, - acceleration_, viscosity, - density_diffusion, correction, - pressure_acceleration, - source_terms, cache) + smoothing_kernel, smoothing_length, acceleration_, + viscosity, density_diffusion, correction, + pressure_acceleration, source_terms, surface_tension, + cache) end create_cache_wcsph(correction, density, NDIMS, nparticles) = (;) @@ -132,6 +138,11 @@ function create_cache_wcsph(::MixedKernelGradientCorrection, density, NDIMS, n_p return (; kernel_correction_coefficient=similar(density), dw_gamma, correction_matrix) end +function create_cache_wcsph(::SurfaceTensionAkinci, ELTYPE, NDIMS, nparticles) + surface_normal = Array{ELTYPE, 2}(undef, NDIMS, nparticles) + return (; surface_normal) +end + function Base.show(io::IO, system::WeaklyCompressibleSPHSystem) @nospecialize system # reduce precompilation time @@ -142,6 +153,7 @@ function Base.show(io::IO, system::WeaklyCompressibleSPHSystem) print(io, ", ", system.smoothing_kernel) print(io, ", ", system.viscosity) print(io, ", ", system.density_diffusion) + print(io, ", ", system.surface_tension) print(io, ", ", system.acceleration) print(io, ", ", system.source_terms) print(io, ") with ", nparticles(system), " particles") @@ -163,6 +175,7 @@ function Base.show(io::IO, ::MIME"text/plain", system::WeaklyCompressibleSPHSyst summary_line(io, "smoothing kernel", system.smoothing_kernel |> typeof |> nameof) summary_line(io, "viscosity", system.viscosity) summary_line(io, "density diffusion", system.density_diffusion) + summary_line(io, "surface tension", system.surface_tension) summary_line(io, "acceleration", system.acceleration) summary_line(io, "source terms", system.source_terms |> typeof |> nameof) summary_footer(io) @@ -201,7 +214,7 @@ function update_quantities!(system::WeaklyCompressibleSPHSystem, v, u, end function update_pressure!(system::WeaklyCompressibleSPHSystem, v, u, v_ode, u_ode, semi, t) - (; density_calculator, correction) = system + (; density_calculator, correction, surface_tension) = system compute_correction_values!(system, correction, u, v_ode, u_ode, semi) @@ -211,6 +224,7 @@ function update_pressure!(system::WeaklyCompressibleSPHSystem, v, u, v_ode, u_od kernel_correct_density!(system, v, u, v_ode, u_ode, semi, correction, density_calculator) compute_pressure!(system, v) + compute_surface_normal!(system, surface_tension, v, u, v_ode, u_ode, semi, t) return system end @@ -325,3 +339,39 @@ end @inline function correction_matrix(system::WeaklyCompressibleSPHSystem, particle) extract_smatrix(system.cache.correction_matrix, system, particle) end + +function compute_surface_normal!(system, surface_tension, v, u, v_ode, u_ode, semi, t) + return system +end + +function compute_surface_normal!(system, surface_tension::SurfaceTensionAkinci, v, u, v_ode, + u_ode, semi, t) + (; cache) = system + + # Reset surface normal + set_zero!(cache.surface_normal) + + @trixi_timeit timer() "compute surface normal" foreach_system(semi) do neighbor_system + u_neighbor_system = wrap_u(u_ode, neighbor_system, semi) + v_neighbor_system = wrap_v(v_ode, neighbor_system, semi) + nhs = get_neighborhood_search(system, semi) + + calc_normal_akinci!(system, neighbor_system, surface_tension, u, v_neighbor_system, + u_neighbor_system, nhs) + end + return system +end + +@inline function surface_normal(::SurfaceTensionAkinci, particle_system::FluidSystem, + particle) + (; cache) = particle_system + return extract_svector(cache.surface_normal, particle_system, particle) +end + +@inline function surface_tension_model(system::WeaklyCompressibleSPHSystem) + return system.surface_tension +end + +@inline function surface_tension_model(system) + return nothing +end diff --git a/src/setups/rectangular_tank.jl b/src/setups/rectangular_tank.jl index 3aa0af76d..70b179e3b 100644 --- a/src/setups/rectangular_tank.jl +++ b/src/setups/rectangular_tank.jl @@ -149,27 +149,35 @@ struct RectangularTank{NDIMS, NDIMSt2, ELTYPE <: Real} particle_spacing, n_particles_per_dim) - if state_equation !== nothing - # Use hydrostatic pressure gradient and calculate density from inverse state - # equation, so don't pass fluid density. - fluid = RectangularShape(particle_spacing, n_particles_per_dim, zeros(NDIMS); - velocity, pressure, acceleration, state_equation, - mass=fluid_mass) - else - fluid = RectangularShape(particle_spacing, n_particles_per_dim, zeros(NDIMS); - density=fluid_density, velocity, pressure, - acceleration, state_equation, mass=fluid_mass) - end - boundary = InitialCondition(coordinates=boundary_coordinates, velocity=boundary_velocities, mass=boundary_masses, density=boundary_densities, particle_spacing=boundary_spacing) # Move the tank corner in the negative coordinate directions to the desired position - fluid.coordinates .+= min_coordinates boundary.coordinates .+= min_coordinates + if norm(fluid_size) > eps() + if state_equation !== nothing + # Use hydrostatic pressure gradient and calculate density from inverse state + # equation, so don't pass fluid density. + fluid = RectangularShape(particle_spacing, n_particles_per_dim, + zeros(NDIMS); + velocity, pressure, acceleration, state_equation, + mass=fluid_mass) + else + fluid = RectangularShape(particle_spacing, n_particles_per_dim, + zeros(NDIMS); + density=fluid_density, velocity, pressure, + acceleration, state_equation, mass=fluid_mass) + end + # Move the tank corner in the negative coordinate directions to the desired position + fluid.coordinates .+= min_coordinates + else + # Fluid is empty + fluid = InitialCondition(coordinates=zeros(ELTYPE, NDIMS, 0), density=1.0) + end + return new{NDIMS, 2 * NDIMS, ELTYPE}(fluid, boundary, fluid_size_, tank_size_, faces, face_indices, particle_spacing, spacing_ratio, n_layers, diff --git a/test/examples/examples.jl b/test/examples/examples.jl index 6a41db723..f99de7d58 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -114,6 +114,60 @@ @test count_rhs_allocations(sol, semi) == 0 end + @trixi_testset "fluid/dam_break_2d_surface_tension.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", + "dam_break_2d_surface_tension.jl"), + tspan=(0.0, 0.1)) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + + @trixi_testset "fluid/sphere_surface_tension_2d.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", + "sphere_surface_tension_2d.jl")) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + + @trixi_testset "fluid/sphere_surface_tension_3d.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", + "sphere_surface_tension_3d.jl")) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + + @trixi_testset "fluid/falling_water_spheres_2d.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", + "falling_water_spheres_2d.jl")) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + + @trixi_testset "fluid/falling_water_spheres_3d.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", + "falling_water_spheres_3d.jl")) [ + r"┌ Info: The desired tank length in x-direction .*\n", + r"└ New tank length in x-direction.*\n", + r"┌ Info: The desired tank length in y-direction .*\n", + r"└ New tank length in y-direction.*\n", + r"┌ Info: The desired tank length in z-direction .*\n", + r"└ New tank length in z-direction.*\n", + ] + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + + @trixi_testset "fluid/sphere_surface_tension_wall_2d.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", + "sphere_surface_tension_wall_2d.jl")) + end + @trixi_testset "fluid/moving_wall_2d.jl" begin @test_nowarn_mod trixi_include(@__MODULE__, tspan=(0.0, 0.5), joinpath(examples_dir(), "fluid", diff --git a/test/schemes/fluid/fluid.jl b/test/schemes/fluid/fluid.jl index bf7c3720c..105fb0ee8 100644 --- a/test/schemes/fluid/fluid.jl +++ b/test/schemes/fluid/fluid.jl @@ -1,3 +1,4 @@ include("weakly_compressible_sph/weakly_compressible_sph.jl") include("rhs.jl") include("pressure_acceleration.jl") +include("surface_tension.jl") diff --git a/test/schemes/fluid/surface_tension.jl b/test/schemes/fluid/surface_tension.jl new file mode 100644 index 000000000..9758463f5 --- /dev/null +++ b/test/schemes/fluid/surface_tension.jl @@ -0,0 +1,92 @@ + +@testset verbose=true "Surface Tension" begin + @testset verbose=true "`cohesion_force_akinci`" begin + surface_tension = SurfaceTensionAkinci(surface_tension_coefficient=1.0) + support_radius = 1.0 + m_b = 1.0 + pos_diff = [1.0, 1.0] + + # These values can be extracted from the graphs in the paper by Akinci et al. or by manual calculation. + # Additional digits have been accepted from the actual calculation. + test_distance = 0.1 + val = TrixiParticles.cohesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance) * test_distance + @test isapprox(val[1], 0.1443038770421044, atol=6e-15) + @test isapprox(val[2], 0.1443038770421044, atol=6e-15) + + # Maximum repulsion force + test_distance = 0.01 + max = TrixiParticles.cohesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance) * test_distance + @test isapprox(max[1], 0.15913517632298307, atol=6e-15) + @test isapprox(max[2], 0.15913517632298307, atol=6e-15) + + # Near 0 + test_distance = 0.2725 + zero = TrixiParticles.cohesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance) * test_distance + @test isapprox(zero[1], 0.0004360543645195717, atol=6e-15) + @test isapprox(zero[2], 0.0004360543645195717, atol=6e-15) + + # Maximum attraction force + test_distance = 0.5 + maxa = TrixiParticles.cohesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance) * test_distance + @test isapprox(maxa[1], -0.15915494309189535, atol=6e-15) + @test isapprox(maxa[2], -0.15915494309189535, atol=6e-15) + + # Should be 0 + test_distance = 1.0 + zero = TrixiParticles.cohesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance) * test_distance + @test isapprox(zero[1], 0.0, atol=6e-15) + @test isapprox(zero[2], 0.0, atol=6e-15) + end + + @testset verbose=true "adhesion_force_akinci" begin + surface_tension = TrixiParticles.SurfaceTensionAkinci(surface_tension_coefficient=1.0) + support_radius = 1.0 + m_b = 1.0 + pos_diff = [1.0, 1.0] + + # These values can be extracted from the graphs in the paper by Akinci et al. or by manual calculation. + # Additional digits have been accepted from the actual calculation. + test_distance = 0.1 + zero = TrixiParticles.adhesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance, 1.0) * + test_distance + @test isapprox(zero[1], 0.0, atol=6e-15) + @test isapprox(zero[2], 0.0, atol=6e-15) + + test_distance = 0.5 + zero = TrixiParticles.adhesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance, 1.0) * + test_distance + @test isapprox(zero[1], 0.0, atol=6e-15) + @test isapprox(zero[2], 0.0, atol=6e-15) + + # Near 0 + test_distance = 0.51 + zero = TrixiParticles.adhesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance, 1.0) * + test_distance + @test isapprox(zero[1], -0.002619160170741761, atol=6e-15) + @test isapprox(zero[2], -0.002619160170741761, atol=6e-15) + + # Maximum adhesion force + test_distance = 0.75 + max = TrixiParticles.adhesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance, 1.0) * + test_distance + @test isapprox(max[1], -0.004949747468305833, atol=6e-15) + @test isapprox(max[2], -0.004949747468305833, atol=6e-15) + + # Should be 0 + test_distance = 1.0 + zero = TrixiParticles.adhesion_force_akinci(surface_tension, support_radius, m_b, + pos_diff, test_distance, 1.0) * + test_distance + @test isapprox(zero[1], 0.0, atol=6e-15) + @test isapprox(zero[2], 0.0, atol=6e-15) + end +end diff --git a/test/systems/boundary_system.jl b/test/systems/boundary_system.jl index dad30e71c..2ffa40af4 100644 --- a/test/systems/boundary_system.jl +++ b/test/systems/boundary_system.jl @@ -111,7 +111,7 @@ system = BoundarySPHSystem(initial_condition, model) - show_compact = "BoundarySPHSystem{2}((hydrodynamic_mass = 3,), nothing) with 2 particles" + show_compact = "BoundarySPHSystem{2}((hydrodynamic_mass = 3,), nothing, 0.0) with 2 particles" @test repr(system) == show_compact show_box = """ @@ -121,6 +121,7 @@ │ #particles: ………………………………………………… 2 │ │ boundary model: ……………………………………… (hydrodynamic_mass = 3,) │ │ movement function: ……………………………… nothing │ + │ adhesion coefficient: ……………………… 0.0 │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box end diff --git a/test/systems/wcsph_system.jl b/test/systems/wcsph_system.jl index 0749261bd..68f69aea8 100644 --- a/test/systems/wcsph_system.jl +++ b/test/systems/wcsph_system.jl @@ -193,7 +193,7 @@ smoothing_length, density_diffusion=density_diffusion) - show_compact = "WeaklyCompressibleSPHSystem{2}(SummationDensity(), nothing, Val{:state_equation}(), Val{:smoothing_kernel}(), nothing, Val{:density_diffusion}(), [0.0, 0.0], nothing) with 2 particles" + show_compact = "WeaklyCompressibleSPHSystem{2}(SummationDensity(), nothing, Val{:state_equation}(), Val{:smoothing_kernel}(), nothing, Val{:density_diffusion}(), nothing, [0.0, 0.0], nothing) with 2 particles" @test repr(system) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ @@ -206,6 +206,7 @@ │ smoothing kernel: ………………………………… Val │ │ viscosity: …………………………………………………… nothing │ │ density diffusion: ……………………………… Val{:density_diffusion}() │ + │ surface tension: …………………………………… nothing │ │ acceleration: …………………………………………… [0.0, 0.0] │ │ source terms: …………………………………………… Nothing │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" From 40e540b09f3d1c4d46f14b39268d1749b97f8748 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 28 May 2024 22:23:52 +0200 Subject: [PATCH 30/49] Fix n-body benchmark (#525) * Fix n-body benchmark * suppress warning * fix formatting * for real --------- Co-authored-by: Sven Berger --- examples/n_body/n_body_benchmark_trixi.jl | 18 ++++++++++-------- test/examples/examples.jl | 4 +++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/n_body/n_body_benchmark_trixi.jl b/examples/n_body/n_body_benchmark_trixi.jl index 17868d201..04c1f1af6 100644 --- a/examples/n_body/n_body_benchmark_trixi.jl +++ b/examples/n_body/n_body_benchmark_trixi.jl @@ -9,8 +9,9 @@ using Polyester include("n_body_system.jl") -# Redefine interact in a more optimized way. -function TrixiParticles.interact!(du, u_particle_system, u_neighbor_system, +# Redefine interact in a more optimized way +function TrixiParticles.interact!(dv, v_particle_system, u_particle_system, + v_neighbor_system, u_neighbor_system, neighborhood_search, particle_system::NBodySystem, neighbor_system::NBodySystem) @@ -34,16 +35,15 @@ function TrixiParticles.interact!(du, u_particle_system, u_neighbor_system, tmp1 = mass[neighbor] * tmp tmp2 = mass[particle] * tmp - for i in 1:ndims(particle_system) - j = i + ndims(particle_system) - # This is slightly faster than du[...] += tmp1 * pos_diff[i] - du[j, particle] = muladd(tmp1, pos_diff[i], du[j, particle]) - du[j, neighbor] = muladd(tmp2, -pos_diff[i], du[j, neighbor]) + @inbounds for i in 1:ndims(particle_system) + # This is slightly faster than dv[...] += tmp1 * pos_diff[i] + dv[i, particle] = muladd(tmp1, pos_diff[i], dv[i, particle]) + dv[i, neighbor] = muladd(tmp2, -pos_diff[i], dv[i, neighbor]) end end end - return du + return dv end # ========================================================================================== @@ -98,6 +98,8 @@ function symplectic_euler!(velocity, coordinates, semi) u[i] += 0.01 * du[i] end end + + return v, u end # One RHS evaluation is so fast that timers make it multiple times slower. diff --git a/test/examples/examples.jl b/test/examples/examples.jl index f99de7d58..0f0032607 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -244,7 +244,9 @@ @trixi_testset "n_body/n_body_benchmark_trixi.jl" begin @test_nowarn_mod trixi_include(@__MODULE__, joinpath(examples_dir(), "n_body", - "n_body_benchmark_trixi.jl")) + "n_body_benchmark_trixi.jl")) [ + r"WARNING: Method definition interact!.*\n", + ] end @trixi_testset "n_body/n_body_benchmark_reference.jl" begin From 17ad1e57a2685616845735d2913b668840f7608f Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Wed, 29 May 2024 09:32:47 +0200 Subject: [PATCH 31/49] Push new version number (#536) Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 20b40306c..47edef062 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TrixiParticles" uuid = "66699cd8-9c01-4e9d-a059-b96c86d16b3a" authors = ["erik.faulhaber <44124897+efaulhaber@users.noreply.github.com>"] -version = "0.1.2-dev" +version = "0.1.2" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" From d6f479ad1edee2b012bec86049922d46012faef6 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Wed, 29 May 2024 09:33:41 +0200 Subject: [PATCH 32/49] Mark 0.1.3-dev (#537) Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 47edef062..ea1bc9aa5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TrixiParticles" uuid = "66699cd8-9c01-4e9d-a059-b96c86d16b3a" authors = ["erik.faulhaber <44124897+efaulhaber@users.noreply.github.com>"] -version = "0.1.2" +version = "0.1.3-dev" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" From 98e58f9b725f55e74219e5bb78de7e12fdac2613 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 29 May 2024 11:58:01 +0200 Subject: [PATCH 33/49] Add type `GPUSystem` to determine if a system is stored on the CPU or GPU (#532) * Add type `GPUSystem` to determine if a system is stored on the CPU or GPU * Fix systems and tests * Reformat * Fix tests * Fix n-body system * Reformat --- examples/n_body/n_body_system.jl | 2 +- src/general/general.jl | 13 +++++++++---- src/general/semidiscretization.jl | 2 ++ src/schemes/boundary/system.jl | 19 ++++++++++--------- .../fluid/entropically_damped_sph/system.jl | 2 +- .../fluid/weakly_compressible_sph/system.jl | 2 +- .../solid/discrete_element_method/system.jl | 6 +++--- .../solid/total_lagrangian_sph/system.jl | 2 +- test/general/semidiscretization.jl | 6 +++--- 9 files changed, 31 insertions(+), 23 deletions(-) diff --git a/examples/n_body/n_body_system.jl b/examples/n_body/n_body_system.jl index 96b6cdebf..caf40a62e 100644 --- a/examples/n_body/n_body_system.jl +++ b/examples/n_body/n_body_system.jl @@ -1,7 +1,7 @@ using TrixiParticles using LinearAlgebra -struct NBodySystem{NDIMS, ELTYPE <: Real} <: TrixiParticles.System{NDIMS} +struct NBodySystem{NDIMS, ELTYPE <: Real} <: TrixiParticles.System{NDIMS, Nothing} initial_condition :: InitialCondition{ELTYPE} mass :: Array{ELTYPE, 1} # [particle] G :: ELTYPE diff --git a/src/general/general.jl b/src/general/general.jl index 4c9531683..dfe52e06a 100644 --- a/src/general/general.jl +++ b/src/general/general.jl @@ -1,12 +1,17 @@ -abstract type System{NDIMS} end +# Abstract supertype for all system types. We additionally store the type of the system's +# initial condition, which is `Nothing` when using KernelAbstractions.jl. +abstract type System{NDIMS, IC} end -abstract type FluidSystem{NDIMS} <: System{NDIMS} end +# When using KernelAbstractions.jl, the initial condition has been replaced by `nothing` +GPUSystem = System{NDIMS, Nothing} where {NDIMS} + +abstract type FluidSystem{NDIMS, IC} <: System{NDIMS, IC} end timer_name(::FluidSystem) = "fluid" -abstract type SolidSystem{NDIMS} <: System{NDIMS} end +abstract type SolidSystem{NDIMS, IC} <: System{NDIMS, IC} end timer_name(::SolidSystem) = "solid" -abstract type BoundarySystem{NDIMS} <: System{NDIMS} end +abstract type BoundarySystem{NDIMS, IC} <: System{NDIMS, IC} end timer_name(::BoundarySystem) = "boundary" @inline function set_zero!(du) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index a3ce1e056..6a1d703b2 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -60,6 +60,8 @@ struct Semidiscretization{S, RU, RV, NS} end end +GPUSemidiscretization = Semidiscretization{<:NTuple{<:Any, GPUSystem}} + function Semidiscretization(systems...; neighborhood_search=GridNeighborhoodSearch, periodic_box_min_corner=nothing, periodic_box_max_corner=nothing, threaded_nhs_update=true) diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl index a1bec02e9..cade5fbe9 100644 --- a/src/schemes/boundary/system.jl +++ b/src/schemes/boundary/system.jl @@ -13,8 +13,7 @@ The interaction between fluid and boundary particles is specified by the boundar - `adhesion_coefficient`: Coefficient specifying the adhesion of a fluid to the surface. Note: currently it is assumed that all fluids have the same adhesion coefficient. """ -struct BoundarySPHSystem{BM, NDIMS, ELTYPE <: Real, IC, CO, M, IM, CA} <: - BoundarySystem{NDIMS} +struct BoundarySPHSystem{BM, NDIMS, ELTYPE, IC, CO, M, IM, CA} <: BoundarySystem{NDIMS, IC} initial_condition :: IC coordinates :: CO # Array{ELTYPE, 2} boundary_model :: BM @@ -68,10 +67,12 @@ The interaction between fluid and boundary particles is specified by the boundar This is an experimental feature and may change in a future releases. """ -struct BoundaryDEMSystem{NDIMS, ELTYPE <: Real, ARRAY1D, ARRAY2D} <: BoundarySystem{NDIMS} - coordinates :: ARRAY2D # [dimension, particle] - radius :: ARRAY1D # [particle] - normal_stiffness :: ELTYPE +struct BoundaryDEMSystem{NDIMS, ELTYPE <: Real, IC, + ARRAY1D, ARRAY2D} <: BoundarySystem{NDIMS, IC} + initial_condition :: IC + coordinates :: ARRAY2D # [dimension, particle] + radius :: ARRAY1D # [particle] + normal_stiffness :: ELTYPE function BoundaryDEMSystem(initial_condition, normal_stiffness) coordinates = initial_condition.coordinates @@ -79,9 +80,9 @@ struct BoundaryDEMSystem{NDIMS, ELTYPE <: Real, ARRAY1D, ARRAY2D} <: BoundarySys ones(length(initial_condition.mass)) NDIMS = size(coordinates, 1) - return new{NDIMS, eltype(coordinates), typeof(radius), typeof(coordinates)}(coordinates, - radius, - normal_stiffness) + return new{NDIMS, eltype(coordinates), typeof(initial_condition), + typeof(radius), typeof(coordinates)}(initial_condition, coordinates, + radius, normal_stiffness) end end diff --git a/src/schemes/fluid/entropically_damped_sph/system.jl b/src/schemes/fluid/entropically_damped_sph/system.jl index 15c765040..f59bca7d3 100644 --- a/src/schemes/fluid/entropically_damped_sph/system.jl +++ b/src/schemes/fluid/entropically_damped_sph/system.jl @@ -40,7 +40,7 @@ See [Entropically Damped Artificial Compressibility for SPH](@ref edac) for more gravity-like source terms. """ struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, IC, M, DC, K, V, - PF, ST, C} <: FluidSystem{NDIMS} + PF, ST, C} <: FluidSystem{NDIMS, IC} initial_condition :: IC mass :: M # Vector{ELTYPE}: [particle] density_calculator :: DC diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index 63a32e6db..cb9a1da87 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -42,7 +42,7 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method. """ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, - V, DD, COR, PF, ST, SRFT, C} <: FluidSystem{NDIMS} + V, DD, COR, PF, ST, SRFT, C} <: FluidSystem{NDIMS, IC} initial_condition :: IC mass :: MA # Array{ELTYPE, 1} pressure :: P # Array{ELTYPE, 1} diff --git a/src/schemes/solid/discrete_element_method/system.jl b/src/schemes/solid/discrete_element_method/system.jl index a7cd2868f..5aadfcfcc 100644 --- a/src/schemes/solid/discrete_element_method/system.jl +++ b/src/schemes/solid/discrete_element_method/system.jl @@ -26,8 +26,8 @@ specified material properties and contact mechanics. !!! warning "Experimental Implementation" This is an experimental feature and may change in a future releases. """ -struct DEMSystem{NDIMS, ELTYPE <: Real, ARRAY1D, ST} <: SolidSystem{NDIMS} - initial_condition :: InitialCondition{ELTYPE} +struct DEMSystem{NDIMS, ELTYPE <: Real, IC, ARRAY1D, ST} <: SolidSystem{NDIMS, IC} + initial_condition :: IC mass :: ARRAY1D # [particle] radius :: ARRAY1D # [particle] elastic_modulus :: ELTYPE @@ -53,7 +53,7 @@ struct DEMSystem{NDIMS, ELTYPE <: Real, ARRAY1D, ST} <: SolidSystem{NDIMS} throw(ArgumentError("`acceleration` must be of length $NDIMS for a $(NDIMS)D problem")) end - return new{NDIMS, ELTYPE, typeof(mass), + return new{NDIMS, ELTYPE, typeof(initial_condition), typeof(mass), typeof(source_terms)}(initial_condition, mass, radius, elastic_modulus, poissons_ratio, normal_stiffness, damping_coefficient, acceleration_, source_terms) diff --git a/src/schemes/solid/total_lagrangian_sph/system.jl b/src/schemes/solid/total_lagrangian_sph/system.jl index fe949b9f1..5b3021b1c 100644 --- a/src/schemes/solid/total_lagrangian_sph/system.jl +++ b/src/schemes/solid/total_lagrangian_sph/system.jl @@ -49,7 +49,7 @@ See [Total Lagrangian SPH](@ref tlsph) for more details on the method. where `beam` and `fixed_particles` are of type `InitialCondition`. """ struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D, ARRAY3D, - K, PF, ST} <: SolidSystem{NDIMS} + K, PF, ST} <: SolidSystem{NDIMS, IC} initial_condition :: IC initial_coordinates :: ARRAY2D # Array{ELTYPE, 2}: [dimension, particle] current_coordinates :: ARRAY2D # Array{ELTYPE, 2}: [dimension, particle] diff --git a/test/general/semidiscretization.jl b/test/general/semidiscretization.jl index 8a74de0ab..633a0e240 100644 --- a/test/general/semidiscretization.jl +++ b/test/general/semidiscretization.jl @@ -1,8 +1,8 @@ # Use `@trixi_testset` to isolate the mock functions in a separate namespace @trixi_testset "Semidiscretization" begin # Mock systems - struct System1 <: TrixiParticles.System{3} end - struct System2 <: TrixiParticles.System{3} end + struct System1 <: TrixiParticles.System{3, Nothing} end + struct System2 <: TrixiParticles.System{3, Nothing} end system1 = System1() system2 = System2() @@ -39,7 +39,7 @@ struct BoundaryModelMock end # Mock fluid system - struct FluidSystemMock <: TrixiParticles.FluidSystem{2} end + struct FluidSystemMock <: TrixiParticles.FluidSystem{2, Nothing} end kernel = Val(:smoothing_kernel) Base.ndims(::Val{:smoothing_kernel}) = 2 From 1ba317bf8d2d0c6218128c911cf77176e9e0c399 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 29 May 2024 13:09:32 +0200 Subject: [PATCH 34/49] Clean up callback conditions (#538) --- src/callbacks/callbacks.jl | 3 +-- src/callbacks/density_reinit.jl | 8 +------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/callbacks/callbacks.jl b/src/callbacks/callbacks.jl index f86f9a586..1aefdff72 100644 --- a/src/callbacks/callbacks.jl +++ b/src/callbacks/callbacks.jl @@ -21,8 +21,7 @@ end # (total #steps) (#accepted steps) # We need to check the number of accepted steps since callbacks are not # activated after a rejected step. - return interval > 0 && (((integrator.stats.naccept % interval == 0) && - !(integrator.stats.naccept == 0 && integrator.iter > 0)) || + return interval > 0 && ((integrator.stats.naccept % interval == 0) || (save_final_solution && isfinished(integrator))) end diff --git a/src/callbacks/density_reinit.jl b/src/callbacks/density_reinit.jl index 5e9f98027..5c8a7b5e1 100644 --- a/src/callbacks/density_reinit.jl +++ b/src/callbacks/density_reinit.jl @@ -89,13 +89,7 @@ end function (reinit_callback::DensityReinitializationCallback{Int})(u, t, integrator) (; interval) = reinit_callback - # With error-based step size control, some steps can be rejected. Thus, - # `integrator.iter >= integrator.stats.naccept` - # (total #steps) (#accepted steps) - # We need to check the number of accepted steps since callbacks are not - # activated after a rejected step. - return interval > 0 && ((integrator.stats.naccept % interval == 0) && - !(integrator.stats.naccept == 0 && integrator.iter > 0)) + return condition_integrator_interval(integrator, interval, save_final_solution=false) end # condition with dt From 17d95b6af731ec68593c9cb2add01df22433e379 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:02:59 +0200 Subject: [PATCH 35/49] Bump julia-actions/cache from 1 to 2 (#541) Bumps [julia-actions/cache](https://github.com/julia-actions/cache) from 1 to 2. - [Release notes](https://github.com/julia-actions/cache/releases) - [Changelog](https://github.com/julia-actions/cache/blob/main/devdocs/making_a_new_release.md) - [Commits](https://github.com/julia-actions/cache/compare/v1...v2) --- updated-dependencies: - dependency-name: julia-actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/Documenter.yml | 2 +- .github/workflows/FormatCheck.yml | 2 +- .github/workflows/ci.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/Documenter.yml b/.github/workflows/Documenter.yml index 4cc31a611..a748be851 100644 --- a/.github/workflows/Documenter.yml +++ b/.github/workflows/Documenter.yml @@ -34,7 +34,7 @@ jobs: with: version: '1' show-versioninfo: true - - uses: julia-actions/cache@v1 + - uses: julia-actions/cache@v2 - name: Build package uses: julia-actions/julia-buildpkg@v1 - name: Install dependencies diff --git a/.github/workflows/FormatCheck.yml b/.github/workflows/FormatCheck.yml index 5000913d4..bdd275a5c 100644 --- a/.github/workflows/FormatCheck.yml +++ b/.github/workflows/FormatCheck.yml @@ -19,7 +19,7 @@ jobs: with: version: '1' - run: julia -e 'using InteractiveUtils; versioninfo(verbose=true)' - - uses: julia-actions/cache@v1 + - uses: julia-actions/cache@v2 - name: Install JuliaFormatter and format # This will use the latest version by default but you can set the version like so: # diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5832a20f..b115c6937 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: uses: julia-actions/setup-julia@v2 with: version: ${{ matrix.version }} - - uses: julia-actions/cache@v1 + - uses: julia-actions/cache@v2 - name: Build package uses: julia-actions/julia-buildpkg@v1 - name: Run unit tests From ad902882bb9a4365670ee8535abff9ea8de2d8d3 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Thu, 13 Jun 2024 16:07:49 +0200 Subject: [PATCH 36/49] Update Michael's affiliation to U of Augsburg (#545) --- .zenodo.json | 2 +- CODE_OF_CONDUCT.md | 4 ++-- README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index ecb62381e..1b2cefaeb 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -20,7 +20,7 @@ "orcid": "0000-0001-6083-7038" }, { - "affiliation": "Applied and Computational Mathematics, RWTH Aachen University, Germany", + "affiliation": "High-Performance Scientific Computing, University of Augsburg, Germany", "name": "Schlottke-Lakemper, Michael", "orcid": "0000-0002-3195-2536" }, diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5c74bcf79..e9f99ce1b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to -[Michael Schlottke-Lakemper](mailto:m.schlottke-lakemper@acom.rwth-aachen.de), +[Michael Schlottke-Lakemper](mailto:michael.schlottke-lakemper@uni-a.de), [Sven Berger](mailto:sven.berger@hereon.de), or any other of the principal developers responsible for enforcement listed in [AUTHORS.md](AUTHORS.md). @@ -128,4 +128,4 @@ enforcement ladder](https://github.com/mozilla/diversity). For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. \ No newline at end of file +https://www.contributor-covenant.org/translations. diff --git a/README.md b/README.md index 9f5defe1a..b40e68fbc 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ with the help of TrixiParticles.jl, please cite it as ## Authors Erik Faulhaber (University of Cologne) and Niklas Neher (HLRS) implemented the foundations for TrixiParticles.jl and are principal developers along with Sven Berger (hereon). -The project was started by Michael Schlottke-Lakemper (RWTH Aachen University/HLRS) +The project was started by Michael Schlottke-Lakemper (University of Augsburg) and Gregor Gassner (University of Cologne), who provide scientific direction and technical advice. The full list of contributors can be found in [AUTHORS.md](AUTHORS.md). From c5dfd077efa620685032788467cf9c759b6fd2a8 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 17 Jun 2024 09:43:23 +0200 Subject: [PATCH 37/49] Precompute git hash to avoid overhead (#542) * Precompute git hash * Store git hash in callbacks * Fix --------- Co-authored-by: Niklas Neher <73897120+LasNikas@users.noreply.github.com> --- src/callbacks/post_process.jl | 15 ++++++++++----- src/callbacks/solution_saving.jl | 16 ++++++++-------- src/general/file_system.jl | 31 ------------------------------- src/general/general.jl | 4 +++- src/util.jl | 18 ++++++++++++++++++ src/visualization/write2vtk.jl | 27 ++++++++++++++++++--------- 6 files changed, 57 insertions(+), 54 deletions(-) delete mode 100644 src/general/file_system.jl diff --git a/src/callbacks/post_process.jl b/src/callbacks/post_process.jl index e2e98a2a0..d68168258 100644 --- a/src/callbacks/post_process.jl +++ b/src/callbacks/post_process.jl @@ -73,6 +73,7 @@ struct PostprocessCallback{I, F} append_timestamp :: Bool write_csv :: Bool write_json :: Bool + git_hash :: Ref{String} end function PostprocessCallback(; interval::Integer=0, dt=0.0, exclude_boundary=true, @@ -94,7 +95,8 @@ function PostprocessCallback(; interval::Integer=0, dt=0.0, exclude_boundary=tru post_callback = PostprocessCallback(interval, write_file_interval, Dict{String, Vector{Any}}(), Float64[], exclude_boundary, funcs, filename, output_directory, - append_timestamp, write_csv, write_json) + append_timestamp, write_csv, write_json, + Ref("UnknownVersion")) if dt > 0 # Add a `tstop` every `dt`, and save the final solution return PeriodicCallback(post_callback, dt, @@ -209,9 +211,12 @@ function initialize_postprocess_callback!(cb, u, t, integrator) end function initialize_postprocess_callback!(cb::PostprocessCallback, u, t, integrator) + cb.git_hash[] = compute_git_hash() + # Apply the callback cb(integrator) - return nothing + + return cb end # `condition` with interval @@ -281,7 +286,7 @@ function write_postprocess_callback(pp::PostprocessCallback) mkpath(pp.output_directory) data = Dict{String, Any}() - write_meta_data!(data) + write_meta_data!(data, pp.git_hash[]) prepare_series_data!(data, pp) time_stamp = "" @@ -331,9 +336,9 @@ function create_series_dict(values, times, system_name="") "time" => times) end -function write_meta_data!(data) +function write_meta_data!(data, git_hash) meta_data = Dict("solver_name" => "TrixiParticles.jl", - "solver_version" => get_git_hash(), + "solver_version" => git_hash, "julia_version" => string(VERSION)) data["meta"] = meta_data diff --git a/src/callbacks/solution_saving.jl b/src/callbacks/solution_saving.jl index 5163199d6..04552d57e 100644 --- a/src/callbacks/solution_saving.jl +++ b/src/callbacks/solution_saving.jl @@ -77,6 +77,7 @@ mutable struct SolutionSavingCallback{I, CQ} max_coordinates :: Float64 custom_quantities :: CQ latest_saved_iter :: Int + git_hash :: Ref{String} end function SolutionSavingCallback(; interval::Integer=0, dt=0.0, @@ -102,7 +103,7 @@ function SolutionSavingCallback(; interval::Integer=0, dt=0.0, save_initial_solution, save_final_solution, write_meta_data, verbose, output_directory, prefix, max_coordinates, custom_quantities, - -1) + -1, Ref("UnknownVersion")) if length(save_times) > 0 # See the large comment below for an explanation why we use `finalize` here @@ -131,6 +132,7 @@ end function initialize_save_cb!(solution_callback::SolutionSavingCallback, u, t, integrator) # Reset `latest_saved_iter` solution_callback.latest_saved_iter = -1 + solution_callback.git_hash[] = compute_git_hash() # Save initial solution if solution_callback.save_initial_solution @@ -156,7 +158,7 @@ end # `affect!` function (solution_callback::SolutionSavingCallback)(integrator) - (; interval, output_directory, custom_quantities, write_meta_data, + (; interval, output_directory, custom_quantities, write_meta_data, git_hash, verbose, prefix, latest_saved_iter, max_coordinates) = solution_callback vu_ode = integrator.u @@ -178,12 +180,10 @@ function (solution_callback::SolutionSavingCallback)(integrator) println("Writing solution to $output_directory at t = $(integrator.t)") end - @trixi_timeit timer() "save solution" trixi2vtk(vu_ode, semi, integrator.t; iter=iter, - output_directory=output_directory, - prefix=prefix, - write_meta_data=write_meta_data, - max_coordinates=max_coordinates, - custom_quantities...) + @trixi_timeit timer() "save solution" trixi2vtk(vu_ode, semi, integrator.t; + iter, output_directory, prefix, + write_meta_data, git_hash=git_hash[], + max_coordinates, custom_quantities...) # Tell OrdinaryDiffEq that `u` has not been modified u_modified!(integrator, false) diff --git a/src/general/file_system.jl b/src/general/file_system.jl deleted file mode 100644 index 0cc4cc56d..000000000 --- a/src/general/file_system.jl +++ /dev/null @@ -1,31 +0,0 @@ -vtkname(system::FluidSystem) = "fluid" -vtkname(system::SolidSystem) = "solid" -vtkname(system::BoundarySystem) = "boundary" - -function system_names(systems) - # Add `_i` to each system name, where `i` is the index of the corresponding - # system type. - # `["fluid", "boundary", "boundary"]` becomes `["fluid_1", "boundary_1", "boundary_2"]`. - cnames = systems .|> vtkname - filenames = [string(cnames[i], "_", count(==(cnames[i]), cnames[1:i])) - for i in eachindex(cnames)] - return filenames -end - -function get_git_hash() - pkg_directory = pkgdir(@__MODULE__) - git_directory = joinpath(pkg_directory, ".git") - - # Check if the .git directory exists - if !isdir(git_directory) - return "UnknownVersion" - end - - try - git_cmd = Cmd(`git describe --tags --always --first-parent --dirty`, - dir=pkg_directory) - return string(readchomp(git_cmd)) - catch e - return "UnknownVersion" - end -end diff --git a/src/general/general.jl b/src/general/general.jl index dfe52e06a..4a60cb52f 100644 --- a/src/general/general.jl +++ b/src/general/general.jl @@ -7,12 +7,15 @@ GPUSystem = System{NDIMS, Nothing} where {NDIMS} abstract type FluidSystem{NDIMS, IC} <: System{NDIMS, IC} end timer_name(::FluidSystem) = "fluid" +vtkname(system::FluidSystem) = "fluid" abstract type SolidSystem{NDIMS, IC} <: System{NDIMS, IC} end timer_name(::SolidSystem) = "solid" +vtkname(system::SolidSystem) = "solid" abstract type BoundarySystem{NDIMS, IC} <: System{NDIMS, IC} end timer_name(::BoundarySystem) = "boundary" +vtkname(system::BoundarySystem) = "boundary" @inline function set_zero!(du) du .= zero(eltype(du)) @@ -29,6 +32,5 @@ include("smoothing_kernels.jl") include("initial_condition.jl") include("system.jl") include("interpolation.jl") -include("file_system.jl") include("custom_quantities.jl") include("neighborhood_search.jl") diff --git a/src/util.jl b/src/util.jl index 2dc86a267..b3cbfb764 100644 --- a/src/util.jl +++ b/src/util.jl @@ -206,3 +206,21 @@ end function type2string(type) return string(nameof(typeof(type))) end + +function compute_git_hash() + pkg_directory = pkgdir(@__MODULE__) + git_directory = joinpath(pkg_directory, ".git") + + # Check if the .git directory exists + if !isdir(git_directory) + return "UnknownVersion" + end + + try + git_cmd = Cmd(`git describe --tags --always --first-parent --dirty`, + dir=pkg_directory) + return string(readchomp(git_cmd)) + catch e + return "UnknownVersion" + end +end diff --git a/src/visualization/write2vtk.jl b/src/visualization/write2vtk.jl index bd1658e32..a05e6727e 100644 --- a/src/visualization/write2vtk.jl +++ b/src/visualization/write2vtk.jl @@ -1,3 +1,13 @@ +function system_names(systems) + # Add `_i` to each system name, where `i` is the index of the corresponding + # system type. + # `["fluid", "boundary", "boundary"]` becomes `["fluid_1", "boundary_1", "boundary_2"]`. + cnames = vtkname.(systems) + filenames = [string(cnames[i], "_", count(==(cnames[i]), cnames[1:i])) + for i in eachindex(cnames)] + return filenames +end + """ trixi2vtk(vu_ode, semi, t; iter=nothing, output_directory="out", prefix="", write_meta_data=true, max_coordinates=Inf, custom_quantities...) @@ -39,13 +49,14 @@ trixi2vtk(sol.u[end], semi, 0.0, iter=1, my_custom_quantity=kinetic_energy) ``` """ function trixi2vtk(vu_ode, semi, t; iter=nothing, output_directory="out", prefix="", - write_meta_data=true, max_coordinates=Inf, custom_quantities...) + write_meta_data=true, git_hash=compute_git_hash(), + max_coordinates=Inf, custom_quantities...) (; systems) = semi v_ode, u_ode = vu_ode.x # Update quantities that are stored in the systems. These quantities (e.g. pressure) # still have the values from the last stage of the previous step if not updated here. - update_systems_and_nhs(v_ode, u_ode, semi, t) + @trixi_timeit timer() "update systems" update_systems_and_nhs(v_ode, u_ode, semi, t) filenames = system_names(systems) @@ -57,17 +68,15 @@ function trixi2vtk(vu_ode, semi, t; iter=nothing, output_directory="out", prefix periodic_box = get_neighborhood_search(system, semi).periodic_box trixi2vtk(v, u, t, system, periodic_box; - output_directory=output_directory, - system_name=filenames[system_index], iter=iter, prefix=prefix, - write_meta_data=write_meta_data, max_coordinates=max_coordinates, - custom_quantities...) + system_name=filenames[system_index], output_directory, iter, prefix, + write_meta_data, git_hash, max_coordinates, custom_quantities...) end end # Convert data for a single TrixiParticle system to VTK format function trixi2vtk(v, u, t, system, periodic_box; output_directory="out", prefix="", iter=nothing, system_name=vtkname(system), write_meta_data=true, - max_coordinates=Inf, + max_coordinates=Inf, git_hash=compute_git_hash(), custom_quantities...) mkpath(output_directory) @@ -98,7 +107,7 @@ function trixi2vtk(v, u, t, system, periodic_box; output_directory="out", prefix end end - vtk_grid(file, points, cells) do vtk + @trixi_timeit timer() "write to vtk" vtk_grid(file, points, cells) do vtk # dispatches based on the different system types e.g. FluidSystem, TotalLagrangianSPHSystem write2vtk!(vtk, v, u, t, system, write_meta_data=write_meta_data) @@ -107,7 +116,7 @@ function trixi2vtk(v, u, t, system, periodic_box; output_directory="out", prefix vtk["time"] = t if write_meta_data - vtk["solver_version"] = get_git_hash() + vtk["solver_version"] = git_hash vtk["julia_version"] = string(VERSION) end From 5deebdd94bd01e785d3e67d742689f5bd606d061 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 17 Jun 2024 10:17:01 +0200 Subject: [PATCH 38/49] Move timers and `@trixi_timeit` to TrixiBase.jl (#543) * Move timers and `@trixi_timeit` to TrixiBase.jl * Require latest version of TrixiBase.jl * Fix tests --------- Co-authored-by: Niklas Neher <73897120+LasNikas@users.noreply.github.com> --- Project.toml | 2 +- examples/n_body/n_body_benchmark_trixi.jl | 4 +-- examples/n_body/n_body_solar_system.jl | 4 +-- src/TrixiParticles.jl | 3 +- src/util.jl | 42 ----------------------- test/count_allocations.jl | 4 +-- 6 files changed, 9 insertions(+), 50 deletions(-) diff --git a/Project.toml b/Project.toml index ea1bc9aa5..bff9874c1 100644 --- a/Project.toml +++ b/Project.toml @@ -42,7 +42,7 @@ SciMLBase = "1, 2" StaticArrays = "1" StrideArrays = "0.1" TimerOutputs = "0.5" -TrixiBase = "0.1" +TrixiBase = "0.1.3" PointNeighbors = "0.2" WriteVTK = "1" julia = "1.9" diff --git a/examples/n_body/n_body_benchmark_trixi.jl b/examples/n_body/n_body_benchmark_trixi.jl index 04c1f1af6..b47f689e7 100644 --- a/examples/n_body/n_body_benchmark_trixi.jl +++ b/examples/n_body/n_body_benchmark_trixi.jl @@ -108,7 +108,7 @@ end filename = tempname() open(filename, "w") do f redirect_stderr(f) do - TrixiParticles.TimerOutputs.disable_debug_timings(TrixiParticles) + TrixiParticles.disable_debug_timings() end end @@ -124,6 +124,6 @@ end # Enable timers again open(filename, "w") do f redirect_stderr(f) do - TrixiParticles.TimerOutputs.enable_debug_timings(TrixiParticles) + TrixiParticles.enable_debug_timings() end end diff --git a/examples/n_body/n_body_solar_system.jl b/examples/n_body/n_body_solar_system.jl index 6fa860089..b4da85aeb 100644 --- a/examples/n_body/n_body_solar_system.jl +++ b/examples/n_body/n_body_solar_system.jl @@ -49,7 +49,7 @@ callbacks = CallbackSet(info_callback, saving_callback) filename = tempname() open(filename, "w") do f redirect_stderr(f) do - TrixiParticles.TimerOutputs.disable_debug_timings(TrixiParticles) + TrixiParticles.disable_debug_timings() end end @@ -63,6 +63,6 @@ sol = solve(ode, SymplecticEuler(), # Enable timers again open(filename, "w") do f redirect_stderr(f) do - TrixiParticles.TimerOutputs.enable_debug_timings(TrixiParticles) + TrixiParticles.enable_debug_timings() end end diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index f170e08d8..2fe865d10 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -21,7 +21,8 @@ using SciMLBase: CallbackSet, DiscreteCallback, DynamicalODEProblem, u_modified! using StaticArrays: @SMatrix, SMatrix, setindex using StrideArrays: PtrArray, StaticInt using TimerOutputs: TimerOutput, TimerOutputs, print_timer, reset_timer! -using TrixiBase: trixi_include +using TrixiBase: trixi_include, @trixi_timeit, timer, timeit_debug_enabled, + disable_debug_timings, enable_debug_timings @reexport using PointNeighbors: TrivialNeighborhoodSearch, GridNeighborhoodSearch using PointNeighbors: PointNeighbors, for_particle_neighbor using WriteVTK: vtk_grid, MeshCell, VTKCellTypes, paraview_collection, vtk_save diff --git a/src/util.jl b/src/util.jl index b3cbfb764..338a94415 100644 --- a/src/util.jl +++ b/src/util.jl @@ -26,48 +26,6 @@ function print_startup_message() println(s) end -# Enable debug timings `@trixi_timeit timer() "name" stuff...`. -# This allows us to disable timings completely by executing -# `TimerOutputs.disable_debug_timings(TrixiParticles)` -# and to enable them again by executing -# `TimerOutputs.enable_debug_timings(TrixiParticles)` -timeit_debug_enabled() = true - -# Store main timer for global timing of functions -const main_timer = TimerOutput() - -# Always call timer() to hide implementation details -timer() = main_timer - -# @trixi_timeit timer() "some label" expression -# -# Basically the same as a special case of `@timeit_debug` from -# [TimerOutputs.jl](https://github.com/KristofferC/TimerOutputs.jl), -# but without `try ... finally ... end` block. Thus, it's not exception-safe, -# but it also avoids some related performance problems. Since we do not use -# exception handling in TrixiParticles, that's not really an issue. -# -# Copied from [Trixi.jl](https://github.com/trixi-framework/Trixi.jl). -macro trixi_timeit(timer_output, label, expr) - timeit_block = quote - if timeit_debug_enabled() - local to = $(esc(timer_output)) - local enabled = to.enabled - if enabled - local accumulated_data = $(TimerOutputs.push!)(to, $(esc(label))) - end - local b0 = $(TimerOutputs.gc_bytes)() - local t0 = $(TimerOutputs.time_ns)() - end - local val = $(esc(expr)) - if timeit_debug_enabled() && enabled - $(TimerOutputs.do_accumulate!)(accumulated_data, t0, b0) - $(TimerOutputs.pop!)(to) - end - val - end -end - """ @threaded for ... end diff --git a/test/count_allocations.jl b/test/count_allocations.jl index 3b5110ba1..1c597f9a0 100644 --- a/test/count_allocations.jl +++ b/test/count_allocations.jl @@ -45,7 +45,7 @@ function count_rhs_allocations(sol, semi) try # Disable timers, which cause extra allocations - TrixiParticles.TimerOutputs.disable_debug_timings(TrixiParticles) + TrixiParticles.disable_debug_timings() # Disable multithreading, which causes extra allocations return disable_polyester_threads() do @@ -57,7 +57,7 @@ function count_rhs_allocations(sol, semi) end finally # Enable timers again - @invokelatest TrixiParticles.TimerOutputs.enable_debug_timings(TrixiParticles) + @invokelatest TrixiParticles.enable_debug_timings() end end From 4af461da201fa6f817ce11d92437fb0c66beecb2 Mon Sep 17 00:00:00 2001 From: Niklas Neher <73897120+LasNikas@users.noreply.github.com> Date: Wed, 19 Jun 2024 09:43:16 +0200 Subject: [PATCH 39/49] Add `trixi2vtk` wrapper for `InitialCondition` (#546) * add wrapper * modify kwarg * implement suggestions --- src/visualization/write2vtk.jl | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/visualization/write2vtk.jl b/src/visualization/write2vtk.jl index a05e6727e..2a4c9f207 100644 --- a/src/visualization/write2vtk.jl +++ b/src/visualization/write2vtk.jl @@ -183,6 +183,32 @@ function trixi2vtk(coordinates; output_directory="out", prefix="", filename="coo return file end +""" + trixi2vtk(initial_condition::InitialCondition; output_directory="out", + prefix="", filename="initial_condition", custom_quantities...) + +Convert [`InitialCondition`](@ref) data to VTK format. + +# Arguments +- `initial_condition`: [`InitialCondition`](@ref) to be saved. + +# Keywords +- `output_directory="out"`: Output directory path. +- `prefix=""`: Prefix for the output file. +- `filename="coordinates"`: Name of the output file. + +# Returns +- `file::AbstractString`: Path to the generated VTK file. +""" +function trixi2vtk(initial_condition::InitialCondition; output_directory="out", + prefix="", filename="initial_condition", custom_quantities...) + (; coordinates, velocity, density, mass, pressure) = initial_condition + + return trixi2vtk(coordinates; output_directory, prefix, filename, + density=density, initial_velocity=velocity, mass=mass, + pressure=pressure) +end + function write2vtk!(vtk, v, u, t, system; write_meta_data=true) vtk["velocity"] = view(v, 1:ndims(system), :) From 39bc7985b8df42beae8b58984b59a9a251544bfa Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Wed, 19 Jun 2024 10:44:19 +0200 Subject: [PATCH 40/49] Example on how to save interpolated planes using postprocessing callback (#462) * add example * replace MSE with MRE * Revert "replace MSE with MRE" This reverts commit 1e0090794f7c13ea02b5c3ae298514caab93f0a4. * revert * update * format * fix and reduce runtime * Fix comment * implement suggestions * format --------- Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> --- .../postprocessing/interpolation_plane.jl | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/examples/postprocessing/interpolation_plane.jl b/examples/postprocessing/interpolation_plane.jl index 5c94df996..ea273d0fc 100644 --- a/examples/postprocessing/interpolation_plane.jl +++ b/examples/postprocessing/interpolation_plane.jl @@ -96,3 +96,25 @@ plot_3d = Plots.plot(scatter_3d, xlabel="X", ylabel="Y", zlabel="Z", combined_plot = Plots.plot(plot1, plot2, plot3, plot_3d, layout=(2, 2), size=(1000, 1500), margin=3mm) + +# If we want to save planes at regular intervals, we can use the postprocessing callback. +function save_interpolated_plane(v, u, t, system) + # Size of the patch to be interpolated + interpolation_start = [0.0, 0.0] + interpolation_end = [tank_size[1], tank_size[2]] + + # The resolution the plane is interpolated to. In this case twice the original resolution. + resolution = 0.5 * fluid_particle_spacing + + file_id = ceil(Int, t * 10000) + interpolate_plane_2d_vtk(interpolation_start, interpolation_end, resolution, + semi, system, v, u, filename="plane_$file_id.vti") + return nothing +end + +save_interpolation_cb = PostprocessCallback(; dt=0.1, write_file_interval=0, + save_interpolated_plane) + +trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "dam_break_2d.jl"), tspan=(0.0, 0.2), + extra_callback=save_interpolation_cb, fluid_particle_spacing=0.01) From ebd5cd98758a5f6d1afa474204621359a689bc64 Mon Sep 17 00:00:00 2001 From: Niklas Neher <73897120+LasNikas@users.noreply.github.com> Date: Wed, 19 Jun 2024 15:08:41 +0200 Subject: [PATCH 41/49] Add open boundaries (#442) * implement `ExtrudeFace` * use `sample_face` in `interpolate_plane_3d` * add docs * add tests * fix bug * create `OpenBoundarySPHSystem` * export OpenBoundarySPHSystem * fix bug * add `tlsph=true` * fix tuple bug * fix typo * fix again * fix for tests * fix layers * calculate particle spacing differently * add show-tests * add `within_boundary_zone` * change `floor` to `ceil` * boundary zone tests * add `evaluate_characteristics` * add tests * add reference functions * add `update_quantities!` * add buffer * add `SystemBuffer` tests * cosmetic change in tests * change `round` to `isapprox` * add update callback * add `UpdateCallback` * add `update_open_boundaries` * fix bugs * add timers * write prescribed quantities * improve dispatching * add example `pipe_flow_2d.jl` * add docs `UpdateCallback` * add `UpdateCallback` * fix typo * apply formatter * modify system buffer * add docstring * add docs * fix tests * fix typos * apply formatter * fix tests * add comment * fix tests * remove update bool * adapt docstring * implement suggestions * fix test * add check if callback is used * add comments in example file * generic types * merge main * undo combining compact support with DEM * remove density and pressure setter * implement suggestions * fix tests * Revert "remove density and pressure setter" This reverts commit 0203a3a21a3700a4646a327c1d7eb137fa9bb43f. * implement suggestions * rework in- and outflow * rename setter functions * rename function * adapt tests * add docs * rename function * apply formatter * fix tests * fix bug in plot recipes * apply formatter * implement suggestions for `boundary_zone.jl` * rename kwarg * implement suggestions * implement suggestions * modify `check_domain!` * add comments and check for fluid system with buffer * modify error message * fix tests * fix `callback_used` * using Measurements * fix tests * sqrt(eps()) * fix tests again * add doc to `boundary.md` * link single fluid system * fix tests * implement suggestions * fix tests * change the order of functions * modify example * fix tests * fix test again * implement doc suggestions * modify dos * fix typos * implement suggestions * implement suggestions * add EDAC to the example * implement suggestions * modify tests * adapt docs * add `initial_callback_flag` * add to `NEWS.md` and `README.md` * implement suggestions * fix typo --- NEWS.md | 10 +- README.md | 2 +- docs/src/systems/boundary.md | 83 +++ examples/fluid/pipe_flow_2d.jl | 131 +++++ examples/n_body/n_body_system.jl | 3 +- src/TrixiParticles.jl | 3 +- src/callbacks/density_reinit.jl | 2 +- src/callbacks/post_process.jl | 2 +- src/callbacks/solution_saving.jl | 2 +- src/callbacks/update.jl | 25 +- src/general/buffer.jl | 87 ++++ src/general/density_calculators.jl | 13 + src/general/general.jl | 1 + src/general/semidiscretization.jl | 56 +- src/general/system.jl | 20 +- .../boundary/open_boundary/boundary_zones.jl | 340 ++++++++++++ src/schemes/boundary/open_boundary/system.jl | 483 ++++++++++++++++++ src/schemes/boundary/rhs.jl | 4 +- src/schemes/boundary/system.jl | 24 +- .../fluid/entropically_damped_sph/system.jl | 39 +- src/schemes/fluid/fluid.jl | 4 + src/schemes/fluid/viscosity.jl | 13 + .../fluid/weakly_compressible_sph/system.jl | 28 +- src/schemes/schemes.jl | 3 + .../solid/discrete_element_method/system.jl | 4 +- .../solid/total_lagrangian_sph/system.jl | 6 +- src/setups/extrude_geometry.jl | 6 +- src/visualization/recipes_plots.jl | 8 +- src/visualization/write2vtk.jl | 45 +- test/examples/examples.jl | 8 + test/general/buffer.jl | 68 +++ test/general/general.jl | 1 + test/general/semidiscretization.jl | 3 + .../boundary/open_boundary/boundary_zone.jl | 221 ++++++++ .../open_boundary/characteristic_variables.jl | 134 +++++ .../boundary/open_boundary/open_boundary.jl | 2 + test/schemes/schemes.jl | 1 + test/systems/open_boundary_system.jl | 90 ++++ test/systems/systems.jl | 1 + 39 files changed, 1913 insertions(+), 63 deletions(-) create mode 100644 examples/fluid/pipe_flow_2d.jl create mode 100644 src/general/buffer.jl create mode 100644 src/schemes/boundary/open_boundary/boundary_zones.jl create mode 100644 src/schemes/boundary/open_boundary/system.jl create mode 100644 test/general/buffer.jl create mode 100644 test/schemes/boundary/open_boundary/boundary_zone.jl create mode 100644 test/schemes/boundary/open_boundary/characteristic_variables.jl create mode 100644 test/schemes/boundary/open_boundary/open_boundary.jl create mode 100644 test/systems/open_boundary_system.jl diff --git a/NEWS.md b/NEWS.md index ffe272e8a..5d7a295d8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,8 @@ # Changelog TrixiParticles.jl follows the interpretation of [semantic versioning (semver)](https://julialang.github.io/Pkg.jl/dev/compatibility/#Version-specifier-format-1) -used in the Julia ecosystem. Notable changes will be documented in this file for human readability. -We aim at 3 to 4 month between major release versions and about 2 weeks between minor versions. +used in the Julia ecosystem. Notable changes will be documented in this file for human readability. +We aim at 3 to 4 month between major release versions and about 2 weeks between minor versions. ## Version 0.2.x @@ -14,11 +14,15 @@ We aim at 3 to 4 month between major release versions and about 2 weeks between ### Deprecated +## Version 0.1.3 + +### Added +Open boundaries using the method of characteristics based on the work of Lastiwka et al., "Permeable and non-reflecting boundary conditions in SPH" (2009) were added for WCSPH and EDAC. ## Version 0.1.2 ### Added -A surface tension and adhesion model based on the work by Akinci et al., "Versatile Surface Tension and Adhesion for SPH Fluids" (2013) was added to WCSPH +A surface tension and adhesion model based on the work by Akinci et al., "Versatile Surface Tension and Adhesion for SPH Fluids" (2013) was added to WCSPH. ## Version 0.1.1 diff --git a/README.md b/README.md index b40e68fbc..76b288ed2 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Its features include: ## Features - Incompressible Navier-Stokes - Methods: Weakly Compressible Smoothed Particle Hydrodynamics (WCSPH), Entropically Damped Artificial Compressibility (EDAC) - - Models: Surface Tension + - Models: Surface Tension, Open Boundaries - Solid-body mechanics - Methods: Total Lagrangian SPH (TLSPH), Discrete Element Method (DEM) - Fluid-Structure Interaction diff --git a/docs/src/systems/boundary.md b/docs/src/systems/boundary.md index 8b374a71e..ae4b9b676 100644 --- a/docs/src/systems/boundary.md +++ b/docs/src/systems/boundary.md @@ -234,3 +234,86 @@ Pages = [joinpath("schemes", "boundary", "monaghan_kajtar", "monaghan_kajtar.jl" - Alireza Valizadeh, Joseph J. Monaghan. "A study of solid wall models for weakly compressible SPH." In: Journal of Computational Physics 300 (2015), pages 5–19. [doi: 10.1016/J.JCP.2015.07.033](https://doi.org/10.1016/J.JCP.2015.07.033) + +# [Open Boundaries](@id open_boundary) + +```@autodocs +Modules = [TrixiParticles] +Pages = [joinpath("schemes", "boundary", "open_boundary", "system.jl")] +``` + +```@autodocs +Modules = [TrixiParticles] +Pages = [joinpath("schemes", "boundary", "open_boundary", "boundary_zones.jl")] +``` + +### [Method of characteristics](@id method_of_characteristics) + +The difficulty in non-reflecting boundary conditions, also called open boundaries, is to determine +the appropriate boundary values of the exact characteristics of the Euler equations. +Assuming the flow near the boundaries is normal to the boundary +and free of shock waves and significant viscous effects, it can be shown that three characteristic variables exist: + +- ``J_1``, associated with convection of entropy and propagates at flow velocity, +- ``J_2``, downstream-running characteristics, +- ``J_3``, upstream-running characteristics. + +Giles (1990) derived those variables based on a linearized set of governing equations: +```math +J_1 = -c_s^2 (\rho - \rho_{\text{ref}}) + (p - p_{\text{ref}}) +``` +```math +J_2 = \rho c_s (v - v_{\text{ref}}) + (p - p_{\text{ref}}) +``` +```math +J_3 = - \rho c_s (v - v_{\text{ref}}) + (p - p_{\text{ref}}) +``` +where the subscript "ref" denotes the reference flow near the boundaries, which can be prescribed. + +Specifying the reference variables is **not** equivalent to prescription of ``\rho``, ``v`` and ``p`` +directly, since the perturbation from the reference flow is allowed. + +Lastiwka et al. (2009) applied the method of characteristic to SPH and determined the number of variables that should be +**prescribed** at the boundary and the number which should be **propagated** from the fluid domain to the boundary: + +- For an **inflow** boundary: + - Prescribe *downstream*-running characteristics ``J_1`` and ``J_2`` + - Transmit ``J_3`` from the fluid domain (allow ``J_3`` to propagate upstream to the boundary). + +- For an **outflow** boundary: + - Prescribe *upstream*-running characteristic ``J_3`` + - Transmit ``J_1`` and ``J_2`` from the fluid domain. + +Prescribing is done by simply setting the characteristics to zero. To transmit the characteristics from the fluid +domain, or in other words, to carry the information of the fluid to the boundaries, Negi et al. (2020) use a Shepard Interpolation +```math +f_i = \frac{\sum_j^N f_j W_{ij}}{\sum_j^N W_{ij}}, +``` +where the ``i``-th particle is a boundary particle, ``f`` is either ``J_1``, ``J_2`` or ``J_3`` and ``N`` is the set of +neighboring fluid particles. + +To express pressure ``p``, density ``\rho`` and velocity ``v`` as functions of the characteristic variables, the system of equations +from the characteristic variables is inverted and gives +```math + \rho - \rho_{\text{ref}} = \frac{1}{c_s^2} \left( -J_1 + \frac{1}{2} J_2 + \frac{1}{2} J_3 \right), +``` +```math +u - u_{\text{ref}}= \frac{1}{2\rho c_s} \left( J_2 - J_3 \right), +``` +```math +p - p_{\text{ref}} = \frac{1}{2} \left( J_2 + J_3 \right). +``` +With ``J_1``, ``J_2`` and ``J_3`` determined, we can easily solve for the actual variables for each particle. + +### References +- M. B. Giles. "Nonreflecting boundary conditions for Euler equation calculations". + In: AIAA Journal, 28.12 pages 2050--2058. + [doi: 10.2514/3.10521](https://doi.org/10.2514/3.10521) +- M. Lastiwka, M. Basa, N. J. Quinlan. + "Permeable and non-reflecting boundary conditions in SPH". + In: International Journal for Numerical Methods in Fluids 61, (2009), pages 709--724. + [doi: 10.1002/fld.1971](https://doi.org/10.1002/fld.1971) +- P. Negi, P. Ramachandran, A. Haftu. + "An improved non-reflecting outlet boundary condition for weakly-compressible SPH". + In: Computer Methods in Applied Mechanics and Engineering 367, (2020), pages 113--119. + [doi: 10.1016/j.cma.2020.113119](https://doi.org/10.1016/j.cma.2020.113119) diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl new file mode 100644 index 000000000..3d642696e --- /dev/null +++ b/examples/fluid/pipe_flow_2d.jl @@ -0,0 +1,131 @@ +# 2D channel flow simulation with open boundaries. + +using TrixiParticles +using OrdinaryDiffEq + +# ========================================================================================== +# ==== Resolution +particle_spacing = 0.05 + +# Make sure that the kernel support of fluid particles at a boundary is always fully sampled +boundary_layers = 3 + +# Make sure that the kernel support of fluid particles at an open boundary is always +# fully sampled. +# Note: Due to the dynamics at the inlets and outlets of open boundaries, +# it is recommended to use `open_boundary_layers > boundary_layers` +open_boundary_layers = 6 + +# ========================================================================================== +# ==== Experiment Setup +tspan = (0.0, 2.0) + +# Boundary geometry and initial fluid particle positions +domain_size = (1.0, 0.4) + +flow_direction = [1.0, 0.0] +reynolds_number = 100 +const prescribed_velocity = 2.0 + +boundary_size = (domain_size[1] + 2 * particle_spacing * open_boundary_layers, + domain_size[2]) + +fluid_density = 1000.0 + +# For this particular example, it is necessary to have a background pressure. +# Otherwise the suction at the outflow is to big and the simulation becomes unstable. +pressure = 1000.0 + +sound_speed = 10 * prescribed_velocity + +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=7, background_pressure=pressure) + +pipe = RectangularTank(particle_spacing, domain_size, boundary_size, fluid_density, + pressure=pressure, n_layers=boundary_layers, + faces=(false, false, true, true)) + +# Shift pipe walls in negative x-direction for the inflow +pipe.boundary.coordinates[1, :] .-= particle_spacing * open_boundary_layers + +n_buffer_particles = 4 * pipe.n_particles_per_dimension[2] + +# ========================================================================================== +# ==== Fluid +smoothing_length = 3.0 * particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() + +fluid_density_calculator = ContinuityDensity() + +kinematic_viscosity = prescribed_velocity * domain_size[2] / reynolds_number + +viscosity = ViscosityAdami(nu=kinematic_viscosity) + +fluid_system = EntropicallyDampedSPHSystem(pipe.fluid, smoothing_kernel, smoothing_length, + sound_speed, viscosity=viscosity, + density_calculator=fluid_density_calculator, + buffer_size=n_buffer_particles) + +# Alternatively the WCSPH scheme can be used +# alpha = 8 * kinematic_viscosity / (smoothing_length * sound_speed) +# viscosity = ArtificialViscosityMonaghan(; alpha, beta=0.0) + +# fluid_system = WeaklyCompressibleSPHSystem(pipe.fluid, fluid_density_calculator, +# state_equation, smoothing_kernel, +# smoothing_length, viscosity=viscosity, +# buffer_size=n_buffer_particles) + +# ========================================================================================== +# ==== Open Boundary +function velocity_function(pos, t) + # Use this for a time-dependent inflow velocity + # return SVector(0.5prescribed_velocity * sin(2pi * t) + prescribed_velocity, 0) + + return SVector(prescribed_velocity, 0.0) +end + +inflow = InFlow(; plane=([0.0, 0.0], [0.0, domain_size[2]]), flow_direction, + open_boundary_layers, density=fluid_density, particle_spacing) + +open_boundary_in = OpenBoundarySPHSystem(inflow; sound_speed, fluid_system, + buffer_size=n_buffer_particles, + reference_pressure=pressure, + reference_velocity=velocity_function) + +outflow = OutFlow(; plane=([domain_size[1], 0.0], [domain_size[1], domain_size[2]]), + flow_direction, open_boundary_layers, density=fluid_density, + particle_spacing) + +open_boundary_out = OpenBoundarySPHSystem(outflow; sound_speed, fluid_system, + buffer_size=n_buffer_particles, + reference_pressure=pressure, + reference_velocity=velocity_function) + +# ========================================================================================== +# ==== Boundary + +boundary_model = BoundaryModelDummyParticles(pipe.boundary.density, pipe.boundary.mass, + AdamiPressureExtrapolation(), + state_equation=state_equation, + #viscosity=ViscosityAdami(nu=1e-4), + smoothing_kernel, smoothing_length) + +boundary_system = BoundarySPHSystem(pipe.boundary, boundary_model) + +# ========================================================================================== +# ==== Simulation +semi = Semidiscretization(fluid_system, open_boundary_in, open_boundary_out, + boundary_system) + +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=100) +saving_callback = SolutionSavingCallback(dt=0.02, prefix="") + +callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback()) + +sol = solve(ode, RDPK3SpFSAL35(), + abstol=1e-5, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) + reltol=1e-3, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) + dtmax=1e-2, # Limit stepsize to prevent crashing + save_everystep=false, callback=callbacks); diff --git a/examples/n_body/n_body_system.jl b/examples/n_body/n_body_system.jl index caf40a62e..c7226357a 100644 --- a/examples/n_body/n_body_system.jl +++ b/examples/n_body/n_body_system.jl @@ -5,12 +5,13 @@ struct NBodySystem{NDIMS, ELTYPE <: Real} <: TrixiParticles.System{NDIMS, Nothin initial_condition :: InitialCondition{ELTYPE} mass :: Array{ELTYPE, 1} # [particle] G :: ELTYPE + buffer :: Nothing function NBodySystem(initial_condition, G) mass = copy(initial_condition.mass) new{size(initial_condition.coordinates, 1), - eltype(mass)}(initial_condition, mass, G) + eltype(mass)}(initial_condition, mass, G, nothing) end end diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index 2fe865d10..aa97ab720 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -44,7 +44,8 @@ include("visualization/recipes_plots.jl") export Semidiscretization, semidiscretize, restart_with! export InitialCondition export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangianSPHSystem, - BoundarySPHSystem, DEMSystem, BoundaryDEMSystem + BoundarySPHSystem, DEMSystem, BoundaryDEMSystem, OpenBoundarySPHSystem, InFlow, + OutFlow export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback, PostprocessCallback, StepsizeCallback, UpdateCallback export ContinuityDensity, SummationDensity diff --git a/src/callbacks/density_reinit.jl b/src/callbacks/density_reinit.jl index 5c8a7b5e1..74c6fe5c9 100644 --- a/src/callbacks/density_reinit.jl +++ b/src/callbacks/density_reinit.jl @@ -74,7 +74,7 @@ function initialize_reinit_cb!(cb::DensityReinitializationCallback, u, t, integr # Update systems to compute quantities like density and pressure. semi = integrator.p v_ode, u_ode = u.x - update_systems_and_nhs(v_ode, u_ode, semi, t) + update_systems_and_nhs(v_ode, u_ode, semi, t; update_from_callback=true) # Apply the callback. cb(integrator) diff --git a/src/callbacks/post_process.jl b/src/callbacks/post_process.jl index d68168258..9c39f3eb4 100644 --- a/src/callbacks/post_process.jl +++ b/src/callbacks/post_process.jl @@ -236,7 +236,7 @@ function (pp::PostprocessCallback)(integrator) new_data = false # Update systems to compute quantities like density and pressure - update_systems_and_nhs(v_ode, u_ode, semi, t) + update_systems_and_nhs(v_ode, u_ode, semi, t; update_from_callback=true) foreach_system(semi) do system if system isa BoundarySystem && pp.exclude_boundary diff --git a/src/callbacks/solution_saving.jl b/src/callbacks/solution_saving.jl index 04552d57e..468a72a1c 100644 --- a/src/callbacks/solution_saving.jl +++ b/src/callbacks/solution_saving.jl @@ -139,7 +139,7 @@ function initialize_save_cb!(solution_callback::SolutionSavingCallback, u, t, in # Update systems to compute quantities like density and pressure semi = integrator.p v_ode, u_ode = u.x - update_systems_and_nhs(v_ode, u_ode, semi, t) + update_systems_and_nhs(v_ode, u_ode, semi, t; update_from_callback=true) # Apply the callback solution_callback(integrator) diff --git a/src/callbacks/update.jl b/src/callbacks/update.jl index ca3b5f114..3595d5c36 100644 --- a/src/callbacks/update.jl +++ b/src/callbacks/update.jl @@ -52,7 +52,16 @@ function initial_update!(cb, u, t, integrator) initial_update!(cb.affect!, u, t, integrator) end -initial_update!(cb::UpdateCallback, u, t, integrator) = cb(integrator) +function initial_update!(cb::UpdateCallback, u, t, integrator) + semi = integrator.p + + # Tell systems that `UpdateCallback` is used + foreach_system(semi) do system + update_callback_used!(system) + end + + return cb(integrator) +end # `condition` function (update_callback!::UpdateCallback)(u, t, integrator) @@ -69,16 +78,12 @@ function (update_callback!::UpdateCallback)(integrator) # Update quantities that are stored in the systems. These quantities (e.g. pressure) # still have the values from the last stage of the previous step if not updated here. - update_systems_and_nhs(v_ode, u_ode, semi, t) + update_systems_and_nhs(v_ode, u_ode, semi, t; update_from_callback=true) # Other updates might be added here later (e.g. Transport Velocity Formulation). - # @trixi_timeit timer() "update open boundary" foreach_system(semi) do system - # update_open_boundary_eachstep!(system, v_ode, u_ode, semi, t) - # end - # - # @trixi_timeit timer() "update TVF" foreach_system(semi) do system - # update_transport_velocity_eachstep!(system, v_ode, u_ode, semi, t) - # end + @trixi_timeit timer() "update open boundary" foreach_system(semi) do system + update_open_boundary_eachstep!(system, v_ode, u_ode, semi, t) + end # Tell OrdinaryDiffEq that `u` has been modified u_modified!(integrator, true) @@ -128,3 +133,5 @@ function Base.show(io::IO, ::MIME"text/plain", summary_box(io, "UpdateCallback", setup) end end + +update_callback_used!(system) = system diff --git a/src/general/buffer.jl b/src/general/buffer.jl new file mode 100644 index 000000000..b12cf05f3 --- /dev/null +++ b/src/general/buffer.jl @@ -0,0 +1,87 @@ +struct SystemBuffer{V} + active_particle :: BitVector + eachparticle :: V # Vector{Int} + buffer_size :: Int + + function SystemBuffer(active_size, buffer_size::Integer) + active_particle = vcat(trues(active_size), falses(buffer_size)) + eachparticle = collect(1:active_size) + + return new{typeof(eachparticle)}(active_particle, eachparticle, buffer_size) + end +end + +allocate_buffer(initial_condition, buffer) = initial_condition + +function allocate_buffer(initial_condition, buffer::SystemBuffer) + (; buffer_size) = buffer + + # Initialize particles far away from simulation domain + coordinates = fill(1e16, ndims(initial_condition), buffer_size) + + if all(rho -> isapprox(rho, first(initial_condition.density), atol=eps(), rtol=eps()), + initial_condition.density) + density = first(initial_condition.density) + else + throw(ArgumentError("`initial_condition.density` needs to be constant when using `SystemBuffer`")) + end + + particle_spacing = initial_condition.particle_spacing + + buffer_ic = InitialCondition(; coordinates, density, particle_spacing) + + return union(initial_condition, buffer_ic) +end + +@inline update_system_buffer!(buffer::Nothing) = buffer + +# TODO `resize` allocates. Find a non-allocating version +@inline function update_system_buffer!(buffer::SystemBuffer) + (; active_particle) = buffer + + resize!(buffer.eachparticle, count(active_particle)) + + i = 1 + for j in eachindex(active_particle) + if active_particle[j] + buffer.eachparticle[i] = j + i += 1 + end + end + + return buffer +end + +@inline each_moving_particle(system, buffer) = buffer.eachparticle + +@inline active_coordinates(u, system, buffer) = view(u, :, buffer.active_particle) + +@inline active_particles(system, buffer) = buffer.eachparticle + +@inline function activate_next_particle(system) + (; active_particle) = system.buffer + + next_particle = findfirst(x -> !x, active_particle) + + if isnothing(next_particle) + error("0 out of $(system.buffer.buffer_size) buffer particles available") + end + + active_particle[next_particle] = true + + return next_particle +end + +@inline function deactivate_particle!(system, particle, u) + (; active_particle) = system.buffer + + active_particle[particle] = false + + # Set particle far away from simulation domain + for dim in 1:ndims(system) + # Inf or NaN causes instability outcome. + u[dim, particle] = 1e16 + end + + return system +end diff --git a/src/general/density_calculators.jl b/src/general/density_calculators.jl index ee89c92a6..7244253b0 100644 --- a/src/general/density_calculators.jl +++ b/src/general/density_calculators.jl @@ -35,6 +35,19 @@ end return v[end, particle] end +# WARNING! +# These functions are intended to be used internally to set the density +# of newly activated particles in a callback. +# DO NOT use outside a callback. OrdinaryDiffEq does not allow changing `v` and `u` +# outside of callbacks. +@inline set_particle_density!(v, system, ::SummationDensity, particle, density) = v + +@inline function set_particle_density!(v, system, ::ContinuityDensity, particle, density) + v[end, particle] = density + + return v +end + function summation_density!(system, semi, u, u_ode, density; particles=each_moving_particle(system)) set_zero!(density) diff --git a/src/general/general.jl b/src/general/general.jl index 4a60cb52f..774d076c9 100644 --- a/src/general/general.jl +++ b/src/general/general.jl @@ -30,6 +30,7 @@ include("density_calculators.jl") include("corrections.jl") include("smoothing_kernels.jl") include("initial_condition.jl") +include("buffer.jl") include("system.jl") include("interpolation.jl") include("custom_quantities.jl") diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 6a1d703b2..73c1fe56d 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -153,7 +153,19 @@ end return compact_support(smoothing_kernel, smoothing_length) end +@inline function compact_support(system::OpenBoundarySPHSystem, neighbor) + # Use the compact support of the fluid + return compact_support(neighbor, system) +end + +@inline function compact_support(system::OpenBoundarySPHSystem, + neighbor::OpenBoundarySPHSystem) + # This NHS is never used + return 0.0 +end + @inline function compact_support(system::BoundaryDEMSystem, neighbor::BoundaryDEMSystem) + # This NHS is never used return 0.0 end @@ -281,6 +293,9 @@ function semidiscretize(semi, tspan; reset_threads=true, data_type=nothing) # Initialize this system initialize!(system, neighborhood_search) + + # Only for systems requiring a mandatory callback + reset_callback_flag!(system) end end @@ -343,6 +358,9 @@ function restart_with!(semi, sol; reset_threads=true) u = wrap_u(sol.u[end].x[2], system, semi) restart_with!(system, v, u) + + # Only for systems requiring a mandatory callback + reset_callback_flag!(system) end return semi @@ -429,7 +447,7 @@ function kick!(dv_ode, v_ode, u_ode, semi, t) end # Update the systems and neighborhood searches (NHS) for a simulation before calling `interact!` to compute forces -function update_systems_and_nhs(v_ode, u_ode, semi, t) +function update_systems_and_nhs(v_ode, u_ode, semi, t; update_from_callback=false) # First update step before updating the NHS # (for example for writing the current coordinates in the solid system) foreach_system(semi) do system @@ -466,7 +484,7 @@ function update_systems_and_nhs(v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) u = wrap_u(u_ode, system, semi) - update_final!(system, v, u, v_ode, u_ode, semi, t) + update_final!(system, v, u, v_ode, u_ode, semi, t; update_from_callback) end end @@ -631,6 +649,32 @@ function update_nhs!(neighborhood_search, particles_moving=(true, neighbor.ismoving[])) end +function update_nhs!(neighborhood_search, + system::FluidSystem, neighbor::OpenBoundarySPHSystem, + u_system, u_neighbor) + # The current coordinates of fluids and open boundaries change over time. + + # TODO: Update only `active_coordinates` of open boundaries. + # Problem: Removing inactive particles from neighboring lists is necessary. + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(true, true)) +end + +function update_nhs!(neighborhood_search, + system::OpenBoundarySPHSystem, neighbor::FluidSystem, + u_system, u_neighbor) + # The current coordinates of both open boundaries and fluids change over time. + + # TODO: Update only `active_coordinates` of open boundaries. + # Problem: Removing inactive particles from neighboring lists is necessary. + PointNeighbors.update!(neighborhood_search, + current_coordinates(u_system, system), + current_coordinates(u_neighbor, neighbor), + particles_moving=(true, true)) +end + function update_nhs!(neighborhood_search, system::TotalLagrangianSPHSystem, neighbor::FluidSystem, u_system, u_neighbor) @@ -725,6 +769,14 @@ function update_nhs!(neighborhood_search, return neighborhood_search end +function update_nhs!(neighborhood_search, + system::Union{BoundarySPHSystem, OpenBoundarySPHSystem}, + neighbor::Union{BoundarySPHSystem, OpenBoundarySPHSystem}, + u_system, u_neighbor) + # Don't update. This NHS is never used. + return neighborhood_search +end + function check_configuration(systems) foreach_system(systems) do system check_configuration(system, systems) diff --git a/src/general/system.jl b/src/general/system.jl index a4c7754c4..c3f10caad 100644 --- a/src/general/system.jl +++ b/src/general/system.jl @@ -17,7 +17,16 @@ initialize!(system, neighborhood_search) = system @inline n_moving_particles(system) = nparticles(system) @inline eachparticle(system) = Base.OneTo(nparticles(system)) -@inline each_moving_particle(system) = Base.OneTo(n_moving_particles(system)) + +# Wrapper for systems with `SystemBuffer` +@inline each_moving_particle(system) = each_moving_particle(system, system.buffer) +@inline each_moving_particle(system, ::Nothing) = Base.OneTo(n_moving_particles(system)) + +@inline active_coordinates(u, system) = active_coordinates(u, system, system.buffer) +@inline active_coordinates(u, system, ::Nothing) = current_coordinates(u, system) + +@inline active_particles(system) = active_particles(system, system.buffer) +@inline active_particles(system, ::Nothing) = eachparticle(system) # This should not be dispatched by system type. We always expect to get a column of `A`. @inline function extract_svector(A, system, i) @@ -64,6 +73,10 @@ end return zero(SVector{ndims(system), eltype(system)}) end +@inline set_particle_density!(v, system, particle, density) = v + +@inline set_particle_pressure!(v, system, particle, pressure) = v + @inline function smoothing_kernel(system, distance) (; smoothing_kernel, smoothing_length) = system return kernel(smoothing_kernel, distance, smoothing_length) @@ -103,6 +116,9 @@ function update_pressure!(system, v, u, v_ode, u_ode, semi, t) return system end -function update_final!(system, v, u, v_ode, u_ode, semi, t) +function update_final!(system, v, u, v_ode, u_ode, semi, t; update_from_callback=false) return system end + +# Only for systems requiring a mandatory callback +reset_callback_flag!(system) = system diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl new file mode 100644 index 000000000..586d565b1 --- /dev/null +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -0,0 +1,340 @@ +@doc raw""" + InFlow(; plane, flow_direction, density, particle_spacing, + initial_condition=nothing, extrude_geometry=nothing, + open_boundary_layers::Integer) + +Inflow boundary zone for [`OpenBoundarySPHSystem`](@ref). + +The specified plane (line in 2D or rectangle in 3D) will be extruded in upstream +direction (the direction opposite to `flow_direction`) to create a box for the boundary zone. +There are three ways to specify the actual shape of the inflow: +1. Don't pass `initial_condition` or `extrude_geometry`. The boundary zone box will then + be filled with inflow particles (default). +2. Specify `extrude_geometry` by passing a 1D shape in 2D or a 2D shape in 3D, + which is then extruded in upstream direction to create the inflow particles. + - In 2D, the shape must be either an initial condition with 2D coordinates, which lies + on the line specified by `plane`, or an initial condition with 1D coordinates, which lies + on the line specified by `plane` when a y-coordinate of `0` is added. + - In 3D, the shape must be either an initial condition with 3D coordinates, which lies + in the rectangle specified by `plane`, or an initial condition with 2D coordinates, + which lies in the rectangle specified by `plane` when a z-coordinate of `0` is added. +3. Specify `initial_condition` by passing a 2D initial condition in 2D or a 3D initial condition in 3D, + which will be used for the inflow particles. + +!!! note "Note" + Particles outside the boundary zone box will be removed. + +# Keywords +- `plane`: Tuple of points defining a part of the surface of the domain. + The points must either span a line in 2D or a rectangle in 3D. + This line or rectangle is then extruded in upstream direction to obtain + the boundary zone. + In 2D, pass two points ``(A, B)``, so that the interval ``[A, B]`` is + the inflow surface. + In 3D, pass three points ``(A, B, C)``, so that the rectangular inflow surface + is spanned by the vectors ``\widehat{AB}`` and ``\widehat{AC}``. + These two vectors must be orthogonal. +- `flow_direction`: Vector defining the flow direction. +- `open_boundary_layers`: Number of particle layers in upstream direction. +- `particle_spacing`: The spacing between the particles (see [`InitialCondition`](@ref)). +- `density`: Particle density (see [`InitialCondition`](@ref)). +- `initial_condition=nothing`: `InitialCondition` for the inflow particles. + Particles outside the boundary zone will be removed. + Do not use together with `extrude_geometry`. +- `extrude_geometry=nothing`: 1D shape in 2D or 2D shape in 3D, which lies on the plane + and is extruded upstream to obtain the inflow particles. + See point 2 above for more details. + +# Examples +```julia +# 2D +plane_points = ([0.0, 0.0], [0.0, 1.0]) +flow_direction=[1.0, 0.0] + +inflow = InFlow(; plane=plane_points, particle_spacing=0.1, flow_direction, density=1.0, + open_boundary_layers=4) + +# 3D +plane_points = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) +flow_direction=[0.0, 0.0, 1.0] + +inflow = InFlow(; plane=plane_points, particle_spacing=0.1, flow_direction, density=1.0, + open_boundary_layers=4) + +# 3D particles sampled as cylinder +circle = SphereShape(0.1, 0.5, (0.5, 0.5), 1.0, sphere_type=RoundSphere()) + +inflow = InFlow(; plane=plane_points, particle_spacing=0.1, flow_direction, density=1.0, + extrude_geometry=circle, open_boundary_layers=4) +``` + +!!! warning "Experimental Implementation" + This is an experimental feature and may change in any future releases. +""" +struct InFlow{NDIMS, IC, S, ZO, ZW, FD} + initial_condition :: IC + spanning_set :: S + zone_origin :: ZO + zone_width :: ZW + flow_direction :: FD + + function InFlow(; plane, flow_direction, density, particle_spacing, + initial_condition=nothing, extrude_geometry=nothing, + open_boundary_layers::Integer) + if open_boundary_layers <= 0 + throw(ArgumentError("`open_boundary_layers` must be positive and greater than zero")) + end + + # Unit vector pointing in downstream direction + flow_direction_ = normalize(SVector(flow_direction...)) + + # Sample particles in boundary zone + if isnothing(initial_condition) && isnothing(extrude_geometry) + initial_condition = TrixiParticles.extrude_geometry(plane; particle_spacing, + density, + direction=-flow_direction_, + n_extrude=open_boundary_layers) + elseif !isnothing(extrude_geometry) + initial_condition = TrixiParticles.extrude_geometry(extrude_geometry; + particle_spacing, + density, + direction=-flow_direction_, + n_extrude=open_boundary_layers) + end + + NDIMS = ndims(initial_condition) + ELTYPE = eltype(initial_condition) + + zone_width = open_boundary_layers * initial_condition.particle_spacing + + # Vectors spanning the boundary zone/box + spanning_set, zone_origin = calculate_spanning_vectors(plane, zone_width) + + # First vector of `spanning_vectors` is normal to the inflow plane. + # The normal vector must point in upstream direction for an inflow boundary. + dot_ = dot(normalize(spanning_set[:, 1]), flow_direction_) + + if !isapprox(abs(dot_), 1.0, atol=1e-7) + throw(ArgumentError("`flow_direction` is not normal to inflow plane")) + else + # Flip the normal vector to point in the opposite direction of `flow_direction` + spanning_set[:, 1] .*= -sign(dot_) + end + + spanning_set_ = reinterpret(reshape, SVector{NDIMS, ELTYPE}, spanning_set) + + # Remove particles outside the boundary zone. + # This check is only necessary when `initial_condition` or `extrude_geometry` are passed. + ic = remove_outside_particles(initial_condition, spanning_set_, zone_origin) + + return new{NDIMS, typeof(ic), typeof(spanning_set_), typeof(zone_origin), + typeof(zone_width), + typeof(flow_direction_)}(ic, spanning_set_, zone_origin, zone_width, + flow_direction_) + end +end + +@doc raw""" + OutFlow(; plane, flow_direction, density, particle_spacing, + initial_condition=nothing, extrude_geometry=nothing, + open_boundary_layers::Integer) + +Outflow boundary zone for [`OpenBoundarySPHSystem`](@ref). + +The specified plane (line in 2D or rectangle in 3D) will be extruded in downstream +direction (the direction in `flow_direction`) to create a box for the boundary zone. +There are three ways to specify the actual shape of the outflow: +1. Don't pass `initial_condition` or `extrude_geometry`. The boundary zone box will then + be filled with outflow particles (default). +2. Specify `extrude_geometry` by passing a 1D shape in 2D or a 2D shape in 3D, + which is then extruded in downstream direction to create the outflow particles. + - In 2D, the shape must be either an initial condition with 2D coordinates, which lies + on the line specified by `plane`, or an initial condition with 1D coordinates, which lies + on the line specified by `plane` when a y-coordinate of `0` is added. + - In 3D, the shape must be either an initial condition with 3D coordinates, which lies + in the rectangle specified by `plane`, or an initial condition with 2D coordinates, + which lies in the rectangle specified by `plane` when a z-coordinate of `0` is added. +3. Specify `initial_condition` by passing a 2D initial condition in 2D or a 3D initial condition in 3D, + which will be used for the outflow particles. + +!!! note "Note" + Particles outside the boundary zone box will be removed. + +# Keywords +- `plane`: Tuple of points defining a part of the surface of the domain. + The points must either span a line in 2D or a rectangle in 3D. + This line or rectangle is then extruded in downstream direction to obtain + the boundary zone. + In 2D, pass two points ``(A, B)``, so that the interval ``[A, B]`` is + the outflow surface. + In 3D, pass three points ``(A, B, C)``, so that the rectangular outflow surface + is spanned by the vectors ``\widehat{AB}`` and ``\widehat{AC}``. + These two vectors must be orthogonal. +- `flow_direction`: Vector defining the flow direction. +- `open_boundary_layers`: Number of particle layers in downstream direction. +- `particle_spacing`: The spacing between the particles (see [`InitialCondition`](@ref)). +- `density`: Particle density (see [`InitialCondition`](@ref)). +- `initial_condition=nothing`: `InitialCondition` for the outflow particles. + Particles outside the boundary zone will be removed. + Do not use together with `extrude_geometry`. +- `extrude_geometry=nothing`: 1D shape in 2D or 2D shape in 3D, which lies on the plane + and is extruded downstream to obtain the outflow particles. + See point 2 above for more details. + +# Examples +```julia +# 2D +plane_points = ([0.0, 0.0], [0.0, 1.0]) +flow_direction = [1.0, 0.0] + +outflow = OutFlow(; plane=plane_points, particle_spacing=0.1, flow_direction, density=1.0, + open_boundary_layers=4) + +# 3D +plane_points = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) +flow_direction = [0.0, 0.0, 1.0] + +outflow = OutFlow(; plane=plane_points, particle_spacing=0.1, flow_direction, density=1.0, + open_boundary_layers=4) + +# 3D particles sampled as cylinder +circle = SphereShape(0.1, 0.5, (0.5, 0.5), 1.0, sphere_type=RoundSphere()) + +outflow = OutFlow(; plane=plane_points, particle_spacing=0.1, flow_direction, density=1.0, + extrude_geometry=circle, open_boundary_layers=4) +``` + +!!! warning "Experimental Implementation" + This is an experimental feature and may change in any future releases. +""" +struct OutFlow{NDIMS, IC, S, ZO, ZW, FD} + initial_condition :: IC + spanning_set :: S + zone_origin :: ZO + zone_width :: ZW + flow_direction :: FD + + function OutFlow(; plane, flow_direction, density, particle_spacing, + initial_condition=nothing, extrude_geometry=nothing, + open_boundary_layers::Integer) + if open_boundary_layers <= 0 + throw(ArgumentError("`open_boundary_layers` must be positive and greater than zero")) + end + + # Unit vector pointing in downstream direction + flow_direction_ = normalize(SVector(flow_direction...)) + + # Sample particles in boundary zone + if isnothing(initial_condition) && isnothing(extrude_geometry) + initial_condition = TrixiParticles.extrude_geometry(plane; particle_spacing, + density, + direction=flow_direction_, + n_extrude=open_boundary_layers) + elseif !isnothing(extrude_geometry) + initial_condition = TrixiParticles.extrude_geometry(extrude_geometry; + particle_spacing, density, + direction=-flow_direction_, + n_extrude=open_boundary_layers) + end + + NDIMS = ndims(initial_condition) + ELTYPE = eltype(initial_condition) + + zone_width = open_boundary_layers * initial_condition.particle_spacing + + # Vectors spanning the boundary zone/box + spanning_set, zone_origin = calculate_spanning_vectors(plane, zone_width) + + # First vector of `spanning_vectors` is normal to the outflow plane. + # The normal vector must point in downstream direction for an outflow boundary. + dot_ = dot(normalize(spanning_set[:, 1]), flow_direction_) + + if !isapprox(abs(dot_), 1.0, atol=1e-7) + throw(ArgumentError("`flow_direction` is not normal to outflow plane")) + else + # Flip the normal vector to point in `flow_direction` + spanning_set[:, 1] .*= sign(dot_) + end + + spanning_set_ = reinterpret(reshape, SVector{NDIMS, ELTYPE}, spanning_set) + + # Remove particles outside the boundary zone. + # This check is only necessary when `initial_condition` or `extrude_geometry` are passed. + ic = remove_outside_particles(initial_condition, spanning_set_, zone_origin) + + return new{NDIMS, typeof(ic), typeof(spanning_set_), typeof(zone_origin), + typeof(zone_width), + typeof(flow_direction_)}(ic, spanning_set_, zone_origin, zone_width, + flow_direction_) + end +end + +@inline Base.ndims(::Union{InFlow{NDIMS}, OutFlow{NDIMS}}) where {NDIMS} = NDIMS + +function calculate_spanning_vectors(plane, zone_width) + return spanning_vectors(Tuple(plane), zone_width), SVector(plane[1]...) +end + +function spanning_vectors(plane_points::NTuple{2}, zone_width) + plane_size = plane_points[2] - plane_points[1] + + # Calculate normal vector of plane + b = normalize([-plane_size[2], plane_size[1]]) * zone_width + + return hcat(b, plane_size) +end + +function spanning_vectors(plane_points::NTuple{3}, zone_width) + # Vectors spanning the plane + edge1 = plane_points[2] - plane_points[1] + edge2 = plane_points[3] - plane_points[1] + + if !isapprox(dot(edge1, edge2), 0.0, atol=1e-7) + throw(ArgumentError("the vectors `AB` and `AC` for the provided points `A`, `B`, `C` must be orthogonal")) + end + + # Calculate normal vector of plane + c = Vector(normalize(cross(edge2, edge1)) * zone_width) + + return hcat(c, edge1, edge2) +end + +@inline function is_in_boundary_zone(boundary_zone::Union{InFlow, OutFlow}, particle_coords) + (; zone_origin, spanning_set) = boundary_zone + particle_position = particle_coords - zone_origin + + return is_in_boundary_zone(spanning_set, particle_position) +end + +@inline function is_in_boundary_zone(spanning_set::AbstractArray, + particle_position::SVector{NDIMS}) where {NDIMS} + for dim in 1:NDIMS + span_dim = spanning_set[dim] + # Checks whether the projection of the particle position + # falls within the range of the zone + if !(0 <= dot(particle_position, span_dim) <= dot(span_dim, span_dim)) + + # Particle is not in boundary zone + return false + end + end + + # Particle is in boundary zone + return true +end + +function remove_outside_particles(initial_condition, spanning_set, zone_origin) + (; coordinates, density, particle_spacing) = initial_condition + + in_zone = trues(nparticles(initial_condition)) + + for particle in eachparticle(initial_condition) + current_position = current_coords(coordinates, initial_condition, particle) + particle_position = current_position - zone_origin + + in_zone[particle] = is_in_boundary_zone(spanning_set, particle_position) + end + + return InitialCondition(; coordinates=coordinates[:, in_zone], density=first(density), + particle_spacing) +end diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl new file mode 100644 index 000000000..43c0a6ff1 --- /dev/null +++ b/src/schemes/boundary/open_boundary/system.jl @@ -0,0 +1,483 @@ +@doc raw""" + OpenBoundarySPHSystem(boundary_zone::Union{InFlow, OutFlow}; sound_speed, + fluid_system::FluidSystem, buffer_size::Integer, + reference_velocity=zeros(ndims(boundary_zone)), + reference_pressure=0.0, + reference_density=first(boundary_zone.initial_condition.density)) + +Open boundary system for in- and outflow particles. +These open boundaries use the characteristic variables to propagate the appropriate values +to the outlet or inlet and have been proposed by Lastiwka et al. (2009). For more information +about the method see [description below](@ref method_of_characteristics). + +# Arguments +- `boundary_zone`: Use [`InFlow`](@ref) for an inflow and [`OutFlow`](@ref) for an outflow boundary. + +# Keywords +- `sound_speed`: Speed of sound. +- `fluid_system`: The corresponding fluid system +- `buffer_size`: Number of buffer particles. +- `reference_velocity`: Reference velocity is either a function mapping each particle's coordinates + and time to its velocity, an array where the ``i``-th column holds + the velocity of particle ``i`` or, for a constant fluid velocity, + a vector holding this velocity. Velocity is constant zero by default. +- `reference_pressure`: Reference pressure is either a function mapping each particle's coordinates + and time to its pressure, a vector holding the pressure of each particle, + or a scalar for a constant pressure over all particles. + Pressure is constant zero by default. +- `reference_density`: Reference density is either a function mapping each particle's coordinates + and time to its density, a vector holding the density of each particle, + or a scalar for a constant density over all particles. + Density is the density of the first particle in the initial condition by default. + +!!! warning "Experimental Implementation" + This is an experimental feature and may change in any future releases. +""" +struct OpenBoundarySPHSystem{BZ, NDIMS, ELTYPE <: Real, IC, FS, ARRAY1D, ARRAY2D, RV, RP, + RD, B} <: System{NDIMS, IC} + initial_condition :: IC + fluid_system :: FS + mass :: ARRAY1D # Array{ELTYPE, 1}: [particle] + density :: ARRAY1D # Array{ELTYPE, 1}: [particle] + volume :: ARRAY1D # Array{ELTYPE, 1}: [particle] + pressure :: ARRAY1D # Array{ELTYPE, 1}: [particle] + characteristics :: ARRAY2D # Array{ELTYPE, 2}: [characteristic, particle] + previous_characteristics :: ARRAY2D # Array{ELTYPE, 2}: [characteristic, particle] + sound_speed :: ELTYPE + boundary_zone :: BZ + flow_direction :: SVector{NDIMS, ELTYPE} + reference_velocity :: RV + reference_pressure :: RP + reference_density :: RD + buffer :: B + update_callback_used :: Ref{Bool} + + function OpenBoundarySPHSystem(boundary_zone::Union{InFlow, OutFlow}; sound_speed, + fluid_system::FluidSystem, buffer_size::Integer, + reference_velocity=zeros(ndims(boundary_zone)), + reference_pressure=0.0, + reference_density=first(boundary_zone.initial_condition.density)) + (; initial_condition) = boundary_zone + + buffer = SystemBuffer(nparticles(initial_condition), buffer_size) + + initial_condition = allocate_buffer(initial_condition, buffer) + + NDIMS = ndims(initial_condition) + ELTYPE = eltype(initial_condition) + + if !(reference_velocity isa Function || + (reference_velocity isa Vector && length(reference_velocity) == NDIMS)) + throw(ArgumentError("`reference_velocity` must be either a function mapping " * + "each particle's coordinates and time to its velocity, " * + "an array where the ``i``-th column holds the velocity of particle ``i`` " * + "or, for a constant fluid velocity, a vector of length $NDIMS for a $(NDIMS)D problem holding this velocity")) + else + reference_velocity_ = wrap_reference_function(reference_velocity, Val(NDIMS)) + end + + if !(reference_pressure isa Function || reference_pressure isa Real) + throw(ArgumentError("`reference_pressure` must be either a function mapping " * + "each particle's coordinates and time to its pressure, " * + "a vector holding the pressure of each particle, or a scalar")) + else + reference_pressure_ = wrap_reference_function(reference_pressure, Val(NDIMS)) + end + + if !(reference_density isa Function || reference_density isa Real) + throw(ArgumentError("`reference_density` must be either a function mapping " * + "each particle's coordinates and time to its density, " * + "a vector holding the density of each particle, or a scalar")) + else + reference_density_ = wrap_reference_function(reference_density, Val(NDIMS)) + end + + mass = copy(initial_condition.mass) + pressure = [reference_pressure_(initial_condition.coordinates[:, i], 0.0) + for i in eachparticle(initial_condition)] + density = copy(initial_condition.density) + volume = similar(initial_condition.density) + + characteristics = zeros(ELTYPE, 3, length(mass)) + previous_characteristics = zeros(ELTYPE, 3, length(mass)) + + flow_direction_ = boundary_zone.flow_direction + + return new{typeof(boundary_zone), NDIMS, ELTYPE, typeof(initial_condition), + typeof(fluid_system), typeof(mass), typeof(characteristics), + typeof(reference_velocity_), typeof(reference_pressure_), + typeof(reference_density_), + typeof(buffer)}(initial_condition, fluid_system, mass, density, volume, + pressure, characteristics, previous_characteristics, + sound_speed, boundary_zone, flow_direction_, + reference_velocity_, reference_pressure_, + reference_density_, buffer, false) + end +end + +timer_name(::OpenBoundarySPHSystem) = "open_boundary" +vtkname(system::OpenBoundarySPHSystem) = "open_boundary" + +function Base.show(io::IO, system::OpenBoundarySPHSystem) + @nospecialize system # reduce precompilation time + + print(io, "OpenBoundarySPHSystem{", ndims(system), "}(") + print(io, type2string(system.boundary_zone)) + print(io, ") with ", nparticles(system), " particles") +end + +function Base.show(io::IO, ::MIME"text/plain", system::OpenBoundarySPHSystem) + @nospecialize system # reduce precompilation time + + if get(io, :compact, false) + show(io, system) + else + summary_header(io, "OpenBoundarySPHSystem{$(ndims(system))}") + summary_line(io, "#particles", nparticles(system)) + summary_line(io, "#buffer_particles", system.buffer.buffer_size) + summary_line(io, "fluid system", type2string(system.fluid_system)) + summary_line(io, "boundary", type2string(system.boundary_zone)) + summary_line(io, "flow direction", system.flow_direction) + summary_line(io, "prescribed velocity", string(nameof(system.reference_velocity))) + summary_line(io, "prescribed pressure", string(nameof(system.reference_pressure))) + summary_line(io, "prescribed density", string(nameof(system.reference_density))) + summary_line(io, "width", round(system.boundary_zone.zone_width, digits=3)) + summary_footer(io) + end +end + +function reset_callback_flag(system::OpenBoundarySPHSystem) + system.update_callback_used[] = false + + return system +end + +update_callback_used!(system::OpenBoundarySPHSystem) = system.update_callback_used[] = true + +@inline source_terms(system::OpenBoundarySPHSystem) = nothing + +@inline hydrodynamic_mass(system::OpenBoundarySPHSystem, particle) = system.mass[particle] + +@inline function particle_density(v, system::OpenBoundarySPHSystem, particle) + return system.density[particle] +end + +@inline function particle_pressure(v, system::OpenBoundarySPHSystem, particle) + return system.pressure[particle] +end + +@inline function update_quantities!(system::OpenBoundarySPHSystem, v, u, t) + (; density, pressure, characteristics, flow_direction, sound_speed, + reference_velocity, reference_pressure, reference_density) = system + + # Update quantities based on the characteristic variables + @threaded for particle in each_moving_particle(system) + particle_position = current_coords(u, system, particle) + + J1 = characteristics[1, particle] + J2 = characteristics[2, particle] + J3 = characteristics[3, particle] + + rho_ref = reference_density(particle_position, t) + density[particle] = rho_ref + ((-J1 + 0.5 * (J2 + J3)) / sound_speed^2) + + p_ref = reference_pressure(particle_position, t) + pressure[particle] = p_ref + 0.5 * (J2 + J3) + + v_ref = reference_velocity(particle_position, t) + rho = density[particle] + v_ = v_ref + ((J2 - J3) / (2 * sound_speed * rho)) * flow_direction + + for dim in 1:ndims(system) + v[dim, particle] = v_[dim] + end + end + + return system +end + +function update_final!(system::OpenBoundarySPHSystem, v, u, v_ode, u_ode, semi, t; + update_from_callback=false) + if !update_from_callback && !(system.update_callback_used[]) + throw(ArgumentError("`UpdateCallback` is required when using `OpenBoundarySPHSystem`")) + end + + @trixi_timeit timer() "evaluate characteristics" evaluate_characteristics!(system, v, u, + v_ode, u_ode, + semi, t) +end + +# ==== Characteristics +# J1: Associated with convection and entropy and propagates at flow velocity. +# J2: Propagates downstream to the local flow +# J3: Propagates upstream to the local flow +function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) + (; volume, characteristics, previous_characteristics, boundary_zone) = system + + for particle in eachparticle(system) + previous_characteristics[1, particle] = characteristics[1, particle] + previous_characteristics[2, particle] = characteristics[2, particle] + previous_characteristics[3, particle] = characteristics[3, particle] + end + + set_zero!(characteristics) + set_zero!(volume) + + # Evaluate the characteristic variables with the fluid system + evaluate_characteristics!(system, system.fluid_system, v, u, v_ode, u_ode, semi, t) + + # Only some of the in-/outlet particles are in the influence of the fluid particles. + # Thus, we compute the characteristics for the particles that are outside the influence + # of fluid particles by using the average of the values of the previous time step. + # See eq. 27 in Negi (2020) https://doi.org/10.1016/j.cma.2020.113119 + @threaded for particle in each_moving_particle(system) + + # Particle is outside of the influence of fluid particles + if isapprox(volume[particle], 0.0) + + # Using the average of the values at the previous time step for particles which + # are outside of the influence of fluid particles. + avg_J1 = 0.0 + avg_J2 = 0.0 + avg_J3 = 0.0 + counter = 0 + + for neighbor in each_moving_particle(system) + # Make sure that only neighbors in the influence of + # the fluid particles are used. + if volume[neighbor] > sqrt(eps()) + avg_J1 += previous_characteristics[1, neighbor] + avg_J2 += previous_characteristics[2, neighbor] + avg_J3 += previous_characteristics[3, neighbor] + counter += 1 + end + end + + characteristics[1, particle] = avg_J1 / counter + characteristics[2, particle] = avg_J2 / counter + characteristics[3, particle] = avg_J3 / counter + else + characteristics[1, particle] /= volume[particle] + characteristics[2, particle] /= volume[particle] + characteristics[3, particle] /= volume[particle] + end + prescribe_conditions!(characteristics, particle, boundary_zone) + end + + return system +end + +function evaluate_characteristics!(system, neighbor_system::FluidSystem, + v, u, v_ode, u_ode, semi, t) + (; volume, sound_speed, characteristics, flow_direction, + reference_velocity, reference_pressure, reference_density) = system + + v_neighbor_system = wrap_v(v_ode, neighbor_system, semi) + u_neighbor_system = wrap_u(u_ode, neighbor_system, semi) + + nhs = get_neighborhood_search(system, neighbor_system, semi) + + system_coords = current_coordinates(u, system) + neighbor_coords = current_coordinates(u_neighbor_system, neighbor_system) + + # Loop over all fluid neighbors within the kernel cutoff + for_particle_neighbor(system, neighbor_system, system_coords, neighbor_coords, + nhs) do particle, neighbor, pos_diff, distance + neighbor_position = current_coords(u_neighbor_system, neighbor_system, neighbor) + + # Determine current and prescribed quantities + rho_b = particle_density(v_neighbor_system, neighbor_system, neighbor) + rho_ref = reference_density(neighbor_position, t) + + p_b = particle_pressure(v_neighbor_system, neighbor_system, neighbor) + p_ref = reference_pressure(neighbor_position, t) + + v_b = current_velocity(v_neighbor_system, neighbor_system, neighbor) + v_neighbor_ref = reference_velocity(neighbor_position, t) + + # Determine characteristic variables + density_term = -sound_speed^2 * (rho_b - rho_ref) + pressure_term = p_b - p_ref + velocity_term = rho_b * sound_speed * (dot(v_b - v_neighbor_ref, flow_direction)) + + kernel_ = smoothing_kernel(neighbor_system, distance) + + characteristics[1, particle] += (density_term + pressure_term) * kernel_ + characteristics[2, particle] += (velocity_term + pressure_term) * kernel_ + characteristics[3, particle] += (-velocity_term + pressure_term) * kernel_ + + volume[particle] += kernel_ + end + + return system +end + +@inline function prescribe_conditions!(characteristics, particle, ::OutFlow) + # J3 is prescribed (i.e. determined from the exterior of the domain). + # J1 and J2 is transimtted from the domain interior. + characteristics[3, particle] = zero(eltype(characteristics)) + + return characteristics +end + +@inline function prescribe_conditions!(characteristics, particle, ::InFlow) + # Allow only J3 to propagate upstream to the boundary + characteristics[1, particle] = zero(eltype(characteristics)) + characteristics[2, particle] = zero(eltype(characteristics)) + + return characteristics +end + +# This function is called by the `UpdateCallback`, as the integrator array might be modified +function update_open_boundary_eachstep!(system::OpenBoundarySPHSystem, v_ode, u_ode, + semi, t) + u = wrap_u(u_ode, system, semi) + v = wrap_v(v_ode, system, semi) + + # Update density, pressure and velocity based on the characteristic variables. + # See eq. 13-15 in Lastiwka (2009) https://doi.org/10.1002/fld.1971 + @trixi_timeit timer() "update quantities" update_quantities!(system, v, u, t) + + @trixi_timeit timer() "check domain" check_domain!(system, v, u, v_ode, u_ode, semi) + + # Update buffers + update_system_buffer!(system.buffer) + update_system_buffer!(system.fluid_system.buffer) +end + +update_open_boundary_eachstep!(system, v_ode, u_ode, semi, t) = system + +function check_domain!(system, v, u, v_ode, u_ode, semi) + (; boundary_zone, fluid_system) = system + + u_fluid = wrap_u(u_ode, fluid_system, semi) + v_fluid = wrap_v(v_ode, fluid_system, semi) + + neighborhood_search = get_neighborhood_search(system, fluid_system, semi) + + for particle in each_moving_particle(system) + particle_coords = current_coords(u, system, particle) + + # Check if boundary particle is outside the boundary zone + if !is_in_boundary_zone(boundary_zone, particle_coords) + convert_particle!(system, fluid_system, boundary_zone, particle, + v, u, v_fluid, u_fluid) + end + + # Check the neighboring fluid particles whether they're entering the boundary zone + for neighbor in PointNeighbors.eachneighbor(particle_coords, neighborhood_search) + fluid_coords = current_coords(u_fluid, fluid_system, neighbor) + + # Check if neighboring fluid particle is in boundary zone + if is_in_boundary_zone(boundary_zone, fluid_coords) + convert_particle!(fluid_system, system, boundary_zone, neighbor, + v, u, v_fluid, u_fluid) + end + end + end + + return system +end + +# Outflow particle is outside the boundary zone +@inline function convert_particle!(system::OpenBoundarySPHSystem, fluid_system, + boundary_zone::OutFlow, particle, v, u, + v_fluid, u_fluid) + deactivate_particle!(system, particle, u) + + return system +end + +# Inflow particle is outside the boundary zone +@inline function convert_particle!(system::OpenBoundarySPHSystem, fluid_system, + boundary_zone::InFlow, particle, v, u, + v_fluid, u_fluid) + (; spanning_set) = boundary_zone + + # Activate a new particle in simulation domain + transfer_particle!(fluid_system, system, particle, v_fluid, u_fluid, v, u) + + # Reset position of boundary particle + for dim in 1:ndims(system) + u[dim, particle] += spanning_set[1][dim] + end + + return system +end + +# Fluid particle is in boundary zone +@inline function convert_particle!(fluid_system::FluidSystem, system, + boundary_zone, particle, v, u, v_fluid, u_fluid) + # Activate particle in boundary zone + transfer_particle!(system, fluid_system, particle, v, u, v_fluid, u_fluid) + + # Deactivate particle in interior domain + deactivate_particle!(fluid_system, particle, u_fluid) + + return fluid_system +end + +@inline function transfer_particle!(system_new, system_old, particle_old, + v_new, u_new, v_old, u_old) + particle_new = activate_next_particle(system_new) + + # Transfer densities + density = particle_density(v_old, system_old, particle_old) + set_particle_density!(v_new, system_new, particle_new, density) + + # Transfer pressure + pressure = particle_pressure(v_old, system_old, particle_old) + set_particle_pressure!(v_new, system_new, particle_new, pressure) + + # Exchange position and velocity + for dim in 1:ndims(system_new) + u_new[dim, particle_new] = u_old[dim, particle_old] + v_new[dim, particle_new] = v_old[dim, particle_old] + end + + # TODO: Only when using TVF: set tvf + + return system_new +end + +function write_v0!(v0, system::OpenBoundarySPHSystem) + (; initial_condition) = system + + for particle in eachparticle(system) + # Write particle velocities + for dim in 1:ndims(system) + v0[dim, particle] = initial_condition.velocity[dim, particle] + end + end + + return v0 +end + +function write_u0!(u0, system::OpenBoundarySPHSystem) + (; initial_condition) = system + + for particle in eachparticle(system) + # Write particle velocities + for dim in 1:ndims(system) + u0[dim, particle] = initial_condition.coordinates[dim, particle] + end + end + + return u0 +end + +function wrap_reference_function(function_::Function, ::Val) + # Already a function + return function_ +end + +# Name the function so that the summary box does know which kind of function this is +function wrap_reference_function(constant_scalar_::Number, ::Val) + return constant_scalar(coords, t) = constant_scalar_ +end + +# For vectors and tuples +# Name the function so that the summary box does know which kind of function this is +function wrap_reference_function(constant_vector_, ::Val{NDIMS}) where {NDIMS} + return constant_vector(coords, t) = SVector{NDIMS}(constant_vector_) +end diff --git a/src/schemes/boundary/rhs.jl b/src/schemes/boundary/rhs.jl index 201a3bbb9..d9cfb1779 100644 --- a/src/schemes/boundary/rhs.jl +++ b/src/schemes/boundary/rhs.jl @@ -1,7 +1,7 @@ -# Interaction of boundary with other systems +# Interaction of boundary with other systems function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, neighborhood_search, - particle_system::BoundarySystem, + particle_system::Union{BoundarySystem, OpenBoundarySPHSystem}, neighbor_system) # TODO Solids and moving boundaries should be considered in the continuity equation return dv diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl index cade5fbe9..73af6a843 100644 --- a/src/schemes/boundary/system.jl +++ b/src/schemes/boundary/system.jl @@ -13,7 +13,8 @@ The interaction between fluid and boundary particles is specified by the boundar - `adhesion_coefficient`: Coefficient specifying the adhesion of a fluid to the surface. Note: currently it is assumed that all fluids have the same adhesion coefficient. """ -struct BoundarySPHSystem{BM, NDIMS, ELTYPE, IC, CO, M, IM, CA} <: BoundarySystem{NDIMS, IC} +struct BoundarySPHSystem{BM, NDIMS, ELTYPE <: Real, IC, CO, M, IM, + CA} <: BoundarySystem{NDIMS, IC} initial_condition :: IC coordinates :: CO # Array{ELTYPE, 2} boundary_model :: BM @@ -21,6 +22,7 @@ struct BoundarySPHSystem{BM, NDIMS, ELTYPE, IC, CO, M, IM, CA} <: BoundarySystem ismoving :: IM # Ref{Bool} (to make a mutable field compatible with GPUs) adhesion_coefficient :: ELTYPE cache :: CA + buffer :: Nothing # This constructor is necessary for Adapt.jl to work with this struct. # See the comments in general/gpu.jl for more details. @@ -28,12 +30,10 @@ struct BoundarySPHSystem{BM, NDIMS, ELTYPE, IC, CO, M, IM, CA} <: BoundarySystem ismoving, adhesion_coefficient, cache) ELTYPE = eltype(coordinates) - new{typeof(boundary_model), size(coordinates, 1), ELTYPE, - typeof(initial_condition), typeof(coordinates), - typeof(movement), typeof(ismoving), typeof(cache)}(initial_condition, - coordinates, boundary_model, - movement, ismoving, - adhesion_coefficient, cache) + new{typeof(boundary_model), size(coordinates, 1), ELTYPE, typeof(initial_condition), + typeof(coordinates), typeof(movement), typeof(ismoving), + typeof(cache)}(initial_condition, coordinates, boundary_model, movement, + ismoving, adhesion_coefficient, cache, nothing) end end @@ -73,6 +73,7 @@ struct BoundaryDEMSystem{NDIMS, ELTYPE <: Real, IC, coordinates :: ARRAY2D # [dimension, particle] radius :: ARRAY1D # [particle] normal_stiffness :: ELTYPE + buffer :: Nothing function BoundaryDEMSystem(initial_condition, normal_stiffness) coordinates = initial_condition.coordinates @@ -80,9 +81,9 @@ struct BoundaryDEMSystem{NDIMS, ELTYPE <: Real, IC, ones(length(initial_condition.mass)) NDIMS = size(coordinates, 1) - return new{NDIMS, eltype(coordinates), typeof(initial_condition), - typeof(radius), typeof(coordinates)}(initial_condition, coordinates, - radius, normal_stiffness) + return new{NDIMS, eltype(coordinates), typeof(initial_condition), typeof(radius), + typeof(coordinates)}(initial_condition, coordinates, radius, + normal_stiffness, nothing) end end @@ -335,7 +336,8 @@ end # This update depends on the computed quantities of the fluid system and therefore # has to be in `update_final!` after `update_quantities!`. -function update_final!(system::BoundarySPHSystem, v, u, v_ode, u_ode, semi, t) +function update_final!(system::BoundarySPHSystem, v, u, v_ode, u_ode, semi, t; + update_from_callback=false) (; boundary_model) = system update_pressure!(boundary_model, system, v, u, v_ode, u_ode, semi) diff --git a/src/schemes/fluid/entropically_damped_sph/system.jl b/src/schemes/fluid/entropically_damped_sph/system.jl index f59bca7d3..4093e8ccb 100644 --- a/src/schemes/fluid/entropically_damped_sph/system.jl +++ b/src/schemes/fluid/entropically_damped_sph/system.jl @@ -4,7 +4,7 @@ pressure_acceleration=inter_particle_averaged_pressure, density_calculator=SummationDensity(), alpha=0.5, viscosity=nothing, - acceleration=ntuple(_ -> 0.0, NDIMS), + acceleration=ntuple(_ -> 0.0, NDIMS), buffer_size=nothing, source_terms=nothing) System for particles of a fluid. @@ -28,6 +28,8 @@ See [Entropically Damped Artificial Compressibility for SPH](@ref edac) for more When set to `nothing`, the pressure acceleration formulation for the corresponding [density calculator](@ref density_calculator) is chosen. - `density_calculator`: [Density calculator](@ref density_calculator) (default: [`SummationDensity`](@ref)) +- `buffer_size`: Number of buffer particles. + This is needed when simulating with [`OpenBoundarySPHSystem`](@ref). - `source_terms`: Additional source terms for this system. Has to be either `nothing` (by default), or a function of `(coords, velocity, density, pressure)` (which are the quantities of a single particle), returning a `Tuple` @@ -40,7 +42,7 @@ See [Entropically Damped Artificial Compressibility for SPH](@ref edac) for more gravity-like source terms. """ struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, IC, M, DC, K, V, - PF, ST, C} <: FluidSystem{NDIMS, IC} + PF, ST, B, C} <: FluidSystem{NDIMS, IC} initial_condition :: IC mass :: M # Vector{ELTYPE}: [particle] density_calculator :: DC @@ -53,6 +55,7 @@ struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, IC, M, DC, K, V, correction :: Nothing pressure_acceleration_formulation :: PF source_terms :: ST + buffer :: B cache :: C function EntropicallyDampedSPHSystem(initial_condition, smoothing_kernel, @@ -62,7 +65,12 @@ struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, IC, M, DC, K, V, alpha=0.5, viscosity=nothing, acceleration=ntuple(_ -> 0.0, ndims(smoothing_kernel)), - source_terms=nothing) + source_terms=nothing, buffer_size=nothing) + buffer = isnothing(buffer_size) ? nothing : + SystemBuffer(nparticles(initial_condition), buffer_size) + + initial_condition = allocate_buffer(initial_condition, buffer) + NDIMS = ndims(initial_condition) ELTYPE = eltype(initial_condition) @@ -89,9 +97,11 @@ struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, IC, M, DC, K, V, new{NDIMS, ELTYPE, typeof(initial_condition), typeof(mass), typeof(density_calculator), typeof(smoothing_kernel), typeof(viscosity), typeof(pressure_acceleration), typeof(source_terms), + typeof(buffer), typeof(cache)}(initial_condition, mass, density_calculator, smoothing_kernel, - smoothing_length, sound_speed, viscosity, nu_edac, acceleration_, - nothing, pressure_acceleration, source_terms, cache) + smoothing_length, sound_speed, viscosity, nu_edac, + acceleration_, nothing, pressure_acceleration, source_terms, + buffer, cache) end end @@ -113,7 +123,12 @@ function Base.show(io::IO, ::MIME"text/plain", system::EntropicallyDampedSPHSyst show(io, system) else summary_header(io, "EntropicallyDampedSPHSystem{$(ndims(system))}") - summary_line(io, "#particles", nparticles(system)) + if system.buffer isa SystemBuffer + summary_line(io, "#particles", nparticles(system)) + summary_line(io, "#buffer_particles", system.buffer.buffer_size) + else + summary_line(io, "#particles", nparticles(system)) + end summary_line(io, "density calculator", system.density_calculator |> typeof |> nameof) summary_line(io, "viscosity", system.viscosity |> typeof |> nameof) @@ -145,6 +160,18 @@ end return v[end, particle] end +# WARNING! +# These functions are intended to be used internally to set the pressure +# of newly activated particles in a callback. +# DO NOT use outside a callback. OrdinaryDiffEq does not allow changing `v` and `u` +# outside of callbacks. +@inline function set_particle_pressure!(v, system::EntropicallyDampedSPHSystem, particle, + pressure) + v[end, particle] = pressure + + return v +end + @inline system_sound_speed(system::EntropicallyDampedSPHSystem) = system.sound_speed function update_quantities!(system::EntropicallyDampedSPHSystem, v, u, diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index 056662bbc..68391b285 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -1,3 +1,7 @@ +@inline function set_particle_density!(v, system::FluidSystem, particle, density) + set_particle_density!(v, system, system.density_calculator, particle, density) +end + function create_cache_density(initial_condition, ::SummationDensity) density = similar(initial_condition.density) diff --git a/src/schemes/fluid/viscosity.jl b/src/schemes/fluid/viscosity.jl index 554f41c14..a678b40d4 100644 --- a/src/schemes/fluid/viscosity.jl +++ b/src/schemes/fluid/viscosity.jl @@ -11,6 +11,19 @@ function dv_viscosity(particle_system, neighbor_system, sound_speed, m_a, m_b, rho_mean) end +function dv_viscosity(particle_system, neighbor_system::OpenBoundarySPHSystem, + v_particle_system, v_neighbor_system, + particle, neighbor, pos_diff, distance, + sound_speed, m_a, m_b, rho_mean) + # No viscosity in the open boundary system. Use viscosity of the fluid system. + viscosity = viscosity_model(particle_system) + + return dv_viscosity(viscosity, particle_system, neighbor_system, + v_particle_system, v_neighbor_system, + particle, neighbor, pos_diff, distance, + sound_speed, m_a, m_b, rho_mean) +end + function dv_viscosity(viscosity, particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index cb9a1da87..a8e28670b 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -4,6 +4,7 @@ smoothing_kernel, smoothing_length; viscosity=nothing, density_diffusion=nothing, acceleration=ntuple(_ -> 0.0, NDIMS), + buffer_size=nothing, correction=nothing, source_terms=nothing) System for particles of a fluid. @@ -26,6 +27,8 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method. See [`ArtificialViscosityMonaghan`](@ref) or [`ViscosityAdami`](@ref). - `density_diffusion`: Density diffusion terms for this system. See [`DensityDiffusion`](@ref). - `acceleration`: Acceleration vector for the system. (default: zero vector) +- `buffer_size`: Number of buffer particles. + This is needed when simulating with [`OpenBoundarySPHSystem`](@ref). - `correction`: Correction method used for this system. (default: no correction, see [Corrections](@ref corrections)) - `source_terms`: Additional source terms for this system. Has to be either `nothing` (by default), or a function of `(coords, velocity, density, pressure)` @@ -42,7 +45,7 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method. """ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, - V, DD, COR, PF, ST, SRFT, C} <: FluidSystem{NDIMS, IC} + V, DD, COR, PF, ST, B, SRFT, C} <: FluidSystem{NDIMS, IC} initial_condition :: IC mass :: MA # Array{ELTYPE, 1} pressure :: P # Array{ELTYPE, 1} @@ -57,6 +60,7 @@ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, pressure_acceleration_formulation :: PF source_terms :: ST surface_tension :: SRFT + buffer :: B cache :: C end @@ -66,11 +70,17 @@ function WeaklyCompressibleSPHSystem(initial_condition, density_calculator, state_equation, smoothing_kernel, smoothing_length; pressure_acceleration=nothing, + buffer_size=nothing, viscosity=nothing, density_diffusion=nothing, acceleration=ntuple(_ -> 0.0, ndims(smoothing_kernel)), correction=nothing, source_terms=nothing, surface_tension=nothing) + buffer = isnothing(buffer_size) ? nothing : + SystemBuffer(nparticles(initial_condition), buffer_size) + + initial_condition = allocate_buffer(initial_condition, buffer) + NDIMS = ndims(initial_condition) ELTYPE = eltype(initial_condition) n_particles = nparticles(initial_condition) @@ -108,10 +118,11 @@ function WeaklyCompressibleSPHSystem(initial_condition, return WeaklyCompressibleSPHSystem(initial_condition, mass, pressure, density_calculator, state_equation, - smoothing_kernel, smoothing_length, acceleration_, - viscosity, density_diffusion, correction, - pressure_acceleration, source_terms, surface_tension, - cache) + smoothing_kernel, smoothing_length, + acceleration_, viscosity, + density_diffusion, correction, + pressure_acceleration, + source_terms, surface_tension, buffer, cache) end create_cache_wcsph(correction, density, NDIMS, nparticles) = (;) @@ -166,7 +177,12 @@ function Base.show(io::IO, ::MIME"text/plain", system::WeaklyCompressibleSPHSyst show(io, system) else summary_header(io, "WeaklyCompressibleSPHSystem{$(ndims(system))}") - summary_line(io, "#particles", nparticles(system)) + if system.buffer isa SystemBuffer + summary_line(io, "#particles", nparticles(system)) + summary_line(io, "#buffer_particles", system.buffer.buffer_size) + else + summary_line(io, "#particles", nparticles(system)) + end summary_line(io, "density calculator", system.density_calculator |> typeof |> nameof) summary_line(io, "correction method", diff --git a/src/schemes/schemes.jl b/src/schemes/schemes.jl index b81a7768b..fadcb240e 100644 --- a/src/schemes/schemes.jl +++ b/src/schemes/schemes.jl @@ -1,5 +1,8 @@ # Include all schemes without rhs first. The rhs depends on the systems to define # interactions between the different system types. +# Viscosity requires the open boundary system. +include("boundary/open_boundary/boundary_zones.jl") +include("boundary/open_boundary/system.jl") include("fluid/fluid.jl") include("boundary/boundary.jl") include("solid/total_lagrangian_sph/total_lagrangian_sph.jl") diff --git a/src/schemes/solid/discrete_element_method/system.jl b/src/schemes/solid/discrete_element_method/system.jl index 5aadfcfcc..eb30dfb34 100644 --- a/src/schemes/solid/discrete_element_method/system.jl +++ b/src/schemes/solid/discrete_element_method/system.jl @@ -36,6 +36,7 @@ struct DEMSystem{NDIMS, ELTYPE <: Real, IC, ARRAY1D, ST} <: SolidSystem{NDIMS, I damping_coefficient :: ELTYPE acceleration :: SVector{NDIMS, ELTYPE} source_terms :: ST + buffer :: Nothing function DEMSystem(initial_condition, normal_stiffness, elastic_modulus, poissons_ratio; damping_coefficient=0.0001, @@ -56,7 +57,8 @@ struct DEMSystem{NDIMS, ELTYPE <: Real, IC, ARRAY1D, ST} <: SolidSystem{NDIMS, I return new{NDIMS, ELTYPE, typeof(initial_condition), typeof(mass), typeof(source_terms)}(initial_condition, mass, radius, elastic_modulus, poissons_ratio, normal_stiffness, - damping_coefficient, acceleration_, source_terms) + damping_coefficient, acceleration_, source_terms, + nothing) end end diff --git a/src/schemes/solid/total_lagrangian_sph/system.jl b/src/schemes/solid/total_lagrangian_sph/system.jl index 5b3021b1c..9203425fb 100644 --- a/src/schemes/solid/total_lagrangian_sph/system.jl +++ b/src/schemes/solid/total_lagrangian_sph/system.jl @@ -69,6 +69,7 @@ struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D, boundary_model :: BM penalty_force :: PF source_terms :: ST + buffer :: Nothing function TotalLagrangianSPHSystem(initial_condition, smoothing_kernel, smoothing_length, @@ -116,7 +117,7 @@ struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D, n_moving_particles, young_modulus, poisson_ratio, lame_lambda, lame_mu, smoothing_kernel, smoothing_length, acceleration_, boundary_model, - penalty_force, source_terms) + penalty_force, source_terms, nothing) end end @@ -246,7 +247,8 @@ function update_quantities!(system::TotalLagrangianSPHSystem, v, u, v_ode, u_ode return system end -function update_final!(system::TotalLagrangianSPHSystem, v, u, v_ode, u_ode, semi, t) +function update_final!(system::TotalLagrangianSPHSystem, v, u, v_ode, u_ode, semi, t; + update_from_callback=false) (; boundary_model) = system # Only update boundary model diff --git a/src/setups/extrude_geometry.jl b/src/setups/extrude_geometry.jl index 9b7a62401..82df9aaa3 100644 --- a/src/setups/extrude_geometry.jl +++ b/src/setups/extrude_geometry.jl @@ -8,8 +8,10 @@ Extrude either a line, a plane or a shape along a specific direction. # Arguments - `geometry`: Either particle coordinates or an [`InitialCondition`](@ref) defining a 2D shape to extrude to a 3D volume, or two 2D points - defining a line to extrude to a plane in 2D, or three 3D points defining - a parallelogram to extrude to a parallelepiped. + ``(A, B)`` defining the interval ``[A, B]`` to extrude to a plane + in 2D, or three 3D points ``(A, B, C)`` defining the parallelogram + spanned by the vectors ``\widehat{AB}`` and ``\widehat {AC}`` to extrude + to a parallelepiped. # Keywords - `particle_spacing`: Spacing between the particles. Can be omitted when `geometry` is an diff --git a/src/visualization/recipes_plots.jl b/src/visualization/recipes_plots.jl index a3fb7de7e..f7e4d5b23 100644 --- a/src/visualization/recipes_plots.jl +++ b/src/visualization/recipes_plots.jl @@ -12,10 +12,8 @@ end RecipesBase.@recipe function f(v_ode, u_ode, semi::Semidiscretization; size=(600, 400)) # Default size systems_data = map(semi.systems) do system - (; initial_condition) = system - u = wrap_u(u_ode, system, semi) - coordinates = current_coordinates(u, system) + coordinates = active_coordinates(u, system) x = collect(coordinates[1, :]) y = collect(coordinates[2, :]) @@ -24,8 +22,8 @@ RecipesBase.@recipe function f(v_ode, u_ode, semi::Semidiscretization; particle_spacing = 0.0 end - x_min, y_min = minimum(initial_condition.coordinates, dims=2) .- 0.5particle_spacing - x_max, y_max = maximum(initial_condition.coordinates, dims=2) .+ 0.5particle_spacing + x_min, y_min = minimum(coordinates, dims=2) .- 0.5particle_spacing + x_max, y_max = maximum(coordinates, dims=2) .+ 0.5particle_spacing return (; x, y, x_min, x_max, y_min, y_max, particle_spacing, label=timer_name(system)) diff --git a/src/visualization/write2vtk.jl b/src/visualization/write2vtk.jl index 2a4c9f207..0372e7167 100644 --- a/src/visualization/write2vtk.jl +++ b/src/visualization/write2vtk.jl @@ -56,7 +56,8 @@ function trixi2vtk(vu_ode, semi, t; iter=nothing, output_directory="out", prefix # Update quantities that are stored in the systems. These quantities (e.g. pressure) # still have the values from the last stage of the previous step if not updated here. - @trixi_timeit timer() "update systems" update_systems_and_nhs(v_ode, u_ode, semi, t) + @trixi_timeit timer() "update systems" update_systems_and_nhs(v_ode, u_ode, semi, t; + update_from_callback=true) filenames = system_names(systems) @@ -94,7 +95,7 @@ function trixi2vtk(v, u, t, system, periodic_box; output_directory="out", prefix # Reset the collection when the iteration is 0 pvd = paraview_collection(collection_file; append=iter > 0) - points = PointNeighbors.periodic_coords(current_coordinates(u, system), + points = PointNeighbors.periodic_coords(active_coordinates(u, system), periodic_box) cells = [MeshCell(VTKCellTypes.VTK_VERTEX, (i,)) for i in axes(points, 2)] @@ -112,7 +113,7 @@ function trixi2vtk(v, u, t, system, periodic_box; output_directory="out", prefix write2vtk!(vtk, v, u, t, system, write_meta_data=write_meta_data) # Store particle index - vtk["index"] = eachparticle(system) + vtk["index"] = active_particles(system) vtk["time"] = t if write_meta_data @@ -216,11 +217,12 @@ function write2vtk!(vtk, v, u, t, system; write_meta_data=true) end function write2vtk!(vtk, v, u, t, system::FluidSystem; write_meta_data=true) - vtk["velocity"] = view(v, 1:ndims(system), :) + vtk["velocity"] = [current_velocity(v, system, particle) + for particle in active_particles(system)] vtk["density"] = [particle_density(v, system, particle) - for particle in eachparticle(system)] + for particle in active_particles(system)] vtk["pressure"] = [particle_pressure(v, system, particle) - for particle in eachparticle(system)] + for particle in active_particles(system)] if write_meta_data vtk["acceleration"] = system.acceleration @@ -295,6 +297,37 @@ function write2vtk!(vtk, v, u, t, system::TotalLagrangianSPHSystem; write_meta_d write2vtk!(vtk, v, u, t, system.boundary_model, system, write_meta_data=write_meta_data) end +function write2vtk!(vtk, v, u, t, system::OpenBoundarySPHSystem; write_meta_data=true) + (; reference_velocity, reference_pressure, reference_density) = system + + vtk["velocity"] = [current_velocity(v, system, particle) + for particle in active_particles(system)] + vtk["density"] = [particle_density(v, system, particle) + for particle in active_particles(system)] + vtk["pressure"] = [particle_pressure(v, system, particle) + for particle in active_particles(system)] + + NDIMS = ndims(system) + ELTYPE = eltype(system) + coords = reinterpret(reshape, SVector{NDIMS, ELTYPE}, active_coordinates(u, system)) + + vtk["prescribed_velocity"] = stack(reference_velocity.(coords, t)) + vtk["prescribed_density"] = reference_density.(coords, t) + vtk["prescribed_pressure"] = reference_pressure.(coords, t) + + if write_meta_data + vtk["boundary_zone"] = type2string(system.boundary_zone) + vtk["sound_speed"] = system.sound_speed + vtk["width"] = round(system.boundary_zone.zone_width, digits=3) + vtk["flow_direction"] = system.flow_direction + vtk["velocity_function"] = string(nameof(system.reference_velocity)) + vtk["pressure_function"] = string(nameof(system.reference_pressure)) + vtk["density_function"] = string(nameof(system.reference_density)) + end + + return vtk +end + function write2vtk!(vtk, v, u, t, system::BoundarySPHSystem; write_meta_data=true) write2vtk!(vtk, v, u, t, system.boundary_model, system, write_meta_data=write_meta_data) end diff --git a/test/examples/examples.jl b/test/examples/examples.jl index 0f0032607..dbe03a130 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -114,6 +114,14 @@ @test count_rhs_allocations(sol, semi) == 0 end + @trixi_testset "fluid/pipe_flow_2d.jl" begin + @test_nowarn_mod trixi_include(@__MODULE__, tspan=(0.0, 0.5), + joinpath(examples_dir(), "fluid", + "pipe_flow_2d.jl")) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + @trixi_testset "fluid/dam_break_2d_surface_tension.jl" begin @test_nowarn_mod trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", diff --git a/test/general/buffer.jl b/test/general/buffer.jl new file mode 100644 index 000000000..7a8371ea7 --- /dev/null +++ b/test/general/buffer.jl @@ -0,0 +1,68 @@ +@testset verbose=true "`SystemBuffer`" begin + # Mock fluid system + struct FluidSystemMock3 <: TrixiParticles.FluidSystem{2, Nothing} end + + inflow = InFlow(; plane=([0.0, 0.0], [0.0, 1.0]), particle_spacing=0.2, + open_boundary_layers=2, density=1.0, flow_direction=[1.0, 0.0]) + system = OpenBoundarySPHSystem(inflow; sound_speed=1.0, fluid_system=FluidSystemMock3(), + buffer_size=0) + system_buffer = OpenBoundarySPHSystem(inflow; sound_speed=1.0, buffer_size=5, + fluid_system=FluidSystemMock3()) + + n_particles = nparticles(system) + + @testset "Iterators" begin + @test TrixiParticles.each_moving_particle(system) == 1:n_particles + + @test TrixiParticles.each_moving_particle(system_buffer) == 1:n_particles + + particle_id = TrixiParticles.activate_next_particle(system_buffer) + + TrixiParticles.update_system_buffer!(system_buffer.buffer) + + @test TrixiParticles.each_moving_particle(system_buffer) == 1:(n_particles + 1) + + TrixiParticles.deactivate_particle!(system_buffer, particle_id, + ones(2, particle_id)) + + TrixiParticles.update_system_buffer!(system_buffer.buffer) + + @test TrixiParticles.each_moving_particle(system_buffer) == 1:n_particles + + particle_id = 5 + TrixiParticles.deactivate_particle!(system_buffer, particle_id, + ones(2, particle_id)) + + TrixiParticles.update_system_buffer!(system_buffer.buffer) + + @test TrixiParticles.each_moving_particle(system_buffer) == + setdiff(1:n_particles, particle_id) + end + + @testset "Allocate Buffer" begin + initial_condition = rectangular_patch(0.1, (3, 3), perturbation_factor=0.0) + buffer = TrixiParticles.SystemBuffer(nparticles(initial_condition), 7) + + ic_with_buffer = TrixiParticles.allocate_buffer(initial_condition, buffer) + + @test nparticles(ic_with_buffer) == nparticles(initial_condition) + 7 + + masses = initial_condition.mass[1] .* ones(nparticles(ic_with_buffer)) + @test masses == ic_with_buffer.mass + + densities = initial_condition.density[1] .* ones(nparticles(ic_with_buffer)) + @test densities == ic_with_buffer.density + + pressures = initial_condition.pressure[1] .* ones(nparticles(ic_with_buffer)) + @test pressures == ic_with_buffer.pressure + + @testset "Illegal Input" begin + # The rectangular patch has a perturbed, non-constant density + ic = rectangular_patch(0.1, (3, 3)) + buffer = TrixiParticles.SystemBuffer(9, 7) + + error_str = "`initial_condition.density` needs to be constant when using `SystemBuffer`" + @test_throws ArgumentError(error_str) TrixiParticles.allocate_buffer(ic, buffer) + end + end +end diff --git a/test/general/general.jl b/test/general/general.jl index b59cff789..27f60ebba 100644 --- a/test/general/general.jl +++ b/test/general/general.jl @@ -3,3 +3,4 @@ include("smoothing_kernels.jl") include("density_calculator.jl") include("semidiscretization.jl") include("interpolation.jl") +include("buffer.jl") diff --git a/test/general/semidiscretization.jl b/test/general/semidiscretization.jl index 633a0e240..4aff1f4fd 100644 --- a/test/general/semidiscretization.jl +++ b/test/general/semidiscretization.jl @@ -131,6 +131,9 @@ v2 = zeros(4 * 3) v_ode = vcat(vec(v1), v2) + # Avoid `SystemBuffer` barrier + TrixiParticles.each_moving_particle(system::Union{System1, System2}) = TrixiParticles.eachparticle(system) + TrixiParticles.add_source_terms!(dv_ode, v_ode, u_ode, semi) dv1 = TrixiParticles.wrap_v(dv_ode, system1, semi) diff --git a/test/schemes/boundary/open_boundary/boundary_zone.jl b/test/schemes/boundary/open_boundary/boundary_zone.jl new file mode 100644 index 000000000..7a34a4bca --- /dev/null +++ b/test/schemes/boundary/open_boundary/boundary_zone.jl @@ -0,0 +1,221 @@ +@testset verbose=true "Boundary Zone" begin + @testset verbose=true "Boundary Zone 2D" begin + particle_spacing = 0.2 + open_boundary_layers = 4 + + plane_points_1 = [[0.0, 0.0], [0.5, -0.5], [1.0, 0.5]] + plane_points_2 = [[0.0, 1.0], [0.2, 2.0], [2.3, 0.5]] + + @testset verbose=true "Points $i" for i in eachindex(plane_points_1) + point_1 = plane_points_1[i] + point_2 = plane_points_2[i] + + plane_size = point_2 - point_1 + + flow_directions = [ + normalize([-plane_size[2], plane_size[1]]), + -normalize([-plane_size[2], plane_size[1]]), + ] + + @testset verbose=true "Flow Direction $j" for j in eachindex(flow_directions) + inflow = InFlow(; plane=(point_1, point_2), particle_spacing, + flow_direction=flow_directions[j], density=1.0, + open_boundary_layers) + outflow = OutFlow(; plane=(point_1, point_2), particle_spacing, + flow_direction=flow_directions[j], density=1.0, + open_boundary_layers) + + boundary_zones = [ + inflow, + outflow, + ] + + @testset verbose=true "$(nameof(typeof(boundary_zone)))" for boundary_zone in boundary_zones + zone_width = open_boundary_layers * + boundary_zone.initial_condition.particle_spacing + sign_ = (boundary_zone isa InFlow) ? -1 : 1 + + @test plane_points_1[i] == boundary_zone.zone_origin + @test plane_points_2[i] - boundary_zone.zone_origin == + boundary_zone.spanning_set[2] + @test isapprox(sign_ * flow_directions[j], + normalize(boundary_zone.spanning_set[1]), atol=1e-14) + @test isapprox(zone_width, norm(boundary_zone.spanning_set[1]), + atol=1e-14) + end + end + end + end + + @testset verbose=true "Boundary Zone 3D" begin + particle_spacing = 0.05 + open_boundary_layers = 4 + + plane_points_1 = [ + [0.0, 0.0, 0.0], + [0.3113730847835541, 0.19079485535621643, -0.440864622592926], + ] + plane_points_2 = [ + [1.0, 0.0, 0.0], + [-0.10468611121177673, 0.252103328704834, -0.44965094327926636], + ] + plane_points_3 = [ + [0.0, 1.0, 0.0], + [0.3113730847835541, 0.25057315826416016, -0.02374829351902008], + ] + + @testset verbose=true "Points $i" for i in eachindex(plane_points_1) + point_1 = plane_points_1[i] + point_2 = plane_points_2[i] + point_3 = plane_points_3[i] + + edge1 = point_2 - point_1 + edge2 = point_3 - point_1 + + flow_directions = [ + normalize(cross(edge1, edge2)), + -normalize(cross(edge1, edge2)), + ] + + @testset verbose=true "Flow Direction $j" for j in eachindex(flow_directions) + inflow = InFlow(; plane=(point_1, point_2, point_3), particle_spacing, + flow_direction=flow_directions[j], density=1.0, + open_boundary_layers) + outflow = OutFlow(; plane=(point_1, point_2, point_3), particle_spacing, + flow_direction=flow_directions[j], density=1.0, + open_boundary_layers) + + boundary_zones = [ + inflow, + outflow, + ] + + @testset verbose=true "$(nameof(typeof(boundary_zone)))" for boundary_zone in boundary_zones + zone_width = open_boundary_layers * + boundary_zone.initial_condition.particle_spacing + sign_ = (boundary_zone isa InFlow) ? -1 : 1 + + @test plane_points_1[i] == boundary_zone.zone_origin + @test plane_points_2[i] - boundary_zone.zone_origin == + boundary_zone.spanning_set[2] + @test plane_points_3[i] - boundary_zone.zone_origin == + boundary_zone.spanning_set[3] + @test isapprox(sign_ * flow_directions[j], + normalize(boundary_zone.spanning_set[1]), atol=1e-14) + @test isapprox(zone_width, norm(boundary_zone.spanning_set[1]), + atol=1e-14) + end + end + end + end + + @testset verbose=true "Particle In Boundary Zone 2D" begin + plane_points = [[-0.2, -0.5], [0.3, 0.6]] + plane_size = plane_points[2] - plane_points[1] + + flow_direction = normalize([-plane_size[2], plane_size[1]]) + + inflow = InFlow(; plane=plane_points, particle_spacing=0.1, + flow_direction, density=1.0, open_boundary_layers=4) + outflow = OutFlow(; plane=plane_points, particle_spacing=0.1, + flow_direction, density=1.0, open_boundary_layers=4) + + boundary_zones = [ + inflow, + outflow, + ] + + @testset verbose=true "$(nameof(typeof(boundary_zone)))" for boundary_zone in boundary_zones + perturb_ = boundary_zone isa InFlow ? sqrt(eps()) : -sqrt(eps()) + + point1 = plane_points[1] + point2 = plane_points[2] + point3 = boundary_zone.spanning_set[1] + boundary_zone.zone_origin + + query_points = Dict( + "Behind" => ([-1.0, -1.0], false), + "Before" => ([2.0, 2.0], false), + "Closely On Point 1" => (point1 + perturb_ * flow_direction, false), + "Closely On Point 2" => (point2 + perturb_ * flow_direction, false), + "Closely On Point 3" => (point3 - perturb_ * flow_direction, false)) + + @testset verbose=true "$k" for k in keys(query_points) + (particle_position, evaluation) = query_points[k] + + @test evaluation == + TrixiParticles.is_in_boundary_zone(boundary_zone, particle_position) + end + end + end + + @testset verbose=true "Particle In Boundary Zone 3D" begin + point1 = [-0.2, -0.5, 0.0] + point2 = [0.3, 0.5, 0.0] + point3 = [0.13111173850909402, -0.665555869254547, 0.0] + + flow_direction = normalize(cross(point2 - point1, point3 - point1)) + + inflow = InFlow(; plane=[point1, point2, point3], particle_spacing=0.1, + flow_direction, density=1.0, open_boundary_layers=4) + outflow = OutFlow(; plane=[point1, point2, point3], particle_spacing=0.1, + flow_direction, density=1.0, open_boundary_layers=4) + + boundary_zones = [ + inflow, + outflow, + ] + + @testset verbose=true "$(nameof(typeof(boundary_zone)))" for boundary_zone in boundary_zones + perturb_ = boundary_zone isa InFlow ? eps() : -eps() + point4 = boundary_zone.spanning_set[1] + boundary_zone.zone_origin + + query_points = Dict( + "Behind" => ([-1.0, -1.0, 1.2], false), + "Before" => ([2.0, 2.0, -1.2], false), + "Closely On Point 1" => (point1 + perturb_ * flow_direction, false), + "Closely On Point 2" => (point2 + perturb_ * flow_direction, false), + "Closely On Point 3" => (point3 + perturb_ * flow_direction, false), + "Closely On Point 4" => (point4 - perturb_ * flow_direction, false)) + + @testset verbose=true "$k" for k in keys(query_points) + (particle_position, evaluation) = query_points[k] + + @test evaluation == + TrixiParticles.is_in_boundary_zone(boundary_zone, particle_position) + end + end + end + + @testset verbose=true "Illegal Inputs" begin + no_rectangular_plane = [[0.2, 0.3, -0.5], [-1.0, 1.5, 0.2], [-0.2, 2.0, -0.5]] + flow_direction = [0.0, 0.0, 1.0] + + error_str = "the vectors `AB` and `AC` for the provided points `A`, `B`, `C` must be orthogonal" + + @test_throws ArgumentError(error_str) InFlow(; plane=no_rectangular_plane, + particle_spacing=0.1, + flow_direction, density=1.0, + open_boundary_layers=2) + @test_throws ArgumentError(error_str) OutFlow(; plane=no_rectangular_plane, + particle_spacing=0.1, + flow_direction, density=1.0, + open_boundary_layers=2) + + rectangular_plane = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] + flow_direction = [0.0, 1.0, 0.0] + + error_str = "`flow_direction` is not normal to inflow plane" + + @test_throws ArgumentError(error_str) InFlow(; plane=rectangular_plane, + particle_spacing=0.1, + flow_direction, density=1.0, + open_boundary_layers=2) + + error_str = "`flow_direction` is not normal to outflow plane" + + @test_throws ArgumentError(error_str) OutFlow(; plane=rectangular_plane, + particle_spacing=0.1, + flow_direction, density=1.0, + open_boundary_layers=2) + end +end diff --git a/test/schemes/boundary/open_boundary/characteristic_variables.jl b/test/schemes/boundary/open_boundary/characteristic_variables.jl new file mode 100644 index 000000000..1fe20722a --- /dev/null +++ b/test/schemes/boundary/open_boundary/characteristic_variables.jl @@ -0,0 +1,134 @@ +@testset verbose=true "Characteristic Variables" begin + particle_spacing = 0.1 + + # Number of boundary particles in the influence of fluid particles + influenced_particles = [20, 52, 26] + + open_boundary_layers = 8 + sound_speed = 20.0 + density = 1000.0 + pressure = 5.0 + + smoothing_kernel = SchoenbergCubicSplineKernel{2}() + smoothing_length = 1.2particle_spacing + + # Prescribed quantities + reference_velocity = (pos, t) -> SVector(t, 0.0) + reference_pressure = (pos, t) -> 50_000.0 * t + reference_density = (pos, t) -> 1000.0 * t + + # Plane points of open boundary + plane_points_1 = [[0.0, 0.0], [0.5, -0.5], [1.0, 0.5]] + plane_points_2 = [[0.0, 1.0], [0.2, 2.0], [2.3, 0.5]] + + @testset "Points $i" for i in eachindex(plane_points_1) + n_influenced = influenced_particles[i] + + plane_points = [plane_points_1[i], plane_points_2[i]] + + plane_size = plane_points[2] - plane_points[1] + flow_directions = [ + normalize([-plane_size[2], plane_size[1]]), + -normalize([-plane_size[2], plane_size[1]]), + ] + + @testset "Flow Direction $j" for j in eachindex(flow_directions) + flow_direction = flow_directions[j] + inflow = InFlow(; plane=plane_points, particle_spacing, density, + flow_direction, open_boundary_layers) + outflow = OutFlow(; plane=plane_points, particle_spacing, density, + flow_direction, open_boundary_layers) + + boundary_zones = [ + inflow, + outflow, + ] + + @testset "`$(nameof(typeof(boundary_zone)))`" for boundary_zone in boundary_zones + sign_ = (boundary_zone isa InFlow) ? 1 : -1 + fluid = extrude_geometry(plane_points; particle_spacing, n_extrude=4, + density, pressure, + direction=(sign_ * flow_direction)) + + fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, + buffer_size=0, + density_calculator=ContinuityDensity(), + smoothing_length, sound_speed) + + boundary_system = OpenBoundarySPHSystem(boundary_zone; sound_speed, + fluid_system, buffer_size=0, + reference_velocity, + reference_pressure, + reference_density) + + semi = Semidiscretization(fluid_system, boundary_system) + + ode = semidiscretize(semi, (0.0, 5.0)) + + v0_ode, u0_ode = ode.u0.x + v = TrixiParticles.wrap_v(v0_ode, boundary_system, semi) + u = TrixiParticles.wrap_u(u0_ode, boundary_system, semi) + + # ==== Characteristic Variables + # `J1 = -sound_speed^2 * (rho - rho_ref) + (p - p_ref)` + # `J2 = rho * sound_speed * (v - v_ref) + (p - p_ref)` + # `J3 = - rho * sound_speed * (v - v_ref) + (p - p_ref)` + function J1(t) + return -sound_speed^2 * (density - reference_density(0, t)) + + (pressure - reference_pressure(0, t)) + end + function J2(t) + return density * sound_speed * + dot(-reference_velocity(0, t), flow_direction) + + (pressure - reference_pressure(0, t)) + end + function J3(t) + return -density * sound_speed * + dot(-reference_velocity(0, t), flow_direction) + + (pressure - reference_pressure(0, t)) + end + + # First evaluation. + # Particles not influenced by the fluid have zero values. + t1 = 2.0 + TrixiParticles.evaluate_characteristics!(boundary_system, + v, u, v0_ode, u0_ode, semi, t1) + evaluated_vars1 = boundary_system.characteristics + + if boundary_zone isa InFlow + @test all(isapprox.(evaluated_vars1[1, :], 0.0)) + @test all(isapprox.(evaluated_vars1[2, :], 0.0)) + @test all(isapprox.(evaluated_vars1[3, 1:n_influenced], J3(t1))) + @test all(isapprox.(evaluated_vars1[3, (n_influenced + 1):end], 0.0)) + + elseif boundary_zone isa OutFlow + @test all(isapprox.(evaluated_vars1[1, 1:n_influenced], J1(t1))) + @test all(isapprox.(evaluated_vars1[2, 1:n_influenced], J2(t1))) + @test all(isapprox.(evaluated_vars1[1:2, (n_influenced + 1):end], 0.0)) + @test all(isapprox.(evaluated_vars1[3, :], 0.0)) + end + + # Second evaluation. + # Particles not influenced by the fluid have previous values. + t2 = 3.0 + TrixiParticles.evaluate_characteristics!(boundary_system, + v, u, v0_ode, u0_ode, semi, t2) + evaluated_vars2 = boundary_system.characteristics + + if boundary_zone isa InFlow + @test all(isapprox.(evaluated_vars2[1, :], 0.0)) + @test all(isapprox.(evaluated_vars2[2, :], 0.0)) + @test all(isapprox.(evaluated_vars2[3, 1:n_influenced], J3(t2))) + @test all(isapprox.(evaluated_vars2[3, (n_influenced + 1):end], J3(t1))) + + elseif boundary_zone isa OutFlow + @test all(isapprox.(evaluated_vars2[1, 1:n_influenced], J1(t2))) + @test all(isapprox.(evaluated_vars2[2, 1:n_influenced], J2(t2))) + @test all(isapprox.(evaluated_vars2[1, (n_influenced + 1):end], J1(t1))) + @test all(isapprox.(evaluated_vars2[2, (n_influenced + 1):end], J2(t1))) + @test all(isapprox.(evaluated_vars2[3, :], 0.0)) + end + end + end + end +end diff --git a/test/schemes/boundary/open_boundary/open_boundary.jl b/test/schemes/boundary/open_boundary/open_boundary.jl new file mode 100644 index 000000000..5fe09c703 --- /dev/null +++ b/test/schemes/boundary/open_boundary/open_boundary.jl @@ -0,0 +1,2 @@ +include("characteristic_variables.jl") +include("boundary_zone.jl") diff --git a/test/schemes/schemes.jl b/test/schemes/schemes.jl index 2e5e609e5..5cf7cd5bd 100644 --- a/test/schemes/schemes.jl +++ b/test/schemes/schemes.jl @@ -1,4 +1,5 @@ include("solid/total_lagrangian_sph/total_lagrangian_sph.jl") include("boundary/dummy_particles/dummy_particles.jl") include("boundary/monaghan_kajtar/monaghan_kajtar.jl") +include("boundary/open_boundary/open_boundary.jl") include("fluid/fluid.jl") diff --git a/test/systems/open_boundary_system.jl b/test/systems/open_boundary_system.jl new file mode 100644 index 000000000..1158aaff0 --- /dev/null +++ b/test/systems/open_boundary_system.jl @@ -0,0 +1,90 @@ +@testset verbose=true "`OpenBoundarySPHSystem`" begin + @testset verbose=true "Illegal Inputs" begin + plane = ([0.0, 0.0], [0.0, 1.0]) + flow_direction = (1.0, 0.0) + + # Mock fluid system + struct FluidSystemMock2 <: TrixiParticles.FluidSystem{2, Nothing} end + + inflow = InFlow(; plane, particle_spacing=0.1, + flow_direction, density=1.0, open_boundary_layers=2) + + error_str = "`reference_velocity` must be either a function mapping " * + "each particle's coordinates and time to its velocity, " * + "an array where the ``i``-th column holds the velocity of particle ``i`` " * + "or, for a constant fluid velocity, a vector of length 2 for a 2D problem holding this velocity" + + reference_velocity = 1.0 + @test_throws ArgumentError(error_str) OpenBoundarySPHSystem(inflow; sound_speed=1.0, + buffer_size=0, + fluid_system=FluidSystemMock2(), + reference_velocity) + + error_str = "`reference_pressure` must be either a function mapping " * + "each particle's coordinates and time to its pressure, " * + "a vector holding the pressure of each particle, or a scalar" + + reference_pressure = [1.0, 1.0] + @test_throws ArgumentError(error_str) OpenBoundarySPHSystem(inflow; sound_speed=1.0, + buffer_size=0, + fluid_system=FluidSystemMock2(), + reference_pressure) + + error_str = "`reference_density` must be either a function mapping " * + "each particle's coordinates and time to its density, " * + "a vector holding the density of each particle, or a scalar" + + reference_density = [1.0, 1.0] + @test_throws ArgumentError(error_str) OpenBoundarySPHSystem(inflow; sound_speed=1.0, + buffer_size=0, + fluid_system=FluidSystemMock2(), + reference_density) + end + @testset "`show`" begin + inflow = InFlow(; plane=([0.0, 0.0], [0.0, 1.0]), particle_spacing=0.05, + flow_direction=(1.0, 0.0), density=1.0, open_boundary_layers=4) + system = OpenBoundarySPHSystem(inflow; sound_speed=1.0, buffer_size=0, + fluid_system=FluidSystemMock2()) + + show_compact = "OpenBoundarySPHSystem{2}(InFlow) with 80 particles" + @test repr(system) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ OpenBoundarySPHSystem{2} │ + │ ════════════════════════ │ + │ #particles: ………………………………………………… 80 │ + │ #buffer_particles: ……………………………… 0 │ + │ fluid system: …………………………………………… FluidSystemMock2 │ + │ boundary: ……………………………………………………… InFlow │ + │ flow direction: ……………………………………… [1.0, 0.0] │ + │ prescribed velocity: ………………………… constant_vector │ + │ prescribed pressure: ………………………… constant_scalar │ + │ prescribed density: …………………………… constant_scalar │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", system) == show_box + + outflow = OutFlow(; plane=([0.0, 0.0], [0.0, 1.0]), particle_spacing=0.05, + flow_direction=(1.0, 0.0), density=1.0, open_boundary_layers=4) + system = OpenBoundarySPHSystem(outflow; sound_speed=1.0, buffer_size=0, + fluid_system=FluidSystemMock2()) + + show_compact = "OpenBoundarySPHSystem{2}(OutFlow) with 80 particles" + @test repr(system) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ OpenBoundarySPHSystem{2} │ + │ ════════════════════════ │ + │ #particles: ………………………………………………… 80 │ + │ #buffer_particles: ……………………………… 0 │ + │ fluid system: …………………………………………… FluidSystemMock2 │ + │ boundary: ……………………………………………………… OutFlow │ + │ flow direction: ……………………………………… [1.0, 0.0] │ + │ prescribed velocity: ………………………… constant_vector │ + │ prescribed pressure: ………………………… constant_scalar │ + │ prescribed density: …………………………… constant_scalar │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + @test repr("text/plain", system) == show_box + end +end diff --git a/test/systems/systems.jl b/test/systems/systems.jl index 6aff417b3..697f575fa 100644 --- a/test/systems/systems.jl +++ b/test/systems/systems.jl @@ -2,4 +2,5 @@ include("wcsph_system.jl") include("edac_system.jl") include("solid_system.jl") include("boundary_system.jl") +include("open_boundary_system.jl") include("dem_system.jl") From ae3a2ffc5bae054a6ad88c40efa018eb77009a97 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Wed, 19 Jun 2024 15:45:48 +0200 Subject: [PATCH 42/49] New Feature Release (#548) Open Boundaries have been added --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index bff9874c1..6f6a24bb1 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TrixiParticles" uuid = "66699cd8-9c01-4e9d-a059-b96c86d16b3a" authors = ["erik.faulhaber <44124897+efaulhaber@users.noreply.github.com>"] -version = "0.1.3-dev" +version = "0.1.3" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" From e1e80f0f62514c65a441ebeeb7ff391a927837da Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Thu, 20 Jun 2024 09:37:05 +0200 Subject: [PATCH 43/49] Set to development version 0.1.4 (#549) * Set to development version 0.1.4 * Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 6f6a24bb1..8c89b8a64 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TrixiParticles" uuid = "66699cd8-9c01-4e9d-a059-b96c86d16b3a" authors = ["erik.faulhaber <44124897+efaulhaber@users.noreply.github.com>"] -version = "0.1.3" +version = "0.1.4-dev" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" From 42d854ebd82b66c33d8fc3212fbed505d17614e9 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:55:48 +0200 Subject: [PATCH 44/49] Rewrite `@threaded` macro to work with GPUs (#534) * Fix wrapping for GPU arrays * Adapt `@threaded` macro to work with GPUs * Use new `@threaded` macro * Fix NHS to run GPU code with CPU arrays * Import `@index` from KernelAbstractions.jl For some reason it doesn't run on the CPU otherwise. * Add proper docs for `@threaded` * Fix `foreach_neighbor` * Reformat * Add test for GPU code on the CPU * Fix unit tests * Add unit tests for GPU interaction * Also add tests for GPU versions of `wrap_v` and `wrap_u` * Add kwarg `neighborhood_search` to dam break example * Fix tests * Reformat * Add function `wrap_array` * Fix merge * Fix open boundary code * Implement suggestions * Fix syntax error from merge * Fix `wrap_u` and `wrap_v` for GPU array types * Fix constructor of `BoundarySPHSystem` --- Project.toml | 4 +- examples/fluid/dam_break_2d.jl | 6 +- examples/n_body/n_body_system.jl | 4 +- src/TrixiParticles.jl | 6 +- src/general/corrections.jl | 2 +- src/general/general.jl | 30 +---- src/general/gpu.jl | 8 ++ src/general/neighborhood_search.jl | 11 ++ src/general/semidiscretization.jl | 42 ++++--- src/general/system.jl | 25 ++++ .../dummy_particles/dummy_particles.jl | 7 +- src/schemes/boundary/open_boundary/system.jl | 4 +- src/schemes/boundary/system.jl | 8 +- .../fluid/weakly_compressible_sph/system.jl | 2 +- .../solid/total_lagrangian_sph/system.jl | 91 +++++++-------- src/util.jl | 93 +++++++++------ test/count_allocations.jl | 17 +-- test/examples/examples.jl | 13 +++ test/general/semidiscretization.jl | 10 +- .../schemes/solid/total_lagrangian_sph/rhs.jl | 109 ++++++++++++------ 20 files changed, 302 insertions(+), 190 deletions(-) diff --git a/Project.toml b/Project.toml index 8c89b8a64..2565a0945 100644 --- a/Project.toml +++ b/Project.toml @@ -11,7 +11,9 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def" FastPow = "c0e83750-1142-43a8-81cf-6c956b72b4d1" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +GPUArrays = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" PointNeighbors = "1c4d5385-0a27-49de-8e2c-43b175c8985c" @@ -35,6 +37,7 @@ FastPow = "0.1" ForwardDiff = "0.10" JSON = "0.21" MuladdMacro = "0.2" +PointNeighbors = "0.2.3" Polyester = "0.7.5" RecipesBase = "1" Reexport = "1" @@ -43,6 +46,5 @@ StaticArrays = "1" StrideArrays = "0.1" TimerOutputs = "0.5" TrixiBase = "0.1.3" -PointNeighbors = "0.2" WriteVTK = "1" julia = "1.9" diff --git a/examples/fluid/dam_break_2d.jl b/examples/fluid/dam_break_2d.jl index e58a7cc06..a1eeb2f38 100644 --- a/examples/fluid/dam_break_2d.jl +++ b/examples/fluid/dam_break_2d.jl @@ -73,8 +73,10 @@ boundary_system = BoundarySPHSystem(tank.boundary, boundary_model, adhesion_coef # ========================================================================================== # ==== Simulation -semi = Semidiscretization(fluid_system, boundary_system, threaded_nhs_update=true) -ode = semidiscretize(semi, tspan) +semi = Semidiscretization(fluid_system, boundary_system, + neighborhood_search=GridNeighborhoodSearch, + threaded_nhs_update=true) +ode = semidiscretize(semi, tspan, data_type=nothing) info_callback = InfoCallback(interval=100) diff --git a/examples/n_body/n_body_system.jl b/examples/n_body/n_body_system.jl index c7226357a..556147ed4 100644 --- a/examples/n_body/n_body_system.jl +++ b/examples/n_body/n_body_system.jl @@ -1,7 +1,9 @@ using TrixiParticles using LinearAlgebra -struct NBodySystem{NDIMS, ELTYPE <: Real} <: TrixiParticles.System{NDIMS, Nothing} +# The second type parameter of `System` can't be `Nothing`, or TrixiParticles will launch +# GPU kernel for `for_particle_neighbor` loops. +struct NBodySystem{NDIMS, ELTYPE <: Real} <: TrixiParticles.System{NDIMS, 0} initial_condition :: InitialCondition{ELTYPE} mass :: Array{ELTYPE, 1} # [particle] G :: ELTYPE diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index aa97ab720..9fe23e3d4 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -9,7 +9,9 @@ using DataFrames: DataFrame using DiffEqCallbacks: PeriodicCallback, PeriodicCallbackAffect, PresetTimeCallback using FastPow: @fastpow using ForwardDiff: ForwardDiff +using GPUArrays: AbstractGPUArray using JSON: JSON +using KernelAbstractions: KernelAbstractions, @kernel, @index using LinearAlgebra: norm, dot, I, tr, inv, pinv, det using MuladdMacro: @muladd using Polyester: Polyester, @batch @@ -27,7 +29,9 @@ using TrixiBase: trixi_include, @trixi_timeit, timer, timeit_debug_enabled, using PointNeighbors: PointNeighbors, for_particle_neighbor using WriteVTK: vtk_grid, MeshCell, VTKCellTypes, paraview_collection, vtk_save -# util needs to be first because of macro @trixi_timeit +# `util.jl` depends on the `GPUSystem` type defined in `system.jl` +include("general/system.jl") +# `util.jl` needs to be next because of the macros `@trixi_timeit` and `@threaded` include("util.jl") include("callbacks/callbacks.jl") include("general/general.jl") diff --git a/src/general/corrections.jl b/src/general/corrections.jl index cf64f6d16..ee894fec4 100644 --- a/src/general/corrections.jl +++ b/src/general/corrections.jl @@ -440,7 +440,7 @@ function compute_gradient_correction_matrix!(corr_matrix::AbstractArray, system, end function correction_matrix_inversion_step!(corr_matrix, system) - @threaded for particle in eachparticle(system) + @threaded system for particle in eachparticle(system) L = extract_smatrix(corr_matrix, system, particle) # The matrix `L` only becomes singular when the particle and all neighbors diff --git a/src/general/general.jl b/src/general/general.jl index 774d076c9..dfd709861 100644 --- a/src/general/general.jl +++ b/src/general/general.jl @@ -1,37 +1,11 @@ -# Abstract supertype for all system types. We additionally store the type of the system's -# initial condition, which is `Nothing` when using KernelAbstractions.jl. -abstract type System{NDIMS, IC} end - -# When using KernelAbstractions.jl, the initial condition has been replaced by `nothing` -GPUSystem = System{NDIMS, Nothing} where {NDIMS} - -abstract type FluidSystem{NDIMS, IC} <: System{NDIMS, IC} end -timer_name(::FluidSystem) = "fluid" -vtkname(system::FluidSystem) = "fluid" - -abstract type SolidSystem{NDIMS, IC} <: System{NDIMS, IC} end -timer_name(::SolidSystem) = "solid" -vtkname(system::SolidSystem) = "solid" - -abstract type BoundarySystem{NDIMS, IC} <: System{NDIMS, IC} end -timer_name(::BoundarySystem) = "boundary" -vtkname(system::BoundarySystem) = "boundary" - -@inline function set_zero!(du) - du .= zero(eltype(du)) - - return du -end - -# Note that `semidiscretization.jl` depends on the system types and has to be -# included later. +# Note that `system.jl` has already been included. +# `semidiscretization.jl` depends on the system types and has to be included later. # `density_calculators.jl` needs to be included before `corrections.jl`. include("density_calculators.jl") include("corrections.jl") include("smoothing_kernels.jl") include("initial_condition.jl") include("buffer.jl") -include("system.jl") include("interpolation.jl") include("custom_quantities.jl") include("neighborhood_search.jl") diff --git a/src/general/gpu.jl b/src/general/gpu.jl index 377ea8b72..8aa481cff 100644 --- a/src/general/gpu.jl +++ b/src/general/gpu.jl @@ -13,6 +13,7 @@ Adapt.@adapt_structure DensityDiffusionAntuono Adapt.@adapt_structure BoundarySPHSystem Adapt.@adapt_structure BoundaryModelDummyParticles Adapt.@adapt_structure BoundaryModelMonaghanKajtar +Adapt.@adapt_structure TotalLagrangianSPHSystem # The initial conditions are only used for initialization, which happens before `adapt`ing # the semidiscretization, so we don't need to store `InitialCondition`s on the GPU. @@ -32,3 +33,10 @@ end function Adapt.adapt_structure(to::typeof(Array), range::UnitRange) return range end + +KernelAbstractions.get_backend(::PtrArray) = KernelAbstractions.CPU() +KernelAbstractions.get_backend(system::System) = KernelAbstractions.get_backend(system.mass) + +function KernelAbstractions.get_backend(system::BoundarySPHSystem) + KernelAbstractions.get_backend(system.coordinates) +end diff --git a/src/general/neighborhood_search.jl b/src/general/neighborhood_search.jl index 0713f153d..afcd2c9b3 100644 --- a/src/general/neighborhood_search.jl +++ b/src/general/neighborhood_search.jl @@ -9,3 +9,14 @@ function PointNeighbors.for_particle_neighbor(f, system, neighbor_system, for_particle_neighbor(f, system_coords, neighbor_coords, neighborhood_search, particles=particles, parallel=parallel) end + +function PointNeighbors.for_particle_neighbor(f, system::GPUSystem, neighbor_system, + system_coords, neighbor_coords, + neighborhood_search; + particles=each_moving_particle(system), + parallel=true) + @threaded system for particle in particles + PointNeighbors.foreach_neighbor(f, system_coords, neighbor_coords, + neighborhood_search, particle) + end +end diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 73c1fe56d..7f6c791db 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -368,6 +368,17 @@ end # We have to pass `system` here for type stability, # since the type of `system` determines the return type. +@inline function wrap_v(v_ode, system, semi) + (; ranges_v) = semi + + range = ranges_v[system_indices(system, semi)] + + @boundscheck @assert length(range) == v_nvariables(system) * n_moving_particles(system) + + return wrap_array(v_ode, range, + (StaticInt(v_nvariables(system)), n_moving_particles(system))) +end + @inline function wrap_u(u_ode, system, semi) (; ranges_u) = semi @@ -375,22 +386,23 @@ end @boundscheck @assert length(range) == u_nvariables(system) * n_moving_particles(system) - # This is a non-allocating version of: - # return unsafe_wrap(Array{eltype(u_ode), 2}, pointer(view(u_ode, range)), - # (u_nvariables(system), n_moving_particles(system))) - return PtrArray(pointer(view(u_ode, range)), - (StaticInt(u_nvariables(system)), n_moving_particles(system))) + return wrap_array(u_ode, range, + (StaticInt(u_nvariables(system)), n_moving_particles(system))) end -@inline function wrap_v(v_ode, system, semi) - (; ranges_v) = semi - - range = ranges_v[system_indices(system, semi)] - - @boundscheck @assert length(range) == v_nvariables(system) * n_moving_particles(system) +@inline function wrap_array(array::Array, range, size) + # This is a non-allocating version of: + # return unsafe_wrap(Array{eltype(array), 2}, pointer(view(array, range)), size) + return PtrArray(pointer(view(array, range)), size) +end - return PtrArray(pointer(view(v_ode, range)), - (StaticInt(v_nvariables(system)), n_moving_particles(system))) +@inline function wrap_array(array, range, size) + # For non-`Array`s (typically GPU arrays), just reshape. Calling the `PtrArray` code + # above for a `CuArray` yields another `CuArray` (instead of a `PtrArray`) + # and is 8 times slower with double the allocations. + # + # Note that `size` might contain `StaticInt`s, so convert to `Int` first. + return reshape(view(array, range), Int.(size)) end function calculate_dt(v_ode, u_ode, cfl_number, semi::Semidiscretization) @@ -409,7 +421,7 @@ function drift!(du_ode, v_ode, u_ode, semi, t) du = wrap_u(du_ode, system, semi) v = wrap_v(v_ode, system, semi) - @threaded for particle in each_moving_particle(system) + @threaded system for particle in each_moving_particle(system) # This can be dispatched per system add_velocity!(du, v, particle, system) end @@ -508,7 +520,7 @@ function add_source_terms!(dv_ode, v_ode, u_ode, semi) v = wrap_v(v_ode, system, semi) u = wrap_u(u_ode, system, semi) - @threaded for particle in each_moving_particle(system) + @threaded system for particle in each_moving_particle(system) # Dispatch by system type to exclude boundary systems add_acceleration!(dv, particle, system) add_source_terms_inner!(dv, v, u, particle, system, source_terms(system)) diff --git a/src/general/system.jl b/src/general/system.jl index c3f10caad..674555aea 100644 --- a/src/general/system.jl +++ b/src/general/system.jl @@ -1,3 +1,28 @@ +# Abstract supertype for all system types. We additionally store the type of the system's +# initial condition, which is `Nothing` when using KernelAbstractions.jl. +abstract type System{NDIMS, IC} end + +# When using KernelAbstractions.jl, the initial condition has been replaced by `nothing` +GPUSystem = System{NDIMS, Nothing} where {NDIMS} + +abstract type FluidSystem{NDIMS, IC} <: System{NDIMS, IC} end +timer_name(::FluidSystem) = "fluid" +vtkname(system::FluidSystem) = "fluid" + +abstract type SolidSystem{NDIMS, IC} <: System{NDIMS, IC} end +timer_name(::SolidSystem) = "solid" +vtkname(system::SolidSystem) = "solid" + +abstract type BoundarySystem{NDIMS, IC} <: System{NDIMS, IC} end +timer_name(::BoundarySystem) = "boundary" +vtkname(system::BoundarySystem) = "boundary" + +@inline function set_zero!(du) + du .= zero(eltype(du)) + + return du +end + initialize!(system, neighborhood_search) = system @inline Base.ndims(::System{NDIMS}) where {NDIMS} = NDIMS diff --git a/src/schemes/boundary/dummy_particles/dummy_particles.jl b/src/schemes/boundary/dummy_particles/dummy_particles.jl index 0f7b42696..3e3abcbd9 100644 --- a/src/schemes/boundary/dummy_particles/dummy_particles.jl +++ b/src/schemes/boundary/dummy_particles/dummy_particles.jl @@ -291,7 +291,7 @@ function compute_pressure!(boundary_model, ::Union{SummationDensity, ContinuityD # Limit pressure to be non-negative to avoid attractive forces between fluid and # boundary particles at free surfaces (sticking artifacts). - @threaded for particle in eachparticle(system) + @threaded system for particle in eachparticle(system) apply_state_equation!(boundary_model, particle_density(v, boundary_model, particle), particle) end @@ -346,14 +346,15 @@ function compute_pressure!(boundary_model, ::AdamiPressureExtrapolation, system_coords, neighbor_coords, v_neighbor_system, nhs) end - for particle in eachparticle(system) + + @threaded system for particle in eachparticle(system) # Limit pressure to be non-negative to avoid attractive forces between fluid and # boundary particles at free surfaces (sticking artifacts). pressure[particle] = max(pressure[particle], 0.0) end end - @trixi_timeit timer() "inverse state equation" @threaded for particle in eachparticle(system) + @trixi_timeit timer() "inverse state equation" @threaded system for particle in eachparticle(system) compute_adami_density!(boundary_model, system, system_coords, particle) end end diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 43c0a6ff1..b5950856e 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -171,7 +171,7 @@ end reference_velocity, reference_pressure, reference_density) = system # Update quantities based on the characteristic variables - @threaded for particle in each_moving_particle(system) + @threaded system for particle in each_moving_particle(system) particle_position = current_coords(u, system, particle) J1 = characteristics[1, particle] @@ -230,7 +230,7 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) # Thus, we compute the characteristics for the particles that are outside the influence # of fluid particles by using the average of the values of the previous time step. # See eq. 27 in Negi (2020) https://doi.org/10.1016/j.cma.2020.113119 - @threaded for particle in each_moving_particle(system) + @threaded system for particle in each_moving_particle(system) # Particle is outside of the influence of fluid particles if isapprox(volume[particle], 0.0) diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl index 73af6a843..f36d515ac 100644 --- a/src/schemes/boundary/system.jl +++ b/src/schemes/boundary/system.jl @@ -27,13 +27,13 @@ struct BoundarySPHSystem{BM, NDIMS, ELTYPE <: Real, IC, CO, M, IM, # This constructor is necessary for Adapt.jl to work with this struct. # See the comments in general/gpu.jl for more details. function BoundarySPHSystem(initial_condition, coordinates, boundary_model, movement, - ismoving, adhesion_coefficient, cache) + ismoving, adhesion_coefficient, cache, buffer) ELTYPE = eltype(coordinates) new{typeof(boundary_model), size(coordinates, 1), ELTYPE, typeof(initial_condition), typeof(coordinates), typeof(movement), typeof(ismoving), typeof(cache)}(initial_condition, coordinates, boundary_model, movement, - ismoving, adhesion_coefficient, cache, nothing) + ismoving, adhesion_coefficient, cache, buffer) end end @@ -54,7 +54,7 @@ function BoundarySPHSystem(initial_condition, model; movement=nothing, # Because of dispatches boundary model needs to be first! return BoundarySPHSystem(initial_condition, coordinates, model, movement, - ismoving, adhesion_coefficient, cache) + ismoving, adhesion_coefficient, cache, nothing) end """ @@ -209,7 +209,7 @@ function (movement::BoundaryMovement)(system, t) is_moving(t) || return system - @threaded for particle in moving_particles + @threaded system for particle in moving_particles pos_new = initial_coords(system, particle) + movement_function(t) vel = ForwardDiff.derivative(movement_function, t) acc = ForwardDiff.derivative(t_ -> ForwardDiff.derivative(movement_function, t_), t) diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index a8e28670b..44e71695b 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -311,7 +311,7 @@ function reinit_density!(system, v, u, v_ode, u_ode, semi) end function compute_pressure!(system, v) - @threaded for particle in eachparticle(system) + @threaded system for particle in eachparticle(system) apply_state_equation!(system, particle_density(v, system, particle), particle) end end diff --git a/src/schemes/solid/total_lagrangian_sph/system.jl b/src/schemes/solid/total_lagrangian_sph/system.jl index 9203425fb..125bb5507 100644 --- a/src/schemes/solid/total_lagrangian_sph/system.jl +++ b/src/schemes/solid/total_lagrangian_sph/system.jl @@ -70,55 +70,50 @@ struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D, penalty_force :: PF source_terms :: ST buffer :: Nothing +end - function TotalLagrangianSPHSystem(initial_condition, - smoothing_kernel, smoothing_length, - young_modulus, poisson_ratio; - n_fixed_particles=0, boundary_model=nothing, - acceleration=ntuple(_ -> 0.0, - ndims(smoothing_kernel)), - penalty_force=nothing, source_terms=nothing) - NDIMS = ndims(initial_condition) - ELTYPE = eltype(initial_condition) - n_particles = nparticles(initial_condition) - - if ndims(smoothing_kernel) != NDIMS - throw(ArgumentError("smoothing kernel dimensionality must be $NDIMS for a $(NDIMS)D problem")) - end +function TotalLagrangianSPHSystem(initial_condition, + smoothing_kernel, smoothing_length, + young_modulus, poisson_ratio; + n_fixed_particles=0, boundary_model=nothing, + acceleration=ntuple(_ -> 0.0, + ndims(smoothing_kernel)), + penalty_force=nothing, source_terms=nothing) + NDIMS = ndims(initial_condition) + ELTYPE = eltype(initial_condition) + n_particles = nparticles(initial_condition) - # Make acceleration an SVector - acceleration_ = SVector(acceleration...) - if length(acceleration_) != NDIMS - throw(ArgumentError("`acceleration` must be of length $NDIMS for a $(NDIMS)D problem")) - end + if ndims(smoothing_kernel) != NDIMS + throw(ArgumentError("smoothing kernel dimensionality must be $NDIMS for a $(NDIMS)D problem")) + end - initial_coordinates = copy(initial_condition.coordinates) - current_coordinates = copy(initial_condition.coordinates) - mass = copy(initial_condition.mass) - material_density = copy(initial_condition.density) - correction_matrix = Array{ELTYPE, 3}(undef, NDIMS, NDIMS, n_particles) - pk1_corrected = Array{ELTYPE, 3}(undef, NDIMS, NDIMS, n_particles) - deformation_grad = Array{ELTYPE, 3}(undef, NDIMS, NDIMS, n_particles) - - n_moving_particles = n_particles - n_fixed_particles - - lame_lambda = young_modulus * poisson_ratio / - ((1 + poisson_ratio) * (1 - 2 * poisson_ratio)) - lame_mu = 0.5 * young_modulus / (1 + poisson_ratio) - - return new{typeof(boundary_model), NDIMS, ELTYPE, - typeof(initial_condition), - typeof(mass), typeof(initial_coordinates), - typeof(deformation_grad), typeof(smoothing_kernel), - typeof(penalty_force), - typeof(source_terms)}(initial_condition, initial_coordinates, - current_coordinates, mass, correction_matrix, - pk1_corrected, deformation_grad, material_density, - n_moving_particles, young_modulus, poisson_ratio, - lame_lambda, lame_mu, smoothing_kernel, - smoothing_length, acceleration_, boundary_model, - penalty_force, source_terms, nothing) + # Make acceleration an SVector + acceleration_ = SVector(acceleration...) + if length(acceleration_) != NDIMS + throw(ArgumentError("`acceleration` must be of length $NDIMS for a $(NDIMS)D problem")) end + + initial_coordinates = copy(initial_condition.coordinates) + current_coordinates = copy(initial_condition.coordinates) + mass = copy(initial_condition.mass) + material_density = copy(initial_condition.density) + correction_matrix = Array{ELTYPE, 3}(undef, NDIMS, NDIMS, n_particles) + pk1_corrected = Array{ELTYPE, 3}(undef, NDIMS, NDIMS, n_particles) + deformation_grad = Array{ELTYPE, 3}(undef, NDIMS, NDIMS, n_particles) + + n_moving_particles = n_particles - n_fixed_particles + + lame_lambda = young_modulus * poisson_ratio / + ((1 + poisson_ratio) * (1 - 2 * poisson_ratio)) + lame_mu = 0.5 * young_modulus / (1 + poisson_ratio) + + return TotalLagrangianSPHSystem(initial_condition, initial_coordinates, + current_coordinates, mass, correction_matrix, + pk1_corrected, deformation_grad, material_density, + n_moving_particles, young_modulus, poisson_ratio, + lame_lambda, lame_mu, smoothing_kernel, + smoothing_length, acceleration_, boundary_model, + penalty_force, source_terms, nothing) end function Base.show(io::IO, system::TotalLagrangianSPHSystem) @@ -260,7 +255,7 @@ end calc_deformation_grad!(deformation_grad, neighborhood_search, system) - @threaded for particle in eachparticle(system) + @threaded system for particle in eachparticle(system) F_particle = deformation_gradient(system, particle) pk1_particle = pk1_stress_tensor(F_particle, system) pk1_particle_corrected = pk1_particle * correction_matrix(system, particle) @@ -397,7 +392,7 @@ end function von_mises_stress(system::TotalLagrangianSPHSystem) von_mises_stress_vector = zeros(eltype(system.pk1_corrected), nparticles(system)) - @threaded for particle in each_moving_particle(system) + @threaded system for particle in each_moving_particle(system) von_mises_stress_vector[particle] = von_mises_stress(system, particle) end @@ -430,7 +425,7 @@ function cauchy_stress(system::TotalLagrangianSPHSystem) cauchy_stress_tensors = zeros(eltype(system.pk1_corrected), NDIMS, NDIMS, nparticles(system)) - @threaded for particle in each_moving_particle(system) + @threaded system for particle in each_moving_particle(system) F = deformation_gradient(system, particle) J = det(F) P = pk1_corrected(system, particle) diff --git a/src/util.jl b/src/util.jl index 338a94415..172f0a800 100644 --- a/src/util.jl +++ b/src/util.jl @@ -27,57 +27,74 @@ function print_startup_message() end """ - @threaded for ... end + @threaded system for ... end +Run either a threaded CPU loop or launch a kernel on the GPU, depending on the type of `system`. Semantically the same as `Threads.@threads` when iterating over a `AbstractUnitRange` but without guarantee that the underlying implementation uses `Threads.@threads` or works for more general `for` loops. -In particular, there may be an additional check whether only one thread is used -to reduce the overhead of serial execution or the underlying threading capabilities -might be provided by other packages such as [Polyester.jl](https://github.com/JuliaSIMD/Polyester.jl). +The first argument must either be a particle system or an array from which can be derived +if the loop has to be run threaded on the CPU or launched as a kernel on the GPU. + +In particular, the underlying threading capabilities might be provided by other packages +such as [Polyester.jl](https://github.com/JuliaSIMD/Polyester.jl). !!! warn This macro does not necessarily work for general `for` loops. For example, it does not necessarily support general iterables such as `eachline(filename)`. - -Some discussion can be found at -[https://discourse.julialang.org/t/overhead-of-threads-threads/53964](https://discourse.julialang.org/t/overhead-of-threads-threads/53964) -and -[https://discourse.julialang.org/t/threads-threads-with-one-thread-how-to-remove-the-overhead/58435](https://discourse.julialang.org/t/threads-threads-with-one-thread-how-to-remove-the-overhead/58435). - -Copied from [Trixi.jl](https://github.com/trixi-framework/Trixi.jl). """ -macro threaded(expr) - # Use `esc(quote ... end)` for nested macro calls as suggested in - # https://github.com/JuliaLang/julia/issues/23221 - # - # The following code is a simple version using only `Threads.@threads` from the - # standard library with an additional check whether only a single thread is used - # to reduce some overhead (and allocations) for serial execution. - # - # return esc(quote - # let - # if Threads.nthreads() == 1 - # $(expr) - # else - # Threads.@threads $(expr) - # end - # end - # end) - # - # However, the code below using `@batch` from Polyester.jl is more efficient, - # since this packages provides threads with less overhead. Since it is written - # by Chris Elrod, the author of LoopVectorization.jl, we expect this package - # to provide the most efficient and useful implementation of threads (as we use - # them) available in Julia. - # !!! danger "Heisenbug" - # Look at the comments for `wrap_array` when considering to change this macro. - +macro threaded(system, expr) + # Reverse-engineer the for loop. + # `expr.args[1]` is the head of the for loop, like `i = eachindex(x)`. + # So, `expr.args[1].args[2]` is the iterator `eachindex(x)` + # and `expr.args[1].args[1]` is the loop variable `i`. + iterator = expr.args[1].args[2] + i = expr.args[1].args[1] + inner_loop = expr.args[2] + + # Assemble the for loop again as a call to `parallel_foreach`, using `$i` to use the + # same loop variable as used in the for loop. return esc(quote - TrixiParticles.@batch $(expr) + TrixiParticles.parallel_foreach($iterator, $system) do $i + $inner_loop + end end) end +# Use `Polyester.@batch` for low-overhead threading +@inline function parallel_foreach(f, iterator, system) + Polyester.@batch for i in iterator + @inline f(i) + end +end + +# On GPUs, execute `f` inside a GPU kernel with KernelAbstractions.jl +@inline function parallel_foreach(f, iterator, system::Union{GPUSystem, AbstractGPUArray}) + # On the GPU, we can only loop over `1:N`. Therefore, we loop over `1:length(iterator)` + # and index with `iterator[eachindex(iterator)[i]]`. + # Note that this only works with vector-like iterators that support arbitrary indexing. + indices = eachindex(iterator) + ndrange = length(indices) + + # Skip empty loops + ndrange == 0 && return + + backend = KernelAbstractions.get_backend(system) + + # Call the generic kernel that is defined below, which only calls a function with + # the global GPU index. + generic_kernel(backend)(ndrange=ndrange) do i + @inline f(iterator[indices[i]]) + end + + KernelAbstractions.synchronize(backend) +end + +@kernel function generic_kernel(f) + i = @index(Global) + @inline f(i) +end + @doc raw""" examples_dir() diff --git a/test/count_allocations.jl b/test/count_allocations.jl index 1c597f9a0..5957bc67a 100644 --- a/test/count_allocations.jl +++ b/test/count_allocations.jl @@ -16,14 +16,15 @@ function copy_semi_with_no_update_nhs(semi) neighborhood_searches) end -# Forward `for_particle_neighbor` to wrapped neighborhood search -@inline function TrixiParticles.for_particle_neighbor(f, system_coords, neighbor_coords, - neighborhood_search::NoUpdateNeighborhoodSearch; - particles=axes(system_coords, 2), - parallel=true) - TrixiParticles.for_particle_neighbor(f, system_coords, neighbor_coords, - neighborhood_search.nhs, - particles=particles, parallel=parallel) +# Forward `foreach_neighbor` to wrapped neighborhood search +@inline function TrixiParticles.PointNeighbors.foreach_neighbor(f, system_coords, + neighbor_coords, + neighborhood_search::NoUpdateNeighborhoodSearch, + particle; + search_radius=neighborhood_search.nhs.search_radius) + TrixiParticles.PointNeighbors.foreach_neighbor(f, system_coords, neighbor_coords, + neighborhood_search.nhs, particle, + search_radius=search_radius) end # No update diff --git a/test/examples/examples.jl b/test/examples/examples.jl index dbe03a130..19b6ba2b9 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -87,6 +87,19 @@ @test count_rhs_allocations(sol, semi) == 0 end + @trixi_testset "fluid/dam_break_2d.jl with KernelAbstractions.jl" begin + # Emulate the GPU code on the CPU by passing `data_type = Array` + @test_nowarn_mod trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", + "dam_break_2d.jl"), tspan=(0.0, 0.1), + data_type=Array) [ + r"┌ Info: The desired tank length in y-direction .*\n", + r"└ New tank length in y-direction.*\n", + ] + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + @trixi_testset "fluid/dam_break_3d.jl" begin @test_nowarn_mod trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", diff --git a/test/general/semidiscretization.jl b/test/general/semidiscretization.jl index 4aff1f4fd..457b5b84d 100644 --- a/test/general/semidiscretization.jl +++ b/test/general/semidiscretization.jl @@ -1,9 +1,15 @@ # Use `@trixi_testset` to isolate the mock functions in a separate namespace @trixi_testset "Semidiscretization" begin - # Mock systems - struct System1 <: TrixiParticles.System{3, Nothing} end + # Mock systems. `System1` will use the CPU backend, `System2` is a `GPUSystem`, using + # the GPU backend (emulated on the CPU). + struct System1 <: TrixiParticles.System{3, String} end struct System2 <: TrixiParticles.System{3, Nothing} end + # `System2` has no field `mass`, so we have to manually define the backend + function TrixiParticles.KernelAbstractions.get_backend(::System2) + return TrixiParticles.KernelAbstractions.CPU() + end + system1 = System1() system2 = System2() diff --git a/test/schemes/solid/total_lagrangian_sph/rhs.jl b/test/schemes/solid/total_lagrangian_sph/rhs.jl index dfc9fc234..b64fb6c97 100644 --- a/test/schemes/solid/total_lagrangian_sph/rhs.jl +++ b/test/schemes/solid/total_lagrangian_sph/rhs.jl @@ -32,7 +32,7 @@ [0.0, 0.0], ] - @testset "Test $i" for i in 1:4 + @testset verbose=true "Test $i" for i in 1:4 #### Setup each_moving_particle = [particle[i]] # Only calculate dv for this one particle eachparticle = [particle[i], neighbor[i]] @@ -53,18 +53,27 @@ kernel_deriv = 1.0 #### Mocking - # Mock the system - system = Val{:mock_system_interact}() - TrixiParticles.ndims(::Val{:mock_system_interact}) = 2 - Base.ntuple(f, ::Symbol) = ntuple(f, 2) # Make `extract_svector` work + # Mock a CPU system to test CPU code + struct MockSystemInteractCPU <: TrixiParticles.System{2, String} end + system = MockSystemInteractCPU() - function TrixiParticles.initial_coordinates(::Val{:mock_system_interact}) + # Mock a GPU system to emulate GPU code on the CPU + struct MockSystemInteractGPU <: TrixiParticles.System{2, Nothing} end + system_gpu = MockSystemInteractGPU() + + function TrixiParticles.KernelAbstractions.get_backend(::MockSystemInteractGPU) + return TrixiParticles.KernelAbstractions.CPU() + end + + MockSystemType = Union{MockSystemInteractCPU, MockSystemInteractGPU} + + function TrixiParticles.initial_coordinates(::MockSystemType) return initial_coordinates end # Unpack calls should return predefined values or # another mock object of the type Val{:mock_property_name}. - function Base.getproperty(::Val{:mock_system_interact}, f::Symbol) + function Base.getproperty(::MockSystemType, f::Symbol) if f === :current_coordinates return current_coordinates elseif f === :material_density @@ -81,10 +90,13 @@ return Val(Symbol("mock_" * string(f))) end - function TrixiParticles.each_moving_particle(::Val{:mock_system_interact}) + function TrixiParticles.each_moving_particle(::MockSystemType) each_moving_particle end - TrixiParticles.eachparticle(::Val{:mock_system_interact}) = eachparticle + TrixiParticles.eachparticle(::MockSystemType) = eachparticle + + # Mock the neighborhood search + nhs = Val{:nhs}() TrixiParticles.PointNeighbors.eachneighbor(_, ::Val{:nhs}) = eachneighbor function Base.getproperty(::Val{:nhs}, f::Symbol) @@ -99,26 +111,25 @@ end TrixiParticles.ndims(::Val{:nhs}) = 2 - function TrixiParticles.pk1_corrected(::Val{:mock_system_dv}, particle_) - if particle_ == particle[i] - return pk1_particle_corrected[i] - end - return pk1_neighbor_corrected[i] - end - - function TrixiParticles.add_acceleration!(_, _, ::Val{:mock_system_interact}) + function TrixiParticles.add_acceleration!(_, _, ::MockSystemType) nothing end TrixiParticles.kernel_deriv(::Val{:mock_smoothing_kernel}, _, _) = kernel_deriv #### Verification - dv = zeros(ndims(system), 10) - dv_expected = copy(dv) - dv_expected[:, particle[i]] = dv_particle_expected[i] + systems = [system, system_gpu] + names = ["CPU code", "Emulate GPU"] + @testset "$(names[j])" for j in eachindex(names) + system_ = systems[j] + + dv = zeros(ndims(system_), 10) + dv_expected = copy(dv) + dv_expected[:, particle[i]] = dv_particle_expected[i] - TrixiParticles.interact_solid_solid!(dv, Val(:nhs), system, system) + TrixiParticles.interact_solid_solid!(dv, nhs, system_, system_) - @test dv ≈ dv_expected + @test dv ≈ dv_expected + end end end @@ -140,7 +151,7 @@ 10 / 1000^2 * 1.5400218087591082 * 324.67072684047224 * 1.224, 0.0, ]) - @testset "Deformation Function: $deformation" for deformation in keys(deformations) + @testset verbose=true "Deformation Function: $deformation" for deformation in keys(deformations) J = deformations[deformation] u = zeros(2, 81) v = zeros(2, 81) @@ -176,22 +187,50 @@ semi = Semidiscretization(system) tspan = (0.0, 1.0) - semidiscretize(semi, tspan) - # Apply the deformation matrix - for particle in axes(u, 2) - # Apply deformation - u[1:2, particle] = deformations[deformation](coordinates[:, particle]) + # To make the code below work + function TrixiParticles.PtrArray{Float64}(::UndefInitializer, length) + TrixiParticles.PtrArray(zeros(length)) end - #### Verification for the particle in the middle - particle = 41 + # We can pass the data type `Array` to convert all systems to `GPUSystem`s + # and emulate the GPU kernels on the GPU. + # But this doesn't test `wrap_v` and `wrap_u` for non-`Array` types. + # In order to test this as well, we need a different data type, so we also + # pass `PtrArray`. + names = ["CPU code", "GPU code with CPU wrapping", "GPU code with GPU wrapping"] + data_types = [nothing, Array, TrixiParticles.PtrArray] + @testset "$(names[i])" for i in eachindex(names) + data_type = data_types[i] + ode = semidiscretize(semi, tspan, data_type=data_type) + + # Apply the deformation matrix + for particle in axes(u, 2) + # Apply deformation + u[1:2, particle] = deformations[deformation](coordinates[:, particle]) + end + + v_ode = ode.u0.x[1] + if isnothing(data_type) + u_ode = vec(u) + else + u_ode = data_type(vec(u)) + end + + @test typeof(v_ode) == typeof(u_ode) + @test length(v_ode) == length(u_ode) + + #### Verification for the particle in the middle + particle = 41 - dv = zeros(ndims(system), 81) - TrixiParticles.kick!(dv, v, u, semi, 0.0) + dv_ode = zero(v_ode) + TrixiParticles.kick!(dv_ode, v_ode, u_ode, ode.p, 0.0) - @test isapprox(dv[:, particle], dv_expected_41[deformation], - rtol=sqrt(eps()), atol=sqrt(eps())) + dv = TrixiParticles.wrap_v(dv_ode, system, semi) + + @test isapprox(dv[:, particle], dv_expected_41[deformation], + rtol=sqrt(eps()), atol=sqrt(eps())) + end end end -end +end; From 7d90ea18d40967e632763835b47cc8fc8f5db9a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 08:18:40 +0000 Subject: [PATCH 45/49] CompatHelper: add new compat entry for KernelAbstractions at version 0.9, (keep existing compat) (#551) Co-authored-by: CompatHelper Julia --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 2565a0945..91715c9d0 100644 --- a/Project.toml +++ b/Project.toml @@ -36,6 +36,7 @@ DiffEqCallbacks = "2.25, 3" FastPow = "0.1" ForwardDiff = "0.10" JSON = "0.21" +KernelAbstractions = "0.9" MuladdMacro = "0.2" PointNeighbors = "0.2.3" Polyester = "0.7.5" From f8ec990851ce7e7ce86664c8bbb37778eb8c43a6 Mon Sep 17 00:00:00 2001 From: Niklas Neher <73897120+LasNikas@users.noreply.github.com> Date: Tue, 25 Jun 2024 21:25:46 +0200 Subject: [PATCH 46/49] fix name (#554) --- src/schemes/boundary/open_boundary/system.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index b5950856e..8c580cf44 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -146,7 +146,7 @@ function Base.show(io::IO, ::MIME"text/plain", system::OpenBoundarySPHSystem) end end -function reset_callback_flag(system::OpenBoundarySPHSystem) +function reset_callback_flag!(system::OpenBoundarySPHSystem) system.update_callback_used[] = false return system From 708ad9d2b3e9b618c6ec9be0ceefb38809ed4e13 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Wed, 26 Jun 2024 09:12:07 +0200 Subject: [PATCH 47/49] Manually set GPU_ARRAY compat (#555) Co-authored-by: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 91715c9d0..981c36192 100644 --- a/Project.toml +++ b/Project.toml @@ -35,6 +35,7 @@ DataFrames = "1.6" DiffEqCallbacks = "2.25, 3" FastPow = "0.1" ForwardDiff = "0.10" +GPUArrays = "9, 10" JSON = "0.21" KernelAbstractions = "0.9" MuladdMacro = "0.2" From ffe3663a536f4e684b1c83909235e3b891ab752b Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Fri, 28 Jun 2024 08:53:11 +0200 Subject: [PATCH 48/49] Add Viscosity Formulation as per Morris et al. (#504) * Add Viscosity Moris * format * fix * implement suggestions * other suggestions * fix equation * format * review comments * fixes * fix * update test * update * fix open boundary * update reference files * update validation criteria * update reference file * new reference files * ups * update ref file * update validation error bounds * update error bound * update error bound * add comment and correct setting of 'v' --- examples/fluid/dam_break_2d.jl | 3 + examples/fluid/oscillating_drop_2d.jl | 2 +- src/TrixiParticles.jl | 2 +- .../fluid/entropically_damped_sph/rhs.jl | 3 +- src/schemes/fluid/viscosity.jl | 135 +++-- .../fluid/weakly_compressible_sph/rhs.jl | 2 +- src/schemes/solid/total_lagrangian_sph/rhs.jl | 2 +- src/visualization/write2vtk.jl | 7 +- test/examples/examples.jl | 2 +- test/schemes/fluid/fluid.jl | 1 + test/schemes/fluid/viscosity.jl | 65 +++ test/validation/validation.jl | 12 +- .../validation_reference_edac_00075.json | 2 +- .../validation_reference_wcsph_0001875.json | 494 +++++++++--------- .../validation_reference_wcsph_00075.json | 468 ++++++++--------- .../validation_reference_wcsph_0015.json | 422 +++++++-------- 16 files changed, 870 insertions(+), 752 deletions(-) create mode 100644 test/schemes/fluid/viscosity.jl diff --git a/examples/fluid/dam_break_2d.jl b/examples/fluid/dam_break_2d.jl index a1eeb2f38..bfcfb9a18 100644 --- a/examples/fluid/dam_break_2d.jl +++ b/examples/fluid/dam_break_2d.jl @@ -48,6 +48,9 @@ smoothing_kernel = WendlandC2Kernel{2}() fluid_density_calculator = ContinuityDensity() viscosity = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) +# nu = 0.02 * smoothing_length * sound_speed/8 +# viscosity = ViscosityMorris(nu=nu) +# viscosity = ViscosityAdami(nu=nu) # Alternatively the density diffusion model by Molteni & Colagrossi can be used, # which will run faster. # density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) diff --git a/examples/fluid/oscillating_drop_2d.jl b/examples/fluid/oscillating_drop_2d.jl index da4e855ac..7ed6ec478 100644 --- a/examples/fluid/oscillating_drop_2d.jl +++ b/examples/fluid/oscillating_drop_2d.jl @@ -89,5 +89,5 @@ exact_solution_ode = ODEProblem(exact_solution_rhs, exact_u0, tspan) sol_exact = solve(exact_solution_ode, RDPK3SpFSAL49(), save_everystep=false) # Error in the semi-major axis of the elliptical drop -error_A = maximum(sol.u[end].x[2]) + 0.5fluid_particle_spacing - +error_A = maximum(sol.u[end].x[2]) + 0.5 * fluid_particle_spacing - maximum(sol_exact.u[end][2:3]) diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index 9fe23e3d4..76e3cffc8 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -58,7 +58,7 @@ export SchoenbergCubicSplineKernel, SchoenbergQuarticSplineKernel, SchoenbergQuinticSplineKernel, GaussianKernel, WendlandC2Kernel, WendlandC4Kernel, WendlandC6Kernel, SpikyKernel, Poly6Kernel export StateEquationCole -export ArtificialViscosityMonaghan, ViscosityAdami +export ArtificialViscosityMonaghan, ViscosityAdami, ViscosityMorris export DensityDiffusion, DensityDiffusionMolteniColagrossi, DensityDiffusionFerrari, DensityDiffusionAntuono export BoundaryModelMonaghanKajtar, BoundaryModelDummyParticles, AdamiPressureExtrapolation, diff --git a/src/schemes/fluid/entropically_damped_sph/rhs.jl b/src/schemes/fluid/entropically_damped_sph/rhs.jl index 16ca94ec0..57bbdc16d 100644 --- a/src/schemes/fluid/entropically_damped_sph/rhs.jl +++ b/src/schemes/fluid/entropically_damped_sph/rhs.jl @@ -17,7 +17,6 @@ function interact!(dv, v_particle_system, u_particle_system, rho_a = particle_density(v_particle_system, particle_system, particle) rho_b = particle_density(v_neighbor_system, neighbor_system, neighbor) - rho_mean = 0.5 * (rho_a + rho_b) p_a = particle_pressure(v_particle_system, particle_system, particle) p_b = particle_pressure(v_neighbor_system, neighbor_system, neighbor) @@ -34,7 +33,7 @@ function interact!(dv, v_particle_system, u_particle_system, dv_viscosity_ = dv_viscosity(particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) for i in 1:ndims(particle_system) dv[i, particle] += dv_pressure[i] + dv_viscosity_[i] diff --git a/src/schemes/fluid/viscosity.jl b/src/schemes/fluid/viscosity.jl index a678b40d4..75ed3ab09 100644 --- a/src/schemes/fluid/viscosity.jl +++ b/src/schemes/fluid/viscosity.jl @@ -1,57 +1,49 @@ +# Unpack the neighboring systems viscosity to dispatch on the viscosity type function dv_viscosity(particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) viscosity = viscosity_model(neighbor_system) return dv_viscosity(viscosity, particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) end function dv_viscosity(particle_system, neighbor_system::OpenBoundarySPHSystem, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) # No viscosity in the open boundary system. Use viscosity of the fluid system. viscosity = viscosity_model(particle_system) return dv_viscosity(viscosity, particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) end function dv_viscosity(viscosity, particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) return viscosity(particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) end function dv_viscosity(viscosity::Nothing, particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) return zero(pos_diff) end @doc raw""" - ArtificialViscosityMonaghan(; alpha, beta, epsilon=0.01) - -# Keywords -- `alpha`: A value of `0.02` is usually used for most simulations. For a relation with the - kinematic viscosity, see description below. -- `beta`: A value of `0.0` works well for simulations with shocks of moderate strength. - In simulations where the Mach number can be very high, eg. astrophysical calculation, - good results can be obtained by choosing a value of `beta=2` and `alpha=1`. -- `epsilon=0.01`: Parameter to prevent singularities. - + ArtificialViscosityMonaghan(; alpha, beta=0.0, epsilon=0.01) Artificial viscosity by Monaghan (Monaghan 1992, Monaghan 1989), given by ```math @@ -77,6 +69,15 @@ To do so, Monaghan (Monaghan 2005) defined an equivalent effective physical kine ``` where ``d`` is the dimension. +# Keywords +- `alpha`: A value of `0.02` is usually used for most simulations. For a relation with the + kinematic viscosity, see description above. +- `beta=0.0`: A value of `0.0` works well for most fluid simulations and simulations + with shocks of moderate strength. In simulations where the Mach number can be + very high, eg. astrophysical calculation, good results can be obtained by + choosing a value of `beta=2.0` and `alpha=1.0`. +- `epsilon=0.01`: Parameter to prevent singularities. + ## References - Joseph J. Monaghan. "Smoothed Particle Hydrodynamics". In: Annual Review of Astronomy and Astrophysics 30.1 (1992), pages 543-574. @@ -93,34 +94,75 @@ struct ArtificialViscosityMonaghan{ELTYPE} beta :: ELTYPE epsilon :: ELTYPE - function ArtificialViscosityMonaghan(; alpha, beta, epsilon=0.01) + function ArtificialViscosityMonaghan(; alpha, beta=0.0, epsilon=0.01) new{typeof(alpha)}(alpha, beta, epsilon) end end -@inline function (viscosity::ArtificialViscosityMonaghan)(particle_system, neighbor_system, - v_particle_system, - v_neighbor_system, - particle, neighbor, pos_diff, - distance, sound_speed, m_a, m_b, - rho_mean) +@doc raw""" + ViscosityMorris(; nu, epsilon=0.01) + +Viscosity by Morris et al. (1997). + +To the force ``f_{ab}`` between two particles ``a`` and ``b`` due to pressure gradients, +an additional force term ``\tilde{f}_{ab}`` is added with +```math +\tilde{f}_{ab} = m_a m_b \frac{(\mu_a + \mu_b) r_{ab} \cdot \nabla W_{ab}}{\rho_a \rho_b (\Vert r_{ab} \Vert^2 + \epsilon h^2)} v_{ab}, +``` +where ``\mu_a = \rho_a \nu`` and ``\mu_b = \rho_b \nu`` denote the dynamic viscosity +of particle ``a`` and ``b`` respectively, and ``\nu`` is the kinematic viscosity. + +# Keywords +- `nu`: Kinematic viscosity +- `epsilon=0.01`: Parameter to prevent singularities + +## References +- Joseph P. Morris, Patrick J. Fox, Yi Zhu. + "Modeling Low Reynolds Number Incompressible Flows Using SPH". + In: Journal of Computational Physics, Volume 136, Issue 1 (1997), pages 214--226. + [doi: doi.org/10.1006/jcph.1997.5776](https://doi.org/10.1006/jcph.1997.5776) +- Georgios Fourtakas, Jose M. Dominguez, Renato Vacondio, Benedict D. Rogers. + "Local uniform stencil (LUST) boundary condition for arbitrary + 3-D boundaries in parallel smoothed particle hydrodynamics (SPH) models". + In: Computers & Fluids, Volume 190 (2019), pages 346--361. + [doi: 10.1016/j.compfluid.2019.06.009](https://doi.org/10.1016/j.compfluid.2019.06.009) +""" +struct ViscosityMorris{ELTYPE} + nu::ELTYPE + epsilon::ELTYPE + + function ViscosityMorris(; nu, epsilon=0.01) + new{typeof(nu)}(nu, epsilon) + end +end + +function kinematic_viscosity(system, viscosity::ViscosityMorris) + return viscosity.nu +end + +@inline function (viscosity::Union{ArtificialViscosityMonaghan, + ViscosityMorris})(particle_system, neighbor_system, + v_particle_system, v_neighbor_system, + particle, neighbor, pos_diff, + distance, sound_speed, m_a, m_b, + rho_a, rho_b, grad_kernel) (; smoothing_length) = particle_system + rho_mean = 0.5 * (rho_a + rho_b) + v_a = viscous_velocity(v_particle_system, particle_system, particle) v_b = viscous_velocity(v_neighbor_system, neighbor_system, neighbor) v_diff = v_a - v_b - pi_ab = viscosity(sound_speed, v_diff, pos_diff, distance, rho_mean, smoothing_length) + pi_ab = viscosity(sound_speed, v_diff, pos_diff, distance, rho_mean, rho_a, rho_b, + smoothing_length, grad_kernel) - if pi_ab < eps() - return zero(pos_diff) - end - - return -m_b * pi_ab * smoothing_kernel_grad(particle_system, pos_diff, distance) + return m_b * pi_ab end @inline function (viscosity::ArtificialViscosityMonaghan)(c, v_diff, pos_diff, distance, - rho_mean, h) + rho_mean, rho_a, rho_b, h, + grad_kernel) (; alpha, beta, epsilon) = viscosity # v_ab ⋅ r_ab @@ -132,17 +174,28 @@ end # viscosity is used for shocks and not rarefactions." if vr < 0 mu = h * vr / (distance^2 + epsilon * h^2) - return -(alpha * c * mu + beta * mu^2) / rho_mean + return (alpha * c * mu + beta * mu^2) / rho_mean * grad_kernel end - return 0.0 + return zero(v_diff) +end + +@inline function (viscosity::ViscosityMorris)(c, v_diff, pos_diff, distance, rho_mean, + rho_a, rho_b, h, grad_kernel) + (; epsilon, nu) = viscosity + + # TODO This is not correct for two different fluids. It should be `nu_a` and `nu_b`. + mu_a = nu * rho_a + mu_b = nu * rho_b + + return (mu_a + mu_b) / (rho_a * rho_b) * dot(pos_diff, grad_kernel) / + (distance^2 + epsilon * h^2) * v_diff end # See, e.g., -# M. Antuono, A. Colagrossi, S. Marrone. -# "Numerical Diffusive Terms in Weakly-Compressible SPH Schemes." -# In: Computer Physics Communications 183, no. 12 (2012), pages 2570-80. -# https://doi.org/10.1016/j.cpc.2012.07.006 +# Joseph J. Monaghan. "Smoothed Particle Hydrodynamics". +# In: Reports on Progress in Physics (2005), pages 1703-1759. +# [doi: 10.1088/0034-4885/68/8/r01](http://dx.doi.org/10.1088/0034-4885/68/8/R01) function kinematic_viscosity(system, viscosity::ArtificialViscosityMonaghan) (; smoothing_length) = system (; alpha) = viscosity @@ -192,7 +245,8 @@ end @inline function (viscosity::ViscosityAdami)(particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, - distance, sound_speed, m_a, m_b, rho_mean) + distance, sound_speed, m_a, m_b, + rho_a, rho_b, grad_kernel) (; epsilon, nu) = viscosity (; smoothing_length) = particle_system @@ -200,9 +254,6 @@ end v_b = viscous_velocity(v_neighbor_system, neighbor_system, neighbor) v_diff = v_a - v_b - rho_a = particle_density(v_particle_system, particle_system, particle) - rho_b = particle_density(v_neighbor_system, neighbor_system, neighbor) - # TODO This is not correct for two different fluids. It should be `nu_a` and `nu_b`. eta_a = nu * rho_a eta_b = nu * rho_b @@ -215,8 +266,6 @@ end volume_a = m_a / rho_a volume_b = m_b / rho_b - grad_kernel = smoothing_kernel_grad(particle_system, pos_diff, distance) - # This formulation was introduced by Hu and Adams (2006). https://doi.org/10.1016/j.jcp.2005.09.001 # They argued that the formulation is more flexible because of the possibility to formulate # different inter-particle averages or to assume different inter-particle distributions. diff --git a/src/schemes/fluid/weakly_compressible_sph/rhs.jl b/src/schemes/fluid/weakly_compressible_sph/rhs.jl index 5f1c41912..12100612f 100644 --- a/src/schemes/fluid/weakly_compressible_sph/rhs.jl +++ b/src/schemes/fluid/weakly_compressible_sph/rhs.jl @@ -56,7 +56,7 @@ function interact!(dv, v_particle_system, u_particle_system, dv_viscosity(particle_system, neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, rho_mean) + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) dv_surface_tension = surface_tension_correction * surface_tension_force(surface_tension_a, surface_tension_b, diff --git a/src/schemes/solid/total_lagrangian_sph/rhs.jl b/src/schemes/solid/total_lagrangian_sph/rhs.jl index 36c8fd286..8057022e5 100644 --- a/src/schemes/solid/total_lagrangian_sph/rhs.jl +++ b/src/schemes/solid/total_lagrangian_sph/rhs.jl @@ -109,7 +109,7 @@ function interact!(dv, v_particle_system, u_particle_system, dv_viscosity_ = dv_viscosity(neighbor_system, particle_system, v_neighbor_system, v_particle_system, neighbor, particle, pos_diff, distance, - sound_speed, m_b, m_a, rho_mean) + sound_speed, m_b, m_a, rho_a, rho_b, grad_kernel) dv_particle = dv_boundary + dv_viscosity_ diff --git a/src/visualization/write2vtk.jl b/src/visualization/write2vtk.jl index 0372e7167..b5235e772 100644 --- a/src/visualization/write2vtk.jl +++ b/src/visualization/write2vtk.jl @@ -255,7 +255,7 @@ end write2vtk!(vtk, viscosity::Nothing) = vtk -function write2vtk!(vtk, viscosity::ViscosityAdami) +function write2vtk!(vtk, viscosity::Union{ViscosityAdami, ViscosityMorris}) vtk["viscosity_nu"] = viscosity.nu vtk["viscosity_epsilon"] = viscosity.epsilon end @@ -373,14 +373,15 @@ function write2vtk!(vtk, v, u, t, model::BoundaryModelDummyParticles, viscosity, end function write2vtk!(vtk, v, u, t, model::BoundaryModelDummyParticles, - viscosity::ViscosityAdami, system; write_meta_data=true) + viscosity::ViscosityAdami, system; + write_meta_data=true) vtk["hydrodynamic_density"] = [particle_density(v, system, particle) for particle in eachparticle(system)] vtk["pressure"] = model.pressure vtk["wall_velocity"] = view(model.cache.wall_velocity, 1:ndims(system), :) if write_meta_data - vtk["viscosity_model"] = "ViscosityAdami" + vtk["viscosity_model"] = type2string(viscosity) end return vtk diff --git a/test/examples/examples.jl b/test/examples/examples.jl index 19b6ba2b9..08d3bc909 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -8,7 +8,7 @@ "oscillating_drop_2d.jl")) @test sol.retcode == ReturnCode.Success # This error varies between serial and multithreaded runs - @test isapprox(error_A, 0.0001717690010767381, atol=5e-7) + @test isapprox(error_A, 0.0, atol=1.73e-4) @test count_rhs_allocations(sol, semi) == 0 end diff --git a/test/schemes/fluid/fluid.jl b/test/schemes/fluid/fluid.jl index 105fb0ee8..639fa6429 100644 --- a/test/schemes/fluid/fluid.jl +++ b/test/schemes/fluid/fluid.jl @@ -2,3 +2,4 @@ include("weakly_compressible_sph/weakly_compressible_sph.jl") include("rhs.jl") include("pressure_acceleration.jl") include("surface_tension.jl") +include("viscosity.jl") diff --git a/test/schemes/fluid/viscosity.jl b/test/schemes/fluid/viscosity.jl new file mode 100644 index 000000000..a094f9fb9 --- /dev/null +++ b/test/schemes/fluid/viscosity.jl @@ -0,0 +1,65 @@ +@testset verbose=true "Viscosity" begin + particle_spacing = 0.2 + smoothing_length = 1.2 * particle_spacing + smoothing_kernel = SchoenbergCubicSplineKernel{2}() + sound_speed = 10 * sqrt(9.81 * 0.9) + + state_equation = StateEquationCole(; sound_speed, reference_density=1000.0, + exponent=7, clip_negative_pressure=false) + + fluid = rectangular_patch(particle_spacing, (3, 3), seed=1) + + system_wcsph = WeaklyCompressibleSPHSystem(fluid, ContinuityDensity(), + state_equation, smoothing_kernel, + smoothing_length) + + v_diff = [0.1, -0.75] + pos_diff = [-0.5 * smoothing_length, 0.75 * smoothing_length] + distance = norm(pos_diff) + rho_a = rho_b = rho_mean = 1000.0 + + grad_kernel = TrixiParticles.smoothing_kernel_grad(system_wcsph, pos_diff, + distance) + + # We only test here that the values don't change + @testset verbose=true "`ArtificialViscosityMonaghan`" begin + viscosity = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) + + dv = viscosity(sound_speed, v_diff, pos_diff, distance, + rho_mean, rho_a, rho_b, smoothing_length, + grad_kernel) + + @test isapprox(dv[1], -0.007073849138494646, atol=6e-15) + @test isapprox(dv[2], 0.01061077370774197, atol=6e-15) + end + @testset verbose=true "`ViscosityMorris`" begin + viscosity = ViscosityMorris(nu=7e-3) + + dv = viscosity(sound_speed, v_diff, pos_diff, distance, + rho_mean, rho_a, rho_b, smoothing_length, + grad_kernel) + + @test isapprox(dv[1], -0.00018421886647594437, atol=6e-15) + @test isapprox(dv[2], 0.0013816414985695826, atol=6e-15) + end + @testset verbose=true "`ViscosityAdami`" begin + viscosity = ViscosityAdami(nu=7e-3) + + v = fluid.velocity + + m_a = 0.01 + m_b = 0.01 + + v[1, 1] = v_diff[1] + v[2, 1] = v_diff[2] + v[1, 2] = 0.0 + v[2, 2] = 0.0 + + dv = viscosity(system_wcsph, system_wcsph, + v, v, 1, 2, pos_diff, distance, + sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) + + @test isapprox(dv[1], -1.8421886647594435e-6, atol=6e-15) + @test isapprox(dv[2], 1.3816414985695826e-5, atol=6e-15) + end +end diff --git a/test/validation/validation.jl b/test/validation/validation.jl index 1fd67ac28..c5aa9bed1 100644 --- a/test/validation/validation.jl +++ b/test/validation/validation.jl @@ -48,16 +48,16 @@ @test count_rhs_allocations(sol, semi) == 0 if VERSION >= v"1.10" - @test isapprox(error_edac_P1, 0, atol=1e-9) - @test isapprox(error_edac_P2, 0, atol=6e-12) + @test isapprox(error_edac_P1, 0, atol=eps()) + @test isapprox(error_edac_P2, 0, atol=eps()) @test isapprox(error_wcsph_P1, 0, atol=eps()) @test isapprox(error_wcsph_P2, 0, atol=eps()) else # 1.9 causes a large difference in the solution - @test isapprox(error_edac_P1, 0, atol=1e-8) - @test isapprox(error_edac_P2, 0, atol=1e-10) - @test isapprox(error_wcsph_P1, 0, atol=10) - @test isapprox(error_wcsph_P2, 0, atol=1e-3) + @test isapprox(error_edac_P1, 0, atol=4e-9) + @test isapprox(error_edac_P2, 0, atol=3e-11) + @test isapprox(error_wcsph_P1, 0, atol=5.1) + @test isapprox(error_wcsph_P2, 0, atol=6e-4) end # Ignore method redefinitions from duplicate `include("../validation_util.jl")` diff --git a/validation/dam_break_2d/validation_reference_edac_00075.json b/validation/dam_break_2d/validation_reference_edac_00075.json index bd53415c1..e4855ef6a 100644 --- a/validation/dam_break_2d/validation_reference_edac_00075.json +++ b/validation/dam_break_2d/validation_reference_edac_00075.json @@ -211,7 +211,7 @@ }, "meta": { "julia_version": "1.10.2", - "solver_version": "98fe4b85-dirty", + "solver_version": "v0.1.0-41-ga05975e-dirty", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { diff --git a/validation/dam_break_2d/validation_reference_wcsph_0001875.json b/validation/dam_break_2d/validation_reference_wcsph_0001875.json index 143fa2d58..70ae2b556 100644 --- a/validation/dam_break_2d/validation_reference_wcsph_0001875.json +++ b/validation/dam_break_2d/validation_reference_wcsph_0001875.json @@ -136,67 +136,67 @@ 0.0, 0.0, 0.0, - 45.784395004426884, - 29.0963004807273, - 82.09143011178865, - 480.5173247987532, - 973.3483022920233, - 950.6415064448477, - 1205.9391286167588, - 1290.217132972779, - 1493.2505500620105, - 791.1303597083299, - 1621.3332817032026, - 1209.649967879809, - 1450.1769634267432, - 1667.706462908013, - 2021.1368753192794, - 2064.739712897202, - 2257.648079488593, - 2059.094957473553, - 2005.5908178585112, - 2245.7005690725623, - 2144.823341827724, - 2401.6276418260254, - 2511.8101954813187, - 2392.8729062435004, - 2728.389883490009, - 2453.68997501138, - 2574.8103376583067, - 2644.8208050406383, - 2795.560194487943, - 2760.374005463031, - 2665.0826676713305, - 2729.435007141164, - 2781.3157684269163, - 2626.429904341887, - 2825.0779162423023, - 2715.766574533961, - 2850.599606872627, - 2717.4272822426187, - 2855.0549332258984, - 2978.689965745355, - 2976.375589014323, - 2921.329964480744, - 3030.587428212174, - 3142.963554938571, - 3064.474348949782, - 3292.874691549051, - 3592.4250266859026, - 4290.388250924816, - 5103.386394463199, - 6745.91427044437, - 5592.075646090989, - 5198.727685514661, - 5192.428024560253, - 4810.974638781969, - 4057.474362834096, - 4982.9085809799635, - 4326.893681723601, - 2365.5792257312305, - 794.455434481232, - 115.03811223063278, - 1595.8828694583096, + 45.795417909753986, + 30.986970334726653, + 88.05948840868432, + 552.7825716933128, + 920.3939791879527, + 843.9803228418943, + 1198.4306383314438, + 1307.4218650259777, + 1551.1614335277905, + 820.9324305229308, + 1618.2777784691862, + 1118.7361526076513, + 1340.3279552181161, + 1819.3970368003422, + 2130.606457954829, + 2169.4926936753154, + 2270.277029561626, + 2182.734498203901, + 2027.1358420234967, + 2204.198718226254, + 1968.7434281115009, + 2492.7425782632013, + 2804.8952931647045, + 2286.915020570262, + 2713.028093827061, + 2557.8946262705217, + 2676.7335881591903, + 2630.692946854259, + 2780.879159625962, + 2566.0554325798157, + 2876.339978484625, + 2687.078292376781, + 2534.9905689398897, + 2805.2451019180644, + 2921.735789046199, + 2609.9054081748445, + 2837.1289059887367, + 2875.3167107168706, + 2833.6939092357015, + 2947.6543723563022, + 2905.713496985916, + 2831.391646895673, + 2942.5289706903586, + 3146.56291085397, + 3186.9261233249576, + 3459.6124426849046, + 3653.2887908885605, + 4297.702961121691, + 5057.085382681299, + 6790.01681537226, + 5714.950602336447, + 5164.43699821667, + 5308.423404900976, + 4797.701901717602, + 4208.150962724796, + 5096.441180151019, + 4805.670702674106, + 3275.206200205639, + 1509.8437133185737, + 8196.388479597907, + 1389.9556140209475, 48.06627062092718, 142.2684567691535, 426.8228505863073, @@ -210,8 +210,8 @@ "type": "series" }, "meta": { - "julia_version": "1.10.3", - "solver_version": "v0.1.1-14-g16ba4ad3-dirty", + "julia_version": "1.10.2", + "solver_version": "v0.1.0-42-gaa74848-dirty", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { @@ -355,58 +355,58 @@ 0.0, 0.0, 0.0, - 8.134978657093146, - 16.116098542012196, - 14.366905888289647, - 16.181410038300935, - 9.851210123836067, - 27.796007277259566, - 11.17503157781834, - 1.0934215179075044, - 12.844439624904116, - 5.940102595559185, - 19.600847118274284, - 38.053279321766254, - 21.295762396264585, - 12.976253972328617, - 81.24650675781987, - 34.479345653339756, - 245.37183177529965, - 306.79960278612987, - 160.13536073045663, - 315.6922797256672, - 296.080651120703, - 333.2708743720141, - 513.4937918935809, - 495.83807208024655, - 520.7690916129709, - 725.2386100241195, - 858.3613537623429, - 898.8101400627029, - 1026.828064254389, - 1190.5527204068717, - 1260.8739368797535, - 1379.7241579310062, - 1441.7980954536122, - 1477.007856603721, - 1547.1434162566334, - 1583.8840399538308, - 1700.7432601788023, - 1785.0753079682509, - 1733.8949904850874, - 1764.7490159275246, - 1432.4338785761856, - 395.27999913305814, - 34.94150836405466, - 24.900856184771346, - 7.679262019233791, - 28.035980801784653, - 8.438531609013937, - 8.387476071262533, - 16.627723954709776, - 16.034203643304302, - 0.0, + 8.137474398356465, + 16.498859955926168, + 15.164345238697965, + 14.497070409731915, + 13.701694162443477, + 17.05819796035522, + 6.862768172486139, + 10.157578690994551, + 14.182020846567593, + 5.092217430219432, + 12.946287418688897, + 19.32316444599005, + 12.839915675024358, + 20.61520320591055, + 132.38172851891431, + 5.79592905224373, + 160.595377745003, + 177.26580283234026, + 243.60522337506455, + 245.64844112230676, + 269.2781916856497, + 421.4220230166705, + 483.7230593494285, + 493.7749362856113, + 502.5221739311165, + 688.2620827693952, + 845.0400854295897, + 994.7161645496323, + 952.950100203993, + 1109.283301027545, + 1256.4344362923243, + 1357.2722316672923, + 1448.776623129004, + 1471.2694024093191, + 1580.8239356843492, + 1641.1726001586471, + 1708.2631397018104, + 1663.758529848884, + 1673.1654103072156, + 1749.8037198896054, + 1348.0922719246669, + 330.25013871710183, + 19.35958286745482, + 3.5368803531079336, + 16.0725928821717, + 16.254801945806612, + 6.1359971374734155, + 8.86353007399404, + 12.474117547442278, + 15.358144333910502, 0.0, + 4.2698150843165505, 0.0, 0.0, 0.0, @@ -531,97 +531,97 @@ "system_name": "fluid", "values": [ 1.1990625, - 1.206291218862206, - 1.2251427920923224, - 1.2533926435243583, - 1.2887327249295564, - 1.3298850673213958, - 1.3762353875886921, - 1.4273098662098027, - 1.4826998207876958, - 1.5420431133536183, - 1.6049044398640613, - 1.6709129699713863, - 1.7398473740262257, - 1.8114203023139126, - 1.8854018187827515, - 1.9616117463234828, - 2.0398817409263104, - 2.1200612626004065, - 2.2020224091181038, - 2.285594356795057, - 2.370625789198274, - 2.4569731709642886, - 2.544597123506079, - 2.633505929520998, - 2.7236864465713833, - 2.8147879428876723, - 2.906985603226265, - 3.000343813149392, - 3.0938721073746325, - 3.1880835992151018, - 3.219445769851805, - 3.2194928505759695, - 3.218972846850055, - 3.218850285198104, - 3.2188789186256095, - 3.218899343489275, - 3.2188060714124114, - 3.218805595407585, - 3.2187922335479557, - 3.2187791224898405, - 3.2188049650925525, - 3.218802965005493, - 3.21874633513394, - 3.218778527620756, - 3.218787800086968, - 3.21877763324876, - 3.2187869503277957, - 3.218801723646658, - 3.2188059646005005, - 3.218802904113761, - 3.2187598378629003, - 3.2187926829108324, - 3.2187830153192003, - 3.2187940663279653, - 3.21887316742644, - 3.2189410001275878, - 3.218960821745065, - 3.218969175054672, - 3.219004759769721, - 3.219034463808112, - 3.2190504796248742, - 3.2190521499490696, - 3.219306855849682, - 3.219525377857718, - 3.2197256432100385, - 3.2199100830560936, - 3.2200888769402294, - 3.220104092135281, - 3.2196277874250585, - 3.2196708582832905, - 3.2192796817127145, - 3.2189086136367777, - 3.218969678322776, - 3.21907942406814, - 3.2190411300367785, - 3.2188676944901733, - 3.2188447959347966, - 3.2189454004639897, - 3.218944669941815, - 3.218860166560762, - 3.2187814287838155, - 3.2188050298515645, - 3.2187352008556602, - 3.2187739282258416, - 3.21912366161124, - 3.2186659672343008, - 3.220265299799684, - 3.218952070255997, - 3.220715876032403, - 3.220063428269404, - 3.221695913083556, - 3.221860930773287, + 1.2062912188622061, + 1.2251427920923226, + 1.2533926435243588, + 1.288732724929557, + 1.329885067321398, + 1.3762353875886937, + 1.427309866209793, + 1.4826998207878175, + 1.5420431133540629, + 1.6049044398646288, + 1.6709129699718779, + 1.7398473740274438, + 1.8114203023159112, + 1.885401818785752, + 1.9616117463289056, + 2.039881740936874, + 2.120061262605599, + 2.2020224091098526, + 2.285594356802947, + 2.370625789283306, + 2.4569731710870077, + 2.5445971236586034, + 2.6335059297158616, + 2.723686446668211, + 2.8147879426926212, + 2.9069856023019787, + 3.000343813002061, + 3.093872107791237, + 3.188083599333948, + 3.219445770962338, + 3.2194928565875323, + 3.218972833025382, + 3.2188364287840585, + 3.2188952464474996, + 3.2188298138468485, + 3.218820009743553, + 3.2188110597955935, + 3.218828763867503, + 3.2188012953477307, + 3.2188133906357725, + 3.2188211972434915, + 3.2187981546861844, + 3.2188060852462024, + 3.21879698019165, + 3.2188088732280495, + 3.2187737495331152, + 3.218903917738651, + 3.218776459547487, + 3.218803714669472, + 3.2187503464220484, + 3.218795721437317, + 3.218754308375021, + 3.2187822988310626, + 3.2187739284318457, + 3.218770122015697, + 3.2187997574578473, + 3.2188318342355196, + 3.2187869809645915, + 3.2187639760733786, + 3.218824288376943, + 3.2190519021135025, + 3.219306541647642, + 3.2195250599738605, + 3.219725399128985, + 3.219909992321128, + 3.220088762580422, + 3.2201018271885893, + 3.2196238979222653, + 3.2193897456983307, + 3.21911564460507, + 3.2190502512824275, + 3.2189232478583474, + 3.2189093636634465, + 3.2188940148358705, + 3.218767637296241, + 3.21888873879008, + 3.2189397605124817, + 3.2188137685882583, + 3.218731381174506, + 3.2188955184409904, + 3.2190142324249567, + 3.218810411312744, + 3.218722729217857, + 3.2190287832726505, + 3.219089215940388, + 3.220891772767404, + 3.220891947678954, + 3.221120116652444, + 3.2206949160717904, + 3.220227866018156, + 3.2199378138478973, 3.2203023876287897, 3.2202986314807363, 3.219811690478277, @@ -779,50 +779,50 @@ 0.0, 0.0, 0.0, - 30.94820884379941, - 22.90822490454815, - 7.396807867640719, - 7.414100503599705, - 7.607592499293037, - 8.584551268164507, - 15.886168735323801, - 17.583653786794144, - 3.998937810844999, - 26.61922910335046, - 1.143643827251536, - 2.827292135466822, - 1.5729938238346048, - 4.595785914798647, - 2.9661048359859876, - 0.279238435624049, - 0.5856484421451172, - 0.009552267527160834, - 0.0010030857424828412, - 3.720823736828451, - 3.9029906118718665, - 0.034405964785396745, - 3.820213730523163, - 8.931893089299567, - 11.186430353660104, - 4.365343796995128, - 9.257621003118746, - 8.875071770189836, - 5.232857413378655, - 35.57141974974755, - 2.695571516358929, - 0.2777628099152869, - 4.869266502185761, - 10.106394305611726, - 4.6574265770569045, - 4.788598767426367, - 16.250354085392793, - 31.4444062316207, - 8.623685759947092, - 20.213228683789133, - 6.33056812306364, + 30.954596433372068, + 22.918495883508655, + 18.68653875959768, + 6.342328285895448, + 6.010974893248376, + 6.921352268527839, + 12.641287660360078, + 5.324096059467818, + 12.299384992232632, + 5.960799532361503, + 1.692622915372987, + 10.229972006718947, + 17.865220878093922, + 2.140105801547046, + 0.18424376218570795, + 14.127723695934293, + 2.3026452180996864, + 3.316254942849902, + 0.08793564479420095, + 0.07533401695691826, + 4.278889300902068, + 7.602004560117197, + 1.4544293901714205, + 2.772638097843874, + 7.52195746562243, + 4.427164942111693, + 1.3496900702494476, + 8.31428790635449, + 4.5454298608070935, + 5.9366427200282414, + 11.463972179316809, + 11.14697704435911, + 9.130044979184483, + 9.767051289777724, + 27.602126502373164, + 3.778250708541731, + 26.35607071767321, + 6.775108546420978, + 11.369481071601523, + 14.612697591736914, + 20.49504646337744, + 1.1996206222897805, 0.0, 0.0, - 20.32651578506585, 0.0, 0.0, 0.0, diff --git a/validation/dam_break_2d/validation_reference_wcsph_00075.json b/validation/dam_break_2d/validation_reference_wcsph_00075.json index fed5edbce..0763d5b02 100644 --- a/validation/dam_break_2d/validation_reference_wcsph_00075.json +++ b/validation/dam_break_2d/validation_reference_wcsph_00075.json @@ -137,81 +137,81 @@ 0.0, 0.0, 0.0, - 58.344991574597245, - 138.3079819297823, - 422.44543275991543, - 916.8379090327911, - 419.0542812820178, - 1347.4169400618655, - 2349.6352547704764, - 1064.5188397369652, - 27.28474677852581, - 101.40272350576802, - 781.2699036849094, - 1397.375646820365, - 1380.062731506342, - 1194.529392969989, - 577.3455172987748, - 837.930707986937, - 3081.161976053418, - 3318.476728313081, - 840.9237550556791, - 2892.0625372919735, - 3033.1045842317258, - 1259.1306900753145, - 3817.228202280832, - 1635.2988377088582, - 3229.66356315531, - 3186.684524742882, - 985.5939469237841, - 4559.785446597643, - 2323.036294969968, - 870.2389644974486, - 3913.740475400634, - 4345.220860174271, - 1727.481685909364, - 317.0817954906028, - 2136.9635058026806, - 4477.765360075153, - 5281.26500304141, - 4350.273753361081, - 2546.9182615226355, - 1165.5955609655477, - 734.9000832904637, - 461.3215749633864, - 519.663763497804, - 387.3458428471696, - 419.23300905557824, - 1315.620427554483, - 3777.79723772579, - 7077.795941168353, - 6982.577978305283, - 7332.201328785472, - 6519.795708535393, - 4443.747044691211, - 7050.928045952259, - 2588.3483986830647, - 1964.994261825345, - 1933.4846460072567, - 5032.453646266516, - 4272.017659452722, - 2275.8426396788036, - 879.224598485221, - 1907.8934504222573, - 1363.6979297710648, - 595.0588557689321, - 2553.1879813651676, - 341.1912981329048, - 1605.7780612629278, - 524.6937275195312, - 2009.0164472926597 + 58.3449915737484, + 138.30798197205348, + 422.4454327143758, + 916.8379084200394, + 419.0542820835238, + 1347.4169383327599, + 2349.6352553794636, + 1064.518846925364, + 27.284751508188627, + 101.4027110447832, + 781.269929694757, + 1397.375595899754, + 1380.062438593228, + 1194.5290438249299, + 577.3446597874912, + 837.9255914093883, + 3081.1744294335917, + 3318.4600872182405, + 840.9522826551704, + 2892.2815052491255, + 3032.9001716109915, + 1260.6934944658822, + 3817.379408690888, + 1635.6748520209878, + 3229.882576401444, + 3186.2844071491168, + 986.1747562380727, + 4559.229103674002, + 2323.662542906834, + 869.9397283308729, + 3913.8661202543517, + 4345.322714334653, + 1727.691083770075, + 318.1022245457738, + 2137.4034881964117, + 4477.132376971367, + 5280.620479827444, + 4348.915605753067, + 2548.152118233453, + 1166.5958655127329, + 733.7269976054581, + 449.6293773957523, + 528.8475316972172, + 433.4970917379593, + 420.1311022195725, + 1286.81821996594, + 3764.7503687159733, + 7145.96986424888, + 7066.063851401108, + 7353.562854052298, + 6472.864968549782, + 4306.347988855297, + 6918.947963743149, + 3843.2505962682435, + 1933.8221271593907, + 1179.7008451321374, + 4632.996937909627, + 3317.094937208304, + 1656.3251394624062, + 388.08068284496295, + 1879.016655252052, + 1609.0588975679866, + 542.3340923494694, + 2371.0119086155237, + 1156.028036930474, + 3685.0239743888806, + 1009.9810839922333, + 1979.0594313323793 ], "datatype": "Float64", "type": "series" }, "meta": { - "julia_version": "1.10.3", - "solver_version": "v0.1.1-12-g81063e0c-dirty", + "julia_version": "1.10.2", + "solver_version": "v0.1.0-41-ga05975e-dirty", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { @@ -357,52 +357,52 @@ 0.0, 0.0, 0.0, - 20.313232646831917, - 45.50619773676554, - 6.167987928920181, - 23.887316107055206, - 4.430146943903635, - 8.768108012133887, - 8.968265715355544, - 40.80561634950723, - 13.266294706155914, - 7.219760928606955, - 35.837355015060986, - 21.21805714727239, - 3.4965726828218804, - 85.24808274134794, - 198.65072969366972, - 84.55703995068527, - 222.2918851291387, - 255.16931211945393, - 287.2663679859116, - 390.13505695583376, - 462.164671744731, - 478.8093301950632, - 667.5463321442569, - 708.8526827117113, - 644.7813417154648, - 1006.6892456341614, - 1022.7891602511103, - 1088.3173425223758, - 974.9347449872591, - 1302.0652060904554, - 1561.291264087963, - 1894.1485159976914, - 1935.3743926330087, - 1664.0454616980896, - 1417.613710039965, - 1572.8675261801695, - 1469.2600241416771, - 1576.6503876078307, - 1357.7509207067628, - 627.6625285260745, - 171.3536996335749, - 16.555442318313464, + 20.31323264129541, + 45.50619772878511, + 6.167987914971771, + 23.88731586293732, + 4.430147325868489, + 8.768109090756356, + 8.968266092741535, + 40.8056081128188, + 13.266289074276875, + 7.21976446106391, + 35.837578093058696, + 21.219114696929093, + 3.49673027837686, + 85.22260579192265, + 198.6594657678087, + 84.42527669302487, + 222.28302368858368, + 255.70201681023167, + 287.17352794962125, + 390.1368774908667, + 461.6354219207832, + 479.2956919031473, + 668.131582134366, + 708.6708868363967, + 645.0338221375232, + 1006.9495976769247, + 1022.5991860047172, + 1088.542578329139, + 974.647785952737, + 1302.905562870689, + 1559.2866782248348, + 1896.6993512365123, + 1934.924534902601, + 1663.477452114545, + 1419.3260734938583, + 1576.7967846720799, + 1467.524148572022, + 1574.549493950828, + 1358.3707085870317, + 626.3587223898397, + 170.68459310977235, + 16.588120937902353, 0.0, 0.0, 0.0, - 6.927952002970758, + 6.927951997943524, 0.0, 0.0, 0.0, @@ -540,96 +540,96 @@ 1.415616412333505, 1.470830679860593, 1.5296724290345078, - 1.5918075373727558, - 1.6569645052768505, - 1.7248666483318564, - 1.7952375999021615, - 1.8679490290611642, - 1.942910014856134, - 2.0200484369245837, - 2.0991787194449247, - 2.1799742972516323, - 2.262067264672173, - 2.3452911203047084, - 2.429713872083454, - 2.515361512205054, - 2.602293896796202, - 2.6906214381249502, - 2.780398787100884, - 2.871371035052104, - 2.9631624013020352, - 3.0557396096262015, - 3.148099574918203, + 1.5918075373727552, + 1.6569645052768507, + 1.7248666483318587, + 1.7952375999021644, + 1.8679490290611684, + 1.9429100148561393, + 2.020048436924589, + 2.0991787194449283, + 2.179974297251634, + 2.2620672646721722, + 2.345291120304705, + 2.4297138720834495, + 2.5153615122050486, + 2.602293896796195, + 2.690621438124939, + 2.780398787100872, + 2.8713710350520953, + 2.963162401302029, + 3.055739609626199, + 3.1480995749182057, 3.217883381034535, - 3.2182521991925985, - 3.2190358988544725, - 3.219663973025696, - 3.2192588413421133, - 3.2183918503396116, - 3.218238394517403, - 3.218217349335105, - 3.2182135276397252, - 3.2182331098537738, - 3.2182123548665516, - 3.2182351451319717, - 3.2181860314410704, - 3.2181826027766878, - 3.218261609150298, - 3.218290039492455, - 3.2181013332172035, - 3.2181807074655313, - 3.2182413760911555, - 3.218236604272375, - 3.2181908383681304, - 3.218206894438717, - 3.218192216494783, - 3.218218304785117, - 3.218209781428932, - 3.2182014695397645, - 3.2181983905211684, - 3.2182177816258646, - 3.2182083303461964, - 3.2182141488580966, - 3.2181940144865506, - 3.2181949194681048, - 3.218204747679178, - 3.2181938551074114, - 3.218197842690469, - 3.218205426948579, - 3.2182070153293743, - 3.218334642138954, - 3.218585647160275, - 3.218886605377294, - 3.2192020710208524, - 3.219574419943034, - 3.2195825815053456, - 3.2189357478307796, - 3.2181998457861263, - 3.2181963889480194, - 3.2181940755753153, - 3.218211667716726, - 3.2182091551294123, - 3.218208689192436, - 3.2182110246884466, - 3.2182138808799348, - 3.2181947548040326, - 3.21908316441958, - 3.218192847601368, - 3.2182125261578234, - 3.2182085567456062, - 3.218202507636954, - 3.2181962807514024, - 3.2182248069249417, - 3.2181984126618266, - 3.2182146801522773, - 3.219360754040278, - 3.2206149671663638, - 3.220484842692027, - 3.219420656013077, - 3.2194090912602475, - 3.2194099228296955, - 3.219378813315998, - 3.2195112418230374 + 3.218252199192597, + 3.2190358988544663, + 3.2196639730256824, + 3.2192588413420857, + 3.218391850339578, + 3.2182383945174027, + 3.2182173493351147, + 3.218213527639729, + 3.2182331098537924, + 3.218212354866577, + 3.2182351451319797, + 3.2181860314410864, + 3.2181826027767317, + 3.2182616091503014, + 3.2182900394924134, + 3.2181013332171955, + 3.218180707465579, + 3.2182413760911537, + 3.2182366042723656, + 3.218190838368207, + 3.218206894438811, + 3.2181922164947707, + 3.2182183047852124, + 3.218209781429125, + 3.2182014695396095, + 3.2181983905210876, + 3.218217781625702, + 3.2182083303459947, + 3.2182141488580243, + 3.2181940144865617, + 3.21819491946819, + 3.218204747678986, + 3.2181938551073994, + 3.2181978426903326, + 3.21820542694873, + 3.2182070153294915, + 3.218334642143193, + 3.218585647166958, + 3.2188866053900216, + 3.21920207104197, + 3.219574419965984, + 3.2195825814614847, + 3.218935747756291, + 3.218199845786071, + 3.21819638894806, + 3.2181940755753295, + 3.2182116677167216, + 3.2182091551294025, + 3.218208689192445, + 3.2182110246884483, + 3.2182138808799365, + 3.218194754804018, + 3.2194851978366095, + 3.218192847601363, + 3.218212526157722, + 3.218208556745474, + 3.2182025076371104, + 3.2181962807514766, + 3.218224806925123, + 3.218198412662153, + 3.2182146801528235, + 3.21819542100968, + 3.2189620377926227, + 3.218968460252789, + 3.2189665397479192, + 3.2189697305224594, + 3.2189438274647815, + 3.218954688518879, + 3.2189532497664337 ], "datatype": "Float64", "type": "series" @@ -782,42 +782,42 @@ 0.0, 0.0, 0.0, - 48.77745540928442, - 10.358164540228152, - 15.049608310077256, - 8.912918416371017, - 3.1038547498987126, - 9.080093846498611, - 11.001862497415107, - 4.389149064297083, - 9.327454050543402, - 8.079108270395704, - 8.873144682478369, - 11.70989304656271, - 9.778468385332287, - 10.139553590994097, - 7.951623034613833, - 3.903962005507205, - 7.599910342535063, - 8.624094009131955, - 8.186379762767322, - 4.029544470513885, - 3.983062631983286, - 3.2253097131812596, - 13.985451999478542, - 2.350909911211228, - 9.466000090043943, - 15.270276355793007, - 4.803286926676352, - 17.17179251061254, - 16.4819672034308, - 35.117451725247186, + 48.777455403164595, + 10.358164544007067, + 15.04960832545812, + 8.912918411774163, + 3.1038547518232447, + 9.080093791795168, + 11.001861768010636, + 4.389149228051656, + 9.327505518523214, + 8.079456786928928, + 8.873355574751367, + 11.709335633413499, + 9.772263422141894, + 10.115246645209131, + 7.9532840524051505, + 3.9255626813013116, + 7.572786369647373, + 8.644475585297913, + 8.179980736326566, + 4.026316518597092, + 3.9816360448056907, + 3.2242512880165095, + 13.986955872502259, + 2.3506997098606854, + 9.465894897841721, + 15.270226515067424, + 4.803273021482147, + 17.17178281543346, + 16.48196895654314, + 35.117449625443314, 0.0, 0.0, 0.0, 0.0, 0.0, - 6.527361143284767, + 6.527361132717613, 0.0, 0.0, 0.0, @@ -844,4 +844,4 @@ "datatype": "Float64", "type": "series" } -} +} \ No newline at end of file diff --git a/validation/dam_break_2d/validation_reference_wcsph_0015.json b/validation/dam_break_2d/validation_reference_wcsph_0015.json index 670a13f7a..56662a459 100644 --- a/validation/dam_break_2d/validation_reference_wcsph_0015.json +++ b/validation/dam_break_2d/validation_reference_wcsph_0015.json @@ -139,79 +139,79 @@ 0.0, 0.0, 0.0, - 741.993989499188, - 1074.8234774705008, - 780.1667978016657, - 750.2646819009395, - 1867.0876175708631, - 2289.2233812476165, - 1854.7785819902779, - 1898.4536989266617, - 1467.7442261288045, - 1634.0625951725135, - 1051.0175330299053, - 1792.9924178916986, - 2224.449593047438, - 2072.497420987851, - 1759.794387609291, - 2213.4462548625984, - 2195.882761495324, - 2296.9359071622694, - 2110.261628995037, - 2199.283711290986, - 2970.3736472205856, - 2306.9377872024497, - 2708.5496860356457, - 3000.1783021126685, - 2269.7935071889146, - 2084.462044237944, - 2584.891574798109, - 2922.9584610302077, - 2700.9638330280814, - 2912.962403023275, - 2333.0896174441677, - 2272.70021772166, - 2482.9249133633994, - 2818.1450177572733, - 2995.435467727107, - 2909.3691069629745, - 3100.77267069721, - 3025.9643852018985, - 3235.2107438665453, - 3257.726890859399, - 2988.8564114485575, - 3115.182446969521, - 3505.8949466499425, - 3713.810524900102, - 4590.058796083151, - 4355.887013844826, - 6158.300373074685, - 6758.121068681193, - 5923.416082268637, - 3778.132671948576, - 853.0072739525398, - 1654.167440624306, - 1293.1342883693856, - 5038.477782149967, - 2514.857909325529, - 2194.8799832441873, - 670.5436529503403, - 1266.7144427699454, - 637.4225382331412, - 1349.35969801679, - 2013.3006836641252, - 1472.444123400624, - 1092.6572808458293, - 1240.5339171417113, - 3713.2055968317923, - 794.5971054101079 + 741.9939895034533, + 1074.823477526557, + 780.1667978278533, + 750.2646818625105, + 1867.0876195480973, + 2289.223377185172, + 1854.778592795079, + 1898.4536965337563, + 1467.7442181784463, + 1634.0625814441341, + 1051.0175421397894, + 1792.9924986054696, + 2224.44957136399, + 2072.497448511862, + 1759.7941823838978, + 2213.446718347899, + 2195.8823765248344, + 2296.936996796624, + 2110.26221033083, + 2199.2841631050296, + 2970.3732277673735, + 2306.9379075970605, + 2708.5489780064477, + 3000.1787276966415, + 2269.7922168984032, + 2084.462803522515, + 2584.892959415741, + 2922.9579331489513, + 2700.96048467049, + 2912.959472927148, + 2333.0945761449198, + 2272.699760539699, + 2482.927145390145, + 2818.1481398849137, + 2995.431879484659, + 2909.3787969527793, + 3100.770191742455, + 3025.9650728686365, + 3235.200330338881, + 3257.7536083922405, + 2988.768529824477, + 3115.2219953824097, + 3505.964207981934, + 3713.9020435212456, + 4590.047833488942, + 4356.981088994886, + 6158.5534685202465, + 6758.256849320384, + 5924.747293352886, + 3779.601511593646, + 853.4159363416477, + 1654.7441086189888, + 1292.725609178739, + 5035.3022616347025, + 2519.4325603311504, + 2200.0503609676116, + 677.4791455662695, + 1260.2892839989977, + 632.0785128025252, + 1342.5190787770055, + 2015.993021332793, + 1480.8010982207184, + 1092.6724464916224, + 1240.7416518698235, + 3695.038480349523, + 790.4034873096698 ], "datatype": "Float64", "type": "series" }, "meta": { - "julia_version": "1.10.3", - "solver_version": "v0.1.1-12-g81063e0c", + "julia_version": "1.10.2", + "solver_version": "v0.1.0-40-gafc1aaf", "solver_name": "TrixiParticles.jl" }, "pressure_P2_fluid_1": { @@ -360,41 +360,41 @@ 0.0, 0.0, 0.0, - 47.77942633833136, - 44.375511308801165, - 30.473913165821585, - 8.476970647743345, - 33.57949027495734, - 1.8632530695075122, - 22.444832138896185, - 5.229749466466734, - 12.45236780256954, - 113.28034507259024, - 16.872509526872864, - 183.5309631867931, - 124.91977274284777, - 119.71991124316344, - 157.1870298933421, - 274.4122702419585, - 366.9283073596733, - 436.7984767499305, - 656.8651152221494, - 693.6807559949332, - 709.8622378950579, - 940.2747009417171, - 713.595404833571, - 1152.7154288766947, - 1175.086016427977, - 1117.072092779762, - 1473.3110894037666, - 1398.3815929624138, - 1426.719077735046, - 1724.6637655473126, - 1689.600981475803, - 1716.4157506396168, - 2313.2400917892637, - 1548.166884927166, - 873.0003498783823, + 47.779426335028596, + 44.3755113096053, + 30.47391316492869, + 8.476970547767014, + 33.57949084782525, + 1.86325295891417, + 22.444830543469468, + 5.2297499045038816, + 12.452369473099422, + 113.28030365232425, + 16.872542628935967, + 183.53132403395512, + 124.91982103570506, + 119.72063152487195, + 157.1866385869409, + 274.41221818832616, + 366.9284030230333, + 436.7981563485061, + 656.8644583863837, + 693.680435675162, + 709.8626787029332, + 940.2768525263258, + 713.5965127260125, + 1152.7101065183833, + 1175.085784491902, + 1117.0751123518626, + 1473.3121783676756, + 1398.3778231249435, + 1426.7193716891154, + 1724.669726207463, + 1689.5768339343053, + 1716.4132703616517, + 2313.2808326123604, + 1548.3843452879687, + 872.6651774696463, 0.0, 0.0, 0.0, @@ -533,23 +533,23 @@ 1.1925, 1.1979096218668641, 1.2114046382489772, - 1.2336420690085275, - 1.264406620833113, - 1.30283624717504, - 1.3476751813676164, - 1.3979265161874295, - 1.4526822092129983, - 1.5111470319630946, - 1.572927201056311, - 1.6376619970447712, - 1.7050782695517803, - 1.7749222353523972, - 1.8470381017706896, - 1.9214486425691817, - 1.9977114877634146, - 2.075603680636452, - 2.1548845384780377, - 2.2354024463749025, + 1.2336420690085277, + 1.2644066208331137, + 1.3028362471750405, + 1.3476751813676169, + 1.39792651618743, + 1.452682209212999, + 1.5111470319630955, + 1.5729272010563116, + 1.6376619970447719, + 1.7050782695517805, + 1.7749222353523975, + 1.847038101770687, + 1.9214486425691792, + 1.997711487763412, + 2.075603680636451, + 2.154884538478037, + 2.235402446374902, 2.317146330306622, 2.400082966455745, 2.4841380843809278, @@ -557,79 +557,79 @@ 2.6555845619869305, 2.742628218729764, 2.8301077178490366, - 2.917793645723983, - 3.0055781193133417, - 3.0934656910674185, - 3.1819553106957756, - 3.210824766445999, - 3.2113869376507562, - 3.211412733613088, - 3.211416086243693, - 3.211389363109834, - 3.211426369650999, - 3.2113609390638658, - 3.2113423890613904, - 3.211373063658337, - 3.211416879532908, - 3.2115410232676393, - 3.211420449026326, - 3.211340414954566, - 3.2114367116108946, - 3.2114314964286104, - 3.211388714333208, - 3.211430070547672, - 3.211389214185582, - 3.211385960112267, - 3.2113923720865305, - 3.2113927640902724, - 3.2113232302750965, - 3.2112382778326545, - 3.2111709478497157, - 3.211044813629002, - 3.2109619319113025, - 3.210835775367167, - 3.210676328655387, - 3.210494708667366, - 3.2102932552996304, - 3.210065134623158, - 3.2097946508076096, - 3.2095209162914307, - 3.2091908718152014, - 3.208858073378806, - 3.208540190789524, - 3.2092530229321037, - 3.21096166446629, - 3.2087142239418385, - 3.207876985261815, - 3.209981082122852, - 3.2099475857031297, - 3.2059177379588446, - 3.205500472975577, - 3.2060914486021095, - 3.205519414840442, - 3.206014703356543, - 3.208394467358398, - 3.2096207184298824, - 3.2099349143153155, - 3.209121978170608, - 3.2060689626565755, - 3.2046673031145434, - 3.204929136720981, - 3.205524292587136, - 3.2054895995204817, - 3.206946403159766, - 3.205982786489014, - 3.2056724075416114, - 3.205688076719014, - 3.2056000742724207, - 3.205427784285096, - 3.205636810334264, - 3.2082911860239833, - 3.210702471698039, - 3.210541425755314, - 3.2095944307240436, - 3.208960536226681, - 3.2047017324364746 + 2.917793645723982, + 3.00557811931334, + 3.093465691067416, + 3.181955310695781, + 3.210824766446009, + 3.2113869376507593, + 3.2114127336130904, + 3.211416086243699, + 3.211389363109841, + 3.2114263696509733, + 3.211360939063853, + 3.2113423890613726, + 3.2113730636581423, + 3.211416879533602, + 3.2115410232660544, + 3.211420449020959, + 3.2113404149551292, + 3.2114367116116433, + 3.2114314964286073, + 3.2113887143331348, + 3.211430070547805, + 3.21138921418565, + 3.2113859601123305, + 3.2113923720866837, + 3.211392764090055, + 3.2113232302753256, + 3.2112382778331168, + 3.2111709478503747, + 3.2110448136299907, + 3.210961931912992, + 3.2108357753696595, + 3.210676328658437, + 3.2104947086715394, + 3.2102932553041774, + 3.2100651346283873, + 3.2097946508137327, + 3.2095209162980893, + 3.2091908718225577, + 3.2088580733865246, + 3.208540190797431, + 3.209252986094329, + 3.2109615902106654, + 3.2087142100000996, + 3.207877151637441, + 3.209981078282075, + 3.2099475145370917, + 3.2059177379649135, + 3.2055004729811776, + 3.206091143678486, + 3.2055193197002003, + 3.2060219413836486, + 3.208390543508341, + 3.209639183492156, + 3.2099090780661865, + 3.2091035797982252, + 3.2060994879120677, + 3.204667302726753, + 3.2049293269676054, + 3.2055243749588707, + 3.205489593713636, + 3.2070488215164983, + 3.2059831350068317, + 3.2056723103164897, + 3.2056894526500916, + 3.2056003904638524, + 3.2054279890859227, + 3.2056363154541985, + 3.2081676171369784, + 3.2106976756102723, + 3.210581134988433, + 3.209557542221741, + 3.208689628218864, + 3.2042633909252842 ], "datatype": "Float64", "type": "series" @@ -789,29 +789,29 @@ 0.0, 0.0, 0.0, - 7.495670456119567, - 4.405996200470255, - 3.8637687267462297, - 5.608251475045593, - 5.873394653044703, - 8.085513883298564, - 5.478878616770271, - 4.599392627242936, - 4.4993047636542105, - 4.593772701085671, - 4.183024838311256, - 1.4104710124483635, - 3.204733460221206, - 11.008683815731874, - 5.7439955641918825, - 4.599460338927322, + 7.495670452200194, + 4.405996201079288, + 3.863768751307385, + 5.608251391833488, + 5.873393414199472, + 8.085514751124725, + 5.478880909811544, + 4.599391660688403, + 4.499303678841139, + 4.5937747885098, + 4.183039155161291, + 1.4104749893139292, + 3.204735219197912, + 11.008687038401128, + 5.743991393979669, + 4.599470537356818, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 6.936334864725218, + 6.936334864959468, 0.0, 0.0, 0.0, @@ -844,4 +844,4 @@ "datatype": "Float64", "type": "series" } -} +} \ No newline at end of file From 7796ac922d44fb88654173cffe2f1741fe1cc39c Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Fri, 28 Jun 2024 15:09:30 +0200 Subject: [PATCH 49/49] Cleanup Viscosity/Boundary Model (#566) * extract changes from #564 * fix --- .../dummy_particles/dummy_particles.jl | 59 ++++++++----------- src/visualization/write2vtk.jl | 28 +-------- 2 files changed, 27 insertions(+), 60 deletions(-) diff --git a/src/schemes/boundary/dummy_particles/dummy_particles.jl b/src/schemes/boundary/dummy_particles/dummy_particles.jl index 3e3abcbd9..d6ca717ef 100644 --- a/src/schemes/boundary/dummy_particles/dummy_particles.jl +++ b/src/schemes/boundary/dummy_particles/dummy_particles.jl @@ -58,18 +58,15 @@ function BoundaryModelDummyParticles(initial_density, hydrodynamic_mass, pressure = initial_boundary_pressure(initial_density, density_calculator, state_equation) NDIMS = ndims(smoothing_kernel) - n_particles = length(initial_density) cache = (; create_cache_model(viscosity, n_particles, NDIMS)..., - create_cache_model(initial_density, density_calculator)...) - cache = (; create_cache_model(correction, initial_density, NDIMS, - n_particles)..., cache...) + create_cache_model(initial_density, density_calculator)..., + create_cache_model(correction, initial_density, NDIMS, n_particles)...) - return BoundaryModelDummyParticles(pressure, hydrodynamic_mass, - state_equation, density_calculator, - smoothing_kernel, smoothing_length, - viscosity, correction, cache) + return BoundaryModelDummyParticles(pressure, hydrodynamic_mass, state_equation, + density_calculator, smoothing_kernel, + smoothing_length, viscosity, correction, cache) end @doc raw""" @@ -105,7 +102,7 @@ struct PressureMirroring end """ struct PressureZeroing end -create_cache_model(correction, density, NDIMS, nparticles) = (;) +@inline create_cache_model(correction, density, NDIMS, nparticles) = (;) function create_cache_model(::ShepardKernelCorrection, density, NDIMS, n_particles) return (; kernel_correction_coefficient=similar(density)) @@ -135,9 +132,7 @@ function create_cache_model(initial_density, return (; density) end -function create_cache_model(initial_density, ::ContinuityDensity) - return (; initial_density) -end +@inline create_cache_model(initial_density, ::ContinuityDensity) = (; initial_density) function create_cache_model(initial_density, ::AdamiPressureExtrapolation) density = copy(initial_density) @@ -146,9 +141,7 @@ function create_cache_model(initial_density, ::AdamiPressureExtrapolation) return (; density, volume) end -function create_cache_model(viscosity::Nothing, n_particles, n_dims) - return (;) -end +@inline create_cache_model(viscosity::Nothing, n_particles, n_dims) = (;) function create_cache_model(viscosity, n_particles, n_dims) ELTYPE = eltype(viscosity.epsilon) @@ -306,10 +299,9 @@ end boundary_model.pressure[particle] = max(boundary_model.state_equation(density), 0.0) end -function compute_pressure!(boundary_model, ::AdamiPressureExtrapolation, - system, v, u, v_ode, u_ode, semi) - (; pressure, state_equation, cache, viscosity) = boundary_model - (; volume, density) = cache +function compute_pressure!(boundary_model, ::AdamiPressureExtrapolation, system, v, u, + v_ode, u_ode, semi) + (; pressure, cache, viscosity) = boundary_model set_zero!(pressure) @@ -386,6 +378,13 @@ function compute_pressure!(boundary_model, ::Union{PressureMirroring, PressureZe return boundary_model end +@inline function adami_pressure_extrapolation_neighbor!(boundary_model, system, + neighbor_system, system_coords, + neighbor_coords, v_neighbor_system, + neighborhood_search) + return boundary_model +end + @inline function adami_pressure_extrapolation_neighbor!(boundary_model, system, neighbor_system::FluidSystem, system_coords, neighbor_coords, @@ -401,17 +400,15 @@ end pos_diff, distance # Since neighbor and particle are switched pos_diff = -pos_diff - adami_pressure_inner!(boundary_model, system, neighbor_system::FluidSystem, + adami_pressure_inner!(boundary_model, system, neighbor_system, v_neighbor_system, particle, neighbor, pos_diff, distance, viscosity, cache, pressure) end end -@inline function adami_pressure_extrapolation_neighbor!(boundary_model, system, - neighbor_system, - system_coords, neighbor_coords, - v_neighbor_system, - neighborhood_search) +@inline function adami_pressure_extrapolation!(boundary_model, system, neighbor_system, + system_coords, neighbor_coords, + v_neighbor_system, neighborhood_search) return boundary_model end @@ -433,14 +430,7 @@ end end end -@inline function adami_pressure_extrapolation!(boundary_model, system, neighbor_system, - system_coords, neighbor_coords, - v_neighbor_system, neighborhood_search) - return boundary_model -end - -@inline function adami_pressure_inner!(boundary_model, system, - neighbor_system::FluidSystem, +@inline function adami_pressure_inner!(boundary_model, system, neighbor_system, v_neighbor_system, particle, neighbor, pos_diff, distance, viscosity, cache, pressure) density_neighbor = particle_density(v_neighbor_system, neighbor_system, neighbor) @@ -510,8 +500,7 @@ end return density end -@inline function smoothing_kernel_grad(system::BoundarySystem, pos_diff, - distance, particle) +@inline function smoothing_kernel_grad(system::BoundarySystem, pos_diff, distance, particle) (; smoothing_kernel, smoothing_length, correction) = system.boundary_model return corrected_kernel_grad(smoothing_kernel, pos_diff, distance, diff --git a/src/visualization/write2vtk.jl b/src/visualization/write2vtk.jl index b5235e772..07651d2c7 100644 --- a/src/visualization/write2vtk.jl +++ b/src/visualization/write2vtk.jl @@ -353,36 +353,14 @@ function write2vtk!(vtk, v, u, t, model::BoundaryModelDummyParticles, system; vtk["smoothing_length"] = system.boundary_model.smoothing_length vtk["density_calculator"] = type2string(system.boundary_model.density_calculator) vtk["state_equation"] = type2string(system.boundary_model.state_equation) + vtk["viscosity_model"] = type2string(model.viscosity) end - write2vtk!(vtk, v, u, t, model, model.viscosity, system, - write_meta_data=write_meta_data) -end - -function write2vtk!(vtk, v, u, t, model::BoundaryModelDummyParticles, viscosity, system; - write_meta_data=true) vtk["hydrodynamic_density"] = [particle_density(v, system, particle) for particle in eachparticle(system)] vtk["pressure"] = model.pressure - if write_meta_data - vtk["viscosity_model"] = type2string(viscosity) + if model.viscosity isa ViscosityAdami + vtk["wall_velocity"] = view(model.cache.wall_velocity, 1:ndims(system), :) end - - return vtk -end - -function write2vtk!(vtk, v, u, t, model::BoundaryModelDummyParticles, - viscosity::ViscosityAdami, system; - write_meta_data=true) - vtk["hydrodynamic_density"] = [particle_density(v, system, particle) - for particle in eachparticle(system)] - vtk["pressure"] = model.pressure - vtk["wall_velocity"] = view(model.cache.wall_velocity, 1:ndims(system), :) - - if write_meta_data - vtk["viscosity_model"] = type2string(viscosity) - end - - return vtk end