diff --git a/NEWS.md b/NEWS.md index 4a8f6299f4..601e39d313 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,59 @@ 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. +## Version 0.4 + +### API Changes + +- Renamed `BoundarySPHSystem` to `WallBoundarySystem` and the keyword argument + `movement` to `prescribed_motion`. + +- Renamed `OpenBoundarySPHSystem` to `OpenBoundarySystem`. + +- Renamed `BoundaryMovement` to `PrescribedMotion`. The `movement_function` must now be + a function of `(x, t)` returning the *new position* instead of an offset. + For example, `movement_function(t) = SVector(t, 0.0)` now needs to be + `movement_function(x, t) = x + SVector(t, 0.0)`. + +- Renamed directory `solid` to `structure` in the examples file tree. + VTK files for the `TotalLagrangianSPHSystem` are now also called `structure_*`. + +- Renamed keyword argument `n_fixed_particles` of the `TotalLagrangianSPHSystem` + to `n_clamped_particles`. + +- API for `OpenBoundarySystem` and `BoundaryZone` changed. + It is now possible to pass multiple `BoundaryZone`s to a single `OpenBoundarySystem`. + Reference values are now assigned individually to each `BoundaryZone`. (#866) + +- Rename keyword arguments `plane` and `plane_normal` for `BoundaryZone` to `boundary_face` and `face_normal` (#597). + +- The argument of `TransportVelocityAdami` is now a keyword argument. + `TransportVelocityAdami(1000.0)` now becomes + `TransportVelocityAdami(background_pressure=1000.0)` (#884). + +- Combined transport velocity formulation (TVF) and particle shifting technique (PST) into + one unified framework. + The keyword argument `transport_velocity` now changed to `shifting_technique`. + The `ParticleShiftingCallback` has been removed. To use PST, use the `UpdateCallback` + instead, and pass `shifting_technique=ParticleShiftingTechniqueSun2017()` to the system (#884). + +- Renamed the keyword argument `tlsph` to `place_on_shell` for `ParticlePackingSystem`, + `sample_boundary`, `extrude_geometry`, `RectangularShape`, and `SphereShape` (#814). + +- Custom quantity functions passed to `SolutionSavingCallback` or `PostprocessCallback` + that were not using the documented API but were functions of + `(system, v_ode, u_ode, semi, t)` now need to be functions + of `(system, dv_ode, du_ode, v_ode, u_ode, semi, t)` (#879). + +- Renamed `each_moving_particle` to `each_integrated_particle`, + `n_moving_particles` to `n_integrated_particles` + and `active_particles` to `each_active_particle`. + +### Features + +- Added consistent particle shifting by Sun et al. (2019) as `ConsistentShiftingSun2019` (#888). + + ## Version 0.3.2 ### Features @@ -19,18 +72,17 @@ used in the Julia ecosystem. Notable changes will be documented in this file for - Fix the coordinates used for TLSPH in Adami extrapolation (#853) - Fix PST for small smoothing length factors (#834) -- The TVF model has been improved to integrate correctly with time stepping (#864) - +- The TVF model has been improved to integrate correctly with time stepping (#864) ## Version 0.3.1 ### Features -- **Simplified SGS Viscosity Models**: Added ViscosityMorrisSGS and ViscosityAdamiSGS, +- **Simplified SGS Viscosity Models**: Added ViscosityMorrisSGS and ViscosityAdamiSGS, which implement a simplified Smagorinsky-type sub-grid-scale viscosity. (#753) -- **Multithreaded Integration Array**: Introduced a new array type for CPU backends +- **Multithreaded Integration Array**: Introduced a new array type for CPU backends that enables multithreaded broadcasting, delivering speed-ups of up to 5× on systems with many threads when combined with thread pinning. (#722) @@ -40,17 +92,17 @@ used in the Julia ecosystem. Notable changes will be documented in this file for - **DXF file format support**: Import complex geometries using the DXF file format. (#821) - **Improved Plane interpolation**: Massively improved interpolation performance for planes (#763). - + ### GPU - Make PST GPU-compatible (#813). - + - Make open boundaries GPU-compatible (#773). - + - Make interpolation GPU-compatible (#812). ### Important Bugfixes - + - Fix validation setups (#801). - Calculate interpolated density instead of computed density when using interpolation (#808). @@ -230,4 +282,3 @@ Features: #### TLSPH An implementation of TLSPH (Total Lagrangian Smoothed Particle Hydrodynamics) for solid bodies enabling FSI (Fluid Structure Interactions). - diff --git a/Project.toml b/Project.toml index 9c8aa85fb3..7f1adba08a 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.3.2" +version = "0.4" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" @@ -27,6 +27,7 @@ RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" StrideArraysCore = "7792a7ef-975c-4747-a70f-980b88e8d1da" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" TrixiBase = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" diff --git a/docs/src/gpu.md b/docs/src/gpu.md index 61d92b90a0..d22f936ad1 100644 --- a/docs/src/gpu.md +++ b/docs/src/gpu.md @@ -2,7 +2,7 @@ GPU support is still an experimental feature that is actively being worked on. Currently, the [`WeaklyCompressibleSPHSystem`](@ref), [`TotalLagrangianSPHSystem`](@ref) -and [`BoundarySPHSystem`](@ref) support GPU execution. +and [`WallBoundarySystem`](@ref) support GPU execution. We have tested GPU support on Nvidia, AMD and Apple GPUs. Note that most Apple GPUs do not support `Float64`. See [below on how to run single precision simulations](@ref single_precision). diff --git a/docs/src/refs.bib b/docs/src/refs.bib index a31156a278..3b853a5250 100644 --- a/docs/src/refs.bib +++ b/docs/src/refs.bib @@ -707,6 +707,17 @@ @article{Sun2018 doi = {10.1016/j.cpc.2017.11.016}, } +@article{Sun2019, + author = {Sun, P. N. and Colagrossi, A. and Marrone, S. and Antuono, M. and Zhang, A. -M.}, + title = {A consistent approach to particle shifting in the δPlus-{SPH} model}, + journal = {Computer Methods in Applied Mechanics and Engineering}, + volume = {348}, + year = {2019}, + issn = {0045-7825}, + pages = {912--934}, + doi = {10.1016/j.cma.2019.01.045}, +} + @Article{Tafuni2018, author = {A. Tafuni and J.M. Dom{\'{\i}}nguez and R. Vacondio and A.J.C. Crespo}, journal = {Computer Methods in Applied Mechanics and Engineering}, diff --git a/docs/src/systems/boundary.md b/docs/src/systems/boundary.md index f898e50171..25a082e803 100644 --- a/docs/src/systems/boundary.md +++ b/docs/src/systems/boundary.md @@ -1,7 +1,7 @@ # Boundary System ```@docs - BoundarySPHSystem + WallBoundarySystem ``` ```@docs @@ -9,7 +9,7 @@ ``` ```@docs - BoundaryMovement + PrescribedMotion ``` @@ -61,7 +61,7 @@ We provide six options to compute the boundary density and pressure, determined This option usually yields the best results of the options listed here. 2. (Only relevant for FSI) With [`BernoulliPressureExtrapolation`](@ref), the pressure is extrapolated from the pressure similar to the [`AdamiPressureExtrapolation`](@ref), but a relative velocity-dependent pressure part - is calculated between moving solids and fluids, which increases the boundary pressure in areas prone to + is calculated between moving bodies and fluids, which increases the boundary pressure in areas prone to penetrations. 3. With [`SummationDensity`](@ref), the density is calculated by summation over the neighboring particles, and the pressure is computed from the density with the state equation. @@ -103,7 +103,7 @@ Identical to the pressure ``p_b `` calculated via [`AdamiPressureExtrapolation`] p_b = \frac{\sum_f (p_f + \frac{1}{2} \, \rho_{\text{neighbor}} \left( \frac{ (\mathbf{v}_f - \mathbf{v}_{\text{body}}) \cdot (\mathbf{x}_f - \mathbf{x}_{\text{neighbor}}) }{ \left\| \mathbf{x}_f - \mathbf{x}_{\text{neighbor}} \right\| } \right)^2 \times \text{factor} +\rho_f (\bm{g} - \bm{a}_b) \cdot \bm{r}_{bf}) W(\Vert r_{bf} \Vert, h)}{\sum_f W(\Vert r_{bf} \Vert, h)} ``` where ``\mathbf{v}_f`` is the velocity of the fluid and ``\mathbf{v}_{\text{body}}`` is the velocity of the body. -This adjustment provides a higher boundary pressure for solid bodies moving with a relative velocity to the fluid to prevent penetration. +This adjustment provides a higher boundary pressure for bodies moving with a relative velocity to the fluid to prevent penetration. This modification is original and not derived from any literature source. ```@docs @@ -217,9 +217,8 @@ a no-slip condition is imposed. When omitting the viscous interaction !!! warning The no-slip conditions for `BoundaryModelMonaghanKajtar` have not been verified yet. -```@autodocs -Modules = [TrixiParticles] -Pages = [joinpath("schemes", "boundary", "monaghan_kajtar", "monaghan_kajtar.jl")] +```@docs + BoundaryModelMonaghanKajtar ``` # [Open Boundaries](@id open_boundary) @@ -234,6 +233,11 @@ Modules = [TrixiParticles] Pages = [joinpath("schemes", "boundary", "open_boundary", "boundary_zones.jl")] ``` +```@autodocs +Modules = [TrixiParticles] +Filter = t -> typeof(t) === typeof(TrixiParticles.planar_geometry_to_face) +``` + # [Open Boundary Models](@id open_boundary_models) We offer two models for open boundaries, with the choice depending on the specific problem and flow characteristics near the boundary: 1. [**Method of characteristics**](@ref method_of_characteristics): The method of characteristics is typically used in problems where tracking of wave propagation diff --git a/docs/src/systems/dem.md b/docs/src/systems/dem.md index 10cfb00b6f..dced109f4b 100644 --- a/docs/src/systems/dem.md +++ b/docs/src/systems/dem.md @@ -19,12 +19,12 @@ and moments acting upon them. ```@autodocs Modules = [TrixiParticles] -Pages = [joinpath("schemes", "solid", "discrete_element_method", "system.jl")] +Pages = [joinpath("schemes", "structure", "discrete_element_method", "system.jl")] ``` ### Contact Models ```@autodocs Modules = [TrixiParticles] -Pages = [joinpath("schemes", "solid", "discrete_element_method", "contact_models.jl")] +Pages = [joinpath("schemes", "structure", "discrete_element_method", "contact_models.jl")] ``` diff --git a/docs/src/systems/entropically_damped_sph.md b/docs/src/systems/entropically_damped_sph.md index 0db8d275e2..96acbad352 100644 --- a/docs/src/systems/entropically_damped_sph.md +++ b/docs/src/systems/entropically_damped_sph.md @@ -7,7 +7,8 @@ this scheme uses a pressure evolution equation to calculate the pressure ``` which is derived by [Clausen (2013)](@cite Clausen2013). This equation is similar to the continuity equation (first term, see [`ContinuityDensity`](@ref)), but also contains a pressure damping term (second term, similar to density diffusion -see [`DensityDiffusion`](@ref)), which reduces acoustic pressure waves through an entropy-generation mechanism. +see [`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion)), +which reduces acoustic pressure waves through an entropy-generation mechanism. The pressure evolution is discretized with the SPH method by [Ramachandran (2019)](@cite Ramachandran2019) as following: @@ -45,64 +46,3 @@ is a good choice for a wide range of Reynolds numbers (0.0125 to 10000). Modules = [TrixiParticles] Pages = [joinpath("schemes", "fluid", "entropically_damped_sph", "system.jl")] ``` - -## [Transport Velocity Formulation (TVF)](@id transport_velocity_formulation) -Standard SPH suffers from problems like tensile instability or the creation of void regions in the flow. -To address these problems, [Adami (2013)](@cite Adami2013) modified the advection velocity and added an extra term to the momentum equation. -The authors introduced the so-called Transport Velocity Formulation (TVF) for WCSPH. [Ramachandran (2019)](@cite Ramachandran2019) applied the TVF -also for the [EDAC](@ref edac) scheme. - -The transport velocity ``\tilde{v}_a`` of particle ``a`` is used to evolve the position of the particle ``r_a`` from one time step to the next by - -```math -\frac{\mathrm{d} r_a}{\mathrm{d}t} = \tilde{v}_a -``` - -and is obtained at every time-step ``\Delta t`` from - -```math -\tilde{v}_a (t + \Delta t) = v_a (t) + \Delta t \left(\frac{\tilde{\mathrm{d}} v_a}{\mathrm{d}t} - \frac{1}{\rho_a} \nabla p_{\text{background}} \right), -``` - -where ``\rho_a`` is the density of particle ``a`` and ``p_{\text{background}}`` is a constant background pressure field. -The tilde in the second term of the right hand side indicates that the material derivative has an advection part. - -The discretized form of the last term is - -```math - -\frac{1}{\rho_a} \nabla p_{\text{background}} \approx -\frac{p_{\text{background}}}{m_a} \sum_b \left(V_a^2 + V_b^2 \right) \nabla_a W_{ab}, -``` - -where ``V_a``, ``V_b`` denote the volume of particles ``a`` and ``b`` respectively. -Note that although in the continuous case ``\nabla p_{\text{background}} = 0``, the discretization is not 0th-order consistent for **non**-uniform particle distribution, -which means that there is a non-vanishing contribution only when particles are disordered. -That also means that ``p_{\text{background}}`` occurs as prefactor to correct the trajectory of a particle resulting in uniform pressure distributions. -Suggested is a background pressure which is in the order of the reference pressure but can be chosen arbitrarily large when the time-step criterion is adjusted. - -The inviscid momentum equation with an additional convection term for a particle moving with ``\tilde{v}`` is - -```math -\frac{\tilde{\mathrm{d}} \left( \rho v \right)}{\mathrm{d}t} = -\nabla p + \nabla \cdot \bm{A}, -``` - - where the tensor ``\bm{A} = \rho v\left(\tilde{v}-v\right)^T`` is a consequence of the modified - advection velocity and can be interpreted as the convection of momentum with the relative velocity ``\tilde{v}-v``. - -The discretized form of the momentum equation for a particle ``a`` reads as - -```math -\frac{\tilde{\mathrm{d}} v_a}{\mathrm{d}t} = \frac{1}{m_a} \sum_b \left(V_a^2 + V_b^2 \right) \left[ -\tilde{p}_{ab} \nabla_a W_{ab} + \frac{1}{2} \left(\bm{A}_a + \bm{A}_b \right) \cdot \nabla_a W_{ab} \right]. -``` - -Here, ``\tilde{p}_{ab}`` is the density-weighted pressure - -```math -\tilde{p}_{ab} = \frac{\rho_b p_a + \rho_a p_b}{\rho_a + \rho_b}, -``` - -with the density ``\rho_a``, ``\rho_b`` and the pressure ``p_a``, ``p_b`` of particles ``a`` and ``b`` respectively. ``\bm{A}_a`` and ``\bm{A}_b`` are the convection tensors for particle ``a`` and ``b`` respectively and are given, e.g. for particle ``a``, as ``\bm{A}_a = \rho v_a\left(\tilde{v}_a-v_a\right)^T``. - -```@autodocs -Modules = [TrixiParticles] -Pages = [joinpath("schemes", "fluid", "transport_velocity.jl")] -``` diff --git a/docs/src/systems/total_lagrangian_sph.md b/docs/src/systems/total_lagrangian_sph.md index 7193a28a26..4ae46cead2 100644 --- a/docs/src/systems/total_lagrangian_sph.md +++ b/docs/src/systems/total_lagrangian_sph.md @@ -59,7 +59,7 @@ The term $\bm{f}_a^{PF}$ is an optional penalty force. See e.g. [`PenaltyForceGa ```@autodocs Modules = [TrixiParticles] -Pages = [joinpath("schemes", "solid", "total_lagrangian_sph", "system.jl")] +Pages = [joinpath("schemes", "structure", "total_lagrangian_sph", "system.jl")] ``` ## Penalty Force @@ -100,7 +100,7 @@ where the error vector is defined as ```@autodocs Modules = [TrixiParticles] -Pages = [joinpath("schemes", "solid", "total_lagrangian_sph", "penalty_force.jl")] +Pages = [joinpath("schemes", "structure", "total_lagrangian_sph", "penalty_force.jl")] ``` ## Viscosity diff --git a/docs/src/systems/weakly_compressible_sph.md b/docs/src/systems/weakly_compressible_sph.md index 51355961f6..2c69f4b3c9 100644 --- a/docs/src/systems/weakly_compressible_sph.md +++ b/docs/src/systems/weakly_compressible_sph.md @@ -58,7 +58,8 @@ by an additional term + \delta h c \sum_{b} V_b \psi_{ab} \cdot \nabla W_{ab}, ``` where ``V_b = m_b / \rho_b`` is the volume of particle ``b`` and ``\psi_{ab}`` depends on -the density diffusion method (see [`DensityDiffusion`](@ref) for available terms). +the density diffusion method (see +[`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion) for available terms). Also, ``\rho_a`` denotes the density of particle ``a`` and ``r_{ab} = r_a - r_b`` is the difference of the coordinates, ``v_{ab} = v_a - v_b`` of the velocities of particles ``a`` and ``b``. @@ -152,8 +153,74 @@ as explained in [Sun2018](@cite Sun2018) on page 29, right above Equation 9. The ``\delta``-SPH method (WCSPH with density diffusion) together with this formulation of PST is commonly referred to as ``\delta^+``-SPH. -The Particle Shifting Technique can be applied in form -of the [`ParticleShiftingCallback`](@ref). +To apply particle shifting, use the keyword argument `shifting_technique` in the constructor +of a system that supports it. + + +## [Transport Velocity Formulation (TVF)](@id transport_velocity_formulation) + +An alternative formulation is the so-called Transport Velocity Formulation (TVF) +by [Adami (2013)](@cite Adami2013). +[Ramachandran (2019)](@cite Ramachandran2019) applied the TVF also for the [EDAC](@ref edac) +scheme. + +The transport velocity ``\tilde{v}_a`` of particle ``a`` is used to evolve the position +of the particle ``r_a`` from one time step to the next by +```math +\frac{\mathrm{d} r_a}{\mathrm{d}t} = \tilde{v}_a +``` +and is obtained at every time step ``\Delta t`` from +```math +\tilde{v}_a (t + \Delta t) = v_a (t) + \Delta t \left(\frac{\tilde{\mathrm{d}} v_a}{\mathrm{d}t} - \frac{1}{\rho_a} \nabla p_{\text{background}} \right), +``` +where ``\rho_a`` is the density of particle ``a`` and ``p_{\text{background}}`` +is a constant background pressure field. +The tilde in the second term of the right-hand side indicates that the material derivative +has an advection part. + +The discretized form of the last term is +```math + -\frac{1}{\rho_a} \nabla p_{\text{background}} \approx -\frac{p_{\text{background}}}{m_a} \sum_b \left(V_a^2 + V_b^2 \right) \nabla_a W_{ab}, +``` +where ``V_a``, ``V_b`` denote the volume of particles ``a`` and ``b`` respectively. +Note that although in the continuous case ``\nabla p_{\text{background}} = 0``, +the discretization is not 0th-order consistent for **non**-uniform particle distribution, +which means that there is a non-vanishing contribution only when particles are disordered. +That also means that ``p_{\text{background}}`` occurs as pre-factor to correct +the trajectory of a particle resulting in uniform pressure distributions. +Suggested is a background pressure which is in the order of the reference pressure, +but it can be chosen arbitrarily large when the time-step criterion is adjusted. + +The inviscid momentum equation with an additional convection term for a particle +moving with ``\tilde{v}`` is +```math +\frac{\tilde{\mathrm{d}} \left( \rho v \right)}{\mathrm{d}t} = -\nabla p + \nabla \cdot \bm{A}, +``` +where the tensor ``\bm{A} = \rho v\left(\tilde{v}-v\right)^T`` is a consequence +of the modified advection velocity and can be interpreted as the convection of momentum +with the relative velocity ``\tilde{v}-v``. + +The discretized form of the momentum equation for a particle ``a`` reads as +```math +\frac{\tilde{\mathrm{d}} v_a}{\mathrm{d}t} = \frac{1}{m_a} \sum_b \left(V_a^2 + V_b^2 \right) \left[ -\tilde{p}_{ab} \nabla_a W_{ab} + \frac{1}{2} \left(\bm{A}_a + \bm{A}_b \right) \cdot \nabla_a W_{ab} \right]. +``` +Here, ``\tilde{p}_{ab}`` is the density-weighted pressure +```math +\tilde{p}_{ab} = \frac{\rho_b p_a + \rho_a p_b}{\rho_a + \rho_b}, +``` +with the density ``\rho_a``, ``\rho_b`` and the pressure ``p_a``, ``p_b`` of particles ``a`` +and ``b``, respectively. ``\bm{A}_a`` and ``\bm{A}_b`` are the convection tensors +for particle ``a`` and ``b``, respectively, and are given, e.g., for particle ``a``, +as ``\bm{A}_a = \rho v_a\left(\tilde{v}_a-v_a\right)^T``. + +To apply the TVF, use the keyword argument `shifting_technique` in the constructor +of a system that supports it. + +```@autodocs +Modules = [TrixiParticles] +Pages = [joinpath("schemes", "fluid", "shifting_techniques.jl")] +``` + ## [Tensile Instability Control](@id tic) diff --git a/docs/src/tutorials_template/tut_beam.md b/docs/src/tutorials_template/tut_beam.md index e1e80e7e65..d85b61127d 100644 --- a/docs/src/tutorials_template/tut_beam.md +++ b/docs/src/tutorials_template/tut_beam.md @@ -1,5 +1,5 @@ # Example file ```julia -!!include:examples/solid/oscillating_beam_2d.jl!! +!!include:examples/structure/oscillating_beam_2d.jl!! -``` \ No newline at end of file +``` diff --git a/docs/src/tutorials_template/tut_setup.md b/docs/src/tutorials_template/tut_setup.md index 411ce1592e..de8c37e3ba 100644 --- a/docs/src/tutorials_template/tut_setup.md +++ b/docs/src/tutorials_template/tut_setup.md @@ -161,7 +161,7 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar state_equation=state_equation, AdamiPressureExtrapolation(), smoothing_kernel, smoothing_length) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model) nothing # hide ``` @@ -240,12 +240,12 @@ and modify them as needed. ### Custom smoothing kernel To implement a custom smoothing kernel, we define a struct extending -`TrixiParticles.SmoothingKernel`. +`TrixiParticles.AbstractSmoothingKernel`. This abstract struct has a type parameter for the number of dimensions, which we set to 2 in this case. ```@example tut_setup -struct MyGaussianKernel <: TrixiParticles.SmoothingKernel{2} end +struct MyGaussianKernel <: TrixiParticles.AbstractSmoothingKernel{2} end ``` This kernel is going to be an implementation of the Gaussian kernel with a cutoff for compact support, which reads diff --git a/examples/dem/collapsing_sand_pile_3d.jl b/examples/dem/collapsing_sand_pile_3d.jl index aa9ba7a56b..f463ac2417 100644 --- a/examples/dem/collapsing_sand_pile_3d.jl +++ b/examples/dem/collapsing_sand_pile_3d.jl @@ -55,7 +55,8 @@ min_coords_floor = (min_boundary[1] - boundary_thickness, floor_particles = RectangularShape(particle_spacing, (n_particles_floor_x, n_particles_floor_y, n_particles_floor_z), - min_coords_floor; density=boundary_density, tlsph=true) + min_coords_floor; density=boundary_density, + place_on_shell=true) boundary_particles = floor_particles # ========================================================================================== diff --git a/examples/fluid/accelerated_tank_2d.jl b/examples/fluid/accelerated_tank_2d.jl index 26a408520e..cb6fbfb9d1 100644 --- a/examples/fluid/accelerated_tank_2d.jl +++ b/examples/fluid/accelerated_tank_2d.jl @@ -12,13 +12,14 @@ using OrdinaryDiffEq fluid_particle_spacing = 0.05 # Function for moving boundaries -movement_function(t) = SVector(0.0, 0.5 * 9.81 * t^2) +movement_function(x, t) = x + SVector(0.0, 0.5 * 9.81 * t^2) is_moving(t) = true -boundary_movement = BoundaryMovement(movement_function, is_moving) +boundary_movement = PrescribedMotion(movement_function, is_moving) trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "hydrostatic_water_column_2d.jl"), - fluid_particle_spacing=fluid_particle_spacing, movement=boundary_movement, + fluid_particle_spacing=fluid_particle_spacing, + prescribed_motion=boundary_movement, tspan=(0.0, 1.0), system_acceleration=(0.0, 0.0)); diff --git a/examples/fluid/dam_break_2d.jl b/examples/fluid/dam_break_2d.jl index ae8c96434b..a25084eca5 100644 --- a/examples/fluid/dam_break_2d.jl +++ b/examples/fluid/dam_break_2d.jl @@ -97,7 +97,8 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar reference_particle_spacing=0, viscosity=viscosity_wall) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model, adhesion_coefficient=0.0) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model, + adhesion_coefficient=0.0) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/dam_break_3d.jl b/examples/fluid/dam_break_3d.jl index fcfce8dbb4..32045f979e 100644 --- a/examples/fluid/dam_break_3d.jl +++ b/examples/fluid/dam_break_3d.jl @@ -59,7 +59,7 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar boundary_density_calculator, smoothing_kernel, smoothing_length) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/falling_water_column_2d.jl b/examples/fluid/falling_water_column_2d.jl index c4eaab6777..f712f88d93 100644 --- a/examples/fluid/falling_water_column_2d.jl +++ b/examples/fluid/falling_water_column_2d.jl @@ -58,7 +58,7 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar boundary_density_calculator, smoothing_kernel, smoothing_length) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/falling_water_spheres_2d.jl b/examples/fluid/falling_water_spheres_2d.jl index 68eae04574..778d404d69 100644 --- a/examples/fluid/falling_water_spheres_2d.jl +++ b/examples/fluid/falling_water_spheres_2d.jl @@ -84,8 +84,8 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar viscosity=ViscosityAdami(nu=wall_viscosity), reference_particle_spacing=fluid_particle_spacing) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model, - adhesion_coefficient=1.0) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model, + adhesion_coefficient=1.0) # ========================================================================================== # ==== Simulation @@ -94,7 +94,7 @@ ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=1000) saving_callback = SolutionSavingCallback(dt=0.01, output_directory="out", - prefix="", write_meta_data=true) + prefix="") callbacks = CallbackSet(info_callback, saving_callback) diff --git a/examples/fluid/hydrostatic_water_column_2d.jl b/examples/fluid/hydrostatic_water_column_2d.jl index 110d5bfdc0..a62dbab249 100644 --- a/examples/fluid/hydrostatic_water_column_2d.jl +++ b/examples/fluid/hydrostatic_water_column_2d.jl @@ -64,7 +64,8 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar boundary_density_calculator, smoothing_kernel, smoothing_length, viscosity=viscosity_wall) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model, movement=nothing) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model, + prescribed_motion=nothing) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/lid_driven_cavity_2d.jl b/examples/fluid/lid_driven_cavity_2d.jl index b6582a1701..9849ca06f7 100644 --- a/examples/fluid/lid_driven_cavity_2d.jl +++ b/examples/fluid/lid_driven_cavity_2d.jl @@ -65,7 +65,7 @@ if wcsph state_equation, smoothing_kernel, pressure_acceleration=TrixiParticles.inter_particle_averaged_pressure, smoothing_length, viscosity=viscosity, - transport_velocity=TransportVelocityAdami(pressure)) + shifting_technique=TransportVelocityAdami(background_pressure=pressure)) else state_equation = nothing density_calculator = ContinuityDensity() @@ -73,17 +73,17 @@ else smoothing_length, density_calculator=density_calculator, sound_speed, viscosity=viscosity, - transport_velocity=TransportVelocityAdami(pressure)) + shifting_technique=TransportVelocityAdami(background_pressure=pressure)) end # ========================================================================================== # ==== Boundary -lid_movement_function(t) = SVector(VELOCITY_LID * t, 0.0) +lid_movement_function(x, t) = x + SVector(VELOCITY_LID * t, 0.0) is_moving(t) = true -lid_movement = BoundaryMovement(lid_movement_function, is_moving) +lid_movement = PrescribedMotion(lid_movement_function, is_moving) boundary_model_cavity = BoundaryModelDummyParticles(cavity.boundary.density, cavity.boundary.mass, @@ -98,9 +98,10 @@ boundary_model_lid = BoundaryModelDummyParticles(lid.density, lid.mass, state_equation=state_equation, smoothing_kernel, smoothing_length) -boundary_system_cavity = BoundarySPHSystem(cavity.boundary, boundary_model_cavity) +boundary_system_cavity = WallBoundarySystem(cavity.boundary, boundary_model_cavity) -boundary_system_lid = BoundarySPHSystem(lid, boundary_model_lid, movement=lid_movement) +boundary_system_lid = WallBoundarySystem(lid, boundary_model_lid, + prescribed_motion=lid_movement) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/moving_wall_2d.jl b/examples/fluid/moving_wall_2d.jl index 44265a2b99..85663188ea 100644 --- a/examples/fluid/moving_wall_2d.jl +++ b/examples/fluid/moving_wall_2d.jl @@ -36,11 +36,11 @@ tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fl reset_wall!(tank, (false, true, false, false), (0.0, tank.fluid_size[1], 0.0, 0.0)) # Movement function -movement_function(t) = SVector(0.5t^2, 0.0) +movement_function(x, t) = x + SVector(0.5 * t^2, 0.0) is_moving(t) = t < 1.5 -boundary_movement = BoundaryMovement(movement_function, is_moving, +boundary_movement = PrescribedMotion(movement_function, is_moving, moving_particles=tank.face_indices[2]) # ========================================================================================== @@ -64,8 +64,8 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar boundary_density_calculator, smoothing_kernel, smoothing_length) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model, - movement=boundary_movement) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model, + prescribed_motion=boundary_movement) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/periodic_array_of_cylinders_2d.jl b/examples/fluid/periodic_array_of_cylinders_2d.jl index 850c9f2ff9..fd71e9a4be 100644 --- a/examples/fluid/periodic_array_of_cylinders_2d.jl +++ b/examples/fluid/periodic_array_of_cylinders_2d.jl @@ -60,7 +60,7 @@ smoothing_length = 1.2 * particle_spacing smoothing_kernel = SchoenbergQuarticSplineKernel{2}() fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, sound_speed, viscosity=ViscosityAdami(; nu), - transport_velocity=TransportVelocityAdami(pressure), + shifting_technique=TransportVelocityAdami(background_pressure=pressure), acceleration=(acceleration_x, 0.0)) # ========================================================================================== @@ -70,7 +70,7 @@ boundary_model = BoundaryModelDummyParticles(boundary.density, boundary.mass, viscosity=ViscosityAdami(; nu), smoothing_kernel, smoothing_length) -boundary_system = BoundarySPHSystem(boundary, boundary_model) +boundary_system = WallBoundarySystem(boundary, boundary_model) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/periodic_channel_2d.jl b/examples/fluid/periodic_channel_2d.jl index 74770c318b..62aed7466d 100644 --- a/examples/fluid/periodic_channel_2d.jl +++ b/examples/fluid/periodic_channel_2d.jl @@ -28,7 +28,7 @@ initial_fluid_size = tank_size initial_velocity = (1.0, 0.0) fluid_density = 1000.0 -sound_speed = initial_velocity[1] +sound_speed = 10 * initial_velocity[1] state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, exponent=7) @@ -48,6 +48,7 @@ viscosity = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) fluid_system = WeaklyCompressibleSPHSystem(tank.fluid, fluid_density_calculator, state_equation, smoothing_kernel, smoothing_length, viscosity=viscosity, + shifting_technique=nothing, pressure_acceleration=nothing) # ========================================================================================== @@ -63,7 +64,7 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar smoothing_kernel, smoothing_length, viscosity=viscosity_wall) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model) # ========================================================================================== # ==== Simulation diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index 3544ac7a8d..df4652b80d 100644 --- a/examples/fluid/pipe_flow_2d.jl +++ b/examples/fluid/pipe_flow_2d.jl @@ -12,7 +12,7 @@ using OrdinaryDiffEq # ========================================================================================== # ==== Resolution -particle_spacing = 0.05 +particle_spacing = 0.02 # Make sure that the kernel support of fluid particles at a boundary is always fully sampled boundary_layers = 4 @@ -21,7 +21,7 @@ boundary_layers = 4 # 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 = 8 +open_boundary_layers = 6 # ========================================================================================== # ==== Experiment Setup @@ -30,62 +30,70 @@ 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 +const prescribed_velocity = (1.0, 0.0) +flow_direction = [1.0, 0.0] -boundary_size = (domain_size[1] + 2 * particle_spacing * open_boundary_layers, - domain_size[2]) +open_boundary_size = (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 = 20 * prescribed_velocity - -state_equation = nothing +sound_speed = 10 * maximum(abs.(prescribed_velocity)) -pipe = RectangularTank(particle_spacing, domain_size, boundary_size, fluid_density, - pressure=pressure, n_layers=boundary_layers, +pipe = RectangularTank(particle_spacing, domain_size, domain_size, fluid_density, + n_layers=boundary_layers, velocity=prescribed_velocity, faces=(false, false, true, true)) -# Shift pipe walls in negative x-direction for the inflow -pipe.boundary.coordinates[1, :] .-= particle_spacing * open_boundary_layers +min_coords_inlet = (-open_boundary_layers * particle_spacing, 0.0) +inlet = RectangularTank(particle_spacing, open_boundary_size, open_boundary_size, + fluid_density, n_layers=boundary_layers, + min_coordinates=min_coords_inlet, + faces=(false, false, true, true)) + +min_coords_outlet = (pipe.fluid_size[1], 0.0) +outlet = RectangularTank(particle_spacing, open_boundary_size, open_boundary_size, + fluid_density, n_layers=boundary_layers, + min_coordinates=min_coords_outlet, + faces=(false, false, true, true)) NDIMS = ndims(pipe.fluid) -n_buffer_particles = 5 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) +n_buffer_particles = 10 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) # ========================================================================================== # ==== Fluid -wcsph = false +wcsph = true smoothing_length = 1.5 * particle_spacing smoothing_kernel = WendlandC2Kernel{NDIMS}() fluid_density_calculator = ContinuityDensity() -kinematic_viscosity = prescribed_velocity * domain_size[2] / reynolds_number +kinematic_viscosity = maximum(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 if wcsph state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, - exponent=1, background_pressure=pressure) - alpha = 8 * kinematic_viscosity / (smoothing_length * sound_speed) - viscosity = ArtificialViscosityMonaghan(; alpha, beta=0.0) + exponent=1) + density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(pipe.fluid, fluid_density_calculator, state_equation, smoothing_kernel, + density_diffusion=density_diffusion, smoothing_length, viscosity=viscosity, + shifting_technique=ParticleShiftingTechnique(v_max_factor=1.5), + buffer_size=n_buffer_particles) +else + # Alternatively the EDAC scheme can be used + state_equation = nothing + + fluid_system = EntropicallyDampedSPHSystem(pipe.fluid, smoothing_kernel, + smoothing_length, sound_speed, + viscosity=viscosity, + density_calculator=fluid_density_calculator, + shifting_technique=ParticleShiftingTechnique(), buffer_size=n_buffer_particles) end @@ -96,75 +104,71 @@ function velocity_function2d(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) + return SVector(prescribed_velocity) end -open_boundary_model = BoundaryModelLastiwka() - -boundary_type_in = InFlow() -plane_in = ([0.0, 0.0], [0.0, domain_size[2]]) -inflow = BoundaryZone(; plane=plane_in, plane_normal=flow_direction, open_boundary_layers, - density=fluid_density, particle_spacing, - boundary_type=boundary_type_in) +open_boundary_model = BoundaryModelMirroringTafuni(; mirror_method=ZerothOrderMirroring()) reference_velocity_in = velocity_function2d -reference_pressure_in = pressure -reference_density_in = fluid_density -open_boundary_in = OpenBoundarySPHSystem(inflow; fluid_system, - boundary_model=open_boundary_model, - buffer_size=n_buffer_particles, - reference_density=reference_density_in, - reference_pressure=reference_pressure_in, - reference_velocity=reference_velocity_in) - +reference_pressure_in = nothing +reference_density_in = nothing +boundary_type_in = InFlow() +face_in = ([0.0, 0.0], [0.0, domain_size[2]]) +inflow = BoundaryZone(; boundary_face=face_in, face_normal=flow_direction, + open_boundary_layers, density=fluid_density, particle_spacing, + reference_density=reference_density_in, + reference_pressure=reference_pressure_in, + reference_velocity=reference_velocity_in, + initial_condition=inlet.fluid, boundary_type=boundary_type_in) + +reference_velocity_out = nothing +reference_pressure_out = nothing +reference_density_out = nothing boundary_type_out = OutFlow() -plane_out = ([domain_size[1], 0.0], [domain_size[1], domain_size[2]]) -outflow = BoundaryZone(; plane=plane_out, plane_normal=(-flow_direction), +face_out = ([min_coords_outlet[1], 0.0], [min_coords_outlet[1], domain_size[2]]) +outflow = BoundaryZone(; boundary_face=face_out, face_normal=(-flow_direction), open_boundary_layers, density=fluid_density, particle_spacing, - boundary_type=boundary_type_out) - -reference_velocity_out = velocity_function2d -reference_pressure_out = pressure -reference_density_out = fluid_density -open_boundary_out = OpenBoundarySPHSystem(outflow; fluid_system, - boundary_model=open_boundary_model, - buffer_size=n_buffer_particles, - reference_density=reference_density_out, - reference_pressure=reference_pressure_out, - reference_velocity=reference_velocity_out) + reference_density=reference_density_out, + reference_pressure=reference_pressure_out, + reference_velocity=reference_velocity_out, + initial_condition=outlet.fluid, boundary_type=boundary_type_out) + +open_boundary = OpenBoundarySystem(inflow, outflow; fluid_system, + boundary_model=open_boundary_model, + buffer_size=n_buffer_particles) + # ========================================================================================== # ==== Boundary -viscosity_boundary = ViscosityAdami(nu=1e-4) -boundary_model = BoundaryModelDummyParticles(pipe.boundary.density, pipe.boundary.mass, +wall = union(pipe.boundary, inlet.boundary, outlet.boundary) +viscosity_boundary = viscosity +boundary_model = BoundaryModelDummyParticles(wall.density, wall.mass, AdamiPressureExtrapolation(), state_equation=state_equation, viscosity=viscosity_boundary, smoothing_kernel, smoothing_length) -boundary_system = BoundarySPHSystem(pipe.boundary, boundary_model) +boundary_system = WallBoundarySystem(wall, boundary_model) # ========================================================================================== # ==== Simulation -min_corner = minimum(pipe.boundary.coordinates .- particle_spacing, dims=2) -max_corner = maximum(pipe.boundary.coordinates .+ particle_spacing, dims=2) +min_corner = minimum(wall.coordinates .- particle_spacing, dims=2) +max_corner = maximum(wall.coordinates .+ particle_spacing, dims=2) nhs = GridNeighborhoodSearch{NDIMS}(; cell_list=FullGridCellList(; min_corner, max_corner), update_strategy=ParallelUpdate()) -semi = Semidiscretization(fluid_system, open_boundary_in, open_boundary_out, - boundary_system, neighborhood_search=nhs, +semi = Semidiscretization(fluid_system, open_boundary, boundary_system, + neighborhood_search=nhs, parallelization_backend=PolyesterBackend()) ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.02, prefix="") -particle_shifting = ParticleShiftingCallback() extra_callback = nothing -callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback(), - particle_shifting, extra_callback) +callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback(), extra_callback) sol = solve(ode, RDPK3SpFSAL35(), abstol=1e-5, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) diff --git a/examples/fluid/pipe_flow_3d.jl b/examples/fluid/pipe_flow_3d.jl index 10035f4308..a0f16b57a6 100644 --- a/examples/fluid/pipe_flow_3d.jl +++ b/examples/fluid/pipe_flow_3d.jl @@ -22,29 +22,25 @@ open_boundary_layers = 6 # ========================================================================================== # ==== Experiment Setup -tspan = (0.0, 2.0) - -function velocity_function3d(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, 0.0) -end +tspan = (0.0, 0.5) domain_size = (1.0, 0.4, 0.4) - -boundary_size = (domain_size[1] + 2 * particle_spacing * open_boundary_layers, - domain_size[2], domain_size[3]) - +const prescribed_velocity = (1.0, 0.0, 0.0) flow_direction = [1.0, 0.0, 0.0] +open_boundary_size = (domain_size[1] + 2 * particle_spacing * open_boundary_layers, + domain_size[2], domain_size[3]) +min_coords_inlet = (-open_boundary_layers * particle_spacing, 0.0, 0.0) +min_coords_outlet = (-open_boundary_layers * particle_spacing, 0.0, 0.0) + # setup simulation trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), - domain_size=domain_size, boundary_size=boundary_size, + domain_size=domain_size, open_boundary_size=open_boundary_size, flow_direction=flow_direction, faces=(false, false, true, true, true, true), - tspan=tspan, reference_velocity=velocity_function3d, - open_boundary_layers=open_boundary_layers, - plane_in=([0.0, 0.0, 0.0], [0.0, domain_size[2], 0.0], - [0.0, 0.0, domain_size[3]]), - plane_out=([domain_size[1], 0.0, 0.0], [domain_size[1], domain_size[2], 0.0], - [domain_size[1], 0.0, domain_size[3]])) + tspan=tspan, prescribed_velocity=prescribed_velocity, + open_boundary_layers=open_boundary_layers, min_coords_inlet=min_coords_inlet, + min_coords_outlet=min_coords_outlet, + face_in=([0.0, 0.0, 0.0], [0.0, domain_size[2], 0.0], + [0.0, 0.0, domain_size[3]]), + face_out=([domain_size[1], 0.0, 0.0], [domain_size[1], domain_size[2], 0.0], + [domain_size[1], 0.0, domain_size[3]])) diff --git a/examples/fluid/taylor_green_vortex_2d.jl b/examples/fluid/taylor_green_vortex_2d.jl index e6e74fbc4b..0e13d3738d 100644 --- a/examples/fluid/taylor_green_vortex_2d.jl +++ b/examples/fluid/taylor_green_vortex_2d.jl @@ -86,13 +86,15 @@ if wcsph pressure_acceleration=TrixiParticles.inter_particle_averaged_pressure, smoothing_length, viscosity=ViscosityAdami(; nu), - transport_velocity=TransportVelocityAdami(background_pressure)) + shifting_technique=TransportVelocityAdami(; + background_pressure)) else density_calculator = SummationDensity() fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, sound_speed, density_calculator=density_calculator, - transport_velocity=TransportVelocityAdami(background_pressure), + shifting_technique=TransportVelocityAdami(; + background_pressure), viscosity=ViscosityAdami(; nu)) end diff --git a/examples/fsi/dam_break_gate_2d.jl b/examples/fsi/dam_break_gate_2d.jl index 529fd56522..015232adf9 100644 --- a/examples/fsi/dam_break_gate_2d.jl +++ b/examples/fsi/dam_break_gate_2d.jl @@ -60,17 +60,17 @@ gate = RectangularShape(boundary_particle_spacing, (initial_fluid_size[1], 0.0), density=fluid_density) # Movement of the gate according to the paper -movement_function(t) = SVector(0.0, -285.115t^3 + 72.305t^2 + 0.1463t) +movement_function(x, t) = x + SVector(0.0, -285.115 * t^3 + 72.305 * t^2 + 0.1463 * t) is_moving(t) = t < 0.1 -gate_movement = BoundaryMovement(movement_function, is_moving) +gate_movement = PrescribedMotion(movement_function, is_moving) # Elastic plate/beam. # The paper is using a thickness of 0.004, which only works properly when a similar fluid # resolution is used. Increase resolution and change to 0.004 to reproduce the results. length_beam = 0.09 thickness = 0.004 * 10 -solid_density = 1161.54 +structure_density = 1161.54 # Young's modulus and Poisson ratio E = 3.5e6 / 10 @@ -78,25 +78,25 @@ nu = 0.45 # The structure starts at the position of the first particle and ends # at the position of the last particle. -solid_particle_spacing = thickness / (n_particles_x - 1) +structure_particle_spacing = thickness / (n_particles_x - 1) -n_particles_y = round(Int, length_beam / solid_particle_spacing) + 1 +n_particles_y = round(Int, length_beam / structure_particle_spacing) + 1 # The bottom layer is sampled separately below. Note that the `RectangularShape` puts the -# first particle half a particle spacing away from the boundary, which is correct for fluids, -# but not for solids. We therefore need to pass `tlsph=true`. +# first particle half a particle spacing away from the shell of the shape, which is +# correct for fluids, but not for structures. We therefore need to pass `place_on_shell=true`. # # The right end of the plate is 0.2 from the right end of the tank. -plate_position = 0.6 - n_particles_x * solid_particle_spacing -plate = RectangularShape(solid_particle_spacing, +plate_position = 0.6 - n_particles_x * structure_particle_spacing +plate = RectangularShape(structure_particle_spacing, (n_particles_x, n_particles_y - 1), - (plate_position, solid_particle_spacing), - density=solid_density, tlsph=true) -fixed_particles = RectangularShape(solid_particle_spacing, - (n_particles_x, 1), (plate_position, 0.0), - density=solid_density, tlsph=true) + (plate_position, structure_particle_spacing), + density=structure_density, place_on_shell=true) +clamped_particles = RectangularShape(structure_particle_spacing, + (n_particles_x, 1), (plate_position, 0.0), + density=structure_density, place_on_shell=true) -solid = union(plate, fixed_particles) +structure = union(plate, clamped_particles) # ========================================================================================== # ==== Fluid @@ -124,34 +124,36 @@ boundary_model_gate = BoundaryModelDummyParticles(gate.density, gate.mass, boundary_density_calculator, smoothing_kernel, smoothing_length) -boundary_system_tank = BoundarySPHSystem(tank.boundary, boundary_model_tank) -boundary_system_gate = BoundarySPHSystem(gate, boundary_model_gate, movement=gate_movement) +boundary_system_tank = WallBoundarySystem(tank.boundary, boundary_model_tank) +boundary_system_gate = WallBoundarySystem(gate, boundary_model_gate, + prescribed_motion=gate_movement) # ========================================================================================== -# ==== Solid -solid_smoothing_length = sqrt(2) * solid_particle_spacing -solid_smoothing_kernel = WendlandC2Kernel{2}() - -# For the FSI we need the hydrodynamic masses and densities in the solid boundary model -hydrodynamic_densites = fluid_density * ones(size(solid.density)) -hydrodynamic_masses = hydrodynamic_densites * solid_particle_spacing^2 - -boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, - hydrodynamic_masses, - state_equation=state_equation, - AdamiPressureExtrapolation(), - smoothing_kernel, smoothing_length) - -solid_system = TotalLagrangianSPHSystem(solid, - solid_smoothing_kernel, solid_smoothing_length, - E, nu, boundary_model=boundary_model_solid, - n_fixed_particles=n_particles_x, - acceleration=(0.0, -gravity)) +# ==== Structure +structure_smoothing_length = sqrt(2) * structure_particle_spacing +structure_smoothing_kernel = WendlandC2Kernel{2}() + +# For the FSI we need the hydrodynamic masses and densities in the structure boundary model +hydrodynamic_densites = fluid_density * ones(size(structure.density)) +hydrodynamic_masses = hydrodynamic_densites * structure_particle_spacing^2 + +boundary_model_structure = BoundaryModelDummyParticles(hydrodynamic_densites, + hydrodynamic_masses, + state_equation=state_equation, + AdamiPressureExtrapolation(), + smoothing_kernel, smoothing_length) + +structure_system = TotalLagrangianSPHSystem(structure, + structure_smoothing_kernel, + structure_smoothing_length, + E, nu, boundary_model=boundary_model_structure, + n_clamped_particles=n_particles_x, + acceleration=(0.0, -gravity)) # ========================================================================================== # ==== Simulation semi = Semidiscretization(fluid_system, boundary_system_tank, - boundary_system_gate, solid_system, + boundary_system_gate, structure_system, parallelization_backend=PolyesterBackend()) ode = semidiscretize(semi, tspan) diff --git a/examples/fsi/dam_break_plate_2d.jl b/examples/fsi/dam_break_plate_2d.jl index f5b42ecaa0..62a9a919f2 100644 --- a/examples/fsi/dam_break_plate_2d.jl +++ b/examples/fsi/dam_break_plate_2d.jl @@ -8,7 +8,7 @@ # https://doi.org/10.1016/j.jfluidstructs.2019.02.002 # # This example simulates a 2D dam break where the collapsing water column impacts -# a flexible elastic plate fixed at its base. +# a flexible elastic plate clamped at its base. # ========================================================================================== using TrixiParticles @@ -44,7 +44,7 @@ tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fl # Elastic plate/beam length_beam = 0.08 thickness = 0.012 -solid_density = 2500 +structure_density = 2500 # Young's modulus and Poisson ratio E = 1e6 @@ -52,22 +52,22 @@ nu = 0.0 # The structure starts at the position of the first particle and ends # at the position of the last particle. -solid_particle_spacing = thickness / (n_particles_x - 1) +structure_particle_spacing = thickness / (n_particles_x - 1) -n_particles_y = round(Int, length_beam / solid_particle_spacing) + 1 +n_particles_y = round(Int, length_beam / structure_particle_spacing) + 1 # The bottom layer is sampled separately below. Note that the `RectangularShape` puts the -# first particle half a particle spacing away from the boundary, which is correct for fluids, -# but not for solids. We therefore need to pass `tlsph=true`. -plate = RectangularShape(solid_particle_spacing, +# first particle half a particle spacing away from the shell of the shape, which is +# correct for fluids, but not for structures. We therefore need to pass `place_on_shell=true`. +plate = RectangularShape(structure_particle_spacing, (n_particles_x, n_particles_y - 1), - (2initial_fluid_size[1], solid_particle_spacing), - density=solid_density, tlsph=true) -fixed_particles = RectangularShape(solid_particle_spacing, - (n_particles_x, 1), (2initial_fluid_size[1], 0.0), - density=solid_density, tlsph=true) + (2initial_fluid_size[1], structure_particle_spacing), + density=structure_density, place_on_shell=true) +clamped_particles = RectangularShape(structure_particle_spacing, + (n_particles_x, 1), (2initial_fluid_size[1], 0.0), + density=structure_density, place_on_shell=true) -solid = union(plate, fixed_particles) +structure = union(plate, clamped_particles) # ========================================================================================== # ==== Fluid @@ -90,46 +90,47 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar boundary_density_calculator, smoothing_kernel, smoothing_length) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model) # ========================================================================================== -# ==== Solid -solid_smoothing_length = sqrt(2) * solid_particle_spacing -solid_smoothing_kernel = WendlandC2Kernel{2}() +# ==== Structure +structure_smoothing_length = sqrt(2) * structure_particle_spacing +structure_smoothing_kernel = WendlandC2Kernel{2}() -# For the FSI we need the hydrodynamic masses and densities in the solid boundary model -hydrodynamic_densites = fluid_density * ones(size(solid.density)) -hydrodynamic_masses = hydrodynamic_densites * solid_particle_spacing^2 +# For the FSI we need the hydrodynamic masses and densities in the structure boundary model +hydrodynamic_densites = fluid_density * ones(size(structure.density)) +hydrodynamic_masses = hydrodynamic_densites * structure_particle_spacing^2 -k_solid = gravity * initial_fluid_size[2] -spacing_ratio_solid = fluid_particle_spacing / solid_particle_spacing -boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, spacing_ratio_solid, - solid_particle_spacing, - hydrodynamic_masses) +k_structure = gravity * initial_fluid_size[2] +spacing_ratio_structure = fluid_particle_spacing / structure_particle_spacing +boundary_model_structure = BoundaryModelMonaghanKajtar(k_structure, spacing_ratio_structure, + structure_particle_spacing, + hydrodynamic_masses) # `BoundaryModelDummyParticles` usually produces better results, since Monaghan-Kajtar BCs # tend to introduce a non-physical gap between fluid and boundary. # However, `BoundaryModelDummyParticles` can only be used when the plate thickness is # at least two fluid particle spacings, so that the compact support is fully sampled, -# or fluid particles can penetrate the solid. +# or fluid particles can penetrate the structure. # With higher fluid resolutions, uncomment the code below for better results. # -# boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, +# boundary_model_structure = BoundaryModelDummyParticles(hydrodynamic_densites, # hydrodynamic_masses, # state_equation=state_equation, # boundary_density_calculator, # smoothing_kernel, smoothing_length) -solid_system = TotalLagrangianSPHSystem(solid, - solid_smoothing_kernel, solid_smoothing_length, - E, nu, boundary_model=boundary_model_solid, - n_fixed_particles=n_particles_x, - acceleration=(0.0, -gravity), - penalty_force=PenaltyForceGanzenmueller(alpha=0.01)) +structure_system = TotalLagrangianSPHSystem(structure, + structure_smoothing_kernel, + structure_smoothing_length, + E, nu, boundary_model=boundary_model_structure, + n_clamped_particles=n_particles_x, + acceleration=(0.0, -gravity), + penalty_force=PenaltyForceGanzenmueller(alpha=0.01)) # ========================================================================================== # ==== Simulation -semi = Semidiscretization(fluid_system, boundary_system, solid_system) +semi = Semidiscretization(fluid_system, boundary_system, structure_system) ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) diff --git a/examples/fsi/falling_sphere_2d.jl b/examples/fsi/falling_sphere_2d.jl index 8e6db81507..4af4b4bad2 100644 --- a/examples/fsi/falling_sphere_2d.jl +++ b/examples/fsi/falling_sphere_2d.jl @@ -8,6 +8,6 @@ using TrixiParticles trixi_include(@__MODULE__, joinpath(examples_dir(), "fsi", "falling_spheres_2d.jl"), - solid_system_2=nothing, fluid_particle_spacing=0.02, + structure_system_2=nothing, fluid_particle_spacing=0.02, initial_fluid_size=(1.0, 0.9), tank_size=(1.0, 1.0), 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 53a709b243..966d203578 100644 --- a/examples/fsi/falling_sphere_3d.jl +++ b/examples/fsi/falling_sphere_3d.jl @@ -8,13 +8,12 @@ using TrixiParticles trixi_include(@__MODULE__, joinpath(examples_dir(), "fsi", "falling_spheres_2d.jl"), - solid_system_2=nothing, fluid_particle_spacing=0.05, + structure_system_2=nothing, fluid_particle_spacing=0.05, initial_fluid_size=(1.0, 0.9, 1.0), tank_size=(1.0, 1.0, 1.0), faces=(true, true, true, false, true, true), acceleration=(0.0, -9.81, 0.0), sphere1_center=(0.5, 2.0, 0.5), fluid_smoothing_kernel=WendlandC2Kernel{3}(), - solid_smoothing_kernel=WendlandC2Kernel{3}(), + structure_smoothing_kernel=WendlandC2Kernel{3}(), sphere_type=RoundSphere(), output_directory="out", prefix="", - write_meta_data=false, # Files with meta data can't be read by meshio 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 8c76b763eb..e938cbf6f8 100644 --- a/examples/fsi/falling_spheres_2d.jl +++ b/examples/fsi/falling_spheres_2d.jl @@ -11,7 +11,7 @@ using OrdinaryDiffEq # ========================================================================================== # ==== Resolution fluid_particle_spacing = 0.02 -solid_particle_spacing = fluid_particle_spacing +structure_particle_spacing = fluid_particle_spacing # Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model boundary_layers = 3 @@ -48,9 +48,9 @@ nu = 0.0 sphere1_center = (0.5, 1.6) sphere2_center = (1.5, 1.6) -sphere1 = SphereShape(solid_particle_spacing, sphere1_radius, sphere1_center, +sphere1 = SphereShape(structure_particle_spacing, sphere1_radius, sphere1_center, sphere1_density, sphere_type=VoxelSphere()) -sphere2 = SphereShape(solid_particle_spacing, sphere2_radius, sphere2_center, +sphere2 = SphereShape(structure_particle_spacing, sphere2_radius, sphere2_center, sphere2_density, sphere_type=VoxelSphere()) # ========================================================================================== @@ -76,56 +76,60 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar boundary_density_calculator, fluid_smoothing_kernel, fluid_smoothing_length) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model) # ========================================================================================== -# ==== Solid -solid_smoothing_length = sqrt(2) * solid_particle_spacing -solid_smoothing_kernel = WendlandC2Kernel{2}() +# ==== Structure +structure_smoothing_length = sqrt(2) * structure_particle_spacing +structure_smoothing_kernel = WendlandC2Kernel{2}() -# For the FSI we need the hydrodynamic masses and densities in the solid boundary model +# For the FSI we need the hydrodynamic masses and densities in the structure boundary model hydrodynamic_densites_1 = fluid_density * ones(size(sphere1.density)) -hydrodynamic_masses_1 = hydrodynamic_densites_1 * solid_particle_spacing^ndims(fluid_system) +hydrodynamic_masses_1 = hydrodynamic_densites_1 * + structure_particle_spacing^ndims(fluid_system) -solid_boundary_model_1 = BoundaryModelDummyParticles(hydrodynamic_densites_1, - hydrodynamic_masses_1, - state_equation=state_equation, - boundary_density_calculator, - fluid_smoothing_kernel, - fluid_smoothing_length) +structure_boundary_model_1 = BoundaryModelDummyParticles(hydrodynamic_densites_1, + hydrodynamic_masses_1, + state_equation=state_equation, + boundary_density_calculator, + fluid_smoothing_kernel, + fluid_smoothing_length) hydrodynamic_densites_2 = fluid_density * ones(size(sphere2.density)) -hydrodynamic_masses_2 = hydrodynamic_densites_2 * solid_particle_spacing^ndims(fluid_system) - -solid_boundary_model_2 = BoundaryModelDummyParticles(hydrodynamic_densites_2, - hydrodynamic_masses_2, - state_equation=state_equation, - boundary_density_calculator, - fluid_smoothing_kernel, - fluid_smoothing_length) - -solid_system_1 = TotalLagrangianSPHSystem(sphere1, - solid_smoothing_kernel, solid_smoothing_length, - sphere1_E, nu, - acceleration=(0.0, -gravity), - boundary_model=solid_boundary_model_1, - penalty_force=PenaltyForceGanzenmueller(alpha=0.3)) - -solid_system_2 = TotalLagrangianSPHSystem(sphere2, - solid_smoothing_kernel, solid_smoothing_length, - sphere2_E, nu, - acceleration=(0.0, -gravity), - boundary_model=solid_boundary_model_2, - penalty_force=PenaltyForceGanzenmueller(alpha=0.3)) +hydrodynamic_masses_2 = hydrodynamic_densites_2 * + structure_particle_spacing^ndims(fluid_system) + +structure_boundary_model_2 = BoundaryModelDummyParticles(hydrodynamic_densites_2, + hydrodynamic_masses_2, + state_equation=state_equation, + boundary_density_calculator, + fluid_smoothing_kernel, + fluid_smoothing_length) + +structure_system_1 = TotalLagrangianSPHSystem(sphere1, + structure_smoothing_kernel, + structure_smoothing_length, + sphere1_E, nu, + acceleration=(0.0, -gravity), + boundary_model=structure_boundary_model_1, + penalty_force=PenaltyForceGanzenmueller(alpha=0.3)) + +structure_system_2 = TotalLagrangianSPHSystem(sphere2, + structure_smoothing_kernel, + structure_smoothing_length, + sphere2_E, nu, + acceleration=(0.0, -gravity), + boundary_model=structure_boundary_model_2, + penalty_force=PenaltyForceGanzenmueller(alpha=0.3)) # ========================================================================================== # ==== Simulation -semi = Semidiscretization(fluid_system, boundary_system, solid_system_1, solid_system_2) +semi = Semidiscretization(fluid_system, boundary_system, structure_system_1, + structure_system_2) ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=50) -saving_callback = SolutionSavingCallback(dt=0.02, output_directory="out", prefix="", - write_meta_data=true) +saving_callback = SolutionSavingCallback(dt=0.02, output_directory="out", prefix="") callbacks = CallbackSet(info_callback, saving_callback) diff --git a/examples/fsi/falling_water_column_2d.jl b/examples/fsi/falling_water_column_2d.jl index cd6357386c..6fec694bfe 100644 --- a/examples/fsi/falling_water_column_2d.jl +++ b/examples/fsi/falling_water_column_2d.jl @@ -2,8 +2,8 @@ # 2D Falling Water Column on an Elastic Beam (FSI) # # This example simulates a column of water falling under gravity and impacting -# an elastic beam, which is fixed at one end (cantilever). -# It demonstrates Fluid-Structure Interaction where the fluid deforms the solid structure. +# an elastic beam, which is clamped at one end (cantilever). +# It demonstrates Fluid-Structure Interaction where the fluid deforms the structure. # ========================================================================================== using TrixiParticles @@ -14,7 +14,7 @@ using OrdinaryDiffEq n_particles_y = 5 # Load setup from oscillating beam example -trixi_include(@__MODULE__, joinpath(examples_dir(), "solid", "oscillating_beam_2d.jl"), +trixi_include(@__MODULE__, joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), thickness=0.05, n_particles_y=n_particles_y, sol=nothing) # Don't run simulation, only include the setup part @@ -53,27 +53,27 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, acceleration=(0.0, -gravity)) # ========================================================================================== -# ==== Solid +# ==== Structure k = gravity * initial_fluid_size[2] spacing_ratio = fluid_particle_spacing / particle_spacing -# For the FSI we need the hydrodynamic masses and densities in the solid boundary model -hydrodynamic_densites = fluid_density * ones(size(solid.density)) +# For the FSI we need the hydrodynamic masses and densities in the structure boundary model +hydrodynamic_densites = fluid_density * ones(size(structure.density)) hydrodynamic_masses = hydrodynamic_densites * particle_spacing^2 boundary_model = BoundaryModelMonaghanKajtar(k, spacing_ratio, particle_spacing, hydrodynamic_masses) -solid_system = TotalLagrangianSPHSystem(solid, - smoothing_kernel, smoothing_length, - material.E, material.nu, - boundary_model=boundary_model, - n_fixed_particles=nparticles(fixed_particles), - acceleration=(0.0, -gravity)) +structure_system = TotalLagrangianSPHSystem(structure, + smoothing_kernel, smoothing_length, + material.E, material.nu, + boundary_model=boundary_model, + n_clamped_particles=nparticles(clamped_particles), + acceleration=(0.0, -gravity)) # ========================================================================================== # ==== Simulation -semi = Semidiscretization(fluid_system, solid_system) +semi = Semidiscretization(fluid_system, structure_system) ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) diff --git a/examples/n_body/n_body_benchmark_trixi.jl b/examples/n_body/n_body_benchmark_trixi.jl index 925f6283d9..828f046bf2 100644 --- a/examples/n_body/n_body_benchmark_trixi.jl +++ b/examples/n_body/n_body_benchmark_trixi.jl @@ -18,7 +18,7 @@ function TrixiParticles.interact!(dv, v_particle_system, u_particle_system, neighbor_system::NBodySystem) (; mass, G) = neighbor_system - for particle in TrixiParticles.each_moving_particle(particle_system) + for particle in TrixiParticles.each_integrated_particle(particle_system) particle_coords = TrixiParticles.current_coords(u_particle_system, particle_system, particle) diff --git a/examples/n_body/n_body_system.jl b/examples/n_body/n_body_system.jl index 9493f2333f..f1913c7120 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, IC} <: TrixiParticles.System{NDIMS} +struct NBodySystem{NDIMS, ELTYPE <: Real, IC} <: TrixiParticles.AbstractSystem{NDIMS} initial_condition :: IC mass :: Array{ELTYPE, 1} # [particle] G :: ELTYPE @@ -105,7 +105,7 @@ end TrixiParticles.vtkname(system::NBodySystem) = "n-body" -function TrixiParticles.write2vtk!(vtk, v, u, t, system::NBodySystem; write_meta_data=true) +function TrixiParticles.write2vtk!(vtk, v, u, t, system::NBodySystem) (; mass) = system vtk["velocity"] = v @@ -114,6 +114,10 @@ function TrixiParticles.write2vtk!(vtk, v, u, t, system::NBodySystem; write_meta return vtk end +function TrixiParticles.add_system_data!(system_data, system::NBodySystem) + return system_data +end + function Base.show(io::IO, system::NBodySystem) print(io, "NBodySystem{", ndims(system), "}() with ") print(io, TrixiParticles.nparticles(system), " particles") diff --git a/examples/postprocessing/interpolation_plane.jl b/examples/postprocessing/interpolation_plane.jl index 900c3daed1..edf4ebed8f 100644 --- a/examples/postprocessing/interpolation_plane.jl +++ b/examples/postprocessing/interpolation_plane.jl @@ -104,10 +104,10 @@ 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. -# Note that the arguments `system, v_ode, u_ode, semi, t` are more powerful than the +# Note that the arguments `system, dv_ode, du_ode, v_ode, u_ode, semi, t` are more powerful than the # documented arguments `system, data, t`, allowing us to use interpolation (which requires # a semidiscretization). -function save_interpolated_plane(system, v_ode, u_ode, semi, t) +function save_interpolated_plane(system, dv_ode, du_ode, v_ode, u_ode, semi, t) # Size of the patch to be interpolated interpolation_start = [0.0, 0.0] interpolation_end = [tank_size[1], tank_size[2]] diff --git a/examples/postprocessing/postprocessing.jl b/examples/postprocessing/postprocessing.jl index 58a1e02cbc..bd0aa86115 100644 --- a/examples/postprocessing/postprocessing.jl +++ b/examples/postprocessing/postprocessing.jl @@ -13,9 +13,9 @@ using CSV using DataFrames using JSON -# Any custom function with the arguments `v, u, t, system` can be passed to the callback +# Any custom function with the arguments `system, data, t` can be passed to the callback # to be called every 10th timestep. See example below: -function hello(system, v_ode, u_ode, semi, t) +function hello(system, data, t) # Will write "hello" and the current simulation time println("hello at ", t) diff --git a/examples/preprocessing/packing_2d.jl b/examples/preprocessing/packing_2d.jl index fc8b330269..0975b6e2ad 100644 --- a/examples/preprocessing/packing_2d.jl +++ b/examples/preprocessing/packing_2d.jl @@ -20,7 +20,7 @@ file = pkgdir(TrixiParticles, "examples", "preprocessing", "data", filename * ". # ========================================================================================== # ==== Packing parameters -tlsph = false +place_on_shell = false # ========================================================================================== # ==== Resolution @@ -50,7 +50,7 @@ shape_sampled = ComplexShape(geometry; particle_spacing, density, # Returns `InitialCondition` boundary_sampled = sample_boundary(signed_distance_field; boundary_density=density, - boundary_thickness, tlsph=tlsph) + boundary_thickness, place_on_shell=place_on_shell) trixi2vtk(shape_sampled) trixi2vtk(boundary_sampled, filename="boundary") @@ -66,12 +66,13 @@ background_pressure = 1.0 smoothing_length = 0.8 * particle_spacing packing_system = ParticlePackingSystem(shape_sampled; smoothing_length=smoothing_length, - signed_distance_field, tlsph=tlsph, + signed_distance_field, place_on_shell=place_on_shell, background_pressure) boundary_system = ParticlePackingSystem(boundary_sampled; smoothing_length=smoothing_length, is_boundary=true, signed_distance_field, - tlsph=tlsph, boundary_compress_factor=0.8, + place_on_shell=place_on_shell, + boundary_compress_factor=0.8, background_pressure) # ========================================================================================== diff --git a/examples/preprocessing/packing_3d.jl b/examples/preprocessing/packing_3d.jl index cb15a255b2..ede3433b38 100644 --- a/examples/preprocessing/packing_3d.jl +++ b/examples/preprocessing/packing_3d.jl @@ -24,5 +24,5 @@ boundary_thickness = 8 * particle_spacing trixi_include(joinpath(examples_dir(), "preprocessing", "packing_2d.jl"), density=1000.0, particle_spacing=particle_spacing, file=file, - boundary_thickness=boundary_thickness, tlsph=true, + boundary_thickness=boundary_thickness, place_on_shell=true, save_intervals=false) diff --git a/examples/solid/oscillating_beam_2d.jl b/examples/structure/oscillating_beam_2d.jl similarity index 73% rename from examples/solid/oscillating_beam_2d.jl rename to examples/structure/oscillating_beam_2d.jl index e2bb13a300..f43126e935 100644 --- a/examples/solid/oscillating_beam_2d.jl +++ b/examples/structure/oscillating_beam_2d.jl @@ -2,8 +2,8 @@ # 2D Oscillating Elastic Beam (Cantilever) Simulation # # This example simulates the oscillation of a 2D elastic beam (cantilever) -# fixed at one end and subjected to gravity. It uses the Total Lagrangian SPH (TLSPH) -# method for solid mechanics. +# clamped at one end and subjected to gravity. It uses the Total Lagrangian SPH (TLSPH) +# method for structure mechanics. # # Based on: # J. O'Connor and B.D. Rogers @@ -34,11 +34,11 @@ clamp_radius = 0.05 particle_spacing = elastic_beam.thickness / (n_particles_y - 1) # Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius -fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, - (0.0, elastic_beam.thickness / 2), material.density, - cutout_min=(0.0, 0.0), - cutout_max=(clamp_radius, elastic_beam.thickness), - tlsph=true) +clamped_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, + (0.0, elastic_beam.thickness / 2), material.density, + cutout_min=(0.0, 0.0), + cutout_max=(clamp_radius, elastic_beam.thickness), + place_on_shell=true) n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) @@ -47,27 +47,27 @@ n_particles_per_dimension = (round(Int, elastic_beam.length / particle_spacing) n_particles_clamp_x + 1, n_particles_y) # Note that the `RectangularShape` puts the first particle half a particle spacing away -# from the boundary, which is correct for fluids, but not for solids. -# We therefore need to pass `tlsph=true`. +# from the boundary, which is correct for fluids, but not for structures. +# We therefore need to pass `place_on_shell=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, - (0.0, 0.0), density=material.density, tlsph=true) + (0.0, 0.0), density=material.density, place_on_shell=true) -solid = union(beam, fixed_particles) +structure = union(beam, clamped_particles) # ========================================================================================== -# ==== Solid +# ==== Structure smoothing_length = sqrt(2) * particle_spacing smoothing_kernel = WendlandC2Kernel{2}() -solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length, - material.E, material.nu, - n_fixed_particles=nparticles(fixed_particles), - acceleration=(0.0, -gravity), - penalty_force=nothing, viscosity=nothing) +structure_system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothing_length, + material.E, material.nu, + n_clamped_particles=nparticles(clamped_particles), + acceleration=(0.0, -gravity), + penalty_force=nothing, viscosity=nothing) # ========================================================================================== # ==== Simulation -semi = Semidiscretization(solid_system, +semi = Semidiscretization(structure_system, neighborhood_search=PrecomputedNeighborhoodSearch{2}(), parallelization_backend=PolyesterBackend()) ode = semidiscretize(semi, tspan) diff --git a/ext/TrixiParticlesOrdinaryDiffEqExt.jl b/ext/TrixiParticlesOrdinaryDiffEqExt.jl index 496e8dd199..7178978156 100644 --- a/ext/TrixiParticlesOrdinaryDiffEqExt.jl +++ b/ext/TrixiParticlesOrdinaryDiffEqExt.jl @@ -8,7 +8,7 @@ module TrixiParticlesOrdinaryDiffEqExt # We need to load the name `PointNeighbors` because `@threaded` translates # to `PointNeighbors.parallel_foreach`, so `PointNeighbors` must be available. -using TrixiParticles: TrixiParticles, @threaded, each_moving_particle, +using TrixiParticles: TrixiParticles, @threaded, each_integrated_particle, WeaklyCompressibleSPHSystem, ContinuityDensity, PointNeighbors @@ -137,7 +137,7 @@ end # For WCSPH, only update the first NDIMS components of the velocity. # With `ContinuityDensity`, the last component is the density, # which is updated separately. - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) for i in 1:ndims(system) du_system[i, particle] = duprev_system[i, particle] + dt * kdu_system[i, particle] @@ -160,7 +160,7 @@ end @muladd function update_density!(du_system, kdu_system, duprev_system, ::ContinuityDensity, system, semi, dt) - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) density_prev = duprev_system[end, particle] density_half = du_system[end, particle] epsilon = -kdu_system[end, particle] / density_half * dt diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index ab7b241aa6..b75a219855 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -23,9 +23,11 @@ using ReadVTK: ReadVTK using RecipesBase: RecipesBase, @series using Random: seed! using SciMLBase: CallbackSet, DiscreteCallback, DynamicalODEProblem, u_modified!, - get_tmp_cache, set_proposed_dt!, ODESolution, ODEProblem, terminate! + get_tmp_cache, set_proposed_dt!, ODESolution, ODEProblem, terminate!, + get_du @reexport using StaticArrays: SVector using StaticArrays: @SMatrix, SMatrix, setindex +using Statistics: Statistics using StrideArraysCore: PtrArray, StaticInt using TimerOutputs: TimerOutput, TimerOutputs, print_timer, reset_timer!, @notimeit using TrixiBase: @trixi_timeit, timer, timeit_debug_enabled, @@ -44,7 +46,7 @@ using WriteVTK: vtk_grid, MeshCell, VTKCellTypes, paraview_collection, vtk_save # `util.jl` needs to be first because of the macros `@trixi_timeit` and `@threaded` include("util.jl") -include("general/system.jl") +include("general/abstract_system.jl") include("general/general.jl") include("setups/setups.jl") include("schemes/schemes.jl") @@ -55,42 +57,43 @@ include("callbacks/callbacks.jl") # included separately. `gpu.jl` in turn depends on the semidiscretization type. include("general/semidiscretization.jl") include("general/gpu.jl") -include("io/io.jl") include("preprocessing/preprocessing.jl") +include("io/io.jl") include("visualization/recipes_plots.jl") export Semidiscretization, semidiscretize, restart_with! export InitialCondition export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangianSPHSystem, - BoundarySPHSystem, DEMSystem, BoundaryDEMSystem, OpenBoundarySPHSystem + WallBoundarySystem, DEMSystem, BoundaryDEMSystem, OpenBoundarySystem export BoundaryZone, InFlow, OutFlow, BidirectionalFlow export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback, - PostprocessCallback, StepsizeCallback, UpdateCallback, SteadyStateReachedCallback, - ParticleShiftingCallback + PostprocessCallback, StepsizeCallback, UpdateCallback, SteadyStateReachedCallback export ContinuityDensity, SummationDensity -export PenaltyForceGanzenmueller, TransportVelocityAdami +export PenaltyForceGanzenmueller, TransportVelocityAdami, ParticleShiftingTechnique, + ParticleShiftingTechniqueSun2017, ConsistentShiftingSun2019, + ContinuityEquationTermSun2019, MomentumEquationTermSun2019 export SchoenbergCubicSplineKernel, SchoenbergQuarticSplineKernel, SchoenbergQuinticSplineKernel, GaussianKernel, WendlandC2Kernel, WendlandC4Kernel, WendlandC6Kernel, SpikyKernel, Poly6Kernel export StateEquationCole, StateEquationIdealGas export ArtificialViscosityMonaghan, ViscosityAdami, ViscosityMorris, ViscosityAdamiSGS, ViscosityMorrisSGS -export DensityDiffusion, DensityDiffusionMolteniColagrossi, DensityDiffusionFerrari, - DensityDiffusionAntuono +export DensityDiffusionMolteniColagrossi, DensityDiffusionFerrari, DensityDiffusionAntuono export tensile_instability_control export BoundaryModelMonaghanKajtar, BoundaryModelDummyParticles, AdamiPressureExtrapolation, - PressureMirroring, PressureZeroing, BoundaryModelLastiwka, BoundaryModelTafuni, + PressureMirroring, PressureZeroing, BoundaryModelCharacteristicsLastiwka, + BoundaryModelMirroringTafuni, BernoulliPressureExtrapolation export FirstOrderMirroring, ZerothOrderMirroring, SimpleMirroring export HertzContactModel, LinearContactModel -export BoundaryMovement +export PrescribedMotion export examples_dir, validation_dir export trixi2vtk, vtk2trixi export RectangularTank, RectangularShape, SphereShape, ComplexShape export ParticlePackingSystem, SignedDistanceField export WindingNumberHormann, WindingNumberJacobson export VoxelSphere, RoundSphere, reset_wall!, extrude_geometry, load_geometry, - sample_boundary + sample_boundary, planar_geometry_to_face export SourceTermDamping export ShepardKernelCorrection, KernelCorrection, AkinciFreeSurfaceCorrection, GradientCorrection, BlendedGradientCorrection, MixedKernelGradientCorrection diff --git a/src/callbacks/callbacks.jl b/src/callbacks/callbacks.jl index 062a16e3a9..e9eb048c3f 100644 --- a/src/callbacks/callbacks.jl +++ b/src/callbacks/callbacks.jl @@ -32,4 +32,3 @@ include("post_process.jl") include("stepsize.jl") include("update.jl") include("steady_state_reached.jl") -include("particle_shifting.jl") diff --git a/src/callbacks/particle_shifting.jl b/src/callbacks/particle_shifting.jl deleted file mode 100644 index 921370f3b1..0000000000 --- a/src/callbacks/particle_shifting.jl +++ /dev/null @@ -1,148 +0,0 @@ -@doc raw""" - ParticleShiftingCallback() - -Callback to apply the Particle Shifting Technique by [Sun et al. (2017)](@cite Sun2017). -Following the original paper, the callback is applied in every time step and not -in every stage of a multi-stage time integration method to reduce the computational -cost and improve the stability of the scheme. - -See [Callbacks](@ref Callbacks) for more information on how to use this callback. -See [Particle Shifting Technique](@ref shifting) for more information on the method itself. - -!!! warning - The Particle Shifting Technique needs to be disabled close to the free surface - and therefore requires a free surface detection method. This is not yet implemented. - **This callback cannot be used in a free surface simulation.** -""" -function ParticleShiftingCallback() - # The first one is the `condition`, the second the `affect!` - return DiscreteCallback((particle_shifting_condition), particle_shifting!, - save_positions=(false, false)) -end - -# `condition` -function particle_shifting_condition(u, t, integrator) - return true -end - -# `affect!` -function particle_shifting!(integrator) - t = integrator.t - semi = integrator.p - v_ode, u_ode = integrator.u.x - dt = integrator.dt - # Internal cache vector, which is safe to use as temporary array - vu_cache = first(get_tmp_cache(integrator)) - - @trixi_timeit timer() "particle shifting callback" begin - # 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 and nhs" begin - # Don't create sub-timers here to avoid cluttering the timer output - @notimeit timer() update_systems_and_nhs(v_ode, u_ode, semi, t) - end - - @trixi_timeit timer() "particle shifting" foreach_system(semi) do system - u = wrap_u(u_ode, system, semi) - v = wrap_v(v_ode, system, semi) - particle_shifting!(u, v, system, v_ode, u_ode, semi, vu_cache, dt) - end - end - - # Tell OrdinaryDiffEq that `u` has been modified - u_modified!(integrator, true) - - return integrator -end - -function particle_shifting!(u, v, system, v_ode, u_ode, semi, u_cache, dt) - return u -end - -function particle_shifting!(u, v, system::FluidSystem, v_ode, u_ode, semi, - vu_cache, dt) - # Wrap the cache vector to an NDIMS x NPARTICLES matrix. - # We need this buffer because we cannot safely update `u` while iterating over it. - _, u_cache = vu_cache.x - delta_r = wrap_u(u_cache, system, semi) - set_zero!(delta_r) - - # This has similar performance to `maximum(..., eachparticle(system))`, - # but is GPU-compatible. - v_max = maximum(x -> sqrt(dot(x, x)), - reinterpret(reshape, SVector{ndims(system), eltype(v)}, - current_velocity(v, system))) - - # TODO this needs to be adapted to multi-resolution. - # Section 3.2 explains what else needs to be changed. - dx = particle_spacing(system, 1) - Wdx = smoothing_kernel(system, dx, 1) - h = smoothing_length(system, 1) - - foreach_system(semi) do neighbor_system - u_neighbor = wrap_u(u_ode, neighbor_system, semi) - v_neighbor = wrap_v(v_ode, neighbor_system, semi) - - system_coords = current_coordinates(u, system) - neighbor_coords = current_coordinates(u_neighbor, neighbor_system) - - foreach_point_neighbor(system, neighbor_system, system_coords, neighbor_coords, - semi; - points=each_moving_particle(system)) do particle, neighbor, - pos_diff, distance - m_b = hydrodynamic_mass(neighbor_system, neighbor) - rho_a = current_density(v, system, particle) - rho_b = current_density(v_neighbor, neighbor_system, neighbor) - - kernel = smoothing_kernel(system, distance, particle) - grad_kernel = smoothing_kernel_grad(system, pos_diff, distance, particle) - - # According to p. 29 below Eq. 9 - R = 2 // 10 - n = 4 - - # Eq. 7 in Sun et al. (2017). - # According to the paper, CFL * Ma can be rewritten as Δt * v_max / h - # (see p. 29, right above Eq. 9), but this does not work when scaling h. - # When setting CFL * Ma = Δt * v_max / (2 * Δx), PST works as expected - # for both small and large smoothing length factors. - # We need to scale - # - quadratically with the smoothing length, - # - linearly with the particle spacing, - # - linearly with the time step. - # See https://github.com/trixi-framework/TrixiParticles.jl/pull/834. - delta_r_ = -dt * v_max * (2 * h)^2 / (2 * dx) * (1 + R * (kernel / Wdx)^n) * - m_b / (rho_a + rho_b) * grad_kernel - - # Write into the buffer - for i in eachindex(delta_r_) - @inbounds delta_r[i, particle] += delta_r_[i] - end - end - end - - # Add δ_r from the buffer to the current coordinates - @threaded semi for particle in eachparticle(system) - for i in axes(delta_r, 1) - @inbounds u[i, particle] += delta_r[i, particle] - end - end - - return u -end - -function Base.show(io::IO, cb::DiscreteCallback{<:Any, typeof(particle_shifting!)}) - @nospecialize cb # reduce precompilation time - print(io, "ParticleShiftingCallback()") -end - -function Base.show(io::IO, ::MIME"text/plain", - cb::DiscreteCallback{<:Any, typeof(particle_shifting!)}) - @nospecialize cb # reduce precompilation time - - if get(io, :compact, false) - show(io, cb) - else - summary_box(io, "ParticleShiftingCallback") - end -end diff --git a/src/callbacks/post_process.jl b/src/callbacks/post_process.jl index 36e16ce7f0..5f5256d96f 100644 --- a/src/callbacks/post_process.jl +++ b/src/callbacks/post_process.jl @@ -229,6 +229,7 @@ end # `affect!` function (pp::PostprocessCallback)(integrator) @trixi_timeit timer() "apply postprocess cb" begin + dv_ode, du_ode = get_du(integrator).x vu_ode = integrator.u v_ode, u_ode = vu_ode.x semi = integrator.p @@ -244,15 +245,17 @@ function (pp::PostprocessCallback)(integrator) end foreach_system(semi) do system - if system isa BoundarySystem && pp.exclude_boundary + if system isa AbstractBoundarySystem && pp.exclude_boundary return end system_index = system_indices(system, semi) for (key, f) in pp.func - result = custom_quantity(f, system, v_ode, u_ode, semi, t) - if result !== nothing + result_ = custom_quantity(f, system, dv_ode, du_ode, v_ode, u_ode, semi, t) + if result_ !== nothing + # Transfer to CPU if data is on the GPU. Do nothing if already on CPU. + result = transfer2cpu(result_) add_entry!(pp, string(key), t, result, filenames[system_index]) new_data = true end @@ -265,7 +268,7 @@ function (pp::PostprocessCallback)(integrator) if isfinished(integrator) || (pp.write_file_interval > 0 && backup_condition(pp, integrator)) - write_postprocess_callback(pp) + write_postprocess_callback(pp, integrator) end # Tell OrdinaryDiffEq that `u` has not been modified @@ -284,13 +287,14 @@ end end # After the simulation has finished, this function is called to write the data to a JSON file -function write_postprocess_callback(pp::PostprocessCallback) +function write_postprocess_callback(pp::PostprocessCallback, integrator) isempty(pp.data) && return mkpath(pp.output_directory) data = Dict{String, Any}() - write_meta_data!(data, pp.git_hash[]) + data["meta"] = create_meta_data_dict(pp, integrator) + prepare_series_data!(data, pp) time_stamp = "" @@ -340,15 +344,6 @@ function create_series_dict(values, times, system_name="") "time" => times) end -function write_meta_data!(data, git_hash) - meta_data = Dict("solver_name" => "TrixiParticles.jl", - "solver_version" => git_hash, - "julia_version" => string(VERSION)) - - data["meta"] = meta_data - return data -end - function write_csv(abs_file_path, data) times = Float64[] diff --git a/src/callbacks/solution_saving.jl b/src/callbacks/solution_saving.jl index f9d8244f2f..0d017249c0 100644 --- a/src/callbacks/solution_saving.jl +++ b/src/callbacks/solution_saving.jl @@ -2,8 +2,7 @@ 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, prefix="", - verbose=false, write_meta_data=true, max_coordinates=2^15, - custom_quantities...) + verbose=false, max_coordinates=2^15, custom_quantities...) Callback to save the current numerical solution in VTK format in regular intervals. @@ -26,9 +25,8 @@ 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=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. @@ -70,7 +68,6 @@ mutable struct SolutionSavingCallback{I, CQ} save_times :: Vector{Float64} save_initial_solution :: Bool save_final_solution :: Bool - write_meta_data :: Bool verbose :: Bool output_directory :: String prefix :: String @@ -84,8 +81,9 @@ function SolutionSavingCallback(; interval::Integer=0, dt=0.0, save_times=Float64[], save_initial_solution=true, save_final_solution=true, output_directory="out", append_timestamp=false, - prefix="", verbose=false, write_meta_data=true, - max_coordinates=Float64(2^15), custom_quantities...) + prefix="", verbose=false, + max_coordinates=Float64(2^15), + custom_quantities...) if (dt > 0 && interval > 0) || (length(save_times) > 0 && (dt > 0 || interval > 0)) throw(ArgumentError("Setting multiple save times for the same solution " * "callback is not possible. Use either `dt`, `interval` or `save_times`.")) @@ -101,8 +99,8 @@ function SolutionSavingCallback(; interval::Integer=0, dt=0.0, solution_callback = SolutionSavingCallback(interval, Float64.(save_times), save_initial_solution, save_final_solution, - write_meta_data, verbose, output_directory, - prefix, max_coordinates, custom_quantities, + verbose, output_directory, prefix, + max_coordinates, custom_quantities, -1, Ref("UnknownVersion")) if length(save_times) > 0 @@ -133,6 +131,8 @@ function initialize_save_cb!(solution_callback::SolutionSavingCallback, u, t, in solution_callback.latest_saved_iter = -1 solution_callback.git_hash[] = compute_git_hash() + write_meta_data(solution_callback, integrator) + # Save initial solution if solution_callback.save_initial_solution solution_callback(integrator) @@ -151,9 +151,10 @@ end # `affect!` function (solution_callback::SolutionSavingCallback)(integrator) - (; interval, output_directory, custom_quantities, write_meta_data, git_hash, - verbose, prefix, latest_saved_iter, max_coordinates) = solution_callback + (; interval, output_directory, custom_quantities, git_hash, verbose, + prefix, latest_saved_iter, max_coordinates) = solution_callback + dvdu_ode = get_du(integrator) vu_ode = integrator.u semi = integrator.p iter = get_iter(interval, integrator) @@ -173,10 +174,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; + @trixi_timeit timer() "save solution" trixi2vtk(dvdu_ode, vu_ode, semi, integrator.t; iter, output_directory, prefix, - write_meta_data, git_hash=git_hash[], - max_coordinates, custom_quantities...) + git_hash=git_hash[], max_coordinates, + custom_quantities...) # Tell OrdinaryDiffEq that `u` has not been modified u_modified!(integrator, false) diff --git a/src/callbacks/steady_state_reached.jl b/src/callbacks/steady_state_reached.jl index 2ff7b5f9a3..2f30177b9a 100644 --- a/src/callbacks/steady_state_reached.jl +++ b/src/callbacks/steady_state_reached.jl @@ -131,11 +131,12 @@ end vu_ode = integrator.u v_ode, u_ode = vu_ode.x + dv_ode, du_ode = get_du(integrator).x semi = integrator.p # Calculate kinetic energy ekin = sum(semi.systems) do system - return kinetic_energy(system, v_ode, u_ode, semi, 0) + return kinetic_energy(system, dv_ode, du_ode, v_ode, u_ode, semi, 0) end if length(previous_ekin) == interval_size diff --git a/src/callbacks/update.jl b/src/callbacks/update.jl index 77496a46c5..8eecb8380a 100644 --- a/src/callbacks/update.jl +++ b/src/callbacks/update.jl @@ -74,22 +74,32 @@ function (update_callback!::UpdateCallback)(integrator) 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) - - # Update open boundaries first, since particles might be activated or deactivated - @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 particle packing" foreach_system(semi) do system - update_particle_packing(system, v_ode, u_ode, semi, integrator) - end - - # This is only used by the particle packing system and should be removed in the future - @trixi_timeit timer() "update TVF" foreach_system(semi) do system - update_transport_velocity!(system, v_ode, semi) + @trixi_timeit timer() "update callback" begin + # 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 and nhs" begin + # Don't create sub-timers here to avoid cluttering the timer output + @notimeit timer() update_systems_and_nhs(v_ode, u_ode, semi, t) + end + + # Update open boundaries first, since particles might be activated or deactivated + @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 particle packing" foreach_system(semi) do system + update_particle_packing(system, v_ode, u_ode, semi, integrator) + end + + # This is only used by the particle packing system and should be removed in the future + @trixi_timeit timer() "update TVF" foreach_system(semi) do system + update_transport_velocity!(system, v_ode, semi) + end + + @trixi_timeit timer() "particle shifting" foreach_system(semi) do system + particle_shifting_from_callback!(u_ode, shifting_technique(system), system, + v_ode, semi, integrator.dt) + end end # Tell OrdinaryDiffEq that `u` has been modified diff --git a/src/general/system.jl b/src/general/abstract_system.jl similarity index 78% rename from src/general/system.jl rename to src/general/abstract_system.jl index 929fb8b425..1d0bc2813f 100644 --- a/src/general/system.jl +++ b/src/general/abstract_system.jl @@ -1,28 +1,20 @@ # Abstract supertype for all system types. -abstract type System{NDIMS} end +abstract type AbstractSystem{NDIMS} end -abstract type FluidSystem{NDIMS} <: System{NDIMS} end -timer_name(::FluidSystem) = "fluid" -vtkname(system::FluidSystem) = "fluid" +@inline Base.ndims(::AbstractSystem{NDIMS}) where {NDIMS} = NDIMS +@inline Base.eltype(system::AbstractSystem) = error("eltype not implemented for system $system") -abstract type SolidSystem{NDIMS} <: System{NDIMS} end -timer_name(::SolidSystem) = "solid" -vtkname(system::SolidSystem) = "solid" +abstract type AbstractFluidSystem{NDIMS} <: AbstractSystem{NDIMS} end +timer_name(::AbstractFluidSystem) = "fluid" +vtkname(system::AbstractFluidSystem) = "fluid" -abstract type BoundarySystem{NDIMS} <: System{NDIMS} end -timer_name(::BoundarySystem) = "boundary" -vtkname(system::BoundarySystem) = "boundary" +abstract type AbstractStructureSystem{NDIMS} <: AbstractSystem{NDIMS} end +timer_name(::AbstractStructureSystem) = "structure" +vtkname(system::AbstractStructureSystem) = "structure" -@inline function set_zero!(du) - du .= zero(eltype(du)) - - return du -end - -initialize!(system, semi) = system - -@inline Base.ndims(::System{NDIMS}) where {NDIMS} = NDIMS -@inline Base.eltype(system::System) = error("eltype not implemented for system $system") +abstract type AbstractBoundarySystem{NDIMS} <: AbstractSystem{NDIMS} end +timer_name(::AbstractBoundarySystem) = "boundary" +vtkname(system::AbstractBoundarySystem) = "boundary" # Number of integrated variables in the first component of the ODE system (coordinates) @inline u_nvariables(system) = ndims(system) @@ -35,19 +27,30 @@ initialize!(system, semi) = system @inline nparticles(system) = length(system.mass) # Number of particles in the system whose positions are to be integrated (corresponds to the size of u and du) -@inline n_moving_particles(system) = nparticles(system) +@inline n_integrated_particles(system) = nparticles(system) -@inline eachparticle(system) = Base.OneTo(nparticles(system)) +@inline eachparticle(system::AbstractSystem) = each_active_particle(system) +@inline eachparticle(initial_condition) = Base.OneTo(nparticles(initial_condition)) # 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 each_integrated_particle(system) = each_integrated_particle(system, buffer(system)) +@inline function each_integrated_particle(system, ::Nothing) + return Base.OneTo(n_integrated_particles(system)) +end -@inline active_coordinates(u, system) = active_coordinates(u, system, system.buffer) +@inline active_coordinates(u, system) = active_coordinates(u, system, buffer(system)) @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) +@inline each_active_particle(system) = each_active_particle(system, buffer(system)) +@inline each_active_particle(system, ::Nothing) = Base.OneTo(nparticles(system)) + +@inline function set_zero!(du) + du .= zero(eltype(du)) + + return du +end + +initialize!(system, semi) = system # This should not be dispatched by system type. We always expect to get a column of `A`. @propagate_inbounds function extract_svector(A, system, i) @@ -99,11 +102,11 @@ end # By default, try to extract it from `v`. @inline current_velocity(v, system) = v -@inline function current_density(v, system::System, particle) +@inline function current_density(v, system::AbstractSystem, particle) return current_density(v, system)[particle] end -@propagate_inbounds function current_pressure(v, system::System, particle) +@propagate_inbounds function current_pressure(v, system::AbstractSystem, particle) return current_pressure(v, system)[particle] end @@ -147,9 +150,6 @@ function update_final!(system, v, u, v_ode, u_ode, semi, t) return system end -# Only for systems requiring the use of the `UpdateCallback` -@inline requires_update_callback(system) = false - @inline initial_smoothing_length(system) = smoothing_length(system, nothing) @inline function smoothing_length(system, particle) diff --git a/src/general/buffer.jl b/src/general/buffer.jl index 3e1c28e8ca..1e2c196799 100644 --- a/src/general/buffer.jl +++ b/src/general/buffer.jl @@ -36,6 +36,10 @@ function allocate_buffer(initial_condition, buffer::SystemBuffer) return union(initial_condition, buffer_ic) end +# By default, there is no buffer. +# Dispatch by system type to handle systems that provide a buffer. +@inline buffer(system) = nothing + @inline update_system_buffer!(buffer::Nothing, semi) = buffer # TODO `resize` allocates. Find a non-allocating version @@ -50,11 +54,13 @@ end return buffer end -@inline each_moving_particle(system, buffer) = active_particles(system, buffer) +@inline each_integrated_particle(system, buffer) = each_active_particle(system, buffer) -@inline active_coordinates(u, system, buffer) = view(u, :, active_particles(system, buffer)) +@inline function active_coordinates(u, system, buffer) + return view(u, :, each_active_particle(system, buffer)) +end -@inline function active_particles(system, buffer) +@inline function each_active_particle(system, buffer) return view(buffer.eachparticle, 1:buffer.active_particle_count[]) end diff --git a/src/general/corrections.jl b/src/general/corrections.jl index 57c586d7ed..425616a21a 100644 --- a/src/general/corrections.jl +++ b/src/general/corrections.jl @@ -107,11 +107,11 @@ which results in a 1st-order-accurate SPH method (see [Bonet, 1999](@cite Bonet1 """ struct MixedKernelGradientCorrection end -function kernel_correction_coefficient(system::FluidSystem, particle) +function kernel_correction_coefficient(system::AbstractFluidSystem, particle) return system.cache.kernel_correction_coefficient[particle] end -function kernel_correction_coefficient(system::BoundarySystem, particle) +function kernel_correction_coefficient(system::AbstractBoundarySystem, particle) return system.boundary_model.cache.kernel_correction_coefficient[particle] end @@ -126,7 +126,8 @@ function compute_correction_values!(system, ::ShepardKernelCorrection, u, v_ode, system.cache.kernel_correction_coefficient) end -function compute_correction_values!(system::BoundarySystem, ::ShepardKernelCorrection, u, +function compute_correction_values!(system::AbstractBoundarySystem, + ::ShepardKernelCorrection, u, v_ode, u_ode, semi) return compute_shepard_coeff!(system, current_coordinates(u, system), v_ode, u_ode, semi, @@ -160,15 +161,15 @@ function compute_shepard_coeff!(system, system_coords, v_ode, u_ode, semi, return kernel_correction_coefficient end -function dw_gamma(system::FluidSystem, particle) +function dw_gamma(system::AbstractFluidSystem, particle) return extract_svector(system.cache.dw_gamma, system, particle) end -function dw_gamma(system::BoundarySystem, particle) +function dw_gamma(system::AbstractBoundarySystem, particle) return extract_svector(system.boundary_model.cache.dw_gamma, system, particle) end -function compute_correction_values!(system::FluidSystem, +function compute_correction_values!(system::AbstractFluidSystem, correction::Union{KernelCorrection, MixedKernelGradientCorrection}, u, v_ode, u_ode, semi) @@ -178,7 +179,7 @@ function compute_correction_values!(system::FluidSystem, system.cache.dw_gamma) end -function compute_correction_values!(system::BoundarySystem, +function compute_correction_values!(system::AbstractBoundarySystem, correction::Union{KernelCorrection, MixedKernelGradientCorrection}, u, v_ode, u_ode, semi) diff --git a/src/general/custom_quantities.jl b/src/general/custom_quantities.jl index 58e40669c9..c35de55589 100644 --- a/src/general/custom_quantities.jl +++ b/src/general/custom_quantities.jl @@ -3,32 +3,36 @@ Returns the total kinetic energy of all particles in a system. """ -function kinetic_energy(system, v_ode, u_ode, semi, t) +function kinetic_energy(system, dv_ode, du_ode, v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) # TODO: `current_velocity` should only contain active particles # (see https://github.com/trixi-framework/TrixiParticles.jl/issues/850) velocity = reinterpret(reshape, SVector{ndims(system), eltype(v)}, - view(current_velocity(v, system), :, active_particles(system))) - mass = view(system.mass, active_particles(system)) + view(current_velocity(v, system), :, + each_active_particle(system))) + mass = view(system.mass, each_active_particle(system)) return mapreduce(+, velocity, mass) do v_i, m_i return m_i * dot(v_i, v_i) / 2 end end -kinetic_energy(system::BoundarySystem, v_ode, u_ode, semi, t) = zero(eltype(system)) +function kinetic_energy(system::AbstractBoundarySystem, + dv_ode, du_ode, v_ode, u_ode, semi, t) + return zero(eltype(system)) +end """ total_mass Returns the total mass of all particles in a system. """ -function total_mass(system, v_ode, u_ode, semi, t) +function total_mass(system, dv_ode, du_ode, v_ode, u_ode, semi, t) return sum(system.mass) end -function total_mass(system::BoundarySystem, v_ode, u_ode, semi, t) +function total_mass(system::AbstractBoundarySystem, dv_ode, du_ode, v_ode, u_ode, semi, t) # It does not make sense to return a mass for boundary systems. # The material density and therefore the physical mass of the boundary is not relevant # when simulating a solid, stationary wall. The boundary always behaves as if it had @@ -47,12 +51,12 @@ end Returns the maximum pressure over all particles in a system. """ -function max_pressure(system::FluidSystem, v_ode, u_ode, semi, t) +function max_pressure(system::AbstractFluidSystem, dv_ode, du_ode, v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) return maximum(current_pressure(v, system)) end -function max_pressure(system, v_ode, u_ode, semi, t) +function max_pressure(system, dv_ode, du_ode, v_ode, u_ode, semi, t) return NaN end @@ -61,12 +65,12 @@ end Returns the minimum pressure over all particles in a system. """ -function min_pressure(system::FluidSystem, v_ode, u_ode, semi, t) +function min_pressure(system::AbstractFluidSystem, dv_ode, du_ode, v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) return minimum(current_pressure(v, system)) end -function min_pressure(system, v_ode, u_ode, semi, t) +function min_pressure(system, dv_ode, du_ode, v_ode, u_ode, semi, t) return NaN end @@ -75,13 +79,13 @@ end Returns the average pressure over all particles in a system. """ -function avg_pressure(system::FluidSystem, v_ode, u_ode, semi, t) +function avg_pressure(system::AbstractFluidSystem, dv_ode, du_ode, v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) sum_ = sum(current_pressure(v, system)) return sum_ / nparticles(system) end -function avg_pressure(system, v_ode, u_ode, semi, t) +function avg_pressure(system, dv_ode, du_ode, v_ode, u_ode, semi, t) return NaN end @@ -90,12 +94,12 @@ end Returns the maximum density over all particles in a system. """ -function max_density(system::FluidSystem, v_ode, u_ode, semi, t) +function max_density(system::AbstractFluidSystem, dv_ode, du_ode, v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) return maximum(current_density(v, system)) end -function max_density(system, v_ode, u_ode, semi, t) +function max_density(system, dv_ode, du_ode, v_ode, u_ode, semi, t) return NaN end @@ -104,12 +108,12 @@ end Returns the minimum density over all particles in a system. """ -function min_density(system::FluidSystem, v_ode, u_ode, semi, t) +function min_density(system::AbstractFluidSystem, dv_ode, du_ode, v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) return minimum(current_density(v, system)) end -function min_density(system, v_ode, u_ode, semi, t) +function min_density(system, dv_ode, du_ode, v_ode, u_ode, semi, t) return NaN end @@ -118,12 +122,12 @@ end Returns the average_density over all particles in a system. """ -function avg_density(system::FluidSystem, v_ode, u_ode, semi, t) +function avg_density(system::AbstractFluidSystem, dv_ode, du_ode, v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) sum_ = sum(current_density(v, system)) return sum_ / nparticles(system) end -function avg_density(system, v_ode, u_ode, semi, t) +function avg_density(system, dv_ode, du_ode, v_ode, u_ode, semi, t) return NaN end diff --git a/src/general/density_calculators.jl b/src/general/density_calculators.jl index efeff6017e..65937de01e 100644 --- a/src/general/density_calculators.jl +++ b/src/general/density_calculators.jl @@ -24,7 +24,7 @@ difference of the coordinates, ``v_{ab} = v_a - v_b`` of the velocities of parti struct ContinuityDensity end function summation_density!(system, semi, u, u_ode, density; - particles=each_moving_particle(system)) + particles=each_integrated_particle(system)) set_zero!(density) # Use all other systems for the density summation diff --git a/src/general/general.jl b/src/general/general.jl index 9307671cd8..87b5b82022 100644 --- a/src/general/general.jl +++ b/src/general/general.jl @@ -1,4 +1,4 @@ -# Note that `system.jl` has already been included. +# Note that `abstract_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") diff --git a/src/general/gpu.jl b/src/general/gpu.jl index 8fe1f85d87..196b24861b 100644 --- a/src/general/gpu.jl +++ b/src/general/gpu.jl @@ -12,19 +12,21 @@ Adapt.@adapt_structure InitialCondition Adapt.@adapt_structure WeaklyCompressibleSPHSystem Adapt.@adapt_structure DensityDiffusionAntuono Adapt.@adapt_structure EntropicallyDampedSPHSystem -Adapt.@adapt_structure BoundarySPHSystem +Adapt.@adapt_structure WallBoundarySystem Adapt.@adapt_structure BoundaryModelDummyParticles Adapt.@adapt_structure BoundaryModelMonaghanKajtar -Adapt.@adapt_structure BoundaryMovement +Adapt.@adapt_structure PrescribedMotion Adapt.@adapt_structure TotalLagrangianSPHSystem Adapt.@adapt_structure BoundaryZone Adapt.@adapt_structure SystemBuffer -Adapt.@adapt_structure OpenBoundarySPHSystem +Adapt.@adapt_structure OpenBoundarySystem KernelAbstractions.get_backend(::PtrArray) = KernelAbstractions.CPU() -KernelAbstractions.get_backend(system::System) = KernelAbstractions.get_backend(system.mass) +function KernelAbstractions.get_backend(system::AbstractSystem) + KernelAbstractions.get_backend(system.mass) +end -function KernelAbstractions.get_backend(system::BoundarySPHSystem) +function KernelAbstractions.get_backend(system::WallBoundarySystem) KernelAbstractions.get_backend(system.coordinates) end diff --git a/src/general/interpolation.jl b/src/general/interpolation.jl index afee4de5b1..bf26f80906 100644 --- a/src/general/interpolation.jl +++ b/src/general/interpolation.jl @@ -191,9 +191,10 @@ function interpolate_plane_2d(min_corner, max_corner, resolution, semi, ref_syst x_range = range(min_corner[1], max_corner[1], length=n_points_per_dimension[1]) y_range = range(min_corner[2], max_corner[2], length=n_points_per_dimension[2]) - # Generate points within the plane. Use `tlsph=true` to generate points on the boundary + # Generate points within the plane. Use `place_on_shell=true` to generate points + # on the shell of the geometry. point_coords = rectangular_shape_coords(resolution, n_points_per_dimension, min_corner, - tlsph=true) + place_on_shell=true) results = interpolate_points(point_coords, semi, ref_system, v_ode, u_ode, smoothing_length=smoothing_length, @@ -593,7 +594,7 @@ end neighbor_count=neighbor_count, cache...) end -@inline function create_cache_interpolation(ref_system::FluidSystem, n_points, semi) +@inline function create_cache_interpolation(ref_system::AbstractFluidSystem, n_points, semi) (; parallelization_backend) = semi velocity = allocate(parallelization_backend, eltype(ref_system), @@ -608,7 +609,8 @@ end return (; velocity, pressure, density) end -@inline function create_cache_interpolation(ref_system::SolidSystem, n_points, semi) +@inline function create_cache_interpolation(ref_system::AbstractStructureSystem, + n_points, semi) (; parallelization_backend) = semi velocity = allocate(parallelization_backend, eltype(ref_system), @@ -626,7 +628,12 @@ end return (; velocity, jacobian, von_mises_stress, cauchy_stress) end -@inline function interpolate_system!(cache, v, system::FluidSystem, +function interpolate_system!(cache, v, neighbor_system, + point, neighbor, volume_b, W_ab, clip_negative_pressure) + return cache +end + +@inline function interpolate_system!(cache, v, system::AbstractFluidSystem, point, neighbor, volume_b, W_ab, clip_negative_pressure) velocity = current_velocity(v, system, neighbor) @@ -646,7 +653,7 @@ end return cache end -@inline function interpolate_system!(cache, v, system::SolidSystem, +@inline function interpolate_system!(cache, v, system::AbstractStructureSystem, point, neighbor, volume_b, W_ab, clip_negative_pressure) velocity = current_velocity(v, system, neighbor) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index a9c78b3349..320e3ad8ba 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -70,7 +70,7 @@ struct Semidiscretization{BACKEND, S, RU, RV, NS, UCU} end end -function Semidiscretization(systems::Union{System, Nothing}...; +function Semidiscretization(systems::Union{AbstractSystem, Nothing}...; neighborhood_search=GridNeighborhoodSearch{ndims(first(systems))}(), parallelization_backend=PolyesterBackend()) systems = filter(system -> !isnothing(system), systems) @@ -79,11 +79,11 @@ function Semidiscretization(systems::Union{System, Nothing}...; # Other checks might be added here later. check_configuration(systems, neighborhood_search) - sizes_u = [u_nvariables(system) * n_moving_particles(system) + sizes_u = [u_nvariables(system) * n_integrated_particles(system) for system in systems] ranges_u = Tuple((sum(sizes_u[1:(i - 1)]) + 1):sum(sizes_u[1:i]) for i in eachindex(sizes_u)) - sizes_v = [v_nvariables(system) * n_moving_particles(system) + sizes_v = [v_nvariables(system) * n_integrated_particles(system) for system in systems] ranges_v = Tuple((sum(sizes_v[1:(i - 1)]) + 1):sum(sizes_v[1:i]) for i in eachindex(sizes_v)) @@ -151,13 +151,13 @@ end return compact_support(smoothing_kernel, initial_smoothing_length(system)) end -@inline function compact_support(system::OpenBoundarySPHSystem, neighbor) +@inline function compact_support(system::OpenBoundarySystem, neighbor) # Use the compact support of the fluid return compact_support(neighbor, system) end -@inline function compact_support(system::OpenBoundarySPHSystem, - neighbor::OpenBoundarySPHSystem) +@inline function compact_support(system::OpenBoundarySystem, + neighbor::OpenBoundarySystem) # This NHS is never used return 0.0 end @@ -178,24 +178,25 @@ end return compact_support(smoothing_kernel, smoothing_length) end -@inline function compact_support(system::Union{TotalLagrangianSPHSystem, BoundarySPHSystem}, +@inline function compact_support(system::Union{TotalLagrangianSPHSystem, + WallBoundarySystem}, neighbor) return compact_support(system, system.boundary_model, neighbor) end @inline function compact_support(system, model::BoundaryModelMonaghanKajtar, neighbor) - # Use the compact support of the fluid for solid-fluid interaction + # Use the compact support of the fluid for structure-fluid interaction return compact_support(neighbor, system) end @inline function compact_support(system, model::BoundaryModelMonaghanKajtar, - neighbor::BoundarySPHSystem) + neighbor::WallBoundarySystem) # This NHS is never used return 0.0 end @inline function compact_support(system, model::BoundaryModelDummyParticles, neighbor) - # TODO: Monaghan-Kajtar BC are using the fluid's compact support for solid-fluid + # TODO: Monaghan-Kajtar BC are using the fluid's compact support for structure-fluid # interaction. Dummy particle BC use the model's compact support, which is also used # for density summations. (; smoothing_kernel, smoothing_length) = model @@ -287,8 +288,8 @@ function semidiscretize(semi, tspan; reset_threads=true) Polyester.reset_threads!() end - 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) + sizes_u = (u_nvariables(system) * n_integrated_particles(system) for system in systems) + sizes_v = (v_nvariables(system) * n_integrated_particles(system) for system in systems) # Use either the specified backend, e.g., `CUDABackend` or `MetalBackend` or # use CPU vectors for all CPU backends. @@ -395,7 +396,7 @@ function initialize_neighborhood_searches!(semi) PointNeighbors.initialize!(get_neighborhood_search(system, neighbor, semi), initial_coordinates(system), initial_coordinates(neighbor), - eachindex_y=active_particles(neighbor)) + eachindex_y=each_active_particle(neighbor)) end end @@ -409,10 +410,11 @@ end range = ranges_v[system_indices(system, semi)] - @boundscheck @assert length(range) == v_nvariables(system) * n_moving_particles(system) + @boundscheck @assert length(range) == + v_nvariables(system) * n_integrated_particles(system) return wrap_array(v_ode, range, - (StaticInt(v_nvariables(system)), n_moving_particles(system))) + (StaticInt(v_nvariables(system)), n_integrated_particles(system))) end @inline function wrap_u(u_ode, system, semi) @@ -420,10 +422,11 @@ end range = ranges_u[system_indices(system, semi)] - @boundscheck @assert length(range) == u_nvariables(system) * n_moving_particles(system) + @boundscheck @assert length(range) == + u_nvariables(system) * n_integrated_particles(system) return wrap_array(u_ode, range, - (StaticInt(u_nvariables(system)), n_moving_particles(system))) + (StaticInt(u_nvariables(system)), n_integrated_particles(system))) end @inline function wrap_array(array::Array, range, size) @@ -461,7 +464,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 semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) # This can be dispatched per system add_velocity!(du, v, particle, system) end @@ -482,10 +485,10 @@ end end # Solid wall boundary system doesn't integrate the particle positions -@inline add_velocity!(du, v, particle, system::BoundarySPHSystem) = du +@inline add_velocity!(du, v, particle, system::WallBoundarySystem) = du -@inline function add_velocity!(du, v, particle, system::FluidSystem) - # This is zero unless a transport velocity is used +@inline function add_velocity!(du, v, particle, system::AbstractFluidSystem) + # This is zero unless a shifting technique is used delta_v_ = delta_v(system, particle) for i in 1:ndims(system) @@ -519,7 +522,7 @@ end # before calling `interact!` to compute forces. function update_systems_and_nhs(v_ode, u_ode, semi, t) # First update step before updating the NHS - # (for example for writing the current coordinates in the solid system) + # (for example for writing the current coordinates in the TLSPH system) foreach_system(semi) do system v = wrap_v(v_ode, system, semi) u = wrap_u(u_ode, system, semi) @@ -587,7 +590,7 @@ function add_source_terms!(dv_ode, v_ode, u_ode, semi, t) v = wrap_v(v_ode, system, semi) u = wrap_u(u_ode, system, semi) - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_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), t) @@ -598,11 +601,13 @@ function add_source_terms!(dv_ode, v_ode, u_ode, semi, t) end @inline source_terms(system) = nothing -@inline source_terms(system::Union{FluidSystem, SolidSystem}) = system.source_terms +@inline source_terms(system::Union{AbstractFluidSystem, AbstractStructureSystem}) = system.source_terms @inline add_acceleration!(dv, particle, system) = dv -@inline function add_acceleration!(dv, particle, system::Union{FluidSystem, SolidSystem}) +@inline function add_acceleration!(dv, particle, + system::Union{AbstractFluidSystem, + AbstractStructureSystem}) (; acceleration) = system for i in 1:ndims(system) @@ -707,18 +712,18 @@ end # NHS updates # To prevent hard-to-find bugs, there is not default version function update_nhs!(neighborhood_search, - system::FluidSystem, - neighbor::Union{FluidSystem, TotalLagrangianSPHSystem}, + system::AbstractFluidSystem, + neighbor::Union{AbstractFluidSystem, TotalLagrangianSPHSystem}, u_system, u_neighbor, semi) - # The current coordinates of fluids and solids change over time + # The current coordinates of fluids and structures change over time update!(neighborhood_search, current_coordinates(u_system, system), current_coordinates(u_neighbor, neighbor), - semi, points_moving=(true, true), eachindex_y=active_particles(neighbor)) + semi, points_moving=(true, true), eachindex_y=each_active_particle(neighbor)) end function update_nhs!(neighborhood_search, - system::FluidSystem, neighbor::BoundarySPHSystem, + system::AbstractFluidSystem, neighbor::WallBoundarySystem, u_system, u_neighbor, semi) # Boundary coordinates only change over time when `neighbor.ismoving[]` update!(neighborhood_search, @@ -728,7 +733,7 @@ function update_nhs!(neighborhood_search, end function update_nhs!(neighborhood_search, - system::FluidSystem, neighbor::OpenBoundarySPHSystem, + system::AbstractFluidSystem, neighbor::OpenBoundarySystem, u_system, u_neighbor, semi) # The current coordinates of fluids and open boundaries change over time. @@ -737,11 +742,11 @@ function update_nhs!(neighborhood_search, update!(neighborhood_search, current_coordinates(u_system, system), current_coordinates(u_neighbor, neighbor), - semi, points_moving=(true, true), eachindex_y=active_particles(neighbor)) + semi, points_moving=(true, true), eachindex_y=each_active_particle(neighbor)) end function update_nhs!(neighborhood_search, - system::OpenBoundarySPHSystem, neighbor::FluidSystem, + system::OpenBoundarySystem, neighbor::AbstractFluidSystem, u_system, u_neighbor, semi) # The current coordinates of both open boundaries and fluids change over time. @@ -750,31 +755,31 @@ function update_nhs!(neighborhood_search, update!(neighborhood_search, current_coordinates(u_system, system), current_coordinates(u_neighbor, neighbor), - semi, points_moving=(true, true), eachindex_y=active_particles(neighbor)) + semi, points_moving=(true, true), eachindex_y=each_active_particle(neighbor)) end function update_nhs!(neighborhood_search, - system::OpenBoundarySPHSystem, neighbor::TotalLagrangianSPHSystem, + system::OpenBoundarySystem, neighbor::TotalLagrangianSPHSystem, u_system, u_neighbor, semi) # Don't update. This NHS is never used. return neighborhood_search end function update_nhs!(neighborhood_search, - system::TotalLagrangianSPHSystem, neighbor::OpenBoundarySPHSystem, + system::TotalLagrangianSPHSystem, neighbor::OpenBoundarySystem, u_system, u_neighbor, semi) # Don't update. This NHS is never used. return neighborhood_search end function update_nhs!(neighborhood_search, - system::TotalLagrangianSPHSystem, neighbor::FluidSystem, + system::TotalLagrangianSPHSystem, neighbor::AbstractFluidSystem, u_system, u_neighbor, semi) - # The current coordinates of fluids and solids change over time + # The current coordinates of fluids and structured change over time update!(neighborhood_search, current_coordinates(u_system, system), current_coordinates(u_neighbor, neighbor), - semi, points_moving=(true, true), eachindex_y=active_particles(neighbor)) + semi, points_moving=(true, true), eachindex_y=each_active_particle(neighbor)) end function update_nhs!(neighborhood_search, @@ -785,9 +790,9 @@ function update_nhs!(neighborhood_search, end function update_nhs!(neighborhood_search, - system::TotalLagrangianSPHSystem, neighbor::BoundarySPHSystem, + system::TotalLagrangianSPHSystem, neighbor::WallBoundarySystem, u_system, u_neighbor, semi) - # The current coordinates of solids change over time. + # The current coordinates of structured change over time. # Boundary coordinates only change over time when `neighbor.ismoving[]`. update!(neighborhood_search, current_coordinates(u_system, system), @@ -797,25 +802,25 @@ end # This function is the same as the one below to avoid ambiguous dispatch when using `Union` function update_nhs!(neighborhood_search, - system::BoundarySPHSystem{<:BoundaryModelDummyParticles}, - neighbor::FluidSystem, u_system, u_neighbor, semi) + system::WallBoundarySystem{<:BoundaryModelDummyParticles}, + neighbor::AbstractFluidSystem, u_system, u_neighbor, semi) # 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. + # The current coordinates of fluids and structured change over time. update!(neighborhood_search, current_coordinates(u_system, system), current_coordinates(u_neighbor, neighbor), semi, points_moving=(system.ismoving[], true), - eachindex_y=active_particles(neighbor)) + eachindex_y=each_active_particle(neighbor)) end # This function is the same as the one above to avoid ambiguous dispatch when using `Union` function update_nhs!(neighborhood_search, - system::BoundarySPHSystem{<:BoundaryModelDummyParticles}, + system::WallBoundarySystem{<:BoundaryModelDummyParticles}, neighbor::TotalLagrangianSPHSystem, u_system, u_neighbor, semi) # Depending on the density calculator of the boundary model, this NHS is used for # - kernel summation (`SummationDensity`) @@ -823,7 +828,7 @@ function update_nhs!(neighborhood_search, # - pressure extrapolation (`AdamiPressureExtrapolation`) # # Boundary coordinates only change over time when `neighbor.ismoving[]`. - # The current coordinates of fluids and solids change over time. + # The current coordinates of fluids and structured change over time. update!(neighborhood_search, current_coordinates(u_system, system), current_coordinates(u_neighbor, neighbor), @@ -831,8 +836,8 @@ function update_nhs!(neighborhood_search, end function update_nhs!(neighborhood_search, - system::BoundarySPHSystem{<:BoundaryModelDummyParticles}, - neighbor::BoundarySPHSystem, + system::WallBoundarySystem{<:BoundaryModelDummyParticles}, + neighbor::WallBoundarySystem, u_system, u_neighbor, semi) # `system` coordinates only change over time when `system.ismoving[]`. # `neighbor` coordinates only change over time when `neighbor.ismoving[]`. @@ -863,8 +868,8 @@ function update_nhs!(neighborhood_search, end function update_nhs!(neighborhood_search, - system::BoundarySPHSystem, - neighbor::FluidSystem, + system::WallBoundarySystem, + neighbor::AbstractFluidSystem, u_system, u_neighbor, semi) # Don't update. This NHS is never used. return neighborhood_search @@ -879,8 +884,8 @@ function update_nhs!(neighborhood_search, end function update_nhs!(neighborhood_search, - system::Union{BoundarySPHSystem, OpenBoundarySPHSystem}, - neighbor::Union{BoundarySPHSystem, OpenBoundarySPHSystem}, + system::Union{WallBoundarySystem, OpenBoundarySystem}, + neighbor::Union{WallBoundarySystem, OpenBoundarySystem}, u_system, u_neighbor, semi) # Don't update. This NHS is never used. return neighborhood_search @@ -912,15 +917,15 @@ function check_configuration(systems, check_system_color(systems) end -check_configuration(system::System, systems, nhs) = nothing +check_configuration(system::AbstractSystem, systems, nhs) = nothing function check_system_color(systems) - if any(system isa FluidSystem && !(system isa ParticlePackingSystem) && + if any(system isa AbstractFluidSystem && !(system isa ParticlePackingSystem) && !isnothing(system.surface_tension) for system in systems) # System indices of all systems that are either a fluid or a boundary system - system_ids = findall(system isa Union{FluidSystem, BoundarySPHSystem} + system_ids = findall(system isa Union{AbstractFluidSystem, WallBoundarySystem} for system in systems) if length(system_ids) > 1 && sum(i -> systems[i].cache.color, system_ids) == 0 @@ -929,18 +934,20 @@ function check_system_color(systems) end end -function check_configuration(fluid_system::FluidSystem, systems, nhs) +function check_configuration(fluid_system::AbstractFluidSystem, systems, nhs) if !(fluid_system isa ParticlePackingSystem) && !isnothing(fluid_system.surface_tension) foreach_system(systems) do neighbor - if neighbor isa FluidSystem && isnothing(fluid_system.surface_tension) && + if neighbor isa AbstractFluidSystem && + isnothing(fluid_system.surface_tension) && isnothing(fluid_system.surface_normal_method) - throw(ArgumentError("All `FluidSystem` need to use a surface tension model or a surface normal method.")) + throw(ArgumentError("either none or all fluid systems in a simulation need " * + "to use a surface tension model or a surface normal method.")) end end end end -function check_configuration(system::BoundarySPHSystem, systems, nhs) +function check_configuration(system::WallBoundarySystem, systems, nhs) (; boundary_model) = system foreach_system(systems) do neighbor @@ -957,7 +964,7 @@ function check_configuration(system::TotalLagrangianSPHSystem, systems, nhs) (; boundary_model) = system foreach_system(systems) do neighbor - if neighbor isa FluidSystem && boundary_model === nothing + if neighbor isa AbstractFluidSystem && boundary_model === nothing throw(ArgumentError("a boundary model for `TotalLagrangianSPHSystem` must be " * "specified when simulating a fluid-structure interaction.")) end @@ -970,23 +977,23 @@ function check_configuration(system::TotalLagrangianSPHSystem, systems, nhs) end end -function check_configuration(system::OpenBoundarySPHSystem, systems, +function check_configuration(system::OpenBoundarySystem, systems, neighborhood_search::PointNeighbors.AbstractNeighborhoodSearch) - (; boundary_model, boundary_zone) = system + (; boundary_model, boundary_zones) = system # Store index of the fluid system. This is necessary for re-linking # in case we use Adapt.jl to create a new semidiscretization. fluid_system_index = findfirst(==(system.fluid_system), systems) system.fluid_system_index[] = fluid_system_index - if boundary_model isa BoundaryModelLastiwka && - boundary_zone isa BoundaryZone{BidirectionalFlow} - throw(ArgumentError("`BoundaryModelLastiwka` needs a specific flow direction. " * - "Please specify inflow and outflow.")) + if boundary_model isa BoundaryModelCharacteristicsLastiwka && + any(zone -> isnothing(zone.flow_direction), boundary_zones) + throw(ArgumentError("`BoundaryModelCharacteristicsLastiwka` needs a specific flow direction. " * + "Please specify `InFlow()` and `OutFlow()`.")) end if first(PointNeighbors.requires_update(neighborhood_search)) - throw(ArgumentError("`OpenBoundarySPHSystem` requires a neighborhood search " * + throw(ArgumentError("`OpenBoundarySystem` requires a neighborhood search " * "that does not require an update for the first set of coordinates (e.g. `GridNeighborhoodSearch`). " * "See the PointNeighbors.jl documentation for more details.")) end @@ -997,24 +1004,22 @@ end # Therefore, we have to re-link them based on the stored system index. set_system_links(system, semi) = system -function set_system_links(system::OpenBoundarySPHSystem, semi) +function set_system_links(system::OpenBoundarySystem, semi) fluid_system = semi.systems[system.fluid_system_index[]] - return OpenBoundarySPHSystem(system.boundary_model, - system.initial_condition, - fluid_system, # link to fluid system - system.fluid_system_index, - system.smoothing_length, - system.mass, - system.density, - system.volume, - system.pressure, - system.boundary_candidates, - system.fluid_candidates, - system.boundary_zone, - system.reference_velocity, - system.reference_pressure, - system.reference_density, - system.buffer, - system.cache) + return OpenBoundarySystem(system.boundary_model, + system.initial_condition, + fluid_system, # link to fluid system + system.fluid_system_index, + system.smoothing_length, + system.mass, + system.density, + system.volume, + system.pressure, + system.boundary_candidates, + system.fluid_candidates, + system.boundary_zone_indices, + system.boundary_zones, + system.buffer, + system.cache) end diff --git a/src/general/smoothing_kernels.jl b/src/general/smoothing_kernels.jl index 507ba2daf0..6a5e8b33f4 100644 --- a/src/general/smoothing_kernels.jl +++ b/src/general/smoothing_kernels.jl @@ -1,6 +1,6 @@ -abstract type SmoothingKernel{NDIMS} end +abstract type AbstractSmoothingKernel{NDIMS} end -@inline Base.ndims(::SmoothingKernel{NDIMS}) where {NDIMS} = NDIMS +@inline Base.ndims(::AbstractSmoothingKernel{NDIMS}) where {NDIMS} = NDIMS @inline function kernel_grad(kernel, pos_diff, distance, h) # TODO Use `eps` relative to `h` to allow scaling of simulations @@ -73,7 +73,7 @@ Note: This truncation makes this Kernel not conservative, which is beneficial in regards to stability but makes it less accurate. """ -struct GaussianKernel{NDIMS} <: SmoothingKernel{NDIMS} end +struct GaussianKernel{NDIMS} <: AbstractSmoothingKernel{NDIMS} end @inline @fastmath function kernel(kernel::GaussianKernel, r::Real, h) q = r / h @@ -133,7 +133,7 @@ where ``\delta`` is the typical particle spacing. For general information and usage see [Smoothing Kernels](@ref smoothing_kernel). """ -struct SchoenbergCubicSplineKernel{NDIMS} <: SmoothingKernel{NDIMS} end +struct SchoenbergCubicSplineKernel{NDIMS} <: AbstractSmoothingKernel{NDIMS} end @muladd @inline function kernel(kernel::SchoenbergCubicSplineKernel, r::Real, h) q = r / h @@ -207,7 +207,7 @@ where ``\delta`` is the typical particle spacing. For general information and usage see [Smoothing Kernels](@ref smoothing_kernel). """ -struct SchoenbergQuarticSplineKernel{NDIMS} <: SmoothingKernel{NDIMS} end +struct SchoenbergQuarticSplineKernel{NDIMS} <: AbstractSmoothingKernel{NDIMS} end # Note that `floating_point_number^integer_literal` is lowered to `Base.literal_pow`. # Currently, specializations reducing this to simple multiplications exist only up @@ -297,7 +297,7 @@ where ``\delta`` is the typical particle spacing. For general information and usage see [Smoothing Kernels](@ref smoothing_kernel). """ -struct SchoenbergQuinticSplineKernel{NDIMS} <: SmoothingKernel{NDIMS} end +struct SchoenbergQuinticSplineKernel{NDIMS} <: AbstractSmoothingKernel{NDIMS} end @fastpow @muladd @inline function kernel(kernel::SchoenbergQuinticSplineKernel, r::Real, h) q = r / h @@ -343,10 +343,10 @@ end @inline normalization_factor(::SchoenbergQuinticSplineKernel{2}, h) = 7 / (pi * h^2 * 478) @inline normalization_factor(::SchoenbergQuinticSplineKernel{3}, h) = 1 / (pi * h^3 * 120) -abstract type WendlandKernel{NDIMS} <: SmoothingKernel{NDIMS} end +abstract type AbstractWendlandKernel{NDIMS} <: AbstractSmoothingKernel{NDIMS} end # Compact support for all Wendland kernels -@inline compact_support(::WendlandKernel, h) = 2h +@inline compact_support(::AbstractWendlandKernel, h) = 2h @doc raw""" WendlandC2Kernel{NDIMS}() @@ -380,7 +380,7 @@ where ``\delta`` is the typical particle spacing. For general information and usage see [Smoothing Kernels](@ref smoothing_kernel). """ -struct WendlandC2Kernel{NDIMS} <: WendlandKernel{NDIMS} end +struct WendlandC2Kernel{NDIMS} <: AbstractWendlandKernel{NDIMS} end @fastpow @inline function kernel(kernel::WendlandC2Kernel, r::Real, h) q = r / h @@ -447,7 +447,7 @@ where ``\delta`` is the typical particle spacing. For general information and usage see [Smoothing Kernels](@ref smoothing_kernel). """ -struct WendlandC4Kernel{NDIMS} <: WendlandKernel{NDIMS} end +struct WendlandC4Kernel{NDIMS} <: AbstractWendlandKernel{NDIMS} end @fastpow @inline function kernel(kernel::WendlandC4Kernel, r::Real, h) q = r / h @@ -511,7 +511,7 @@ where ``\delta`` is the typical particle spacing. For general information and usage see [Smoothing Kernels](@ref smoothing_kernel). """ -struct WendlandC6Kernel{NDIMS} <: WendlandKernel{NDIMS} end +struct WendlandC6Kernel{NDIMS} <: AbstractWendlandKernel{NDIMS} end @fastpow @inline function kernel(kernel::WendlandC6Kernel, r::Real, h) q = r / h @@ -577,7 +577,7 @@ where ``\delta`` is the typical particle spacing. For general information and usage see [Smoothing Kernels](@ref smoothing_kernel). """ -struct Poly6Kernel{NDIMS} <: SmoothingKernel{NDIMS} end +struct Poly6Kernel{NDIMS} <: AbstractSmoothingKernel{NDIMS} end @inline function kernel(kernel::Poly6Kernel, r::Real, h) q = r / h @@ -642,7 +642,7 @@ where ``\delta`` is the typical particle spacing. For general information and usage see [Smoothing Kernels](@ref smoothing_kernel). """ -struct SpikyKernel{NDIMS} <: SmoothingKernel{NDIMS} end +struct SpikyKernel{NDIMS} <: AbstractSmoothingKernel{NDIMS} end @inline function kernel(kernel::SpikyKernel, r::Real, h) q = r / h diff --git a/src/io/io.jl b/src/io/io.jl index 380151bcff..ed245a4c7e 100644 --- a/src/io/io.jl +++ b/src/io/io.jl @@ -1,2 +1,315 @@ include("write_vtk.jl") include("read_vtk.jl") + +# Handle "_" on optional prefix strings +add_underscore_to_optional_prefix(str) = (str === "" ? "" : "$(str)_") +# Same for optional postfix strings +add_underscore_to_optional_postfix(str) = (str === "" ? "" : "_$(str)") + +function write_meta_data(callback::SolutionSavingCallback, integrator) + prefix = callback.prefix + + meta_data = create_meta_data_dict(callback, integrator) + + # write JSON file + output_directory = callback.output_directory + mkpath(output_directory) + json_file = joinpath(output_directory, + "meta" * add_underscore_to_optional_postfix(prefix) * ".json") + + open(json_file, "w") do file + JSON.print(file, meta_data, 2) + end +end + +function create_meta_data_dict(callback, integrator) + git_hash = callback.git_hash + prefix = hasproperty(callback, :prefix) ? callback.prefix : "" + semi = integrator.p + names = system_names(semi.systems) + + meta_data = Dict{String, Any}() + + info = Dict{String, Any}() + add_simulation_info!(info, git_hash, integrator) + meta_data["simulation_info"] = info + + systems = Dict{String, Any}() + foreach_system(semi) do system + idx = system_indices(system, semi) + name = add_underscore_to_optional_prefix(prefix) * names[idx] + + system_data = Dict{String, Any}() + add_system_data!(system_data, system) + + systems[name] = system_data + end + meta_data["system_data"] = systems + + return meta_data +end + +function add_simulation_info!(info, git_hash, integrator) + info["solver_name"] = "TrixiParticles.jl" + info["solver_version"] = git_hash[] + info["julia_version"] = string(VERSION) + + info["time_integrator"] = Dict{String, Any}() + info["time_integrator"]["integrator_type"] = type2string(integrator.alg) + info["time_integrator"]["start_time"] = first(integrator.sol.prob.tspan) + info["time_integrator"]["final_time"] = last(integrator.sol.prob.tspan) + info["time_integrator"]["adaptive"] = integrator.opts.adaptive + if integrator.opts.adaptive + info["time_integrator"]["abstol"] = integrator.opts.abstol + info["time_integrator"]["reltol"] = integrator.opts.reltol + info["time_integrator"]["controller"] = type2string(integrator.opts.controller) + else + info["time_integrator"]["dt"] = integrator.dt + info["time_integrator"]["dt_max"] = integrator.opts.dtmax + end + + info["technical_setup"] = Dict{String, Any}() + info["technical_setup"]["parallelization_backend"] = type2string(integrator.p.parallelization_backend) + info["technical_setup"]["number_of_threads"] = Threads.nthreads() +end + +add_system_data!(system_data, data::Nothing) = system_data + +function add_system_data!(system_data, system::AbstractFluidSystem) + system_data["system_type"] = type2string(system) + system_data["particle_spacing"] = particle_spacing(system, 1) + system_data["density_calculator"] = type2string(system.density_calculator) + system_data["smoothing_kernel"] = type2string(system.smoothing_kernel) + system_data["smoothing_length"] = system.cache.smoothing_length + system_data["acceleration"] = system.acceleration + system_data["sound_speed"] = system_sound_speed(system) + system_data["pressure_acceleration_formulation"] = nameof(system.pressure_acceleration_formulation) + add_system_data!(system_data, shifting_technique(system)) + add_system_data!(system_data, system.surface_tension) + add_system_data!(system_data, system.surface_normal_method) + add_system_data!(system_data, system.viscosity) + add_system_data!(system_data, system.correction) + add_system_data!(system_data, system_state_equation(system)) + if hasfield(typeof(system), :density_diffusion) + add_system_data!(system_data, system.density_diffusion) + end + if hasfield(typeof(system), :alpha) + system_data["alpha"] = system.alpha + end +end + +function add_system_data!(system_data, system::TotalLagrangianSPHSystem) + system_data["system_type"] = type2string(system) + system_data["particle_spacing"] = particle_spacing(system, 1) + system_data["smoothing_kernel"] = type2string(system.smoothing_kernel) + system_data["smoothing_length"] = system.smoothing_length + system_data["acceleration"] = system.acceleration + add_system_data!(system_data, system.boundary_model) + add_system_data!(system_data, system.viscosity) + add_system_data!(system_data, system.penalty_force) +end + +function add_system_data!(system_data, system::WallBoundarySystem) + system_data["system_type"] = type2string(system) + system_data["particle_spacing"] = particle_spacing(system, 1) + system_data["adhesion_coefficient"] = system.adhesion_coefficient + add_system_data!(system_data, system.boundary_model) + add_system_data!(system_data, system.prescribed_motion) +end + +function add_system_data!(system_data, system::BoundaryDEMSystem) + system_data["system_type"] = type2string(system) + system_data["particle_spacing"] = particle_spacing(system, 1) + system_data["normal_stiffness"] = system.normal_stiffness +end + +function add_system_data!(system_data, system::DEMSystem) + system_data["system_type"] = type2string(system) + system_data["particle_spacing"] = particle_spacing(system, 1) + system_data["damping_coefficient"] = system.damping_coefficient + system_data["acceleration"] = system.acceleration + add_system_data!(system_data, system.contact_model) +end + +function add_system_data!(system_data, system::OpenBoundarySystem) + system_data["system_type"] = type2string(system) + system_data["fluid_system_index"] = system.fluid_system_index[] + system_data["smoothing_length"] = system.smoothing_length + system_data["number_of_boundary_zones"] = length(system.boundary_zones) + add_system_data!(system_data, system.boundary_model) +end + +function add_system_data!(system_data, system::ParticlePackingSystem) + system_data["system_type"] = type2string(system) + system_data["particle_spacing"] = system.particle_spacing + system_data["smoothing_kernel"] = type2string(system.smoothing_kernel) + system_data["smoothing_length_interpolation"] = system.smoothing_length_interpolation + system_data["background_pressure"] = system.background_pressure + system_data["place_on_shell"] = system.place_on_shell + system_data["shift_length"] = system.shift_length +end + +function add_system_data!(system_data, boundary_model::BoundaryModelDummyParticles) + system_data["boundary_model"] = Dict{String, Any}() + system_data["boundary_model"]["model"] = type2string(boundary_model) + system_data["boundary_model"]["smoothing_kernel"] = type2string(boundary_model.smoothing_kernel) + system_data["boundary_model"]["smoothing_length"] = boundary_model.smoothing_length + system_data["boundary_model"]["density_calculator"] = type2string(boundary_model.density_calculator) + add_system_data!(system_data["boundary_model"], boundary_model.state_equation) + add_system_data!(system_data["boundary_model"], boundary_model.viscosity) + add_system_data!(system_data["boundary_model"], boundary_model.correction) +end + +function add_system_data!(system_data, boundary_model::BoundaryModelMonaghanKajtar) + system_data["boundary_model"] = Dict{String, Any}() + system_data["boundary_model"]["model"] = type2string(boundary_model) + system_data["boundary_model"]["beta"] = boundary_model.beta + system_data["boundary_model"]["K"] = boundary_model.K + add_system_data!(system_data["boundary_model"], boundary_model.viscosity) +end + +function add_system_data!(system_data, boundary_model::BoundaryModelMirroringTafuni) + system_data["boundary_model"] = Dict{String, Any}() + system_data["boundary_model"]["model"] = type2string(boundary_model) + system_data["boundary_model"]["mirror_method"] = type2string(boundary_model.mirror_method) +end + +function add_system_data!(system_data, boundary_model::BoundaryModelCharacteristicsLastiwka) + system_data["boundary_model"] = Dict{String, Any}() + system_data["boundary_model"]["model"] = type2string(boundary_model) + system_data["boundary_model"]["extrapolate_reference_values"] = boundary_model.extrapolate_reference_values +end + +function add_system_data!(system_data, contact_model::HertzContactModel) + system_data["contact_model"] = Dict{String, Any}() + system_data["contact_model"]["model"] = type2string(contact_model) + system_data["contact_model"]["elastic_modulus"] = contact_model.elastic_modulus + system_data["contact_model"]["poissons_ratio"] = contact_model.poissons_ratio +end + +function add_system_data!(system_data, contact_model::LinearContactModel) + system_data["contact_model"] = Dict{String, Any}() + system_data["contact_model"]["model"] = type2string(contact_model) + system_data["contact_model"]["normal_stiffness"] = contact_model.normal_stiffness +end + +function add_system_data!(system_data, state_equation::StateEquationCole) + system_data["state_equation"] = Dict{String, Any}() + system_data["state_equation"]["model"] = type2string(state_equation) + system_data["state_equation"]["reference_density"] = state_equation.reference_density + system_data["state_equation"]["background_pressure"] = state_equation.background_pressure + system_data["state_equation"]["exponent"] = state_equation.exponent +end + +function add_system_data!(system_data, state_equation::StateEquationIdealGas) + system_data["state_equation"] = Dict{String, Any}() + system_data["state_equation"]["model"] = type2string(state_equation) + system_data["state_equation"]["reference_density"] = state_equation.reference_density + system_data["state_equation"]["background_pressure"] = state_equation.background_pressure + system_data["state_equation"]["gamma"] = state_equation.gamma +end + +function add_system_data!(system_data, viscosity::Union{ViscosityAdami, ViscosityMorris}) + system_data["viscosity_model"] = Dict{String, Any}() + system_data["viscosity_model"]["model"] = type2string(viscosity) + system_data["viscosity_model"]["nu"] = viscosity.nu + system_data["viscosity_model"]["epsilon"] = viscosity.epsilon +end + +function add_system_data!(system_data, + viscosity::Union{ViscosityAdamiSGS, ViscosityMorrisSGS}) + system_data["viscosity_model"] = Dict{String, Any}() + system_data["viscosity_model"]["model"] = type2string(viscosity) + system_data["viscosity_model"]["nu"] = viscosity.nu + system_data["viscosity_model"]["C_S"] = viscosity.C_S + system_data["viscosity_model"]["epsilon"] = viscosity.epsilon +end + +function add_system_data!(system_data, viscosity::ArtificialViscosityMonaghan) + system_data["viscosity_model"] = Dict{String, Any}() + system_data["viscosity_model"]["model"] = type2string(viscosity) + system_data["viscosity_model"]["alpha"] = viscosity.alpha + system_data["viscosity_model"]["beta"] = viscosity.beta + system_data["viscosity_model"]["epsilon"] = viscosity.epsilon +end + +function add_system_data!(system_data, + density_diffusion::Union{DensityDiffusionAntuono, + DensityDiffusionMolteniColagrossi}) + system_data["density_diffusion"] = Dict{String, Any}() + system_data["density_diffusion"]["model"] = type2string(density_diffusion) + system_data["density_diffusion"]["delta"] = density_diffusion.delta +end + +function add_system_data!(system_data, density_diffusion::DensityDiffusionFerrari) + system_data["density_diffusion"] = Dict{String, Any}() + system_data["density_diffusion"]["model"] = type2string(density_diffusion) +end + +function add_system_data!(system_data, correction::AkinciFreeSurfaceCorrection) + system_data["correction_method"] = Dict{String, Any}() + system_data["correction_method"]["model"] = type2string(correction) + system_data["correction_method"]["rho0"] = correction.rho0 +end + +function add_system_data!(system_data, + correction::Union{BlendedGradientCorrection, GradientCorrection, + KernelCorrection, MixedKernelGradientCorrection, + ShepardKernelCorrection}) + system_data["correction_method"] = Dict{String, Any}() + system_data["correction_method"]["model"] = type2string(correction) +end + +function add_system_data!(system_data, + surface_tension::Union{CohesionForceAkinci, SurfaceTensionAkinci, + SurfaceTensionMorris, + SurfaceTensionMomentumMorris}) + system_data["surface_tension"] = Dict{String, Any}() + system_data["surface_tension"]["model"] = type2string(surface_tension) + system_data["surface_tension"]["surface_tension_coefficient"] = surface_tension.surface_tension_coefficient +end + +function add_system_data!(system_data, surface_normal_method::ColorfieldSurfaceNormal) + system_data["surface_normal_method"] = Dict{String, Any}() + system_data["surface_normal_method"]["model"] = type2string(surface_normal_method) + system_data["surface_normal_method"]["boundary_contact_threshold"] = surface_normal_method.boundary_contact_threshold + system_data["surface_normal_method"]["ideal_density_threshold"] = surface_normal_method.ideal_density_threshold +end + +function add_system_data!(system_data, boundary_zone::BoundaryZone, indice) + zone_name = "boundary_zone_" * string(indice) + system_data[zone_name] = Dict{String, Any}() + system_data[zone_name]["spanning_set"] = boundary_zone.spanning_set + system_data[zone_name]["zone_origin"] = boundary_zone.zone_origin + system_data[zone_name]["zone_width"] = boundary_zone.zone_width + system_data[zone_name]["flow_direction"] = boundary_zone.flow_direction + system_data[zone_name]["face_normal"] = boundary_zone.face_normal + system_data[zone_name]["reference_values"] = boundary_zone.reference_values + system_data[zone_name]["average_inflow_velocity"] = boundary_zone.average_inflow_velocity + system_data[zone_name]["prescribed_density"] = boundary_zone.prescribed_density + system_data[zone_name]["prescribed_pressure"] = boundary_zone.prescribed_pressure + system_data[zone_name]["prescribed_velocity"] = boundary_zone.prescribed_velocity +end + +function add_system_data!(system_data, motion::PrescribedMotion) + system_data["prescribed_motion"] = Dict{String, Any}() + system_data["prescribed_motion"]["model"] = type2string(motion) + system_data["prescribed_motion"]["movement_function"] = type2string(motion.movement_function) +end + +function add_system_data!(system_data, penalty_force::PenaltyForceGanzenmueller) + system_data["penalty_force"] = Dict{String, Any}() + system_data["penalty_force"]["model"] = type2string(penalty_force) + system_data["penalty_force"]["alpha"] = penalty_force.alpha +end + +function add_system_data!(system_data, shifting_technique::TransportVelocityAdami) + system_data["shifting_technique"] = Dict{String, Any}() + system_data["shifting_technique"]["model"] = type2string(shifting_technique) + system_data["shifting_technique"]["background_pressure"] = shifting_technique.background_pressure +end + +function add_system_data!(system_data, shifting_technique::ParticleShiftingTechnique) + system_data["shifting_technique"] = Dict{String, Any}() + system_data["shifting_technique"]["model"] = type2string(shifting_technique) +end diff --git a/src/io/write_vtk.jl b/src/io/write_vtk.jl index b93e25f324..f1baa51671 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -10,7 +10,7 @@ end """ trixi2vtk(vu_ode, semi, t; iter=nothing, output_directory="out", prefix="", - write_meta_data=true, max_coordinates=Inf, custom_quantities...) + max_coordinates=Inf, custom_quantities...) Convert Trixi simulation data to VTK format. @@ -25,7 +25,6 @@ Convert Trixi simulation data to VTK format. separate files. This number is just appended to the filename. - `output_directory="out"`: Output directory path. - `prefix=""`: Prefix for output files. -- `write_meta_data=true`: Write meta data. - `max_coordinates=Inf` The coordinates of particles will be clipped if their absolute values exceed this threshold. - `custom_quantities...`: Additional custom quantities to include in the VTK output. @@ -48,15 +47,26 @@ 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, git_hash=compute_git_hash(), - max_coordinates=Inf, custom_quantities...) +function trixi2vtk(vu_ode, semi, t; iter=nothing, output_directory="out", + prefix="", git_hash=compute_git_hash(), max_coordinates=Inf, + custom_quantities...) + + # The first argument is not necessary in most cases. Since it is usually not available to the user, + # this API wrapper makes it optional. + # Note that custom quantities using the fluid acceleration will not work and return NaN acceleration. + return trixi2vtk(fill!(similar(vu_ode), NaN), vu_ode, semi, t; iter, output_directory, + prefix, git_hash, max_coordinates, custom_quantities...) +end + +function trixi2vtk(dvdu_ode, vu_ode, semi, t; iter=nothing, output_directory="out", + prefix="", 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. @trixi_timeit timer() "update systems" begin + v_ode, u_ode = vu_ode.x # Don't create sub-timers here to avoid cluttering the timer output @notimeit timer() update_systems_and_nhs(v_ode, u_ode, semi, t) end @@ -67,17 +77,17 @@ function trixi2vtk(vu_ode, semi, t; iter=nothing, output_directory="out", prefix system_index = system_indices(system, semi) periodic_box = get_neighborhood_search(system, semi).periodic_box - trixi2vtk(system, v_ode, u_ode, semi, t, periodic_box; + trixi2vtk(system, dvdu_ode, vu_ode, semi, t, periodic_box; system_name=filenames[system_index], output_directory, iter, prefix, - write_meta_data, git_hash, max_coordinates, custom_quantities...) + git_hash, max_coordinates, custom_quantities...) end end # Convert data for a single TrixiParticle system to VTK format -function trixi2vtk(system_, v_ode_, u_ode_, semi_, t, periodic_box; output_directory="out", - prefix="", iter=nothing, system_name=vtkname(system_), - write_meta_data=true, max_coordinates=Inf, git_hash=compute_git_hash(), - custom_quantities...) +function trixi2vtk(system_, dvdu_ode_, vu_ode_, semi_, t, periodic_box; + output_directory="out", prefix="", iter=nothing, + system_name=vtkname(system_), max_coordinates=Inf, + git_hash=compute_git_hash(), custom_quantities...) mkpath(output_directory) # Skip empty systems @@ -85,22 +95,20 @@ function trixi2vtk(system_, v_ode_, u_ode_, semi_, t, periodic_box; output_direc return end + v_ode_, u_ode_ = vu_ode_.x + # Transfer to CPU if data is on the GPU. Do nothing if already on CPU. v_ode, u_ode, system, semi = transfer2cpu(v_ode_, u_ode_, system_, semi_) v = wrap_v(v_ode, system, semi) u = wrap_u(u_ode, system, semi) - # handle "_" on optional pre/postfix strings - add_opt_str_pre(str) = (str === "" ? "" : "$(str)_") - add_opt_str_post(str) = (str === nothing ? "" : "_$(str)") - file = joinpath(output_directory, - add_opt_str_pre(prefix) * "$system_name" - * add_opt_str_post(iter)) + add_underscore_to_optional_prefix(prefix) * "$system_name" + * add_underscore_to_optional_postfix(iter)) collection_file = joinpath(output_directory, - add_opt_str_pre(prefix) * "$system_name") + add_underscore_to_optional_prefix(prefix) * "$system_name") # Reset the collection when the iteration is 0 pvd = paraview_collection(collection_file; append=iter > 0) @@ -119,27 +127,28 @@ function trixi2vtk(system_, v_ode_, u_ode_, semi_, t, periodic_box; output_direc end @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) + # Dispatches based on the different system types e.g. AbstractFluidSystem + write2vtk!(vtk, v, u, t, system) # Store particle index - vtk["index"] = active_particles(system) + vtk["index"] = eachparticle(system) vtk["time"] = t vtk["ndims"] = ndims(system) vtk["particle_spacing"] = [particle_spacing(system, particle) - for particle in active_particles(system)] - - if write_meta_data - vtk["solver_version"] = git_hash - vtk["julia_version"] = string(VERSION) - end + for particle in each_active_particle(system)] # Extract custom quantities for this system - for (key, quantity) in custom_quantities - value = custom_quantity(quantity, system, v_ode, u_ode, semi, t) - if value !== nothing - vtk[string(key)] = value + if !isempty(custom_quantities) + dv_ode_, du_ode_ = dvdu_ode_.x + dv_ode, du_ode = transfer2cpu(dv_ode_, du_ode_) + + for (key, quantity) in custom_quantities + value = custom_quantity(quantity, system, dv_ode, du_ode, v_ode, u_ode, + semi, t) + if value !== nothing + vtk[string(key)] = value + end end end @@ -150,12 +159,12 @@ function trixi2vtk(system_, v_ode_, u_ode_, semi_, t, periodic_box; output_direc end function transfer2cpu(v_::AbstractGPUArray, u_, system_, semi_) - v = Adapt.adapt(Array, v_) - u = Adapt.adapt(Array, u_) semi = Adapt.adapt(Array, semi_) system_index = system_indices(system_, semi_) system = semi.systems[system_index] + v, u = transfer2cpu(v_, u_) + return v, u, system, semi end @@ -163,20 +172,40 @@ function transfer2cpu(v_, u_, system_, semi_) return v_, u_, system_, semi_ end -function custom_quantity(quantity::AbstractArray, system, v_ode, u_ode, semi, t) +function transfer2cpu(v_::AbstractGPUArray, u_) + v = transfer2cpu(v_) + u = transfer2cpu(u_) + + return v, u +end + +function transfer2cpu(v_, u_) + return v_, u_ +end + +function transfer2cpu(a_::AbstractGPUArray) + return Adapt.adapt(Array, a_) +end + +function transfer2cpu(a_) + return a_ +end + +function custom_quantity(quantity::AbstractArray, system, dv_ode, du_ode, v_ode, u_ode, + semi, t) return quantity end -function custom_quantity(quantity, system, v_ode, u_ode, semi, t) +function custom_quantity(quantity, system, dv_ode, du_ode, v_ode, u_ode, semi, t) # Check if `quantity` is a function of `system`, `v_ode`, `u_ode`, `semi` and `t` if !isempty(methods(quantity, - (typeof(system), typeof(v_ode), typeof(u_ode), - typeof(semi), typeof(t)))) - return quantity(system, v_ode, u_ode, semi, t) + (typeof(system), typeof(dv_ode), typeof(du_ode), typeof(v_ode), + typeof(u_ode), typeof(semi), typeof(t)))) + return quantity(system, dv_ode, du_ode, v_ode, u_ode, semi, t) end # Assume `quantity` is a function of `data` - data = system_data(system, v_ode, u_ode, semi) + data = system_data(system, dv_ode, du_ode, v_ode, u_ode, semi) return quantity(system, data, t) end @@ -253,29 +282,29 @@ function trixi2vtk(initial_condition::InitialCondition; output_directory="out", pressure=pressure, custom_quantities...) end -function write2vtk!(vtk, v, u, t, system; write_meta_data=true) +function write2vtk!(vtk, v, u, t, system) vtk["velocity"] = view(v, 1:ndims(system), :) return vtk end -function write2vtk!(vtk, v, u, t, system::DEMSystem; write_meta_data=true) +function write2vtk!(vtk, v, u, t, system::DEMSystem) vtk["velocity"] = view(v, 1:ndims(system), :) vtk["mass"] = [hydrodynamic_mass(system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] vtk["radius"] = [particle_radius(system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] return vtk end -function write2vtk!(vtk, v, u, t, system::FluidSystem; write_meta_data=true) +function write2vtk!(vtk, v, u, t, system::AbstractFluidSystem) vtk["velocity"] = [current_velocity(v, system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] vtk["density"] = [current_density(v, system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] # Indexing the pressure is a workaround for slicing issue (see https://github.com/JuliaSIMD/StrideArrays.jl/issues/88) vtk["pressure"] = [current_pressure(v, system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] if system.surface_normal_method !== nothing vtk["surf_normal"] = [surface_normal(system, particle) @@ -286,7 +315,7 @@ function write2vtk!(vtk, v, u, t, system::FluidSystem; write_meta_data=true) if system.surface_tension isa SurfaceTensionMorris || system.surface_tension isa SurfaceTensionMomentumMorris - surface_tension = zeros((ndims(system), n_moving_particles(system))) + surface_tension = zeros((ndims(system), n_integrated_particles(system))) system_coords = current_coordinates(u, system) surface_tension_a = surface_tension_model(system) @@ -302,9 +331,14 @@ function write2vtk!(vtk, v, u, t, system::FluidSystem; write_meta_data=true) surface_tension[1:ndims(system), particle] .+= surface_tension_force(surface_tension_a, surface_tension_b, - system, system, particle, - neighbor, pos_diff, - distance, rho_a, rho_b, + system, + system, + particle, + neighbor, + pos_diff, + distance, + rho_a, + rho_b, grad_kernel) end vtk["surface_tension"] = surface_tension @@ -317,44 +351,6 @@ function write2vtk!(vtk, v, u, t, system::FluidSystem; write_meta_data=true) end end - if write_meta_data - vtk["acceleration"] = system.acceleration - vtk["viscosity"] = type2string(system.viscosity) - write2vtk!(vtk, system.viscosity) - vtk["smoothing_kernel"] = type2string(system.smoothing_kernel) - vtk["smoothing_length_factor"] = system.cache.smoothing_length_factor - vtk["density_calculator"] = type2string(system.density_calculator) - - if system isa WeaklyCompressibleSPHSystem - vtk["solver"] = "WCSPH" - - vtk["correction_method"] = type2string(system.correction) - if system.correction isa AkinciFreeSurfaceCorrection - vtk["correction_rho0"] = system.correction.rho0 - end - - if system.state_equation isa StateEquationCole - vtk["state_equation_exponent"] = system.state_equation.exponent - end - - if system.state_equation isa StateEquationIdealGas - vtk["state_equation_gamma"] = system.state_equation.gamma - end - - vtk["state_equation"] = type2string(system.state_equation) - vtk["state_equation_rho0"] = system.state_equation.reference_density - vtk["state_equation_pa"] = system.state_equation.background_pressure - vtk["state_equation_c"] = system.state_equation.sound_speed - vtk["solver"] = "WCSPH" - else - vtk["solver"] = "EDAC" - vtk["sound_speed"] = system.sound_speed - vtk["background_pressure_TVF"] = system.transport_velocity isa Nothing ? - "-" : - system.transport_velocity.background_pressure - end - end - return vtk end @@ -373,11 +369,9 @@ function write2vtk!(vtk, viscosity::ArtificialViscosityMonaghan) vtk["viscosity_epsilon"] = viscosity.epsilon end -function write2vtk!(vtk, v, u, t, system::TotalLagrangianSPHSystem; write_meta_data=true) - n_fixed_particles = nparticles(system) - n_moving_particles(system) - +function write2vtk!(vtk, v, u, t, system::TotalLagrangianSPHSystem) vtk["velocity"] = [current_velocity(v, system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] vtk["jacobian"] = [det(deformation_gradient(system, particle)) for particle in eachparticle(system)] @@ -387,6 +381,11 @@ function write2vtk!(vtk, v, u, t, system::TotalLagrangianSPHSystem; write_meta_d initial_coords(system, particle) for particle in eachparticle(system)] + vtk["lame_lambda"] = system.lame_lambda + vtk["lame_mu"] = system.lame_mu + vtk["young_modulus"] = system.young_modulus + vtk["poisson_ratio"] = system.poisson_ratio + sigma = cauchy_stress(system) vtk["sigma_11"] = sigma[1, 1, :] vtk["sigma_22"] = sigma[2, 2, :] @@ -396,64 +395,33 @@ function write2vtk!(vtk, v, u, t, system::TotalLagrangianSPHSystem; write_meta_d vtk["material_density"] = system.material_density - if write_meta_data - vtk["lame_lambda"] = system.lame_lambda - vtk["lame_mu"] = system.lame_mu - vtk["smoothing_kernel"] = type2string(system.smoothing_kernel) - vtk["smoothing_length_factor"] = initial_smoothing_length(system) / - particle_spacing(system, 1) - end - - write2vtk!(vtk, v, u, t, system.boundary_model, system, write_meta_data=write_meta_data) + write2vtk!(vtk, v, u, t, system.boundary_model, system) end -function write2vtk!(vtk, v, u, t, system::OpenBoundarySPHSystem; write_meta_data=true) +function write2vtk!(vtk, v, u, t, system::OpenBoundarySystem) vtk["velocity"] = [current_velocity(v, system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] vtk["density"] = [current_density(v, system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] vtk["pressure"] = [current_pressure(v, system, particle) - for particle in active_particles(system)] - - if write_meta_data - vtk["boundary_zone"] = type2string(first(typeof(system.boundary_zone).parameters)) - vtk["width"] = round(system.boundary_zone.zone_width, digits=3) - vtk["velocity_function"] = type2string(system.reference_velocity) - vtk["pressure_function"] = type2string(system.reference_pressure) - vtk["density_function"] = type2string(system.reference_density) - end + for particle in eachparticle(system)] 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) +function write2vtk!(vtk, v, u, t, system::WallBoundarySystem) + write2vtk!(vtk, v, u, t, system.boundary_model, system) end -function write2vtk!(vtk, v, u, t, model::Nothing, system; write_meta_data=true) +function write2vtk!(vtk, v, u, t, model::Nothing, system) return vtk end -function write2vtk!(vtk, v, u, t, model::BoundaryModelMonaghanKajtar, system; - write_meta_data=true) - if write_meta_data - vtk["boundary_model"] = "BoundaryModelMonaghanKajtar" - vtk["boundary_spacing_ratio"] = model.beta - vtk["boundary_K"] = model.K - end +function write2vtk!(vtk, v, u, t, model::BoundaryModelMonaghanKajtar, system) + return vtk end -function write2vtk!(vtk, v, u, t, model::BoundaryModelDummyParticles, system; - write_meta_data=true) - if write_meta_data - vtk["boundary_model"] = "BoundaryModelDummyParticles" - vtk["smoothing_kernel"] = type2string(model.smoothing_kernel) - vtk["smoothing_length"] = model.smoothing_length - vtk["density_calculator"] = type2string(model.density_calculator) - vtk["state_equation"] = type2string(model.state_equation) - vtk["viscosity_model"] = type2string(model.viscosity) - end - +function write2vtk!(vtk, v, u, t, model::BoundaryModelDummyParticles, system) vtk["hydrodynamic_density"] = current_density(v, system) vtk["pressure"] = model.pressure @@ -468,6 +436,6 @@ function write2vtk!(vtk, v, u, t, model::BoundaryModelDummyParticles, system; end end -function write2vtk!(vtk, v, u, t, system::BoundaryDEMSystem; write_meta_data=true) +function write2vtk!(vtk, v, u, t, system::BoundaryDEMSystem) return vtk end diff --git a/src/preprocessing/geometries/geometries.jl b/src/preprocessing/geometries/geometries.jl index f18e344186..3a7b633522 100644 --- a/src/preprocessing/geometries/geometries.jl +++ b/src/preprocessing/geometries/geometries.jl @@ -55,3 +55,73 @@ function Base.intersect(initial_condition::InitialCondition, return intersect(result, Base.tail(geometries)...) end + +""" + planar_geometry_to_face(planar_geometry::TriangleMesh) + +Extracts a simplified rectangular face and its normal vector from an arbitrary planar geometry +(`TriangleMesh` loaded via [`load_geometry`](@ref)) +for use as a boundary zone interface in [`BoundaryZone`](@ref). +This function computes the corner points of an oriented bounding box +that best represents the essential orientation and extent of the input geometry. +The geometry must be planar (all vertices should lie in the same plane), +but can have complex or non-rectangular boundaries. + +!!! note "Face Normal Orientation" + All face normals of the input geometry must point inside the fluid domain. + The returned plane normal is computed by averaging all face normals, so consistent orientation is required. + +# Arguments +- `planar_geometry`: A planar geometry (`TriangleMesh` loaded via [`load_geometry`](@ref)). + +# Returns +- `face_vertices`: Tuple of three vertices defining the rectangular face (corner points of the oriented bounding box). +- `face_normal`: Normalized normal vector of the face. + +# Example +```jldoctest; output=false, filter=r"face = .*" +file = pkgdir(TrixiParticles, "test", "preprocessing", "data") +planar_geometry = load_geometry(joinpath(file, "inflow_geometry.stl")) + +face, face_normal = planar_geometry_to_face(planar_geometry) + +# output +(face = ...) +``` +""" +function planar_geometry_to_face(planar_geometry::TriangleMesh) + face_normal = normalize(sum(planar_geometry.face_normals) / nfaces(planar_geometry)) + + face_vertices = oriented_bounding_box(stack(planar_geometry.vertices)) + + # Vectors spanning the face + edge1 = face_vertices[:, 2] - face_vertices[:, 1] + edge2 = face_vertices[:, 3] - face_vertices[:, 1] + + if abs(dot(edge1, face_normal)) > 1e-2 || abs(dot(edge2, face_normal)) > 1e-2 + throw(ArgumentError("geometry is not planar")) + end + + return (; face=(face_vertices[:, 1], face_vertices[:, 2], face_vertices[:, 3]), + face_normal=face_normal) +end + +# According to: +# https://logicatcore.github.io/scratchpad/lidar/sensor-fusion/jupyter/2021/04/20/3D-Oriented-Bounding-Box.html +function oriented_bounding_box(point_cloud) + covariance_matrix = Statistics.cov(point_cloud; dims=2) + eigen_vectors = Statistics.eigvecs(covariance_matrix) + means = Statistics.mean(point_cloud, dims=2) + + centered_data = point_cloud .- means + + aligned_coords = eigen_vectors' * centered_data + + min_corner = minimum(aligned_coords, dims=2) + max_corner = maximum(aligned_coords, dims=2) + + face_vertices = hcat(min_corner, max_corner, + [min_corner[1], max_corner[2], min_corner[3]]) + + return eigen_vectors * face_vertices .+ means +end diff --git a/src/preprocessing/particle_packing/signed_distance.jl b/src/preprocessing/particle_packing/signed_distance.jl index 7e957e124d..082deced5a 100644 --- a/src/preprocessing/particle_packing/signed_distance.jl +++ b/src/preprocessing/particle_packing/signed_distance.jl @@ -59,7 +59,7 @@ function SignedDistanceField(geometry, particle_spacing; particle_spacing)) grid = rectangular_shape_coords(particle_spacing, n_particles_per_dimension, - min_corner; tlsph=true) + min_corner; place_on_shell=true) points = reinterpret(reshape, SVector{NDIMS, ELTYPE}, grid) end diff --git a/src/preprocessing/particle_packing/system.jl b/src/preprocessing/particle_packing/system.jl index f0772589ff..70aedd7dcb 100644 --- a/src/preprocessing/particle_packing/system.jl +++ b/src/preprocessing/particle_packing/system.jl @@ -6,7 +6,7 @@ smoothing_length_interpolation=smoothing_length, is_boundary=false, boundary_compress_factor=1, neighborhood_search=GridNeighborhoodSearch{ndims(shape)}(), - background_pressure, tlsph=false, fixed_system=false) + background_pressure, place_on_shell=false, fixed_system=false) System to generate body-fitted particles for complex shapes. For more information on the methods, see [particle packing](@ref particle_packing). @@ -18,10 +18,11 @@ For more information on the methods, see [particle packing](@ref particle_packin - `background_pressure`: Constant background pressure to physically pack the particles. A large `background_pressure` can cause high accelerations which requires a properly adjusted time step. -- `tlsph`: With the [`TotalLagrangianSPHSystem`](@ref), particles need to be placed - on the boundary of the shape and not half a particle spacing away, - as for fluids. When `tlsph=true`, particles will be placed - on the boundary of the shape. +- `place_on_shell`: If `place_on_shell=true`, particles will be placed + on the shell of the geometry. For example, + the [`TotalLagrangianSPHSystem`](@ref) requires particles to be placed + on the shell of the geometry and not half a particle spacing away, + as for fluids. - `is_boundary`: When `shape` is inside the geometry that was used to create `signed_distance_field`, set `is_boundary=false`. Otherwise (`shape` is the sampled boundary), set `is_boundary=true`. @@ -55,7 +56,7 @@ For more information on the methods, see [particle packing](@ref particle_packin Recommended values are `0.8` or `0.9`. """ struct ParticlePackingSystem{S, F, NDIMS, ELTYPE <: Real, PR, C, AV, - IC, M, D, K, N, SD} <: FluidSystem{NDIMS} + IC, M, D, K, N, SD} <: AbstractFluidSystem{NDIMS} initial_condition :: IC advection_velocity :: AV mass :: M @@ -64,7 +65,7 @@ struct ParticlePackingSystem{S, F, NDIMS, ELTYPE <: Real, PR, C, AV, smoothing_kernel :: K smoothing_length_interpolation :: ELTYPE background_pressure :: ELTYPE - tlsph :: Bool + place_on_shell :: Bool signed_distance_field :: S is_boundary :: Bool shift_length :: ELTYPE @@ -78,7 +79,8 @@ struct ParticlePackingSystem{S, F, NDIMS, ELTYPE <: Real, PR, C, AV, # See the comments in general/gpu.jl for more details. function ParticlePackingSystem(initial_condition, mass, density, particle_spacing, smoothing_kernel, smoothing_length_interpolation, - background_pressure, tlsph, signed_distance_field, + background_pressure, place_on_shell, + signed_distance_field, is_boundary, shift_length, neighborhood_search, signed_distances, particle_refinement, buffer, fixed_system, cache, advection_velocity) @@ -90,7 +92,7 @@ struct ParticlePackingSystem{S, F, NDIMS, ELTYPE <: Real, PR, C, AV, mass, density, particle_spacing, smoothing_kernel, smoothing_length_interpolation, - background_pressure, tlsph, + background_pressure, place_on_shell, signed_distance_field, is_boundary, shift_length, neighborhood_search, signed_distances, particle_refinement, @@ -105,7 +107,8 @@ function ParticlePackingSystem(shape::InitialCondition; smoothing_length_interpolation=smoothing_length, is_boundary=false, boundary_compress_factor=1, neighborhood_search=GridNeighborhoodSearch{ndims(shape)}(), - background_pressure, tlsph=false, fixed_system=false) + background_pressure, place_on_shell=false, + fixed_system=false) NDIMS = ndims(shape) ELTYPE = eltype(shape) mass = copy(shape.mass) @@ -144,12 +147,12 @@ function ParticlePackingSystem(shape::InitialCondition; # Its value is negative if the particle is inside the geometry. # Otherwise (if outside), the value is positive. if is_boundary - offset = tlsph ? shape.particle_spacing : shape.particle_spacing / 2 + offset = place_on_shell ? shape.particle_spacing : shape.particle_spacing / 2 shift_length = -boundary_compress_factor * signed_distance_field.max_signed_distance - offset else - shift_length = tlsph ? zero(ELTYPE) : shape.particle_spacing / 2 + shift_length = place_on_shell ? zero(ELTYPE) : shape.particle_spacing / 2 end cache = (; create_cache_refinement(shape, particle_refinement, smoothing_length)...) @@ -158,7 +161,7 @@ function ParticlePackingSystem(shape::InitialCondition; return ParticlePackingSystem(shape, mass, density, shape.particle_spacing, smoothing_kernel, smoothing_length_interpolation, - background_pressure, tlsph, signed_distance_field, + background_pressure, place_on_shell, signed_distance_field, is_boundary, shift_length, nhs, fill(zero(ELTYPE), nparticles(shape)), particle_refinement, nothing, fixed_system, cache, advection_velocity) @@ -183,7 +186,7 @@ function Base.show(io::IO, ::MIME"text/plain", system::ParticlePackingSystem) system.neighborhood_search |> typeof |> nameof) summary_line(io, "#particles", nparticles(system)) summary_line(io, "smoothing kernel", system.smoothing_kernel |> typeof |> nameof) - summary_line(io, "tlsph", system.tlsph ? "yes" : "no") + summary_line(io, "place_on_shell", system.place_on_shell ? "yes" : "no") summary_line(io, "boundary", system.is_boundary ? "yes" : "no") summary_footer(io) end @@ -213,12 +216,9 @@ end @inline requires_update_callback(system::ParticlePackingSystem) = true -function write2vtk!(vtk, v, u, t, system::ParticlePackingSystem; write_meta_data=true) +function write2vtk!(vtk, v, u, t, system::ParticlePackingSystem) vtk["velocity"] = [advection_velocity(v, system, particle) - for particle in active_particles(system)] - if write_meta_data - vtk["signed_distances"] = system.signed_distances - end + for particle in eachparticle(system)] end # Skip for fixed systems @@ -251,8 +251,8 @@ function kinetic_energy(system::ParticlePackingSystem, v_ode, u_ode, semi, t) # Exclude boundary packing system is_boundary && return zero(eltype(system)) - # If `each_moving_particle` is empty (no moving particles), return zero - return sum(each_moving_particle(system), init=zero(eltype(system))) do particle + # If `each_integrated_particle` is empty (no integrated particles), return zero + return sum(each_integrated_particle(system), init=zero(eltype(system))) do particle velocity = advection_velocity(v, system, particle) return initial_condition.mass[particle] * dot(velocity, velocity) / 2 end @@ -330,8 +330,8 @@ function constrain_particle!(u, system, particle, distance_signed, normal_vector (; shift_length) = system # For fluid particles: - # - `tlsph = true`: `shift_length = 0` - # - `tlsph = false`: `shift_length = particle_spacing / 2` + # - `place_on_shell = true`: `shift_length = 0` + # - `place_on_shell = false`: `shift_length = particle_spacing / 2` # For boundary particles: # `shift_length` is the thickness of the boundary. if distance_signed >= -shift_length @@ -346,7 +346,7 @@ function constrain_particle!(u, system, particle, distance_signed, normal_vector system.is_boundary || return u particle_spacing = system.initial_condition.particle_spacing - shift_length_inner = system.tlsph ? particle_spacing : particle_spacing / 2 + shift_length_inner = system.place_on_shell ? particle_spacing : particle_spacing / 2 if distance_signed < shift_length_inner shift = (distance_signed - shift_length_inner) * normal_vector @@ -366,7 +366,7 @@ end # Update from `UpdateCallback` (between time steps) @inline function update_transport_velocity!(system::ParticlePackingSystem, v_ode, semi) v = wrap_v(v_ode, system, semi) - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) for i in 1:ndims(system) system.advection_velocity[i, particle] = v[i, particle] diff --git a/src/schemes/boundary/boundary.jl b/src/schemes/boundary/boundary.jl index a09545a9b7..343c0d022a 100644 --- a/src/schemes/boundary/boundary.jl +++ b/src/schemes/boundary/boundary.jl @@ -1,10 +1,4 @@ -include("dummy_particles/dummy_particles.jl") -include("system.jl") -include("open_boundary/boundary_zones.jl") -include("open_boundary/mirroring.jl") -include("open_boundary/method_of_characteristics.jl") -include("open_boundary/system.jl") -# Monaghan-Kajtar repulsive boundary particles require the `BoundarySPHSystem` -# and the `TotalLagrangianSPHSystem` and are therefore included later. - -@inline Base.ndims(boundary_model::BoundaryModelDummyParticles) = ndims(boundary_model.smoothing_kernel) +include("prescribed_motion.jl") +include("wall_boundary/wall_boundary.jl") +include("open_boundary/open_boundary.jl") +include("dem_boundary/system.jl") diff --git a/src/schemes/boundary/dem_boundary/system.jl b/src/schemes/boundary/dem_boundary/system.jl new file mode 100644 index 0000000000..d288a081cf --- /dev/null +++ b/src/schemes/boundary/dem_boundary/system.jl @@ -0,0 +1,92 @@ +""" + 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, IC, + ARRAY1D, ARRAY2D} <: AbstractBoundarySystem{NDIMS} + initial_condition :: IC + coordinates :: ARRAY2D # [dimension, particle] + radius :: ARRAY1D # [particle] + normal_stiffness :: ELTYPE + buffer :: Nothing + + 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(initial_condition), typeof(radius), + typeof(coordinates)}(initial_condition, coordinates, radius, + normal_stiffness, nothing) + end +end + +@inline function Base.eltype(system::BoundaryDEMSystem) + eltype(system.coordinates) +end + +@inline function nparticles(system::BoundaryDEMSystem) + size(system.coordinates, 2) +end + +# No particle positions are advanced for DEM boundary systems +@inline function n_integrated_particles(system::BoundaryDEMSystem) + return 0 +end + +@inline v_nvariables(system::BoundaryDEMSystem) = 0 +@inline u_nvariables(system::BoundaryDEMSystem) = 0 + +@inline initial_coordinates(system::BoundaryDEMSystem) = system.coordinates + +@inline function current_coordinates(u, system::BoundaryDEMSystem) + return system.coordinates +end + +@inline function current_velocity(v, system::BoundaryDEMSystem, particle) + return zero(SVector{ndims(system), eltype(system)}) +end + +function write_u0!(u0, ::BoundaryDEMSystem) + return u0 +end + +function write_v0!(v0, ::BoundaryDEMSystem) + return v0 +end + +function system_data(system::BoundaryDEMSystem, dv_ode, du_ode, v_ode, u_ode, semi) + (; coordinates, radius, normal_stiffness) = system + + return (; coordinates, radius, normal_stiffness) +end + +function available_data(::BoundaryDEMSystem) + return (:coordinates, :radius, :normal_stiffness) +end + +function Base.show(io::IO, system::BoundaryDEMSystem) + @nospecialize system # reduce precompilation time + + print(io, "BoundaryDEMSystem{", ndims(system), "}(") + 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 diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 30e589e1c9..30d1f444f3 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -5,26 +5,31 @@ struct InFlow end struct OutFlow end @doc raw""" - BoundaryZone(; plane, plane_normal, density, particle_spacing, + BoundaryZone(; boundary_face, face_normal, density, particle_spacing, initial_condition=nothing, extrude_geometry=nothing, - open_boundary_layers::Integer, boundary_type=BidirectionalFlow(), - average_inflow_velocity=true) - -Boundary zone for [`OpenBoundarySPHSystem`](@ref). - -The specified plane (line in 2D or rectangle in 3D) will be extruded in the direction -opposite to `plane_normal` to create a box for the boundary zone. + open_boundary_layers::Integer, average_inflow_velocity=true, + boundary_type=BidirectionalFlow(), + reference_density=nothing, reference_pressure=nothing, + reference_velocity=nothing) + +Boundary zone for [`OpenBoundarySystem`](@ref). + +The specified `boundary_face` (line in 2D or rectangle in 3D) will be extruded in the direction +opposite to `face_normal` to create a box for the boundary zone. +To specify the `boundary_face`, pass the required vertices as described below. +For complex 3D simulations, these vertices can also be extracted from an STL file +(see [`planar_geometry_to_face`](@ref)). There are three ways to specify the actual shape of the boundary zone: 1. Don't pass `initial_condition` or `extrude_geometry`. The boundary zone box will then be filled with boundary particles (default). 2. Specify `extrude_geometry` by passing a 1D shape in 2D or a 2D shape in 3D, - which is then extruded in the direction opposite to `plane_normal` to create the boundary particles. + which is then extruded in the direction opposite to `face_normal` to create the boundary 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. + on the line specified by `boundary_face`, or an initial condition with 1D coordinates, + which lies on the line specified by `boundary_face` 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. + in the rectangle specified by `boundary_face`, or an initial condition with 2D coordinates, + which lies in the rectangle specified by `boundary_face` 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 boundary particles. @@ -32,27 +37,27 @@ There are three ways to specify the actual shape of the boundary zone: 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. -- `plane_normal`: Vector defining the plane normal. It always points inside the fluid domain. +- `boundary_face`: Tuple of vertices defining a part of the surface of the domain. + The vertices 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 vertices ``(A, B)``, so that the interval ``[A, B]`` is + the inflow surface. + In 3D, pass three vertices ``(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. +- `face_normal`: Vector defining the normal of the `boundary_face`. It always points inside the fluid domain. - `boundary_type=BidirectionalFlow()`: Specify the type of the boundary. Available types are - `InFlow()` for an inflow boundary - `OutFlow()` for an outflow boundary - `BidirectionalFlow()` (default) for an bidirectional flow boundary -- `open_boundary_layers`: Number of particle layers in the direction opposite to `plane_normal`. +- `open_boundary_layers`: Number of particle layers in the direction opposite to `face_normal`. - `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 +- `extrude_geometry=nothing`: 1D shape in 2D or 2D shape in 3D, which lies on the `boundary_face` and is extruded upstream to obtain the inflow particles. See point 2 above for more details. - `average_inflow_velocity=true`: If `true`, the extrapolated inflow velocity is averaged @@ -63,101 +68,249 @@ There are three ways to specify the actual shape of the boundary zone: anisotropic buffer-particles distribution, resulting in a potential numerical instability. Averaging mitigates these effects. +- `reference_velocity`: Reference velocity is either a function mapping each particle's coordinates + and time to its velocity, or, for a constant fluid velocity, + a vector holding this velocity. +- `reference_pressure`: Reference pressure is either a function mapping each particle's coordinates + and time to its pressure, or a scalar for a constant pressure over all particles. +- `reference_density`: Reference density is either a function mapping each particle's coordinates + and time to its density, or a scalar for a constant density over all particles. + +!!! note "Note" + The reference values (`reference_velocity`, `reference_pressure`, `reference_density`) + can also be set to `nothing`. + In this case, they will either be extrapolated from the fluid domain ([BoundaryModelMirroringTafuni](@ref BoundaryModelMirroringTafuni)) + or evolved using the characteristic flow variables ([BoundaryModelCharacteristicsLastiwka](@ref BoundaryModelCharacteristicsLastiwka)). # Examples -```julia +```jldoctest; output=false # 2D -plane_points = ([0.0, 0.0], [0.0, 1.0]) -plane_normal=[1.0, 0.0] +face_vertices = ([0.0, 0.0], [0.0, 1.0]) +face_normal = [1.0, 0.0] + +# Constant reference velocity: +velocity_const = [1.0, 0.0] -inflow = BoundaryZone(; plane=plane_points, plane_normal, particle_spacing=0.1, density=1.0, - open_boundary_layers=4, boundary_type=InFlow()) +inflow_1 = BoundaryZone(; boundary_face=face_vertices, face_normal, particle_spacing=0.1, + density=1.0, open_boundary_layers=4, boundary_type=InFlow(), + reference_velocity=velocity_const) + +# Reference velocity as a function (parabolic velocity profile): +velocity_func = (pos, t) -> SVector(4.0 * pos[2] * (1.0 - pos[2]), 0.0) + +inflow_2 = BoundaryZone(; boundary_face=face_vertices, face_normal, particle_spacing=0.1, + density=1.0, open_boundary_layers=4, boundary_type=InFlow(), + reference_velocity=velocity_func) # 3D -plane_points = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) -plane_normal=[0.0, 0.0, 1.0] +face_vertices = ([0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]) +face_normal = [0.0, 0.0, 1.0] + +# Constant reference pressure: +pressure_const = 0.0 + +outflow_1 = BoundaryZone(; boundary_face=face_vertices, face_normal, particle_spacing=0.1, + density=1.0, open_boundary_layers=4, boundary_type=OutFlow(), + reference_pressure=pressure_const) + +# Reference pressure as a function (y-dependent profile, sinusoidal in time): +pressure_func = (pos, t) -> pos[2] * sin(2pi * t) -outflow = BoundaryZone(; plane=plane_points, plane_normal, particle_spacing=0.1, density=1.0, - open_boundary_layers=4, boundary_type=OutFlow()) +outflow_2 = BoundaryZone(; boundary_face=face_vertices, face_normal, particle_spacing=0.1, + density=1.0, open_boundary_layers=4, boundary_type=OutFlow(), + reference_pressure=pressure_func) # 3D particles sampled as cylinder circle = SphereShape(0.1, 0.5, (0.5, 0.5), 1.0, sphere_type=RoundSphere()) -bidirectional_flow = BoundaryZone(; plane=plane_points, plane_normal, particle_spacing=0.1, - density=1.0, extrude_geometry=circle, open_boundary_layers=4) +bidirectional_flow = BoundaryZone(; boundary_face=face_vertices, face_normal, + particle_spacing=0.1, density=1.0, + boundary_type=BidirectionalFlow(), + extrude_geometry=circle, open_boundary_layers=4) + +# output +┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ BoundaryZone │ +│ ════════════ │ +│ boundary type: ………………………………………… bidirectional_flow │ +│ #particles: ………………………………………………… 234 │ +│ width: ……………………………………………………………… 0.4 │ +└──────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. """ -struct BoundaryZone{BT, IC, S, ZO, ZW, FD, PN} - initial_condition :: IC - spanning_set :: S - zone_origin :: ZO - zone_width :: ZW - flow_direction :: FD - plane_normal :: PN - boundary_type :: BT +struct BoundaryZone{IC, S, ZO, ZW, FD, FN, R} + initial_condition :: IC + spanning_set :: S + zone_origin :: ZO + zone_width :: ZW + flow_direction :: FD + face_normal :: FN + reference_values :: R + # Note that the following can't be static type parameters, as all boundary zones in a system + # must have the same type, so that we can loop over them in a type-stable way. average_inflow_velocity :: Bool + prescribed_density :: Bool + prescribed_pressure :: Bool + prescribed_velocity :: Bool end -function BoundaryZone(; plane, plane_normal, density, particle_spacing, +function BoundaryZone(; boundary_face, face_normal, density, particle_spacing, initial_condition=nothing, extrude_geometry=nothing, - open_boundary_layers::Integer, boundary_type=BidirectionalFlow(), - average_inflow_velocity=true) + open_boundary_layers::Integer, average_inflow_velocity=true, + boundary_type=BidirectionalFlow(), + reference_density=nothing, reference_pressure=nothing, + reference_velocity=nothing) if open_boundary_layers <= 0 throw(ArgumentError("`open_boundary_layers` must be positive and greater than zero")) end - # `plane_normal` always points in fluid domain - plane_normal_ = normalize(SVector(plane_normal...)) - - if boundary_type isa BidirectionalFlow - flow_direction = nothing + # `face_normal` always points in fluid domain + face_normal_ = normalize(SVector(face_normal...)) + + ic, flow_direction, spanning_set_, zone_origin, + zone_width = set_up_boundary_zone(boundary_face, face_normal_, density, + particle_spacing, initial_condition, extrude_geometry, + open_boundary_layers, boundary_type) + + NDIMS = ndims(ic) + ELTYPE = eltype(ic) + if !(reference_velocity isa Function || isnothing(reference_velocity) || + (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, " * + "or, for a constant fluid velocity, a vector of length $NDIMS for a $(NDIMS)D problem holding this velocity")) + else + if reference_velocity isa Function + test_result = reference_velocity(zeros(NDIMS), 0.0) + if length(test_result) != NDIMS + throw(ArgumentError("`velocity` function must be of dimension $NDIMS")) + end + end + # We need this dummy for type stability reasons + velocity_dummy = SVector(ntuple(dim -> convert(ELTYPE, Inf), NDIMS)) + velocity_ref = wrap_reference_function(reference_velocity, velocity_dummy) + end - elseif boundary_type isa InFlow - # Unit vector pointing in downstream direction - flow_direction = plane_normal_ + if !(reference_pressure isa Function || reference_pressure isa Real || + isnothing(reference_pressure)) + throw(ArgumentError("`reference_pressure` must be either a function mapping " * + "each particle's coordinates and time to its pressure, " * + "or a scalar")) + else + if reference_pressure isa Function + test_result = reference_pressure(zeros(NDIMS), 0.0) + if length(test_result) != 1 + throw(ArgumentError("`reference_pressure` function must be a scalar function")) + end + end + # We need this dummy for type stability reasons + pressure_dummy = convert(ELTYPE, Inf) + pressure_ref = wrap_reference_function(reference_pressure, pressure_dummy) + end - elseif boundary_type isa OutFlow - # Unit vector pointing in downstream direction - flow_direction = -plane_normal_ + if !(reference_density isa Function || reference_density isa Real || + isnothing(reference_density)) + throw(ArgumentError("`reference_density` must be either a function mapping " * + "each particle's coordinates and time to its density, " * + "or a scalar")) + else + if reference_density isa Function + test_result = reference_density(zeros(NDIMS), 0.0) + if length(test_result) != 1 + throw(ArgumentError("`reference_density` function must be a scalar function")) + end + end + # We need this dummy for type stability reasons + density_dummy = convert(ELTYPE, Inf) + density_ref = wrap_reference_function(reference_density, density_dummy) end - ic, spanning_set_, zone_origin, - zone_width = set_up_boundary_zone(plane, plane_normal_, flow_direction, density, - particle_spacing, initial_condition, - extrude_geometry, open_boundary_layers; - boundary_type=boundary_type) + prescribed_pressure = isnothing(reference_pressure) ? false : true + prescribed_density = isnothing(reference_density) ? false : true + prescribed_velocity = isnothing(reference_velocity) ? false : true + + reference_values = (reference_velocity=velocity_ref, reference_pressure=pressure_ref, + reference_density=density_ref) + + coordinates_svector = reinterpret(reshape, SVector{NDIMS, ELTYPE}, ic.coordinates) + + if prescribed_pressure + ic.pressure .= pressure_ref.(coordinates_svector, 0) + end + if prescribed_density + ic.density .= density_ref.(coordinates_svector, 0) + ic.mass .= ic.density * ic.particle_spacing^NDIMS + end + if prescribed_velocity + ic.velocity .= stack(velocity_ref.(coordinates_svector, 0)) + end return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, - flow_direction, plane_normal_, boundary_type, - average_inflow_velocity) + flow_direction, face_normal_, reference_values, + average_inflow_velocity, prescribed_density, prescribed_pressure, + prescribed_velocity) +end + +function boundary_type_name(boundary_zone::BoundaryZone) + (; flow_direction, face_normal) = boundary_zone + + if isnothing(flow_direction) + return "bidirectional_flow" + elseif signbit(dot(flow_direction, face_normal)) + return "outflow" + else + return "inflow" + end +end + +function Base.show(io::IO, boundary_zone::BoundaryZone) + @nospecialize boundary_zone # reduce precompilation time + + print(io, "BoundaryZone(") + print(io, ") with ", nparticles(boundary_zone.initial_condition), " particles") +end + +function Base.show(io::IO, ::MIME"text/plain", boundary_zone::BoundaryZone) + @nospecialize boundary_zone # reduce precompilation time + + if get(io, :compact, false) + show(io, boundary_zone) + else + summary_header(io, "BoundaryZone") + summary_line(io, "boundary type", boundary_type_name(boundary_zone)) + summary_line(io, "#particles", nparticles(boundary_zone.initial_condition)) + summary_line(io, "width", round(boundary_zone.zone_width, digits=3)) + summary_footer(io) + end end -function set_up_boundary_zone(plane, plane_normal, flow_direction, density, - particle_spacing, initial_condition, extrude_geometry, - open_boundary_layers; boundary_type) +function set_up_boundary_zone(boundary_face, face_normal, density, particle_spacing, + initial_condition, extrude_geometry, open_boundary_layers, + boundary_type) if boundary_type isa InFlow - extrude_direction = -flow_direction + # Unit vector pointing in downstream direction + flow_direction = face_normal elseif boundary_type isa OutFlow - extrude_direction = flow_direction + # Unit vector pointing in downstream direction + flow_direction = -face_normal elseif boundary_type isa BidirectionalFlow - # `plane_normal` is always pointing in the fluid domain - extrude_direction = -plane_normal + flow_direction = nothing end # Sample particles in boundary zone if isnothing(initial_condition) && isnothing(extrude_geometry) - initial_condition = TrixiParticles.extrude_geometry(plane; particle_spacing, + initial_condition = TrixiParticles.extrude_geometry(boundary_face; particle_spacing, density, - direction=extrude_direction, + direction=(-face_normal), n_extrude=open_boundary_layers) elseif !isnothing(extrude_geometry) initial_condition = TrixiParticles.extrude_geometry(extrude_geometry; particle_spacing, density, - direction=extrude_direction, + direction=(-face_normal), n_extrude=open_boundary_layers) else initial_condition = initial_condition @@ -169,17 +322,17 @@ function set_up_boundary_zone(plane, plane_normal, flow_direction, density, 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) + spanning_set, zone_origin = calculate_spanning_vectors(boundary_face, zone_width) - # First vector of `spanning_vectors` is normal to the boundary plane. - dot_plane_normal = dot(normalize(spanning_set[:, 1]), plane_normal) + # First vector of `spanning_vectors` is normal to the boundary face. + dot_face_normal = dot(normalize(spanning_set[:, 1]), face_normal) - if !isapprox(abs(dot_plane_normal), 1.0, atol=1e-7) - throw(ArgumentError("`plane_normal` is not normal to the boundary plane")) + if !isapprox(abs(dot_face_normal), 1, rtol=1e-5) + throw(ArgumentError("`face_normal` is not normal to the boundary face")) end if boundary_type isa InFlow - # First vector of `spanning_vectors` is normal to the boundary plane + # First vector of `spanning_vectors` is normal to the boundary face dot_flow = dot(normalize(spanning_set[:, 1]), flow_direction) # The vector must point in upstream direction for an inflow boundary. @@ -187,7 +340,7 @@ function set_up_boundary_zone(plane, plane_normal, flow_direction, density, spanning_set[:, 1] .*= -sign(dot_flow) elseif boundary_type isa OutFlow - # First vector of `spanning_vectors` is normal to the boundary plane + # First vector of `spanning_vectors` is normal to the boundary face dot_flow = dot(normalize(spanning_set[:, 1]), flow_direction) # The vector must point in downstream direction for an outflow boundary. @@ -196,7 +349,7 @@ function set_up_boundary_zone(plane, plane_normal, flow_direction, density, elseif boundary_type isa BidirectionalFlow # Flip the normal vector to point opposite to fluid domain - spanning_set[:, 1] .*= -sign(dot_plane_normal) + spanning_set[:, 1] .*= -sign(dot_face_normal) end spanning_set_ = reinterpret(reshape, SVector{NDIMS, ELTYPE}, spanning_set) @@ -205,39 +358,39 @@ function set_up_boundary_zone(plane, plane_normal, flow_direction, density, # This check is only necessary when `initial_condition` or `extrude_geometry` are passed. ic = remove_outside_particles(initial_condition, spanning_set_, zone_origin) - return ic, spanning_set_, zone_origin, zone_width + return ic, flow_direction, spanning_set_, zone_origin, zone_width end -function calculate_spanning_vectors(plane, zone_width) - return spanning_vectors(Tuple(plane), zone_width), SVector(plane[1]...) +function calculate_spanning_vectors(boundary_face, zone_width) + return spanning_vectors(Tuple(boundary_face), zone_width), SVector(boundary_face[1]...) end -function spanning_vectors(plane_points::NTuple{2}, zone_width) - plane_size = plane_points[2] - plane_points[1] +function spanning_vectors(face_vertices::NTuple{2}, zone_width) + face_size = face_vertices[2] - face_vertices[1] - # Calculate normal vector of plane - b = normalize([-plane_size[2], plane_size[1]]) * zone_width + # Calculate normal vector of `boundary_face` + b = normalize([-face_size[2], face_size[1]]) * zone_width - return hcat(b, plane_size) + return hcat(b, face_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] +function spanning_vectors(face_vertices::NTuple{3}, zone_width) + # Vectors spanning the `boundary_face` + edge1 = face_vertices[2] - face_vertices[1] + edge2 = face_vertices[3] - face_vertices[1] # Check if the edges are linearly dependent (to avoid degenerate planes) if isapprox(norm(cross(edge1, edge2)), 0.0; atol=eps()) throw(ArgumentError("the vectors `AB` and `AC` must not be collinear")) end - # Calculate normal vector of plane + # Calculate normal vector of `boundary_face` c = Vector(normalize(cross(edge2, edge1)) * zone_width) return hcat(c, edge1, edge2) end -@inline function is_in_boundary_zone(boundary_zone::BoundaryZone, particle_coords) +@inline function is_in_boundary_zone(boundary_zone, particle_coords) (; zone_origin, spanning_set) = boundary_zone particle_position = particle_coords - zone_origin @@ -261,6 +414,27 @@ end return true end +function update_boundary_zone_indices!(system, u, boundary_zones, semi) + set_zero!(system.boundary_zone_indices) + + @threaded semi for particle in each_integrated_particle(system) + particle_coords = current_coords(u, system, particle) + + for (zone_id, boundary_zone) in enumerate(boundary_zones) + # Check if boundary particle is in the boundary zone + if is_in_boundary_zone(boundary_zone, particle_coords) + system.boundary_zone_indices[particle] = zone_id + end + end + end + + return system +end + +function current_boundary_zone(system, particle) + return system.boundary_zones[system.boundary_zone_indices[particle]] +end + function remove_outside_particles(initial_condition, spanning_set, zone_origin) (; coordinates, density, particle_spacing) = initial_condition @@ -276,3 +450,64 @@ function remove_outside_particles(initial_condition, spanning_set, zone_origin) return InitialCondition(; coordinates=coordinates[:, in_zone], density=first(density), particle_spacing) end + +function wrap_reference_function(function_::Nothing, ref_dummy) + # Return a dummy value for type stability + return @inline((coords, t)->ref_dummy) +end + +function wrap_reference_function(function_::Function, ref_dummy) + # Already a function + return function_ +end + +function wrap_reference_function(constant_scalar::Number, ref_dummy) + return @inline((coords, t)->constant_scalar) +end + +function wrap_reference_function(constant_vector::AbstractVector, + ref_dummy::SVector{NDIMS, ELTYPE}) where {NDIMS, ELTYPE} + return @inline((coords, t)->SVector{NDIMS, ELTYPE}(constant_vector)) +end + +function reference_pressure(boundary_zone, v, system, particle, pos, t) + (; prescribed_pressure) = boundary_zone + (; pressure_reference_values) = system.cache + + if prescribed_pressure + zone_id = system.boundary_zone_indices[particle] + + # `pressure_reference_values[zone_id](pos, t)`, but in a type-stable way + return apply_ith_function(pressure_reference_values, zone_id, pos, t) + else + return current_pressure(v, system, particle) + end +end + +function reference_density(boundary_zone, v, system, particle, pos, t) + (; prescribed_density) = boundary_zone + (; density_reference_values) = system.cache + + if prescribed_density + zone_id = system.boundary_zone_indices[particle] + + # `density_reference_values[zone_id](pos, t)`, but in a type-stable way + return apply_ith_function(density_reference_values, zone_id, pos, t) + else + return current_density(v, system, particle) + end +end + +function reference_velocity(boundary_zone, v, system, particle, pos, t) + (; prescribed_velocity) = boundary_zone + (; velocity_reference_values) = system.cache + + if prescribed_velocity + zone_id = system.boundary_zone_indices[particle] + + # `velocity_reference_values[zone_id](pos, t)`, but in a type-stable way + return apply_ith_function(velocity_reference_values, zone_id, pos, t) + else + return current_velocity(v, system, particle) + end +end diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index a37a9cd605..aebbacc724 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -1,7 +1,7 @@ @doc raw""" - BoundaryModelLastiwka(; extrapolate_reference_values=nothing) + BoundaryModelCharacteristicsLastiwka(; extrapolate_reference_values=nothing) -Boundary model for [`OpenBoundarySPHSystem`](@ref). +Boundary model for [`OpenBoundarySystem`](@ref). This model uses the characteristic variables to propagate the appropriate values to the outlet or inlet and was proposed by Lastiwka et al. (2009). It requires a specific flow direction to be passed to the [`BoundaryZone`](@ref). @@ -19,58 +19,55 @@ For more information about the method see [description below](@ref method_of_cha Note that even without this extrapolation feature, the reference values don't need to be prescribed - they're computed from the characteristics. """ -struct BoundaryModelLastiwka{T} +struct BoundaryModelCharacteristicsLastiwka{T} extrapolate_reference_values::T - function BoundaryModelLastiwka(; extrapolate_reference_values=nothing) + function BoundaryModelCharacteristicsLastiwka(; extrapolate_reference_values=nothing) return new{typeof(extrapolate_reference_values)}(extrapolate_reference_values) end end # Called from update callback via `update_open_boundary_eachstep!` -@inline function update_boundary_quantities!(system, boundary_model::BoundaryModelLastiwka, +@inline function update_boundary_quantities!(system, + boundary_model::BoundaryModelCharacteristicsLastiwka, v, u, v_ode, u_ode, semi, t) - (; density, pressure, cache, boundary_zone, - reference_velocity, reference_pressure, reference_density) = system - (; flow_direction) = boundary_zone - - fluid_system = corresponding_fluid_system(system, semi) + (; density, pressure, cache, boundary_zones, fluid_system) = system sound_speed = system_sound_speed(fluid_system) if !isnothing(boundary_model.extrapolate_reference_values) - (; prescribed_pressure, prescribed_velocity, prescribed_density) = cache v_fluid = wrap_v(v_ode, fluid_system, semi) u_fluid = wrap_u(u_ode, fluid_system, semi) @trixi_timeit timer() "extrapolate and correct values" begin extrapolate_values!(system, boundary_model.extrapolate_reference_values, - v, v_fluid, u, u_fluid, semi, t; - prescribed_pressure, prescribed_velocity, - prescribed_density) + v, v_fluid, u, u_fluid, semi) end end # Update quantities based on the characteristic variables - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) + boundary_zone = current_boundary_zone(system, particle) + (; flow_direction) = boundary_zone + particle_position = current_coords(u, system, particle) J1 = cache.characteristics[1, particle] J2 = cache.characteristics[2, particle] J3 = cache.characteristics[3, particle] - rho_ref = reference_value(reference_density, density[particle], - particle_position, t) + rho_ref = reference_density(boundary_zone, v, system, particle, + particle_position, t) + density[particle] = rho_ref + ((-J1 + (J2 + J3) / 2) / sound_speed^2) - p_ref = reference_value(reference_pressure, pressure[particle], - particle_position, t) + p_ref = reference_pressure(boundary_zone, v, system, particle, particle_position, t) + pressure[particle] = p_ref + (J2 + J3) / 2 - v_current = current_velocity(v, system, particle) - v_ref = reference_value(reference_velocity, v_current, - particle_position, t) - rho = density[particle] + v_ref = reference_velocity(boundary_zone, v, system, particle, particle_position, t) + + rho = current_density(v, system, particle) v_ = v_ref + ((J2 - J3) / (2 * sound_speed * rho)) * flow_direction for dim in 1:ndims(system) @@ -78,19 +75,21 @@ end end end - if boundary_zone.average_inflow_velocity - # Even if the velocity is prescribed, this boundary model computes the velocity for each particle individually. - # Thus, turbulent flows near the inflow can lead to a non-uniform buffer particle distribution, - # resulting in a potential numerical instability. Averaging mitigates these effects. - average_velocity!(v, u, system, boundary_model, boundary_zone, semi) + for boundary_zone in boundary_zones + if boundary_zone.average_inflow_velocity + # Even if the velocity is prescribed, this boundary model computes the velocity for each particle individually. + # Thus, turbulent flows near the inflow can lead to a non-uniform buffer particle distribution, + # resulting in a potential numerical instability. Averaging mitigates these effects. + average_velocity!(v, u, system, boundary_model, boundary_zone, semi) + end end return system end # Called from semidiscretization -function update_boundary_model!(system, ::BoundaryModelLastiwka, v, u, v_ode, u_ode, - semi, t) +function update_boundary_model!(system, ::BoundaryModelCharacteristicsLastiwka, + v, u, v_ode, u_ode, semi, t) @trixi_timeit timer() "evaluate characteristics" begin evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) end @@ -103,9 +102,8 @@ end # 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, cache, boundary_zone) = system + (; volume, cache, fluid_system, density, pressure) = system (; characteristics, previous_characteristics) = cache - fluid_system = corresponding_fluid_system(system, semi) @threaded semi for particle in eachparticle(system) previous_characteristics[1, particle] = characteristics[1, particle] @@ -117,14 +115,56 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) set_zero!(volume) # Evaluate the characteristic variables with the fluid system - evaluate_characteristics!(system, fluid_system, v, u, v_ode, u_ode, semi, t) + v_fluid = wrap_v(v_ode, fluid_system, semi) + u_fluid = wrap_u(u_ode, fluid_system, semi) + + system_coords = current_coordinates(u, system) + fluid_coords = current_coordinates(u_fluid, fluid_system) + sound_speed = system_sound_speed(fluid_system) + + # Loop over all fluid neighbors within the kernel cutoff + foreach_point_neighbor(system, fluid_system, system_coords, fluid_coords, semi; + points=each_integrated_particle(system)) do particle, neighbor, + pos_diff, distance + boundary_zone = current_boundary_zone(system, particle) + (; flow_direction) = boundary_zone + + neighbor_position = current_coords(u_fluid, fluid_system, neighbor) + + # Determine current and prescribed quantities + rho_b = current_density(v_fluid, fluid_system, neighbor) + + rho_ref = reference_density(boundary_zone, v, system, particle, + neighbor_position, t) + + p_b = current_pressure(v_fluid, fluid_system, neighbor) + + p_ref = reference_pressure(boundary_zone, v, system, particle, neighbor_position, t) + + v_b = current_velocity(v_fluid, fluid_system, neighbor) + + v_neighbor_ref = reference_velocity(boundary_zone, v, system, particle, + 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(fluid_system, distance, particle) + + 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 # 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 semi for particle in each_moving_particle(system) - + @threaded semi for particle in each_integrated_particle(system) # Particle is outside of the influence of fluid particles if isapprox(volume[particle], 0) @@ -135,7 +175,7 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) avg_J3 = zero(eltype(volume)) counter = 0 - for neighbor in each_moving_particle(system) + for neighbor in each_integrated_particle(system) # Make sure that only neighbors in the influence of # the fluid particles are used. if volume[neighbor] > sqrt(eps()) @@ -161,93 +201,43 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) 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, cache, boundary_zone, density, pressure, - reference_velocity, reference_pressure, reference_density) = system - (; flow_direction) = boundary_zone - (; characteristics) = cache - - v_neighbor_system = wrap_v(v_ode, neighbor_system, semi) - u_neighbor_system = wrap_u(u_ode, neighbor_system, semi) - - system_coords = current_coordinates(u, system) - neighbor_coords = current_coordinates(u_neighbor_system, neighbor_system) - sound_speed = system_sound_speed(neighbor_system) - - # Loop over all fluid neighbors within the kernel cutoff - foreach_point_neighbor(system, neighbor_system, system_coords, neighbor_coords, semi; - points=each_moving_particle(system)) do particle, neighbor, - pos_diff, distance - neighbor_position = current_coords(u_neighbor_system, neighbor_system, neighbor) - - # Determine current and prescribed quantities - rho_b = current_density(v_neighbor_system, neighbor_system, neighbor) - rho_ref = reference_value(reference_density, density[particle], - neighbor_position, t) - - p_b = current_pressure(v_neighbor_system, neighbor_system, neighbor) - p_ref = reference_value(reference_pressure, pressure[particle], - neighbor_position, t) - - v_b = current_velocity(v_neighbor_system, neighbor_system, neighbor) - v_particle = current_velocity(v, system, particle) - v_neighbor_ref = reference_value(reference_velocity, v_particle, - 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, particle) + boundary_zone = current_boundary_zone(system, particle) + (; flow_direction, face_normal) = boundary_zone - characteristics[1, particle] += (density_term + pressure_term) * kernel_ - characteristics[2, particle] += (velocity_term + pressure_term) * kernel_ - characteristics[3, particle] += (-velocity_term + pressure_term) * kernel_ + # Outflow + if signbit(dot(flow_direction, face_normal)) + # J3 is prescribed (i.e. determined from the exterior of the domain). + # J1 and J2 is transmitted from the domain interior. + characteristics[3, particle] = zero(eltype(characteristics)) - volume[particle] += kernel_ + else # Inflow + # Allow only J3 to propagate upstream to the boundary + characteristics[1, particle] = zero(eltype(characteristics)) + characteristics[2, particle] = zero(eltype(characteristics)) + end end return system end -@inline function prescribe_conditions!(characteristics, particle, ::BoundaryZone{OutFlow}) - # J3 is prescribed (i.e. determined from the exterior of the domain). - # J1 and J2 is transmitted from the domain interior. - characteristics[3, particle] = zero(eltype(characteristics)) - - return characteristics -end - -@inline function prescribe_conditions!(characteristics, particle, ::BoundaryZone{InFlow}) - # Allow only J3 to propagate upstream to the boundary - characteristics[1, particle] = zero(eltype(characteristics)) - characteristics[2, particle] = zero(eltype(characteristics)) +function average_velocity!(v, u, system, ::BoundaryModelCharacteristicsLastiwka, + boundary_zone, semi) + (; flow_direction, face_normal) = boundary_zone - return characteristics -end - -function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, semi) - # Only apply averaging at the inflow - return v -end + # This is an outflow. Only apply averaging at the inflow. + signbit(dot(flow_direction, face_normal)) && return v -function average_velocity!(v, u, system, ::BoundaryModelLastiwka, ::BoundaryZone{InFlow}, - semi) + particles_in_zone = findall(particle -> boundary_zone == + current_boundary_zone(system, particle), + each_integrated_particle(system)) # Division inside the `sum` closure to maintain GPU compatibility - avg_velocity = sum(each_moving_particle(system)) do particle - return current_velocity(v, system, particle) / system.buffer.active_particle_count[] + avg_velocity = sum(particles_in_zone) do particle + return current_velocity(v, system, particle) / length(particles_in_zone) end - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in particles_in_zone # Set the velocity of the ghost node to the average velocity of the fluid domain for dim in eachindex(avg_velocity) @inbounds v[dim, particle] = avg_velocity[dim] diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 45a98ad7c2..c516a4acb0 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -47,9 +47,9 @@ The interpolated values at the ghost nodes are then assigned to the correspondin struct ZerothOrderMirroring end @doc raw""" - BoundaryModelTafuni(; mirror_method=FirstOrderMirroring()) + BoundaryModelMirroringTafuni(; mirror_method=FirstOrderMirroring()) -Boundary model for the `OpenBoundarySPHSystem`. +Boundary model for the [`OpenBoundarySystem`](@ref). This model implements the method of [Tafuni et al. (2018)](@cite Tafuni2018) to extrapolate the properties from the fluid domain to the buffer zones (inflow and outflow) using ghost nodes. The position of the ghost nodes is obtained by mirroring the boundary particles @@ -61,62 +61,56 @@ We provide three different mirroring methods: - [`FirstOrderMirroring`](@ref): Uses a first order correction based on the gradient of the interpolated values . - [`SimpleMirroring`](@ref): Similar to the first order mirroring, but does not use the gradient of the interpolated values. """ -struct BoundaryModelTafuni{MM} +struct BoundaryModelMirroringTafuni{MM} mirror_method::MM end -function BoundaryModelTafuni(; mirror_method=FirstOrderMirroring()) - return BoundaryModelTafuni(mirror_method) +function BoundaryModelMirroringTafuni(; mirror_method=FirstOrderMirroring()) + return BoundaryModelMirroringTafuni(mirror_method) end -function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni, +function update_boundary_quantities!(system, boundary_model::BoundaryModelMirroringTafuni, v, u, v_ode, u_ode, semi, t) - (; reference_pressure, reference_density, reference_velocity, boundary_zone, - pressure, density, cache) = system - (; prescribed_pressure, prescribed_density, prescribed_velocity) = cache + (; boundary_zones, pressure, density, fluid_system, cache) = system @trixi_timeit timer() "extrapolate and correct values" begin - fluid_system = corresponding_fluid_system(system, semi) - v_fluid = wrap_v(v_ode, fluid_system, semi) u_fluid = wrap_u(u_ode, fluid_system, semi) - extrapolate_values!(system, boundary_model.mirror_method, v, v_fluid, - u, u_fluid, semi, t; prescribed_pressure, - prescribed_density, prescribed_velocity) - end - - if !prescribed_velocity && boundary_zone.average_inflow_velocity - # When no velocity is prescribed at the inflow, the velocity is extrapolated from the fluid domain. - # Thus, turbulent flows near the inflow can lead to a non-uniform buffer particle distribution, - # resulting in a potential numerical instability. Averaging mitigates these effects. - average_velocity!(v, u, system, boundary_zone, semi) + extrapolate_values!(system, boundary_model.mirror_method, + v, v_fluid, u, u_fluid, semi) end - if prescribed_pressure - @threaded semi for particle in each_moving_particle(system) - particle_coords = current_coords(u, system, particle) + for boundary_zone in boundary_zones + (; average_inflow_velocity, prescribed_velocity) = boundary_zone - pressure[particle] = reference_value(reference_pressure, pressure[particle], - particle_coords, t) + if !prescribed_velocity && average_inflow_velocity + # When no velocity is prescribed at the inflow, the velocity is extrapolated from the fluid domain. + # Thus, turbulent flows near the inflow can lead to a non-uniform buffer particle distribution, + # resulting in a potential numerical instability. Averaging mitigates these effects. + average_velocity!(v, u, system, boundary_zone, semi) end end - if prescribed_density - @threaded semi for particle in each_moving_particle(system) - particle_coords = current_coords(u, system, particle) + @threaded semi for particle in each_integrated_particle(system) + boundary_zone = current_boundary_zone(system, particle) + (; prescribed_density, prescribed_pressure, prescribed_velocity) = boundary_zone - density[particle] = reference_value(reference_density, density[particle], - particle_coords, t) + particle_coords = current_coords(u, system, particle) + + if prescribed_pressure + pressure[particle] = reference_pressure(boundary_zone, v, system, particle, + particle_coords, t) end - end - if prescribed_velocity - @threaded semi for particle in each_moving_particle(system) - particle_coords = current_coords(u, system, particle) - v_particle = current_velocity(v, system, particle) + if prescribed_density + density[particle] = reference_density(boundary_zone, v, system, particle, + particle_coords, t) + end - v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) + if prescribed_velocity + v_ref = reference_velocity(boundary_zone, v, system, particle, + particle_coords, t) for dim in eachindex(v_ref) @inbounds v[dim, particle] = v_ref[dim] @@ -125,16 +119,15 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni end end -update_boundary_model!(system, ::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) = system +function update_boundary_model!(system, ::BoundaryModelMirroringTafuni, v, u, v_ode, u_ode, + semi, t) + return system +end function extrapolate_values!(system, mirror_method::Union{FirstOrderMirroring, SimpleMirroring}, - v_open_boundary, v_fluid, u_open_boundary, u_fluid, - semi, t; prescribed_density=false, - prescribed_pressure=false, prescribed_velocity=false) - (; pressure, density, boundary_zone) = system - - fluid_system = corresponding_fluid_system(system, semi) + v_open_boundary, v_fluid, u_open_boundary, u_fluid, semi) + (; pressure, density, fluid_system) = system # Static indices to avoid allocations two_to_end = SVector{ndims(system)}(2:(ndims(system) + 1)) @@ -148,7 +141,10 @@ function extrapolate_values!(system, # of the ghost node positions of each particle. # We can do this because we require the neighborhood search to support querying neighbors # of arbitrary positions (see `PointNeighbors.requires_update`). - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) + boundary_zone = current_boundary_zone(system, particle) + (; prescribed_density, prescribed_pressure, prescribed_velocity) = boundary_zone + particle_coords = current_coords(u_open_boundary, system, particle) ghost_node_position = mirror_position(particle_coords, boundary_zone) @@ -229,8 +225,8 @@ function extrapolate_values!(system, # See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.: # "Because flow from the inlet interface occurs perpendicular to the boundary, # only this component of interpolated velocity is kept [...]" - project_velocity_on_plane_normal!(v_open_boundary, system, particle, - boundary_zone) + project_velocity_on_face_normal!(v_open_boundary, system, particle, + boundary_zone) end # No else: `correction_matrix[][1, 1] <= eps()` means no fluid neighbors @@ -270,8 +266,8 @@ function extrapolate_values!(system, # See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.: # "Because flow from the inlet interface occurs perpendicular to the boundary, # only this component of interpolated velocity is kept [...]" - project_velocity_on_plane_normal!(v_open_boundary, system, particle, - boundary_zone) + project_velocity_on_face_normal!(v_open_boundary, system, particle, + boundary_zone) end end end @@ -280,12 +276,8 @@ function extrapolate_values!(system, end function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, - v_open_boundary, v_fluid, u_open_boundary, u_fluid, semi, t; - prescribed_density=false, prescribed_pressure=false, - prescribed_velocity=false) - (; pressure, density, boundary_zone) = system - - fluid_system = corresponding_fluid_system(system, semi) + v_open_boundary, v_fluid, u_open_boundary, u_fluid, semi) + (; pressure, density, fluid_system) = system # Use the fluid-fluid nhs, since the boundary particles are mirrored into the fluid domain nhs = get_neighborhood_search(fluid_system, fluid_system, semi) @@ -296,7 +288,10 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, # of the ghost node positions of each particle. # We can do this because we require the neighborhood search to support querying neighbors # of arbitrary positions (see `PointNeighbors.requires_update`). - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) + boundary_zone = current_boundary_zone(system, particle) + (; prescribed_pressure, prescribed_density, prescribed_velocity) = boundary_zone + particle_coords = current_coords(u_open_boundary, system, particle) ghost_node_position = mirror_position(particle_coords, boundary_zone) @@ -358,8 +353,8 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, # See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.: # "Because flow from the inlet interface occurs perpendicular to the boundary, # only this component of interpolated velocity is kept [...]" - project_velocity_on_plane_normal!(v_open_boundary, system, particle, - boundary_zone) + project_velocity_on_face_normal!(v_open_boundary, system, particle, + boundary_zone) end end end @@ -481,31 +476,41 @@ end function mirror_position(particle_coords, boundary_zone) particle_position = particle_coords - boundary_zone.zone_origin - dist = dot(particle_position, boundary_zone.plane_normal) + dist = dot(particle_position, boundary_zone.face_normal) - return particle_coords - 2 * dist * boundary_zone.plane_normal + return particle_coords - 2 * dist * boundary_zone.face_normal end -average_velocity!(v, u, system, boundary_zone, semi) = v +# Only for inflow boundary zones +function average_velocity!(v, u, system, boundary_zone, semi) + (; face_normal, zone_origin, initial_condition, flow_direction) = boundary_zone + + # Bidirectional flow + isnothing(flow_direction) && return v -function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, semi) - (; plane_normal, zone_origin, initial_condition) = boundary_zone + # Outflow + signbit(dot(flow_direction, face_normal)) && return v # We only use the extrapolated velocity in the vicinity of the transition region. # Otherwise, if the boundary zone is too large, averaging would be excessively influenced # by the fluid velocity further away from the boundary. max_dist = initial_condition.particle_spacing * 110 / 100 - candidates = findall(x -> dot(x - zone_origin, -plane_normal) <= max_dist, - reinterpret(reshape, SVector{ndims(system), eltype(u)}, - active_coordinates(u, system))) + candidates = findall(x -> dot(x - zone_origin, -face_normal) <= max_dist, + reinterpret(reshape, SVector{ndims(system), eltype(u)}, u)) + + particles_in_zone = findall(particle -> boundary_zone == + current_boundary_zone(system, particle), + each_integrated_particle(system)) + + intersect!(candidates, particles_in_zone) # Division inside the `sum` closure to maintain GPU compatibility avg_velocity = sum(candidates) do particle return current_velocity(v, system, particle) / length(candidates) end - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in particles_in_zone # Set the velocity of the ghost node to the average velocity of the fluid domain for dim in eachindex(avg_velocity) @inbounds v[dim, particle] = avg_velocity[dim] @@ -515,17 +520,23 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se return v end -project_velocity_on_plane_normal!(v, system, particle, boundary_zone) = v +# Only for inflow boundary zones +function project_velocity_on_face_normal!(v, system, particle, boundary_zone) + (; face_normal, flow_direction) = boundary_zone + + # Bidirectional flow + isnothing(flow_direction) && return v + + # Outflow + signbit(dot(flow_direction, face_normal)) && return v -function project_velocity_on_plane_normal!(v, system, particle, - boundary_zone::BoundaryZone{InFlow}) # Project `vel` on the normal direction of the boundary zone # See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.: # "Because flow from the inlet interface occurs perpendicular to the boundary, # only this component of interpolated velocity is kept [...]" v_particle = current_velocity(v, system, particle) - v_particle_projected = dot(v_particle, boundary_zone.plane_normal) * - boundary_zone.plane_normal + v_particle_projected = dot(v_particle, boundary_zone.face_normal) * + boundary_zone.face_normal for dim in eachindex(v_particle) @inbounds v[dim, particle] = v_particle_projected[dim] diff --git a/src/schemes/boundary/open_boundary/open_boundary.jl b/src/schemes/boundary/open_boundary/open_boundary.jl new file mode 100644 index 0000000000..de691fd580 --- /dev/null +++ b/src/schemes/boundary/open_boundary/open_boundary.jl @@ -0,0 +1,4 @@ +include("boundary_zones.jl") +include("mirroring.jl") +include("method_of_characteristics.jl") +include("system.jl") diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 2f512e6b52..9cf5233c17 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -1,10 +1,7 @@ @doc raw""" - OpenBoundarySPHSystem(boundary_zone::BoundaryZone; - fluid_system::FluidSystem, buffer_size::Integer, - boundary_model, - reference_velocity=nothing, - reference_pressure=nothing, - reference_density=nothing) + OpenBoundarySystem(boundary_zone::BoundaryZone; + fluid_system::AbstractFluidSystem, buffer_size::Integer, + boundary_model) Open boundary system for in- and outflow particles. @@ -15,267 +12,209 @@ Open boundary system for in- and outflow particles. - `fluid_system`: The corresponding fluid system - `boundary_model`: Boundary model (see [Open Boundary Models](@ref open_boundary_models)) - `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. -- `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. -- `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. - -!!! note "Note" - The reference values (`reference_velocity`, `reference_pressure`, `reference_density`) - can also be set to `nothing`. - In this case, they will either be extrapolated from the fluid domain ([BoundaryModelTafuni](@ref BoundaryModelTafuni)) - or evolved using the characteristic flow variables ([BoundaryModelLastiwka](@ref BoundaryModelLastiwka)). !!! warning "Experimental Implementation" - This is an experimental feature and may change in future releases. - It is GPU-compatible (e.g., with CUDA.jl and AMDGPU.jl), but currently **not** supported with Metal.jl. + This is an experimental feature and may change in any future releases. """ -struct OpenBoundarySPHSystem{BM, ELTYPE, NDIMS, IC, FS, FSI, ARRAY1D, BC, FC, BZ, RV, - RP, RD, B, C} <: System{NDIMS} - boundary_model :: BM - initial_condition :: IC - fluid_system :: FS - fluid_system_index :: FSI - smoothing_length :: ELTYPE - mass :: ARRAY1D # Array{ELTYPE, 1}: [particle] - density :: ARRAY1D # Array{ELTYPE, 1}: [particle] - volume :: ARRAY1D # Array{ELTYPE, 1}: [particle] - pressure :: ARRAY1D # Array{ELTYPE, 1}: [particle] - boundary_candidates :: BC # Array{UInt32, 1}: [particle] - fluid_candidates :: FC # Array{UInt32, 1}: [particle] - boundary_zone :: BZ - reference_velocity :: RV - reference_pressure :: RP - reference_density :: RD - buffer :: B - cache :: C +struct OpenBoundarySystem{BM, ELTYPE, NDIMS, IC, FS, FSI, ARRAY1D, BC, FC, BZI, BZ, + B, C} <: AbstractSystem{NDIMS} + boundary_model :: BM + initial_condition :: IC + fluid_system :: FS + fluid_system_index :: FSI + smoothing_length :: ELTYPE + mass :: ARRAY1D # Array{ELTYPE, 1}: [particle] + density :: ARRAY1D # Array{ELTYPE, 1}: [particle] + volume :: ARRAY1D # Array{ELTYPE, 1}: [particle] + pressure :: ARRAY1D # Array{ELTYPE, 1}: [particle] + boundary_candidates :: BC # Array{Bool, 1}: [particle] + fluid_candidates :: FC # Array{Bool, 1}: [particle] + boundary_zone_indices :: BZI # Array{UInt8, 1}: [particle] + boundary_zones :: BZ + buffer :: B + cache :: C end -function OpenBoundarySPHSystem(boundary_model, initial_condition, fluid_system, - fluid_system_index, smoothing_length, mass, density, volume, - pressure, boundary_candidates, fluid_candidates, - boundary_zone, reference_velocity, - reference_pressure, reference_density, buffer, cache) - OpenBoundarySPHSystem{typeof(boundary_model), eltype(mass), ndims(initial_condition), - typeof(initial_condition), typeof(fluid_system), - typeof(fluid_system_index), typeof(mass), - typeof(boundary_candidates), typeof(fluid_candidates), - typeof(boundary_zone), typeof(reference_velocity), - typeof(reference_pressure), typeof(reference_density), - typeof(buffer), - typeof(cache)}(boundary_model, initial_condition, fluid_system, - fluid_system_index, smoothing_length, mass, - density, volume, pressure, boundary_candidates, - fluid_candidates, boundary_zone, - reference_velocity, reference_pressure, - reference_density, buffer, cache) +function OpenBoundarySystem(boundary_model, initial_condition, fluid_system, + fluid_system_index, smoothing_length, mass, density, volume, + pressure, boundary_candidates, fluid_candidates, + boundary_zone_indices, boundary_zone, buffer, cache) + OpenBoundarySystem{typeof(boundary_model), eltype(mass), ndims(initial_condition), + typeof(initial_condition), typeof(fluid_system), + typeof(fluid_system_index), typeof(mass), + typeof(boundary_candidates), typeof(fluid_candidates), + typeof(boundary_zone_indices), typeof(boundary_zone), + typeof(buffer), + typeof(cache)}(boundary_model, initial_condition, fluid_system, + fluid_system_index, smoothing_length, mass, + density, volume, pressure, boundary_candidates, + fluid_candidates, boundary_zone_indices, + boundary_zone, buffer, cache) end -function OpenBoundarySPHSystem(boundary_zone::BoundaryZone; - fluid_system::FluidSystem, - buffer_size::Integer, boundary_model, - reference_velocity=nothing, - reference_pressure=nothing, - reference_density=nothing) - (; initial_condition) = boundary_zone +function OpenBoundarySystem(boundary_zones::Union{BoundaryZone, Nothing}...; + fluid_system::AbstractFluidSystem, buffer_size::Integer, + boundary_model) + boundary_zones_ = filter(bz -> !isnothing(bz), boundary_zones) + reference_values_ = map(bz -> bz.reference_values, boundary_zones_) - buffer = SystemBuffer(nparticles(initial_condition), buffer_size) + initial_conditions = union((bz.initial_condition for bz in boundary_zones)...) - initial_condition = allocate_buffer(initial_condition, buffer) + buffer = SystemBuffer(nparticles(initial_conditions), buffer_size) - NDIMS = ndims(initial_condition) + initial_conditions = allocate_buffer(initial_conditions, buffer) - pressure = copy(initial_condition.pressure) - mass = copy(initial_condition.mass) - density = copy(initial_condition.density) - volume = similar(initial_condition.density) + pressure = copy(initial_conditions.pressure) + mass = copy(initial_conditions.mass) + density = copy(initial_conditions.density) + volume = similar(initial_conditions.density) - if !(reference_velocity isa Function || isnothing(reference_velocity) || - (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 - if reference_velocity isa Function - test_result = reference_velocity(zeros(NDIMS), 0.0) - if length(test_result) != NDIMS - throw(ArgumentError("`reference_velocity` function must be of dimension $NDIMS")) - end - end - reference_velocity_ = wrap_reference_function(reference_velocity, Val(NDIMS)) - end - - if !(reference_pressure isa Function || reference_pressure isa Real || - isnothing(reference_pressure)) - 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 - if reference_pressure isa Function - test_result = reference_pressure(zeros(NDIMS), 0.0) - if length(test_result) != 1 - throw(ArgumentError("`reference_pressure` function must be a scalar function")) - end - end - reference_pressure_ = wrap_reference_function(reference_pressure, Val(NDIMS)) - end - - if !(reference_density isa Function || reference_density isa Real || - isnothing(reference_density)) - 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 - if reference_density isa Function - test_result = reference_density(zeros(NDIMS), 0.0) - if length(test_result) != 1 - throw(ArgumentError("`reference_density` function must be a scalar function")) - end - end - reference_density_ = wrap_reference_function(reference_density, Val(NDIMS)) - end - - cache = create_cache_open_boundary(boundary_model, initial_condition, - reference_density, reference_velocity, - reference_pressure) + cache = create_cache_open_boundary(boundary_model, initial_conditions, + reference_values_) fluid_system_index = Ref(0) smoothing_length = initial_smoothing_length(fluid_system) - boundary_candidates = fill(false, nparticles(initial_condition)) + boundary_candidates = fill(false, nparticles(initial_conditions)) fluid_candidates = fill(false, nparticles(fluid_system)) - return OpenBoundarySPHSystem(boundary_model, initial_condition, fluid_system, - fluid_system_index, smoothing_length, mass, density, - volume, pressure, boundary_candidates, fluid_candidates, - boundary_zone, reference_velocity_, - reference_pressure_, reference_density_, buffer, cache) + boundary_zone_indices = zeros(Int, nparticles(initial_conditions)) + + # Create new `BoundaryZone`s with `reference_values` set to `nothing` for type stability. + # `reference_values` are only used as API feature to temporarily store the reference values + # in the `BoundaryZone`, but they are not used in the actual simulation. + boundary_zones_new = map(zone -> BoundaryZone(zone.initial_condition, + zone.spanning_set, + zone.zone_origin, + zone.zone_width, + zone.flow_direction, + zone.face_normal, + nothing, + zone.average_inflow_velocity, + zone.prescribed_density, + zone.prescribed_pressure, + zone.prescribed_velocity), + boundary_zones) + + return OpenBoundarySystem(boundary_model, initial_conditions, fluid_system, + fluid_system_index, smoothing_length, mass, density, + volume, pressure, boundary_candidates, fluid_candidates, + boundary_zone_indices, boundary_zones_new, buffer, cache) end -function create_cache_open_boundary(boundary_model, initial_condition, - reference_density, reference_velocity, - reference_pressure) - ELTYPE = eltype(initial_condition) +function initialize!(system::OpenBoundarySystem, semi) + (; boundary_zones) = system - prescribed_pressure = isnothing(reference_pressure) ? false : true - prescribed_velocity = isnothing(reference_velocity) ? false : true - prescribed_density = isnothing(reference_density) ? false : true + update_boundary_zone_indices!(system, initial_coordinates(system), boundary_zones, semi) - if boundary_model isa BoundaryModelTafuni - return (; prescribed_pressure=prescribed_pressure, - prescribed_density=prescribed_density, - prescribed_velocity=prescribed_velocity) - end + return system +end - characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) - previous_characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) +function create_cache_open_boundary(boundary_model, initial_condition, reference_values) + ELTYPE = eltype(initial_condition) + + # Separate `reference_values` into pressure, density and velocity reference values + pressure_reference_values = map(ref -> ref.reference_pressure, reference_values) + density_reference_values = map(ref -> ref.reference_density, reference_values) + velocity_reference_values = map(ref -> ref.reference_velocity, reference_values) - return (; characteristics=characteristics, - previous_characteristics=previous_characteristics, - prescribed_pressure=prescribed_pressure, - prescribed_density=prescribed_density, prescribed_velocity=prescribed_velocity) + if boundary_model isa BoundaryModelCharacteristicsLastiwka + characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) + previous_characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) + + return (; characteristics=characteristics, + previous_characteristics=previous_characteristics, + pressure_reference_values=pressure_reference_values, + density_reference_values=density_reference_values, + velocity_reference_values=velocity_reference_values) + else + return (; pressure_reference_values=pressure_reference_values, + density_reference_values=density_reference_values, + velocity_reference_values=velocity_reference_values) + end end -timer_name(::OpenBoundarySPHSystem) = "open_boundary" -vtkname(system::OpenBoundarySPHSystem) = "open_boundary" -boundary_type_name(::BoundaryZone{ZT}) where {ZT} = string(nameof(ZT)) +timer_name(::OpenBoundarySystem) = "open_boundary" +vtkname(system::OpenBoundarySystem) = "open_boundary" -function Base.show(io::IO, system::OpenBoundarySPHSystem) +function Base.show(io::IO, system::OpenBoundarySystem) @nospecialize system # reduce precompilation time - print(io, "OpenBoundarySPHSystem{", ndims(system), "}(") - print(io, boundary_type_name(system.boundary_zone)) + print(io, "OpenBoundarySystem{", ndims(system), "}(") print(io, ") with ", nparticles(system), " particles") end -function Base.show(io::IO, ::MIME"text/plain", system::OpenBoundarySPHSystem) +function Base.show(io::IO, ::MIME"text/plain", system::OpenBoundarySystem) @nospecialize system # reduce precompilation time if get(io, :compact, false) show(io, system) else - summary_header(io, "OpenBoundarySPHSystem{$(ndims(system))}") + summary_header(io, "OpenBoundarySystem{$(ndims(system))}") summary_line(io, "#particles", nparticles(system)) summary_line(io, "#buffer_particles", system.buffer.buffer_size) + summary_line(io, "#boundary_zones", length(system.boundary_zones)) summary_line(io, "fluid system", type2string(system.fluid_system)) summary_line(io, "boundary model", type2string(system.boundary_model)) - summary_line(io, "boundary type", boundary_type_name(system.boundary_zone)) - summary_line(io, "prescribed velocity", type2string(system.reference_velocity)) - summary_line(io, "prescribed pressure", type2string(system.reference_pressure)) - summary_line(io, "prescribed density", type2string(system.reference_density)) - summary_line(io, "width", round(system.boundary_zone.zone_width, digits=3)) summary_footer(io) end end -@inline function Base.eltype(::OpenBoundarySPHSystem{<:Any, ELTYPE}) where {ELTYPE} +@inline function Base.eltype(::OpenBoundarySystem{<:Any, ELTYPE}) where {ELTYPE} return ELTYPE end -# The `UpdateCallback` is required to update particle positions between time steps -@inline requires_update_callback(system::OpenBoundarySPHSystem) = true +@inline buffer(system::OpenBoundarySystem) = system.buffer -function corresponding_fluid_system(system::OpenBoundarySPHSystem, semi) - return system.fluid_system -end +# The `UpdateCallback` is required to update particle positions between time steps +@inline requires_update_callback(system::OpenBoundarySystem) = true -function smoothing_length(system::OpenBoundarySPHSystem, particle) +function smoothing_length(system::OpenBoundarySystem, particle) return system.smoothing_length end -@inline hydrodynamic_mass(system::OpenBoundarySPHSystem, particle) = system.mass[particle] +@inline hydrodynamic_mass(system::OpenBoundarySystem, particle) = system.mass[particle] -@inline function current_density(v, system::OpenBoundarySPHSystem) +@inline function current_density(v, system::OpenBoundarySystem) return system.density end -@inline function current_pressure(v, system::OpenBoundarySPHSystem) +@inline function current_pressure(v, system::OpenBoundarySystem) return system.pressure end -@inline function set_particle_pressure!(v, system::OpenBoundarySPHSystem, particle, - pressure) +@inline function set_particle_pressure!(v, system::OpenBoundarySystem, particle, pressure) system.pressure[particle] = pressure return v end -@inline function set_particle_density!(v, system::OpenBoundarySPHSystem, particle, - density) +@inline function set_particle_density!(v, system::OpenBoundarySystem, particle, density) system.density[particle] = density return v end -function update_boundary_interpolation!(system::OpenBoundarySPHSystem, v, u, v_ode, u_ode, +function update_boundary_interpolation!(system::OpenBoundarySystem, v, u, v_ode, u_ode, semi, t) update_boundary_model!(system, system.boundary_model, v, u, v_ode, u_ode, semi, t) 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, +function update_open_boundary_eachstep!(system::OpenBoundarySystem, v_ode, u_ode, semi, t) + (; boundary_model) = system + u = wrap_u(u_ode, system, semi) v = wrap_v(v_ode, system, semi) @trixi_timeit timer() "check domain" check_domain!(system, v, u, v_ode, u_ode, 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 boundary quantities" update_boundary_quantities!(system, - system.boundary_model, - v, u, - v_ode, - u_ode, - semi, t) + # Update density, pressure and velocity based on the specific boundary model + @trixi_timeit timer() "update boundary quantities" begin + update_boundary_quantities!(system, boundary_model, v, u, v_ode, u_ode, semi, t) + end return system end @@ -283,8 +222,7 @@ 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, boundary_candidates, fluid_candidates) = system - fluid_system = corresponding_fluid_system(system, semi) + (; boundary_zones, boundary_candidates, fluid_candidates, fluid_system) = system u_fluid = wrap_u(u_ode, fluid_system, semi) v_fluid = wrap_v(v_ode, fluid_system, semi) @@ -292,10 +230,11 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) boundary_candidates .= false # Check the boundary particles whether they're leaving the boundary zone - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) particle_coords = current_coords(u, system, particle) # Check if boundary particle is outside the boundary zone + boundary_zone = current_boundary_zone(system, particle) if !is_in_boundary_zone(boundary_zone, particle_coords) boundary_candidates[particle] = true end @@ -311,6 +250,7 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) particle = crossed_boundary_particles[i] particle_new = available_fluid_particles[i] + boundary_zone = current_boundary_zone(system, particle) convert_particle!(system, fluid_system, boundary_zone, particle, particle_new, v, u, v_fluid, u_fluid) end @@ -321,12 +261,14 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) fluid_candidates .= false # Check the fluid particles whether they're entering the boundary zone - @threaded semi for fluid_particle in each_moving_particle(fluid_system) + @threaded semi for fluid_particle in each_integrated_particle(fluid_system) fluid_coords = current_coords(u_fluid, fluid_system, fluid_particle) - # Check if fluid particle is in boundary zone - if is_in_boundary_zone(boundary_zone, fluid_coords) - fluid_candidates[fluid_particle] = true + # Check if fluid particle is in any boundary zone + for boundary_zone in boundary_zones + if is_in_boundary_zone(boundary_zone, fluid_coords) + fluid_candidates[fluid_particle] = true + end end end @@ -340,7 +282,7 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) particle = crossed_fluid_particles[i] particle_new = available_boundary_particles[i] - convert_particle!(fluid_system, system, boundary_zone, particle, particle_new, + convert_particle!(fluid_system, system, particle, particle_new, v, u, v_fluid, u_fluid) end @@ -350,44 +292,20 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) # Since particles have been transferred, the neighborhood searches must be updated update_nhs!(semi, u_ode) - return system -end - -# Outflow particle is outside the boundary zone -@inline function convert_particle!(system::OpenBoundarySPHSystem, fluid_system, - boundary_zone::BoundaryZone{OutFlow}, particle, - particle_new, 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::BoundaryZone{InFlow}, particle, - particle_new, v, u, v_fluid, u_fluid) - (; spanning_set) = boundary_zone - - # Activate a new particle in simulation domain - transfer_particle!(fluid_system, system, particle, particle_new, 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 + update_boundary_zone_indices!(system, u, boundary_zones, semi) return system end # Buffer particle is outside the boundary zone -@inline function convert_particle!(system::OpenBoundarySPHSystem, fluid_system, - boundary_zone::BoundaryZone{BidirectionalFlow}, - particle, particle_new, v, u, v_fluid, u_fluid) +@inline function convert_particle!(system::OpenBoundarySystem, fluid_system, + boundary_zone, particle, particle_new, + v, u, v_fluid, u_fluid) relative_position = current_coords(u, system, particle) - boundary_zone.zone_origin # Check if particle is in- or outside the fluid domain. - # `plane_normal` is always pointing into the fluid domain. - if signbit(dot(relative_position, boundary_zone.plane_normal)) + # `face_normal` is always pointing into the fluid domain. + if signbit(dot(relative_position, boundary_zone.face_normal)) deactivate_particle!(system, particle, u) return system @@ -405,9 +323,8 @@ end end # Fluid particle is in boundary zone -@inline function convert_particle!(fluid_system::FluidSystem, system, - boundary_zone, particle, particle_new, - v, u, v_fluid, u_fluid) +@inline function convert_particle!(fluid_system::AbstractFluidSystem, system, + particle, particle_new, v, u, v_fluid, u_fluid) # Activate particle in boundary zone transfer_particle!(system, fluid_system, particle, particle_new, v, u, v_fluid, u_fluid) @@ -439,7 +356,7 @@ end return system_new end -function write_v0!(v0, system::OpenBoundarySPHSystem) +function write_v0!(v0, system::OpenBoundarySystem) # 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) @@ -447,7 +364,7 @@ function write_v0!(v0, system::OpenBoundarySPHSystem) return v0 end -function write_u0!(u0, system::OpenBoundarySPHSystem) +function write_u0!(u0, system::OpenBoundarySystem) (; initial_condition) = system # This is as fast as a loop with `@inbounds`, but it's GPU-compatible @@ -457,48 +374,22 @@ function write_u0!(u0, system::OpenBoundarySPHSystem) return u0 end -wrap_reference_function(::Nothing, ::Val) = nothing - -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 - -function reference_value(value::Function, quantity, position, t) - return value(position, t) -end - -# This method is used when extrapolating quantities from the domain -# instead of using the method of characteristics -reference_value(value::Nothing, quantity, position, t) = quantity - # To account for boundary effects in the viscosity term of the RHS, use the viscosity model # of the neighboring particle systems. -@inline function viscosity_model(system::OpenBoundarySPHSystem, - neighbor_system::FluidSystem) +@inline function viscosity_model(system::OpenBoundarySystem, + neighbor_system::AbstractFluidSystem) return neighbor_system.viscosity end -@inline function viscosity_model(system::OpenBoundarySPHSystem, - neighbor_system::BoundarySystem) +@inline function viscosity_model(system::OpenBoundarySystem, + neighbor_system::AbstractBoundarySystem) return neighbor_system.boundary_model.viscosity end # When the neighbor is an open boundary system, just use the viscosity of the fluid `system` instead -@inline viscosity_model(system, neighbor_system::OpenBoundarySPHSystem) = system.viscosity +@inline viscosity_model(system, neighbor_system::OpenBoundarySystem) = system.viscosity -function system_data(system::OpenBoundarySPHSystem, v_ode, u_ode, semi) +function system_data(system::OpenBoundarySystem, dv_ode, du_ode, v_ode, u_ode, semi) v = wrap_v(v_ode, system, semi) u = wrap_u(u_ode, system, semi) @@ -510,6 +401,6 @@ function system_data(system::OpenBoundarySPHSystem, v_ode, u_ode, semi) return (; coordinates, velocity, density, pressure) end -function available_data(::OpenBoundarySPHSystem) +function available_data(::OpenBoundarySystem) return (:coordinates, :velocity, :density, :pressure) end diff --git a/src/schemes/boundary/prescribed_motion.jl b/src/schemes/boundary/prescribed_motion.jl new file mode 100644 index 0000000000..609fed8387 --- /dev/null +++ b/src/schemes/boundary/prescribed_motion.jl @@ -0,0 +1,100 @@ +""" + PrescribedMotion(movement_function, is_moving; moving_particles=nothing) + +# Arguments +- `movement_function`: Function of `(x, t)` where `x` is an `SVector` of the *initial* + particle position and `t` is the time, returning an `SVector` + of ``d`` dimensions for a ``d``-dimensional problem containing + the new particle position at time `t`. +- `is_moving`: Function of `t` to determine in each timestep if the particles + are moving or not. Its boolean return value determines + if the neighborhood search will be updated. + +# Keyword Arguments +- `moving_particles`: Indices of moving particles. Default is each particle in the system. + +# Examples +```jldoctest; output = false +# Circular motion of particles for t < 1.5 +movement_function(x, t) = x + SVector(cos(2pi * t), sin(2pi * t)) +is_moving(t) = t < 1.5 + +motion = PrescribedMotion(movement_function, is_moving) + +# Rotation around the origin +movement_function2(x, t) = SVector(cos(2pi * t) * x[1] - sin(2pi * t) * x[2], + sin(2pi * t) * x[1] + cos(2pi * t) * x[2]) +is_moving2(t) = true + +motion2 = PrescribedMotion(movement_function2, is_moving2) + +# output +PrescribedMotion{typeof(movement_function2), typeof(is_moving2), Vector{Int64}}(movement_function2, is_moving2, Int64[]) +``` +""" +struct PrescribedMotion{MF, IM, MP} + movement_function :: MF + is_moving :: IM + moving_particles :: MP # Vector{Int} +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 PrescribedMotion(movement_function, is_moving; moving_particles=nothing) + # Default value is an empty vector, which will be resized in the `WallBoundarySystem` + # constructor to move all particles. + moving_particles = isnothing(moving_particles) ? Int[] : vec(moving_particles) + + return PrescribedMotion(movement_function, is_moving, moving_particles) +end + +function initialize!(prescribed_motion::PrescribedMotion, initial_condition) + # Test `movement_function` return type + pos = extract_svector(initial_condition.coordinates, + Val(size(initial_condition.coordinates, 1)), 1) + if !(prescribed_motion.movement_function(pos, 0.0) isa SVector) + @warn "Return value of `movement_function` is not of type `SVector`. " * + "Returning regular `Vector`s causes allocations and significant performance overhead." + end + + # Empty `moving_particles` means all particles are moving + if isempty(prescribed_motion.moving_particles) + # Default is an empty vector, since the number of particles is not known when + # instantiating `PrescribedMotion`. + resize!(prescribed_motion.moving_particles, nparticles(initial_condition)) + prescribed_motion.moving_particles .= collect(1:nparticles(initial_condition)) + end +end + +function (prescribed_motion::PrescribedMotion)(system, t, semi) + (; coordinates, cache) = system + (; movement_function, is_moving, moving_particles) = prescribed_motion + (; acceleration, velocity) = cache + + system.ismoving[] = is_moving(t) + + is_moving(t) || return system + + @threaded semi for particle in moving_particles + pos_original = initial_coords(system, particle) + pos_new = movement_function(pos_original, t) + pos_deriv(t_) = ForwardDiff.derivative(t__ -> movement_function(pos_original, t__), + t_) + vel = pos_deriv(t) + acc = ForwardDiff.derivative(pos_deriv, t) + + @inbounds for i in 1:ndims(system) + coordinates[i, particle] = pos_new[i] + velocity[i, particle] = vel[i] + acceleration[i, particle] = acc[i] + end + end + + return system +end + +function (prescribed_motion::Nothing)(system::AbstractSystem, t, semi) + system.ismoving[] = false + + return system +end diff --git a/src/schemes/boundary/system.jl b/src/schemes/boundary/system.jl deleted file mode 100644 index 1e3a526551..0000000000 --- a/src/schemes/boundary/system.jl +++ /dev/null @@ -1,480 +0,0 @@ -""" - 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. - -# Arguments -- `initial_condition`: Initial condition (see [`InitialCondition`](@ref)) -- `boundary_model`: Boundary model (see [Boundary Models](@ref boundary_models)) - -# 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, 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 - buffer :: Nothing - - # 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, 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, buffer) - end -end - -function BoundarySPHSystem(initial_condition, model; movement=nothing, - adhesion_coefficient=0.0, color_value=0) - coordinates = copy(initial_condition.coordinates) - - ismoving = Ref(!isnothing(movement)) - - cache = create_cache_boundary(movement, initial_condition) - cache = (cache..., color=Int(color_value)) - - 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 - - # Because of dispatches boundary model needs to be first! - return BoundarySPHSystem(initial_condition, coordinates, model, movement, - ismoving, adhesion_coefficient, cache, nothing) -end - -function Base.show(io::IO, system::BoundarySPHSystem) - @nospecialize system # reduce precompilation time - - print(io, "BoundarySPHSystem{", ndims(system), "}(") - print(io, system.boundary_model) - print(io, ", ", system.movement) - print(io, ", ", system.adhesion_coefficient) - print(io, ", ", system.cache.color) - print(io, ") with ", nparticles(system), " particles") -end - -function Base.show(io::IO, ::MIME"text/plain", system::BoundarySPHSystem) - @nospecialize system # reduce precompilation time - - if get(io, :compact, false) - show(io, system) - else - summary_header(io, "BoundarySPHSystem{$(ndims(system))}") - summary_line(io, "#particles", nparticles(system)) - summary_line(io, "boundary model", system.boundary_model) - summary_line(io, "movement function", - isnothing(system.movement) ? "nothing" : - string(system.movement.movement_function)) - summary_line(io, "adhesion coefficient", system.adhesion_coefficient) - summary_line(io, "color", system.cache.color) - summary_footer(io) - 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, IC, - ARRAY1D, ARRAY2D} <: BoundarySystem{NDIMS} - initial_condition :: IC - coordinates :: ARRAY2D # [dimension, particle] - radius :: ARRAY1D # [particle] - normal_stiffness :: ELTYPE - buffer :: Nothing - - 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(initial_condition), typeof(radius), - typeof(coordinates)}(initial_condition, coordinates, radius, - normal_stiffness, nothing) - end -end - -function Base.show(io::IO, system::BoundaryDEMSystem) - @nospecialize system # reduce precompilation time - - print(io, "BoundaryDEMSystem{", ndims(system), "}(") - 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) - -# Arguments -- `movement_function`: Time-dependent function returning an `SVector` of ``d`` dimensions - for a ``d``-dimensional problem. -- `is_moving`: Function to determine in each timestep if the particles are moving or not. Its - boolean return value is mandatory to determine if the neighborhood search will be updated. - -# Keyword Arguments -- `moving_particles`: Indices of moving particles. Default is each particle in [`BoundarySPHSystem`](@ref). - -In the example below, `movement` describes particles moving in a circle as long as -the time is lower than `1.5`. - -# Examples -```jldoctest; output = false -movement_function(t) = SVector(cos(2pi*t), sin(2pi*t)) -is_moving(t) = t < 1.5 - -movement = BoundaryMovement(movement_function, is_moving) - -# output -BoundaryMovement{typeof(movement_function), typeof(is_moving), Vector{Int64}}(movement_function, is_moving, Int64[]) -``` -""" -struct BoundaryMovement{MF, IM, MP} - movement_function :: MF - is_moving :: IM - moving_particles :: MP # Vector{Int} -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 BoundaryMovement(movement_function, is_moving; moving_particles=nothing) - if !(movement_function(0.0) isa SVector) - @warn "Return value of `movement_function` is not of type `SVector`. " * - "Returning regular `Vector`s causes allocations and significant performance overhead." - end - - # Default value is an empty vector, which will be resized in the `BoundarySPHSystem` - # constructor to move all particles. - moving_particles = isnothing(moving_particles) ? Int[] : vec(moving_particles) - - return BoundaryMovement(movement_function, is_moving, moving_particles) -end - -create_cache_boundary(::Nothing, initial_condition) = (;) - -function create_cache_boundary(::BoundaryMovement, initial_condition) - initial_coordinates = copy(initial_condition.coordinates) - velocity = zero(initial_condition.velocity) - acceleration = zero(initial_condition.velocity) - return (; velocity, acceleration, initial_coordinates) -end - -timer_name(::Union{BoundarySPHSystem, BoundaryDEMSystem}) = "boundary" - -@inline function Base.eltype(system::Union{BoundarySPHSystem, BoundaryDEMSystem}) - eltype(system.coordinates) -end - -@inline function initial_coordinates(system::BoundarySPHSystem) - initial_coordinates(system::BoundarySPHSystem, system.movement) -end - -@inline initial_coordinates(system::BoundaryDEMSystem) = system.coordinates - -@inline initial_coordinates(system::BoundarySPHSystem, ::Nothing) = system.coordinates - -# We need static initial coordinates as reference when system is moving -@inline function initial_coordinates(system::BoundarySPHSystem, movement) - return system.cache.initial_coordinates -end - -function (movement::BoundaryMovement)(system, t, semi) - (; coordinates, cache) = system - (; movement_function, is_moving, moving_particles) = movement - (; acceleration, velocity) = cache - - system.ismoving[] = is_moving(t) - - is_moving(t) || return system - - @threaded semi 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) - - @inbounds for i in 1:ndims(system) - coordinates[i, particle] = pos_new[i] - velocity[i, particle] = vel[i] - acceleration[i, particle] = acc[i] - end - end - - return system -end - -function (movement::Nothing)(system::System, t, semi) - system.ismoving[] = false - - return system -end - -@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::Union{BoundarySPHSystem, BoundaryDEMSystem}) - return 0 -end - -@inline function n_moving_particles(system::BoundarySPHSystem{<:BoundaryModelDummyParticles{ContinuityDensity}}) - return nparticles(system) -end - -@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::Union{BoundarySPHSystem, BoundaryDEMSystem}) - return system.coordinates -end - -@inline function current_velocity(v, system::BoundaryDEMSystem, particle) - return zero(SVector{ndims(system), eltype(system)}) -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 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_velocity(v, system::BoundarySPHSystem) - error("`current_velocity(v, system)` is not implemented for `BoundarySPHSystem`") -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 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) - return viscous_velocity(v, system.boundary_model.viscosity, system, particle) -end - -@inline function viscous_velocity(v, viscosity, system, particle) - return extract_svector(system.boundary_model.cache.wall_velocity, system, particle) -end - -@inline function viscous_velocity(v, viscosity::Nothing, system, particle) - return current_velocity(v, system, particle) -end - -@inline function current_density(v, system::BoundarySPHSystem) - return current_density(v, system.boundary_model, system) -end - -@inline function current_pressure(v, system::BoundarySPHSystem) - return current_pressure(v, system.boundary_model, system) -end - -@inline function hydrodynamic_mass(system::BoundarySPHSystem, particle) - return system.boundary_model.hydrodynamic_mass[particle] -end - -@inline function smoothing_kernel(system::BoundarySPHSystem, distance, particle) - (; smoothing_kernel, smoothing_length) = system.boundary_model - return kernel(smoothing_kernel, distance, smoothing_length) -end - -@inline function smoothing_length(system::BoundarySPHSystem, particle) - return smoothing_length(system.boundary_model, particle) -end - -function update_positions!(system::BoundarySPHSystem, v, u, v_ode, u_ode, semi, t) - (; movement) = system - - movement(system, t, semi) -end - -function update_quantities!(system::BoundarySPHSystem, v, u, v_ode, u_ode, semi, t) - (; boundary_model) = system - - update_density!(boundary_model, system, v, u, v_ode, u_ode, semi) - - return system -end - -# This update depends on the computed quantities of the fluid system and therefore -# has to be in `update_boundary_interpolation!` after `update_quantities!`. -function update_boundary_interpolation!(system::BoundarySPHSystem, v, u, v_ode, u_ode, - semi, t) - (; boundary_model) = system - - # Note that `update_pressure!(::BoundarySPHSystem, ...)` is empty, - # so no pressure is updated in the previous update steps. - update_pressure!(boundary_model, system, v, u, v_ode, u_ode, semi) - - return system -end - -function write_u0!(u0, system::Union{BoundarySPHSystem, BoundaryDEMSystem}) - return u0 -end - -function write_v0!(v0, - system::Union{BoundarySPHSystem, - BoundaryDEMSystem}) - return v0 -end - -function write_v0!(v0, - system::BoundarySPHSystem{<:BoundaryModelDummyParticles{ContinuityDensity}}) - (; cache) = system.boundary_model - (; initial_density) = cache - - v0[1, :] = initial_density - - return v0 -end - -function restart_with!(system::BoundarySPHSystem, v, u) - return system -end - -function restart_with!(system::BoundarySPHSystem{<:BoundaryModelDummyParticles{ContinuityDensity}}, - v, u) - (; initial_density) = model.cache - - for particle in eachparticle(system) - initial_density[particle] = v[1, particle] - end - - return system -end - -# To incorporate the effect at boundaries in the viscosity term of the RHS the neighbor -# viscosity model has to be used. -@inline function viscosity_model(system::BoundarySPHSystem, - neighbor_system::FluidSystem) - return neighbor_system.viscosity -end - -function calculate_dt(v_ode, u_ode, cfl_number, system::BoundarySystem, semi) - return Inf -end - -function initialize!(system::BoundarySPHSystem, semi) - initialize_colorfield!(system, system.boundary_model, semi) - return system -end - -function initialize_colorfield!(system, boundary_model, semi) - return system -end - -function initialize_colorfield!(system, ::BoundaryModelDummyParticles, semi) - system_coords = system.coordinates - (; smoothing_kernel, smoothing_length, cache) = system.boundary_model - - if haskey(cache, :initial_colorfield) - foreach_point_neighbor(system, system, system_coords, system_coords, semi, - points=eachparticle(system)) do particle, neighbor, - pos_diff, distance - cache.initial_colorfield[particle] += system.initial_condition.mass[particle] / - system.initial_condition.density[particle] * - system.cache.color * - kernel(smoothing_kernel, - distance, - smoothing_length) - cache.neighbor_count[particle] += 1 - end - end - return system -end - -function system_smoothing_kernel(system::BoundarySPHSystem{<:BoundaryModelDummyParticles}) - return system.boundary_model.smoothing_kernel -end - -function system_correction(system::BoundarySPHSystem{<:BoundaryModelDummyParticles}) - return system.boundary_model.correction -end - -function system_data(system::BoundarySPHSystem, v_ode, u_ode, semi) - v = wrap_v(v_ode, system, semi) - u = wrap_u(u_ode, system, semi) - - coordinates = current_coordinates(u, system) - velocity = current_velocity(v, system) - density = current_density(v, system) - pressure = current_pressure(v, system) - - return (; coordinates, velocity, density, pressure) -end - -function available_data(::BoundarySPHSystem) - return (:coordinates, :velocity, :density, :pressure) -end - -function system_data(system::BoundaryDEMSystem, v_ode, u_ode, semi) - (; coordinates, radius, normal_stiffness) = system - - return (; coordinates, radius, normal_stiffness) -end - -function available_data(::BoundaryDEMSystem) - return (:coordinates, :radius, :normal_stiffness) -end diff --git a/src/schemes/boundary/dummy_particles/dummy_particles.jl b/src/schemes/boundary/wall_boundary/dummy_particles.jl similarity index 97% rename from src/schemes/boundary/dummy_particles/dummy_particles.jl rename to src/schemes/boundary/wall_boundary/dummy_particles.jl index f0051bdb3a..1ed34e8883 100644 --- a/src/schemes/boundary/dummy_particles/dummy_particles.jl +++ b/src/schemes/boundary/wall_boundary/dummy_particles.jl @@ -5,7 +5,7 @@ state_equation=nothing, correction=nothing, reference_particle_spacing=0.0) -Boundary model for `BoundarySPHSystem`. +Boundary model for [`WallBoundarySystem`](@ref). # Arguments - `initial_density`: Vector holding the initial density of each boundary particle. @@ -84,6 +84,8 @@ function BoundaryModelDummyParticles(initial_density, hydrodynamic_mass, smoothing_length, viscosity, correction, cache) end +@inline Base.ndims(boundary_model::BoundaryModelDummyParticles) = ndims(boundary_model.smoothing_kernel) + @doc raw""" AdamiPressureExtrapolation(; pressure_offset=0, allow_loop_flipping=true) @@ -469,7 +471,8 @@ end end @inline function boundary_pressure_extrapolation!(parallel::Val{true}, boundary_model, - system, neighbor_system::FluidSystem, + system, + neighbor_system::AbstractFluidSystem, system_coords, neighbor_coords, v, v_neighbor_system, semi) (; pressure, cache, viscosity, density_calculator) = boundary_model @@ -491,7 +494,8 @@ end # Note that this needs to be serial, as we are writing into the same # pressure entry from different loop iterations. @inline function boundary_pressure_extrapolation!(parallel::Val{false}, boundary_model, - system, neighbor_system::FluidSystem, + system, + neighbor_system::AbstractFluidSystem, system_coords, neighbor_coords, v, v_neighbor_system, semi) (; pressure, cache, viscosity, density_calculator) = boundary_model @@ -499,7 +503,7 @@ end # This needs to be serial to avoid race conditions when writing into `system` foreach_point_neighbor(neighbor_system, system, neighbor_coords, system_coords, semi; - points=each_moving_particle(neighbor_system), + points=each_integrated_particle(neighbor_system), parallelization_backend=SerialBackend()) do neighbor, particle, pos_diff, distance # Since neighbor and particle are switched @@ -512,7 +516,7 @@ end end @inline function boundary_pressure_inner!(boundary_model, boundary_density_calculator, - system, neighbor_system::FluidSystem, v, + system, neighbor_system::AbstractFluidSystem, v, v_neighbor_system, particle, neighbor, pos_diff, distance, viscosity, cache, pressure, pressure_offset) @@ -554,7 +558,7 @@ end @inline function dynamic_pressure(boundary_density_calculator::BernoulliPressureExtrapolation, density_neighbor, v, v_neighbor_system, pos_diff, distance, particle, neighbor, - system::BoundarySystem, neighbor_system) + system::AbstractBoundarySystem, neighbor_system) if system.ismoving[] relative_velocity = current_velocity(v, system, particle) .- current_velocity(v_neighbor_system, neighbor_system, neighbor) @@ -569,7 +573,7 @@ end @inline function dynamic_pressure(boundary_density_calculator::BernoulliPressureExtrapolation, density_neighbor, v, v_neighbor_system, pos_diff, distance, particle, neighbor, - system::SolidSystem, neighbor_system) + system::AbstractStructureSystem, neighbor_system) relative_velocity = current_velocity(v, system, particle) .- current_velocity(v_neighbor_system, neighbor_system, neighbor) normal_velocity = dot(relative_velocity, pos_diff) / distance @@ -627,6 +631,6 @@ end return density end -@inline function correction_matrix(system::BoundarySystem, particle) +@inline function correction_matrix(system::AbstractBoundarySystem, particle) extract_smatrix(system.boundary_model.cache.correction_matrix, system, particle) end diff --git a/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl b/src/schemes/boundary/wall_boundary/monaghan_kajtar.jl similarity index 93% rename from src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl rename to src/schemes/boundary/wall_boundary/monaghan_kajtar.jl index 6fcdf7ec37..1df9a38cf9 100644 --- a/src/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl +++ b/src/schemes/boundary/wall_boundary/monaghan_kajtar.jl @@ -2,7 +2,7 @@ BoundaryModelMonaghanKajtar(K, beta, boundary_particle_spacing, mass; viscosity=nothing) -Boundary model for `BoundarySPHSystem`. +Boundary model for [`WallBoundarySystem`](@ref). # Arguments - `K`: Scaling factor for repulsive force. @@ -44,7 +44,7 @@ function Base.show(io::IO, model::BoundaryModelMonaghanKajtar) end @inline function pressure_acceleration(particle_system, - neighbor_system::Union{BoundarySPHSystem{<:BoundaryModelMonaghanKajtar}, + neighbor_system::Union{WallBoundarySystem{<:BoundaryModelMonaghanKajtar}, TotalLagrangianSPHSystem{<:BoundaryModelMonaghanKajtar}}, particle, neighbor, m_a, m_b, p_a, p_b, rho_a, rho_b, pos_diff, distance, grad_kernel, correction) @@ -80,7 +80,7 @@ end end @inline function current_density(v, - system::Union{BoundarySPHSystem{<:BoundaryModelMonaghanKajtar}, + system::Union{WallBoundarySystem{<:BoundaryModelMonaghanKajtar}, TotalLagrangianSPHSystem{<:BoundaryModelMonaghanKajtar}}, particle) (; hydrodynamic_mass, boundary_particle_spacing) = system.boundary_model @@ -97,7 +97,7 @@ end # This model does not not use any particle pressure @inline function current_pressure(v, - system::Union{BoundarySPHSystem{<:BoundaryModelMonaghanKajtar}, + system::Union{WallBoundarySystem{<:BoundaryModelMonaghanKajtar}, TotalLagrangianSPHSystem{<:BoundaryModelMonaghanKajtar}}, particle) return zero(eltype(v)) diff --git a/src/schemes/boundary/rhs.jl b/src/schemes/boundary/wall_boundary/rhs.jl similarity index 90% rename from src/schemes/boundary/rhs.jl rename to src/schemes/boundary/wall_boundary/rhs.jl index 2e56d637e0..a5998f00a6 100644 --- a/src/schemes/boundary/rhs.jl +++ b/src/schemes/boundary/wall_boundary/rhs.jl @@ -1,7 +1,7 @@ # Interaction of boundary with other systems function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, - particle_system::Union{BoundarySystem, OpenBoundarySPHSystem}, + particle_system::Union{AbstractBoundarySystem, OpenBoundarySystem}, neighbor_system, semi) # TODO Solids and moving boundaries should be considered in the continuity equation return dv @@ -10,8 +10,8 @@ end # For dummy particles with `ContinuityDensity`, solve the continuity equation function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, - particle_system::BoundarySPHSystem{<:BoundaryModelDummyParticles{ContinuityDensity}}, - neighbor_system::FluidSystem, semi) + particle_system::WallBoundarySystem{<:BoundaryModelDummyParticles{ContinuityDensity}}, + neighbor_system::AbstractFluidSystem, semi) (; boundary_model) = particle_system fluid_density_calculator = neighbor_system.density_calculator diff --git a/src/schemes/boundary/wall_boundary/system.jl b/src/schemes/boundary/wall_boundary/system.jl new file mode 100644 index 0000000000..17466c4e60 --- /dev/null +++ b/src/schemes/boundary/wall_boundary/system.jl @@ -0,0 +1,329 @@ +""" + WallBoundarySystem(initial_condition, boundary_model; + prescribed_motion=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. + +# Arguments +- `initial_condition`: Initial condition (see [`InitialCondition`](@ref)) +- `boundary_model`: Boundary model (see [Boundary Models](@ref boundary_models)) + +# Keyword Arguments +- `prescribed_motion`: For moving boundaries, a [`PrescribedMotion`](@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 WallBoundarySystem{BM, NDIMS, ELTYPE <: Real, IC, CO, M, IM, + CA} <: AbstractBoundarySystem{NDIMS} + initial_condition :: IC + coordinates :: CO # Array{ELTYPE, 2} + boundary_model :: BM + prescribed_motion :: 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 WallBoundarySystem(initial_condition, coordinates, boundary_model, + prescribed_motion, ismoving, adhesion_coefficient, cache) + ELTYPE = eltype(coordinates) + + new{typeof(boundary_model), size(coordinates, 1), ELTYPE, typeof(initial_condition), + typeof(coordinates), typeof(prescribed_motion), typeof(ismoving), + typeof(cache)}(initial_condition, coordinates, boundary_model, + prescribed_motion, ismoving, adhesion_coefficient, cache) + end +end + +function WallBoundarySystem(initial_condition, model; prescribed_motion=nothing, + adhesion_coefficient=0.0, color_value=0) + coordinates = copy(initial_condition.coordinates) + + ismoving = Ref(!isnothing(prescribed_motion)) + initialize!(prescribed_motion, initial_condition) + + cache = create_cache_boundary(prescribed_motion, initial_condition) + cache = (cache..., color=Int(color_value)) + + # Because of dispatches boundary model needs to be first! + return WallBoundarySystem(initial_condition, coordinates, model, prescribed_motion, + ismoving, adhesion_coefficient, cache) +end + +create_cache_boundary(::Nothing, initial_condition) = (;) + +function create_cache_boundary(prescribed_motion::PrescribedMotion, initial_condition) + initial_coordinates = copy(initial_condition.coordinates) + velocity = zero(initial_condition.velocity) + acceleration = zero(initial_condition.velocity) + + return (; velocity, acceleration, initial_coordinates) +end + +@inline function Base.eltype(system::WallBoundarySystem) + eltype(system.coordinates) +end + +@inline function nparticles(system::WallBoundarySystem) + size(system.coordinates, 2) +end + +# No particle positions are advanced for wall boundary systems, +# except when using `BoundaryModelDummyParticles` with `ContinuityDensity`. +@inline function n_integrated_particles(system::WallBoundarySystem) + return 0 +end + +@inline function n_integrated_particles(system::WallBoundarySystem{<:BoundaryModelDummyParticles{ContinuityDensity}}) + return nparticles(system) +end + +@inline u_nvariables(system::WallBoundarySystem) = 0 + +# For BoundaryModelDummyParticles with ContinuityDensity, this needs to be 1. +# For all other models and density calculators, it's irrelevant. +@inline v_nvariables(system::WallBoundarySystem) = 1 + +@inline function initial_coordinates(system::WallBoundarySystem) + initial_coordinates(system::WallBoundarySystem, system.prescribed_motion) +end + +@inline initial_coordinates(system::WallBoundarySystem, ::Nothing) = system.coordinates + +# We need static initial coordinates as reference when system is moving +@inline function initial_coordinates(system::WallBoundarySystem, prescribed_motion) + return system.cache.initial_coordinates +end + +@inline function current_coordinates(u, system::WallBoundarySystem) + return system.coordinates +end + +@inline function current_velocity(v, system::WallBoundarySystem, particle) + return current_velocity(v, system, system.prescribed_motion, particle) +end + +@inline function current_velocity(v, system, movement, particle) + (; cache, ismoving) = system + + if ismoving[] + return extract_svector(cache.velocity, system, particle) + end + + 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_velocity(v, system::WallBoundarySystem) + error("`current_velocity(v, system)` is not implemented for `WallBoundarySystem`") +end + +@inline function current_acceleration(system::WallBoundarySystem, particle) + return current_acceleration(system, system.prescribed_motion, particle) +end + +@inline function current_acceleration(system, ::PrescribedMotion, particle) + (; cache, ismoving) = system + + if ismoving[] + return extract_svector(cache.acceleration, system, particle) + end + + return zero(SVector{ndims(system), eltype(system)}) +end + +@inline function current_acceleration(system, ::Nothing, particle) + return zero(SVector{ndims(system), eltype(system)}) +end + +@inline function viscous_velocity(v, system::WallBoundarySystem, particle) + return viscous_velocity(v, system.boundary_model.viscosity, system, particle) +end + +@inline function viscous_velocity(v, viscosity, system::WallBoundarySystem, particle) + return extract_svector(system.boundary_model.cache.wall_velocity, system, particle) +end + +@inline function viscous_velocity(v, ::Nothing, system::WallBoundarySystem, particle) + return current_velocity(v, system, particle) +end + +@inline function current_density(v, system::WallBoundarySystem) + return current_density(v, system.boundary_model, system) +end + +@inline function current_pressure(v, system::WallBoundarySystem) + return current_pressure(v, system.boundary_model, system) +end + +@inline function hydrodynamic_mass(system::WallBoundarySystem, particle) + return system.boundary_model.hydrodynamic_mass[particle] +end + +@inline function smoothing_kernel(system::WallBoundarySystem, distance, particle) + (; smoothing_kernel, smoothing_length) = system.boundary_model + return kernel(smoothing_kernel, distance, smoothing_length) +end + +@inline function smoothing_length(system::WallBoundarySystem, particle) + return smoothing_length(system.boundary_model, particle) +end + +function update_positions!(system::WallBoundarySystem, v, u, v_ode, u_ode, semi, t) + (; prescribed_motion) = system + + prescribed_motion(system, t, semi) +end + +function update_quantities!(system::WallBoundarySystem, v, u, v_ode, u_ode, semi, t) + (; boundary_model) = system + + update_density!(boundary_model, system, v, u, v_ode, u_ode, semi) + + return system +end + +# This update depends on the computed quantities of the fluid system and therefore +# has to be in `update_boundary_interpolation!` after `update_quantities!`. +function update_boundary_interpolation!(system::WallBoundarySystem, v, u, v_ode, u_ode, + semi, t) + (; boundary_model) = system + + # Note that `update_pressure!(::WallBoundarySystem, ...)` is empty, + # so no pressure is updated in the previous update steps. + update_pressure!(boundary_model, system, v, u, v_ode, u_ode, semi) + + return system +end + +function write_u0!(u0, ::WallBoundarySystem) + return u0 +end + +function write_v0!(v0, ::WallBoundarySystem) + return v0 +end + +function write_v0!(v0, + system::WallBoundarySystem{<:BoundaryModelDummyParticles{ContinuityDensity}}) + (; cache) = system.boundary_model + (; initial_density) = cache + + v0[1, :] = initial_density + + return v0 +end + +function restart_with!(system::WallBoundarySystem, v, u) + return system +end + +function restart_with!(system::WallBoundarySystem{<:BoundaryModelDummyParticles{ContinuityDensity}}, + v, u) + (; initial_density) = model.cache + + for particle in eachparticle(system) + initial_density[particle] = v[1, particle] + end + + return system +end + +# To incorporate the effect at boundaries in the viscosity term of the RHS, the neighbor +# viscosity model has to be used. +@inline function viscosity_model(system::WallBoundarySystem, + neighbor_system::AbstractFluidSystem) + return neighbor_system.viscosity +end + +function calculate_dt(v_ode, u_ode, cfl_number, system::AbstractBoundarySystem, semi) + return Inf +end + +function initialize!(system::WallBoundarySystem, semi) + initialize_colorfield!(system, system.boundary_model, semi) + return system +end + +function initialize_colorfield!(system, boundary_model, semi) + return system +end + +function initialize_colorfield!(system, ::BoundaryModelDummyParticles, semi) + system_coords = system.coordinates + (; smoothing_kernel, smoothing_length, cache) = system.boundary_model + + if haskey(cache, :initial_colorfield) + foreach_point_neighbor(system, system, system_coords, system_coords, semi, + points=eachparticle(system)) do particle, neighbor, + pos_diff, distance + cache.initial_colorfield[particle] += system.initial_condition.mass[particle] / + system.initial_condition.density[particle] * + system.cache.color * + kernel(smoothing_kernel, + distance, + smoothing_length) + cache.neighbor_count[particle] += 1 + end + end + return system +end + +function system_smoothing_kernel(system::WallBoundarySystem{<:BoundaryModelDummyParticles}) + return system.boundary_model.smoothing_kernel +end + +function system_correction(system::WallBoundarySystem{<:BoundaryModelDummyParticles}) + return system.boundary_model.correction +end + +function system_data(system::WallBoundarySystem, dv_ode, du_ode, v_ode, u_ode, semi) + dv = [current_acceleration(system, particle) for particle in eachparticle(system)] + v = wrap_v(v_ode, system, semi) + u = wrap_u(u_ode, system, semi) + + coordinates = current_coordinates(u, system) + velocity = [current_velocity(v, system, particle) for particle in eachparticle(system)] + density = current_density(v, system) + pressure = current_pressure(v, system) + + return (; coordinates, velocity, density, pressure, acceleration=dv) +end + +function available_data(::WallBoundarySystem) + return (:coordinates, :velocity, :density, :pressure, :acceleration) +end + +function Base.show(io::IO, system::WallBoundarySystem) + @nospecialize system # reduce precompilation time + + print(io, "WallBoundarySystem{", ndims(system), "}(") + print(io, system.boundary_model) + print(io, ", ", system.prescribed_motion) + print(io, ", ", system.adhesion_coefficient) + print(io, ", ", system.cache.color) + print(io, ") with ", nparticles(system), " particles") +end + +function Base.show(io::IO, ::MIME"text/plain", system::WallBoundarySystem) + @nospecialize system # reduce precompilation time + + if get(io, :compact, false) + show(io, system) + else + summary_header(io, "WallBoundarySystem{$(ndims(system))}") + summary_line(io, "#particles", nparticles(system)) + summary_line(io, "boundary model", system.boundary_model) + summary_line(io, "movement function", + isnothing(system.prescribed_motion) ? "nothing" : + string(system.prescribed_motion.movement_function)) + summary_line(io, "adhesion coefficient", system.adhesion_coefficient) + summary_line(io, "color", system.cache.color) + summary_footer(io) + end +end diff --git a/src/schemes/boundary/wall_boundary/wall_boundary.jl b/src/schemes/boundary/wall_boundary/wall_boundary.jl new file mode 100644 index 0000000000..f20a5dbf20 --- /dev/null +++ b/src/schemes/boundary/wall_boundary/wall_boundary.jl @@ -0,0 +1,4 @@ +include("dummy_particles.jl") +include("system.jl") +# Monaghan-Kajtar repulsive boundary particles require the `WallBoundarySystem` +# and the `TotalLagrangianSPHSystem` and are therefore included later. diff --git a/src/schemes/fluid/entropically_damped_sph/rhs.jl b/src/schemes/fluid/entropically_damped_sph/rhs.jl index 08139e2ca6..f3e44d927a 100644 --- a/src/schemes/fluid/entropically_damped_sph/rhs.jl +++ b/src/schemes/fluid/entropically_damped_sph/rhs.jl @@ -3,7 +3,7 @@ function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, particle_system::EntropicallyDampedSPHSystem, neighbor_system, semi) - (; sound_speed, density_calculator, correction) = particle_system + (; sound_speed, density_calculator, correction, nu_edac) = particle_system system_coords = current_coordinates(u_particle_system, particle_system) neighbor_coords = current_coordinates(u_neighbor_system, neighbor_system) @@ -11,21 +11,21 @@ function interact!(dv, v_particle_system, u_particle_system, surface_tension_a = surface_tension_model(particle_system) surface_tension_b = surface_tension_model(neighbor_system) - # Loop over all pairs of particles and neighbors within the kernel cutoff. + # Loop over all pairs of particles and neighbors within the kernel cutoff foreach_point_neighbor(particle_system, neighbor_system, system_coords, neighbor_coords, semi; - points=each_moving_particle(particle_system)) do particle, - neighbor, - pos_diff, - distance - # Only consider particles with a distance > 0. - distance < sqrt(eps()) && return - - rho_a = current_density(v_particle_system, particle_system, particle) - rho_b = current_density(v_neighbor_system, neighbor_system, neighbor) - - p_a = current_pressure(v_particle_system, particle_system, particle) - p_b = current_pressure(v_neighbor_system, neighbor_system, neighbor) + points=each_integrated_particle(particle_system)) do particle, + neighbor, + pos_diff, + distance + # `foreach_point_neighbor` makes sure that `particle` and `neighbor` are + # in bounds of the respective system. For performance reasons, we use `@inbounds` + # in this hot loop to avoid bounds checking when extracting particle quantities. + rho_a = @inbounds current_density(v_particle_system, particle_system, particle) + rho_b = @inbounds current_density(v_neighbor_system, neighbor_system, neighbor) + + p_a = @inbounds current_pressure(v_particle_system, particle_system, particle) + p_b = @inbounds current_pressure(v_neighbor_system, neighbor_system, neighbor) # This technique by Basa et al. 2017 (10.1002/fld.1927) aims to reduce numerical # errors due to large pressures by subtracting the average pressure of neighboring @@ -33,10 +33,10 @@ function interact!(dv, v_particle_system, u_particle_system, # It results in significant improvement for EDAC, especially with TVF, # but not for WCSPH, according to Ramachandran & Puri (2019), Section 3.2. # Note that the return value is zero when not using average pressure reduction. - p_avg = average_pressure(particle_system, particle) + p_avg = @inbounds average_pressure(particle_system, particle) - m_a = hydrodynamic_mass(particle_system, particle) - m_b = hydrodynamic_mass(neighbor_system, neighbor) + m_a = @inbounds hydrodynamic_mass(particle_system, particle) + m_b = @inbounds hydrodynamic_mass(neighbor_system, neighbor) grad_kernel = smoothing_kernel_grad(particle_system, pos_diff, distance, particle) @@ -46,18 +46,18 @@ function interact!(dv, v_particle_system, u_particle_system, rho_b, pos_diff, distance, grad_kernel, correction) - 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_a, rho_b, grad_kernel) + dv_viscosity_ = @inbounds dv_viscosity(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) - # Add convection term (only when using `TransportVelocityAdami`) - dv_tvf = dv_transport_velocity(transport_velocity(particle_system), + # Extra terms in the momentum equation when using a shifting technique + dv_tvf = @inbounds dv_shifting(shifting_technique(particle_system), particle_system, neighbor_system, - particle, neighbor, v_particle_system, v_neighbor_system, - m_a, m_b, rho_a, rho_b, pos_diff, distance, - grad_kernel, correction) + particle, neighbor, m_a, m_b, rho_a, rho_b, + pos_diff, distance, grad_kernel, correction) dv_surface_tension = surface_tension_force(surface_tension_a, surface_tension_b, particle_system, neighbor_system, @@ -78,10 +78,14 @@ function interact!(dv, v_particle_system, u_particle_system, pressure_evolution!(dv, particle_system, neighbor_system, v_diff, grad_kernel, particle, neighbor, pos_diff, distance, - sound_speed, m_a, m_b, p_a, p_b, rho_a, rho_b) - - continuity_equation!(dv, density_calculator, v_diff, particle, m_b, rho_a, rho_b, - particle_system, grad_kernel) + sound_speed, m_a, m_b, p_a, p_b, rho_a, rho_b, nu_edac) + + # TODO If variable smoothing_length is used, this should use the neighbor smoothing length + # Propagate `@inbounds` to the continuity equation, which accesses particle data + @inbounds continuity_equation!(dv, density_calculator, particle_system, + neighbor_system, v_particle_system, + v_neighbor_system, particle, neighbor, + pos_diff, distance, m_b, rho_a, rho_b, grad_kernel) end return dv @@ -90,7 +94,7 @@ end @inline function pressure_evolution!(dv, particle_system, neighbor_system, v_diff, grad_kernel, particle, neighbor, pos_diff, distance, sound_speed, m_a, m_b, - p_a, p_b, rho_a, rho_b) + p_a, p_b, rho_a, rho_b, nu_edac) volume_a = m_a / rho_a volume_b = m_b / rho_b volume_term = (volume_a^2 + volume_b^2) / m_a @@ -101,8 +105,8 @@ end # This is basically the continuity equation times `sound_speed^2` artificial_eos = m_b * rho_a / rho_b * sound_speed^2 * dot(v_diff, grad_kernel) - eta_a = rho_a * particle_system.nu_edac - eta_b = rho_b * particle_system.nu_edac + eta_a = rho_a * nu_edac + eta_b = rho_b * nu_edac eta_tilde = 2 * eta_a * eta_b / (eta_a + eta_b) smoothing_length_average = (smoothing_length(particle_system, particle) + @@ -122,24 +126,8 @@ end # This is similar to density diffusion in WCSPH damping_term = volume_term * tmp * pressure_diff * dot(grad_kernel, pos_diff) - dv[end, particle] += artificial_eos + damping_term - - return dv -end - -# We need a separate method for EDAC since the density is stored in `v[end-1,:]`. -@inline function continuity_equation!(dv, density_calculator::ContinuityDensity, - vdiff, particle, m_b, rho_a, rho_b, - particle_system::EntropicallyDampedSPHSystem, - grad_kernel) - dv[end - 1, particle] += rho_a / rho_b * m_b * dot(vdiff, grad_kernel) - - return dv -end + # Pressure is stored in `v` right after the velocity + dv[ndims(particle_system) + 1, particle] += artificial_eos + damping_term -@inline function continuity_equation!(dv, density_calculator, - vdiff, particle, m_b, rho_a, rho_b, - particle_system::EntropicallyDampedSPHSystem, - grad_kernel) return dv end diff --git a/src/schemes/fluid/entropically_damped_sph/system.jl b/src/schemes/fluid/entropically_damped_sph/system.jl index b34e04506c..08518c48ad 100644 --- a/src/schemes/fluid/entropically_damped_sph/system.jl +++ b/src/schemes/fluid/entropically_damped_sph/system.jl @@ -3,7 +3,7 @@ smoothing_length, sound_speed; pressure_acceleration=inter_particle_averaged_pressure, density_calculator=SummationDensity(), - transport_velocity=nothing, + shifting_technique=nothing, alpha=0.5, viscosity=nothing, acceleration=ntuple(_ -> 0.0, NDIMS), surface_tension=nothing, surface_normal_method=nothing, buffer_size=nothing, @@ -30,12 +30,13 @@ 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)) -- `transport_velocity`: [Transport Velocity Formulation (TVF)](@ref transport_velocity_formulation). - Default is no TVF. +- `shifting_technique`: [Shifting technique](@ref shifting) or [transport velocity + formulation](@ref transport_velocity_formulation) to use + with this system. Default is no shifting. - `average_pressure_reduction`: Whether to subtract the average pressure of neighboring particles - from the local pressure (default: `true` when using TVF, `false` otherwise). + from the local pressure (default: `true` when using shifting, `false` otherwise). - `buffer_size`: Number of buffer particles. - This is needed when simulating with [`OpenBoundarySPHSystem`](@ref). + This is needed when simulating with [`OpenBoundarySystem`](@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, t)` @@ -56,7 +57,8 @@ See [Entropically Damped Artificial Compressibility for SPH](@ref edac) for more """ struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, IC, M, DC, K, V, COR, PF, TV, - AVGP, ST, SRFT, SRFN, B, PR, C} <: FluidSystem{NDIMS} + AVGP, ST, SRFT, SRFN, B, PR, + C} <: AbstractFluidSystem{NDIMS} initial_condition :: IC mass :: M # Vector{ELTYPE}: [particle] density_calculator :: DC @@ -67,7 +69,7 @@ struct EntropicallyDampedSPHSystem{NDIMS, ELTYPE <: Real, IC, M, DC, K, V, COR, acceleration :: SVector{NDIMS, ELTYPE} correction :: COR pressure_acceleration_formulation :: PF - transport_velocity :: TV + shifting_technique :: TV average_pressure_reduction :: AVGP source_terms :: ST surface_tension :: SRFT @@ -83,8 +85,8 @@ function EntropicallyDampedSPHSystem(initial_condition, smoothing_kernel, smoothing_length, sound_speed; pressure_acceleration=inter_particle_averaged_pressure, density_calculator=SummationDensity(), - transport_velocity=nothing, - average_pressure_reduction=(!isnothing(transport_velocity)), + shifting_technique=nothing, + average_pressure_reduction=(!isnothing(shifting_technique)), alpha=0.5, viscosity=nothing, acceleration=ntuple(_ -> 0.0, ndims(smoothing_kernel)), @@ -137,7 +139,7 @@ function EntropicallyDampedSPHSystem(initial_condition, smoothing_kernel, nu_edac = (alpha * smoothing_length * sound_speed) / 8 cache = (; create_cache_density(initial_condition, density_calculator)..., - create_cache_tvf(initial_condition, transport_velocity)..., + create_cache_shifting(initial_condition, shifting_technique)..., create_cache_avg_pressure_reduction(initial_condition, avg_pressure_reduction)..., create_cache_surface_normal(surface_normal_method, ELTYPE, NDIMS, @@ -161,14 +163,14 @@ function EntropicallyDampedSPHSystem(initial_condition, smoothing_kernel, EntropicallyDampedSPHSystem{NDIMS, ELTYPE, typeof(initial_condition), typeof(mass), typeof(density_calculator), typeof(smoothing_kernel), typeof(viscosity), typeof(correction), - typeof(pressure_acceleration), typeof(transport_velocity), + typeof(pressure_acceleration), typeof(shifting_technique), typeof(avg_pressure_reduction), typeof(source_terms), typeof(surface_tension), typeof(surface_normal_method), typeof(buffer), Nothing, typeof(cache)}(initial_condition, mass, density_calculator, smoothing_kernel, sound_speed, viscosity, nu_edac, acceleration_, correction, - pressure_acceleration, transport_velocity, + pressure_acceleration, shifting_technique, avg_pressure_reduction, source_terms, surface_tension, surface_normal_method, buffer, @@ -218,8 +220,7 @@ function Base.show(io::IO, ::MIME"text/plain", system::EntropicallyDampedSPHSyst summary_line(io, "viscosity", system.viscosity |> typeof |> nameof) summary_line(io, "ν₍EDAC₎", "≈ $(round(system.nu_edac; digits=3))") summary_line(io, "smoothing kernel", system.smoothing_kernel |> typeof |> nameof) - summary_line(io, "tansport velocity formulation", - system.transport_velocity |> typeof |> nameof) + summary_line(io, "shifting technique", system.shifting_technique) summary_line(io, "average pressure reduction", typeof(system.average_pressure_reduction).parameters[1] ? "yes" : "no") summary_line(io, "acceleration", system.acceleration) @@ -245,11 +246,9 @@ end return ndims(system) + 2 end -system_correction(system::EntropicallyDampedSPHSystem) = system.correction +@inline buffer(system::EntropicallyDampedSPHSystem) = system.buffer -@inline function current_pressure(v, system::EntropicallyDampedSPHSystem, particle) - return v[end, particle] -end +system_correction(system::EntropicallyDampedSPHSystem) = system.correction @inline function current_velocity(v, system::EntropicallyDampedSPHSystem) return view(v, 1:ndims(system), :) @@ -259,13 +258,13 @@ end @inline system_sound_speed(system::EntropicallyDampedSPHSystem) = system.sound_speed -@inline transport_velocity(system::EntropicallyDampedSPHSystem) = system.transport_velocity +@inline shifting_technique(system::EntropicallyDampedSPHSystem) = system.shifting_technique -@inline function average_pressure(system::EntropicallyDampedSPHSystem, particle) +@propagate_inbounds function average_pressure(system::EntropicallyDampedSPHSystem, particle) average_pressure(system, system.average_pressure_reduction, particle) end -@inline function average_pressure(system, ::Val{true}, particle) +@propagate_inbounds function average_pressure(system, ::Val{true}, particle) return system.cache.pressure_average[particle] end @@ -283,12 +282,12 @@ end @inline function current_density(v, ::ContinuityDensity, system::EntropicallyDampedSPHSystem) - # When using `ContinuityDensity`, the density is stored in the second to last row of `v` - return view(v, size(v, 1) - 1, :) + # When using `ContinuityDensity`, the density is stored in the last row of `v` + return view(v, size(v, 1), :) end -@inline function current_pressure(v, ::EntropicallyDampedSPHSystem) - return view(v, size(v, 1), :) +@inline function current_pressure(v, system::EntropicallyDampedSPHSystem) + return view(v, ndims(system) + 1, :) end function update_quantities!(system::EntropicallyDampedSPHSystem, v, u, @@ -309,7 +308,7 @@ function update_final!(system::EntropicallyDampedSPHSystem, v, u, v_ode, u_ode, compute_curvature!(system, surface_tension, v, u, v_ode, u_ode, semi, t) compute_stress_tensors!(system, surface_tension, v, u, v_ode, u_ode, semi, t) update_average_pressure!(system, system.average_pressure_reduction, v_ode, u_ode, semi) - update_tvf!(system, transport_velocity(system), v, u, v_ode, u_ode, semi, t) + update_shifting!(system, shifting_technique(system), v, u, v_ode, u_ode, semi) end # No average pressure reduction is used @@ -342,8 +341,10 @@ function update_average_pressure!(system, ::Val{true}, v_ode, u_ode, semi) # Loop over all pairs of particles and neighbors within the kernel cutoff. foreach_point_neighbor(system, neighbor_system, system_coords, neighbor_coords, semi; - points=each_moving_particle(system)) do particle, neighbor, - pos_diff, distance + points=each_integrated_particle(system)) do particle, + neighbor, + pos_diff, + distance pressure_average[particle] += current_pressure(v_neighbor_system, neighbor_system, neighbor) neighbor_counter[particle] += 1 @@ -359,21 +360,21 @@ end function write_v0!(v0, system::EntropicallyDampedSPHSystem, ::SummationDensity) # Note that `.=` is very slightly faster, but not GPU-compatible - v0[end, :] = system.initial_condition.pressure + v0[ndims(system) + 1, :] = system.initial_condition.pressure return v0 end function write_v0!(v0, system::EntropicallyDampedSPHSystem, ::ContinuityDensity) # Note that `.=` is very slightly faster, but not GPU-compatible - v0[end - 1, :] = system.initial_condition.density - v0[end, :] = system.initial_condition.pressure + v0[end, :] = system.initial_condition.density + v0[ndims(system) + 1, :] = system.initial_condition.pressure return v0 end function restart_with!(system::EntropicallyDampedSPHSystem, v, u) - for particle in each_moving_particle(system) + for particle in each_integrated_particle(system) system.initial_condition.coordinates[:, particle] .= u[:, particle] system.initial_condition.velocity[:, particle] .= v[1:ndims(system), particle] system.initial_condition.pressure[particle] = v[end, particle] diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index 47e05dcf66..a787a69841 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -3,7 +3,7 @@ # 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_density!(v, system::FluidSystem, particle, density) +@inline function set_particle_density!(v, system::AbstractFluidSystem, particle, density) current_density(v, system)[particle] = density return v @@ -14,7 +14,7 @@ end # 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::FluidSystem, particle, pressure) +@inline function set_particle_pressure!(v, system::AbstractFluidSystem, particle, pressure) current_pressure(v, system)[particle] = pressure return v @@ -41,17 +41,19 @@ function create_cache_refinement(initial_condition, refinement, smoothing_length # TODO: If refinement is not `Nothing` and `correction` is not `Nothing`, then throw an error end -@propagate_inbounds hydrodynamic_mass(system::FluidSystem, particle) = system.mass[particle] +@propagate_inbounds function hydrodynamic_mass(system::AbstractFluidSystem, particle) + return system.mass[particle] +end -function smoothing_length(system::FluidSystem, particle) +function smoothing_length(system::AbstractFluidSystem, particle) return smoothing_length(system, system.particle_refinement, particle) end -function smoothing_length(system::FluidSystem, ::Nothing, particle) +function smoothing_length(system::AbstractFluidSystem, ::Nothing, particle) return system.cache.smoothing_length end -function initial_smoothing_length(system::FluidSystem) +function initial_smoothing_length(system::AbstractFluidSystem) return initial_smoothing_length(system, system.particle_refinement) end @@ -63,7 +65,7 @@ function initial_smoothing_length(system, refinement) system.initial_condition.particle_spacing end -@inline function particle_spacing(system::FluidSystem, particle) +@inline function particle_spacing(system::AbstractFluidSystem, particle) return particle_spacing(system, system.particle_refinement, particle) end @@ -74,7 +76,7 @@ end return smoothing_length(system, particle) / smoothing_length_factor end -function write_u0!(u0, system::FluidSystem) +function write_u0!(u0, system::AbstractFluidSystem) (; initial_condition) = system # This is as fast as a loop with `@inbounds`, but it's GPU-compatible @@ -84,7 +86,7 @@ function write_u0!(u0, system::FluidSystem) return u0 end -function write_v0!(v0, system::FluidSystem) +function write_v0!(v0, system::AbstractFluidSystem) # 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) @@ -94,20 +96,22 @@ function write_v0!(v0, system::FluidSystem) return v0 end -write_v0!(v0, system::FluidSystem, _) = v0 +write_v0!(v0, system::AbstractFluidSystem, _) = v0 # To account for boundary effects in the viscosity term of the RHS, use the viscosity model # of the neighboring particle systems. -@inline function viscosity_model(system::FluidSystem, neighbor_system::FluidSystem) +@inline function viscosity_model(system::AbstractFluidSystem, + neighbor_system::AbstractFluidSystem) return neighbor_system.viscosity end -@inline function viscosity_model(system::FluidSystem, neighbor_system::BoundarySystem) +@inline function viscosity_model(system::AbstractFluidSystem, + neighbor_system::AbstractBoundarySystem) return neighbor_system.boundary_model.viscosity end -@inline system_state_equation(system::FluidSystem) = system.state_equation +@inline system_state_equation(system::AbstractFluidSystem) = system.state_equation function compute_density!(system, u, u_ode, semi, ::ContinuityDensity) # No density update with `ContinuityDensity` @@ -121,7 +125,43 @@ function compute_density!(system, u, u_ode, semi, ::SummationDensity) summation_density!(system, semi, u, u_ode, density) end -function calculate_dt(v_ode, u_ode, cfl_number, system::FluidSystem, semi) +# With 'SummationDensity', density is calculated in wcsph/system.jl:compute_density! +@inline function continuity_equation!(dv, density_calculator::SummationDensity, + particle_system, neighbor_system, + v_particle_system, v_neighbor_system, + particle, neighbor, pos_diff, distance, + m_b, rho_a, rho_b, grad_kernel) + return dv +end + +# This formulation was chosen to be consistent with the used pressure_acceleration formulations +@propagate_inbounds function continuity_equation!(dv, density_calculator::ContinuityDensity, + particle_system::AbstractFluidSystem, + neighbor_system, + v_particle_system, v_neighbor_system, + particle, neighbor, pos_diff, distance, + m_b, rho_a, rho_b, grad_kernel) + vdiff = current_velocity(v_particle_system, particle_system, particle) - + current_velocity(v_neighbor_system, neighbor_system, neighbor) + + dv[end, particle] += rho_a / rho_b * m_b * dot(vdiff, grad_kernel) + + # Artificial density diffusion should only be applied to systems representing a fluid + # with the same physical properties i.e. density and viscosity. + # TODO: shouldn't be applied to particles on the interface (depends on PR #539) + if particle_system === neighbor_system + density_diffusion!(dv, density_diffusion(particle_system), + v_particle_system, particle, neighbor, + pos_diff, distance, m_b, rho_a, rho_b, particle_system, + grad_kernel) + end + + continuity_equation_shifting!(dv, shifting_technique(particle_system), + particle_system, neighbor_system, + particle, neighbor, grad_kernel, rho_a, rho_b, m_b) +end + +function calculate_dt(v_ode, u_ode, cfl_number, system::AbstractFluidSystem, semi) (; viscosity, acceleration, surface_tension) = system # TODO @@ -162,7 +202,7 @@ function calculate_dt(v_ode, u_ode, cfl_number, system::FluidSystem, semi) return dt end -@inline function surface_tension_model(system::FluidSystem) +@inline function surface_tension_model(system::AbstractFluidSystem) return system.surface_tension end @@ -170,7 +210,7 @@ end return nothing end -@inline function surface_normal_method(system::FluidSystem) +@inline function surface_normal_method(system::AbstractFluidSystem) return system.surface_normal_method end @@ -178,27 +218,29 @@ end return nothing end -function system_data(system::FluidSystem, v_ode, u_ode, semi) +function system_data(system::AbstractFluidSystem, dv_ode, du_ode, v_ode, u_ode, semi) (; mass) = system + dv = wrap_v(dv_ode, system, semi) v = wrap_v(v_ode, system, semi) u = wrap_u(u_ode, system, semi) coordinates = current_coordinates(u, system) velocity = current_velocity(v, system) + acceleration = current_velocity(dv, system) density = current_density(v, system) pressure = current_pressure(v, system) - return (; coordinates, velocity, mass, density, pressure) + return (; coordinates, velocity, mass, density, pressure, acceleration) end -function available_data(::FluidSystem) - return (:coordinates, :velocity, :mass, :density, :pressure) +function available_data(::AbstractFluidSystem) + return (:coordinates, :velocity, :mass, :density, :pressure, :acceleration) end include("pressure_acceleration.jl") include("viscosity.jl") -include("transport_velocity.jl") +include("shifting_techniques.jl") include("surface_tension.jl") include("surface_normal_sph.jl") include("weakly_compressible_sph/weakly_compressible_sph.jl") diff --git a/src/schemes/fluid/shifting_techniques.jl b/src/schemes/fluid/shifting_techniques.jl new file mode 100644 index 0000000000..44de644911 --- /dev/null +++ b/src/schemes/fluid/shifting_techniques.jl @@ -0,0 +1,685 @@ +abstract type AbstractShiftingTechnique end + +# No shifting for a system by default +@inline shifting_technique(system) = nothing + +# WARNING: Be careful if defining this function for a specific system type. +# The version for a specific system type will override this generic version. +requires_update_callback(system) = requires_update_callback(shifting_technique(system)) +requires_update_callback(::Nothing) = false +requires_update_callback(::AbstractShiftingTechnique) = true + +# This is called from the `UpdateCallback` +particle_shifting_from_callback!(u_ode, shifting, system, v_ode, semi, dt) = u_ode + +create_cache_shifting(initial_condition, ::Nothing) = (;) + +function create_cache_shifting(initial_condition, ::AbstractShiftingTechnique) + delta_v = zeros(eltype(initial_condition), ndims(initial_condition), + nparticles(initial_condition)) + + return (; delta_v) +end + +# `δv` is the correction to the particle velocity due to the shifting. +# Particles are advected with the velocity `v + δv`. +@propagate_inbounds function delta_v(system, particle) + return delta_v(system, shifting_technique(system), particle) +end + +# Zero when no shifting is used +@inline function delta_v(system, shifting, particle) + return zero(SVector{ndims(system), eltype(system)}) +end + +@propagate_inbounds function delta_v(system, ::AbstractShiftingTechnique, particle) + return extract_svector(system.cache.delta_v, system, particle) +end + +function update_shifting!(system, shifting, v, u, v_ode, u_ode, semi) + return system +end + +# Additional term in the momentum equation due to the shifting technique +@inline function dv_shifting(shifting, system, neighbor_system, + v_system, v_neighbor_system, particle, neighbor, + m_a, m_b, rho_a, rho_b, pos_diff, distance, + grad_kernel, correction) + return zero(grad_kernel) +end + +# Additional term(s) in the continuity equation due to the shifting technique +function continuity_equation_shifting!(dv, shifting, + particle_system, neighbor_system, + particle, neighbor, grad_kernel, rho_a, rho_b, m_b) + return dv +end + +@doc raw""" + ParticleShiftingTechnique(; integrate_shifting_velocity=true, + update_everystage=false, + modify_continuity_equation=true, + second_continuity_equation_term=ContinuityEquationTermSun2019(), + momentum_equation_term=MomentumEquationTermSun2019(), + v_max_factor=1, sound_speed_factor=0) + +Particle Shifting Technique by [Sun et al. (2017)](@cite Sun2017) +and [Sun et al. (2019)](@cite Sun2019). +The keyword arguments allow to choose between the original methods from the two papers +and variants in between. + +The default values of the keyword arguments provide the version of shifting +that we recommend based on our experiments. +The default values are subject to change in future releases. + +See [Particle Shifting Technique](@ref shifting) for more information on the method. + +We provide the following convenience constructors for common variants of the method: +- [`ParticleShiftingTechniqueSun2017()`](@ref): + Particle Shifting Technique by [Sun et al. (2017)](@cite Sun2017). + Shifting is applied as a position correction in a callback after each time step. + No additional terms are added to the momentum or continuity equations. +- [`ConsistentShiftingSun2019()`](@ref): + Consistent Particle Shifting Technique by [Sun et al. (2019)](@cite Sun2019). + Shifting is applied with a shifting velocity in each stage of the time integration. + Additional terms are added to the momentum and continuity equations, most importantly + to guarantee conservation of volume in closed systems, which is not the case for the + original method by [Sun et al. (2017)](@cite Sun2017). + +# Keywords +- `integrate_shifting_velocity`: If `true`, the shifting is applied in each stage of the + time integration method as a shifting velocity that is + added to the physical velocity in the time integration. + If `false`, the shifting is applied as a position correction + in a callback after each time step. +- `update_everystage`: If `true`, the shifting velocity is updated in every stage + of a multi-stage time integration method. + This requires `integrate_shifting_velocity=true`. + If `false`, the shifting velocity is only updated once + per time step in a callback, and the same shifting velocity + is used for all stages. + `update_everystage=false` reduces the computational cost, + but may reduce the stability of the scheme and require + a smaller time step. +- `modify_continuity_equation`: If `true`, the continuity equation is modified to be based + on the transport velocity instead of the physical velocity. + This guarantees conservation of volume in closed systems, + but is unstable at solid wall boundaries, according to our + experiments. + This requires `integrate_shifting_velocity=true`. +- `second_continuity_equation_term`: Additional term to be added to the + continuity equation to solve the stability problems with + the modified continuity equation at solid wall boundaries. + This requires `modify_continuity_equation=true`. + See [`ContinuityEquationTermSun2019`](@ref). +- `momentum_equation_term`: Additional term to be added to the momentum equation + to account for the shifting velocity. + This requires `integrate_shifting_velocity=true`. + See [`MomentumEquationTermSun2019`](@ref). +- `v_max_factor`: Factor to scale the expected maximum velocity used in the + shifting velocity. The maximum expected velocity is computed as + `v_max_factor * max(|v|)`, where `v` is the physical velocity. + As opposed to `sound_speed_factor`, the computed expected + maximum velocity depends on the current flow field + and can change over time. + Only one of `v_max_factor` and `sound_speed_factor` + can be non-zero. +- `sound_speed_factor`: Factor to compute the maximum expected velocity used in the + shifting velocity from the speed of sound. + The maximum expected velocity is computed as + `sound_speed_factor * c`, where `c` is the speed of sound. + Only one of `v_max_factor` and `sound_speed_factor` + can be non-zero. + +!!! warning + The Particle Shifting Technique needs to be disabled close to the free surface + and therefore requires a free surface detection method. This is not yet implemented. + **This technique cannot be used in a free surface simulation.** +""" +struct ParticleShiftingTechnique{integrate_shifting_velocity, + update_everystage, + modify_continuity_equation, + compute_v_max, + ELTYPE, S, M} <: AbstractShiftingTechnique + v_factor :: ELTYPE + second_continuity_equation_term :: S + momentum_equation_term :: M + + function ParticleShiftingTechnique(; integrate_shifting_velocity=true, + update_everystage=false, + modify_continuity_equation=true, + second_continuity_equation_term=ContinuityEquationTermSun2019(), + momentum_equation_term=MomentumEquationTermSun2019(), + v_max_factor=1, sound_speed_factor=0) + if !integrate_shifting_velocity && update_everystage + throw(ArgumentError("ParticleShiftingTechnique: " * + "integrate_shifting_velocity=false requires " * + "update_everystage=false")) + end + + if !integrate_shifting_velocity && modify_continuity_equation + throw(ArgumentError("ParticleShiftingTechnique: " * + "modify_continuity_equation=true requires " * + "integrate_shifting_velocity=true")) + end + + if !modify_continuity_equation && !isnothing(second_continuity_equation_term) + throw(ArgumentError("ParticleShiftingTechnique: " * + "a second_continuity_equation_term requires " * + "modify_continuity_equation=true")) + end + + if !integrate_shifting_velocity && !isnothing(momentum_equation_term) + throw(ArgumentError("ParticleShiftingTechnique: " * + "a momentum_equation_term requires " * + "integrate_shifting_velocity=true")) + end + + if v_max_factor > 0 && sound_speed_factor > 0 + throw(ArgumentError("ParticleShiftingTechnique: " * + "Only one of v_max_factor and sound_speed_factor " * + "can be non-zero")) + end + + if v_max_factor <= 0 && sound_speed_factor <= 0 + throw(ArgumentError("ParticleShiftingTechnique: " * + "One of v_max_factor and sound_speed_factor " * + "must be positive")) + end + + v_factor = max(v_max_factor, sound_speed_factor) + compute_v_max = v_max_factor > 0 + + new{integrate_shifting_velocity, + update_everystage, + modify_continuity_equation, + compute_v_max, typeof(v_factor), + typeof(second_continuity_equation_term), + typeof(momentum_equation_term)}(v_factor, + second_continuity_equation_term, + momentum_equation_term) + end +end + +""" + ParticleShiftingTechniqueSun2017(; kwargs...) + +Particle Shifting Technique by [Sun et al. (2017)](@cite Sun2017). +Following the original paper, the callback is applied in every time step and not +in every stage of a multi-stage time integration method to reduce the computational cost. + +This is a convenience constructor for: +```jldoctest; output = false +ParticleShiftingTechnique(integrate_shifting_velocity=false, + update_everystage=false, + modify_continuity_equation=false, + second_continuity_equation_term=nothing, + momentum_equation_term=nothing, + v_max_factor=1, sound_speed_factor=0) + +# output +ParticleShiftingTechnique{false, false, false, true, Int64, Nothing, Nothing}(1, nothing, nothing) +``` + +See [ParticleShiftingTechnique](@ref ParticleShiftingTechnique) for all available options. + +# Keywords +- `kwargs...`: All keywords are passed to the main constructor. + +# Examples +```jldoctest; output = false +shifting_technique = ParticleShiftingTechniqueSun2017() + +# output +ParticleShiftingTechnique{false, false, false, true, Int64, Nothing, Nothing}(1, nothing, nothing) +``` + +!!! warning + The Particle Shifting Technique needs to be disabled close to the free surface + and therefore requires a free surface detection method. This is not yet implemented. + **This technique cannot be used in a free surface simulation.** +""" +function ParticleShiftingTechniqueSun2017(; kwargs...) + return ParticleShiftingTechnique(; integrate_shifting_velocity=false, + update_everystage=false, + modify_continuity_equation=false, + second_continuity_equation_term=nothing, + momentum_equation_term=nothing, + v_max_factor=1, sound_speed_factor=0, + kwargs...) +end + +""" + ConsistentShiftingSun2019(; sound_speed_factor=0.1, kwargs...) + +Consistent Particle Shifting Technique by [Sun et al. (2019)](@cite Sun2019). + +This is a convenience constructor for: +```jldoctest; output = false +ParticleShiftingTechnique(integrate_shifting_velocity=true, + update_everystage=true, + modify_continuity_equation=true, + second_continuity_equation_term=ContinuityEquationTermSun2019(), + momentum_equation_term=MomentumEquationTermSun2019(), + v_max_factor=0, sound_speed_factor=0.1) + +# output +ParticleShiftingTechnique{true, true, true, false, Float64, ContinuityEquationTermSun2019, MomentumEquationTermSun2019}(0.1, ContinuityEquationTermSun2019(), MomentumEquationTermSun2019()) +``` + +See [ParticleShiftingTechnique](@ref ParticleShiftingTechnique) for all available options. + +# Keywords +- `sound_speed_factor`: Factor to compute the maximum expected velocity used in the + shifting velocity from the speed of sound. + The maximum expected velocity is computed as + `sound_speed_factor * c`, where `c` is the speed of sound. + Since the speed of sound is usually chosen as 10 times the maximum + expected velocity in weakly compressible SPH, a value of 0.1 + corresponds to the maximum expected velocity. +- `kwargs...`: All keywords are passed to the main constructor. + +# Examples +```jldoctest; output = false +shifting_technique = ConsistentShiftingSun2019() + +# output +ParticleShiftingTechnique{true, true, true, false, Float64, ContinuityEquationTermSun2019, MomentumEquationTermSun2019}(0.1, ContinuityEquationTermSun2019(), MomentumEquationTermSun2019()) +``` + +!!! warning + The Particle Shifting Technique needs to be disabled close to the free surface + and therefore requires a free surface detection method. This is not yet implemented. + **This technique cannot be used in a free surface simulation.** +""" +function ConsistentShiftingSun2019(; kwargs...) + return ParticleShiftingTechnique(; integrate_shifting_velocity=true, + update_everystage=true, + modify_continuity_equation=true, + second_continuity_equation_term=ContinuityEquationTermSun2019(), + momentum_equation_term=MomentumEquationTermSun2019(), + v_max_factor=0, sound_speed_factor=0.1, + kwargs...) +end + +# `ParticleShiftingTechnique{false}` means `integrate_shifting_velocity=false`. +# Zero if PST is applied in a callback as a position correction +# and not with a shifting velocity in the time integration stages +# (which would be `integrate_shifting_velocity=false`). +@inline function delta_v(system, ::ParticleShiftingTechnique{false}, particle) + return zero(SVector{ndims(system), eltype(system)}) +end + +""" + MomentumEquationTermSun2019() + +A term by [Sun et al. (2019)](@cite Sun2019) to be added to the momentum equation. + +See [`ParticleShiftingTechnique`](@ref). +""" +struct MomentumEquationTermSun2019 end + +# Additional term in the momentum equation due to the shifting technique +@inline function dv_shifting(shifting::ParticleShiftingTechnique, system, neighbor_system, + v_system, v_neighbor_system, particle, neighbor, + m_a, m_b, rho_a, rho_b, pos_diff, distance, + grad_kernel, correction) + return dv_shifting(shifting.momentum_equation_term, system, neighbor_system, + v_system, v_neighbor_system, particle, neighbor, + m_a, m_b, rho_a, rho_b, pos_diff, distance, + grad_kernel, correction) +end + +@propagate_inbounds function dv_shifting(::MomentumEquationTermSun2019, + system, neighbor_system, + v_system, v_neighbor_system, + particle, neighbor, m_a, m_b, rho_a, rho_b, + pos_diff, distance, grad_kernel, correction) + delta_v_a = delta_v(system, particle) + delta_v_b = delta_v(neighbor_system, neighbor) + + v_a = current_velocity(v_system, system, particle) + v_b = current_velocity(v_neighbor_system, neighbor_system, neighbor) + + tensor_product = v_a * delta_v_a' + v_b * delta_v_b' + return m_b / rho_b * + (tensor_product * grad_kernel + v_a * dot(delta_v_a - delta_v_b, grad_kernel)) +end + +# `ParticleShiftingTechnique{<:Any, <:Any, true}` means `modify_continuity_equation=true` +function continuity_equation_shifting!(dv, + shifting::ParticleShiftingTechnique{<:Any, <:Any, + true}, + system, neighbor_system, + particle, neighbor, grad_kernel, rho_a, rho_b, m_b) + delta_v_diff = delta_v(system, particle) - + delta_v(neighbor_system, neighbor) + + dv[end, particle] += rho_a / rho_b * m_b * dot(delta_v_diff, grad_kernel) + + second_continuity_equation_term!(dv, shifting.second_continuity_equation_term, + system, neighbor_system, + particle, neighbor, grad_kernel, rho_a, rho_b, m_b) + + return dv +end + +""" + ContinuityEquationTermSun2019() + +A second term by [Sun et al. (2019)](@cite Sun2019) to be added to the continuity equation +to solve the stability problems with the modified continuity equation at solid wall boundaries. + +See [`ParticleShiftingTechnique`](@ref). +""" +struct ContinuityEquationTermSun2019 end + +@inline function second_continuity_equation_term!(dv, + ::ContinuityEquationTermSun2019, + system, neighbor_system, + particle, neighbor, grad_kernel, + rho_a, rho_b, m_b) + rho_v = rho_a * delta_v(system, particle) + rho_b * delta_v(neighbor_system, neighbor) + + dv[end, particle] += m_b / rho_b * dot(rho_v, grad_kernel) + + return dv +end + +@inline function second_continuity_equation_term!(dv, second_continuity_equation_term, + system, neighbor_system, + particle, neighbor, grad_kernel, + rho_a, rho_b, m_b) + return dv +end + +# `ParticleShiftingTechnique{<:Any, true}` means `update_everystage=true` +function update_shifting!(system, shifting::ParticleShiftingTechnique{<:Any, true}, + v, u, v_ode, u_ode, semi) + update_shifting_inner!(system, shifting, v, u, v_ode, u_ode, semi) +end + +# `ParticleShiftingTechnique{<:Any, false}` means `update_everystage=false` +function update_shifting_from_callback!(system, + shifting::ParticleShiftingTechnique{<:Any, false}, + v_ode, u_ode, semi) + v = wrap_v(v_ode, system, semi) + u = wrap_u(u_ode, system, semi) + + update_shifting_inner!(system, shifting, v, u, v_ode, u_ode, semi) +end + +# `ParticleShiftingTechnique{<:Any, <:Any, <:Any, true}` +# means `compute_v_max=true` +function v_max(shifting::ParticleShiftingTechnique{<:Any, <:Any, <:Any, true}, + v, system) + # This has similar performance to `maximum(..., eachparticle(system))`, + # but is GPU-compatible. + v_max = maximum(x -> sqrt(dot(x, x)), + reinterpret(reshape, SVector{ndims(system), eltype(v)}, + current_velocity(v, system))) + return shifting.v_factor * v_max +end + +# `ParticleShiftingTechnique{<:Any, <:Any, <:Any, false}` +# means `compute_v_max=false` +function v_max(shifting::ParticleShiftingTechnique{<:Any, <:Any, <:Any, false}, + v, system) + sound_speed = system_sound_speed(system) + + return shifting.v_factor * sound_speed +end + +function update_shifting_inner!(system, shifting::ParticleShiftingTechnique, + v, u, v_ode, u_ode, semi) + (; cache) = system + (; delta_v) = cache + + set_zero!(delta_v) + + v_max_ = v_max(shifting, v, system) + + # TODO this needs to be adapted to multi-resolution. + # Section 3.2 explains what else needs to be changed. + dx = particle_spacing(system, 1) + Wdx = smoothing_kernel(system, dx, 1) + h = smoothing_length(system, 1) + + foreach_system(semi) do neighbor_system + u_neighbor = wrap_u(u_ode, neighbor_system, semi) + v_neighbor = wrap_v(v_ode, neighbor_system, semi) + + system_coords = current_coordinates(u, system) + neighbor_coords = current_coordinates(u_neighbor, neighbor_system) + + foreach_point_neighbor(system, neighbor_system, system_coords, neighbor_coords, + semi; + points=each_integrated_particle(system)) do particle, + neighbor, + pos_diff, + distance + m_b = hydrodynamic_mass(neighbor_system, neighbor) + rho_a = current_density(v, system, particle) + rho_b = current_density(v_neighbor, neighbor_system, neighbor) + + kernel = smoothing_kernel(system, distance, particle) + grad_kernel = smoothing_kernel_grad(system, pos_diff, distance, particle) + + # According to p. 29 below Eq. 9 + R = 2 // 10 + n = 4 + + # Eq. 7 in Sun et al. (2017). + # According to the paper, CFL * Ma can be rewritten as Δt * v_max / h + # (see p. 29, right above Eq. 9), but this does not yield the same amount + # of shifting when scaling h. + # When setting CFL * Ma = Δt * v_max / (2 * Δx), PST works as expected + # for both small and large smoothing length factors. + # We need to scale + # - quadratically with the smoothing length, + # - linearly with the particle spacing, + # - linearly with the time step. + # See https://github.com/trixi-framework/TrixiParticles.jl/pull/834. + delta_v_ = -v_max_ * (2 * h)^2 / (2 * dx) * (1 + R * (kernel / Wdx)^n) * + m_b / (rho_a + rho_b) * grad_kernel + + # Write into the buffer + for i in eachindex(delta_v_) + @inbounds delta_v[i, particle] += delta_v_[i] + end + end + end + + return system +end + +# `ParticleShiftingTechnique{<:Any, false}` means `update_everystage=false`. +# Only update shifting from callback if `update_everystage=false`. +# Only apply shifting from callback if PST is to be applied in a callback +# (`integrate_shifting_velocity=false`), but this also requires `update_everystage=false`. +function particle_shifting_from_callback!(u_ode, + shifting::ParticleShiftingTechnique{<:Any, false}, + system, v_ode, semi, dt) + @trixi_timeit timer() "particle shifting" begin + # Update the shifting velocity + update_shifting_from_callback!(system, shifting, v_ode, u_ode, semi) + + # Update the particle positions with the shifting velocity + apply_particle_shifting!(u_ode, shifting, system, semi, dt) + end +end + +# `ParticleShiftingTechnique{false}` means `integrate_shifting_velocity=false`. +# Only apply shifting from callback if PST is to be applied in a callback +# and not with a shifting velocity in the time integration stages +# (which would be `integrate_shifting_velocity=false`). +function apply_particle_shifting!(u_ode, ::ParticleShiftingTechnique{false}, + system, semi, dt) + (; cache) = system + (; delta_v) = cache + + u = wrap_u(u_ode, system, semi) + + # Add δr from the cache to the current coordinates + @threaded semi for particle in eachparticle(system) + for i in axes(delta_v, 1) + @inbounds u[i, particle] += dt * delta_v[i, particle] + end + end + + return u +end + +function apply_particle_shifting!(u_ode, ::ParticleShiftingTechnique{true}, + system, semi, dt) + return u_ode +end + +""" + TransportVelocityAdami(; background_pressure::Real, modify_continuity_equation=false) + +Transport Velocity Formulation (TVF) by [Adami et al. (2013)](@cite Adami2013) +to suppress pairing and tensile instability. +See [TVF](@ref transport_velocity_formulation) for more details of the method. + +# Keywords +- `background_pressure`: Background pressure. Suggested is a background pressure which is + on the order of the reference pressure. +- `modify_continuity_equation`: If `true`, the continuity equation is modified to be based + on the transport velocity instead of the physical velocity. + This guarantees conservation of volume in closed systems, + but is unstable at solid wall boundaries, according to our + experiments. + +!!! warning + The Transport Velocity Formulation needs to be disabled close to the free surface + and therefore requires a free surface detection method. This is not yet implemented. + **This technique cannot be used in a free surface simulation.** +""" +struct TransportVelocityAdami{modify_continuity_equation, T <: Real} <: + AbstractShiftingTechnique + background_pressure::T + + function TransportVelocityAdami(; background_pressure, modify_continuity_equation=false) + new{modify_continuity_equation, typeof(background_pressure)}(background_pressure) + end +end + +@propagate_inbounds function dv_shifting(::TransportVelocityAdami, system, neighbor_system, + v_system, v_neighbor_system, particle, neighbor, + m_a, m_b, rho_a, rho_b, pos_diff, distance, + grad_kernel, correction) + v_a = current_velocity(v_system, system, particle) + delta_v_a = delta_v(system, particle) + + v_b = current_velocity(v_neighbor_system, neighbor_system, neighbor) + delta_v_b = delta_v(neighbor_system, neighbor) + + A_a = rho_a * v_a * delta_v_a' + A_b = rho_b * v_b * delta_v_b' + + # The following term depends on the pressure acceleration formulation. + # See the large comment below. In the original paper (Adami et al., 2013), this is + # (V_a^2 + V_b^2) / m_a * ((A_a + A_b) / 2) * ∇W_ab. + # With the most common pressure acceleration formulation, this is + # m_b * (A_a + A_b) / (ρ_a * ρ_b) * ∇W_ab. + # In order to obtain this, we pass `p_a = A_a` and `p_b = A_b` to the + # `pressure_acceleration` function. + return pressure_acceleration(system, neighbor_system, particle, neighbor, + m_a, m_b, A_a, A_b, rho_a, rho_b, pos_diff, + distance, grad_kernel, correction) +end + +function continuity_equation_shifting!(dv, shifting::TransportVelocityAdami{true}, + particle_system, neighbor_system, + particle, neighbor, grad_kernel, rho_a, rho_b, m_b) + delta_v_diff = delta_v(particle_system, particle) - + delta_v(neighbor_system, neighbor) + + dv[end, particle] += rho_a / rho_b * m_b * dot(delta_v_diff, grad_kernel) + + return dv +end + +function update_shifting!(system, shifting::TransportVelocityAdami, v, u, v_ode, + u_ode, semi) + (; cache, correction) = system + (; delta_v) = cache + (; background_pressure) = shifting + + sound_speed = system_sound_speed(system) + + set_zero!(delta_v) + + foreach_system(semi) do neighbor_system + v_neighbor = wrap_v(v_ode, neighbor_system, semi) + u_neighbor = wrap_u(u_ode, neighbor_system, semi) + + system_coords = current_coordinates(u, system) + neighbor_coords = current_coordinates(u_neighbor, neighbor_system) + + foreach_point_neighbor(system, neighbor_system, system_coords, neighbor_coords, + semi; + points=each_integrated_particle(system)) do particle, + neighbor, + pos_diff, + distance + m_a = @inbounds hydrodynamic_mass(system, particle) + m_b = @inbounds hydrodynamic_mass(neighbor_system, neighbor) + + rho_a = @inbounds current_density(v, system, particle) + rho_b = @inbounds current_density(v_neighbor, neighbor_system, neighbor) + + h = smoothing_length(system, particle) + + grad_kernel = smoothing_kernel_grad(system, pos_diff, distance, particle) + + # In the original paper (Adami et al., 2013), the transport velocity is applied + # as follows: + # v_{1/2} = v_0 + Δt/2 * a, + # where a is the regular SPH acceleration term (pressure, viscosity, etc.). + # r_1 = r_0 + Δt * (v_{1/2}, + # where ̃v_{1/2} = v_{1/2} + Δt/2 * p_0 / m_a * \sum_b[ (V_a^2 + V_b^2) * ∇W_ab ] + # is the transport velocity. + # We call δv_{1/2} = ̃v_{1/2} - v_{1/2} the shifting velocity. + # We will call δv_{1/2} the shifting velocity, which is given by + # δv = -Δt/2 * p_0 / m_a * \sum_b[ (V_a^2 + V_b^2) * ∇W_ab ], + # where p_0 is the background pressure, V_a = m_a / ρ_a, V_b = m_b / ρ_b. + # This term depends on the pressure acceleration formulation. + # In Zhang et al. (2017), the pressure acceleration term + # m_b * (p_a / ρ_a^2 + p_b / ρ_b^2) * ∇W_ab + # is used. They consequently changed the shifting velocity to + # δv = -Δt/2 * p_0 * \sum_b[ m_b * (1 / ρ_a^2 + 1 / ρ_b^2) * ∇W_ab ]. + # We therefore use the function `pressure_acceleration` to compute the + # shifting velocity according to the used pressure acceleration formulation. + # In most cases, this will be + # δv = -Δt/2 * p_0 * \sum_b[ m_b * (1 + 1) / (ρ_a * ρ_b) * ∇W_ab ]. + # + # In these papers, the shifting velocity is scaled by the time step Δt. + # We generally want the spatial discretization to be independent of the time step. + # Scaling the shifting velocity by the time step would lead to less shifting + # when very small time steps are used for testing/debugging purposes. + # This is especially problematic in TrixiParticles.jl, as the time step can vary + # significantly between different time integration methods (low vs high order). + # In order to eliminate the time step from the shifting velocity, we apply the + # CFL condition used in Adami et al. (2013): + # Δt <= 0.25 * h / c, + # where h is the smoothing length and c is the sound speed. + # Applying this equation as equality yields the shifting velocity + # δv = -p_0 / 8 * h / c * \sum_b[ m_b * (1 + 1) / (ρ_a * ρ_b) * ∇W_ab ]. + # The last part is achieved by passing `p_a = 1` and `p_b = 1` to the + # `pressure_acceleration` function. + delta_v_ = background_pressure / 8 * h / sound_speed * + pressure_acceleration(system, neighbor_system, particle, neighbor, + m_a, m_b, 1, 1, rho_a, rho_b, pos_diff, + distance, grad_kernel, correction) + + # Write into the buffer + for i in eachindex(delta_v_) + @inbounds delta_v[i, particle] += delta_v_[i] + end + end + end + + return system +end diff --git a/src/schemes/fluid/surface_normal_sph.jl b/src/schemes/fluid/surface_normal_sph.jl index da2d50f327..30b389eed2 100644 --- a/src/schemes/fluid/surface_normal_sph.jl +++ b/src/schemes/fluid/surface_normal_sph.jl @@ -33,7 +33,7 @@ function create_cache_surface_normal(::ColorfieldSurfaceNormal, ELTYPE, NDIMS, n return (; surface_normal, neighbor_count, colorfield, correction_factor) end -@inline function surface_normal(particle_system::FluidSystem, particle) +@inline function surface_normal(particle_system::AbstractFluidSystem, particle) (; cache) = particle_system return extract_svector(cache.surface_normal, particle_system, particle) end @@ -47,7 +47,8 @@ end # Section 2.2 in Akinci et al. 2013 "Versatile Surface Tension and Adhesion for SPH Fluids" # and Section 5 in Morris 2000 "Simulating surface tension with smoothed particle hydrodynamics". -function calc_normal!(system::FluidSystem, neighbor_system::FluidSystem, u_system, v, +function calc_normal!(system::AbstractFluidSystem, neighbor_system::AbstractFluidSystem, + u_system, v, v_neighbor_system, u_neighbor_system, semi, surface_normal_method, ::ColorfieldSurfaceNormal) (; cache) = system @@ -57,8 +58,8 @@ function calc_normal!(system::FluidSystem, neighbor_system::FluidSystem, u_syste foreach_point_neighbor(system, neighbor_system, system_coords, neighbor_system_coords, semi; - points=each_moving_particle(system)) do particle, neighbor, - pos_diff, distance + points=each_integrated_particle(system)) do particle, neighbor, + pos_diff, distance m_b = hydrodynamic_mass(neighbor_system, neighbor) density_neighbor = current_density(v_neighbor_system, neighbor_system, neighbor) @@ -76,9 +77,9 @@ end # Section 2.2 in Akinci et al. 2013 "Versatile Surface Tension and Adhesion for SPH Fluids" # Note: This is the simplest form of normal approximation commonly used in SPH and comes # with serious deficits in accuracy especially at corners, small neighborhoods and boundaries -function calc_normal!(system::FluidSystem, neighbor_system::BoundarySystem, u_system, - v, v_neighbor_system, u_neighbor_system, semi, surface_normal_method, - neighbor_surface_normal_method) +function calc_normal!(system::AbstractFluidSystem, neighbor_system::AbstractBoundarySystem, + u_system, v, v_neighbor_system, u_neighbor_system, semi, + surface_normal_method, neighbor_surface_normal_method) (; cache) = system (; colorfield, initial_colorfield) = neighbor_system.boundary_model.cache (; boundary_contact_threshold) = surface_normal_method @@ -123,12 +124,12 @@ function calc_normal!(system::FluidSystem, neighbor_system::BoundarySystem, u_sy return system end -function remove_invalid_normals!(system::FluidSystem, surface_tension, +function remove_invalid_normals!(system::AbstractFluidSystem, surface_tension, surface_normal_method) (; cache) = system # We remove invalid normals (too few neighbors) to reduce the impact of underdefined normals - for particle in each_moving_particle(system) + for particle in each_integrated_particle(system) # A corner has that many neighbors assuming a regular 2 * r distribution and a compact_support of 4r if cache.neighbor_count[particle] < 2^ndims(system) + 1 cache.surface_normal[1:ndims(system), particle] .= 0 @@ -139,7 +140,7 @@ function remove_invalid_normals!(system::FluidSystem, surface_tension, end # See Morris 2000 "Simulating surface tension with smoothed particle hydrodynamics" -function remove_invalid_normals!(system::FluidSystem, +function remove_invalid_normals!(system::AbstractFluidSystem, surface_tension::Union{SurfaceTensionMorris, SurfaceTensionMomentumMorris}, surface_normal_method::ColorfieldSurfaceNormal) @@ -153,7 +154,7 @@ function remove_invalid_normals!(system::FluidSystem, normal_condition2 = (interface_threshold / compact_support(smoothing_kernel, smoothing_length_))^2 - for particle in each_moving_particle(system) + for particle in each_integrated_particle(system) # Heuristic condition if there is no gas phase to find the free surface. # We remove normals for particles which have a lot of support e.g. they are in the interior. @@ -185,7 +186,7 @@ function compute_surface_normal!(system, surface_normal_method, v, u, v_ode, u_o return system end -function compute_surface_normal!(system::FluidSystem, +function compute_surface_normal!(system::AbstractFluidSystem, surface_normal_method_::ColorfieldSurfaceNormal, v, u, v_ode, u_ode, semi, t) (; cache, surface_tension) = system @@ -214,8 +215,8 @@ function calc_curvature!(system, neighbor_system, u_system, v, end # Section 5 in Morris 2000 "Simulating surface tension with smoothed particle hydrodynamics" -function calc_curvature!(system::FluidSystem, neighbor_system::FluidSystem, u_system, v, - v_neighbor_system, u_neighbor_system, semi, +function calc_curvature!(system::AbstractFluidSystem, neighbor_system::AbstractFluidSystem, + u_system, v, v_neighbor_system, u_neighbor_system, semi, surface_normal_method::ColorfieldSurfaceNormal, neighbor_surface_normal_method::ColorfieldSurfaceNormal) (; cache) = system @@ -249,8 +250,8 @@ function calc_curvature!(system::FluidSystem, neighbor_system::FluidSystem, u_sy end # Eq. 23 - for i in 1:n_moving_particles(system) - curvature[i] /= (correction_factor[i] + eps()) + for particle in each_integrated_particle(system) + curvature[particle] /= (correction_factor[particle] + eps()) end return system @@ -260,8 +261,9 @@ function compute_curvature!(system, surface_tension, v, u, v_ode, u_ode, semi, t return system end -function compute_curvature!(system::FluidSystem, surface_tension::SurfaceTensionMorris, v, - u, v_ode, u_ode, semi, t) +function compute_curvature!(system::AbstractFluidSystem, + surface_tension::SurfaceTensionMorris, + v, u, v_ode, u_ode, semi, t) (; cache, surface_tension) = system # Reset surface curvature diff --git a/src/schemes/fluid/surface_tension.jl b/src/schemes/fluid/surface_tension.jl index 3d4025f3e1..9514c2f4e8 100644 --- a/src/schemes/fluid/surface_tension.jl +++ b/src/schemes/fluid/surface_tension.jl @@ -1,5 +1,5 @@ -abstract type SurfaceTension end -abstract type AkinciTypeSurfaceTension <: SurfaceTension end +abstract type AbstractSurfaceTension end +abstract type AkinciTypeSurfaceTension <: AbstractSurfaceTension end @doc raw""" CohesionForceAkinci(surface_tension_coefficient=1.0) @@ -58,7 +58,7 @@ See [`surface_tension`](@ref) for more details. - `surface_tension_coefficient=1.0`: Adjusts the magnitude of the surface tension forces, enabling tuning of fluid surface behaviors in simulations. """ -struct SurfaceTensionMorris{ELTYPE} <: SurfaceTension +struct SurfaceTensionMorris{ELTYPE} <: AbstractSurfaceTension surface_tension_coefficient::ELTYPE function SurfaceTensionMorris(; surface_tension_coefficient=1.0) @@ -90,7 +90,7 @@ See [`surface_tension`](@ref) for more details. - `surface_tension_coefficient=1.0`: A parameter to adjust the strength of surface tension forces, allowing fine-tuning to replicate physical behavior. """ -struct SurfaceTensionMomentumMorris{ELTYPE} <: SurfaceTension +struct SurfaceTensionMomentumMorris{ELTYPE} <: AbstractSurfaceTension surface_tension_coefficient::ELTYPE function SurfaceTensionMomentumMorris(; surface_tension_coefficient=1.0) @@ -106,7 +106,7 @@ function create_cache_surface_tension(::SurfaceTensionMomentumMorris, ELTYPE, ND return (; stress_tensor, delta_s) end -@inline function stress_tensor(particle_system::FluidSystem, particle) +@inline function stress_tensor(particle_system::AbstractFluidSystem, particle) return extract_smatrix(particle_system.cache.stress_tensor, particle_system, particle) end @@ -170,9 +170,10 @@ 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, rho_a, rho_b, grad_kernel) + particle_system::AbstractFluidSystem, + neighbor_system::AbstractFluidSystem, + particle, neighbor, pos_diff, distance, + rho_a, rho_b, grad_kernel) # No cohesion with oneself distance < sqrt(eps()) && return zero(pos_diff) @@ -185,8 +186,9 @@ end @inline function surface_tension_force(surface_tension_a::SurfaceTensionAkinci, surface_tension_b::SurfaceTensionAkinci, - particle_system::FluidSystem, - neighbor_system::FluidSystem, particle, neighbor, + particle_system::AbstractFluidSystem, + neighbor_system::AbstractFluidSystem, particle, + neighbor, pos_diff, distance, rho_a, rho_b, grad_kernel) (; smoothing_kernel) = particle_system (; surface_tension_coefficient) = surface_tension_a @@ -207,9 +209,10 @@ end @inline function surface_tension_force(surface_tension_a::SurfaceTensionMorris, surface_tension_b::SurfaceTensionMorris, - particle_system::FluidSystem, - neighbor_system::FluidSystem, particle, neighbor, - pos_diff, distance, rho_a, rho_b, grad_kernel) + particle_system::AbstractFluidSystem, + neighbor_system::AbstractFluidSystem, + particle, neighbor, pos_diff, distance, + rho_a, rho_b, grad_kernel) (; surface_tension_coefficient) = surface_tension_a # No surface tension with oneself @@ -226,7 +229,8 @@ function compute_stress_tensors!(system, surface_tension, v, u, v_ode, u_ode, se end # Section 6 in Morris 2000 "Simulating surface tension with smoothed particle hydrodynamics" -function compute_stress_tensors!(system::FluidSystem, ::SurfaceTensionMomentumMorris, +function compute_stress_tensors!(system::AbstractFluidSystem, + ::SurfaceTensionMomentumMorris, v, u, v_ode, u_ode, semi, t) (; cache) = system (; delta_s, stress_tensor) = cache @@ -238,7 +242,7 @@ function compute_stress_tensors!(system::FluidSystem, ::SurfaceTensionMomentumMo NDIMS = ndims(system) @trixi_timeit timer() "compute surface stress tensor" begin - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) normal = surface_normal(system, particle) delta_s_particle = delta_s[particle] if delta_s_particle > eps() @@ -267,7 +271,7 @@ function compute_surface_delta_function!(system, ::SurfaceTensionMomentumMorris, set_zero!(delta_s) - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) delta_s[particle] = norm(surface_normal(system, particle)) end return system @@ -275,9 +279,10 @@ end @inline function surface_tension_force(surface_tension_a::SurfaceTensionMomentumMorris, surface_tension_b::SurfaceTensionMomentumMorris, - particle_system::FluidSystem, - neighbor_system::FluidSystem, particle, neighbor, - pos_diff, distance, rho_a, rho_b, grad_kernel) + particle_system::AbstractFluidSystem, + neighbor_system::AbstractFluidSystem, + particle, neighbor, pos_diff, distance, + rho_a, rho_b, grad_kernel) (; surface_tension_coefficient) = surface_tension_a # No surface tension with oneself @@ -292,8 +297,8 @@ end end @inline function adhesion_force(surface_tension::AkinciTypeSurfaceTension, - particle_system::FluidSystem, - neighbor_system::BoundarySystem, particle, neighbor, + particle_system::AbstractFluidSystem, + neighbor_system::AbstractBoundarySystem, particle, neighbor, pos_diff, distance) (; adhesion_coefficient) = neighbor_system diff --git a/src/schemes/fluid/transport_velocity.jl b/src/schemes/fluid/transport_velocity.jl deleted file mode 100644 index fce7375dd5..0000000000 --- a/src/schemes/fluid/transport_velocity.jl +++ /dev/null @@ -1,158 +0,0 @@ -""" - TransportVelocityAdami(background_pressure::Real) - -Transport Velocity Formulation (TVF) by [Adami et al. (2013)](@cite Adami2013) -to suppress pairing and tensile instability. -See [TVF](@ref transport_velocity_formulation) for more details of the method. - -# Arguments -- `background_pressure`: Background pressure. Suggested is a background pressure which is - on the order of the reference pressure. -""" -struct TransportVelocityAdami{T <: Real} - background_pressure::T -end - -# No TVF for a system by default -@inline transport_velocity(system) = nothing - -create_cache_tvf(initial_condition, ::Nothing) = (;) - -function create_cache_tvf(initial_condition, ::TransportVelocityAdami) - delta_v = zeros(eltype(initial_condition), ndims(initial_condition), - nparticles(initial_condition)) - - return (; delta_v) -end - -# `δv` is the correction to the particle velocity due to the TVF. -# Particles are advected with the velocity `v + δv`. -@propagate_inbounds function delta_v(system, particle) - return delta_v(system, transport_velocity(system), particle) -end - -@propagate_inbounds function delta_v(system, ::TransportVelocityAdami, particle) - return extract_svector(system.cache.delta_v, system, particle) -end - -# Zero when no TVF is used -@inline function delta_v(system, transport_velocity, particle) - return zero(SVector{ndims(system), eltype(system)}) -end - -@inline function dv_transport_velocity(::Nothing, system, neighbor_system, - particle, neighbor, v_system, v_neighbor_system, - m_a, m_b, rho_a, rho_b, pos_diff, distance, - grad_kernel, correction) - return zero(grad_kernel) -end - -@inline function dv_transport_velocity(::TransportVelocityAdami, system, neighbor_system, - particle, neighbor, v_system, v_neighbor_system, - m_a, m_b, rho_a, rho_b, pos_diff, distance, - grad_kernel, correction) - v_a = current_velocity(v_system, system, particle) - delta_v_a = delta_v(system, particle) - - v_b = current_velocity(v_neighbor_system, neighbor_system, neighbor) - delta_v_b = delta_v(neighbor_system, neighbor) - - A_a = rho_a * v_a * delta_v_a' - A_b = rho_b * v_b * delta_v_b' - - # The following term depends on the pressure acceleration formulation. - # See the large comment below. In the original paper (Adami et al., 2013), this is - # (V_a^2 + V_b^2) / m_a * ((A_a + A_b) / 2) * ∇W_ab. - # With the most common pressure acceleration formulation, this is - # m_b * (A_a + A_b) / (ρ_a * ρ_b) * ∇W_ab. - # In order to obtain this, we pass `p_a = A_a` and `p_b = A_b` to the - # `pressure_acceleration` function. - return pressure_acceleration(system, neighbor_system, particle, neighbor, - m_a, m_b, A_a, A_b, rho_a, rho_b, pos_diff, - distance, grad_kernel, correction) -end - -function update_tvf!(system, transport_velocity, v, u, v_ode, u_ode, semi, t) - return system -end - -function update_tvf!(system, transport_velocity::TransportVelocityAdami, v, u, v_ode, - u_ode, semi, t) - (; cache, correction) = system - (; delta_v) = cache - (; background_pressure) = transport_velocity - - sound_speed = system_sound_speed(system) - - set_zero!(delta_v) - - foreach_system(semi) do neighbor_system - v_neighbor = wrap_v(v_ode, neighbor_system, semi) - u_neighbor = wrap_u(u_ode, neighbor_system, semi) - - system_coords = current_coordinates(u, system) - neighbor_coords = current_coordinates(u_neighbor, neighbor_system) - - foreach_point_neighbor(system, neighbor_system, system_coords, neighbor_coords, - semi; - points=each_moving_particle(system)) do particle, neighbor, - pos_diff, distance - m_a = @inbounds hydrodynamic_mass(system, particle) - m_b = @inbounds hydrodynamic_mass(neighbor_system, neighbor) - - rho_a = @inbounds current_density(v, system, particle) - rho_b = @inbounds current_density(v_neighbor, neighbor_system, neighbor) - - h = smoothing_length(system, particle) - - grad_kernel = smoothing_kernel_grad(system, pos_diff, distance, particle) - - # In the original paper (Adami et al., 2013), the transport velocity is applied - # as follows: - # v_{1/2} = v_0 + Δt/2 * a, - # where a is the regular SPH acceleration term (pressure, viscosity, etc.). - # r_1 = r_0 + Δt * (v_{1/2}, - # where ̃v_{1/2} = v_{1/2} + Δt/2 * p_0 / m_a * \sum_b[ (V_a^2 + V_b^2) * ∇W_ab ] - # is the transport velocity. - # We call δv_{1/2} = ̃v_{1/2} - v_{1/2} the shifting velocity. - # We will call δv_{1/2} the shifting velocity, which is given by - # δv = -Δt/2 * p_0 / m_a * \sum_b[ (V_a^2 + V_b^2) * ∇W_ab ], - # where p_0 is the background pressure, V_a = m_a / ρ_a, V_b = m_b / ρ_b. - # This term depends on the pressure acceleration formulation. - # In Zhang et al. (2017), the pressure acceleration term - # m_b * (p_a / ρ_a^2 + p_b / ρ_b^2) * ∇W_ab - # is used. They consequently changed the shifting velocity to - # δv = -Δt/2 * p_0 * \sum_b[ m_b * (1 / ρ_a^2 + 1 / ρ_b^2) * ∇W_ab ]. - # We therefore use the function `pressure_acceleration` to compute the - # shifting velocity according to the used pressure acceleration formulation. - # In most cases, this will be - # δv = -Δt/2 * p_0 * \sum_b[ m_b * (1 + 1) / (ρ_a * ρ_b) * ∇W_ab ]. - # - # In these papers, the shifting velocity is scaled by the time step Δt. - # We generally want the spatial discretization to be independent of the time step. - # Scaling the shifting velocity by the time step would lead to less shifting - # when very small time steps are used for testing/debugging purposes. - # This is especially problematic in TrixiParticles.jl, as the time step can vary - # significantly between different time integration methods (low vs high order). - # In order to eliminate the time step from the shifting velocity, we apply the - # CFL condition used in Adami et al. (2013): - # Δt <= 0.25 * h / c, - # where h is the smoothing length and c is the sound speed. - # Applying this equation as equality yields the shifting velocity - # δv = -p_0 / 8 * h / c * \sum_b[ m_b * (1 + 1) / (ρ_a * ρ_b) * ∇W_ab ]. - # The last part is achieved by passing `p_a = 1` and `p_b = 1` to the - # `pressure_acceleration` function. - delta_v_ = background_pressure / 8 * h / sound_speed * - pressure_acceleration(system, neighbor_system, particle, neighbor, - m_a, m_b, 1, 1, rho_a, rho_b, pos_diff, - distance, grad_kernel, correction) - - # Write into the buffer - for i in eachindex(delta_v_) - @inbounds delta_v[i, particle] += delta_v_[i] - end - end - end - - return system -end diff --git a/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl b/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl index 60d1967442..ff75ed21d6 100644 --- a/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl +++ b/src/schemes/fluid/weakly_compressible_sph/density_diffusion.jl @@ -1,5 +1,5 @@ @doc raw""" - DensityDiffusion + AbstractDensityDiffusion An abstract supertype of all density diffusion formulations. @@ -13,7 +13,9 @@ Currently, the following formulations are available: See [Density Diffusion](@ref density_diffusion) for a comparison and more details. """ -abstract type DensityDiffusion end +abstract type AbstractDensityDiffusion end + +@inline density_diffusion(system) = nothing # Most density diffusion formulations don't need updating function update!(density_diffusion, v, u, system, semi) @@ -25,18 +27,18 @@ end The commonly used density diffusion term by [Molteni (2009)](@cite Molteni2009). -The term ``\psi_{ab}`` in the continuity equation in [`DensityDiffusion`](@ref) is defined -by +The term ``\psi_{ab}`` in the continuity equation in +[`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion) is defined by ```math \psi_{ab} = 2(\rho_a - \rho_b) \frac{r_{ab}}{\Vert r_{ab} \Vert^2}, ``` where ``\rho_a`` and ``\rho_b`` denote the densities of particles ``a`` and ``b`` respectively and ``r_{ab} = r_a - r_b`` is the difference of the coordinates of particles ``a`` and ``b``. -See [`DensityDiffusion`](@ref) for an overview and comparison of implemented density -diffusion terms. +See [`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion) +for an overview and comparison of implemented density diffusion terms. """ -struct DensityDiffusionMolteniColagrossi{ELTYPE} <: DensityDiffusion +struct DensityDiffusionMolteniColagrossi{ELTYPE} <: AbstractDensityDiffusion delta::ELTYPE function DensityDiffusionMolteniColagrossi(; delta) @@ -54,8 +56,8 @@ end A density diffusion term by [Ferrari (2009)](@cite Ferrari2009). -The term ``\psi_{ab}`` in the continuity equation in [`DensityDiffusion`](@ref) is defined -by +The term ``\psi_{ab}`` in the continuity equation in +[`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion) is defined by ```math \psi_{ab} = \frac{\rho_a - \rho_b}{h_a + h_b} \frac{r_{ab}}{\Vert r_{ab} \Vert}, ``` @@ -63,10 +65,10 @@ where ``\rho_a`` and ``\rho_b`` denote the densities of particles ``a`` and ``b` ``r_{ab} = r_a - r_b`` is the difference of the coordinates of particles ``a`` and ``b`` and ``h_a`` and ``h_b`` are the smoothing lengths of particles ``a`` and ``b`` respectively. -See [`DensityDiffusion`](@ref) for an overview and comparison of implemented density -diffusion terms. +See [`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion) +for an overview and comparison of implemented density diffusion terms. """ -struct DensityDiffusionFerrari <: DensityDiffusion +struct DensityDiffusionFerrari <: AbstractDensityDiffusion delta::Int # δ is always 1 in this formulation @@ -87,8 +89,8 @@ The commonly used density diffusion terms by [Antuono (2010)](@cite Antuono2010) δ-SPH. The density diffusion term by [Molteni (2009)](@cite Molteni2009) is extended by a second term, which is nicely written down by [Antuono (2012)](@cite Antuono2012). -The term ``\psi_{ab}`` in the continuity equation in [`DensityDiffusion`](@ref) is defined -by +The term ``\psi_{ab}`` in the continuity equation in +[`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion) is defined by ```math \psi_{ab} = 2\left(\rho_a - \rho_b - \frac{1}{2}\big(\nabla\rho^L_a + \nabla\rho^L_b\big) \cdot r_{ab}\right) \frac{r_{ab}}{\Vert r_{ab} \Vert^2}, @@ -105,10 +107,10 @@ L_a := \left( -\sum_{b} V_b r_{ab} \otimes \nabla W_{ab} \right)^{-1} \in \R^{d ``` where ``d`` is the number of dimensions. -See [`DensityDiffusion`](@ref) for an overview and comparison of implemented density -diffusion terms. +See [`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion) +for an overview and comparison of implemented density diffusion terms. """ -struct DensityDiffusionAntuono{NDIMS, ELTYPE, ARRAY2D, ARRAY3D} <: DensityDiffusion +struct DensityDiffusionAntuono{NDIMS, ELTYPE, ARRAY2D, ARRAY3D} <: AbstractDensityDiffusion delta :: ELTYPE correction_matrix :: ARRAY3D # Array{ELTYPE, 3}: [i, j, particle] normalized_density_gradient :: ARRAY2D # Array{ELTYPE, 2}: [i, particle] @@ -183,8 +185,8 @@ function update!(density_diffusion::DensityDiffusionAntuono, v, u, system, semi) set_zero!(normalized_density_gradient) foreach_point_neighbor(system, system, system_coords, system_coords, semi; - points=each_moving_particle(system)) do particle, neighbor, - pos_diff, distance + points=each_integrated_particle(system)) do particle, neighbor, + pos_diff, distance # Only consider particles with a distance > 0 distance < sqrt(eps(typeof(distance))) && return @@ -207,16 +209,17 @@ function update!(density_diffusion::DensityDiffusionAntuono, v, u, system, semi) return density_diffusion end -@propagate_inbounds function density_diffusion!(dv, density_diffusion::DensityDiffusion, +@propagate_inbounds function density_diffusion!(dv, + density_diffusion::AbstractDensityDiffusion, v_particle_system, particle, neighbor, pos_diff, distance, m_b, rho_a, rho_b, - particle_system::FluidSystem, grad_kernel) + particle_system::AbstractFluidSystem, + grad_kernel) # Density diffusion terms are all zero for distance zero distance < sqrt(eps(typeof(distance))) && return (; delta) = density_diffusion - (; state_equation) = particle_system - (; sound_speed) = state_equation + sound_speed = system_sound_speed(particle_system) volume_b = m_b / rho_b diff --git a/src/schemes/fluid/weakly_compressible_sph/rhs.jl b/src/schemes/fluid/weakly_compressible_sph/rhs.jl index af088e0a1b..413a58aaf6 100644 --- a/src/schemes/fluid/weakly_compressible_sph/rhs.jl +++ b/src/schemes/fluid/weakly_compressible_sph/rhs.jl @@ -19,13 +19,13 @@ function interact!(dv, v_particle_system, u_particle_system, # the following code and the two other lines below that are marked as "debug example". # debug_array = zeros(ndims(particle_system), nparticles(particle_system)) - # Loop over all pairs of particles and neighbors within the kernel cutoff. + # Loop over all pairs of particles and neighbors within the kernel cutoff foreach_point_neighbor(particle_system, neighbor_system, system_coords, neighbor_system_coords, semi; - points=each_moving_particle(particle_system)) do particle, - neighbor, - pos_diff, - distance + points=each_integrated_particle(particle_system)) do particle, + neighbor, + pos_diff, + distance # `foreach_point_neighbor` makes sure that `particle` and `neighbor` are # in bounds of the respective system. For performance reasons, we use `@inbounds` # in this hot loop to avoid bounds checking when extracting particle quantities. @@ -47,7 +47,7 @@ function interact!(dv, v_particle_system, u_particle_system, # The following call is equivalent to # `p_a = current_pressure(v_particle_system, particle_system, particle)` # `p_b = current_pressure(v_neighbor_system, neighbor_system, neighbor)` - # Only when the neighbor system is a `BoundarySPHSystem` or a `TotalLagrangianSPHSystem` + # Only when the neighbor system is a `WallBoundarySystem` or a `TotalLagrangianSPHSystem` # with the boundary model `PressureMirroring`, this will return `p_b = p_a`, which is # the pressure of the fluid particle. p_a, @@ -70,13 +70,12 @@ function interact!(dv, v_particle_system, u_particle_system, sound_speed, m_a, m_b, rho_a, rho_b, grad_kernel) - # Add convection term (only when using `TransportVelocityAdami`) - dv_tvf = dv_transport_velocity(transport_velocity(particle_system), + # Extra terms in the momentum equation when using a shifting technique + dv_tvf = @inbounds dv_shifting(shifting_technique(particle_system), particle_system, neighbor_system, - particle, neighbor, v_particle_system, v_neighbor_system, - m_a, m_b, rho_a, rho_b, pos_diff, distance, - grad_kernel, correction) + particle, neighbor, m_a, m_b, rho_a, rho_b, + pos_diff, distance, grad_kernel, correction) dv_surface_tension = surface_tension_correction * surface_tension_force(surface_tension_a, surface_tension_b, @@ -97,10 +96,10 @@ function interact!(dv, v_particle_system, u_particle_system, # TODO If variable smoothing_length is used, this should use the neighbor smoothing length # Propagate `@inbounds` to the continuity equation, which accesses particle data - @inbounds continuity_equation!(dv, density_calculator, v_particle_system, + @inbounds continuity_equation!(dv, density_calculator, particle_system, + neighbor_system, v_particle_system, v_neighbor_system, particle, neighbor, - pos_diff, distance, m_b, rho_a, rho_b, - particle_system, neighbor_system, grad_kernel) + pos_diff, distance, m_b, rho_a, rho_b, grad_kernel) end # Debug example # periodic_box = neighborhood_search.periodic_box @@ -112,39 +111,6 @@ function interact!(dv, v_particle_system, u_particle_system, return dv end -# With 'SummationDensity', density is calculated in wcsph/system.jl:compute_density! -@inline function continuity_equation!(dv, density_calculator::SummationDensity, - 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 - -# This formulation was chosen to be consistent with the used pressure_acceleration formulations. -@propagate_inbounds function continuity_equation!(dv, density_calculator::ContinuityDensity, - v_particle_system, v_neighbor_system, - particle, neighbor, pos_diff, distance, - m_b, rho_a, rho_b, - particle_system::WeaklyCompressibleSPHSystem, - neighbor_system, grad_kernel) - (; density_diffusion) = particle_system - - vdiff = current_velocity(v_particle_system, particle_system, particle) - - current_velocity(v_neighbor_system, neighbor_system, neighbor) - - dv[end, particle] += rho_a / rho_b * m_b * dot(vdiff, grad_kernel) - - # Artificial density diffusion should only be applied to system(s) representing a fluid - # with the same physical properties i.e. density and viscosity. - # TODO: shouldn't be applied to particles on the interface (depends on PR #539) - if particle_system === neighbor_system - density_diffusion!(dv, density_diffusion, v_particle_system, particle, neighbor, - pos_diff, distance, m_b, rho_a, rho_b, particle_system, - grad_kernel) - end -end - @propagate_inbounds function particle_neighbor_pressure(v_particle_system, v_neighbor_system, particle_system, neighbor_system, @@ -157,7 +123,7 @@ end @inline function particle_neighbor_pressure(v_particle_system, v_neighbor_system, particle_system, - neighbor_system::BoundarySPHSystem{<:BoundaryModelDummyParticles{PressureMirroring}}, + neighbor_system::WallBoundarySystem{<:BoundaryModelDummyParticles{PressureMirroring}}, particle, neighbor) p_a = current_pressure(v_particle_system, particle_system, particle) diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index a5f56499b8..13c69dcf7f 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -5,7 +5,7 @@ acceleration=ntuple(_ -> 0.0, NDIMS), viscosity=nothing, density_diffusion=nothing, pressure_acceleration=nothing, - transport_velocity=nothing, + shifting_technique=nothing, buffer_size=nothing, correction=nothing, source_terms=nothing, surface_tension=nothing, surface_normal_method=nothing, @@ -30,16 +30,18 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method. - `acceleration`: Acceleration vector for the system. (default: zero vector) - `viscosity`: Viscosity model for this system (default: no viscosity). See [`ArtificialViscosityMonaghan`](@ref) or [`ViscosityAdami`](@ref). -- `density_diffusion`: Density diffusion terms for this system. See [`DensityDiffusion`](@ref). +- `density_diffusion`: Density diffusion terms for this system. + See [`AbstractDensityDiffusion`](@ref TrixiParticles.AbstractDensityDiffusion). - `pressure_acceleration`: Pressure acceleration formulation for this system. By default, the correct formulation is chosen based on the density calculator and the correction method. To use [Tensile Instability Control](@ref tic), pass [`tensile_instability_control`](@ref) here. -- `transport_velocity`: [Transport Velocity Formulation (TVF)](@ref transport_velocity_formulation). - Default is no TVF. +- `shifting_technique`: [Shifting technique](@ref shifting) or [transport velocity + formulation](@ref transport_velocity_formulation) to use + with this system. Default is no shifting. - `buffer_size`: Number of buffer particles. - This is needed when simulating with [`OpenBoundarySPHSystem`](@ref). + This is needed when simulating with [`OpenBoundarySystem`](@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, t)` @@ -59,7 +61,8 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method. - `color_value`: The value used to initialize the color of particles in the system. """ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, V, DD, COR, - PF, TV, ST, B, SRFT, SRFN, PR, C} <: FluidSystem{NDIMS} + PF, SC, ST, B, SRFT, SRFN, PR, + C} <: AbstractFluidSystem{NDIMS} initial_condition :: IC mass :: MA # Array{ELTYPE, 1} pressure :: P # Array{ELTYPE, 1} @@ -71,7 +74,7 @@ struct WeaklyCompressibleSPHSystem{NDIMS, ELTYPE <: Real, IC, MA, P, DC, SE, K, density_diffusion :: DD correction :: COR pressure_acceleration_formulation :: PF - transport_velocity :: TV + shifting_technique :: SC source_terms :: ST surface_tension :: SRFT surface_normal_method :: SRFN @@ -89,7 +92,7 @@ function WeaklyCompressibleSPHSystem(initial_condition, ndims(smoothing_kernel)), viscosity=nothing, density_diffusion=nothing, pressure_acceleration=nothing, - transport_velocity=nothing, + shifting_technique=nothing, buffer_size=nothing, correction=nothing, source_terms=nothing, surface_tension=nothing, surface_normal_method=nothing, @@ -147,7 +150,7 @@ function WeaklyCompressibleSPHSystem(initial_condition, n_particles)..., create_cache_refinement(initial_condition, particle_refinement, smoothing_length)..., - create_cache_tvf(initial_condition, transport_velocity)..., + create_cache_shifting(initial_condition, shifting_technique)..., color=Int(color_value)) # If the `reference_density_spacing` is set calculate the `ideal_neighbor_count` @@ -162,7 +165,7 @@ function WeaklyCompressibleSPHSystem(initial_condition, density_calculator, state_equation, smoothing_kernel, acceleration_, viscosity, density_diffusion, correction, pressure_acceleration, - transport_velocity, source_terms, surface_tension, + shifting_technique, source_terms, surface_tension, surface_normal_method, buffer, particle_refinement, cache) end @@ -177,6 +180,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.shifting_technique) print(io, ", ", system.surface_tension) print(io, ", ", system.surface_normal_method) if system.surface_normal_method isa ColorfieldSurfaceNormal @@ -207,9 +211,8 @@ function Base.show(io::IO, ::MIME"text/plain", system::WeaklyCompressibleSPHSyst summary_line(io, "state equation", system.state_equation |> typeof |> nameof) summary_line(io, "smoothing kernel", system.smoothing_kernel |> typeof |> nameof) summary_line(io, "viscosity", system.viscosity) - summary_line(io, "tansport velocity formulation", - system.transport_velocity |> typeof |> nameof) summary_line(io, "density diffusion", system.density_diffusion) + summary_line(io, "shifting technique", system.shifting_technique) summary_line(io, "surface tension", system.surface_tension) summary_line(io, "surface normal method", system.surface_normal_method) if system.surface_normal_method isa ColorfieldSurfaceNormal @@ -237,6 +240,8 @@ end return ndims(system) + 1 end +@inline buffer(system::WeaklyCompressibleSPHSystem) = system.buffer + system_correction(system::WeaklyCompressibleSPHSystem) = system.correction @inline function current_velocity(v, system::WeaklyCompressibleSPHSystem) @@ -278,7 +283,9 @@ end @inline system_sound_speed(system::WeaklyCompressibleSPHSystem) = system.state_equation.sound_speed -@inline transport_velocity(system::WeaklyCompressibleSPHSystem) = system.transport_velocity +@inline shifting_technique(system::WeaklyCompressibleSPHSystem) = system.shifting_technique + +@inline density_diffusion(system::WeaklyCompressibleSPHSystem) = system.density_diffusion function update_quantities!(system::WeaklyCompressibleSPHSystem, v, u, v_ode, u_ode, semi, t) @@ -316,7 +323,7 @@ function update_final!(system::WeaklyCompressibleSPHSystem, v, u, v_ode, u_ode, # Surface normal of neighbor and boundary needs to have been calculated already compute_curvature!(system, surface_tension, v, u, v_ode, u_ode, semi, t) compute_stress_tensors!(system, surface_tension, v, u, v_ode, u_ode, semi, t) - update_tvf!(system, transport_velocity(system), v, u, v_ode, u_ode, semi, t) + update_shifting!(system, shifting_technique(system), v, u, v_ode, u_ode, semi) end function kernel_correct_density!(system::WeaklyCompressibleSPHSystem, v, u, v_ode, u_ode, @@ -405,7 +412,7 @@ function write_v0!(v0, system::WeaklyCompressibleSPHSystem, ::ContinuityDensity) end function restart_with!(system::WeaklyCompressibleSPHSystem, v, u) - for particle in each_moving_particle(system) + for particle in each_integrated_particle(system) system.initial_condition.coordinates[:, particle] .= u[:, particle] system.initial_condition.velocity[:, particle] .= v[1:ndims(system), particle] end @@ -418,7 +425,7 @@ function restart_with!(system, ::SummationDensity, v, u) end function restart_with!(system, ::ContinuityDensity, v, u) - for particle in each_moving_particle(system) + for particle in each_integrated_particle(system) system.initial_condition.density[particle] = v[end, particle] end @@ -429,7 +436,7 @@ end extract_smatrix(system.cache.correction_matrix, system, particle) end -@inline function curvature(particle_system::FluidSystem, particle) +@inline function curvature(particle_system::AbstractFluidSystem, particle) (; cache) = particle_system return cache.curvature[particle] end diff --git a/src/schemes/schemes.jl b/src/schemes/schemes.jl index b81a7768b4..c181d0ed24 100644 --- a/src/schemes/schemes.jl +++ b/src/schemes/schemes.jl @@ -2,15 +2,15 @@ # interactions between the different system types. 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` +include("structure/total_lagrangian_sph/total_lagrangian_sph.jl") +include("structure/discrete_element_method/discrete_element_method.jl") +# Monaghan-Kajtar repulsive boundary particles require the `WallBoundarySystem` # and the `TotalLagrangianSPHSystem`. -include("boundary/monaghan_kajtar/monaghan_kajtar.jl") +include("boundary/wall_boundary/monaghan_kajtar.jl") # Include rhs for all schemes 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") +include("boundary/wall_boundary/rhs.jl") +include("structure/total_lagrangian_sph/rhs.jl") +include("structure/discrete_element_method/rhs.jl") diff --git a/src/schemes/solid/discrete_element_method/contact_models.jl b/src/schemes/structure/discrete_element_method/contact_models.jl similarity index 97% rename from src/schemes/solid/discrete_element_method/contact_models.jl rename to src/schemes/structure/discrete_element_method/contact_models.jl index 398664df0b..5a0220f42c 100644 --- a/src/schemes/solid/discrete_element_method/contact_models.jl +++ b/src/schemes/structure/discrete_element_method/contact_models.jl @@ -1,5 +1,5 @@ # Define an abstract type for contact models. -abstract type ContactModel end +abstract type AbstractContactModel end @doc raw""" HertzContactModel(; elastic_modulus, poissons_ratio) @@ -49,7 +49,7 @@ The total normal force is ``F_n = F_{\text{elastic}} + F_{\text{damping}}``. - `elastic_modulus::Float64`: Material Young's modulus ``E``. - `poissons_ratio::Float64`: Material Poisson's ratio ``\nu``. """ -struct HertzContactModel{ELTYPE <: Real} <: ContactModel +struct HertzContactModel{ELTYPE <: Real} <: AbstractContactModel elastic_modulus::ELTYPE # Material elastic modulus poissons_ratio::ELTYPE # Material Poisson's ratio end @@ -141,7 +141,7 @@ contacting objects. # Fields - `normal_stiffness::Real`: Constant spring stiffness ``k_n`` for the normal direction. """ -struct LinearContactModel{ELTYPE <: Real} <: ContactModel +struct LinearContactModel{ELTYPE <: Real} <: AbstractContactModel normal_stiffness::ELTYPE end diff --git a/src/schemes/solid/discrete_element_method/discrete_element_method.jl b/src/schemes/structure/discrete_element_method/discrete_element_method.jl similarity index 100% rename from src/schemes/solid/discrete_element_method/discrete_element_method.jl rename to src/schemes/structure/discrete_element_method/discrete_element_method.jl diff --git a/src/schemes/solid/discrete_element_method/rhs.jl b/src/schemes/structure/discrete_element_method/rhs.jl similarity index 96% rename from src/schemes/solid/discrete_element_method/rhs.jl rename to src/schemes/structure/discrete_element_method/rhs.jl index 969fe720bb..77e548cf7d 100644 --- a/src/schemes/solid/discrete_element_method/rhs.jl +++ b/src/schemes/structure/discrete_element_method/rhs.jl @@ -8,10 +8,10 @@ function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, foreach_point_neighbor(particle_system, neighbor_system, system_coords, neighbor_coords, semi; - points=each_moving_particle(particle_system)) do particle, - neighbor, - pos_diff, - distance + points=each_integrated_particle(particle_system)) do particle, + neighbor, + pos_diff, + distance distance < sqrt(eps()) && return # Retrieve particle properties diff --git a/src/schemes/solid/discrete_element_method/system.jl b/src/schemes/structure/discrete_element_method/system.jl similarity index 96% rename from src/schemes/solid/discrete_element_method/system.jl rename to src/schemes/structure/discrete_element_method/system.jl index 1b65c7e264..5f6220d9e4 100644 --- a/src/schemes/solid/discrete_element_method/system.jl +++ b/src/schemes/structure/discrete_element_method/system.jl @@ -29,7 +29,8 @@ specified material properties and contact mechanics. ## References [Bicanic2004](@cite), [Cundall1979](@cite), [DiRenzo2004](@cite) """ -struct DEMSystem{NDIMS, ELTYPE <: Real, IC, ARRAY1D, ST, CM} <: SolidSystem{NDIMS} +struct DEMSystem{NDIMS, ELTYPE <: Real, IC, ARRAY1D, ST, + CM} <: AbstractStructureSystem{NDIMS} initial_condition :: IC mass :: ARRAY1D # [particle] radius :: ARRAY1D # [particle] @@ -37,7 +38,6 @@ struct DEMSystem{NDIMS, ELTYPE <: Real, IC, ARRAY1D, ST, CM} <: SolidSystem{NDIM acceleration :: SVector{NDIMS, ELTYPE} source_terms :: ST contact_model :: CM - buffer :: Nothing function DEMSystem(initial_condition, contact_model; damping_coefficient=0.0001, acceleration=ntuple(_ -> 0.0, @@ -65,7 +65,7 @@ struct DEMSystem{NDIMS, ELTYPE <: Real, IC, ARRAY1D, ST, CM} <: SolidSystem{NDIM typeof(mass), typeof(source_terms), typeof(contact_model)}(initial_condition, mass, radius, damping_coefficient, acceleration_, source_terms, - contact_model, nothing) + contact_model) end end @@ -150,7 +150,7 @@ end return system.radius[particle] end -function system_data(system::DEMSystem, v_ode, u_ode, semi) +function system_data(system::DEMSystem, dv_ode, du_ode, v_ode, u_ode, semi) (; mass, radius, damping_coefficient) = system v = wrap_v(v_ode, system, semi) diff --git a/src/schemes/solid/total_lagrangian_sph/penalty_force.jl b/src/schemes/structure/total_lagrangian_sph/penalty_force.jl similarity index 100% rename from src/schemes/solid/total_lagrangian_sph/penalty_force.jl rename to src/schemes/structure/total_lagrangian_sph/penalty_force.jl diff --git a/src/schemes/solid/total_lagrangian_sph/rhs.jl b/src/schemes/structure/total_lagrangian_sph/rhs.jl similarity index 81% rename from src/schemes/solid/total_lagrangian_sph/rhs.jl rename to src/schemes/structure/total_lagrangian_sph/rhs.jl index e804263427..434d2d93df 100644 --- a/src/schemes/solid/total_lagrangian_sph/rhs.jl +++ b/src/schemes/structure/total_lagrangian_sph/rhs.jl @@ -1,28 +1,28 @@ -# Solid-solid interaction +# Structure-structure interaction function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, particle_system::TotalLagrangianSPHSystem, neighbor_system::TotalLagrangianSPHSystem, semi) - # Different solids do not interact with each other (yet) + # Different structures do not interact with each other (yet) particle_system !== neighbor_system && return dv - interact_solid_solid!(dv, v_particle_system, particle_system, semi) + interact_structure_structure!(dv, v_particle_system, particle_system, semi) end # Function barrier without dispatch for unit testing -@inline function interact_solid_solid!(dv, v_system, system, semi) +@inline function interact_structure_structure!(dv, v_system, system, semi) (; penalty_force) = system # Everything here is done in the initial coordinates system_coords = initial_coordinates(system) # Loop over all pairs of particles and neighbors within the kernel cutoff. - # For solid-solid interaction, this has to happen in the initial coordinates. + # For structure-structure interaction, this has to happen in the initial coordinates. foreach_point_neighbor(system, system, system_coords, system_coords, semi; - points=each_moving_particle(system)) do particle, neighbor, - initial_pos_diff, - initial_distance - # Only consider particles with a distance > 0. + points=each_integrated_particle(system)) do particle, neighbor, + initial_pos_diff, + initial_distance + # Only consider particles with a distance > 0 initial_distance < sqrt(eps()) && return rho_a = @inbounds system.material_density[particle] @@ -63,32 +63,32 @@ end return dv end -# Solid-fluid interaction +# Structure-fluid interaction function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, particle_system::TotalLagrangianSPHSystem, - neighbor_system::FluidSystem, semi) + neighbor_system::AbstractFluidSystem, semi) sound_speed = system_sound_speed(neighbor_system) system_coords = current_coordinates(u_particle_system, particle_system) neighbor_coords = current_coordinates(u_neighbor_system, neighbor_system) - # Loop over all pairs of particles and neighbors within the kernel cutoff. + # Loop over all pairs of particles and neighbors within the kernel cutoff foreach_point_neighbor(particle_system, neighbor_system, system_coords, neighbor_coords, semi; - points=each_moving_particle(particle_system)) do particle, - neighbor, - pos_diff, - distance - # Only consider particles with a distance > 0. + points=each_integrated_particle(particle_system)) do particle, + neighbor, + pos_diff, + distance + # Only consider particles with a distance > 0 distance < sqrt(eps()) && return - # Apply the same force to the solid particle - # that the fluid particle experiences due to the solid particle. - # Note that the same arguments are passed here as in fluid-solid interact!, + # Apply the same force to the structure particle + # that the fluid particle experiences due to the structure particle. + # Note that the same arguments are passed here as in fluid-structure interact!, # except that pos_diff has a flipped sign. # - # In fluid-solid interaction, use the "hydrodynamic mass" of the solid particles + # In fluid-structure interaction, use the "hydrodynamic mass" of the structure particles # corresponding to the rest density of the fluid and not the material density. m_a = hydrodynamic_mass(particle_system, particle) m_b = hydrodynamic_mass(neighbor_system, neighbor) @@ -97,19 +97,19 @@ function interact!(dv, v_particle_system, u_particle_system, rho_b = current_density(v_neighbor_system, neighbor_system, neighbor) # Use kernel from the fluid system in order to get the same force here in - # solid-fluid interaction as for fluid-solid interaction. + # structure-fluid interaction as for fluid-structure interaction. # TODO this will not use corrections if the fluid uses corrections. grad_kernel = smoothing_kernel_grad(neighbor_system, pos_diff, distance, particle) - # In fluid-solid interaction, use the "hydrodynamic pressure" of the solid particles + # In fluid-structure interaction, use the "hydrodynamic pressure" of the structure particles # corresponding to the chosen boundary model. p_a = current_pressure(v_particle_system, particle_system, particle) p_b = current_pressure(v_neighbor_system, neighbor_system, neighbor) # Particle and neighbor (and corresponding systems and all corresponding quantities) # are switched in the following two calls. - # This way, we obtain the exact same force as for the fluid-solid interaction, - # but with a flipped sign (because `pos_diff` is flipped compared to fluid-solid). + # This way, we obtain the exact same force as for the fluid-structure interaction, + # but with a flipped sign (because `pos_diff` is flipped compared to fluid-structure). dv_boundary = pressure_acceleration(neighbor_system, particle_system, neighbor, particle, m_b, m_a, p_b, p_a, rho_b, rho_a, pos_diff, @@ -125,9 +125,9 @@ function interact!(dv, v_particle_system, u_particle_system, for i in 1:ndims(particle_system) # Multiply `dv` (acceleration on fluid particle b) by the mass of - # particle b to obtain the same force as for the fluid-solid interaction. + # particle b to obtain the same force as for the fluid-structure interaction. # Divide by the material mass of particle a to obtain the acceleration - # of solid particle a. + # of structure particle a. dv[i, particle] += dv_particle[i] * m_b / particle_system.mass[particle] end @@ -144,7 +144,7 @@ end particle, neighbor, pos_diff, distance, m_b, rho_a, rho_b, particle_system::TotalLagrangianSPHSystem, - neighbor_system::FluidSystem, + neighbor_system::AbstractFluidSystem, grad_kernel) return dv end @@ -153,7 +153,7 @@ end particle, neighbor, pos_diff, distance, m_b, rho_a, rho_b, particle_system::TotalLagrangianSPHSystem{<:BoundaryModelDummyParticles{ContinuityDensity}}, - neighbor_system::FluidSystem, + neighbor_system::AbstractFluidSystem, grad_kernel) fluid_density_calculator = neighbor_system.density_calculator @@ -165,11 +165,11 @@ end grad_kernel, particle) end -# Solid-boundary interaction +# Structure-boundary interaction function interact!(dv, v_particle_system, u_particle_system, v_neighbor_system, u_neighbor_system, particle_system::TotalLagrangianSPHSystem, - neighbor_system::Union{BoundarySPHSystem, OpenBoundarySPHSystem}, semi) + neighbor_system::Union{WallBoundarySystem, OpenBoundarySystem}, semi) # TODO continuity equation? return dv end diff --git a/src/schemes/solid/total_lagrangian_sph/system.jl b/src/schemes/structure/total_lagrangian_sph/system.jl similarity index 84% rename from src/schemes/solid/total_lagrangian_sph/system.jl rename to src/schemes/structure/total_lagrangian_sph/system.jl index f843fdaf33..10c64b0430 100644 --- a/src/schemes/solid/total_lagrangian_sph/system.jl +++ b/src/schemes/structure/total_lagrangian_sph/system.jl @@ -2,7 +2,7 @@ TotalLagrangianSPHSystem(initial_condition, smoothing_kernel, smoothing_length, young_modulus, poisson_ratio; - n_fixed_particles=0, boundary_model=nothing, + n_clamped_particles=0, boundary_model=nothing, acceleration=ntuple(_ -> 0.0, NDIMS), penalty_force=nothing, source_terms=nothing) @@ -23,9 +23,9 @@ See [Total Lagrangian SPH](@ref tlsph) for more details on the method. See [Smoothing Kernels](@ref smoothing_kernel). # Keyword Arguments -- `n_fixed_particles`: Number of fixed particles which are used to clamp the structure - particles. Note that the fixed particles must be the **last** - particles in the `InitialCondition`. See the info box below. +- `n_clamped_particles`: Number of clamped particles which are fixed and not integrated + to clamp the structure. Note that the clamped particles must be the **last** + particles in the `InitialCondition`. See the info box below. - `boundary_model`: Boundary model to compute the hydrodynamic density and pressure for fluid-structure interaction (see [Boundary Models](@ref boundary_models)). - `penalty_force`: Penalty force to ensure regular particle position under large deformations @@ -40,10 +40,10 @@ See [Total Lagrangian SPH](@ref tlsph) for more details on the method. See, for example, [`SourceTermDamping`](@ref). !!! note - The fixed particles must be the **last** particles in the `InitialCondition`. + The clamped particles must be the **last** particles in the `InitialCondition`. To do so, e.g. use the `union` function: - ```jldoctest; output = false, setup = :(fixed_particles = RectangularShape(0.1, (1, 4), (0.0, 0.0), density=1.0); beam = RectangularShape(0.1, (3, 4), (0.1, 0.0), density=1.0)) - solid = union(beam, fixed_particles) + ```jldoctest; output = false, setup = :(clamped_particles = RectangularShape(0.1, (1, 4), (0.0, 0.0), density=1.0); beam = RectangularShape(0.1, (3, 4), (0.1, 0.0), density=1.0)) + structure = union(beam, clamped_particles) # output ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ @@ -54,37 +54,37 @@ See [Total Lagrangian SPH](@ref tlsph) for more details on the method. │ particle spacing: ………………………………… 0.1 │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` - where `beam` and `fixed_particles` are of type `InitialCondition`. + where `beam` and `clamped_particles` are of type `InitialCondition`. """ struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D, ARRAY3D, - YM, PR, LL, LM, K, PF, V, 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 :: YM - poisson_ratio :: PR - lame_lambda :: LL - lame_mu :: LM - smoothing_kernel :: K - smoothing_length :: ELTYPE - acceleration :: SVector{NDIMS, ELTYPE} - boundary_model :: BM - penalty_force :: PF - viscosity :: V - source_terms :: ST - buffer :: Nothing + YM, PR, LL, LM, K, PF, V, + ST} <: AbstractStructureSystem{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_integrated_particles :: Int64 + young_modulus :: YM + poisson_ratio :: PR + lame_lambda :: LL + lame_mu :: LM + smoothing_kernel :: K + smoothing_length :: ELTYPE + acceleration :: SVector{NDIMS, ELTYPE} + boundary_model :: BM + penalty_force :: PF + viscosity :: V + source_terms :: ST end function TotalLagrangianSPHSystem(initial_condition, smoothing_kernel, smoothing_length, young_modulus, poisson_ratio; - n_fixed_particles=0, boundary_model=nothing, + n_clamped_particles=0, boundary_model=nothing, acceleration=ntuple(_ -> 0.0, ndims(smoothing_kernel)), penalty_force=nothing, viscosity=nothing, @@ -111,7 +111,7 @@ function TotalLagrangianSPHSystem(initial_condition, 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 + n_integrated_particles = n_particles - n_clamped_particles lame_lambda = @. young_modulus * poisson_ratio / ((1 + poisson_ratio) * (1 - 2 * poisson_ratio)) @@ -120,10 +120,10 @@ function TotalLagrangianSPHSystem(initial_condition, return TotalLagrangianSPHSystem(initial_condition, initial_coordinates, current_coordinates, mass, correction_matrix, pk1_corrected, deformation_grad, material_density, - n_moving_particles, young_modulus, poisson_ratio, + n_integrated_particles, young_modulus, poisson_ratio, lame_lambda, lame_mu, smoothing_kernel, smoothing_length, acceleration_, boundary_model, - penalty_force, viscosity, source_terms, nothing) + penalty_force, viscosity, source_terms) end function Base.show(io::IO, system::TotalLagrangianSPHSystem) @@ -154,11 +154,11 @@ function Base.show(io::IO, ::MIME"text/plain", system::TotalLagrangianSPHSystem) if get(io, :compact, false) show(io, system) else - n_fixed_particles = nparticles(system) - n_moving_particles(system) + n_clamped_particles = nparticles(system) - n_integrated_particles(system) summary_header(io, "TotalLagrangianSPHSystem{$(ndims(system))}") summary_line(io, "total #particles", nparticles(system)) - summary_line(io, "#fixed particles", n_fixed_particles) + summary_line(io, "#clamped particles", n_clamped_particles) summary_line(io, "Young's modulus", display_param(system.young_modulus)) summary_line(io, "Poisson ratio", display_param(system.poisson_ratio)) summary_line(io, "smoothing kernel", system.smoothing_kernel |> typeof |> nameof) @@ -182,8 +182,8 @@ end return ndims(system) + 1 end -@inline function n_moving_particles(system::TotalLagrangianSPHSystem) - system.n_moving_particles +@inline function n_integrated_particles(system::TotalLagrangianSPHSystem) + system.n_integrated_particles end @inline initial_coordinates(system::TotalLagrangianSPHSystem) = system.initial_coordinates @@ -200,7 +200,7 @@ end end @inline function current_velocity(v, system::TotalLagrangianSPHSystem, particle) - if particle > n_moving_particles(system) + if particle > n_integrated_particles(system) return zero(SVector{ndims(system), eltype(system)}) end @@ -215,7 +215,7 @@ end return current_density(v, system.boundary_model, system) end -# In fluid-solid interaction, use the "hydrodynamic pressure" of the solid particles +# In fluid-structure interaction, use the "hydrodynamic pressure" of the structure particles # corresponding to the chosen boundary model. @inline function current_pressure(v, system::TotalLagrangianSPHSystem) return current_pressure(v, system.boundary_model, system) @@ -277,9 +277,9 @@ end function update_positions!(system::TotalLagrangianSPHSystem, v, u, v_ode, u_ode, semi, t) (; current_coordinates) = system - # `current_coordinates` stores the coordinates of both integrated and fixed particles. + # `current_coordinates` stores the coordinates of both integrated and clamped particles. # Copy the coordinates of the integrated particles from `u`. - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in each_integrated_particle(system) for i in 1:ndims(system) current_coordinates[i, particle] = u[i, particle] end @@ -381,7 +381,7 @@ function write_u0!(u0, system::TotalLagrangianSPHSystem) (; initial_condition) = system # This is as fast as a loop with `@inbounds`, but it's GPU-compatible - indices = CartesianIndices((ndims(system), each_moving_particle(system))) + indices = CartesianIndices((ndims(system), each_integrated_particle(system))) copyto!(u0, indices, initial_condition.coordinates, indices) return u0 @@ -391,7 +391,7 @@ function write_v0!(v0, system::TotalLagrangianSPHSystem) (; initial_condition, boundary_model) = system # This is as fast as a loop with `@inbounds`, but it's GPU-compatible - indices = CartesianIndices((ndims(system), each_moving_particle(system))) + indices = CartesianIndices((ndims(system), each_integrated_particle(system))) copyto!(v0, indices, initial_condition.velocity, indices) write_v0!(v0, boundary_model, system) @@ -408,7 +408,7 @@ function write_v0!(v0, ::BoundaryModelDummyParticles{ContinuityDensity}, (; cache) = system.boundary_model (; initial_density) = cache - for particle in each_moving_particle(system) + for particle in each_integrated_particle(system) # Set particle densities v0[ndims(system) + 1, particle] = initial_density[particle] end @@ -417,7 +417,7 @@ function write_v0!(v0, ::BoundaryModelDummyParticles{ContinuityDensity}, end function restart_with!(system::TotalLagrangianSPHSystem, v, u) - for particle in each_moving_particle(system) + for particle in each_integrated_particle(system) system.current_coordinates[:, particle] .= u[:, particle] system.initial_condition.velocity[:, particle] .= v[1:ndims(system), particle] end @@ -435,7 +435,7 @@ function von_mises_stress(system) von_mises_stress_vector = zeros(eltype(system.pk1_corrected), nparticles(system)) @threaded default_backend(von_mises_stress_vector) for particle in - each_moving_particle(system) + each_integrated_particle(system) von_mises_stress_vector[particle] = von_mises_stress(system, particle) end @@ -469,7 +469,7 @@ function cauchy_stress(system::TotalLagrangianSPHSystem) nparticles(system)) @threaded default_backend(cauchy_stress_tensors) for particle in - each_moving_particle(system) + each_integrated_particle(system) F = deformation_gradient(system, particle) J = det(F) P = pk1_corrected(system, particle) @@ -486,15 +486,16 @@ end return neighbor_system.viscosity end -@inline function viscosity_model(system::FluidSystem, +@inline function viscosity_model(system::AbstractFluidSystem, neighbor_system::TotalLagrangianSPHSystem) return neighbor_system.boundary_model.viscosity end -function system_data(system::TotalLagrangianSPHSystem, v_ode, u_ode, semi) +function system_data(system::TotalLagrangianSPHSystem, dv_ode, du_ode, v_ode, u_ode, semi) (; mass, material_density, deformation_grad, pk1_corrected, young_modulus, poisson_ratio, lame_lambda, lame_mu) = system + dv = wrap_v(dv_ode, system, semi) v = wrap_v(v_ode, system, semi) u = wrap_u(u_ode, system, semi) @@ -504,11 +505,11 @@ function system_data(system::TotalLagrangianSPHSystem, v_ode, u_ode, semi) return (; coordinates, initial_coordinates=initial_coordinates_, velocity, mass, material_density, deformation_grad, pk1_corrected, young_modulus, poisson_ratio, - lame_lambda, lame_mu) + lame_lambda, lame_mu, acceleration=current_velocity(dv, system)) end function available_data(::TotalLagrangianSPHSystem) return (:coordinates, :initial_coordinates, :velocity, :mass, :material_density, :deformation_grad, :pk1_corrected, :young_modulus, :poisson_ratio, - :lame_lambda, :lame_mu) + :lame_lambda, :lame_mu, :acceleration) end diff --git a/src/schemes/solid/total_lagrangian_sph/total_lagrangian_sph.jl b/src/schemes/structure/total_lagrangian_sph/total_lagrangian_sph.jl similarity index 100% rename from src/schemes/solid/total_lagrangian_sph/total_lagrangian_sph.jl rename to src/schemes/structure/total_lagrangian_sph/total_lagrangian_sph.jl diff --git a/src/schemes/solid/total_lagrangian_sph/viscosity.jl b/src/schemes/structure/total_lagrangian_sph/viscosity.jl similarity index 100% rename from src/schemes/solid/total_lagrangian_sph/viscosity.jl rename to src/schemes/structure/total_lagrangian_sph/viscosity.jl diff --git a/src/setups/complex_shape.jl b/src/setups/complex_shape.jl index cdb8e146b6..032250ce56 100644 --- a/src/setups/complex_shape.jl +++ b/src/setups/complex_shape.jl @@ -79,7 +79,7 @@ end """ sample_boundary(signed_distance_field; - boundary_density, boundary_thickness, tlsph=true) + boundary_density, boundary_thickness, place_on_shell=true) Sample boundary particles of a complex geometry by using the [`SignedDistanceField`](@ref) of the geometry. @@ -90,9 +90,9 @@ of the geometry. # Keywords - `boundary_thickness`: Thickness of the boundary - `boundary_density`: Density of each boundary particle. -- `tlsph` : When `tlsph=true`, boundary particles will be placed +- `place_on_shell`: When `place_on_shell=true`, boundary particles will be placed one particle spacing from the surface of the geometry. - Otherwise when `tlsph=true` (simulating fluid particles), + Otherwise when `place_on_shell=true` (simulating fluid particles), boundary particles will be placed half particle spacing away from the surface. @@ -118,7 +118,7 @@ boundary_sampled = sample_boundary(signed_distance_field; boundary_density=1.0, ``` """ function sample_boundary(signed_distance_field; - boundary_density, boundary_thickness, tlsph=true) + boundary_density, boundary_thickness, place_on_shell=true) (; max_signed_distance, boundary_packing, positions, distances, particle_spacing) = signed_distance_field @@ -158,6 +158,6 @@ function particle_grid(geometry, particle_spacing; end grid = rectangular_shape_coords(particle_spacing, n_particles_per_dimension, - min_corner; tlsph=true) + min_corner; place_on_shell=true) return reinterpret(reshape, SVector{ndims(geometry), eltype(geometry)}, grid) end diff --git a/src/setups/extrude_geometry.jl b/src/setups/extrude_geometry.jl index 498266ecd0..6d169762c7 100644 --- a/src/setups/extrude_geometry.jl +++ b/src/setups/extrude_geometry.jl @@ -30,9 +30,11 @@ Returns an [`InitialCondition`](@ref). - `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. -- `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. +- `place_on_shell`: If `place_on_shell=true`, particles will be placed + on the shell of the geometry. For example, + the [`TotalLagrangianSPHSystem`](@ref) requires particles to be placed + on the shell of the geometry and not half a particle spacing away, + as for fluids. # Examples ```jldoctest; output = false @@ -79,7 +81,7 @@ shape = extrude_geometry(shape; direction, particle_spacing=0.1, n_extrude=4, de 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, + velocity=zeros(length(direction)), place_on_shell=false, mass=nothing, density=nothing, pressure=0.0) direction_ = normalize(direction) NDIMS = length(direction_) @@ -95,9 +97,11 @@ function extrude_geometry(geometry; particle_spacing=-1, direction, n_extrude::I throw(ArgumentError("`particle_spacing` must be specified when not extruding an `InitialCondition`")) end - geometry = shift_plane_corners(geometry, direction_, particle_spacing, tlsph) + geometry = shift_plane_corners(geometry, direction_, particle_spacing, place_on_shell) - face_coords, particle_spacing_ = sample_plane(geometry, particle_spacing; tlsph=tlsph) + face_coords, + particle_spacing_ = sample_plane(geometry, particle_spacing; + place_on_shell=place_on_shell) if !isapprox(particle_spacing, particle_spacing_, rtol=5e-2) @info "The desired size is not a multiple of the particle spacing $particle_spacing." * @@ -119,12 +123,13 @@ 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) +function sample_plane(geometry::AbstractMatrix, particle_spacing; place_on_shell) 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 : particle_spacing / 2, size(geometry, 2))') + # When `place_on_shell=true`, particles will be placed on the x-y plane + coords = vcat(geometry, + fill(place_on_shell ? 0 : particle_spacing / 2, size(geometry, 2))') # TODO: 2D shapes not only in x-y plane but in any user-defined plane return coords, particle_spacing @@ -133,13 +138,14 @@ function sample_plane(geometry::AbstractMatrix, particle_spacing; tlsph) return geometry, particle_spacing end -function sample_plane(shape::InitialCondition, particle_spacing; tlsph) +function sample_plane(shape::InitialCondition, particle_spacing; place_on_shell) 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 + # When `place_on_shell=true`, particles will be placed on the x-y plane coords = vcat(shape.coordinates, - fill(tlsph ? 0 : particle_spacing / 2, size(shape.coordinates, 2))') + fill(place_on_shell ? 0 : particle_spacing / 2, + size(shape.coordinates, 2))') # TODO: 2D shapes not only in x-y plane but in any user-defined plane return coords, particle_spacing @@ -148,13 +154,13 @@ function sample_plane(shape::InitialCondition, particle_spacing; tlsph) return shape.coordinates, particle_spacing end -function sample_plane(plane_points, particle_spacing; tlsph=nothing) +function sample_plane(plane_points, particle_spacing; place_on_shell=nothing) # Convert to tuple - return sample_plane(tuple(plane_points...), particle_spacing; tlsph=nothing) + return sample_plane(tuple(plane_points...), particle_spacing; place_on_shell=nothing) end -function sample_plane(plane_points::NTuple{2}, particle_spacing; tlsph=nothing) +function sample_plane(plane_points::NTuple{2}, particle_spacing; place_on_shell=nothing) # Verify that points are in 2D space if any(length.(plane_points) .!= 2) throw(ArgumentError("all points must be 2D coordinates")) @@ -168,7 +174,7 @@ function sample_plane(plane_points::NTuple{2}, particle_spacing; tlsph=nothing) return coords, particle_spacing_new end -function sample_plane(plane_points::NTuple{3}, particle_spacing; tlsph=nothing) +function sample_plane(plane_points::NTuple{3}, particle_spacing; place_on_shell=nothing) # Verify that points are in 3D space if any(length.(plane_points) .!= 3) throw(ArgumentError("all points must be 3D coordinates")) @@ -209,21 +215,22 @@ function sample_plane(plane_points::NTuple{3}, particle_spacing; tlsph=nothing) return coords, particle_spacing_new end -# Shift corners of the plane/line inwards by half a particle spacing with `tlsph=false` +# Shift corners of the plane/line inwards by half a particle spacing with `place_on_shell=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) + direction, particle_spacing, place_on_shell) return geometry end -function shift_plane_corners(plane_points, direction, particle_spacing, tlsph) - shift_plane_corners(tuple(plane_points...), direction, particle_spacing, tlsph) +function shift_plane_corners(plane_points, direction, particle_spacing, place_on_shell) + shift_plane_corners(tuple(plane_points...), direction, particle_spacing, place_on_shell) 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 +function shift_plane_corners(plane_points::NTuple{2}, direction, particle_spacing, + place_on_shell) + # With `place_on_shell`, particles need to be AT the min coordinates and not half a particle # spacing away from it. - (tlsph) && (return plane_points) + (place_on_shell) && (return plane_points) plane_point1 = copy(plane_points[1]) plane_point2 = copy(plane_points[2]) @@ -238,10 +245,11 @@ function shift_plane_corners(plane_points::NTuple{2}, direction, particle_spacin 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 +function shift_plane_corners(plane_points::NTuple{3}, direction, particle_spacing, + place_on_shell) + # With `place_on_shell`, particles need to be AT the min coordinates and not half a particle # spacing away from it. - (tlsph) && (return plane_points) + (place_on_shell) && (return plane_points) plane_point1 = copy(plane_points[1]) plane_point2 = copy(plane_points[2]) diff --git a/src/setups/rectangular_shape.jl b/src/setups/rectangular_shape.jl index 98160bcac9..1c30ef5c1b 100644 --- a/src/setups/rectangular_shape.jl +++ b/src/setups/rectangular_shape.jl @@ -3,7 +3,7 @@ velocity=zeros(length(n_particles_per_dimension)), mass=nothing, density=nothing, pressure=0.0, acceleration=nothing, state_equation=nothing, - tlsph=false, loop_order=nothing) + place_on_shell=false, loop_order=nothing) Rectangular shape filled with particles. Returns an [`InitialCondition`](@ref). @@ -40,9 +40,10 @@ Rectangular shape filled with particles. Returns an [`InitialCondition`](@ref). - `state_equation`: When calculating a hydrostatic pressure gradient by setting `acceleration`, the `state_equation` will be used to set the corresponding density. Cannot be used together with `density`. -- `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. +- `place_on_shell`: If `place_on_shell=true`, particles will be placed on the shell of the shape. + For example, the [`TotalLagrangianSPHSystem`](@ref) requires particles + to be placed on the shell of the shape and not half a particle spacing away, + as for fluids. - `coordinates_perturbation`: Add a small random displacement to the particle positions, where the amplitude is `coordinates_perturbation * particle_spacing`. @@ -75,7 +76,7 @@ function RectangularShape(particle_spacing, n_particles_per_dimension, min_coord coordinates_perturbation=nothing, mass=nothing, density=nothing, pressure=0.0, acceleration=nothing, state_equation=nothing, - tlsph=false, loop_order=nothing) + place_on_shell=false, loop_order=nothing) if particle_spacing < eps() throw(ArgumentError("`particle_spacing` needs to be positive and larger than $(eps())")) end @@ -95,7 +96,7 @@ function RectangularShape(particle_spacing, n_particles_per_dimension, min_coord n_particles = prod(n_particles_per_dimension) coordinates = rectangular_shape_coords(particle_spacing, n_particles_per_dimension, - min_coordinates, tlsph=tlsph, + min_coordinates, place_on_shell=place_on_shell, loop_order=loop_order) if !isnothing(coordinates_perturbation) @@ -190,15 +191,15 @@ function loop_permutation(loop_order, NDIMS::Val{3}) end function rectangular_shape_coords(particle_spacing, n_particles_per_dimension, - min_coordinates; tlsph=false, loop_order=nothing) + min_coordinates; place_on_shell=false, loop_order=nothing) ELTYPE = eltype(particle_spacing) NDIMS = length(n_particles_per_dimension) coordinates = Array{ELTYPE, 2}(undef, NDIMS, prod(n_particles_per_dimension)) - # With TLSPH, particles need to be AT the min coordinates and not half a particle + # With place_on_shell, particles need to be AT the min coordinates and not half a particle # spacing away from it. - if tlsph + if place_on_shell min_coordinates = min_coordinates .- 0.5particle_spacing end diff --git a/src/setups/sphere_shape.jl b/src/setups/sphere_shape.jl index 0233fc8995..3c6a7466d5 100644 --- a/src/setups/sphere_shape.jl +++ b/src/setups/sphere_shape.jl @@ -1,7 +1,7 @@ """ SphereShape(particle_spacing, radius, center_position, density; sphere_type=VoxelSphere(), n_layers=-1, layer_outwards=false, - cutout_min=(0.0, 0.0), cutout_max=(0.0, 0.0), tlsph=false, + cutout_min=(0.0, 0.0), cutout_max=(0.0, 0.0), place_on_shell=false, velocity=zeros(length(center_position)), mass=nothing, pressure=0.0) Generate a sphere that is either completely filled (by default) @@ -35,18 +35,19 @@ coordinate directions as `cutout_min` and `cutout_max`. cut out of the sphere. - `cutout_max`: Corner in positive coordinate directions of a cuboid that is to be cut out of the sphere. -- `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. -- `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. -- `pressure`: Either a function mapping each particle's coordinates to its pressure, - or a scalar for a constant pressure over all particles. This is optional and - only needed when using the [`EntropicallyDampedSPHSystem`](@ref). +- `place_on_shell`: If `place_on_shell=true`, particles will be placed on the shell of the shape. + For example, the [`TotalLagrangianSPHSystem`](@ref) requires particles + to be placed on the shell of the shape and not half a particle spacing away, + as for fluids. +- `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. +- `pressure`: Either a function mapping each particle's coordinates to its pressure, + or a scalar for a constant pressure over all particles. This is optional and + only needed when using the [`EntropicallyDampedSPHSystem`](@ref). # Examples ```jldoctest; output = false @@ -89,7 +90,7 @@ SphereShape(0.1, 0.5, (0.2, 0.4, 0.3), 1000.0, sphere_type=RoundSphere()) """ function SphereShape(particle_spacing, radius, center_position, density; sphere_type=VoxelSphere(), n_layers=-1, layer_outwards=false, - cutout_min=(0.0, 0.0), cutout_max=(0.0, 0.0), tlsph=false, + cutout_min=(0.0, 0.0), cutout_max=(0.0, 0.0), place_on_shell=false, velocity=zeros(length(center_position)), mass=nothing, pressure=0) if particle_spacing < eps() throw(ArgumentError("`particle_spacing` needs to be positive and larger than $(eps())")) @@ -99,7 +100,7 @@ function SphereShape(particle_spacing, radius, center_position, density; coordinates = sphere_shape_coords(sphere_type, particle_spacing, radius, SVector{NDIMS}(center_position), - n_layers, layer_outwards, tlsph) + n_layers, layer_outwards, place_on_shell) # Convert tuples to vectors cutout_min_ = collect(cutout_min) @@ -169,13 +170,13 @@ struct RoundSphere{AR} end function sphere_shape_coords(::VoxelSphere, particle_spacing, radius, center_position, - n_layers, layer_outwards, tlsph) + n_layers, layer_outwards, place_on_shell) if n_layers > 0 if layer_outwards inner_radius = radius outer_radius = radius + n_layers * particle_spacing - if !tlsph + if !place_on_shell # Put first layer of particles half a particle spacing outside of `radius` inner_radius += particle_spacing / 2 outer_radius += particle_spacing / 2 @@ -184,7 +185,7 @@ function sphere_shape_coords(::VoxelSphere, particle_spacing, radius, center_pos inner_radius = radius - n_layers * particle_spacing outer_radius = radius - if !tlsph + if !place_on_shell # Put first layer of particles half a particle spacing inside of `radius` inner_radius -= particle_spacing / 2 outer_radius -= particle_spacing / 2 @@ -194,7 +195,7 @@ function sphere_shape_coords(::VoxelSphere, particle_spacing, radius, center_pos outer_radius = radius inner_radius = -1 - if !tlsph + if !place_on_shell # Put first layer of particles half a particle spacing inside of `radius` outer_radius -= particle_spacing / 2 end @@ -225,7 +226,7 @@ function sphere_shape_coords(::VoxelSphere, particle_spacing, radius, center_pos end function sphere_shape_coords(sphere::RoundSphere, particle_spacing, radius, center, - n_layers, layer_outwards, tlsph) + n_layers, layer_outwards, place_on_shell) if n_layers > 0 if layer_outwards inner_radius = radius @@ -233,12 +234,12 @@ function sphere_shape_coords(sphere::RoundSphere, particle_spacing, radius, cent inner_radius = radius - n_layers * particle_spacing end - if !tlsph + if !place_on_shell # Put first layer of particles half a particle spacing outside of inner radius inner_radius += particle_spacing / 2 end else - if tlsph + if place_on_shell # Just create a sphere that is 0.5 particle spacing larger radius += particle_spacing / 2 end diff --git a/src/util.jl b/src/util.jl index 88be2096a2..48f5fe4be6 100644 --- a/src/util.jl +++ b/src/util.jl @@ -11,6 +11,17 @@ end @inline foreach_noalloc(func, collection::Tuple{}) = nothing +# Returns `functions[index](args...)`, but in a type-stable way for a heterogeneous tuple `functions` +@inline function apply_ith_function(functions, index, args...) + if index == 1 + # Found the function to apply, apply it and return + return first(functions)(args...) + end + + # Process remaining functions + apply_ith_function(Base.tail(functions), index - 1, args...) +end + # Print informative message at startup function print_startup_message() s = """ diff --git a/test/callbacks/solution_saving.jl b/test/callbacks/solution_saving.jl index 3b29d2df4a..b63cb6b201 100644 --- a/test/callbacks/solution_saving.jl +++ b/test/callbacks/solution_saving.jl @@ -69,17 +69,21 @@ @testset verbose=true "custom quantities" begin # Test that `custom_quantity` correctly chooses the correct method quantity1(system, data, t) = data - quantity2(system, v_ode, u_ode, semi, t) = 2 + quantity2(system, dv_ode, du_ode, v_ode, u_ode, semi, t) = 2 quantity3() = 3 system = Val(:mock_system) - TrixiParticles.system_data(::Val{:mock_system}, v_ode, u_ode, semi) = 1 + TrixiParticles.system_data(::Val{:mock_system}, dv_ode, du_ode, v_ode, u_ode, + semi) = 1 - data = v_ode = u_ode = semi = t = nothing + data = v_ode = u_ode = dv_ode = du_ode = semi = t = nothing - @test TrixiParticles.custom_quantity(quantity1, system, v_ode, u_ode, semi, t) == 1 - @test TrixiParticles.custom_quantity(quantity2, system, v_ode, u_ode, semi, t) == 2 - @test_throws MethodError TrixiParticles.custom_quantity(quantity3, system, v_ode, - u_ode, semi, t) + @test TrixiParticles.custom_quantity(quantity1, system, dv_ode, du_ode, v_ode, + u_ode, semi, t) == 1 + @test TrixiParticles.custom_quantity(quantity2, system, dv_ode, du_ode, v_ode, + u_ode, semi, t) == 2 + @test_throws MethodError TrixiParticles.custom_quantity(quantity3, system, dv_ode, + du_ode, v_ode, u_ode, + semi, t) end end diff --git a/test/examples/examples.jl b/test/examples/examples.jl index 5d73485c68..decb9ea711 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -3,19 +3,19 @@ @testset verbose=true "Examples" begin include("examples_fluid.jl") - @testset verbose=true "Solid" begin - @trixi_testset "solid/oscillating_beam_2d.jl" begin + @testset verbose=true "Structure" begin + @trixi_testset "structure/oscillating_beam_2d.jl" begin @trixi_test_nowarn trixi_include(@__MODULE__, - joinpath(examples_dir(), "solid", + joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), tspan=(0.0, 0.1)) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "solid/oscillating_beam_2d.jl with penalty force and viscosity" begin + @trixi_testset "structure/oscillating_beam_2d.jl with penalty force and viscosity" begin @trixi_test_nowarn trixi_include(@__MODULE__, - joinpath(examples_dir(), "solid", + joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), tspan=(0.0, 0.1), penalty_force=PenaltyForceGanzenmueller(alpha=0.1), diff --git a/test/examples/examples_fluid.jl b/test/examples/examples_fluid.jl index ede9e6bbf9..71084aa5e1 100644 --- a/test/examples/examples_fluid.jl +++ b/test/examples/examples_fluid.jl @@ -273,7 +273,19 @@ joinpath(examples_dir(), "fluid", "periodic_channel_2d.jl"), tspan=(0.0, 0.2), - extra_callback=ParticleShiftingCallback()) + shifting_technique=ParticleShiftingTechniqueSun2017(), + extra_callback=UpdateCallback()) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + + @trixi_testset "fluid/periodic_channel_2d.jl with TVF" begin + @trixi_test_nowarn trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", + "periodic_channel_2d.jl"), + tspan=(0.0, 0.2), + shifting_technique=TransportVelocityAdami(background_pressure=50_000.0), + extra_callback=UpdateCallback()) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end @@ -283,68 +295,71 @@ joinpath(examples_dir(), "fluid", "periodic_channel_2d.jl"), tspan=(0.0, 0.2), - extra_callback=ParticleShiftingCallback(), - pressure_acceleration=tensile_instability_control) + shifting_technique=ParticleShiftingTechniqueSun2017(), + pressure_acceleration=tensile_instability_control, + extra_callback=UpdateCallback()) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelLastiwka (WCSPH)" begin - @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), + @trixi_testset "fluid/periodic_channel_2d.jl with consistent PST and TIC" begin + @trixi_test_nowarn trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", - "pipe_flow_2d.jl"), - wcsph=true) + "periodic_channel_2d.jl"), + tspan=(0.0, 0.2), + shifting_technique=ConsistentShiftingSun2019(), + pressure_acceleration=tensile_instability_control, + extra_callback=UpdateCallback()) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelLastiwka (EDAC)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelCharacteristicsLastiwka (WCSPH)" begin @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), + open_boundary_model=BoundaryModelCharacteristicsLastiwka(), joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl")) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuni (EDAC)" begin - @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelCharacteristicsLastiwka (EDAC)" begin + @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), wcsph=false, + open_boundary_model=BoundaryModelCharacteristicsLastiwka(), + joinpath(examples_dir(), "fluid", + "pipe_flow_2d.jl")) + @test sol.retcode == ReturnCode.Success + @test count_rhs_allocations(sol, semi) == 0 + end + + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelMirroringTafuni (EDAC)" begin + @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), wcsph=false, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), - open_boundary_model=BoundaryModelTafuni(), boundary_type_in=BidirectionalFlow(), - boundary_type_out=BidirectionalFlow(), - reference_density_in=nothing, - reference_pressure_in=nothing, - reference_density_out=nothing, - reference_velocity_out=nothing) + boundary_type_out=BidirectionalFlow()) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuni (WCSPH)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelMirroringTafuni (WCSPH)" begin @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), - wcsph=true, sound_speed=20.0, pressure=0.0, - open_boundary_model=BoundaryModelTafuni(), boundary_type_in=BidirectionalFlow(), - boundary_type_out=BidirectionalFlow(), - reference_density_in=nothing, - reference_pressure_in=nothing, - reference_density_out=nothing, - reference_pressure_out=nothing, - reference_velocity_out=nothing) + boundary_type_out=BidirectionalFlow()) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end @trixi_testset "fluid/pipe_flow_2d.jl - steady state reached (`dt`)" begin - steady_state_reached = SteadyStateReachedCallback(; dt=0.002, interval_size=10, + steady_state_reached = SteadyStateReachedCallback(; dt=0.002, interval_size=5, reltol=1e-3) @trixi_test_nowarn trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), + open_boundary_model=BoundaryModelCharacteristicsLastiwka(), extra_callback=steady_state_reached, tspan=(0.0, 1.5), viscosity_boundary=nothing) @@ -354,11 +369,12 @@ end @trixi_testset "fluid/pipe_flow_2d.jl - steady state reached (`interval`)" begin - steady_state_reached = SteadyStateReachedCallback(; interval=1, interval_size=10, + steady_state_reached = SteadyStateReachedCallback(; interval=1, interval_size=5, reltol=1e-3) @trixi_test_nowarn trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), + open_boundary_model=BoundaryModelCharacteristicsLastiwka(), extra_callback=steady_state_reached, dtmax=2e-3, tspan=(0.0, 1.5), viscosity_boundary=nothing) @@ -368,7 +384,7 @@ end @trixi_testset "fluid/pipe_flow_3d.jl" begin - @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), + @trixi_test_nowarn trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "pipe_flow_3d.jl")) @test sol.retcode == ReturnCode.Success diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index 038e0a2b89..3acba3e36d 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -305,7 +305,7 @@ end end # Test open boundaries and steady-state callback - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelLastiwka (WCSPH)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelCharacteristicsLastiwka (WCSPH)" begin @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, tspan=(0.0f0, 0.5f0), joinpath(examples_dir(), @@ -318,7 +318,7 @@ end @test backend == Main.parallelization_backend end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelLastiwka (EDAC)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelCharacteristicsLastiwka (EDAC)" begin @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, tspan=(0.0f0, 0.5f0), joinpath(examples_dir(), @@ -330,13 +330,13 @@ end @test backend == Main.parallelization_backend end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuni (EDAC)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelMirroringTafuni (EDAC)" begin @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, tspan=(0.0f0, 0.5f0), joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), - open_boundary_model=BoundaryModelTafuni(), + open_boundary_model=BoundaryModelMirroringTafuni(), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, @@ -349,16 +349,15 @@ end @test backend == Main.parallelization_backend end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuni (WCSPH)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelMirroringTafuni (WCSPH)" begin @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, tspan=(0.0f0, 0.5f0), joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), wcsph=true, sound_speed=20.0f0, - pressure=0.0f0, - open_boundary_model=BoundaryModelTafuni(; - mirror_method=ZerothOrderMirroring()), + open_boundary_model=BoundaryModelMirroringTafuni(; + mirror_method=ZerothOrderMirroring()), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, @@ -373,14 +372,14 @@ end end @trixi_testset "fluid/pipe_flow_2d.jl - steady state reached (`dt`)" begin - steady_state_reached = SteadyStateReachedCallback(; dt=0.002f0, - interval_size=10, + steady_state_reached = SteadyStateReachedCallback(; dt=0.002f0, interval_size=5, reltol=1.0f-3) @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), + open_boundary_model=BoundaryModelCharacteristicsLastiwka(), extra_callback=steady_state_reached, tspan=(0.0f0, 1.5f0), parallelization_backend=Main.parallelization_backend, @@ -393,13 +392,14 @@ end @trixi_testset "fluid/pipe_flow_2d.jl - steady state reached (`interval`)" begin steady_state_reached = SteadyStateReachedCallback(; interval=1, - interval_size=10, + interval_size=5, reltol=1.0f-3) @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), extra_callback=steady_state_reached, + open_boundary_model=BoundaryModelCharacteristicsLastiwka(), dtmax=2.0f-3, tspan=(0.0f0, 1.5f0), parallelization_backend=Main.parallelization_backend, @@ -411,28 +411,28 @@ end end end - @testset verbose=true "Solid" begin + @testset verbose=true "Structure" begin # TODO after https://github.com/trixi-framework/PointNeighbors.jl/pull/10 # is merged, there should be no need to use the `FullGridCellList`. - @trixi_testset "solid/oscillating_beam_2d.jl" begin + @trixi_testset "structure/oscillating_beam_2d.jl" begin # Import variables into scope trixi_include_changeprecision(Float32, @__MODULE__, - joinpath(examples_dir(), "solid", + joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), sol=nothing, ode=nothing) # Neighborhood search with `FullGridCellList` for GPU compatibility - min_corner = minimum(solid.coordinates, dims=2) - max_corner = maximum(solid.coordinates, dims=2) + min_corner = minimum(structure.coordinates, dims=2) + max_corner = maximum(structure.coordinates, dims=2) cell_list = FullGridCellList(; min_corner, max_corner) - semi_fullgrid = Semidiscretization(solid_system, + semi_fullgrid = Semidiscretization(structure_system, neighborhood_search=GridNeighborhoodSearch{2}(; cell_list), parallelization_backend=Main.parallelization_backend) @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, joinpath(examples_dir(), - "solid", + "structure", "oscillating_beam_2d.jl"), tspan=(0.0f0, 0.1f0), semi=semi_fullgrid) @@ -453,12 +453,12 @@ end # Neighborhood search with `FullGridCellList` for GPU compatibility min_corner = minimum(tank.boundary.coordinates, dims=2) max_corner = maximum(tank.boundary.coordinates, dims=2) - max_corner[2] = gate_height + movement_function(0.1)[2] + max_corner[2] = gate_height + movement_function([0, 0], 0.1f0)[2] # We need a very high `max_points_per_cell` because the plate resolution # is much finer than the fluid resolution. cell_list = FullGridCellList(; min_corner, max_corner) semi_fullgrid = Semidiscretization(fluid_system, boundary_system_tank, - boundary_system_gate, solid_system, + boundary_system_gate, structure_system, neighborhood_search=GridNeighborhoodSearch{2}(; cell_list), parallelization_backend=Main.parallelization_backend) diff --git a/test/general/buffer.jl b/test/general/buffer.jl index 18758590ea..28fa1748e0 100644 --- a/test/general/buffer.jl +++ b/test/general/buffer.jl @@ -1,28 +1,26 @@ @testset verbose=true "`SystemBuffer`" begin # Mock fluid system - struct FluidSystemMock3 <: TrixiParticles.FluidSystem{2} end + struct FluidSystemMock3 <: TrixiParticles.AbstractFluidSystem{2} end TrixiParticles.initial_smoothing_length(system::FluidSystemMock3) = 1.0 TrixiParticles.nparticles(system::FluidSystemMock3) = 1 - zone = BoundaryZone(; plane=([0.0, 0.0], [0.0, 1.0]), particle_spacing=0.2, - open_boundary_layers=2, density=1.0, plane_normal=[1.0, 0.0], - boundary_type=InFlow()) - system = OpenBoundarySPHSystem(zone; fluid_system=FluidSystemMock3(), - reference_density=0.0, reference_pressure=0.0, - reference_velocity=[0, 0], - boundary_model=BoundaryModelLastiwka(), buffer_size=0) - system_buffer = OpenBoundarySPHSystem(zone; buffer_size=5, - reference_density=0.0, reference_pressure=0.0, - reference_velocity=[0, 0], - boundary_model=BoundaryModelLastiwka(), - fluid_system=FluidSystemMock3()) + zone = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), particle_spacing=0.2, + open_boundary_layers=2, density=1.0, face_normal=[1.0, 0.0], + reference_density=1.0, reference_pressure=0.0, + reference_velocity=[0, 0], boundary_type=InFlow()) + system = OpenBoundarySystem(zone; fluid_system=FluidSystemMock3(), + boundary_model=BoundaryModelCharacteristicsLastiwka(), + buffer_size=0) + system_buffer = OpenBoundarySystem(zone; buffer_size=5, + boundary_model=BoundaryModelCharacteristicsLastiwka(), + fluid_system=FluidSystemMock3()) n_particles = nparticles(system) @testset "Iterators" begin - @test TrixiParticles.each_moving_particle(system) == 1:n_particles + @test TrixiParticles.each_integrated_particle(system) == 1:n_particles - @test TrixiParticles.each_moving_particle(system_buffer) == 1:n_particles + @test TrixiParticles.each_integrated_particle(system_buffer) == 1:n_particles # Activate a particle particle_id = findfirst(==(false), system_buffer.buffer.active_particle) @@ -31,7 +29,7 @@ TrixiParticles.update_system_buffer!(system_buffer.buffer, DummySemidiscretization()) - @test TrixiParticles.each_moving_particle(system_buffer) == 1:(n_particles + 1) + @test TrixiParticles.each_integrated_particle(system_buffer) == 1:(n_particles + 1) TrixiParticles.deactivate_particle!(system_buffer, particle_id, ones(2, particle_id)) @@ -39,7 +37,7 @@ TrixiParticles.update_system_buffer!(system_buffer.buffer, DummySemidiscretization()) - @test TrixiParticles.each_moving_particle(system_buffer) == 1:n_particles + @test TrixiParticles.each_integrated_particle(system_buffer) == 1:n_particles particle_id = 5 TrixiParticles.deactivate_particle!(system_buffer, particle_id, @@ -48,7 +46,7 @@ TrixiParticles.update_system_buffer!(system_buffer.buffer, DummySemidiscretization()) - @test TrixiParticles.each_moving_particle(system_buffer) == + @test TrixiParticles.each_integrated_particle(system_buffer) == setdiff(1:n_particles, particle_id) end diff --git a/test/general/custom_quantities.jl b/test/general/custom_quantities.jl index 37cb59ea92..54e976b395 100644 --- a/test/general/custom_quantities.jl +++ b/test/general/custom_quantities.jl @@ -21,16 +21,17 @@ AdamiPressureExtrapolation(), smoothing_kernel, smoothing_length) - boundary_system = BoundarySPHSystem(initial_condition, boundary_model) + boundary_system = WallBoundarySystem(initial_condition, boundary_model) semi = Semidiscretization(fluid_system, boundary_system) v_ode, u_ode = semidiscretize(semi, (0, 1)).u0.x + dv_ode, du_ode = similar(v_ode) t = 0.0 @testset "Kinetic Energy" begin @testset "Fluid System" begin - ekin = kinetic_energy(fluid_system, v_ode, u_ode, semi, t) + ekin = kinetic_energy(fluid_system, dv_ode, du_ode, v_ode, u_ode, semi, t) expected_ekin = sum(velocities) do velocity return 0.5 * first(fluid_system.mass) * dot(velocity, velocity) @@ -40,21 +41,21 @@ end @testset "Boundary System" begin - ekin = kinetic_energy(boundary_system, v_ode, u_ode, semi, t) + ekin = kinetic_energy(boundary_system, dv_ode, du_ode, v_ode, u_ode, semi, t) @test ekin == 0 end end @testset "Total Mass" begin @testset "Fluid System" begin - mass = total_mass(fluid_system, v_ode, u_ode, semi, t) + mass = total_mass(fluid_system, dv_ode, du_ode, v_ode, u_ode, semi, t) expected_mass = first(fluid_system.mass) * nparticles(fluid_system) @test isapprox(mass, expected_mass) end @testset "Boundary System" begin - mass = total_mass(boundary_system, v_ode, u_ode, semi, t) + mass = total_mass(boundary_system, dv_ode, du_ode, v_ode, u_ode, semi, t) @test isnan(mass) end @@ -62,54 +63,57 @@ @testset "Pressure Quantities" begin @testset "Max Pressure" begin - max_p = max_pressure(fluid_system, v_ode, u_ode, semi, t) + max_p = max_pressure(fluid_system, dv_ode, du_ode, v_ode, u_ode, semi, t) @test isapprox(max_p, 101330.0) # Boundary system should return NaN - @test isnan(max_pressure(boundary_system, v_ode, u_ode, semi, t)) + @test isnan(max_pressure(boundary_system, dv_ode, du_ode, v_ode, u_ode, + semi, t)) end @testset "Min Pressure" begin - min_p = min_pressure(fluid_system, v_ode, u_ode, semi, t) + min_p = min_pressure(fluid_system, dv_ode, du_ode, v_ode, u_ode, semi, t) @test min_p ≈ 101320.0 # Boundary system should return NaN - @test isnan(min_pressure(boundary_system, v_ode, u_ode, semi, t)) + @test isnan(min_pressure(boundary_system, dv_ode, du_ode, v_ode, u_ode, + semi, t)) end @testset "Average Pressure" begin - avg_p = avg_pressure(fluid_system, v_ode, u_ode, semi, t) + avg_p = avg_pressure(fluid_system, dv_ode, du_ode, v_ode, u_ode, semi, t) expected_avg = (101325.0 + 101330.0 + 101320.0) / 3 @test isapprox(avg_p, expected_avg) # Boundary system should return NaN - @test isnan(avg_pressure(boundary_system, v_ode, u_ode, semi, t)) + @test isnan(avg_pressure(boundary_system, dv_ode, du_ode, v_ode, u_ode, + semi, t)) end end @testset "Density Quantities" begin @testset "max_density" begin - max_d = max_density(fluid_system, v_ode, u_ode, semi, t) + max_d = max_density(fluid_system, dv_ode, du_ode, v_ode, u_ode, semi, t) @test isapprox(max_d, 1000.0) # All particles have same density # Boundary system should return NaN - @test isnan(max_density(boundary_system, v_ode, u_ode, semi, t)) + @test isnan(max_density(boundary_system, dv_ode, du_ode, v_ode, u_ode, semi, t)) end @testset "min_density" begin - min_d = min_density(fluid_system, v_ode, u_ode, semi, t) + min_d = min_density(fluid_system, dv_ode, du_ode, v_ode, u_ode, semi, t) @test isapprox(min_d, 1000.0) # Boundary system should return NaN - @test isnan(min_density(boundary_system, v_ode, u_ode, semi, t)) + @test isnan(min_density(boundary_system, dv_ode, du_ode, v_ode, u_ode, semi, t)) end @testset "avg_density" begin - avg_d = avg_density(fluid_system, v_ode, u_ode, semi, t) + avg_d = avg_density(fluid_system, dv_ode, du_ode, v_ode, u_ode, semi, t) @test isapprox(avg_d, 1000.0) # Boundary system should return NaN - @test isnan(avg_density(boundary_system, v_ode, u_ode, semi, t)) + @test isnan(avg_density(boundary_system, dv_ode, du_ode, v_ode, u_ode, semi, t)) end end end diff --git a/test/general/interpolation.jl b/test/general/interpolation.jl index b7ee61a66b..60d668aef8 100644 --- a/test/general/interpolation.jl +++ b/test/general/interpolation.jl @@ -110,7 +110,7 @@ AdamiPressureExtrapolation(), smoothing_kernel, smoothing_length) - boundary_system = BoundarySPHSystem(bnd, boundary_model) + boundary_system = WallBoundarySystem(bnd, boundary_model) # Overwrite `system.pressure` because we skip the update step fluid_system.pressure .= fluid.pressure @@ -624,7 +624,7 @@ AdamiPressureExtrapolation(), smoothing_kernel, smoothing_length) - boundary_system = BoundarySPHSystem(bnd, boundary_model) + boundary_system = WallBoundarySystem(bnd, boundary_model) # Overwrite `system.pressure` because we skip the update step fluid_system.pressure .= fluid.pressure diff --git a/test/general/semidiscretization.jl b/test/general/semidiscretization.jl index dea8c356ab..bb2aa75c5f 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.AbstractSystem{3} end + struct System2 <: TrixiParticles.AbstractSystem{3} end system1 = System1() system2 = System2() @@ -13,8 +13,8 @@ TrixiParticles.v_nvariables(::System2) = 2 TrixiParticles.nparticles(::System1) = 2 TrixiParticles.nparticles(::System2) = 3 - TrixiParticles.n_moving_particles(::System1) = 2 - TrixiParticles.n_moving_particles(::System2) = 3 + TrixiParticles.n_integrated_particles(::System1) = 2 + TrixiParticles.n_integrated_particles(::System2) = 3 TrixiParticles.compact_support(::System1, neighbor) = 0.2 TrixiParticles.compact_support(::System2, neighbor) = 0.2 @@ -38,12 +38,12 @@ end @testset verbose=true "Check Configuration" begin - @testset verbose=true "Solid-Fluid Interaction" begin + @testset verbose=true "Structure-Fluid Interaction" begin # Mock boundary model struct BoundaryModelMock end # Mock fluid system - struct FluidSystemMock <: TrixiParticles.FluidSystem{2} + struct FluidSystemMock <: TrixiParticles.AbstractFluidSystem{2} surface_tension::Nothing FluidSystemMock() = new(nothing) end @@ -60,28 +60,29 @@ 1.0) # FSI without boundary model. - solid_system1 = TotalLagrangianSPHSystem(ic, kernel, 1.0, 1.0, 1.0) + structure_system1 = TotalLagrangianSPHSystem(ic, kernel, 1.0, 1.0, 1.0) error_str = "a boundary model for `TotalLagrangianSPHSystem` must be " * "specified when simulating a fluid-structure interaction." @test_throws ArgumentError(error_str) Semidiscretization(fluid_system, - solid_system1, + structure_system1, neighborhood_search=nothing) # FSI with boundary model - solid_system2 = TotalLagrangianSPHSystem(ic, kernel, 1.0, 1.0, 1.0, - boundary_model=model_a) + structure_system2 = TotalLagrangianSPHSystem(ic, kernel, 1.0, 1.0, 1.0, + boundary_model=model_a) - @test_nowarn TrixiParticles.check_configuration((solid_system2, fluid_system), + @test_nowarn TrixiParticles.check_configuration((structure_system2, + fluid_system), nothing) # FSI with wrong boundary model - solid_system3 = TotalLagrangianSPHSystem(ic, kernel, 1.0, 1.0, 1.0, - boundary_model=model_b) + structure_system3 = TotalLagrangianSPHSystem(ic, kernel, 1.0, 1.0, 1.0, + boundary_model=model_b) error_str = "`BoundaryModelDummyParticles` with density calculator " * "`ContinuityDensity` is not yet supported for a `TotalLagrangianSPHSystem`" - @test_throws ArgumentError(error_str) Semidiscretization(solid_system3, + @test_throws ArgumentError(error_str) Semidiscretization(structure_system3, fluid_system, neighborhood_search=nothing) end @@ -93,7 +94,7 @@ boundary_model = BoundaryModelDummyParticles(ic.density, ic.mass, SummationDensity(), kernel, 1.0) - boundary_system = BoundarySPHSystem(ic, boundary_model) + boundary_system = WallBoundarySystem(ic, boundary_model) fluid_system = WeaklyCompressibleSPHSystem(ic, SummationDensity(), nothing, kernel, 1.0) @@ -139,9 +140,6 @@ 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, 0.0) dv1 = TrixiParticles.wrap_v(dv_ode, system1, semi) diff --git a/test/io/read_vtk.jl b/test/io/read_vtk.jl index 5db6406c7e..d82e8c50c3 100644 --- a/test/io/read_vtk.jl +++ b/test/io/read_vtk.jl @@ -1,7 +1,7 @@ @testset verbose=true "`vtk2trixi`" begin mktempdir() do tmp_dir - coordinates=fill(1.0, 2, 12) - velocity=fill(2.0, 2, 12) + coordinates = fill(1.0, 2, 12) + velocity = fill(2.0, 2, 12) expected_ic = InitialCondition(; coordinates=coordinates, velocity=velocity, density=1000.0, pressure=900.0, mass=50.0) @@ -18,7 +18,7 @@ @test isapprox(expected_ic.pressure, test_ic.pressure, rtol=1e-5) end - @testset verbose=true "`FluidSystem`" begin + @testset verbose=true "`AbstractFluidSystem`" begin fluid_system = EntropicallyDampedSPHSystem(expected_ic, SchoenbergCubicSplineKernel{2}(), 1.5, 1.5) @@ -29,19 +29,21 @@ semi = Semidiscretization(fluid_system) # Create random ODE solutions + dvdu_ode = nothing v = fill(2.0, ndims(fluid_system), nparticles(fluid_system)) pressure = fill(3.0, nparticles(fluid_system)) v_ode = vec([v; pressure']) - u = fill(1.0, ndims(fluid_system), nparticles(fluid_system)) u_ode = vec(u) + x = (; v_ode, u_ode) + vu_ode = (; x) - # Write out `FluidSystem` Simulation-File - trixi2vtk(fluid_system, v_ode, u_ode, semi, 0.0, + # Write out `AbstractFluidSystem` Simulation-File + trixi2vtk(fluid_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_fluid", output_directory=tmp_dir, iter=1) - # Load `FluidSystem` Simulation-File + # Load `AbstractFluidSystem` Simulation-File test = vtk2trixi(joinpath(tmp_dir, "tmp_file_fluid_1.vtu")) @test isapprox(u, test.coordinates, rtol=1e-5) @@ -50,7 +52,7 @@ @test isapprox(fluid_system.cache.density, test.density, rtol=1e-5) end - @testset verbose=true "`BoundarySystem`" begin + @testset verbose=true "`WallBoundarySystem`" begin boundary_model = BoundaryModelDummyParticles(expected_ic.density, expected_ic.mass, SummationDensity(), @@ -61,23 +63,26 @@ boundary_model.pressure .= expected_ic.pressure boundary_model.cache.density .= expected_ic.density - boundary_system = BoundarySPHSystem(expected_ic, boundary_model) + boundary_system = WallBoundarySystem(expected_ic, boundary_model) semi = Semidiscretization(boundary_system) # Create dummy ODE solutions + dvdu_ode = nothing v_ode = zeros(ndims(boundary_system) * nparticles(boundary_system)) u_ode = zeros(ndims(boundary_system) * nparticles(boundary_system)) + x = (; v_ode, u_ode) + vu_ode = (; x) - # Write out `BoundarySystem` Simulation-File - trixi2vtk(boundary_system, v_ode, u_ode, semi, 0.0, + # Write out `WallBoundarySystem` Simulation-File + trixi2vtk(boundary_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_boundary", output_directory=tmp_dir, iter=1) - # Load `BoundarySystem` Simulation-File + # Load `WallBoundarySystem` Simulation-File test = vtk2trixi(joinpath(tmp_dir, "tmp_file_boundary_1.vtu")) @test isapprox(boundary_system.coordinates, test.coordinates, rtol=1e-5) - # The velocity is always zero for `BoundarySystem` + # The velocity is always zero for `WallBoundarySystem` @test isapprox(zeros(size(test.velocity)), test.velocity, rtol=1e-5) @test isapprox(boundary_model.pressure, test.pressure, rtol=1e-5) @test isapprox(boundary_model.cache.density, test.density, rtol=1e-5) diff --git a/test/preprocessing/data/inflow.stl b/test/preprocessing/data/inflow.stl new file mode 100644 index 0000000000..028f34b16e Binary files /dev/null and b/test/preprocessing/data/inflow.stl differ diff --git a/test/preprocessing/data/inflow_geometry.stl b/test/preprocessing/data/inflow_geometry.stl new file mode 100644 index 0000000000..b435306f11 Binary files /dev/null and b/test/preprocessing/data/inflow_geometry.stl differ diff --git a/test/preprocessing/geometries/geometries.jl b/test/preprocessing/geometries/geometries.jl index a165fbd0b9..d39f3269fa 100644 --- a/test/preprocessing/geometries/geometries.jl +++ b/test/preprocessing/geometries/geometries.jl @@ -129,6 +129,21 @@ end end + @testset verbose=true "Boundary Face" begin + file = pkgdir(TrixiParticles, "test", "preprocessing", "data") + planar_geometry = load_geometry(joinpath(file, "inflow_geometry.stl")) + + face, face_normal = planar_geometry_to_face(planar_geometry) + + expected_face = ([-0.10239515072676975, 0.2644994251485518, -0.36036119092034713], + [0.3064669575380171, 0.2392044626289733, -0.10866880239395837], + [-0.02275190052262935, 0.299506937268509, -0.034649329562556]) + ecpected_normal = [0.14372397390844055, 0.979596249614303, -0.14047991694743392] + + @test any(isapprox.(face, expected_face)) + @test isapprox(face_normal, ecpected_normal) + end + @testset verbose=true "Show" begin data_dir = pkgdir(TrixiParticles, "examples", "preprocessing", "data") geometry = load_geometry(joinpath(data_dir, "circle.asc")) diff --git a/test/schemes/boundary/dummy_particles/dummy_particles.jl b/test/schemes/boundary/dummy_particles/dummy_particles.jl index e0b433b856..4f46abe701 100644 --- a/test/schemes/boundary/dummy_particles/dummy_particles.jl +++ b/test/schemes/boundary/dummy_particles/dummy_particles.jl @@ -66,8 +66,8 @@ viscosity=viscosity) boundary_systems = [ - BoundarySPHSystem(boundary, boundary_model_adami), - BoundarySPHSystem(boundary, boundary_model_bernoulli), + WallBoundarySystem(boundary, boundary_model_adami), + WallBoundarySystem(boundary, boundary_model_bernoulli), TotalLagrangianSPHSystem(boundary, smoothing_kernel, smoothing_length, 1e6, 0.3; boundary_model=boundary_model_adami), @@ -234,7 +234,7 @@ AdamiPressureExtrapolation(), smoothing_kernel, smoothing_length) - boundary_system = BoundarySPHSystem(tank1.boundary, boundary_model) + boundary_system = WallBoundarySystem(tank1.boundary, boundary_model) viscosity = boundary_system.boundary_model.viscosity semi = DummySemidiscretization() diff --git a/test/schemes/boundary/dummy_particles/rhs.jl b/test/schemes/boundary/dummy_particles/rhs.jl index ebc8216031..4d64fc61a5 100644 --- a/test/schemes/boundary/dummy_particles/rhs.jl +++ b/test/schemes/boundary/dummy_particles/rhs.jl @@ -47,8 +47,8 @@ PressureZeroing(), smoothing_kernel, smoothing_length) - boundary_system_zeroing = BoundarySPHSystem(initial_condition, - boundary_model_zeroing) + boundary_system_zeroing = WallBoundarySystem(initial_condition, + boundary_model_zeroing) boundary_model_continuity = BoundaryModelDummyParticles(initial_condition.density, initial_condition.mass, ContinuityDensity(), @@ -56,8 +56,8 @@ smoothing_length) # Overwrite `boundary_model_continuity.pressure` because we skip the update step boundary_model_continuity.pressure .= initial_condition.pressure - boundary_system_continuity = BoundarySPHSystem(initial_condition, - boundary_model_continuity) + boundary_system_continuity = WallBoundarySystem(initial_condition, + boundary_model_continuity) boundary_model_summation = BoundaryModelDummyParticles(initial_condition.density, initial_condition.mass, @@ -68,8 +68,8 @@ boundary_model_summation.pressure .= initial_condition.pressure # Density is stored in the cache boundary_model_summation.cache.density .= initial_condition.density - boundary_system_summation = BoundarySPHSystem(initial_condition, - boundary_model_summation) + boundary_system_summation = WallBoundarySystem(initial_condition, + boundary_model_summation) u_boundary = zeros(0, TrixiParticles.nparticles(initial_condition)) v_boundary = zeros(0, TrixiParticles.nparticles(initial_condition)) @@ -77,20 +77,20 @@ v_boundary_continuity = copy(initial_condition.density') # TLSPH system - solid_system = TotalLagrangianSPHSystem(initial_condition, smoothing_kernel, - smoothing_length, 0.0, 0.0, - boundary_model=boundary_model_continuity) + structure_system = TotalLagrangianSPHSystem(initial_condition, smoothing_kernel, + smoothing_length, 0.0, 0.0, + boundary_model=boundary_model_continuity) - # Positions of the solid particles are not used here - u_solid = zeros(0, TrixiParticles.nparticles(solid_system)) - v_solid = vcat(initial_condition.velocity, - initial_condition.density') + # Positions of the structure particles are not used here + u_structure = zeros(0, TrixiParticles.nparticles(structure_system)) + v_structure = vcat(initial_condition.velocity, + initial_condition.density') systems = Dict( "Fluid-Fluid" => second_fluid_system, "Fluid-BoundaryDummyPressureZeroing" => boundary_system_zeroing, "Fluid-BoundaryDummyContinuityDensity" => boundary_system_continuity, - "Fluid-TLSPH" => solid_system + "Fluid-TLSPH" => structure_system ) if density_calculator isa SummationDensity @@ -105,7 +105,7 @@ "Fluid-BoundaryDummyContinuityDensity" => (v_boundary_continuity, u_boundary), "Fluid-BoundaryDummySummationDensity" => (v_boundary, u_boundary), - "Fluid-TLSPH" => (v_solid, u_solid) + "Fluid-TLSPH" => (v_structure, u_structure) ) return systems, vu diff --git a/test/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl b/test/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl index 50face550b..d5c0042c28 100644 --- a/test/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl +++ b/test/schemes/boundary/monaghan_kajtar/monaghan_kajtar.jl @@ -35,7 +35,7 @@ spacing_ratio = 0.5 boundary_model = BoundaryModelMonaghanKajtar(K, spacing_ratio, 2particle_spacing, boundary.mass) - boundary_system = BoundarySPHSystem(boundary, boundary_model) + boundary_system = WallBoundarySystem(boundary, boundary_model) # Density is integrated with `ContinuityDensity` v = vcat(fluid.velocity, fluid.density') diff --git a/test/schemes/boundary/open_boundary/boundary_zone.jl b/test/schemes/boundary/open_boundary/boundary_zone.jl index b14906b630..24fc1fba74 100644 --- a/test/schemes/boundary/open_boundary/boundary_zone.jl +++ b/test/schemes/boundary/open_boundary/boundary_zone.jl @@ -1,28 +1,125 @@ @testset verbose=true "Boundary Zone" begin + @testset "`show`" begin + inflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), + particle_spacing=0.05, + face_normal=(1.0, 0.0), density=1.0, + reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + open_boundary_layers=4, boundary_type=InFlow()) + + show_compact = "BoundaryZone() with 80 particles" + @test repr(inflow) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… inflow │ + │ #particles: ………………………………………………… 80 │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", inflow) == show_box + + outflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), + particle_spacing=0.05, + reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + face_normal=(1.0, 0.0), density=1.0, open_boundary_layers=4, + boundary_type=OutFlow()) + + show_compact = "BoundaryZone() with 80 particles" + @test repr(outflow) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… outflow │ + │ #particles: ………………………………………………… 80 │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", outflow) == show_box + end + + @testset verbose=true "Illegal Inputs" begin + boundary_face = ([0.0, 0.0], [0.0, 1.0]) + flow_direction = (1.0, 0.0) + + error_str = "`reference_velocity` must be either a function mapping " * + "each particle's coordinates and time to its velocity, " * + "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) BoundaryZone(; boundary_face, + particle_spacing=0.1, + face_normal=flow_direction, + density=1.0, + reference_density=0, + reference_pressure=0, + reference_velocity, + open_boundary_layers=2, + boundary_type=InFlow()) + + error_str = "`reference_pressure` must be either a function mapping " * + "each particle's coordinates and time to its pressure, " * + "or a scalar" + + reference_pressure = [1.0, 1.0] + + @test_throws ArgumentError(error_str) BoundaryZone(; boundary_face, + particle_spacing=0.1, + face_normal=flow_direction, + density=1.0, + reference_density=0, + reference_velocity=[1.0, + 1.0], reference_pressure, + open_boundary_layers=2, + boundary_type=InFlow()) + + error_str = "`reference_density` must be either a function mapping " * + "each particle's coordinates and time to its density, " * + "or a scalar" + + reference_density = [1.0, 1.0] + @test_throws ArgumentError(error_str) BoundaryZone(; boundary_face, + particle_spacing=0.1, + face_normal=flow_direction, + density=1.0, + reference_density, + reference_velocity=[1.0, + 1.0], + reference_pressure=0, + open_boundary_layers=2, + boundary_type=InFlow()) + end @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]] + face_vertices_1 = [[0.0, 0.0], [0.5, -0.5], [1.0, 0.5]] + face_vertices_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] + @testset verbose=true "Points $i" for i in eachindex(face_vertices_1) + vertex_1 = face_vertices_1[i] + vertex_2 = face_vertices_2[i] - plane_size = point_2 - point_1 + face_size = vertex_2 - vertex_1 flow_directions = [ - normalize([-plane_size[2], plane_size[1]]), - -normalize([-plane_size[2], plane_size[1]]) + normalize([-face_size[2], face_size[1]]), + -normalize([-face_size[2], face_size[1]]) ] @testset verbose=true "Flow Direction $j" for j in eachindex(flow_directions) - inflow = BoundaryZone(; plane=(point_1, point_2), particle_spacing, - plane_normal=flow_directions[j], density=1.0, + inflow = BoundaryZone(; boundary_face=(vertex_1, vertex_2), + particle_spacing, + face_normal=flow_directions[j], density=1.0, open_boundary_layers, boundary_type=InFlow()) - outflow = BoundaryZone(; plane=(point_1, point_2), particle_spacing, - plane_normal=(-flow_directions[j]), density=1.0, + outflow = BoundaryZone(; boundary_face=(vertex_1, vertex_2), + particle_spacing, + face_normal=(-flow_directions[j]), density=1.0, open_boundary_layers, boundary_type=OutFlow()) boundary_zones = [ @@ -35,11 +132,11 @@ zone_width = open_boundary_layers * boundary_zone.initial_condition.particle_spacing - sign_ = (first(typeof(boundary_zone).parameters) === - TrixiParticles.InFlow) ? -1 : 1 + sign_ = (TrixiParticles.boundary_type_name(boundary_zone) == "inflow") ? + -1 : 1 - @test plane_points_1[i] == boundary_zone.zone_origin - @test plane_points_2[i] - boundary_zone.zone_origin == + @test face_vertices_1[i] == boundary_zone.zone_origin + @test face_vertices_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) @@ -54,26 +151,26 @@ particle_spacing = 0.05 open_boundary_layers = 4 - plane_points_1 = [ + face_vertices_1 = [ [0.0, 0.0, 0.0], [0.3113730847835541, 0.19079485535621643, -0.440864622592926] ] - plane_points_2 = [ + face_vertices_2 = [ [1.0, 0.0, 0.0], [-0.10468611121177673, 0.252103328704834, -0.44965094327926636] ] - plane_points_3 = [ + face_vertices_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] + @testset verbose=true "Points $i" for i in eachindex(face_vertices_1) + vertex_1 = face_vertices_1[i] + vertex_2 = face_vertices_2[i] + vertex_3 = face_vertices_3[i] - edge1 = point_2 - point_1 - edge2 = point_3 - point_1 + edge1 = vertex_2 - vertex_1 + edge2 = vertex_3 - vertex_1 flow_directions = [ normalize(cross(edge1, edge2)), @@ -81,12 +178,13 @@ ] @testset verbose=true "Flow Direction $j" for j in eachindex(flow_directions) - inflow = BoundaryZone(; plane=(point_1, point_2, point_3), particle_spacing, - plane_normal=flow_directions[j], density=1.0, + inflow = BoundaryZone(; boundary_face=(vertex_1, vertex_2, vertex_3), + particle_spacing, + face_normal=flow_directions[j], density=1.0, open_boundary_layers, boundary_type=InFlow()) - outflow = BoundaryZone(; plane=(point_1, point_2, point_3), + outflow = BoundaryZone(; boundary_face=(vertex_1, vertex_2, vertex_3), particle_spacing, - plane_normal=(-flow_directions[j]), density=1.0, + face_normal=(-flow_directions[j]), density=1.0, open_boundary_layers, boundary_type=OutFlow()) boundary_zones = [ @@ -99,13 +197,13 @@ zone_width = open_boundary_layers * boundary_zone.initial_condition.particle_spacing - sign_ = (first(typeof(boundary_zone).parameters) === - TrixiParticles.InFlow) ? -1 : 1 + sign_ = (TrixiParticles.boundary_type_name(boundary_zone) == "inflow") ? + -1 : 1 - @test plane_points_1[i] == boundary_zone.zone_origin - @test plane_points_2[i] - boundary_zone.zone_origin == + @test face_vertices_1[i] == boundary_zone.zone_origin + @test face_vertices_2[i] - boundary_zone.zone_origin == boundary_zone.spanning_set[2] - @test plane_points_3[i] - boundary_zone.zone_origin == + @test face_vertices_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) @@ -117,16 +215,16 @@ 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] + face_vertices = [[-0.2, -0.5], [0.3, 0.6]] + face_size = face_vertices[2] - face_vertices[1] - flow_direction = normalize([-plane_size[2], plane_size[1]]) + flow_direction = normalize([-face_size[2], face_size[1]]) - inflow = BoundaryZone(; plane=plane_points, particle_spacing=0.1, - plane_normal=flow_direction, density=1.0, + inflow = BoundaryZone(; boundary_face=face_vertices, particle_spacing=0.1, + face_normal=flow_direction, density=1.0, open_boundary_layers=4, boundary_type=InFlow()) - outflow = BoundaryZone(; plane=plane_points, particle_spacing=0.1, - plane_normal=(-flow_direction), density=1.0, + outflow = BoundaryZone(; boundary_face=face_vertices, particle_spacing=0.1, + face_normal=(-flow_direction), density=1.0, open_boundary_layers=4, boundary_type=OutFlow()) boundary_zones = [ @@ -137,12 +235,12 @@ @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones - perturb_ = first(typeof(boundary_zone).parameters) === TrixiParticles.InFlow ? + perturb_ = TrixiParticles.boundary_type_name(boundary_zone) == "inflow" ? sqrt(eps()) : -sqrt(eps()) - point1 = plane_points[1] - point2 = plane_points[2] + point1 = face_vertices[1] + point2 = face_vertices[2] point3 = boundary_zone.spanning_set[1] + boundary_zone.zone_origin query_points = Dict( @@ -168,11 +266,13 @@ flow_direction = normalize(cross(point2 - point1, point3 - point1)) - inflow = BoundaryZone(; plane=[point1, point2, point3], particle_spacing=0.1, - plane_normal=flow_direction, density=1.0, + inflow = BoundaryZone(; boundary_face=[point1, point2, point3], + particle_spacing=0.1, + face_normal=flow_direction, density=1.0, open_boundary_layers=4, boundary_type=InFlow()) - outflow = BoundaryZone(; plane=[point1, point2, point3], particle_spacing=0.1, - plane_normal=(-flow_direction), density=1.0, + outflow = BoundaryZone(; boundary_face=[point1, point2, point3], + particle_spacing=0.1, + face_normal=(-flow_direction), density=1.0, open_boundary_layers=4, boundary_type=OutFlow()) boundary_zones = [ @@ -183,7 +283,7 @@ @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones - perturb_ = first(typeof(boundary_zone).parameters) === TrixiParticles.InFlow ? + perturb_ = TrixiParticles.boundary_type_name(boundary_zone) == "inflow" ? eps() : -eps() point4 = boundary_zone.spanning_set[1] + boundary_zone.zone_origin @@ -205,41 +305,45 @@ end @testset verbose=true "Illegal Inputs" begin - no_rectangular_plane = [[0.2, 0.3, -0.5], [-1.0, 1.5, 0.2], [-0.4, 0.9, -0.15]] + no_rectangular_face = [[0.2, 0.3, -0.5], [-1.0, 1.5, 0.2], [-0.4, 0.9, -0.15]] flow_direction = [0.0, 0.0, 1.0] error_str = "the vectors `AB` and `AC` must not be collinear" - @test_throws ArgumentError(error_str) BoundaryZone(; plane=no_rectangular_plane, + @test_throws ArgumentError(error_str) BoundaryZone(; + boundary_face=no_rectangular_face, particle_spacing=0.1, - plane_normal=flow_direction, + face_normal=flow_direction, density=1.0, open_boundary_layers=2, boundary_type=InFlow()) - @test_throws ArgumentError(error_str) BoundaryZone(; plane=no_rectangular_plane, + @test_throws ArgumentError(error_str) BoundaryZone(; + boundary_face=no_rectangular_face, particle_spacing=0.1, - plane_normal=(-flow_direction), + face_normal=(-flow_direction), density=1.0, open_boundary_layers=2, boundary_type=OutFlow()) - rectangular_plane = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] + rectangular_face = [[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 = "`plane_normal` is not normal to the boundary plane" + error_str = "`face_normal` is not normal to the boundary face" - @test_throws ArgumentError(error_str) BoundaryZone(; plane=rectangular_plane, + @test_throws ArgumentError(error_str) BoundaryZone(; + boundary_face=rectangular_face, particle_spacing=0.1, - plane_normal=flow_direction, + face_normal=flow_direction, density=1.0, open_boundary_layers=2, boundary_type=InFlow()) - error_str = "`plane_normal` is not normal to the boundary plane" + error_str = "`face_normal` is not normal to the boundary face" - @test_throws ArgumentError(error_str) BoundaryZone(; plane=rectangular_plane, + @test_throws ArgumentError(error_str) BoundaryZone(; + boundary_face=rectangular_face, particle_spacing=0.1, - plane_normal=(-flow_direction), + face_normal=(-flow_direction), density=1.0, open_boundary_layers=2, boundary_type=OutFlow()) diff --git a/test/schemes/boundary/open_boundary/characteristic_variables.jl b/test/schemes/boundary/open_boundary/characteristic_variables.jl index 570818f8f9..8ca3e41c05 100644 --- a/test/schemes/boundary/open_boundary/characteristic_variables.jl +++ b/test/schemes/boundary/open_boundary/characteristic_variables.jl @@ -15,31 +15,34 @@ # 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 + # Add small offset to avoid "ArgumentError: density must be positive and larger than `eps()`" + reference_density = (pos, t) -> 1000.0 * (t + sqrt(eps())) - # 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]] + # Face vertices of open boundary + face_vertices_1 = [[0.0, 0.0], [0.5, -0.5], [1.0, 0.5]] + face_vertices_2 = [[0.0, 1.0], [0.2, 2.0], [2.3, 0.5]] - @testset "Points $i" for i in eachindex(plane_points_1) + @testset "Points $i" for i in eachindex(face_vertices_1) n_influenced = influenced_particles[i] - plane_points = [plane_points_1[i], plane_points_2[i]] + face_vertices = [face_vertices_1[i], face_vertices_2[i]] - plane_size = plane_points[2] - plane_points[1] + face_size = face_vertices[2] - face_vertices[1] flow_directions = [ - normalize([-plane_size[2], plane_size[1]]), - -normalize([-plane_size[2], plane_size[1]]) + normalize([-face_size[2], face_size[1]]), + -normalize([-face_size[2], face_size[1]]) ] @testset "Flow Direction $j" for j in eachindex(flow_directions) flow_direction = flow_directions[j] - inflow = BoundaryZone(; plane=plane_points, particle_spacing, density, - plane_normal=flow_direction, open_boundary_layers, - boundary_type=InFlow()) - outflow = BoundaryZone(; plane=plane_points, particle_spacing, density, - plane_normal=(-flow_direction), open_boundary_layers, - boundary_type=OutFlow()) + inflow = BoundaryZone(; boundary_face=face_vertices, particle_spacing, density, + face_normal=flow_direction, open_boundary_layers, + boundary_type=InFlow(), reference_velocity, + reference_pressure, reference_density) + outflow = BoundaryZone(; boundary_face=face_vertices, particle_spacing, density, + face_normal=(-flow_direction), open_boundary_layers, + boundary_type=OutFlow(), reference_velocity, + reference_pressure, reference_density) boundary_zones = [ inflow, @@ -49,9 +52,9 @@ @testset "`$(TrixiParticles.boundary_type_name(boundary_zone))`" for boundary_zone in boundary_zones - sign_ = (first(typeof(boundary_zone).parameters) === TrixiParticles.InFlow) ? + sign_ = (TrixiParticles.boundary_type_name(boundary_zone) == "inflow") ? 1 : -1 - fluid = extrude_geometry(plane_points; particle_spacing, n_extrude=4, + fluid = extrude_geometry(face_vertices; particle_spacing, n_extrude=4, density, pressure, direction=(sign_ * flow_direction)) @@ -60,12 +63,9 @@ density_calculator=ContinuityDensity(), smoothing_length, sound_speed) - boundary_system = OpenBoundarySPHSystem(boundary_zone; - fluid_system, buffer_size=0, - boundary_model=BoundaryModelLastiwka(), - reference_velocity, - reference_pressure, - reference_density) + boundary_system = OpenBoundarySystem(boundary_zone; + fluid_system, buffer_size=0, + boundary_model=BoundaryModelCharacteristicsLastiwka()) semi = Semidiscretization(fluid_system, boundary_system) @@ -101,13 +101,13 @@ v, u, v0_ode, u0_ode, semi, t1) evaluated_vars1 = boundary_system.cache.characteristics - if first(typeof(boundary_zone).parameters) === TrixiParticles.InFlow + if TrixiParticles.boundary_type_name(boundary_zone) == "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 first(typeof(boundary_zone).parameters) === TrixiParticles.OutFlow + elseif TrixiParticles.boundary_type_name(boundary_zone) == "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)) @@ -121,13 +121,13 @@ v, u, v0_ode, u0_ode, semi, t2) evaluated_vars2 = boundary_system.cache.characteristics - if first(typeof(boundary_zone).parameters) === TrixiParticles.InFlow + if TrixiParticles.boundary_type_name(boundary_zone) == "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 first(typeof(boundary_zone).parameters) === TrixiParticles.OutFlow + elseif TrixiParticles.boundary_type_name(boundary_zone) == "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))) diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 6e3fb9cd6f..fc55c4411f 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -11,11 +11,11 @@ particle_spacing = 0.05 domain_length = 1.0 - plane_boundary = [ + boundary_faces = [ ([0.0, 0.0], [0.0, domain_length]), ([0.0, 0.0], [-domain_length, domain_length]) ] - plane_boundary_normal = [[1.0, 0.0], [1.0, 1.0]] + boundary_face_normal = [[1.0, 0.0], [1.0, 1.0]] function pressure_function(pos) t = 0 @@ -54,18 +54,19 @@ fluid_system.cache.density .= domain_fluid.density - @testset verbose=true "plane normal $i" for i in eachindex(files) - inflow = BoundaryZone(; plane=plane_boundary[i], boundary_type=InFlow(), - plane_normal=plane_boundary_normal[i], + @testset verbose=true "face normal $i" for i in eachindex(files) + inflow = BoundaryZone(; boundary_face=boundary_faces[i], boundary_type=InFlow(), + face_normal=boundary_face_normal[i], average_inflow_velocity=false, open_boundary_layers=10, density=1000.0, particle_spacing) - open_boundary = OpenBoundarySPHSystem(inflow; fluid_system, - boundary_model=BoundaryModelTafuni(), - buffer_size=0) + open_boundary = OpenBoundarySystem(inflow; fluid_system, + boundary_model=BoundaryModelMirroringTafuni(), + buffer_size=0) semi = Semidiscretization(fluid_system, open_boundary) TrixiParticles.initialize_neighborhood_searches!(semi) + TrixiParticles.initialize!(open_boundary, semi) v_open_boundary = zero(inflow.initial_condition.velocity) v_fluid = vcat(domain_fluid.velocity, domain_fluid.pressure') @@ -75,9 +76,7 @@ TrixiParticles.extrapolate_values!(open_boundary, FirstOrderMirroring(), v_open_boundary, v_fluid, inflow.initial_condition.coordinates, - domain_fluid.coordinates, semi, 0.0; - prescribed_pressure=false, - prescribed_velocity=false) + domain_fluid.coordinates, semi) # Checked visually in ParaView: # trixi2vtk(fluid_system.initial_condition, filename="fluid", # v=domain_fluid.velocity, p=domain_fluid.pressure) @@ -106,12 +105,12 @@ particle_spacing = 0.05 domain_length = 1.0 - plane_boundary = [ + boundary_faces = [ ([0.0, 0.0, 0.0], [domain_length, 0.0, 0.0], [0.0, domain_length, 0.0]), ([0.0, 0.0, 0.0], [domain_length, 0.0, 0.0], [0.0, domain_length, domain_length]) ] - plane_boundary_normal = [[0.0, 0.0, 1.0], [0.0, -1.0, 1.0]] + boundary_face_normal = [[0.0, 0.0, 1.0], [0.0, -1.0, 1.0]] function pressure_function(pos) t = 0 @@ -151,18 +150,19 @@ fluid_system.cache.density .= domain_fluid.density - @testset verbose=true "plane normal $i" for i in eachindex(files) - inflow = BoundaryZone(; plane=plane_boundary[i], boundary_type=InFlow(), - plane_normal=plane_boundary_normal[i], + @testset verbose=true "face normal $i" for i in eachindex(files) + inflow = BoundaryZone(; boundary_face=boundary_faces[i], boundary_type=InFlow(), + face_normal=boundary_face_normal[i], average_inflow_velocity=false, open_boundary_layers=10, density=1000.0, particle_spacing) - open_boundary = OpenBoundarySPHSystem(inflow; fluid_system, - boundary_model=BoundaryModelTafuni(), - buffer_size=0) + open_boundary = OpenBoundarySystem(inflow; fluid_system, + boundary_model=BoundaryModelMirroringTafuni(), + buffer_size=0) semi = Semidiscretization(fluid_system, open_boundary) TrixiParticles.initialize_neighborhood_searches!(semi) + TrixiParticles.initialize!(open_boundary, semi) v_open_boundary = zero(inflow.initial_condition.velocity) v_fluid = vcat(domain_fluid.velocity, domain_fluid.pressure') @@ -172,9 +172,7 @@ TrixiParticles.extrapolate_values!(open_boundary, FirstOrderMirroring(), v_open_boundary, v_fluid, inflow.initial_condition.coordinates, - domain_fluid.coordinates, semi, 0.0; - prescribed_pressure=false, - prescribed_velocity=false) + domain_fluid.coordinates, semi) # Checked visually in ParaView: # trixi2vtk(fluid_system.initial_condition, filename="fluid", # v=domain_fluid.velocity, p=domain_fluid.pressure) @@ -220,22 +218,23 @@ fluid_system.cache.density .= 1000.0 if i == 2 - plane_in = ([0.0, 0.0], [0.0, domain_length]) + face_in = ([0.0, 0.0], [0.0, domain_length]) else - plane_in = ([0.0, 0.0, 0.0], [0.0, domain_length, 0.0], - [0.0, 0.0, domain_length]) + face_in = ([0.0, 0.0, 0.0], [0.0, domain_length, 0.0], + [0.0, 0.0, domain_length]) end - inflow = BoundaryZone(; plane=plane_in, boundary_type=InFlow(), - plane_normal=(i == 2 ? [1.0, 0.0] : [1.0, 0.0, 0.0]), + inflow = BoundaryZone(; boundary_face=face_in, boundary_type=InFlow(), + face_normal=(i == 2 ? [1.0, 0.0] : [1.0, 0.0, 0.0]), open_boundary_layers=open_boundary_layers, density=1000.0, particle_spacing, average_inflow_velocity=true) - open_boundary_in = OpenBoundarySPHSystem(inflow; fluid_system, - boundary_model=BoundaryModelTafuni(), - buffer_size=0) + open_boundary_in = OpenBoundarySystem(inflow; fluid_system, + boundary_model=BoundaryModelMirroringTafuni(), + buffer_size=0) semi = Semidiscretization(fluid_system, open_boundary_in) TrixiParticles.initialize_neighborhood_searches!(semi) + TrixiParticles.initialize!(open_boundary_in, semi) v_open_boundary = zero(inflow.initial_condition.velocity) u_open_boundary = inflow.initial_condition.coordinates @@ -246,10 +245,10 @@ TrixiParticles.extrapolate_values!(open_boundary_in, FirstOrderMirroring(), v_open_boundary, v_fluid, inflow.initial_condition.coordinates, - domain_fluid.coordinates, semi, 0.0) + domain_fluid.coordinates, semi) TrixiParticles.average_velocity!(v_open_boundary, u_open_boundary, open_boundary_in, - inflow, semi) + first(open_boundary_in.boundary_zones), semi) # Since the velocity profile increases linearly in positive x-direction, # we can use the first velocity entry as a representative value. @@ -275,19 +274,20 @@ fluid_system.cache.density .= domain_fluid.density - plane_out = ([domain_size[1], 0.0], [domain_size[1], domain_size[2]]) + face_out = ([domain_size[1], 0.0], [domain_size[1], domain_size[2]]) - outflow = BoundaryZone(; plane=plane_out, boundary_type=OutFlow(), - plane_normal=[-1.0, 0.0], + outflow = BoundaryZone(; boundary_face=face_out, boundary_type=OutFlow(), + face_normal=[-1.0, 0.0], open_boundary_layers=10, density=1000.0, particle_spacing) - open_boundary_out = OpenBoundarySPHSystem(outflow; fluid_system, - boundary_model=BoundaryModelTafuni(), - buffer_size=0) + open_boundary_out = OpenBoundarySystem(outflow; fluid_system, + boundary_model=BoundaryModelMirroringTafuni(), + buffer_size=0) # Temporary semidiscretization just to extrapolate the pressure into the outflow system semi = Semidiscretization(fluid_system, open_boundary_out) TrixiParticles.initialize_neighborhood_searches!(semi) + TrixiParticles.initialize!(open_boundary_out, semi) v_open_boundary = zero(outflow.initial_condition.velocity) v_fluid = vcat(domain_fluid.velocity, domain_fluid.pressure') @@ -297,21 +297,21 @@ TrixiParticles.extrapolate_values!(open_boundary_out, mirror_method, v_open_boundary, v_fluid, outflow.initial_condition.coordinates, - domain_fluid.coordinates, semi, 0.0; - prescribed_pressure=false) + domain_fluid.coordinates, semi) - plane_in = ([0.0, 0.0], [0.0, domain_size[2]]) + face_in = ([0.0, 0.0], [0.0, domain_size[2]]) - inflow = BoundaryZone(; plane=plane_in, boundary_type=InFlow(), - plane_normal=[1.0, 0.0], + inflow = BoundaryZone(; boundary_face=face_in, boundary_type=InFlow(), + face_normal=[1.0, 0.0], open_boundary_layers=10, density=1000.0, particle_spacing) - open_boundary_in = OpenBoundarySPHSystem(inflow; fluid_system, - boundary_model=BoundaryModelTafuni(), - buffer_size=0) + open_boundary_in = OpenBoundarySystem(inflow; fluid_system, + boundary_model=BoundaryModelMirroringTafuni(), + buffer_size=0) # Temporary semidiscretization just to extrapolate the pressure into the outflow system semi = Semidiscretization(fluid_system, open_boundary_in) TrixiParticles.initialize_neighborhood_searches!(semi) + TrixiParticles.initialize!(open_boundary_in, semi) v_open_boundary = zero(inflow.initial_condition.velocity) @@ -320,8 +320,7 @@ TrixiParticles.extrapolate_values!(open_boundary_in, mirror_method, v_open_boundary, v_fluid, inflow.initial_condition.coordinates, - domain_fluid.coordinates, semi, 0.0; - prescribed_pressure=false) + domain_fluid.coordinates, semi) return fluid_system, open_boundary_in, open_boundary_out, v_fluid end @@ -335,7 +334,7 @@ v_fluid = mirror(pressure_func, mirror_method) p_fluid = [TrixiParticles.current_pressure(v_fluid, fluid_system, particle) - for particle in TrixiParticles.active_particles(fluid_system)] + for particle in TrixiParticles.eachparticle(fluid_system)] fluid_system.initial_condition.pressure .= p_fluid open_boundary_in.initial_condition.pressure .= open_boundary_in.pressure diff --git a/test/schemes/fluid/fluid.jl b/test/schemes/fluid/fluid.jl index 664325a1ad..c234bb1a3b 100644 --- a/test/schemes/fluid/fluid.jl +++ b/test/schemes/fluid/fluid.jl @@ -4,3 +4,4 @@ include("pressure_acceleration.jl") include("surface_normal_sph.jl") include("surface_tension.jl") include("viscosity.jl") +include("shifting_techniques.jl") diff --git a/test/schemes/fluid/rhs.jl b/test/schemes/fluid/rhs.jl index 8ce6c01013..de14020a87 100644 --- a/test/schemes/fluid/rhs.jl +++ b/test/schemes/fluid/rhs.jl @@ -151,7 +151,7 @@ # Density is integrated with `ContinuityDensity` if system isa EntropicallyDampedSPHSystem - v = vcat(fluid.velocity, fluid.density', fluid.pressure') + v = vcat(fluid.velocity, fluid.pressure', fluid.density') else v = vcat(fluid.velocity, fluid.density') end @@ -185,14 +185,10 @@ @test isapprox(deriv_angular_momentum, zeros(3), atol=4e-15) # Total energy conservation - function drho(::ContinuityDensity, ::WeaklyCompressibleSPHSystem, + function drho(::ContinuityDensity, ::TrixiParticles.AbstractFluidSystem, particle) return dv[end, particle] end - function drho(::ContinuityDensity, ::EntropicallyDampedSPHSystem, - particle) - return dv[end - 1, particle] - end function drho(::SummationDensity, system, particle) return sum(neighbor -> drho_particle(particle, neighbor), diff --git a/test/schemes/fluid/shifting_techniques.jl b/test/schemes/fluid/shifting_techniques.jl new file mode 100644 index 0000000000..20ef2372fa --- /dev/null +++ b/test/schemes/fluid/shifting_techniques.jl @@ -0,0 +1,31 @@ +@testset verbose=true "Shifting Techniques" begin + @testset "Constructors" begin + @test_nowarn TransportVelocityAdami(background_pressure=1.0) + @test_nowarn ParticleShiftingTechniqueSun2017() + pst = @test_nowarn ParticleShiftingTechniqueSun2017(v_max_factor=1.2) + @test pst.v_factor == 1.2 + @test_nowarn ConsistentShiftingSun2019() + pst = @test_nowarn ConsistentShiftingSun2019(sound_speed_factor=0.2) + @test pst.v_factor == 0.2 + + # Can't use both `v_max_factor` and `sound_speed_factor` + @test_throws ArgumentError ParticleShiftingTechnique(v_max_factor=1.0, + sound_speed_factor=0.5) + # At least one of `v_max_factor` and `sound_speed_factor` must be positive + @test_throws ArgumentError ParticleShiftingTechnique(v_max_factor=0.0, + sound_speed_factor=0.0) + # Can't update every stage if not integrating shifting velocity + @test_throws ArgumentError ParticleShiftingTechnique(integrate_shifting_velocity=false, + update_everystage=true) + # Can't modify continuity equation if not integrating shifting velocity + @test_throws ArgumentError ParticleShiftingTechnique(integrate_shifting_velocity=false, + modify_continuity_equation=true) + # Can't modify momentum equation if not integrating shifting velocity + @test_throws ArgumentError ParticleShiftingTechnique(integrate_shifting_velocity=false, + momentum_equation_term=MomentumEquationTermSun2019()) + # Can't use second continuity equation term if not modifying continuity equation + @test_throws ArgumentError ParticleShiftingTechnique(integrate_shifting_velocity=true, + modify_continuity_equation=false, + second_continuity_equation_term=ContinuityEquationTermSun2019()) + end +end diff --git a/test/schemes/fluid/surface_normal_sph.jl b/test/schemes/fluid/surface_normal_sph.jl index 35404d621d..cec4ea3a71 100644 --- a/test/schemes/fluid/surface_normal_sph.jl +++ b/test/schemes/fluid/surface_normal_sph.jl @@ -36,7 +36,7 @@ function create_boundary_system(coordinates, particle_spacing, state_equation, k correction=nothing, reference_particle_spacing=particle_spacing) - boundary_system = BoundarySPHSystem(wall, boundary_model, adhesion_coefficient=0.0) + boundary_system = WallBoundarySystem(wall, boundary_model, adhesion_coefficient=0.0) return boundary_system end diff --git a/test/schemes/schemes.jl b/test/schemes/schemes.jl index 5cf7cd5bd2..7a2d5d2800 100644 --- a/test/schemes/schemes.jl +++ b/test/schemes/schemes.jl @@ -1,4 +1,4 @@ -include("solid/total_lagrangian_sph/total_lagrangian_sph.jl") +include("structure/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") diff --git a/test/schemes/solid/total_lagrangian_sph/rhs.jl b/test/schemes/structure/total_lagrangian_sph/rhs.jl similarity index 95% rename from test/schemes/solid/total_lagrangian_sph/rhs.jl rename to test/schemes/structure/total_lagrangian_sph/rhs.jl index fc35808fae..92fc18cccb 100644 --- a/test/schemes/solid/total_lagrangian_sph/rhs.jl +++ b/test/schemes/structure/total_lagrangian_sph/rhs.jl @@ -1,4 +1,4 @@ -@testset verbose=true "Solid RHS" begin +@testset verbose=true "Structure RHS" begin # Use `@trixi_testset` to isolate the mock functions in a separate namespace @trixi_testset "interact! Mocked" begin # Pass specific PK1 and `pos_diff` to `interact!` and verify with @@ -34,7 +34,7 @@ @testset verbose=true "Test $i" for i in 1:4 #### Setup - each_moving_particle = [particle[i]] # Only calculate dv for this one particle + each_integrated_particle = [particle[i]] # Only calculate dv for this one particle eachparticle = [particle[i], neighbor[i]] initial_coordinates = 1000 * ones(2, 10) # Just something that's not zero to catch errors initial_coordinates[:, particle[i]] = initial_coordinates_particle[i] @@ -53,7 +53,7 @@ kernel_deriv = 1.0 #### Mocking - struct MockSystem <: TrixiParticles.System{2} end + struct MockSystem <: TrixiParticles.AbstractSystem{2} end system = MockSystem() function TrixiParticles.initial_coordinates(::MockSystem) @@ -84,7 +84,7 @@ end TrixiParticles.eachparticle(::MockSystem) = eachparticle - TrixiParticles.each_moving_particle(::MockSystem) = each_moving_particle + TrixiParticles.each_integrated_particle(::MockSystem) = each_integrated_particle function TrixiParticles.add_acceleration!(_, _, ::MockSystem) return nothing @@ -108,7 +108,7 @@ dv_expected[:, particle[i]] = dv_particle_expected[i] semi = DummySemidiscretization(parallelization_backend=backends[j]) - TrixiParticles.interact_solid_solid!(dv, v_system, system, semi) + TrixiParticles.interact_structure_structure!(dv, v_system, system, semi) @test dv ≈ dv_expected end diff --git a/test/schemes/solid/total_lagrangian_sph/total_lagrangian_sph.jl b/test/schemes/structure/total_lagrangian_sph/total_lagrangian_sph.jl similarity index 100% rename from test/schemes/solid/total_lagrangian_sph/total_lagrangian_sph.jl rename to test/schemes/structure/total_lagrangian_sph/total_lagrangian_sph.jl diff --git a/test/setups/extrude_geometry.jl b/test/setups/extrude_geometry.jl index e695f2e170..5a927dbee4 100644 --- a/test/setups/extrude_geometry.jl +++ b/test/setups/extrude_geometry.jl @@ -28,7 +28,8 @@ ] @testset "Direction $i" for i in eachindex(directions) - shape = extrude_geometry((point1, point2); direction=directions[i], tlsph=true, + shape = extrude_geometry((point1, point2); direction=directions[i], + place_on_shell=true, particle_spacing=0.15, n_extrude=5, density=1.0) @test shape.coordinates ≈ expected_coords[i] @@ -68,7 +69,7 @@ end @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) + n_extrude=5, place_on_shell=true, density=1.0) @test shape.coordinates ≈ expected_coords[i] end @@ -86,7 +87,7 @@ end 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) + density=1000.0, place_on_shell=true) @test shape.coordinates ≈ expected_coords end diff --git a/test/setups/rectangular_shape.jl b/test/setups/rectangular_shape.jl index f3e77043b0..7c2fd6875b 100644 --- a/test/setups/rectangular_shape.jl +++ b/test/setups/rectangular_shape.jl @@ -41,7 +41,8 @@ ] @testset "$(loop_orders[i])" for i in eachindex(loop_orders) - shape = RectangularShape(1.0, (2, 2), (0.0, 0.0), density=1.0, tlsph=true, + shape = RectangularShape(1.0, (2, 2), (0.0, 0.0), density=1.0, + place_on_shell=true, loop_order=loop_orders[i]) @test shape.coordinates == expected_coords[i] @@ -242,7 +243,7 @@ end @testset "$(loop_orders[i])" for i in eachindex(loop_orders) shape = RectangularShape(1.0, (2, 2, 2), (0.0, 0.0, 0.0), density=1.0, - tlsph=true, loop_order=loop_orders[i]) + place_on_shell=true, loop_order=loop_orders[i]) @test shape.coordinates == expected_coords[i] end diff --git a/test/setups/sphere_shape.jl b/test/setups/sphere_shape.jl index 57d904a376..c8b87d76db 100644 --- a/test/setups/sphere_shape.jl +++ b/test/setups/sphere_shape.jl @@ -106,14 +106,14 @@ SphereShape(1.0, 1.1, (0.2, -1.0, 0.3), 1000.0, sphere_type=RoundSphere()), SphereShape(1.0, 1.2, (-0.3, 0.1, 0.8), 1000.0, sphere_type=RoundSphere()), SphereShape(0.1, 0.5, (0.3, 0.4, 0.5), 1000.0, cutout_min=(0.18, 0.4, 0.5), - cutout_max=(0.42, 10.0, 1.0), tlsph=true), - SphereShape(0.1, 0.5, (0.3, 0.4, 0.5), 1000.0, n_layers=2, tlsph=true), + cutout_max=(0.42, 10.0, 1.0), place_on_shell=true), + SphereShape(0.1, 0.5, (0.3, 0.4, 0.5), 1000.0, n_layers=2, place_on_shell=true), SphereShape(0.1, 0.5, (0.3, 0.4, 0.5), 1000.0, n_layers=2, - layer_outwards=true, tlsph=true), + layer_outwards=true, place_on_shell=true), SphereShape(0.1, 0.5, (0.3, 0.4, 0.5), 1000.0, n_layers=2, sphere_type=RoundSphere()), SphereShape(0.1, 0.55, (0.3, 0.4, 0.5), 1000.0, n_layers=2, layer_outwards=true, - sphere_type=RoundSphere(), tlsph=true) + sphere_type=RoundSphere(), place_on_shell=true) ] expected_coords = [ diff --git a/test/systems/boundary_system.jl b/test/systems/boundary_system.jl index 7d97628c76..3318e310d6 100644 --- a/test/systems/boundary_system.jl +++ b/test/systems/boundary_system.jl @@ -1,4 +1,4 @@ -@testset verbose=true "BoundarySystem" begin +@testset verbose=true "WallBoundarySystem" begin coordinates_ = [ [1.0 2.0 1.0 2.0], @@ -16,14 +16,14 @@ initial_condition = InitialCondition(; coordinates, mass, density) model = Val(:boundary_model) - system = BoundarySPHSystem(initial_condition, model) + system = WallBoundarySystem(initial_condition, model) TrixiParticles.update_positions!(system, 0, 0, 0, 0, 0, 0.0) - @test system isa BoundarySPHSystem + @test system isa WallBoundarySystem @test ndims(system) == NDIMS @test system.coordinates == coordinates @test system.boundary_model == model - @test system.movement === nothing + @test system.prescribed_motion === nothing @test system.ismoving[] == false end end @@ -36,22 +36,22 @@ initial_condition = InitialCondition(; coordinates, mass, density) model = (; hydrodynamic_mass=3) - function movement_function(t) + function movement_function(x, t) if NDIMS == 2 - return SVector(0.5 * t, 0.3 * t^2) + return x + SVector(0.5 * t, 0.3 * t^2) end - return SVector(0.5 * t, 0.3 * t^2, 0.1 * t^3) + return x + SVector(0.5 * t, 0.3 * t^2, 0.1 * t^3) end is_moving(t) = t < 1.0 - bm = BoundaryMovement(movement_function, is_moving) - system = BoundarySPHSystem(initial_condition, model, movement=bm) + bm = PrescribedMotion(movement_function, is_moving) + system = WallBoundarySystem(initial_condition, model, prescribed_motion=bm) # Moving t = 0.6 # semi is only passed to `@threaded` - system.movement(system, t, SerialBackend()) + system.prescribed_motion(system, t, SerialBackend()) if NDIMS == 2 new_coordinates = coordinates .+ [0.5 * t, 0.3 * t^2] new_velocity = [0.5, 0.6 * t] .* ones(size(new_coordinates)) @@ -68,7 +68,7 @@ # Stop moving t = 1.0 - system.movement(system, t, false) + system.prescribed_motion(system, t, false) @test isapprox(new_coordinates, system.coordinates) @@ -79,12 +79,12 @@ initial_condition = InitialCondition(; coordinates, mass, density) - bm = BoundaryMovement(movement_function, is_moving, moving_particles=[2]) - system = BoundarySPHSystem(initial_condition, model, movement=bm) + bm = PrescribedMotion(movement_function, is_moving, moving_particles=[2]) + system = WallBoundarySystem(initial_condition, model, prescribed_motion=bm) t = 0.1 # semi is only passed to `@threaded` - system.movement(system, t, SerialBackend()) + system.prescribed_motion(system, t, SerialBackend()) if NDIMS == 2 new_coordinates[:, 2] .+= [0.5 * t, 0.3 * t^2] @@ -111,15 +111,15 @@ initial_condition = InitialCondition(; coordinates, mass, density) model = (; hydrodynamic_mass=3) - system = BoundarySPHSystem(initial_condition, model) + system = WallBoundarySystem(initial_condition, model) - show_compact = "BoundarySPHSystem{2}((hydrodynamic_mass = 3,), nothing, 0.0, 0) with 2 particles" + show_compact = "WallBoundarySystem{2}((hydrodynamic_mass = 3,), nothing, 0.0, 0) with 2 particles" @test repr(system) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ BoundarySPHSystem{2} │ - │ ════════════════════ │ + │ WallBoundarySystem{2} │ + │ ═════════════════════ │ │ #particles: ………………………………………………… 2 │ │ boundary model: ……………………………………… (hydrodynamic_mass = 3,) │ │ movement function: ……………………………… nothing │ diff --git a/test/systems/edac_system.jl b/test/systems/edac_system.jl index 3d8cf9babb..ac8a424853 100644 --- a/test/systems/edac_system.jl +++ b/test/systems/edac_system.jl @@ -32,7 +32,7 @@ @test system.mass == mass @test system.smoothing_kernel == smoothing_kernel @test TrixiParticles.initial_smoothing_length(system) == smoothing_length - @test system.transport_velocity isa Nothing + @test system.shifting_technique isa Nothing @test system.viscosity === nothing @test system.nu_edac == (0.5 * smoothing_length * sound_speed) / 8 @test system.acceleration == [0.0 for _ in 1:NDIMS] @@ -87,7 +87,7 @@ @test system.mass == setup.mass @test system.smoothing_kernel == smoothing_kernel @test TrixiParticles.initial_smoothing_length(system) == smoothing_length - @test system.transport_velocity isa Nothing + @test system.shifting_technique isa Nothing @test system.viscosity === nothing @test system.nu_edac == (0.5 * smoothing_length * sound_speed) / 8 @test system.acceleration == [0.0 for _ in 1:NDIMS] @@ -140,7 +140,7 @@ │ viscosity: …………………………………………………… Nothing │ │ ν₍EDAC₎: ………………………………………………………… ≈ 0.226 │ │ smoothing kernel: ………………………………… Val │ - │ tansport velocity formulation: Nothing │ + │ shifting technique: …………………………… nothing │ │ average pressure reduction: ……… no │ │ acceleration: …………………………………………… [0.0, 0.0] │ │ surface tension: …………………………………… nothing │ @@ -165,7 +165,7 @@ smoothing_length, sound_speed) u0 = zeros(TrixiParticles.u_nvariables(system), - TrixiParticles.n_moving_particles(system)) + TrixiParticles.n_integrated_particles(system)) TrixiParticles.write_u0!(u0, system) @test u0 == coordinates @@ -191,7 +191,7 @@ smoothing_length, sound_speed) v0 = zeros(TrixiParticles.v_nvariables(system), - TrixiParticles.n_moving_particles(system)) + TrixiParticles.n_integrated_particles(system)) TrixiParticles.write_v0!(v0, system) system.cache.density .= density @@ -208,7 +208,7 @@ smoothing_length, sound_speed) v0 = zeros(TrixiParticles.v_nvariables(system), - TrixiParticles.n_moving_particles(system)) + TrixiParticles.n_integrated_particles(system)) TrixiParticles.write_v0!(v0, system) @test v0 == vcat(velocity, [0.8, 1.0]') @@ -221,11 +221,11 @@ fluid = rectangular_patch(particle_spacing, (3, 3), seed=1) - transport_velocity = [nothing, TransportVelocityAdami(10000.0)] + transport_velocity = [nothing, TransportVelocityAdami(background_pressure=10000.0)] names = ["No TVF", "TransportVelocityAdami"] @testset "$(names[i])" for i in eachindex(transport_velocity) system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, - transport_velocity=transport_velocity[i], + shifting_technique=transport_velocity[i], average_pressure_reduction=true, smoothing_length, 0.0) semi = Semidiscretization(system) diff --git a/test/systems/open_boundary_system.jl b/test/systems/open_boundary_system.jl index 0fdf98f907..f864208004 100644 --- a/test/systems/open_boundary_system.jl +++ b/test/systems/open_boundary_system.jl @@ -1,114 +1,72 @@ -@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) +@testset verbose=true "`OpenBoundarySystem`" begin + @testset "`show`" begin # Mock fluid system - struct FluidSystemMock2 <: TrixiParticles.FluidSystem{2} end + struct FluidSystemMock2 <: TrixiParticles.AbstractFluidSystem{2} end TrixiParticles.initial_smoothing_length(system::FluidSystemMock2) = 1.0 TrixiParticles.nparticles(system::FluidSystemMock2) = 1 - inflow = BoundaryZone(; plane, particle_spacing=0.1, - plane_normal=flow_direction, density=1.0, - open_boundary_layers=2, boundary_type=InFlow()) - - 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; - boundary_model=BoundaryModelLastiwka(), - buffer_size=0, - fluid_system=FluidSystemMock2(), - reference_density=0, - reference_pressure=0, - 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; - boundary_model=BoundaryModelLastiwka(), - buffer_size=0, - fluid_system=FluidSystemMock2(), - reference_density=0, - reference_velocity=[1.0, - 1.0], - 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; - boundary_model=BoundaryModelLastiwka(), - buffer_size=0, - fluid_system=FluidSystemMock2(), - reference_density, - reference_velocity=[1.0, - 1.0], - reference_pressure=0) - end - @testset "`show`" begin - inflow = BoundaryZone(; plane=([0.0, 0.0], [0.0, 1.0]), particle_spacing=0.05, - plane_normal=(1.0, 0.0), density=1.0, + inflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), + particle_spacing=0.05, + face_normal=(1.0, 0.0), density=1.0, open_boundary_layers=4, boundary_type=InFlow()) - system = OpenBoundarySPHSystem(inflow; buffer_size=0, - boundary_model=BoundaryModelLastiwka(), - reference_density=0.0, - reference_pressure=0.0, - reference_velocity=[0.0, 0.0], - fluid_system=FluidSystemMock2()) + system = OpenBoundarySystem(inflow; buffer_size=0, + boundary_model=BoundaryModelCharacteristicsLastiwka(), + fluid_system=FluidSystemMock2()) - show_compact = "OpenBoundarySPHSystem{2}(InFlow) with 80 particles" + show_compact = "OpenBoundarySystem{2}() with 80 particles" @test repr(system) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ OpenBoundarySPHSystem{2} │ - │ ════════════════════════ │ + │ OpenBoundarySystem{2} │ + │ ═════════════════════ │ │ #particles: ………………………………………………… 80 │ │ #buffer_particles: ……………………………… 0 │ + │ #boundary_zones: …………………………………… 1 │ │ fluid system: …………………………………………… FluidSystemMock2 │ - │ boundary model: ……………………………………… BoundaryModelLastiwka │ - │ boundary type: ………………………………………… InFlow │ - │ prescribed velocity: ………………………… constant_vector │ - │ prescribed pressure: ………………………… constant_scalar │ - │ prescribed density: …………………………… constant_scalar │ - │ width: ……………………………………………………………… 0.2 │ + │ boundary model: ……………………………………… BoundaryModelCharacteristicsLastiwka │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box - outflow = BoundaryZone(; plane=([0.0, 0.0], [0.0, 1.0]), particle_spacing=0.05, - plane_normal=(1.0, 0.0), density=1.0, open_boundary_layers=4, + outflow = BoundaryZone(; boundary_face=([5.0, 0.0], [5.0, 1.0]), + particle_spacing=0.05, + face_normal=(1.0, 0.0), density=1.0, open_boundary_layers=4, boundary_type=OutFlow()) - system = OpenBoundarySPHSystem(outflow; buffer_size=0, - boundary_model=BoundaryModelLastiwka(), - reference_density=0.0, - reference_pressure=0.0, - reference_velocity=[0.0, 0.0], - fluid_system=FluidSystemMock2()) + system = OpenBoundarySystem(outflow; buffer_size=0, + boundary_model=BoundaryModelMirroringTafuni(), + fluid_system=FluidSystemMock2()) - show_compact = "OpenBoundarySPHSystem{2}(OutFlow) with 80 particles" + show_compact = "OpenBoundarySystem{2}() with 80 particles" @test repr(system) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ OpenBoundarySPHSystem{2} │ - │ ════════════════════════ │ + │ OpenBoundarySystem{2} │ + │ ═════════════════════ │ │ #particles: ………………………………………………… 80 │ │ #buffer_particles: ……………………………… 0 │ + │ #boundary_zones: …………………………………… 1 │ + │ fluid system: …………………………………………… FluidSystemMock2 │ + │ boundary model: ……………………………………… BoundaryModelMirroringTafuni │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", system) == show_box + + system = OpenBoundarySystem(outflow, inflow; buffer_size=0, + boundary_model=BoundaryModelMirroringTafuni(), + fluid_system=FluidSystemMock2()) + + show_compact = "OpenBoundarySystem{2}() with 160 particles" + @test repr(system) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ OpenBoundarySystem{2} │ + │ ═════════════════════ │ + │ #particles: ………………………………………………… 160 │ + │ #buffer_particles: ……………………………… 0 │ + │ #boundary_zones: …………………………………… 2 │ │ fluid system: …………………………………………… FluidSystemMock2 │ - │ boundary model: ……………………………………… BoundaryModelLastiwka │ - │ boundary type: ………………………………………… OutFlow │ - │ prescribed velocity: ………………………… constant_vector │ - │ prescribed pressure: ………………………… constant_scalar │ - │ prescribed density: …………………………… constant_scalar │ - │ width: ……………………………………………………………… 0.2 │ + │ boundary model: ……………………………………… BoundaryModelMirroringTafuni │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box diff --git a/test/systems/packing_system.jl b/test/systems/packing_system.jl index d55afd559a..b42e534275 100644 --- a/test/systems/packing_system.jl +++ b/test/systems/packing_system.jl @@ -19,7 +19,7 @@ │ neighborhood search: ………………………… GridNeighborhoodSearch │ │ #particles: ………………………………………………… 307 │ │ smoothing kernel: ………………………………… SchoenbergQuinticSplineKernel │ - │ tlsph: ……………………………………………………………… no │ + │ place_on_shell: ……………………………………… no │ │ boundary: ……………………………………………………… no │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box @@ -36,7 +36,7 @@ │ neighborhood search: ………………………… GridNeighborhoodSearch │ │ #particles: ………………………………………………… 307 │ │ smoothing kernel: ………………………………… SchoenbergQuinticSplineKernel │ - │ tlsph: ……………………………………………………………… no │ + │ place_on_shell: ……………………………………… no │ │ boundary: ……………………………………………………… yes │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box @@ -52,7 +52,7 @@ │ neighborhood search: ………………………… Nothing │ │ #particles: ………………………………………………… 307 │ │ smoothing kernel: ………………………………… SchoenbergQuinticSplineKernel │ - │ tlsph: ……………………………………………………………… no │ + │ place_on_shell: ……………………………………… no │ │ boundary: ……………………………………………………… no │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" end diff --git a/test/systems/systems.jl b/test/systems/systems.jl index 21246721dd..0dedbed7a0 100644 --- a/test/systems/systems.jl +++ b/test/systems/systems.jl @@ -1,6 +1,6 @@ include("wcsph_system.jl") include("edac_system.jl") -include("solid_system.jl") +include("tlsph_system.jl") include("boundary_system.jl") include("open_boundary_system.jl") include("dem_system.jl") diff --git a/test/systems/solid_system.jl b/test/systems/tlsph_system.jl similarity index 97% rename from test/systems/solid_system.jl rename to test/systems/tlsph_system.jl index ae85ee1b2e..f8c587e3fa 100644 --- a/test/systems/solid_system.jl +++ b/test/systems/tlsph_system.jl @@ -34,7 +34,7 @@ @test system.current_coordinates == coordinates @test system.mass == mass @test system.material_density == material_densities - @test system.n_moving_particles == 2 + @test system.n_integrated_particles == 2 @test system.young_modulus == E @test system.poisson_ratio == nu @test system.lame_lambda == 1.0 @@ -75,7 +75,7 @@ │ TotalLagrangianSPHSystem{2} │ │ ═══════════════════════════ │ │ total #particles: ………………………………… 2 │ - │ #fixed particles: ………………………………… 0 │ + │ #clamped particles: …………………………… 0 │ │ Young's modulus: …………………………………… 2.5 │ │ Poisson ratio: ………………………………………… 0.25 │ │ smoothing kernel: ………………………………… Val │ @@ -97,7 +97,7 @@ │ TotalLagrangianSPHSystem{2} │ │ ═══════════════════════════ │ │ total #particles: ………………………………… 2 │ - │ #fixed particles: ………………………………… 0 │ + │ #clamped particles: …………………………… 0 │ │ Young's modulus: …………………………………… min = 1.2, max = 3.4 │ │ Poisson ratio: ………………………………………… min = 0.2, max = 0.4 │ │ smoothing kernel: ………………………………… Val │ @@ -330,7 +330,7 @@ boundary_model=boundary_model) u0 = zeros(TrixiParticles.u_nvariables(system), - TrixiParticles.n_moving_particles(system)) + TrixiParticles.n_integrated_particles(system)) TrixiParticles.write_u0!(u0, system) @test u0 == coordinates @@ -357,7 +357,7 @@ boundary_model=boundary_model) v0 = zeros(TrixiParticles.v_nvariables(system), - TrixiParticles.n_moving_particles(system)) + TrixiParticles.n_integrated_particles(system)) TrixiParticles.write_v0!(v0, system) @test v0 == velocity diff --git a/test/systems/wcsph_system.jl b/test/systems/wcsph_system.jl index cf6020d068..7c034e7757 100644 --- a/test/systems/wcsph_system.jl +++ b/test/systems/wcsph_system.jl @@ -199,7 +199,7 @@ smoothing_length, density_diffusion=density_diffusion) - show_compact = "WeaklyCompressibleSPHSystem{2}(SummationDensity(), nothing, Val{:state_equation}(), Val{:smoothing_kernel}(), nothing, Val{:density_diffusion}(), nothing, nothing, [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, nothing, nothing, [0.0, 0.0], nothing) with 2 particles" @test repr(system) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ @@ -211,8 +211,8 @@ │ state equation: ……………………………………… Val │ │ smoothing kernel: ………………………………… Val │ │ viscosity: …………………………………………………… nothing │ - │ tansport velocity formulation: Nothing │ │ density diffusion: ……………………………… Val{:density_diffusion}() │ + │ shifting technique: …………………………… nothing │ │ surface tension: …………………………………… nothing │ │ surface normal method: …………………… nothing │ │ acceleration: …………………………………………… [0.0, 0.0] │ @@ -238,7 +238,7 @@ smoothing_length) u0 = zeros(TrixiParticles.u_nvariables(system), - TrixiParticles.n_moving_particles(system)) + TrixiParticles.n_integrated_particles(system)) TrixiParticles.write_u0!(u0, system) @test u0 == coordinates @@ -263,7 +263,7 @@ smoothing_length) v0 = zeros(TrixiParticles.v_nvariables(system), - TrixiParticles.n_moving_particles(system)) + TrixiParticles.n_integrated_particles(system)) TrixiParticles.write_v0!(v0, system) system.cache.density .= density @@ -281,7 +281,7 @@ smoothing_length) v0 = zeros(TrixiParticles.v_nvariables(system), - TrixiParticles.n_moving_particles(system)) + TrixiParticles.n_integrated_particles(system)) TrixiParticles.write_v0!(v0, system) @test v0 == vcat(velocity, density') diff --git a/test/validation/validation.jl b/test/validation/validation.jl index 70aceb4bf4..c156cbbd95 100644 --- a/test/validation/validation.jl +++ b/test/validation/validation.jl @@ -65,8 +65,8 @@ @test isapprox(error_wcsph_P2, 0, atol=5e-4) else # Reference values are computed with 1.11 - @test isapprox(error_edac_P1, 0, atol=eps()) - @test isapprox(error_edac_P2, 0, atol=eps()) + @test isapprox(error_edac_P1, 0, atol=5e-7) + @test isapprox(error_edac_P2, 0, atol=4e-11) @test isapprox(error_wcsph_P1, 0, atol=eps()) @test isapprox(error_wcsph_P2, 0, atol=eps()) end diff --git a/validation/dam_break_2d/validation_dam_break_2d.jl b/validation/dam_break_2d/validation_dam_break_2d.jl index aee5f72e0d..3f9b008c3b 100644 --- a/validation/dam_break_2d/validation_dam_break_2d.jl +++ b/validation/dam_break_2d/validation_dam_break_2d.jl @@ -49,15 +49,15 @@ sensor_names = ["P1", "P2", "P3"] tank_right_wall_x = floor(5.366 * H / particle_spacing) * particle_spacing - 0.5 * particle_spacing -pressure_P1 = (system, v_ode, u_ode, semi, +pressure_P1 = (system, dv_ode, du_ode, v_ode, u_ode, semi, t) -> interpolated_pressure([tank_right_wall_x, P1_y_top], [tank_right_wall_x, P1_y_bottom], v_ode, u_ode, t, system, semi) -pressure_P2 = (system, v_ode, u_ode, semi, +pressure_P2 = (system, dv_ode, du_ode, v_ode, u_ode, semi, t) -> interpolated_pressure([tank_right_wall_x, P2_y_top], [tank_right_wall_x, P2_y_bottom], v_ode, u_ode, t, system, semi) -pressure_P3 = (system, v_ode, u_ode, semi, +pressure_P3 = (system, dv_ode, du_ode, v_ode, u_ode, semi, t) -> interpolated_pressure([tank_right_wall_x, P3_y_top], [tank_right_wall_x, P3_y_bottom], v_ode, u_ode, t, system, semi) diff --git a/validation/lid_driven_cavity_2d/validation_lid_driven_cavity_2d.jl b/validation/lid_driven_cavity_2d/validation_lid_driven_cavity_2d.jl index c75d1500e3..b805667d4e 100644 --- a/validation/lid_driven_cavity_2d/validation_lid_driven_cavity_2d.jl +++ b/validation/lid_driven_cavity_2d/validation_lid_driven_cavity_2d.jl @@ -13,9 +13,10 @@ reynolds_numbers = [100.0, 1000.0, 10_000.0] const SENSOR_CAPTURE_TIME = 24.8 const CAPTURE_STARTED = Ref(false) -interpolated_velocity(system, v, u, semi, t) = nothing +interpolated_velocity(system, dv_ode, du_ode, v_ode, u_ode, semi, t) = nothing -function interpolated_velocity(system::TrixiParticles.FluidSystem, v, u, semi, t) +function interpolated_velocity(system::TrixiParticles.AbstractFluidSystem, + dv_ode, du_ode, v_ode, u_ode, semi, t) if t < SENSOR_CAPTURE_TIME return nothing end @@ -23,10 +24,10 @@ function interpolated_velocity(system::TrixiParticles.FluidSystem, v, u, semi, t n_particles_xy = round(Int, 1.0 / system.initial_condition.particle_spacing) values_y = interpolate_line([0.5, 0.0], [0.5, 1.0], n_particles_xy, semi, system, - v, u; endpoint=true, cut_off_bnd=true) + v_ode, u_ode; endpoint=true, cut_off_bnd=true) values_x = interpolate_line([0.0, 0.5], [1.0, 0.5], n_particles_xy, semi, system, - v, u; endpoint=true, cut_off_bnd=true) + v_ode, u_ode; endpoint=true, cut_off_bnd=true) vy_y = stack(values_y.velocity)[2, :] vy_x = stack(values_y.velocity)[1, :] diff --git a/validation/oscillating_beam_2d/plot_oscillating_beam_results.jl b/validation/oscillating_beam_2d/plot_oscillating_beam_results.jl index 32c15f7321..8c89f7098d 100644 --- a/validation/oscillating_beam_2d/plot_oscillating_beam_results.jl +++ b/validation/oscillating_beam_2d/plot_oscillating_beam_results.jl @@ -24,8 +24,8 @@ merged_files = vcat(reference_files, simulation_files) input_files = sort(merged_files, by=extract_number_from_filename) # Regular expressions for matching keys -key_pattern_x = r"deflection_x_solid_\d+" -key_pattern_y = r"deflection_y_solid_\d+" +key_pattern_x = r"deflection_x_structure_\d+" +key_pattern_y = r"deflection_y_structure_\d+" # Setup for Makie plotting fig = Figure(size=(1200, 800)) diff --git a/validation/oscillating_beam_2d/validation_oscillating_beam_2d.jl b/validation/oscillating_beam_2d/validation_oscillating_beam_2d.jl index 8e6cc83401..d610157ebc 100644 --- a/validation/oscillating_beam_2d/validation_oscillating_beam_2d.jl +++ b/validation/oscillating_beam_2d/validation_oscillating_beam_2d.jl @@ -26,7 +26,7 @@ n_particles_beam_y = 5 # Overwrite `sol` assignment to skip time integration trixi_include(@__MODULE__, - joinpath(examples_dir(), "solid", "oscillating_beam_2d.jl"), + joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), n_particles_y=n_particles_beam_y, sol=nothing, tspan=tspan, penalty_force=PenaltyForceGanzenmueller(alpha=0.01)) @@ -49,12 +49,12 @@ run_file_name = joinpath("out", reference_data = JSON.parsefile(reference_file_name) run_data = JSON.parsefile(run_file_name) -error_deflection_x = interpolated_mse(reference_data["deflection_x_solid_1"]["time"], - reference_data["deflection_x_solid_1"]["values"], - run_data["deflection_x_solid_1"]["time"], - run_data["deflection_x_solid_1"]["values"]) +error_deflection_x = interpolated_mse(reference_data["deflection_x_structure_1"]["time"], + reference_data["deflection_x_structure_1"]["values"], + run_data["deflection_x_structure_1"]["time"], + run_data["deflection_x_structure_1"]["values"]) -error_deflection_y = interpolated_mse(reference_data["deflection_y_solid_1"]["time"], - reference_data["deflection_y_solid_1"]["values"], - run_data["deflection_y_solid_1"]["time"], - run_data["deflection_y_solid_1"]["values"]) +error_deflection_y = interpolated_mse(reference_data["deflection_y_structure_1"]["time"], + reference_data["deflection_y_structure_1"]["values"], + run_data["deflection_y_structure_1"]["time"], + run_data["deflection_y_structure_1"]["values"]) diff --git a/validation/oscillating_beam_2d/validation_reference_17.json b/validation/oscillating_beam_2d/validation_reference_17.json index 6a1a210a51..7594808f31 100644 --- a/validation/oscillating_beam_2d/validation_reference_17.json +++ b/validation/oscillating_beam_2d/validation_reference_17.json @@ -4,7 +4,7 @@ "solver_version": "v0.1.0-6-g3164e99d-dirty", "solver_name": "TrixiParticles.jl" }, - "deflection_x_solid_1": { + "deflection_x_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -1009,7 +1009,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -2.399319076751283e-10, @@ -2016,7 +2016,7 @@ "datatype": "Float64", "type": "series" }, - "deflection_y_solid_1": { + "deflection_y_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -3021,7 +3021,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -9.999997556011525e-5, diff --git a/validation/oscillating_beam_2d/validation_reference_33.json b/validation/oscillating_beam_2d/validation_reference_33.json index 45b764827e..bc40349492 100644 --- a/validation/oscillating_beam_2d/validation_reference_33.json +++ b/validation/oscillating_beam_2d/validation_reference_33.json @@ -4,7 +4,7 @@ "solver_version": "v0.1.0-6-g3164e99d-dirty", "solver_name": "TrixiParticles.jl" }, - "deflection_x_solid_1": { + "deflection_x_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -1009,7 +1009,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -2.5558510863277206e-10, @@ -2016,7 +2016,7 @@ "datatype": "Float64", "type": "series" }, - "deflection_y_solid_1": { + "deflection_y_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -3021,7 +3021,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -9.999997643593724e-5, diff --git a/validation/oscillating_beam_2d/validation_reference_5.json b/validation/oscillating_beam_2d/validation_reference_5.json index da111b580b..5ecfb68e2f 100644 --- a/validation/oscillating_beam_2d/validation_reference_5.json +++ b/validation/oscillating_beam_2d/validation_reference_5.json @@ -4,7 +4,7 @@ "solver_version": "f836223-dirty", "solver_name": "TrixiParticles.jl" }, - "deflection_x_solid_1": { + "deflection_x_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -1009,7 +1009,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -2.1757223800378256e-10, @@ -2016,7 +2016,7 @@ "datatype": "Float64", "type": "series" }, - "deflection_y_solid_1": { + "deflection_y_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -3021,7 +3021,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -9.999999694490329e-5, diff --git a/validation/oscillating_beam_2d/validation_reference_65.json b/validation/oscillating_beam_2d/validation_reference_65.json index 7e69d42b10..567a9af232 100644 --- a/validation/oscillating_beam_2d/validation_reference_65.json +++ b/validation/oscillating_beam_2d/validation_reference_65.json @@ -4,7 +4,7 @@ "solver_version": "v0.1.0-6-g3164e99d-dirty", "solver_name": "TrixiParticles.jl" }, - "deflection_x_solid_1": { + "deflection_x_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -1009,7 +1009,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -2.739832249964991e-10, @@ -2016,7 +2016,7 @@ "datatype": "Float64", "type": "series" }, - "deflection_y_solid_1": { + "deflection_y_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -3021,7 +3021,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -9.999995508498498e-5, diff --git a/validation/oscillating_beam_2d/validation_reference_9.json b/validation/oscillating_beam_2d/validation_reference_9.json index 929b94bebc..0c2f91f4ad 100644 --- a/validation/oscillating_beam_2d/validation_reference_9.json +++ b/validation/oscillating_beam_2d/validation_reference_9.json @@ -4,7 +4,7 @@ "solver_version": "c6184f4-dirty", "solver_name": "TrixiParticles.jl" }, - "deflection_y_solid_1": { + "deflection_y_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -1009,7 +1009,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -9.999999490424051e-5, @@ -2016,7 +2016,7 @@ "datatype": "Float64", "type": "series" }, - "deflection_x_solid_1": { + "deflection_x_structure_1": { "n_values": 1001, "time": [ 0.0, @@ -3021,7 +3021,7 @@ 9.99, 10.0 ], - "system_name": "solid", + "system_name": "structure", "values": [ 0.0, -2.0271367917601424e-10, diff --git a/validation/taylor_green_vortex_2d/validation_taylor_green_vortex_2d.jl b/validation/taylor_green_vortex_2d/validation_taylor_green_vortex_2d.jl index 849ddd38b6..20292524da 100644 --- a/validation/taylor_green_vortex_2d/validation_taylor_green_vortex_2d.jl +++ b/validation/taylor_green_vortex_2d/validation_taylor_green_vortex_2d.jl @@ -23,7 +23,7 @@ perturb_coordinates = [false, true] return zero(eltype(system)) end -function compute_l1v_error(system, v_ode, u_ode, semi, t) +function compute_l1v_error(system, dv_ode, du_ode, v_ode, u_ode, semi, t) v_analytical_avg = 0.0 v_avg = 0.0 @@ -46,7 +46,7 @@ function compute_l1v_error(system, v_ode, u_ode, semi, t) return v_avg /= v_analytical_avg end -function compute_l1p_error(system, v_ode, u_ode, semi, t) +function compute_l1p_error(system, dv_ode, du_ode, v_ode, u_ode, semi, t) p_max_exact = 0.0 L1p = 0.0 @@ -74,8 +74,8 @@ end # The pressure plotted in the paper is the difference of the local pressure minus # the average of the pressure of all particles. -function diff_p_loc_p_avg(system, v, u, semi, t) - p_avg_tot = avg_pressure(system, v, u, semi, t) +function diff_p_loc_p_avg(system, dv_ode, du_ode, v, u, semi, t) + p_avg_tot = avg_pressure(system, dv_ode, du_ode, v, u, semi, t) return v[end, :] .- p_avg_tot end