From 87da03c8b49d7ea74ddb525bc5977f19f16c8344 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 2 Jul 2025 14:02:52 +0200 Subject: [PATCH 01/54] swap extrapolation --- src/schemes/boundary/open_boundary/mirroring.jl | 12 ++---------- src/schemes/boundary/open_boundary/system.jl | 3 +++ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 2eb7b286d..dbe98e6a3 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -92,8 +92,7 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, correction_matrix[] += L - # For a WCSPH system, the pressure is determined by the state equation if it is not prescribed - if !prescribed_pressure && !(fluid_system isa WeaklyCompressibleSPHSystem) + if !prescribed_pressure extrapolated_pressure_correction[] += pressure_b * R end @@ -141,19 +140,12 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, density[particle] = reference_value(reference_density, density[particle], particle_coords, t) else - f_d = L_inv * extrapolated_density_correction[] - df_d = f_d[two_to_end] - - density[particle] = f_d[1] + dot(pos_diff, df_d) + inverse_state_equation!(density, state_equation, pressure, particle) end if prescribed_pressure pressure[particle] = reference_value(reference_pressure, pressure[particle], particle_coords, t) - elseif fluid_system isa WeaklyCompressibleSPHSystem - # For a WCSPH system, the pressure is determined by the state equation - # if it is not prescribed - pressure[particle] = state_equation(density[particle]) else f_d = L_inv * extrapolated_pressure_correction[] df_d = f_d[two_to_end] diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index f299fd928..bf07d5f25 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -450,6 +450,9 @@ end function check_reference_values!(boundary_model::BoundaryModelLastiwka, reference_density, reference_pressure, reference_velocity) + + # TODO + return boundary_model boundary_model.extrapolate_reference_values && return boundary_model if any(isnothing.([reference_density, reference_pressure, reference_velocity])) From f30f01934d1cb8eef91654e85ce21c22d4c00410 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 3 Jul 2025 12:54:52 +0200 Subject: [PATCH 02/54] swap density pressure --- src/schemes/boundary/open_boundary/mirroring.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index dbe98e6a3..5c64fdea8 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -136,13 +136,6 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, end end - if prescribed_density - density[particle] = reference_value(reference_density, density[particle], - particle_coords, t) - else - inverse_state_equation!(density, state_equation, pressure, particle) - end - if prescribed_pressure pressure[particle] = reference_value(reference_pressure, pressure[particle], particle_coords, t) @@ -152,6 +145,13 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, pressure[particle] = f_d[1] + dot(pos_diff, df_d) end + + if prescribed_density + density[particle] = reference_value(reference_density, density[particle], + particle_coords, t) + else + inverse_state_equation!(density, state_equation, pressure, particle) + end end if !(prescribed_velocity) && boundary_zone.average_inflow_velocity From 61ea4568dec9786b44033b70d37571ee0e1d5b9f Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 3 Jul 2025 17:07:15 +0200 Subject: [PATCH 03/54] adapt docs --- examples/fluid/play_ground.jl | 233 ++++++++++++++++++ .../method_of_characteristics.jl | 3 +- .../boundary/open_boundary/mirroring.jl | 5 +- src/schemes/boundary/open_boundary/system.jl | 34 +-- 4 files changed, 244 insertions(+), 31 deletions(-) create mode 100644 examples/fluid/play_ground.jl diff --git a/examples/fluid/play_ground.jl b/examples/fluid/play_ground.jl new file mode 100644 index 000000000..5bc88fb7c --- /dev/null +++ b/examples/fluid/play_ground.jl @@ -0,0 +1,233 @@ +# Flow past a circular cylinder (vortex street), Tafuni et al. (2018). +# Other literature using this validation: +# Vacandio et al. (2013), Marrone et al. (2013), Calhoun (2002), Liu et al. (1998) + +using TrixiParticles +using OrdinaryDiffEq + +# ========================================================================================== +# ==== Resolution +factor_d = 0.08 # Resolution in the paper is `0.01` (5M particles) + +const cylinder_diameter = 0.1 +particle_spacing = factor_d * cylinder_diameter + +# Make sure that the kernel support of fluid particles at a boundary is always fully sampled +boundary_layers = 4 + +# Make sure that the kernel support of fluid particles at an open boundary is always +# fully sampled. +# Note: Due to the dynamics at the inlets and outlets of open boundaries, +# it is recommended to use `open_boundary_layers > boundary_layers` +open_boundary_layers = 8 + +# ========================================================================================== +# ==== Experiment Setup +tspan = (0.0, 1.5) + +# Boundary geometry and initial fluid particle positions +domain_size = (25 * cylinder_diameter, 20 * cylinder_diameter) + +flow_direction = [1.0, 0.0] +reynolds_number = 200 +const prescribed_velocity = 1.0 +const fluid_density = 1000.0 +sound_speed = 10 * prescribed_velocity + +boundary_size = (domain_size[1] + 2 * particle_spacing * open_boundary_layers, + domain_size[2]) + +pipe = RectangularTank(particle_spacing, domain_size, boundary_size, fluid_density, + n_layers=boundary_layers, velocity=[prescribed_velocity, 0.0], + faces=(false, false, true, true)) + +# Shift pipe walls in negative x-direction for the inflow +pipe.boundary.coordinates[1, :] .-= particle_spacing * open_boundary_layers + +n_buffer_particles = 10 * pipe.n_particles_per_dimension[2]^(ndims(pipe.fluid) - 1) + +cylinder_center = (5 * cylinder_diameter, domain_size[2] / 2) +cylinder = SphereShape(particle_spacing, cylinder_diameter / 2, + cylinder_center, fluid_density, sphere_type=RoundSphere()) + +fluid = setdiff(pipe.fluid, cylinder) + +# ========================================================================================== +# ==== Fluid +wcsph = true + +h_factor = 1.5 +smoothing_length = h_factor * particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() + +fluid_density_calculator = ContinuityDensity() + +kinematic_viscosity = prescribed_velocity * cylinder_diameter / reynolds_number + +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=7) +viscosity = ViscosityAdami(nu=kinematic_viscosity) + +if wcsph + density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) + + fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, + state_equation, smoothing_kernel, + density_diffusion=density_diffusion, + smoothing_length, viscosity=viscosity, + pressure_acceleration=tensile_instability_control, + buffer_size=n_buffer_particles) +else + state_equation = nothing + fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, + sound_speed, viscosity=viscosity, + density_calculator=fluid_density_calculator, + buffer_size=n_buffer_particles) +end + +# ========================================================================================== +# ==== Open Boundary +function velocity_function2d(pos, t) + return SVector(prescribed_velocity, 0.0) +end + +open_boundary_model = BoundaryModelTafuni() + +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) + +reference_velocity_in = velocity_function2d +# At the inlet, neither pressure nor density are prescribed; instead, +# these values are extrapolated from the fluid domain +reference_pressure_in = nothing +reference_density_in = nothing +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) + +boundary_type_out = OutFlow() + +plane_out = ([pipe.fluid_size[1], 0.0], [pipe.fluid_size[1], domain_size[2]]) +outflow = BoundaryZone(; plane=plane_out, plane_normal=(-flow_direction), + open_boundary_layers, density=fluid_density, particle_spacing, + boundary_type=boundary_type_out) + +# At the outlet, we allow the flow to exit freely without imposing any boundary conditions +reference_velocity_out = nothing +reference_pressure_out = nothing +reference_density_out = nothing +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) +# ========================================================================================== +# ==== Boundary +boundary_model = BoundaryModelDummyParticles(pipe.boundary.density, pipe.boundary.mass, + AdamiPressureExtrapolation(), + state_equation=state_equation, + smoothing_kernel, smoothing_length) + +boundary_system_wall = BoundarySPHSystem(pipe.boundary, boundary_model) + +boundary_model_cylinder = BoundaryModelDummyParticles(cylinder.density, cylinder.mass, + AdamiPressureExtrapolation(), + state_equation=state_equation, + viscosity=viscosity, + smoothing_kernel, smoothing_length) + +boundary_system_cylinder = BoundarySPHSystem(cylinder, boundary_model_cylinder) + +# ========================================================================================== +# ==== Postprocessing +circle = SphereShape(particle_spacing, (cylinder_diameter + particle_spacing) / 2, + cylinder_center, fluid_density, n_layers=1, + sphere_type=RoundSphere()) + +# Points for pressure interpolation, located at the wall interface +const data_points = copy(circle.coordinates) +const center = SVector(cylinder_center) + +calculate_lift_force(system, v_ode, u_ode, semi, t) = nothing +function calculate_lift_force(system::TrixiParticles.FluidSystem, v_ode, u_ode, semi, t) + force = zero(SVector{ndims(system), eltype(system)}) + + values = interpolate_points(data_points, semi, system, v_ode, u_ode; cut_off_bnd=false, + clip_negative_pressure=false) + pressure = Array(values.pressure) + + for i in axes(data_points, 2) + point = TrixiParticles.current_coords(data_points, system, i) + + # F = ∑ -p_i * A_i * n_i + force -= pressure[i] * particle_spacing .* + TrixiParticles.normalize(point - center) + end + + return 2 * force[2] / (fluid_density * prescribed_velocity^2 * cylinder_diameter) +end + +calculate_drag_force(system, v_ode, u_ode, semi, t) = nothing +function calculate_drag_force(system::TrixiParticles.FluidSystem, v_ode, u_ode, semi, t) + force = zero(SVector{ndims(system), eltype(system)}) + + values = interpolate_points(data_points, semi, system, v_ode, u_ode; cut_off_bnd=false, + clip_negative_pressure=false) + pressure = Array(values.pressure) + + for i in axes(data_points, 2) + point = TrixiParticles.current_coords(data_points, system, i) + + # F = ∑ -p_i * A_i * n_i + force -= pressure[i] * particle_spacing .* + TrixiParticles.normalize(point - center) + end + + return 2 * force[1] / (fluid_density * prescribed_velocity^2 * cylinder_diameter) +end + +# ========================================================================================== +# ==== Simulation +min_corner = minimum(pipe.boundary.coordinates .- 5 * particle_spacing, dims=2) +max_corner = maximum(pipe.boundary.coordinates .+ 5 * particle_spacing, dims=2) +cell_list = FullGridCellList(; min_corner, max_corner) + +neighborhood_search = GridNeighborhoodSearch{2}(; cell_list, + update_strategy=ParallelUpdate()) + +semi = Semidiscretization(fluid_system, open_boundary_in, open_boundary_out, + boundary_system_wall, boundary_system_cylinder; + neighborhood_search=neighborhood_search, + parallelization_backend=PolyesterBackend()) + +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=100) + +output_directory = "out" + +saving_callback = SolutionSavingCallback(; dt=0.02, prefix="", output_directory) + +pp_callback = PostprocessCallback(; dt=0.02, + f_l=calculate_lift_force, f_d=calculate_drag_force, + output_directory, filename="resulting_force", + write_csv=true, write_file_interval=10) + +extra_callback = nothing + +callbacks = CallbackSet(info_callback, UpdateCallback(), saving_callback, + ParticleShiftingCallback(), # To obtain a near-uniform particle distribution in the wake + pp_callback, extra_callback) + +sol = solve(ode, RDPK3SpFSAL35(), + abstol=1e-6, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) + reltol=1e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) + dtmax=1e-2, # Limit stepsize to prevent crashing + save_everystep=false, callback=callbacks); diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 8dacc446e..757a9328a 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -9,8 +9,7 @@ For more information about the method see [description below](@ref method_of_cha # Keywords - `extrapolate_reference_values=false`: If `true`, the reference values are extrapolated - from the fluid domain to the boundary particles. This is useful for open boundaries where - the reference values are not known a priori. + from the fluid domain to the boundary particles. **Note:** This feature is experimental and has not been fully validated yet. As of now, we are not aware of any published literature supporting its use. """ diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 5c64fdea8..869a03bd0 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -150,7 +150,10 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, density[particle] = reference_value(reference_density, density[particle], particle_coords, t) else - inverse_state_equation!(density, state_equation, pressure, particle) + f_d = L_inv * extrapolated_density_correction[] + df_d = f_d[two_to_end] + + density[particle] = f_d[1] + dot(pos_diff, df_d) end end diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index bf07d5f25..af5a5aa4a 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -27,10 +27,10 @@ Open boundary system for in- and outflow particles. or a scalar for a constant density over all particles. !!! note "Note" - When using the [`BoundaryModelTafuni()`](@ref), the reference values (`reference_velocity`, - `reference_pressure`, `reference_density`) can also be set to `nothing` - since this model allows for either assigning physical quantities a priori or extrapolating them - from the fluid domaim to the buffer zones (inflow and outflow) using ghost nodes. + 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. @@ -83,9 +83,6 @@ function OpenBoundarySPHSystem(boundary_zone::BoundaryZone; reference_density=nothing) (; initial_condition) = boundary_zone - check_reference_values!(boundary_model, reference_density, reference_pressure, - reference_velocity) - buffer = SystemBuffer(nparticles(initial_condition), buffer_size) initial_condition = allocate_buffer(initial_condition, buffer) @@ -261,6 +258,8 @@ function update_open_boundary_eachstep!(system::OpenBoundarySPHSystem, v_ode, u_ 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, @@ -270,8 +269,6 @@ function update_open_boundary_eachstep!(system::OpenBoundarySPHSystem, v_ode, u_ u_ode, semi, t) - @trixi_timeit timer() "check domain" check_domain!(system, v, u, v_ode, u_ode, semi) - return system end @@ -443,25 +440,6 @@ end # instead of using the method of characteristics reference_value(value::Nothing, quantity, position, t) = quantity -function check_reference_values!(boundary_model, reference_density, reference_pressure, - reference_velocity) - return boundary_model -end - -function check_reference_values!(boundary_model::BoundaryModelLastiwka, - reference_density, reference_pressure, reference_velocity) - - # TODO - return boundary_model - boundary_model.extrapolate_reference_values && return boundary_model - - if any(isnothing.([reference_density, reference_pressure, reference_velocity])) - throw(ArgumentError("for `BoundaryModelLastiwka` all reference values must be specified")) - end - - return boundary_model -end - # 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, From 350c79172ca28e1dc799b0cf2a82407db06bf14a Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 3 Jul 2025 17:08:28 +0200 Subject: [PATCH 04/54] rm example --- examples/fluid/play_ground.jl | 233 ---------------------------------- 1 file changed, 233 deletions(-) delete mode 100644 examples/fluid/play_ground.jl diff --git a/examples/fluid/play_ground.jl b/examples/fluid/play_ground.jl deleted file mode 100644 index 5bc88fb7c..000000000 --- a/examples/fluid/play_ground.jl +++ /dev/null @@ -1,233 +0,0 @@ -# Flow past a circular cylinder (vortex street), Tafuni et al. (2018). -# Other literature using this validation: -# Vacandio et al. (2013), Marrone et al. (2013), Calhoun (2002), Liu et al. (1998) - -using TrixiParticles -using OrdinaryDiffEq - -# ========================================================================================== -# ==== Resolution -factor_d = 0.08 # Resolution in the paper is `0.01` (5M particles) - -const cylinder_diameter = 0.1 -particle_spacing = factor_d * cylinder_diameter - -# Make sure that the kernel support of fluid particles at a boundary is always fully sampled -boundary_layers = 4 - -# Make sure that the kernel support of fluid particles at an open boundary is always -# fully sampled. -# Note: Due to the dynamics at the inlets and outlets of open boundaries, -# it is recommended to use `open_boundary_layers > boundary_layers` -open_boundary_layers = 8 - -# ========================================================================================== -# ==== Experiment Setup -tspan = (0.0, 1.5) - -# Boundary geometry and initial fluid particle positions -domain_size = (25 * cylinder_diameter, 20 * cylinder_diameter) - -flow_direction = [1.0, 0.0] -reynolds_number = 200 -const prescribed_velocity = 1.0 -const fluid_density = 1000.0 -sound_speed = 10 * prescribed_velocity - -boundary_size = (domain_size[1] + 2 * particle_spacing * open_boundary_layers, - domain_size[2]) - -pipe = RectangularTank(particle_spacing, domain_size, boundary_size, fluid_density, - n_layers=boundary_layers, velocity=[prescribed_velocity, 0.0], - faces=(false, false, true, true)) - -# Shift pipe walls in negative x-direction for the inflow -pipe.boundary.coordinates[1, :] .-= particle_spacing * open_boundary_layers - -n_buffer_particles = 10 * pipe.n_particles_per_dimension[2]^(ndims(pipe.fluid) - 1) - -cylinder_center = (5 * cylinder_diameter, domain_size[2] / 2) -cylinder = SphereShape(particle_spacing, cylinder_diameter / 2, - cylinder_center, fluid_density, sphere_type=RoundSphere()) - -fluid = setdiff(pipe.fluid, cylinder) - -# ========================================================================================== -# ==== Fluid -wcsph = true - -h_factor = 1.5 -smoothing_length = h_factor * particle_spacing -smoothing_kernel = WendlandC2Kernel{2}() - -fluid_density_calculator = ContinuityDensity() - -kinematic_viscosity = prescribed_velocity * cylinder_diameter / reynolds_number - -state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, - exponent=7) -viscosity = ViscosityAdami(nu=kinematic_viscosity) - -if wcsph - density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) - - fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, - state_equation, smoothing_kernel, - density_diffusion=density_diffusion, - smoothing_length, viscosity=viscosity, - pressure_acceleration=tensile_instability_control, - buffer_size=n_buffer_particles) -else - state_equation = nothing - fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, - sound_speed, viscosity=viscosity, - density_calculator=fluid_density_calculator, - buffer_size=n_buffer_particles) -end - -# ========================================================================================== -# ==== Open Boundary -function velocity_function2d(pos, t) - return SVector(prescribed_velocity, 0.0) -end - -open_boundary_model = BoundaryModelTafuni() - -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) - -reference_velocity_in = velocity_function2d -# At the inlet, neither pressure nor density are prescribed; instead, -# these values are extrapolated from the fluid domain -reference_pressure_in = nothing -reference_density_in = nothing -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) - -boundary_type_out = OutFlow() - -plane_out = ([pipe.fluid_size[1], 0.0], [pipe.fluid_size[1], domain_size[2]]) -outflow = BoundaryZone(; plane=plane_out, plane_normal=(-flow_direction), - open_boundary_layers, density=fluid_density, particle_spacing, - boundary_type=boundary_type_out) - -# At the outlet, we allow the flow to exit freely without imposing any boundary conditions -reference_velocity_out = nothing -reference_pressure_out = nothing -reference_density_out = nothing -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) -# ========================================================================================== -# ==== Boundary -boundary_model = BoundaryModelDummyParticles(pipe.boundary.density, pipe.boundary.mass, - AdamiPressureExtrapolation(), - state_equation=state_equation, - smoothing_kernel, smoothing_length) - -boundary_system_wall = BoundarySPHSystem(pipe.boundary, boundary_model) - -boundary_model_cylinder = BoundaryModelDummyParticles(cylinder.density, cylinder.mass, - AdamiPressureExtrapolation(), - state_equation=state_equation, - viscosity=viscosity, - smoothing_kernel, smoothing_length) - -boundary_system_cylinder = BoundarySPHSystem(cylinder, boundary_model_cylinder) - -# ========================================================================================== -# ==== Postprocessing -circle = SphereShape(particle_spacing, (cylinder_diameter + particle_spacing) / 2, - cylinder_center, fluid_density, n_layers=1, - sphere_type=RoundSphere()) - -# Points for pressure interpolation, located at the wall interface -const data_points = copy(circle.coordinates) -const center = SVector(cylinder_center) - -calculate_lift_force(system, v_ode, u_ode, semi, t) = nothing -function calculate_lift_force(system::TrixiParticles.FluidSystem, v_ode, u_ode, semi, t) - force = zero(SVector{ndims(system), eltype(system)}) - - values = interpolate_points(data_points, semi, system, v_ode, u_ode; cut_off_bnd=false, - clip_negative_pressure=false) - pressure = Array(values.pressure) - - for i in axes(data_points, 2) - point = TrixiParticles.current_coords(data_points, system, i) - - # F = ∑ -p_i * A_i * n_i - force -= pressure[i] * particle_spacing .* - TrixiParticles.normalize(point - center) - end - - return 2 * force[2] / (fluid_density * prescribed_velocity^2 * cylinder_diameter) -end - -calculate_drag_force(system, v_ode, u_ode, semi, t) = nothing -function calculate_drag_force(system::TrixiParticles.FluidSystem, v_ode, u_ode, semi, t) - force = zero(SVector{ndims(system), eltype(system)}) - - values = interpolate_points(data_points, semi, system, v_ode, u_ode; cut_off_bnd=false, - clip_negative_pressure=false) - pressure = Array(values.pressure) - - for i in axes(data_points, 2) - point = TrixiParticles.current_coords(data_points, system, i) - - # F = ∑ -p_i * A_i * n_i - force -= pressure[i] * particle_spacing .* - TrixiParticles.normalize(point - center) - end - - return 2 * force[1] / (fluid_density * prescribed_velocity^2 * cylinder_diameter) -end - -# ========================================================================================== -# ==== Simulation -min_corner = minimum(pipe.boundary.coordinates .- 5 * particle_spacing, dims=2) -max_corner = maximum(pipe.boundary.coordinates .+ 5 * particle_spacing, dims=2) -cell_list = FullGridCellList(; min_corner, max_corner) - -neighborhood_search = GridNeighborhoodSearch{2}(; cell_list, - update_strategy=ParallelUpdate()) - -semi = Semidiscretization(fluid_system, open_boundary_in, open_boundary_out, - boundary_system_wall, boundary_system_cylinder; - neighborhood_search=neighborhood_search, - parallelization_backend=PolyesterBackend()) - -ode = semidiscretize(semi, tspan) - -info_callback = InfoCallback(interval=100) - -output_directory = "out" - -saving_callback = SolutionSavingCallback(; dt=0.02, prefix="", output_directory) - -pp_callback = PostprocessCallback(; dt=0.02, - f_l=calculate_lift_force, f_d=calculate_drag_force, - output_directory, filename="resulting_force", - write_csv=true, write_file_interval=10) - -extra_callback = nothing - -callbacks = CallbackSet(info_callback, UpdateCallback(), saving_callback, - ParticleShiftingCallback(), # To obtain a near-uniform particle distribution in the wake - pp_callback, extra_callback) - -sol = solve(ode, RDPK3SpFSAL35(), - abstol=1e-6, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) - reltol=1e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) - dtmax=1e-2, # Limit stepsize to prevent crashing - save_everystep=false, callback=callbacks); From a8b6555eac254c3af6e9001e3af5e6c0fde47aa2 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 9 Jul 2025 14:05:41 +0200 Subject: [PATCH 05/54] fix missing nhs update --- src/schemes/boundary/open_boundary/system.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index af5a5aa4a..cf747dd1f 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -309,6 +309,9 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) update_system_buffer!(system.buffer, semi) update_system_buffer!(fluid_system.buffer, semi) + # Since particles have been transferred, the neighborhood searches must be updated + update_nhs!(semi, u_ode) + return system end From ac9108c632637dbdebaba6879640964ad90b9af2 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 9 Jul 2025 22:38:01 +0200 Subject: [PATCH 06/54] implement different mirror methods --- src/TrixiParticles.jl | 1 + .../boundary/open_boundary/mirroring.jl | 282 +++++++++++++++--- 2 files changed, 237 insertions(+), 46 deletions(-) diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index 5e79798f3..bad506163 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -81,6 +81,7 @@ export tensile_instability_control export BoundaryModelMonaghanKajtar, BoundaryModelDummyParticles, AdamiPressureExtrapolation, PressureMirroring, PressureZeroing, BoundaryModelLastiwka, BoundaryModelTafuni, BernoulliPressureExtrapolation +export FirstOrderMirroring, ZerothOrderMirroring, SimpleMirroring export HertzContactModel, LinearContactModel export BoundaryMovement export examples_dir, validation_dir diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 869a03bd0..9de93a244 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -7,10 +7,33 @@ to the buffer zones (inflow and outflow) using ghost nodes. The position of the ghost nodes is obtained by mirroring the boundary particles into the fluid along a direction that is normal to the open boundary. """ -struct BoundaryModelTafuni end +struct BoundaryModelTafuni{MM} + mirror_method::MM +end + +struct FirstOrderMirroring{ELTYPE} + firstorder_tolerance::ELTYPE + function FirstOrderMirroring(; firstorder_tolerance::ELTYPE=1e-3) where {ELTYPE} + return new{typeof(firstorder_tolerance)}(firstorder_tolerance) + end +end + +struct SimpleMirroring{ELTYPE} + firstorder_tolerance::ELTYPE + function SimpleMirroring(; firstorder_tolerance::Real=1e-3) + return new{typeof(firstorder_tolerance)}(firstorder_tolerance) + end +end -function update_boundary_quantities!(system, ::BoundaryModelTafuni, v, u, v_ode, u_ode, - semi, t) +struct ZerothOrderMirroring end + +function BoundaryModelTafuni(; + mirror_method=FirstOrderMirroring(; firstorder_tolerance=1e-3)) + return BoundaryModelTafuni(mirror_method) +end + +function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni, + v, u, v_ode, u_ode, semi, t) @trixi_timeit timer() "extrapolate and correct values" begin fluid_system = corresponding_fluid_system(system, semi) @@ -19,14 +42,16 @@ function update_boundary_quantities!(system, ::BoundaryModelTafuni, v, u, v_ode, u_open_boundary = wrap_u(u_ode, system, semi) u_fluid = wrap_u(u_ode, fluid_system, semi) - extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, u_fluid, - semi, t; system.cache...) + extrapolate_values!(system, boundary_model.mirror_method, v_open_boundary, v_fluid, + u_open_boundary, u_fluid, semi, t; system.cache...) end end update_final!(system, ::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) = system -function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, u_fluid, +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, reference_density, @@ -34,8 +59,6 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, fluid_system = corresponding_fluid_system(system, semi) - state_equation = system_state_equation(fluid_system) - # Static indices to avoid allocations two_to_end = SVector{ndims(system)}(2:(ndims(system) + 1)) @@ -74,13 +97,7 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, m_b = hydrodynamic_mass(fluid_system, neighbor) rho_b = current_density(v_fluid, fluid_system, neighbor) pressure_b = current_pressure(v_fluid, fluid_system, neighbor) - v_b_ = current_velocity(v_fluid, fluid_system, neighbor) - - # Project `v_b_` on the normal direction of the boundary zone (only for inflow boundaries). - # 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_b = project_velocity_on_plane_normal(v_b_, boundary_zone) + v_b = current_velocity(v_fluid, fluid_system, neighbor) kernel_value = smoothing_kernel(fluid_system, distance, particle) grad_kernel = smoothing_kernel_grad(fluid_system, pos_diff, distance, @@ -105,22 +122,189 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, end end - # See also `correction_matrix_inversion_step!` for an explanation - if abs(det(correction_matrix[])) < 1.0f-9 - L_inv = typeof(correction_matrix[])(I) - else + pos_diff = particle_coords - ghost_node_position + + # Mirror back the ghost node values to the boundary particles + if abs(det(correction_matrix[])) >= mirror_method.firstorder_tolerance L_inv = inv(correction_matrix[]) + + # pressure + if prescribed_pressure + pressure[particle] = reference_value(reference_pressure, pressure[particle], + particle_coords, t) + else + f_p = L_inv * extrapolated_pressure_correction[] + df_p = f_p[two_to_end] # f_p[2:end] as SVector + + gradient_part = mirror_method isa SimpleMirroring ? 0 : dot(pos_diff, df_p) + + pressure[particle] = f_p[1] + gradient_part + end + + # density + if prescribed_density + density[particle] = reference_value(reference_density, density[particle], + particle_coords, t) + else + f_d = L_inv * extrapolated_density_correction[] + df_d = f_d[two_to_end] # f_d[2:end] as SVector + + gradient_part = mirror_method isa SimpleMirroring ? 0 : dot(pos_diff, df_d) + + density[particle] = f_d[1] + gradient_part + end + + # velocity + if prescribed_velocity + v_particle = current_velocity(v_open_boundary, system, particle) + v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) + @inbounds for dim in eachindex(v_ref) + v_open_boundary[dim, particle] = v_ref[dim] + end + else + @inbounds for dim in eachindex(pos_diff) + f_v = L_inv * extrapolated_velocity_correction[][dim, :] + df_v = f_v[two_to_end] # f_v[2:end] as SVector + + gradient_part = mirror_method isa SimpleMirroring ? 0 : + dot(pos_diff, df_v) + + v_open_boundary[dim, particle] = f_v[1] + gradient_part + end + + # Project the velocity on the normal direction of the boundary zone (only for inflow boundaries). + # 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) + end + + elseif correction_matrix[][1, 1] > eps() + # Determinant is small, fallback to zero-th order mirroring + shepard_coefficient = correction_matrix[][1, 1] + + # pressure + if prescribed_pressure + pressure[particle] = reference_value(reference_pressure, pressure[particle], + particle_coords, t) + else + pressure[particle] = first(extrapolated_pressure_correction[]) / + shepard_coefficient + end + + # density + if prescribed_density + density[particle] = reference_value(reference_density, density[particle], + particle_coords, t) + else + density[particle] = first(extrapolated_density_correction[]) / + shepard_coefficient + end + + # velocity + if prescribed_velocity + v_particle = current_velocity(v_open_boundary, system, particle) + v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) + @inbounds for dim in eachindex(v_ref) + v_open_boundary[dim, particle] = v_ref[dim] + end + else + velocity_interpolated = extrapolated_velocity_correction[][:, 1] / + shepard_coefficient + + @inbounds for dim in eachindex(velocity_interpolated) + v_open_boundary[dim, particle] = velocity_interpolated[dim] + end + + # Project the velocity on the normal direction of the boundary zone (only for inflow boundaries). + # 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) + end + end + 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 non-uniform buffer particles distribution, + # resulting in a potential numerical instability. Averaging mitigates these effects. + average_velocity!(v_open_boundary, u_open_boundary, system, boundary_zone, semi) + end + + return system +end + +function extrapolate_values!(system, ::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, reference_density, + reference_velocity, reference_pressure) = system + + fluid_system = corresponding_fluid_system(system, semi) + + # Use the fluid-fluid nhs, since the boundary particles are mirrored into the fluid domain + nhs = get_neighborhood_search(fluid_system, fluid_system, semi) + + fluid_coords = current_coordinates(u_fluid, fluid_system) + + # We cannot use `foreach_point_neighbor` here because we are looking for neighbors + # 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) + particle_coords = current_coords(u_open_boundary, system, particle) + ghost_node_position = mirror_position(particle_coords, boundary_zone) + + # Use `Ref` to ensure the variables are accessible and mutable within the closure below + # (see https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured). + shepard_coefficient = Ref(zero(eltype(system))) + interpolated_density = Ref(zero(eltype(system))) + interpolated_pressure = Ref(zero(eltype(system))) + interpolated_velocity = Ref(zero(particle_coords)) + + # TODO: Not public API + PointNeighbors.foreach_neighbor(fluid_coords, nhs, particle, ghost_node_position, + nhs.search_radius) do particle, neighbor, pos_diff, + distance + m_b = hydrodynamic_mass(fluid_system, neighbor) + rho_b = current_density(v_fluid, fluid_system, neighbor) + volume_b = m_b / rho_b + pressure_b = current_pressure(v_fluid, fluid_system, neighbor) + vel_b = current_velocity(v_fluid, fluid_system, neighbor) + + W_ab = smoothing_kernel(fluid_system, distance, particle) + + shepard_coefficient[] += volume_b * W_ab + + if !prescribed_pressure + interpolated_pressure[] += pressure_b * volume_b * W_ab + end + + if !prescribed_velocity + interpolated_velocity[] += vel_b * volume_b * W_ab + end + + if !prescribed_density + interpolated_density[] += rho_b * volume_b * W_ab + end + end + + if shepard_coefficient[] > sqrt(eps()) + interpolated_density[] /= shepard_coefficient[] + interpolated_pressure[] /= shepard_coefficient[] + interpolated_velocity[] /= shepard_coefficient[] + else + interpolated_density[] = current_density(v_open_boundary, system, particle) + interpolated_pressure[] = current_pressure(v_open_boundary, system, particle) + interpolated_velocity[] = current_velocity(v_open_boundary, system, particle) end pos_diff = particle_coords - ghost_node_position - # In Negi et al. (2020) https://doi.org/10.1016/j.cma.2020.113119, - # they have modified the equation for extrapolation to - # - # f_0 = f_k - (r_0 - r_k) ⋅ ∇f_k - # - # in order to get zero gradient at the outlet interface. - # Note: This modification is mentioned here for reference only and is NOT applied in this implementation. if prescribed_velocity v_particle = current_velocity(v_open_boundary, system, particle) v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) @@ -129,31 +313,29 @@ function extrapolate_values!(system, v_open_boundary, v_fluid, u_open_boundary, end else @inbounds for dim in eachindex(pos_diff) - f_v = L_inv * extrapolated_velocity_correction[][dim, :] - df_v = f_v[two_to_end] - - v_open_boundary[dim, particle] = f_v[1] + dot(pos_diff, df_v) + v_open_boundary[dim, particle] = interpolated_velocity[][dim] end - end - if prescribed_pressure - pressure[particle] = reference_value(reference_pressure, pressure[particle], - particle_coords, t) - else - f_d = L_inv * extrapolated_pressure_correction[] - df_d = f_d[two_to_end] - - pressure[particle] = f_d[1] + dot(pos_diff, df_d) + # Project the velocity on the normal direction of the boundary zone (only for inflow boundaries). + # 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) end if prescribed_density density[particle] = reference_value(reference_density, density[particle], particle_coords, t) else - f_d = L_inv * extrapolated_density_correction[] - df_d = f_d[two_to_end] + density[particle] = interpolated_density[] + end - density[particle] = f_d[1] + dot(pos_diff, df_d) + if prescribed_pressure + pressure[particle] = reference_value(reference_pressure, pressure[particle], + particle_coords, t) + else + pressure[particle] = interpolated_pressure[] end end @@ -252,12 +434,20 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se return v end -project_velocity_on_plane_normal(vel, boundary_zone) = vel +project_velocity_on_plane_normal!(v, system, particle, boundary_zone) = v -function project_velocity_on_plane_normal(vel, boundary_zone::BoundaryZone{InFlow}) - # Project `v_b` on the normal direction of the boundary zone +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 [...]" - return dot(vel, boundary_zone.plane_normal) * boundary_zone.plane_normal + vel = current_velocity(v, system, particle) + vel_ = dot(vel, boundary_zone.plane_normal) * boundary_zone.plane_normal + + @inbounds for dim in eachindex(vel) + v[dim, particle] = vel_[dim] + end + + return v end From d69f03d158fc0d2ac3bf2c20fbbfd173822c309a Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 10 Jul 2025 10:48:32 +0200 Subject: [PATCH 07/54] add docs --- docs/src/refs.bib | 26 +++++++++++++++++ .../method_of_characteristics.jl | 23 +++++++++------ .../boundary/open_boundary/mirroring.jl | 28 ++++++++++++++++++- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/docs/src/refs.bib b/docs/src/refs.bib index b857c7a12..2f2a636dd 100644 --- a/docs/src/refs.bib +++ b/docs/src/refs.bib @@ -406,6 +406,19 @@ @Article{Li1996 publisher = {Elsevier BV}, } +@Article{Liu2006, + author = {Liu, M.B. and Liu, G.R.}, + journal = {Applied Numerical Mathematics}, + title = {Restoring particle consistency in smoothed particle hydrodynamics}, + year = {2006}, + issn = {0168-9274}, + month = jan, + number = {1}, + pages = {19--36}, + volume = {56}, + doi = {10.1016/j.apnum.2005.02.012}, + publisher = {Elsevier BV}, +} @Article{Molteni2009, author = {Molteni, Diego and Colagrossi, Andrea}, @@ -561,6 +574,19 @@ @Article{Negi2020 publisher = {Elsevier BV}, } +@Article{Negi2022, + author = {Negi, Pawan and Ramachandran, Prabhu}, + journal = {Physics of Fluids}, + title = {How to train your solver: Verification of boundary conditions for smoothed particle hydrodynamics}, + year = {2022}, + issn = {1089-7666}, + month = nov, + number = {11}, + volume = {34}, + doi = {10.1063/5.0126234}, + publisher = {AIP Publishing}, +} + @Article{O’Connor2021, author = {O’Connor, Joseph and Rogers, Benedict D.}, journal = {Journal of Fluids and Structures}, diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 757a9328a..21ae82f42 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -1,5 +1,5 @@ @doc raw""" - BoundaryModelLastiwka(; extrapolate_reference_values::Bool=false) + BoundaryModelLastiwka(; extrapolate_reference_values=nothing) Boundary model for [`OpenBoundarySPHSystem`](@ref). This model uses the characteristic variables to propagate the appropriate values @@ -8,15 +8,19 @@ It requires a specific flow direction to be passed to the [`BoundaryZone`](@ref) For more information about the method see [description below](@ref method_of_characteristics). # Keywords -- `extrapolate_reference_values=false`: If `true`, the reference values are extrapolated - from the fluid domain to the boundary particles. +- `extrapolate_reference_values=nothing`: If one of the follwoing mirror methods is choosen, + the reference values are extrapolated from the fluid domain to the boundary particles. + - [`ZerothOrderMirroring`](@ref) + - [`FirstOrderMirroring`](@ref) + - [`SimpleMirroring`](@ref) + **Note:** This feature is experimental and has not been fully validated yet. As of now, we are not aware of any published literature supporting its use. """ -struct BoundaryModelLastiwka - extrapolate_reference_values::Bool - function BoundaryModelLastiwka(; extrapolate_reference_values::Bool=false) - return new{}(extrapolate_reference_values) +struct BoundaryModelLastiwka{T} + extrapolate_reference_values::T + function BoundaryModelLastiwka(; extrapolate_reference_values=nothing) + return new{typeof(extrapolate_reference_values)}(extrapolate_reference_values) end end @@ -31,13 +35,14 @@ end sound_speed = system_sound_speed(fluid_system) - if boundary_model.extrapolate_reference_values + 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, v, v_fluid, u, u_fluid, semi, t; + extrapolate_values!(system, boundary_model.extrapolate_reference_values, + v, v_fluid, u, u_fluid, semi, t; prescribed_pressure, prescribed_velocity, prescribed_density) end diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 9de93a244..f820d6987 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -1,16 +1,28 @@ @doc raw""" - BoundaryModelTafuni() + BoundaryModelTafuni(; mirror_method=FirstOrderMirroring(; firstorder_tolerance=1e-3)) Boundary model for the `OpenBoundarySPHSystem`. 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 into the fluid along a direction that is normal to the open boundary. +Fluid properties are then interpolated at these ghost node positions using surrounding fluid particles. +The values are then mirrored back to the boundary particles. +We provide three different mirroring methods: + - [`ZerothOrderMirroring`](@ref): Uses a Shepard interpolation to interpolate the values. + - [`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} mirror_method::MM end +""" + FirstOrderMirroring + +Fluid properties are interpolated onto ghost nodes using the method propsed by [Liu and Liu (2006)](@cite Liu2006), +to retrieve first oder kernel and particle consistency. +""" struct FirstOrderMirroring{ELTYPE} firstorder_tolerance::ELTYPE function FirstOrderMirroring(; firstorder_tolerance::ELTYPE=1e-3) where {ELTYPE} @@ -18,6 +30,12 @@ struct FirstOrderMirroring{ELTYPE} end end +""" + SimpleMirroring + +This method is similar to [`FirstOrderMirroring`](@ref), but does not use +the corrected gradient as proposed by [Negi et al. (2022)](@cite Negi2022). +""" struct SimpleMirroring{ELTYPE} firstorder_tolerance::ELTYPE function SimpleMirroring(; firstorder_tolerance::Real=1e-3) @@ -25,6 +43,14 @@ struct SimpleMirroring{ELTYPE} end end +""" + ZerothOrderMirroring + +Fluid properties are interpolated onto ghost nodes using Shepard interpolation. +The position of the ghost nodes is obtained by mirroring the boundary particles +into the fluid along a direction that is normal to the open boundary. +The interpolated values at the ghost nodes are then assigned to the corresponding boundary particles. +""" struct ZerothOrderMirroring end function BoundaryModelTafuni(; From 8863884afdd8afdccb8d2f2e90330d05cfd10b54 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 10 Jul 2025 10:56:17 +0200 Subject: [PATCH 08/54] fix tests --- examples/fluid/pipe_flow_2d.jl | 2 +- test/schemes/boundary/open_boundary/mirroring.jl | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index d21a68497..430161c8b 100644 --- a/examples/fluid/pipe_flow_2d.jl +++ b/examples/fluid/pipe_flow_2d.jl @@ -49,7 +49,7 @@ pipe.boundary.coordinates[1, :] .-= particle_spacing * open_boundary_layers NDIMS = ndims(pipe.fluid) -n_buffer_particles = 4 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) +n_buffer_particles = 5 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) # ========================================================================================== # ==== Fluid diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index cc4c970cb..72613c410 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -72,7 +72,8 @@ TrixiParticles.set_zero!(open_boundary.pressure) - TrixiParticles.extrapolate_values!(open_boundary, v_open_boundary, v_fluid, + TrixiParticles.extrapolate_values!(open_boundary, FirstOrderMirroring(), + v_open_boundary, v_fluid, inflow.initial_condition.coordinates, domain_fluid.coordinates, semi, 0.0; prescribed_pressure=false, @@ -168,7 +169,8 @@ TrixiParticles.set_zero!(open_boundary.pressure) - TrixiParticles.extrapolate_values!(open_boundary, v_open_boundary, v_fluid, + TrixiParticles.extrapolate_values!(open_boundary, FirstOrderMirroring(), + v_open_boundary, v_fluid, inflow.initial_condition.coordinates, domain_fluid.coordinates, semi, 0.0; prescribed_pressure=false, @@ -240,7 +242,8 @@ TrixiParticles.set_zero!(open_boundary_in.pressure) - TrixiParticles.extrapolate_values!(open_boundary_in, v_open_boundary, v_fluid, + TrixiParticles.extrapolate_values!(open_boundary_in, FirstOrderMirroring(), + v_open_boundary, v_fluid, inflow.initial_condition.coordinates, domain_fluid.coordinates, semi, 0.0) From 8beb4d994ec6ba970d03276ca2cece93558cd9d3 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 10 Jul 2025 11:05:49 +0200 Subject: [PATCH 09/54] fix typos --- .../boundary/open_boundary/method_of_characteristics.jl | 4 ++-- src/schemes/boundary/open_boundary/mirroring.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 21ae82f42..a2c9e347e 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -8,8 +8,8 @@ It requires a specific flow direction to be passed to the [`BoundaryZone`](@ref) For more information about the method see [description below](@ref method_of_characteristics). # Keywords -- `extrapolate_reference_values=nothing`: If one of the follwoing mirror methods is choosen, - the reference values are extrapolated from the fluid domain to the boundary particles. +- `extrapolate_reference_values=nothing`: If one of the following mirroring methods is selected, + the reference values are extrapolated from the fluid domain to the boundary particles: - [`ZerothOrderMirroring`](@ref) - [`FirstOrderMirroring`](@ref) - [`SimpleMirroring`](@ref) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index f820d6987..f3f8adf40 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -20,8 +20,8 @@ end """ FirstOrderMirroring -Fluid properties are interpolated onto ghost nodes using the method propsed by [Liu and Liu (2006)](@cite Liu2006), -to retrieve first oder kernel and particle consistency. +Fluid properties are interpolated onto ghost nodes using the method proposed by [Liu and Liu (2006)](@cite Liu2006), +to retrieve first order kernel and particle consistency. """ struct FirstOrderMirroring{ELTYPE} firstorder_tolerance::ELTYPE From f06031842a763b41d6babddafa86ef145f4a24b4 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 10 Jul 2025 11:16:36 +0200 Subject: [PATCH 10/54] add docs --- src/schemes/boundary/open_boundary/mirroring.jl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index f3f8adf40..95dbfa1e1 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -18,10 +18,14 @@ struct BoundaryModelTafuni{MM} end """ - FirstOrderMirroring + FirstOrderMirroring(; firstorder_tolerance::ELTYPE=1e-3) Fluid properties are interpolated onto ghost nodes using the method proposed by [Liu and Liu (2006)](@cite Liu2006), to retrieve first order kernel and particle consistency. + +# Keywords +- `firstorder_tolerance`: If the determinant of the correction matrix is smaller than this value, + the method falls back to [`ZerothOrderMirroring`](@ref). Default is `1e-3`. """ struct FirstOrderMirroring{ELTYPE} firstorder_tolerance::ELTYPE @@ -31,10 +35,14 @@ struct FirstOrderMirroring{ELTYPE} end """ - SimpleMirroring + SimpleMirroring(; firstorder_tolerance::ELTYPE=1e-3)) This method is similar to [`FirstOrderMirroring`](@ref), but does not use the corrected gradient as proposed by [Negi et al. (2022)](@cite Negi2022). + +# Keywords +- `firstorder_tolerance`: If the determinant of the correction matrix is smaller than this value, + the method falls back to [`ZerothOrderMirroring`](@ref). Default is `1e-3`. """ struct SimpleMirroring{ELTYPE} firstorder_tolerance::ELTYPE From bf610b8cc54055a928233648f7cf53f17bc5d8a8 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 10 Jul 2025 12:10:51 +0200 Subject: [PATCH 11/54] add tests --- .../boundary/open_boundary/mirroring.jl | 274 ++++++++++++++++++ 1 file changed, 274 insertions(+) diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 72613c410..7c93df561 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -253,4 +253,278 @@ @test all(isapprox(-v_x_fluid_first), v_open_boundary[1, :]) end + + @testset verbose=true "Mirroring Methods" begin + function mirror(pressure_function, mirror_method; + particle_spacing=0.05, domain_size=(2.0, 1.0)) + domain_fluid = RectangularShape(particle_spacing, + round.(Int, domain_size ./ particle_spacing), + (0.0, 0.0), density=1000.0, + pressure=pressure_function) + + smoothing_length = 1.2 * particle_spacing + smoothing_kernel = WendlandC2Kernel{2}() + fluid_system = EntropicallyDampedSPHSystem(domain_fluid, smoothing_kernel, + smoothing_length, 1.0) + + fluid_system.cache.density .= domain_fluid.density + + plane_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], + open_boundary_layers=10, density=1000.0, + particle_spacing) + open_boundary_out = OpenBoundarySPHSystem(outflow; fluid_system, + boundary_model=BoundaryModelTafuni(), + buffer_size=0) + + semi = Semidiscretization(fluid_system, open_boundary_out) + + TrixiParticles.initialize_neighborhood_searches!(semi) + + v_open_boundary = zero(outflow.initial_condition.velocity) + v_fluid = vcat(domain_fluid.velocity, domain_fluid.pressure') + + TrixiParticles.set_zero!(open_boundary_out.pressure) + + 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) + + plane_in = ([0.0, 0.0], [0.0, domain_size[2]]) + + inflow = BoundaryZone(; plane=plane_in, boundary_type=InFlow(), + plane_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) + + semi = Semidiscretization(fluid_system, open_boundary_in) + TrixiParticles.initialize_neighborhood_searches!(semi) + + v_open_boundary = zero(inflow.initial_condition.velocity) + + TrixiParticles.set_zero!(open_boundary_in.pressure) + + 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) + + return fluid_system, open_boundary_in, open_boundary_out, v_fluid + end + + function interpolate_pressure(mirror_method, pressure_func; particle_spacing=0.05) + fluid_system, open_boundary_in, open_boundary_out, + 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)] + + fluid_system.initial_condition.pressure .= p_fluid + open_boundary_in.initial_condition.pressure .= open_boundary_in.pressure + open_boundary_out.initial_condition.pressure .= open_boundary_out.pressure + + entire_domain = union(fluid_system.initial_condition, + open_boundary_in.initial_condition, + open_boundary_out.initial_condition) + + smoothing_length = 1.2 * particle_spacing + smoothing_kernel = WendlandC2Kernel{2}() + + # Use a fluid system to interpolate the pressure + interpolation_system = WeaklyCompressibleSPHSystem(entire_domain, + ContinuityDensity(), + nothing, smoothing_kernel, + smoothing_length) + interpolation_system.pressure .= entire_domain.pressure + + semi = Semidiscretization(interpolation_system) + ode = semidiscretize(semi, (0, 0)) + v_ode, u_ode = ode.u0.x + + result = interpolate_line([-0.5, 0.5], [2.5, 0.5], 50, semi, + interpolation_system, v_ode, u_ode) + + return result.pressure + end + + pressure_func(pos) = cos(2pi * pos[1]) + + pressures = interpolate_pressure.([ + SimpleMirroring(), + FirstOrderMirroring(), + ZerothOrderMirroring() + ], pressure_func) + + pressures_expected = [ + [ + -0.961368753262176, + -0.889650754605612, + -0.6905728633912162, + -0.3906982503900935, + -0.03185103264546474, + 0.333177250635236, + 0.6476757904268203, + 0.8708718636029472, + 0.9770644543539139, + 0.930127404224564, + 0.7453239657417114, + 0.45327391778794734, + 0.0957991316946515, + -0.278044550109137, + -0.6122210347366616, + -0.8549953877169134, + -0.972402370920352, + -0.9485947076098263, + -0.7852083273404225, + -0.5067072947529646, + -0.15639554738985995, + 0.216475912074678, + 0.5602009648581898, + 0.8223333154520063, + 0.9626909343468545, + 0.9626909343468544, + 0.8223333154520058, + 0.5602009648581916, + 0.2164759120746806, + -0.15639554738985995, + -0.5067072947529652, + -0.7852083273404221, + -0.9485947076098261, + -0.9724023709203522, + -0.8549953877169136, + -0.6122210347366618, + -0.2780445501091364, + 0.0957991316946491, + 0.45327391778794657, + 0.7453239657417114, + 0.9301274042245636, + 0.9770644543539139, + 0.8708718636029484, + 0.647675790426819, + 0.33317725063523634, + -0.03185103264546614, + -0.3906982503900926, + -0.6905728633912179, + -0.8896507546056125, + -0.9613687532621761 + ], + [ + 0.06915008702843263, + 1.0737102752866752, + 2.4308935813709276, + 3.074251266025277, + 3.047730973768249, + 2.5546812207882246, + 1.8827468502566729, + 1.330793458342126, + 1.0685482295301676, + 0.9337092788362713, + 0.7453239657417114, + 0.45327391778794734, + 0.0957991316946515, + -0.278044550109137, + -0.6122210347366616, + -0.8549953877169134, + -0.972402370920352, + -0.9485947076098263, + -0.7852083273404225, + -0.5067072947529646, + -0.15639554738985995, + 0.216475912074678, + 0.5602009648581898, + 0.8223333154520063, + 0.9626909343468545, + 0.9626909343468544, + 0.8223333154520058, + 0.5602009648581916, + 0.2164759120746806, + -0.15639554738985995, + -0.5067072947529652, + -0.7852083273404221, + -0.9485947076098261, + -0.9724023709203522, + -0.8549953877169136, + -0.6122210347366618, + -0.2780445501091364, + 0.0957991316946491, + 0.45327391778794657, + 0.7453239657417114, + 0.9337092788362712, + 1.0685482295301671, + 1.3307934583421233, + 1.8827468502566747, + 2.55468122078822, + 3.047730973768248, + 3.0742512660252768, + 2.4308935813709227, + 1.0737102752866743, + 0.06915008702844015 + ], + [ + -0.961368753262176, + -0.8896507546056119, + -0.690572863391216, + -0.3906982503900935, + -0.03185103264546471, + 0.333177250635236, + 0.6476223100256845, + 0.8652532179882182, + 0.9638320291788572, + 0.929273656897224, + 0.7453239657417114, + 0.45327391778794734, + 0.0957991316946515, + -0.278044550109137, + -0.6122210347366616, + -0.8549953877169134, + -0.972402370920352, + -0.9485947076098263, + -0.7852083273404225, + -0.5067072947529646, + -0.15639554738985995, + 0.216475912074678, + 0.5602009648581898, + 0.8223333154520063, + 0.9626909343468545, + 0.9626909343468544, + 0.8223333154520058, + 0.5602009648581916, + 0.2164759120746806, + -0.15639554738985995, + -0.5067072947529652, + -0.7852083273404221, + -0.9485947076098261, + -0.9724023709203522, + -0.8549953877169136, + -0.6122210347366618, + -0.2780445501091364, + 0.0957991316946491, + 0.45327391778794657, + 0.7453239657417114, + 0.9292736568972241, + 0.9638320291788572, + 0.865253217988219, + 0.6476223100256829, + 0.33317725063523645, + -0.03185103264546612, + -0.3906982503900927, + -0.6905728633912182, + -0.8896507546056123, + -0.961368753262176 + ] + ] + + @testset verbose=true "$method" for (i, method) in enumerate(("simple mirroring", + "first order mirroring", + "zeroth order mirroring")) + @test isapprox(pressures[i], pressures_expected[i]) + end + end end From 8ca3825e6369cfb6be1aec3b5c868f890a4b8202 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 10 Jul 2025 12:14:18 +0200 Subject: [PATCH 12/54] apply formatter --- test/schemes/boundary/open_boundary/mirroring.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 7c93df561..76e3829d4 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -522,8 +522,8 @@ ] @testset verbose=true "$method" for (i, method) in enumerate(("simple mirroring", - "first order mirroring", - "zeroth order mirroring")) + "first order mirroring", + "zeroth order mirroring")) @test isapprox(pressures[i], pressures_expected[i]) end end From 15c737c4423889aa88e98e83862452a5b896ffc8 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 15 Jul 2025 08:43:21 +0200 Subject: [PATCH 13/54] add `average_velocity!` for `BoundaryModelLastiwka` --- .../method_of_characteristics.jl | 29 +++++++++++++++++++ .../boundary/open_boundary/mirroring.jl | 8 +++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index a2c9e347e..37384e16f 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -75,6 +75,13 @@ 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 non-uniform buffer particles distribution, + # resulting in a potential numerical instability. Averaging mitigates these effects. + average_velocity!(v, u, system, boundary_model, boundary_zone, semi) + end + return system end @@ -222,3 +229,25 @@ end return characteristics end + +function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, semi) + return v +end + +function average_velocity!(v, u, system, ::BoundaryModelLastiwka, ::BoundaryZone{InFlow}, + semi) + avg_velocity = sum(each_moving_particle(system)) do particle + return current_velocity(v, system, particle) + end + + avg_velocity /= system.buffer.active_particle_count[] + + @threaded semi for particle in each_moving_particle(system) + # Set the velocity of the ghost node to the average velocity of the fluid domain + @inbounds for dim in eachindex(avg_velocity) + v[dim, particle] = avg_velocity[dim] + end + end + + return v +end diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 95dbfa1e1..ac37a339c 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -265,7 +265,8 @@ function extrapolate_values!(system, # 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 non-uniform buffer particles distribution, # resulting in a potential numerical instability. Averaging mitigates these effects. - average_velocity!(v_open_boundary, u_open_boundary, system, boundary_zone, semi) + average_velocity!(v_open_boundary, u_open_boundary, system, boundary_model, + boundary_zone, semi) end return system @@ -438,9 +439,10 @@ function mirror_position(particle_coords, boundary_zone) return particle_coords - 2 * dist * boundary_zone.plane_normal end -average_velocity!(v, u, system, boundary_zone, semi) = v +average_velocity!(v, u, system, ::BoundaryModelTafuni, boundary_zone, semi) = v -function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, semi) +function average_velocity!(v, u, system, ::BoundaryModelTafuni, + boundary_zone::BoundaryZone{InFlow}, semi) (; plane_normal, zone_origin, initial_condition) = boundary_zone # We only use the extrapolated velocity in the vicinity of the transition region. From f285ccd29f0670225fbc31191606ff81437ea73e Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 15 Jul 2025 09:00:34 +0200 Subject: [PATCH 14/54] fix --- src/schemes/boundary/open_boundary/mirroring.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index ac37a339c..95dbfa1e1 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -265,8 +265,7 @@ function extrapolate_values!(system, # 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 non-uniform buffer particles distribution, # resulting in a potential numerical instability. Averaging mitigates these effects. - average_velocity!(v_open_boundary, u_open_boundary, system, boundary_model, - boundary_zone, semi) + average_velocity!(v_open_boundary, u_open_boundary, system, boundary_zone, semi) end return system @@ -439,10 +438,9 @@ function mirror_position(particle_coords, boundary_zone) return particle_coords - 2 * dist * boundary_zone.plane_normal end -average_velocity!(v, u, system, ::BoundaryModelTafuni, boundary_zone, semi) = v +average_velocity!(v, u, system, boundary_zone, semi) = v -function average_velocity!(v, u, system, ::BoundaryModelTafuni, - boundary_zone::BoundaryZone{InFlow}, semi) +function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, semi) (; plane_normal, zone_origin, initial_condition) = boundary_zone # We only use the extrapolated velocity in the vicinity of the transition region. From 5eb6442235d876233ed802cab949085e9d0b67b9 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 23 Jul 2025 12:19:19 +0200 Subject: [PATCH 15/54] add interpolation functions --- .../boundary/open_boundary/mirroring.jl | 331 ++++++++++-------- 1 file changed, 187 insertions(+), 144 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 95dbfa1e1..6a35c748e 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -1,22 +1,3 @@ -@doc raw""" - BoundaryModelTafuni(; mirror_method=FirstOrderMirroring(; firstorder_tolerance=1e-3)) - -Boundary model for the `OpenBoundarySPHSystem`. -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 -into the fluid along a direction that is normal to the open boundary. -Fluid properties are then interpolated at these ghost node positions using surrounding fluid particles. -The values are then mirrored back to the boundary particles. -We provide three different mirroring methods: - - [`ZerothOrderMirroring`](@ref): Uses a Shepard interpolation to interpolate the values. - - [`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} - mirror_method::MM -end - """ FirstOrderMirroring(; firstorder_tolerance::ELTYPE=1e-3) @@ -61,6 +42,25 @@ The interpolated values at the ghost nodes are then assigned to the correspondin """ struct ZerothOrderMirroring end +@doc raw""" + BoundaryModelTafuni(; mirror_method=FirstOrderMirroring(; firstorder_tolerance=1e-3)) + +Boundary model for the `OpenBoundarySPHSystem`. +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 +into the fluid along a direction that is normal to the open boundary. +Fluid properties are then interpolated at these ghost node positions using surrounding fluid particles. +The values are then mirrored back to the boundary particles. +We provide three different mirroring methods: + - [`ZerothOrderMirroring`](@ref): Uses a Shepard interpolation to interpolate the values. + - [`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} + mirror_method::MM +end + function BoundaryModelTafuni(; mirror_method=FirstOrderMirroring(; firstorder_tolerance=1e-3)) return BoundaryModelTafuni(mirror_method) @@ -68,16 +68,49 @@ end function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) + (; reference_pressure, reference_density, reference_velocity, cache) = system + (; prescribed_pressure, prescribed_density, prescribed_velocity) = cache + @trixi_timeit timer() "extrapolate and correct values" begin fluid_system = corresponding_fluid_system(system, semi) - v_open_boundary = wrap_v(v_ode, system, semi) v_fluid = wrap_v(v_ode, fluid_system, semi) - u_open_boundary = wrap_u(u_ode, system, semi) u_fluid = wrap_u(u_ode, fluid_system, semi) - extrapolate_values!(system, boundary_model.mirror_method, v_open_boundary, v_fluid, - u_open_boundary, u_fluid, semi, t; system.cache...) + extrapolate_values!(system, boundary_model.mirror_method, v, v_fluid, + u, u_fluid, semi, t; prescribed_pressure, + prescribed_density, prescribed_velocity) + end + + if prescribed_pressure + @threaded semi for particle in each_moving_particle(system) + particle_coords = current_coords(u_open_boundary, system, particle) + + pressure[particle] = reference_value(reference_pressure, pressure[particle], + particle_coords, t) + end + end + + if prescribed_density + @threaded semi for particle in each_moving_particle(system) + particle_coords = current_coords(u_open_boundary, system, particle) + + density[particle] = reference_value(reference_density, density[particle], + particle_coords, t) + end + end + + if prescribed_velocity + @threaded semi for particle in each_moving_particle(system) + particle_coords = current_coords(u_open_boundary, system, particle) + v_particle = current_velocity(v_open_boundary, system, particle) + + v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) + + @inbounds for dim in eachindex(v_ref) + v[dim, particle] = v_ref[dim] + end + end end end @@ -88,8 +121,7 @@ function extrapolate_values!(system, 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, reference_density, - reference_velocity, reference_pressure) = system + (; pressure, density, boundary_zone) = system fluid_system = corresponding_fluid_system(system, semi) @@ -114,13 +146,13 @@ function extrapolate_values!(system, correction_matrix = Ref(zero(SMatrix{ndims(system) + 1, ndims(system) + 1, eltype(system)})) - extrapolated_density_correction = Ref(zero(SVector{ndims(system) + 1, + interpolated_density_correction = Ref(zero(SVector{ndims(system) + 1, eltype(system)})) - extrapolated_pressure_correction = Ref(zero(SVector{ndims(system) + 1, + interpolated_pressure_correction = Ref(zero(SVector{ndims(system) + 1, eltype(system)})) - extrapolated_velocity_correction = Ref(zero(SMatrix{ndims(system), + interpolated_velocity_correction = Ref(zero(SMatrix{ndims(system), ndims(system) + 1, eltype(system)})) @@ -143,16 +175,16 @@ function extrapolate_values!(system, correction_matrix[] += L - if !prescribed_pressure - extrapolated_pressure_correction[] += pressure_b * R + if !(prescribed_pressure) + interpolated_pressure_correction[] += pressure_b * R end - if !prescribed_velocity - extrapolated_velocity_correction[] += v_b * R' + if !(prescribed_density) + interpolated_density_correction[] += rho_b * R end - if !prescribed_density - extrapolated_density_correction[] += rho_b * R + if !(prescribed_velocity) + interpolated_velocity_correction[] += v_b * R' end end @@ -163,48 +195,24 @@ function extrapolate_values!(system, L_inv = inv(correction_matrix[]) # pressure - if prescribed_pressure - pressure[particle] = reference_value(reference_pressure, pressure[particle], - particle_coords, t) - else - f_p = L_inv * extrapolated_pressure_correction[] - df_p = f_p[two_to_end] # f_p[2:end] as SVector - - gradient_part = mirror_method isa SimpleMirroring ? 0 : dot(pos_diff, df_p) - - pressure[particle] = f_p[1] + gradient_part + if !(prescribed_pressure) + first_order_scalar_interpolation!(pressure, particle, L_inv, + interpolated_pressure_correction, + two_to_end, pos_diff, mirror_method) end # density - if prescribed_density - density[particle] = reference_value(reference_density, density[particle], - particle_coords, t) - else - f_d = L_inv * extrapolated_density_correction[] - df_d = f_d[two_to_end] # f_d[2:end] as SVector - - gradient_part = mirror_method isa SimpleMirroring ? 0 : dot(pos_diff, df_d) - - density[particle] = f_d[1] + gradient_part + if !(prescribed_density) + first_order_scalar_interpolation!(density, particle, L_inv, + interpolated_density_correction, + two_to_end, pos_diff, mirror_method) end # velocity - if prescribed_velocity - v_particle = current_velocity(v_open_boundary, system, particle) - v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) - @inbounds for dim in eachindex(v_ref) - v_open_boundary[dim, particle] = v_ref[dim] - end - else - @inbounds for dim in eachindex(pos_diff) - f_v = L_inv * extrapolated_velocity_correction[][dim, :] - df_v = f_v[two_to_end] # f_v[2:end] as SVector - - gradient_part = mirror_method isa SimpleMirroring ? 0 : - dot(pos_diff, df_v) - - v_open_boundary[dim, particle] = f_v[1] + gradient_part - end + if !(prescribed_velocity) + first_order_velocity_interpolation!(v_open_boundary, system, particle, + L_inv, interpolated_velocity_correction, + two_to_end, pos_diff, mirror_method) # Project the velocity on the normal direction of the boundary zone (only for inflow boundaries). # See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.: @@ -219,37 +227,25 @@ function extrapolate_values!(system, shepard_coefficient = correction_matrix[][1, 1] # pressure - if prescribed_pressure - pressure[particle] = reference_value(reference_pressure, pressure[particle], - particle_coords, t) - else - pressure[particle] = first(extrapolated_pressure_correction[]) / - shepard_coefficient + if !(prescribed_pressure) + interpolated_pressure = first(interpolated_pressure_correction[]) + zeroth_order_scalar_interpolation!(pressure, particle, shepard_coefficient, + interpolated_pressure) end # density - if prescribed_density - density[particle] = reference_value(reference_density, density[particle], - particle_coords, t) - else - density[particle] = first(extrapolated_density_correction[]) / - shepard_coefficient + if !(prescribed_density) + interpolated_density = first(interpolated_density_correction[]) + zeroth_order_scalar_interpolation!(density, particle, shepard_coefficient, + interpolated_density) end # velocity - if prescribed_velocity - v_particle = current_velocity(v_open_boundary, system, particle) - v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) - @inbounds for dim in eachindex(v_ref) - v_open_boundary[dim, particle] = v_ref[dim] - end - else - velocity_interpolated = extrapolated_velocity_correction[][:, 1] / - shepard_coefficient - - @inbounds for dim in eachindex(velocity_interpolated) - v_open_boundary[dim, particle] = velocity_interpolated[dim] - end + if !(prescribed_velocity) + interpolated_velocity = interpolated_velocity_correction[][:, 1] + zeroth_order_velocity_interpolation!(v_open_boundary, system, particle, + shepard_coefficient, + interpolated_velocity) # Project the velocity on the normal direction of the boundary zone (only for inflow boundaries). # See https://doi.org/10.1016/j.jcp.2020.110029 Section 3.3.: @@ -271,12 +267,11 @@ function extrapolate_values!(system, return system end -function extrapolate_values!(system, ::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, reference_density, - reference_velocity, reference_pressure) = system +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) @@ -314,62 +309,46 @@ function extrapolate_values!(system, ::ZerothOrderMirroring, shepard_coefficient[] += volume_b * W_ab - if !prescribed_pressure + if !(prescribed_pressure) interpolated_pressure[] += pressure_b * volume_b * W_ab end - if !prescribed_velocity - interpolated_velocity[] += vel_b * volume_b * W_ab - end - - if !prescribed_density + if !(prescribed_density) interpolated_density[] += rho_b * volume_b * W_ab end - end - if shepard_coefficient[] > sqrt(eps()) - interpolated_density[] /= shepard_coefficient[] - interpolated_pressure[] /= shepard_coefficient[] - interpolated_velocity[] /= shepard_coefficient[] - else - interpolated_density[] = current_density(v_open_boundary, system, particle) - interpolated_pressure[] = current_pressure(v_open_boundary, system, particle) - interpolated_velocity[] = current_velocity(v_open_boundary, system, particle) + if !(prescribed_velocity) + interpolated_velocity[] += vel_b * volume_b * W_ab + end end - pos_diff = particle_coords - ghost_node_position + if shepard_coefficient[] > eps() + pos_diff = particle_coords - ghost_node_position - if prescribed_velocity - v_particle = current_velocity(v_open_boundary, system, particle) - v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) - @inbounds for dim in eachindex(v_ref) - v_open_boundary[dim, particle] = v_ref[dim] - end - else - @inbounds for dim in eachindex(pos_diff) - v_open_boundary[dim, particle] = interpolated_velocity[][dim] + if !(prescribed_pressure) + zeroth_order_scalar_interpolation!(pressure, particle, + shepard_coefficient[], + interpolated_pressure[]) end - # Project the velocity on the normal direction of the boundary zone (only for inflow boundaries). - # 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) - end + if !(prescribed_density) + zeroth_order_scalar_interpolation!(density, particle, + shepard_coefficient[], + interpolated_density[]) + end - if prescribed_density - density[particle] = reference_value(reference_density, density[particle], - particle_coords, t) - else - density[particle] = interpolated_density[] - end + if !(prescribed_velocity) + zeroth_order_velocity_interpolation!(v_open_boundary, system, particle, + shepard_coefficient[], + interpolated_velocity[]) - if prescribed_pressure - pressure[particle] = reference_value(reference_pressure, pressure[particle], - particle_coords, t) - else - pressure[particle] = interpolated_pressure[] + # Project the velocity on the normal direction of the boundary zone (only for inflow boundaries). + # 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) + end end end @@ -383,6 +362,70 @@ function extrapolate_values!(system, ::ZerothOrderMirroring, return system end +function zeroth_order_scalar_interpolation!(values, particle, shepard_coefficient, + extrapolated_value) + values[particle] = extrapolated_value / shepard_coefficient + + return values +end + +function zeroth_order_velocity_interpolation!(v, system, particle, shepard_coefficient, + extrapolated_velocity) + velocity_interpolated = extrapolated_velocity / shepard_coefficient + + @inbounds for dim in eachindex(velocity_interpolated) + v[dim, particle] = velocity_interpolated[dim] + end + + return v +end + +function first_order_scalar_interpolation!(values, particle, L_inv, + extrapolated_values_correction, + two_to_end, pos_diff, ::FirstOrderMirroring) + f_s = L_inv * extrapolated_values_correction[] + df_p = f_s[two_to_end] # f_s[2:end] as SVector + + values[particle] = f_s[1] + dot(pos_diff, df_p) + + return values +end + +function first_order_scalar_interpolation!(values, particle, L_inv, + extrapolated_values_correction, + two_to_end, pos_diff, ::SimpleMirroring) + f_s = L_inv * extrapolated_values_correction[] + + values[particle] = f_s[1] + + return values +end + +function first_order_velocity_interpolation!(v, system, particle, L_inv, + interpolated_velocity_correction, + two_to_end, pos_diff, ::FirstOrderMirroring) + @inbounds for dim in eachindex(pos_diff) + f_v = L_inv * interpolated_velocity_correction[][dim, :] + df_v = f_v[two_to_end] # f_v[2:end] as SVector + + v[dim, particle] = f_v[1] + dot(pos_diff, df_v) + end + + return v +end + +function first_order_velocity_interpolation!(v, system, particle, L_inv, + interpolated_velocity_correction, + two_to_end, pos_diff, ::SimpleMirroring) + @inbounds for dim in eachindex(pos_diff) + f_v = L_inv * interpolated_velocity_correction[][dim, :] + + v[dim, particle] = f_v[1] + end + + return v +end + function correction_arrays(W_ab, grad_W_ab, pos_diff::SVector{3}, rho_b, m_b) # `pos_diff` corresponds to `x_{kl} = x_k - x_l` in the paper (Tafuni et al., 2018), # where `x_k` is the position of the ghost node and `x_l` is the position of the neighbor particle From 888ef53df8b3b7eeff0673287c0b0ade43202068 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 23 Jul 2025 12:51:15 +0200 Subject: [PATCH 16/54] restructure again --- .../method_of_characteristics.jl | 4 +- .../boundary/open_boundary/mirroring.jl | 38 +++++++++---------- .../boundary/open_boundary/mirroring.jl | 4 ++ 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 37384e16f..04f34799b 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -3,7 +3,7 @@ Boundary model for [`OpenBoundarySPHSystem`](@ref). This model uses the characteristic variables to propagate the appropriate values -to the outlet or inlet and have been proposed by Lastiwka et al. (2009). +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). For more information about the method see [description below](@ref method_of_characteristics). @@ -77,7 +77,7 @@ 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 non-uniform buffer particles distribution, + # 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 diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 6a35c748e..3252159e8 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -68,7 +68,8 @@ end function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) - (; reference_pressure, reference_density, reference_velocity, cache) = system + (; reference_pressure, reference_density, reference_velocity, boundary_zone, + cache) = system (; prescribed_pressure, prescribed_density, prescribed_velocity) = cache @trixi_timeit timer() "extrapolate and correct values" begin @@ -82,9 +83,16 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni 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) + end + if prescribed_pressure @threaded semi for particle in each_moving_particle(system) - particle_coords = current_coords(u_open_boundary, system, particle) + particle_coords = current_coords(u, system, particle) pressure[particle] = reference_value(reference_pressure, pressure[particle], particle_coords, t) @@ -93,7 +101,7 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni if prescribed_density @threaded semi for particle in each_moving_particle(system) - particle_coords = current_coords(u_open_boundary, system, particle) + particle_coords = current_coords(u, system, particle) density[particle] = reference_value(reference_density, density[particle], particle_coords, t) @@ -102,8 +110,8 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni if prescribed_velocity @threaded semi for particle in each_moving_particle(system) - particle_coords = current_coords(u_open_boundary, system, particle) - v_particle = current_velocity(v_open_boundary, system, particle) + particle_coords = current_coords(u, system, particle) + v_particle = current_velocity(v, system, particle) v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) @@ -228,6 +236,8 @@ function extrapolate_values!(system, # pressure if !(prescribed_pressure) + # Only the first entry is used, as the subsequent entries represent gradient + # components that are not required for zeroth-order interpolation interpolated_pressure = first(interpolated_pressure_correction[]) zeroth_order_scalar_interpolation!(pressure, particle, shepard_coefficient, interpolated_pressure) @@ -235,6 +245,8 @@ function extrapolate_values!(system, # density if !(prescribed_density) + # Only the first entry is used, as the subsequent entries represent gradient + # components that are not required for zeroth-order interpolation interpolated_density = first(interpolated_density_correction[]) zeroth_order_scalar_interpolation!(density, particle, shepard_coefficient, interpolated_density) @@ -242,6 +254,8 @@ function extrapolate_values!(system, # velocity if !(prescribed_velocity) + # Only the first column is used, as the subsequent entries represent gradient + # components that are not required for zeroth-order interpolation interpolated_velocity = interpolated_velocity_correction[][:, 1] zeroth_order_velocity_interpolation!(v_open_boundary, system, particle, shepard_coefficient, @@ -257,13 +271,6 @@ function extrapolate_values!(system, end 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 non-uniform buffer particles distribution, - # resulting in a potential numerical instability. Averaging mitigates these effects. - average_velocity!(v_open_boundary, u_open_boundary, system, boundary_zone, semi) - end - return system end @@ -352,13 +359,6 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, end 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 non-uniform buffer particles distribution, - # resulting in a potential numerical instability. Averaging mitigates these effects. - average_velocity!(v_open_boundary, u_open_boundary, system, boundary_zone, semi) - end - return system end diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 76e3829d4..4efe2fe2e 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -238,6 +238,7 @@ TrixiParticles.initialize_neighborhood_searches!(semi) v_open_boundary = zero(inflow.initial_condition.velocity) + u_open_boundary = inflow.initial_condition.coordinates v_fluid = vcat(domain_fluid.velocity, domain_fluid.pressure') TrixiParticles.set_zero!(open_boundary_in.pressure) @@ -247,6 +248,9 @@ inflow.initial_condition.coordinates, domain_fluid.coordinates, semi, 0.0) + TrixiParticles.average_velocity!(v_open_boundary, u_open_boundary, open_boundary_in, + inflow, semi) + # Since the velocity profile increases linearly in positive x-direction, # we can use the first velocity entry as a representative value. v_x_fluid_first = v_fluid[1, 1] From 4fd1b32458dfeb4f10fdfb7811721b78759c31b4 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 23 Jul 2025 16:02:15 +0200 Subject: [PATCH 17/54] fix gpu bugs --- .../method_of_characteristics.jl | 4 +-- .../boundary/open_boundary/mirroring.jl | 33 ++++++++++--------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 04f34799b..20aeb305b 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -237,11 +237,9 @@ end function average_velocity!(v, u, system, ::BoundaryModelLastiwka, ::BoundaryZone{InFlow}, semi) avg_velocity = sum(each_moving_particle(system)) do particle - return current_velocity(v, system, particle) + return current_velocity(v, system, particle) / system.buffer.active_particle_count[] end - avg_velocity /= system.buffer.active_particle_count[] - @threaded semi for particle in each_moving_particle(system) # Set the velocity of the ghost node to the average velocity of the fluid domain @inbounds for dim in eachindex(avg_velocity) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 3252159e8..06094f9fd 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -10,7 +10,7 @@ to retrieve first order kernel and particle consistency. """ struct FirstOrderMirroring{ELTYPE} firstorder_tolerance::ELTYPE - function FirstOrderMirroring(; firstorder_tolerance::ELTYPE=1e-3) where {ELTYPE} + function FirstOrderMirroring(; firstorder_tolerance::Real=1 / 1000.0f0) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end end @@ -27,7 +27,7 @@ the corrected gradient as proposed by [Negi et al. (2022)](@cite Negi2022). """ struct SimpleMirroring{ELTYPE} firstorder_tolerance::ELTYPE - function SimpleMirroring(; firstorder_tolerance::Real=1e-3) + function SimpleMirroring(; firstorder_tolerance::Real=1 / 1000.0f0) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end end @@ -62,14 +62,16 @@ struct BoundaryModelTafuni{MM} end function BoundaryModelTafuni(; - mirror_method=FirstOrderMirroring(; firstorder_tolerance=1e-3)) + mirror_method=FirstOrderMirroring(; + firstorder_tolerance=1 / + 1000.0f0)) return BoundaryModelTafuni(mirror_method) end function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) (; reference_pressure, reference_density, reference_velocity, boundary_zone, - cache) = system + pressure, density, cache) = system (; prescribed_pressure, prescribed_density, prescribed_velocity) = cache @trixi_timeit timer() "extrapolate and correct values" begin @@ -205,21 +207,22 @@ function extrapolate_values!(system, # pressure if !(prescribed_pressure) first_order_scalar_interpolation!(pressure, particle, L_inv, - interpolated_pressure_correction, + interpolated_pressure_correction[], two_to_end, pos_diff, mirror_method) end # density if !(prescribed_density) first_order_scalar_interpolation!(density, particle, L_inv, - interpolated_density_correction, + interpolated_density_correction[], two_to_end, pos_diff, mirror_method) end # velocity if !(prescribed_velocity) first_order_velocity_interpolation!(v_open_boundary, system, particle, - L_inv, interpolated_velocity_correction, + L_inv, + interpolated_velocity_correction[], two_to_end, pos_diff, mirror_method) # Project the velocity on the normal direction of the boundary zone (only for inflow boundaries). @@ -381,9 +384,9 @@ function zeroth_order_velocity_interpolation!(v, system, particle, shepard_coeff end function first_order_scalar_interpolation!(values, particle, L_inv, - extrapolated_values_correction, + interpolated_values_correction, two_to_end, pos_diff, ::FirstOrderMirroring) - f_s = L_inv * extrapolated_values_correction[] + f_s = L_inv * interpolated_values_correction df_p = f_s[two_to_end] # f_s[2:end] as SVector values[particle] = f_s[1] + dot(pos_diff, df_p) @@ -392,9 +395,9 @@ function first_order_scalar_interpolation!(values, particle, L_inv, end function first_order_scalar_interpolation!(values, particle, L_inv, - extrapolated_values_correction, + interpolated_values_correction, two_to_end, pos_diff, ::SimpleMirroring) - f_s = L_inv * extrapolated_values_correction[] + f_s = L_inv * interpolated_values_correction values[particle] = f_s[1] @@ -405,7 +408,7 @@ function first_order_velocity_interpolation!(v, system, particle, L_inv, interpolated_velocity_correction, two_to_end, pos_diff, ::FirstOrderMirroring) @inbounds for dim in eachindex(pos_diff) - f_v = L_inv * interpolated_velocity_correction[][dim, :] + f_v = L_inv * interpolated_velocity_correction[dim, :] df_v = f_v[two_to_end] # f_v[2:end] as SVector v[dim, particle] = f_v[1] + dot(pos_diff, df_v) @@ -418,7 +421,7 @@ function first_order_velocity_interpolation!(v, system, particle, L_inv, interpolated_velocity_correction, two_to_end, pos_diff, ::SimpleMirroring) @inbounds for dim in eachindex(pos_diff) - f_v = L_inv * interpolated_velocity_correction[][dim, :] + f_v = L_inv * interpolated_velocity_correction[dim, :] v[dim, particle] = f_v[1] end @@ -496,11 +499,9 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se active_coordinates(u, system))) avg_velocity = sum(candidates) do particle - return current_velocity(v, system, particle) + return current_velocity(v, system, particle) / length(candidates) end - avg_velocity /= length(candidates) - @threaded semi for particle in each_moving_particle(system) # Set the velocity of the ghost node to the average velocity of the fluid domain @inbounds for dim in eachindex(avg_velocity) From fb9f33353293c5ec7435e721255619051b907604 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 23 Jul 2025 16:14:23 +0200 Subject: [PATCH 18/54] remove unnecessary argument --- .../boundary/open_boundary/mirroring.jl | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 06094f9fd..070d6e3eb 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -10,7 +10,7 @@ to retrieve first order kernel and particle consistency. """ struct FirstOrderMirroring{ELTYPE} firstorder_tolerance::ELTYPE - function FirstOrderMirroring(; firstorder_tolerance::Real=1 / 1000.0f0) + function FirstOrderMirroring(; firstorder_tolerance::Real=1/1000) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end end @@ -27,7 +27,7 @@ the corrected gradient as proposed by [Negi et al. (2022)](@cite Negi2022). """ struct SimpleMirroring{ELTYPE} firstorder_tolerance::ELTYPE - function SimpleMirroring(; firstorder_tolerance::Real=1 / 1000.0f0) + function SimpleMirroring(; firstorder_tolerance::Real=1/1000) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end end @@ -63,8 +63,7 @@ end function BoundaryModelTafuni(; mirror_method=FirstOrderMirroring(; - firstorder_tolerance=1 / - 1000.0f0)) + firstorder_tolerance=1/1000)) return BoundaryModelTafuni(mirror_method) end @@ -220,8 +219,7 @@ function extrapolate_values!(system, # velocity if !(prescribed_velocity) - first_order_velocity_interpolation!(v_open_boundary, system, particle, - L_inv, + first_order_velocity_interpolation!(v_open_boundary, particle, L_inv, interpolated_velocity_correction[], two_to_end, pos_diff, mirror_method) @@ -260,7 +258,7 @@ function extrapolate_values!(system, # Only the first column is used, as the subsequent entries represent gradient # components that are not required for zeroth-order interpolation interpolated_velocity = interpolated_velocity_correction[][:, 1] - zeroth_order_velocity_interpolation!(v_open_boundary, system, particle, + zeroth_order_velocity_interpolation!(v_open_boundary, particle, shepard_coefficient, interpolated_velocity) @@ -348,7 +346,7 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, end if !(prescribed_velocity) - zeroth_order_velocity_interpolation!(v_open_boundary, system, particle, + zeroth_order_velocity_interpolation!(v_open_boundary, particle, shepard_coefficient[], interpolated_velocity[]) @@ -372,7 +370,7 @@ function zeroth_order_scalar_interpolation!(values, particle, shepard_coefficien return values end -function zeroth_order_velocity_interpolation!(v, system, particle, shepard_coefficient, +function zeroth_order_velocity_interpolation!(v, particle, shepard_coefficient, extrapolated_velocity) velocity_interpolated = extrapolated_velocity / shepard_coefficient @@ -404,7 +402,7 @@ function first_order_scalar_interpolation!(values, particle, L_inv, return values end -function first_order_velocity_interpolation!(v, system, particle, L_inv, +function first_order_velocity_interpolation!(v, particle, L_inv, interpolated_velocity_correction, two_to_end, pos_diff, ::FirstOrderMirroring) @inbounds for dim in eachindex(pos_diff) @@ -417,7 +415,7 @@ function first_order_velocity_interpolation!(v, system, particle, L_inv, return v end -function first_order_velocity_interpolation!(v, system, particle, L_inv, +function first_order_velocity_interpolation!(v, particle, L_inv, interpolated_velocity_correction, two_to_end, pos_diff, ::SimpleMirroring) @inbounds for dim in eachindex(pos_diff) From c5fa956eb83d44d11da938ba35f77239524f7f31 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 23 Jul 2025 16:58:43 +0200 Subject: [PATCH 19/54] fix gpu again --- src/schemes/boundary/open_boundary/mirroring.jl | 6 +++--- test/examples/gpu.jl | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 070d6e3eb..34bc47dda 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -10,7 +10,7 @@ to retrieve first order kernel and particle consistency. """ struct FirstOrderMirroring{ELTYPE} firstorder_tolerance::ELTYPE - function FirstOrderMirroring(; firstorder_tolerance::Real=1/1000) + function FirstOrderMirroring(; firstorder_tolerance::Real=1e-3) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end end @@ -27,7 +27,7 @@ the corrected gradient as proposed by [Negi et al. (2022)](@cite Negi2022). """ struct SimpleMirroring{ELTYPE} firstorder_tolerance::ELTYPE - function SimpleMirroring(; firstorder_tolerance::Real=1/1000) + function SimpleMirroring(; firstorder_tolerance::Real=1e-3) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end end @@ -63,7 +63,7 @@ end function BoundaryModelTafuni(; mirror_method=FirstOrderMirroring(; - firstorder_tolerance=1/1000)) + firstorder_tolerance=1e-3)) return BoundaryModelTafuni(mirror_method) end diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index a9a2e791d..df0cef63e 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -319,7 +319,9 @@ end joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), - open_boundary_model=BoundaryModelTafuni(), + open_boundary_model=BoundaryModelTafuni(; + mirror_method=FirstOrderMirroring(; + firstorder_tolerance=0.001f0)), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, @@ -340,7 +342,9 @@ end "pipe_flow_2d.jl"), wcsph=true, sound_speed=20.0f0, pressure=0.0f0, - open_boundary_model=BoundaryModelTafuni(), + open_boundary_model=BoundaryModelTafuni(; + mirror_method=FirstOrderMirroring(; + firstorder_tolerance=0.001f0)), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, From 0925255de2c6494e8c05274ab79422062af7296f Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 24 Jul 2025 08:39:57 +0200 Subject: [PATCH 20/54] fix gpu --- test/examples/gpu.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index df0cef63e..f6490f4a0 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -343,8 +343,7 @@ end wcsph=true, sound_speed=20.0f0, pressure=0.0f0, open_boundary_model=BoundaryModelTafuni(; - mirror_method=FirstOrderMirroring(; - firstorder_tolerance=0.001f0)), + mirror_method=ZerothOrderMirroring()), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, From 3a001f308b2498facb69a45cf6e4a43863c86502 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 24 Jul 2025 16:59:52 +0200 Subject: [PATCH 21/54] implement suggestions --- docs/src/refs.bib | 9 +- .../method_of_characteristics.jl | 6 +- .../boundary/open_boundary/mirroring.jl | 143 +++++++++--------- test/examples/gpu.jl | 4 +- 4 files changed, 85 insertions(+), 77 deletions(-) diff --git a/docs/src/refs.bib b/docs/src/refs.bib index 2f2a636dd..b7711bc31 100644 --- a/docs/src/refs.bib +++ b/docs/src/refs.bib @@ -761,7 +761,14 @@ @book{Poling2001 publisher = {McGraw-Hill}, address = {New York} } - +@article{Shepard1968, + author = {Shepard, Donald}, + booktitle = {Proceedings of the 1968 23rd ACM national conference on -}, + title = {A two-dimensional interpolation function for irregularly-spaced data}, + year = {1968}, + publisher = {ACM Press}, + doi = {10.1145/800186.810616}, +} @article{Smagorinsky1963, author = {Smagorinsky, Joseph}, title = {General Circulation Experiments with the Primitive Equations. I. The Basic Experiment}, diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 20aeb305b..061db7cd4 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -19,6 +19,7 @@ For more information about the method see [description below](@ref method_of_cha """ struct BoundaryModelLastiwka{T} extrapolate_reference_values::T + function BoundaryModelLastiwka(; extrapolate_reference_values=nothing) return new{typeof(extrapolate_reference_values)}(extrapolate_reference_values) end @@ -231,6 +232,7 @@ end end function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, semi) + # Only apply averaging at the inflow return v end @@ -242,8 +244,8 @@ function average_velocity!(v, u, system, ::BoundaryModelLastiwka, ::BoundaryZone @threaded semi for particle in each_moving_particle(system) # Set the velocity of the ghost node to the average velocity of the fluid domain - @inbounds for dim in eachindex(avg_velocity) - v[dim, particle] = avg_velocity[dim] + for dim in eachindex(avg_velocity) + @inbounds v[dim, particle] = avg_velocity[dim] end end diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 34bc47dda..9ffa11ba9 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -1,41 +1,42 @@ """ - FirstOrderMirroring(; firstorder_tolerance::ELTYPE=1e-3) + FirstOrderMirroring(; firstorder_tolerance::=1f-3) -Fluid properties are interpolated onto ghost nodes using the method proposed by [Liu and Liu (2006)](@cite Liu2006), -to retrieve first order kernel and particle consistency. +Fluid properties are extrapolated onto ghost nodes using the method proposed by [Liu and Liu (2006)](@cite Liu2006), +to extend the gradient into the boundary zone. # Keywords -- `firstorder_tolerance`: If the determinant of the correction matrix is smaller than this value, - the method falls back to [`ZerothOrderMirroring`](@ref). Default is `1e-3`. +- `firstorder_tolerance=1f-3`: If the determinant of the correction matrix is smaller than this value, + the method falls back to [`ZerothOrderMirroring`](@ref). """ struct FirstOrderMirroring{ELTYPE} firstorder_tolerance::ELTYPE - function FirstOrderMirroring(; firstorder_tolerance::Real=1e-3) + function FirstOrderMirroring(; firstorder_tolerance::Real=1.0f-3) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end end """ - SimpleMirroring(; firstorder_tolerance::ELTYPE=1e-3)) + SimpleMirroring(; firstorder_tolerance=1f-3)) This method is similar to [`FirstOrderMirroring`](@ref), but does not use the corrected gradient as proposed by [Negi et al. (2022)](@cite Negi2022). # Keywords -- `firstorder_tolerance`: If the determinant of the correction matrix is smaller than this value, - the method falls back to [`ZerothOrderMirroring`](@ref). Default is `1e-3`. +- `firstorder_tolerance=1f-3`: If the determinant of the correction matrix is smaller than this value, + the method falls back to [`ZerothOrderMirroring`](@ref). """ struct SimpleMirroring{ELTYPE} firstorder_tolerance::ELTYPE - function SimpleMirroring(; firstorder_tolerance::Real=1e-3) + + function SimpleMirroring(; firstorder_tolerance::Real=1.0f-3) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end end """ - ZerothOrderMirroring + ZerothOrderMirroring() -Fluid properties are interpolated onto ghost nodes using Shepard interpolation. +Fluid properties are interpolated onto ghost nodes using Shepard interpolation [Shepard1968](@cite). The position of the ghost nodes is obtained by mirroring the boundary particles into the fluid along a direction that is normal to the open boundary. The interpolated values at the ghost nodes are then assigned to the corresponding boundary particles. @@ -43,7 +44,7 @@ The interpolated values at the ghost nodes are then assigned to the correspondin struct ZerothOrderMirroring end @doc raw""" - BoundaryModelTafuni(; mirror_method=FirstOrderMirroring(; firstorder_tolerance=1e-3)) + BoundaryModelTafuni(; mirror_method=FirstOrderMirroring()) Boundary model for the `OpenBoundarySPHSystem`. This model implements the method of [Tafuni et al. (2018)](@cite Tafuni2018) to extrapolate the properties from the fluid domain @@ -61,9 +62,7 @@ struct BoundaryModelTafuni{MM} mirror_method::MM end -function BoundaryModelTafuni(; - mirror_method=FirstOrderMirroring(; - firstorder_tolerance=1e-3)) +function BoundaryModelTafuni(; mirror_method=FirstOrderMirroring()) return BoundaryModelTafuni(mirror_method) end @@ -84,7 +83,7 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni prescribed_density, prescribed_velocity) end - if !(prescribed_velocity) && boundary_zone.average_inflow_velocity + 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. @@ -116,8 +115,8 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) - @inbounds for dim in eachindex(v_ref) - v[dim, particle] = v_ref[dim] + for dim in eachindex(v_ref) + @inbounds v[dim, particle] = v_ref[dim] end end end @@ -184,15 +183,15 @@ function extrapolate_values!(system, correction_matrix[] += L - if !(prescribed_pressure) + if !prescribed_pressure interpolated_pressure_correction[] += pressure_b * R end - if !(prescribed_density) + if !prescribed_density interpolated_density_correction[] += rho_b * R end - if !(prescribed_velocity) + if !prescribed_velocity interpolated_velocity_correction[] += v_b * R' end end @@ -204,22 +203,22 @@ function extrapolate_values!(system, L_inv = inv(correction_matrix[]) # pressure - if !(prescribed_pressure) - first_order_scalar_interpolation!(pressure, particle, L_inv, + if !prescribed_pressure + first_order_scalar_extrapolation!(pressure, particle, L_inv, interpolated_pressure_correction[], two_to_end, pos_diff, mirror_method) end # density - if !(prescribed_density) - first_order_scalar_interpolation!(density, particle, L_inv, + if !prescribed_density + first_order_scalar_extrapolation!(density, particle, L_inv, interpolated_density_correction[], two_to_end, pos_diff, mirror_method) end # velocity - if !(prescribed_velocity) - first_order_velocity_interpolation!(v_open_boundary, particle, L_inv, + if !prescribed_velocity + first_order_velocity_extrapolation!(v_open_boundary, particle, L_inv, interpolated_velocity_correction[], two_to_end, pos_diff, mirror_method) @@ -231,32 +230,34 @@ function extrapolate_values!(system, boundary_zone) end + # No else: `correction_matrix[][1, 1] <= eps()` means no fluid neighbors + # and thus no reliable interpolation, so boundary values remain at their current state elseif correction_matrix[][1, 1] > eps() # Determinant is small, fallback to zero-th order mirroring shepard_coefficient = correction_matrix[][1, 1] - # pressure - if !(prescribed_pressure) + # Pressure + if !prescribed_pressure # Only the first entry is used, as the subsequent entries represent gradient - # components that are not required for zeroth-order interpolation + # components that are not required for zeroth-order interpolation. interpolated_pressure = first(interpolated_pressure_correction[]) - zeroth_order_scalar_interpolation!(pressure, particle, shepard_coefficient, + zeroth_order_scalar_extrapolation!(pressure, particle, shepard_coefficient, interpolated_pressure) end - # density - if !(prescribed_density) + # Density + if !prescribed_density # Only the first entry is used, as the subsequent entries represent gradient - # components that are not required for zeroth-order interpolation + # components that are not required for zeroth-order interpolation. interpolated_density = first(interpolated_density_correction[]) - zeroth_order_scalar_interpolation!(density, particle, shepard_coefficient, + zeroth_order_scalar_extrapolation!(density, particle, shepard_coefficient, interpolated_density) end - # velocity - if !(prescribed_velocity) + # Velocity + if !prescribed_velocity # Only the first column is used, as the subsequent entries represent gradient - # components that are not required for zeroth-order interpolation + # components that are not required for zeroth-order interpolation. interpolated_velocity = interpolated_velocity_correction[][:, 1] zeroth_order_velocity_interpolation!(v_open_boundary, particle, shepard_coefficient, @@ -311,41 +312,41 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, rho_b = current_density(v_fluid, fluid_system, neighbor) volume_b = m_b / rho_b pressure_b = current_pressure(v_fluid, fluid_system, neighbor) - vel_b = current_velocity(v_fluid, fluid_system, neighbor) + v_b = current_velocity(v_fluid, fluid_system, neighbor) W_ab = smoothing_kernel(fluid_system, distance, particle) shepard_coefficient[] += volume_b * W_ab - if !(prescribed_pressure) + if !prescribed_pressure interpolated_pressure[] += pressure_b * volume_b * W_ab end - if !(prescribed_density) + if !prescribed_density interpolated_density[] += rho_b * volume_b * W_ab end - if !(prescribed_velocity) - interpolated_velocity[] += vel_b * volume_b * W_ab + if !prescribed_velocity + interpolated_velocity[] += v_b * volume_b * W_ab end end if shepard_coefficient[] > eps() pos_diff = particle_coords - ghost_node_position - if !(prescribed_pressure) - zeroth_order_scalar_interpolation!(pressure, particle, + if !prescribed_pressure + zeroth_order_scalar_extrapolation!(pressure, particle, shepard_coefficient[], interpolated_pressure[]) end - if !(prescribed_density) - zeroth_order_scalar_interpolation!(density, particle, + if !prescribed_density + zeroth_order_scalar_extrapolation!(density, particle, shepard_coefficient[], interpolated_density[]) end - if !(prescribed_velocity) + if !prescribed_velocity zeroth_order_velocity_interpolation!(v_open_boundary, particle, shepard_coefficient[], interpolated_velocity[]) @@ -363,7 +364,7 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, return system end -function zeroth_order_scalar_interpolation!(values, particle, shepard_coefficient, +function zeroth_order_scalar_extrapolation!(values, particle, shepard_coefficient, extrapolated_value) values[particle] = extrapolated_value / shepard_coefficient @@ -374,25 +375,25 @@ function zeroth_order_velocity_interpolation!(v, particle, shepard_coefficient, extrapolated_velocity) velocity_interpolated = extrapolated_velocity / shepard_coefficient - @inbounds for dim in eachindex(velocity_interpolated) - v[dim, particle] = velocity_interpolated[dim] + for dim in eachindex(velocity_interpolated) + @inbounds v[dim, particle] = velocity_interpolated[dim] end return v end -function first_order_scalar_interpolation!(values, particle, L_inv, +function first_order_scalar_extrapolation!(values, particle, L_inv, interpolated_values_correction, two_to_end, pos_diff, ::FirstOrderMirroring) f_s = L_inv * interpolated_values_correction - df_p = f_s[two_to_end] # f_s[2:end] as SVector + df_s = f_s[two_to_end] # `f_s[2:end]` as SVector - values[particle] = f_s[1] + dot(pos_diff, df_p) + values[particle] = f_s[1] + dot(pos_diff, df_s) return values end -function first_order_scalar_interpolation!(values, particle, L_inv, +function first_order_scalar_extrapolation!(values, particle, L_inv, interpolated_values_correction, two_to_end, pos_diff, ::SimpleMirroring) f_s = L_inv * interpolated_values_correction @@ -402,26 +403,26 @@ function first_order_scalar_interpolation!(values, particle, L_inv, return values end -function first_order_velocity_interpolation!(v, particle, L_inv, +function first_order_velocity_extrapolation!(v, particle, L_inv, interpolated_velocity_correction, two_to_end, pos_diff, ::FirstOrderMirroring) - @inbounds for dim in eachindex(pos_diff) - f_v = L_inv * interpolated_velocity_correction[dim, :] - df_v = f_v[two_to_end] # f_v[2:end] as SVector + for dim in eachindex(pos_diff) + @inbounds f_v = L_inv * interpolated_velocity_correction[dim, :] + df_v = f_v[two_to_end] # `f_v[2:end]` as SVector - v[dim, particle] = f_v[1] + dot(pos_diff, df_v) + @inbounds v[dim, particle] = f_v[1] + dot(pos_diff, df_v) end return v end -function first_order_velocity_interpolation!(v, particle, L_inv, +function first_order_velocity_extrapolation!(v, particle, L_inv, interpolated_velocity_correction, two_to_end, pos_diff, ::SimpleMirroring) - @inbounds for dim in eachindex(pos_diff) - f_v = L_inv * interpolated_velocity_correction[dim, :] + for dim in eachindex(pos_diff) + @inbounds f_v = L_inv * interpolated_velocity_correction[dim, :] - v[dim, particle] = f_v[1] + @inbounds v[dim, particle] = f_v[1] end return v @@ -502,8 +503,8 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se @threaded semi for particle in each_moving_particle(system) # Set the velocity of the ghost node to the average velocity of the fluid domain - @inbounds for dim in eachindex(avg_velocity) - v[dim, particle] = avg_velocity[dim] + for dim in eachindex(avg_velocity) + @inbounds v[dim, particle] = avg_velocity[dim] end end @@ -518,11 +519,11 @@ function project_velocity_on_plane_normal!(v, system, particle, # 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 [...]" - vel = current_velocity(v, system, particle) - vel_ = dot(vel, boundary_zone.plane_normal) * boundary_zone.plane_normal + v_particle = current_velocity(v, system, particle) + v_particle_projected = dot(vel, boundary_zone.plane_normal) * boundary_zone.plane_normal - @inbounds for dim in eachindex(vel) - v[dim, particle] = vel_[dim] + for dim in eachindex(vel) + @inbounds v[dim, particle] = v_particle_projected[dim] end return v diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index f6490f4a0..0acdcb852 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -319,9 +319,7 @@ end joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), - open_boundary_model=BoundaryModelTafuni(; - mirror_method=FirstOrderMirroring(; - firstorder_tolerance=0.001f0)), + open_boundary_model=BoundaryModelTafuni(), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, From d611748d8764a39813830c1c59d24246ec84ed22 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 24 Jul 2025 17:29:13 +0200 Subject: [PATCH 22/54] fix --- src/schemes/boundary/open_boundary/mirroring.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 9ffa11ba9..829670af9 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -520,9 +520,10 @@ function project_velocity_on_plane_normal!(v, system, particle, # "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(vel, boundary_zone.plane_normal) * boundary_zone.plane_normal + v_particle_projected = dot(v_particle, boundary_zone.plane_normal) * + boundary_zone.plane_normal - for dim in eachindex(vel) + for dim in eachindex(v_particle) @inbounds v[dim, particle] = v_particle_projected[dim] end From 70105bbd88a34958673052069b994e452d90a092 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 24 Jul 2025 17:35:32 +0200 Subject: [PATCH 23/54] add comments --- .../boundary/open_boundary/method_of_characteristics.jl | 4 ++++ src/schemes/boundary/open_boundary/mirroring.jl | 1 + 2 files changed, 5 insertions(+) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 061db7cd4..addc29210 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -16,6 +16,8 @@ For more information about the method see [description below](@ref method_of_cha **Note:** This feature is experimental and has not been fully validated yet. As of now, we are not aware of any published literature supporting its use. + 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} extrapolate_reference_values::T @@ -238,6 +240,8 @@ end function average_velocity!(v, u, system, ::BoundaryModelLastiwka, ::BoundaryZone{InFlow}, semi) + + # 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[] end diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 829670af9..6412c9150 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -497,6 +497,7 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se reinterpret(reshape, SVector{ndims(system), eltype(u)}, active_coordinates(u, system))) + # Division inside the `sum` closure to maintain GPU compatibility avg_velocity = sum(candidates) do particle return current_velocity(v, system, particle) / length(candidates) end From 142563750e005fdbe64d93d4882015381c2d7169 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 24 Jul 2025 18:07:52 +0200 Subject: [PATCH 24/54] implement suggestions --- src/schemes/boundary/open_boundary/mirroring.jl | 11 ++++++----- test/schemes/boundary/open_boundary/mirroring.jl | 11 +++++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 6412c9150..c5f6082bb 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -1,5 +1,5 @@ """ - FirstOrderMirroring(; firstorder_tolerance::=1f-3) + FirstOrderMirroring(; firstorder_tolerance=1f-3) Fluid properties are extrapolated onto ghost nodes using the method proposed by [Liu and Liu (2006)](@cite Liu2006), to extend the gradient into the boundary zone. @@ -10,6 +10,7 @@ to extend the gradient into the boundary zone. """ struct FirstOrderMirroring{ELTYPE} firstorder_tolerance::ELTYPE + function FirstOrderMirroring(; firstorder_tolerance::Real=1.0f-3) return new{typeof(firstorder_tolerance)}(firstorder_tolerance) end @@ -202,21 +203,21 @@ function extrapolate_values!(system, if abs(det(correction_matrix[])) >= mirror_method.firstorder_tolerance L_inv = inv(correction_matrix[]) - # pressure + # Pressure if !prescribed_pressure first_order_scalar_extrapolation!(pressure, particle, L_inv, interpolated_pressure_correction[], two_to_end, pos_diff, mirror_method) end - # density + # Density if !prescribed_density first_order_scalar_extrapolation!(density, particle, L_inv, interpolated_density_correction[], two_to_end, pos_diff, mirror_method) end - # velocity + # Velocity if !prescribed_velocity first_order_velocity_extrapolation!(v_open_boundary, particle, L_inv, interpolated_velocity_correction[], @@ -231,7 +232,7 @@ function extrapolate_values!(system, end # No else: `correction_matrix[][1, 1] <= eps()` means no fluid neighbors - # and thus no reliable interpolation, so boundary values remain at their current state + # and thus no reliable interpolation, so boundary values remain at their current state. elseif correction_matrix[][1, 1] > eps() # Determinant is small, fallback to zero-th order mirroring shepard_coefficient = correction_matrix[][1, 1] diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 4efe2fe2e..8f658a04a 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -261,6 +261,8 @@ @testset verbose=true "Mirroring Methods" begin function mirror(pressure_function, mirror_method; particle_spacing=0.05, domain_size=(2.0, 1.0)) + # Initialize a fluid block with pressure according to `pressure_function` + # and a adjacent inflow and outflow open boundaries to test the pressure extrapolation. domain_fluid = RectangularShape(particle_spacing, round.(Int, domain_size ./ particle_spacing), (0.0, 0.0), density=1000.0, @@ -283,8 +285,8 @@ boundary_model=BoundaryModelTafuni(), 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) v_open_boundary = zero(outflow.initial_condition.velocity) @@ -307,6 +309,7 @@ boundary_model=BoundaryModelTafuni(), 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) @@ -324,6 +327,10 @@ end function interpolate_pressure(mirror_method, pressure_func; particle_spacing=0.05) + # First call the function above to initialize fluid with pressure according to the function + # and then extrapolate pressure to the inflow and outflow boundary systems. + # Then, in this function, we apply an SPH interpolation on this extrapolated pressure field + # to get a continuous representation of the extrapolated pressure field to validate. fluid_system, open_boundary_in, open_boundary_out, v_fluid = mirror(pressure_func, mirror_method) @@ -341,7 +348,7 @@ smoothing_length = 1.2 * particle_spacing smoothing_kernel = WendlandC2Kernel{2}() - # Use a fluid system to interpolate the pressure + # Use a temporary fluid system just to interpolate the pressure interpolation_system = WeaklyCompressibleSPHSystem(entire_domain, ContinuityDensity(), nothing, smoothing_kernel, From 956ad94e89f3e48384f8bad1e8f6350606a2f079 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 24 Jul 2025 23:27:24 +0200 Subject: [PATCH 25/54] first prototype: NOT VALIDATED YET --- examples/fluid/pipe_flow_2d.jl | 39 +-- src/general/semidiscretization.jl | 4 +- src/io/write_vtk.jl | 8 - .../boundary/open_boundary/boundary_zones.jl | 157 +++++++++++- .../method_of_characteristics.jl | 138 +++++----- .../boundary/open_boundary/mirroring.jl | 79 +++--- src/schemes/boundary/open_boundary/system.jl | 235 ++++++------------ 7 files changed, 347 insertions(+), 313 deletions(-) diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index 3544ac7a8..4e2e0820c 100644 --- a/examples/fluid/pipe_flow_2d.jl +++ b/examples/fluid/pipe_flow_2d.jl @@ -101,37 +101,38 @@ end open_boundary_model = BoundaryModelLastiwka() +reference_velocity_in = velocity_function2d +reference_pressure_in = pressure +reference_density_in = fluid_density 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, + reference_density=reference_density_in, + reference_pressure=reference_pressure_in, + reference_velocity=reference_velocity_in, + # average_inflow_velocity=false, boundary_type=boundary_type_in) -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) +# open_boundary_in = OpenBoundarySPHSystem(inflow; fluid_system, +# boundary_model=open_boundary_model, +# buffer_size=n_buffer_particles) +reference_velocity_out = velocity_function2d +reference_pressure_out = pressure +reference_density_out = fluid_density 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), open_boundary_layers, density=fluid_density, particle_spacing, + reference_density=reference_density_out, + reference_pressure=reference_pressure_out, + reference_velocity=reference_velocity_out, 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) +open_boundary = OpenBoundarySPHSystem(inflow, outflow; fluid_system, + boundary_model=open_boundary_model, + buffer_size=n_buffer_particles) # ========================================================================================== # ==== Boundary viscosity_boundary = ViscosityAdami(nu=1e-4) @@ -151,7 +152,7 @@ max_corner = maximum(pipe.boundary.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, +semi = Semidiscretization(fluid_system, open_boundary, boundary_system, neighborhood_search=nhs, parallelization_backend=PolyesterBackend()) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 3e033e447..61fdee8a9 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -927,7 +927,7 @@ end function check_configuration(system::OpenBoundarySPHSystem, 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. @@ -935,7 +935,7 @@ function check_configuration(system::OpenBoundarySPHSystem, systems, system.fluid_system_index[] = fluid_system_index if boundary_model isa BoundaryModelLastiwka && - boundary_zone isa BoundaryZone{BidirectionalFlow} + any(zone -> zone isa BoundaryZone{BidirectionalFlow}, boundary_zones) throw(ArgumentError("`BoundaryModelLastiwka` needs a specific flow direction. " * "Please specify inflow and outflow.")) end diff --git a/src/io/write_vtk.jl b/src/io/write_vtk.jl index a2b40f31e..9eddef59c 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -416,14 +416,6 @@ function write2vtk!(vtk, v, u, t, system::OpenBoundarySPHSystem; write_meta_data 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 - return vtk end diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 30e589e1c..2aef1607e 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -90,21 +90,28 @@ bidirectional_flow = BoundaryZone(; plane=plane_points, plane_normal, particle_s !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. """ -struct BoundaryZone{BT, IC, S, ZO, ZW, FD, PN} +struct BoundaryZone{BT, IC, S, ZO, ZW, FD, PN, RD, RP, RV} + boundary_type :: BT initial_condition :: IC spanning_set :: S zone_origin :: ZO zone_width :: ZW flow_direction :: FD plane_normal :: PN - boundary_type :: BT average_inflow_velocity :: Bool + prescribed_density :: Bool + prescribed_pressure :: Bool + prescribed_velocity :: Bool + reference_density :: RD + reference_pressure :: RP + reference_velocity :: RV end function BoundaryZone(; plane, plane_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_velocity=nothing, + reference_pressure=nothing, reference_density=nothing) if open_boundary_layers <= 0 throw(ArgumentError("`open_boundary_layers` must be positive and greater than zero")) end @@ -129,10 +136,88 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, particle_spacing, initial_condition, extrude_geometry, open_boundary_layers; boundary_type=boundary_type) + NDIMS = ndims(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, " * + "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 + + prescribed_density = isnothing(reference_density) ? false : true + prescribed_pressure = isnothing(reference_pressure) ? false : true + prescribed_velocity = isnothing(reference_velocity) ? false : true + + return BoundaryZone(boundary_type, ic, spanning_set_, zone_origin, zone_width, + flow_direction, plane_normal_, average_inflow_velocity, + prescribed_density, prescribed_pressure, prescribed_velocity, + reference_density_, reference_pressure_, reference_velocity_) +end + +boundary_type_name(::BoundaryZone{ZT}) where {ZT} = string(nameof(ZT)) + +function Base.show(io::IO, boundary_zone::BoundaryZone) + @nospecialize boundary_zone # reduce precompilation time - return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, - flow_direction, plane_normal_, boundary_type, - average_inflow_velocity) + print(io, "BoundaryZone{", boundary_type_name(boundary_zone), "}(") + 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{$(boundary_type_name(boundary_zone))}") + summary_line(io, "#particles", nparticles(boundary_zone.initial_condition)) + summary_line(io, "prescribed velocity", + type2string(boundary_zone.reference_velocity)) + summary_line(io, "prescribed pressure", + type2string(boundary_zone.reference_pressure)) + summary_line(io, "prescribed density", type2string(boundary_zone.reference_density)) + 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, @@ -237,6 +322,17 @@ function spanning_vectors(plane_points::NTuple{3}, zone_width) return hcat(c, edge1, edge2) end +@inline function is_in_boundary_zone(boundary_zones::NTuple{N, BoundaryZone}, + particle_coords) where {N} + for boundary_zone in boundary_zones + if is_in_boundary_zone(boundary_zone, particle_coords) + return true + end + end + + return false +end + @inline function is_in_boundary_zone(boundary_zone::BoundaryZone, particle_coords) (; zone_origin, spanning_set) = boundary_zone particle_position = particle_coords - zone_origin @@ -261,6 +357,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_moving_particle(system) + particle_coords = current_coords(u, system, particle) + + for (i, 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] = i + end + end + end + + return system +end + +function current_boundary_zone(system, boundary_zones, particle) + return 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 +393,29 @@ function remove_outside_particles(initial_condition, spanning_set, zone_origin) return InitialCondition(; coordinates=coordinates[:, in_zone], density=first(density), particle_spacing) 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 diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index addc29210..48fd32f2d 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -30,29 +30,26 @@ end # Called from update callback via `update_open_boundary_eachstep!` @inline function update_boundary_quantities!(system, boundary_model::BoundaryModelLastiwka, 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) + boundary_zone = current_boundary_zone(system, boundary_zones, particle) + (; reference_pressure, reference_density, reference_velocity, + flow_direction) = boundary_zone + particle_position = current_coords(u, system, particle) J1 = cache.characteristics[1, particle] @@ -78,11 +75,13 @@ 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 @@ -102,9 +101,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, boundary_zones, 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] @@ -116,13 +114,58 @@ 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_moving_particle(system)) do particle, neighbor, + pos_diff, distance + boundary_zone = current_boundary_zone(system, boundary_zones, particle) + + (; reference_velocity, reference_pressure, reference_density, + 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_value(reference_density, density[particle], + neighbor_position, t) + + p_b = current_pressure(v_fluid, fluid_system, neighbor) + p_ref = reference_value(reference_pressure, pressure[particle], + neighbor_position, t) + + v_b = current_velocity(v_fluid, fluid_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(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) + boundary_zone = current_boundary_zone(system, boundary_zones, particle) # Particle is outside of the influence of fluid particles if isapprox(volume[particle], 0) @@ -166,57 +209,6 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) 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) - - characteristics[1, particle] += (density_term + pressure_term) * kernel_ - characteristics[2, particle] += (velocity_term + pressure_term) * kernel_ - characteristics[3, particle] += (-velocity_term + pressure_term) * kernel_ - - volume[particle] += kernel_ - end - - return system -end - @inline function prescribe_conditions!(characteristics, particle, ::BoundaryZone{OutFlow}) # J3 is prescribed (i.e. determined from the exterior of the domain). # J1 and J2 is transmitted from the domain interior. @@ -238,15 +230,17 @@ function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, return v end -function average_velocity!(v, u, system, ::BoundaryModelLastiwka, ::BoundaryZone{InFlow}, - semi) +function average_velocity!(v, u, system, ::BoundaryModelLastiwka, + boundary_zone::BoundaryZone{InFlow}, semi) + candidates = findall(x -> is_in_boundary_zone(boundary_zone, x), + reinterpret(reshape, SVector{ndims(system), eltype(u)}, u)) # Division inside the `sum` closure to maintain GPU compatibility - avg_velocity = sum(each_moving_particle(system)) do particle + avg_velocity = sum(candidates) do particle return current_velocity(v, system, particle) / system.buffer.active_particle_count[] end - @threaded semi for particle in each_moving_particle(system) + @threaded semi for particle in candidates # 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 c5f6082bb..d14c7a198 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -69,49 +69,45 @@ end function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni, 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) = 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) + extrapolate_values!(system, boundary_model.mirror_method, + v, v_fluid, u, u_fluid, semi) 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) + for boundary_zone in boundary_zones + (; prescribed_velocity, average_inflow_velocity) = boundary_zone + + 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_pressure - @threaded semi for particle in each_moving_particle(system) - particle_coords = current_coords(u, system, particle) + @threaded semi for particle in each_moving_particle(system) + boundary_zone = current_boundary_zone(system, boundary_zones, particle) + (; prescribed_pressure, prescribed_density, prescribed_velocity, + reference_pressure, reference_density, reference_velocity) = boundary_zone + particle_coords = current_coords(u, system, particle) + + if prescribed_pressure pressure[particle] = reference_value(reference_pressure, pressure[particle], particle_coords, t) end - end - - if prescribed_density - @threaded semi for particle in each_moving_particle(system) - particle_coords = current_coords(u, system, particle) + if prescribed_density density[particle] = reference_value(reference_density, density[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) + if prescribed_velocity v_particle = current_velocity(v, system, particle) v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) @@ -127,12 +123,8 @@ update_final!(system, ::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) = syst 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, boundary_zones, fluid_system) = system # Static indices to avoid allocations two_to_end = SVector{ndims(system)}(2:(ndims(system) + 1)) @@ -147,6 +139,9 @@ function extrapolate_values!(system, # 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) + boundary_zone = current_boundary_zone(system, boundary_zones, 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) @@ -278,10 +273,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 + v_open_boundary, v_fluid, u_open_boundary, u_fluid, semi) + (; pressure, density, boundary_zones) = system fluid_system = corresponding_fluid_system(system, semi) @@ -295,6 +288,9 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, # 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) + boundary_zone = current_boundary_zone(system, boundary_zones, 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) @@ -495,8 +491,7 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se 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))) + reinterpret(reshape, SVector{ndims(system), eltype(u)}, u)) # Division inside the `sum` closure to maintain GPU compatibility avg_velocity = sum(candidates) do particle @@ -504,9 +499,13 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se end @threaded semi for particle in each_moving_particle(system) - # 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] + particle_coords = current_coords(u, system, particle) + + if is_in_boundary_zone(boundary_zone, particle_coords) + # 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] + end end end diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 19bf85792..a76d00a3a 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -36,118 +36,61 @@ Open boundary system for in- and outflow particles. 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. """ -struct OpenBoundarySPHSystem{BM, ELTYPE, NDIMS, IC, FS, FSI, ARRAY1D, BC, FC, BZ, RV, - RP, RD, B, UCU, 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 - update_callback_used :: UCU - cache :: C +struct OpenBoundarySPHSystem{BM, ELTYPE, NDIMS, IC, FS, FSI, ARRAY1D, BC, FC, BZI, BZ, + B, UCU, 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{Bool, 1}: [particle] + fluid_candidates :: FC # Array{Bool, 1}: [particle] + boundary_zone_indices :: BZI # Array{UInt8, 1}: [particle] + boundary_zones :: BZ + buffer :: B + update_callback_used :: UCU + 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, + boundary_zone_indices, boundary_zone, buffer, update_callback_used, 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(boundary_zone_indices), typeof(boundary_zone), typeof(buffer), typeof(update_callback_used), 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, update_callback_used, - cache) + fluid_candidates, boundary_zone_indices, + boundary_zone, buffer, update_callback_used, 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 OpenBoundarySPHSystem(boundary_zones::Union{BoundaryZone, Nothing}...; + fluid_system::FluidSystem, buffer_size::Integer, + boundary_model) + boundary_zones = filter(boundary_zone -> !isnothing(boundary_zone), 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) # These will be set later update_callback_used = Ref(false) @@ -155,44 +98,41 @@ function OpenBoundarySPHSystem(boundary_zone::BoundaryZone; 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, + boundary_zone_indices = zeros(UInt8, nparticles(initial_conditions)) + + return OpenBoundarySPHSystem(boundary_model, initial_conditions, fluid_system, fluid_system_index, smoothing_length, mass, density, volume, pressure, boundary_candidates, fluid_candidates, - boundary_zone, reference_velocity_, - reference_pressure_, reference_density_, buffer, + boundary_zone_indices, boundary_zones, buffer, update_callback_used, cache) end -function create_cache_open_boundary(boundary_model, initial_condition, - reference_density, reference_velocity, - reference_pressure) - ELTYPE = eltype(initial_condition) +function initialize!(system::OpenBoundarySPHSystem, 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 + +create_cache_open_boundary(boundary_model, initial_condition) = (;) + +function create_cache_open_boundary(boundary_model::BoundaryModelLastiwka, + initial_condition) + ELTYPE = eltype(initial_condition) characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) previous_characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) return (; characteristics=characteristics, - previous_characteristics=previous_characteristics, - prescribed_pressure=prescribed_pressure, - prescribed_density=prescribed_density, prescribed_velocity=prescribed_velocity) + previous_characteristics=previous_characteristics) end timer_name(::OpenBoundarySPHSystem) = "open_boundary" vtkname(system::OpenBoundarySPHSystem) = "open_boundary" -boundary_type_name(::BoundaryZone{ZT}) where {ZT} = string(nameof(ZT)) function Base.show(io::IO, system::OpenBoundarySPHSystem) @nospecialize system # reduce precompilation time @@ -213,11 +153,6 @@ function Base.show(io::IO, ::MIME"text/plain", system::OpenBoundarySPHSystem) summary_line(io, "#buffer_particles", system.buffer.buffer_size) 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 @@ -234,10 +169,6 @@ end update_callback_used!(system::OpenBoundarySPHSystem) = system.update_callback_used[] = true -function corresponding_fluid_system(system::OpenBoundarySPHSystem, semi) - return system.fluid_system -end - function smoothing_length(system::OpenBoundarySPHSystem, particle) return system.smoothing_length end @@ -264,19 +195,17 @@ end # This function is called by the `UpdateCallback`, as the integrator array might be modified function update_open_boundary_eachstep!(system::OpenBoundarySPHSystem, v_ode, u_ode, semi, t) + (; 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 @@ -284,8 +213,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) @@ -297,6 +225,7 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) particle_coords = current_coords(u, system, particle) # Check if boundary particle is outside the boundary zone + boundary_zone = current_boundary_zone(system, boundary_zones, particle) if !is_in_boundary_zone(boundary_zone, particle_coords) boundary_candidates[particle] = true end @@ -312,6 +241,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, boundary_zones, particle) convert_particle!(system, fluid_system, boundary_zone, particle, particle_new, v, u, v_fluid, u_fluid) end @@ -325,8 +255,8 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) @threaded semi for fluid_particle in each_moving_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) + # Check if fluid particle is in any boundary zone + if is_in_boundary_zone(boundary_zones, fluid_coords) fluid_candidates[fluid_particle] = true end end @@ -341,7 +271,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 @@ -351,13 +281,15 @@ 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) + update_boundary_zone_indices!(system, u, boundary_zones, semi) + 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) + boundary_zone::BoundaryZone{OutFlow}, + particle, particle_new, v, u, v_fluid, u_fluid) deactivate_particle!(system, particle, u) return system @@ -365,8 +297,8 @@ 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) + boundary_zone::BoundaryZone{InFlow}, + particle, particle_new, v, u, v_fluid, u_fluid) (; spanning_set) = boundary_zone # Activate a new particle in simulation domain @@ -407,8 +339,7 @@ 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) + 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) @@ -458,32 +389,6 @@ 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, From b888b3f6a7efe20beaf748a106e3264507ef3167 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 25 Jul 2025 08:36:16 +0200 Subject: [PATCH 26/54] fix avg vel in LastiwkaModel --- .../open_boundary/method_of_characteristics.jl | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 48fd32f2d..b31541f70 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -232,15 +232,19 @@ end function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone::BoundaryZone{InFlow}, semi) - candidates = findall(x -> is_in_boundary_zone(boundary_zone, x), - reinterpret(reshape, SVector{ndims(system), eltype(u)}, u)) + (; boundary_zones) = system + + particles_in_zone = findall(particle -> boundary_zone == + current_boundary_zone(system, boundary_zones, + particle), + each_moving_particle(system)) # Division inside the `sum` closure to maintain GPU compatibility - avg_velocity = sum(candidates) 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 candidates + @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] From df291e7161ff796ae057900263e35dcd359dc9f4 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 25 Jul 2025 08:40:11 +0200 Subject: [PATCH 27/54] fix avg in mirroring --- .../boundary/open_boundary/mirroring.jl | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index d14c7a198..0d871e0b9 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -493,19 +493,19 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se candidates = findall(x -> dot(x - zone_origin, -plane_normal) <= max_dist, reinterpret(reshape, SVector{ndims(system), eltype(u)}, u)) + particles_in_zone = findall(particle -> boundary_zone == + current_boundary_zone(system, boundary_zones, + particle), candidates) + # Division inside the `sum` closure to maintain GPU compatibility - avg_velocity = sum(candidates) do particle - return current_velocity(v, system, particle) / length(candidates) + 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) - particle_coords = current_coords(u, system, particle) - - if is_in_boundary_zone(boundary_zone, particle_coords) - # 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] - end + @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] end end From 91b308fff4376718a309a730be957166c1ea114a Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 25 Jul 2025 09:49:41 +0200 Subject: [PATCH 28/54] fix tests --- .../boundary/open_boundary/mirroring.jl | 14 +-- src/schemes/boundary/open_boundary/system.jl | 2 +- test/general/buffer.jl | 7 +- .../boundary/open_boundary/boundary_zone.jl | 95 ++++++++++++++++++ .../open_boundary/characteristic_variables.jl | 11 +-- .../boundary/open_boundary/mirroring.jl | 21 ++-- test/systems/open_boundary_system.jl | 98 +++++-------------- 7 files changed, 148 insertions(+), 100 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 0d871e0b9..3fd052c6f 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -274,9 +274,7 @@ end function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, v_open_boundary, v_fluid, u_open_boundary, u_fluid, semi) - (; pressure, density, boundary_zones) = system - - fluid_system = corresponding_fluid_system(system, semi) + (; pressure, density, boundary_zones, 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) @@ -483,6 +481,7 @@ end average_velocity!(v, u, system, boundary_zone, semi) = v function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, semi) + (; boundary_zones) = system (; plane_normal, zone_origin, initial_condition) = boundary_zone # We only use the extrapolated velocity in the vicinity of the transition region. @@ -495,11 +494,14 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se particles_in_zone = findall(particle -> boundary_zone == current_boundary_zone(system, boundary_zones, - particle), candidates) + particle), + each_moving_particle(system)) + + intersect!(candidates, particles_in_zone) # Division inside the `sum` closure to maintain GPU compatibility - avg_velocity = sum(particles_in_zone) do particle - return current_velocity(v, system, particle) / length(particles_in_zone) + avg_velocity = sum(candidates) do particle + return current_velocity(v, system, particle) / length(candidates) end @threaded semi for particle in particles_in_zone diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index a76d00a3a..343c0d5aa 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -138,7 +138,6 @@ function Base.show(io::IO, system::OpenBoundarySPHSystem) @nospecialize system # reduce precompilation time print(io, "OpenBoundarySPHSystem{", ndims(system), "}(") - print(io, boundary_type_name(system.boundary_zone)) print(io, ") with ", nparticles(system), " particles") end @@ -151,6 +150,7 @@ function Base.show(io::IO, ::MIME"text/plain", system::OpenBoundarySPHSystem) summary_header(io, "OpenBoundarySPHSystem{$(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_footer(io) diff --git a/test/general/buffer.jl b/test/general/buffer.jl index 18758590e..41f827402 100644 --- a/test/general/buffer.jl +++ b/test/general/buffer.jl @@ -6,14 +6,11 @@ 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()) + reference_density=0.0, reference_pressure=0.0, + reference_velocity=[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()) diff --git a/test/schemes/boundary/open_boundary/boundary_zone.jl b/test/schemes/boundary/open_boundary/boundary_zone.jl index b14906b63..b1ff8d6fb 100644 --- a/test/schemes/boundary/open_boundary/boundary_zone.jl +++ b/test/schemes/boundary/open_boundary/boundary_zone.jl @@ -1,4 +1,99 @@ @testset verbose=true "Boundary Zone" begin + @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, + reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + open_boundary_layers=4, boundary_type=InFlow()) + + show_compact = "BoundaryZone{InFlow}() with 80 particles" + @test repr(inflow) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone{InFlow} │ + │ ════════════════════ │ + │ #particles: ………………………………………………… 80 │ + │ prescribed velocity: ………………………… constant_vector │ + │ prescribed pressure: ………………………… constant_scalar │ + │ prescribed density: …………………………… constant_scalar │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", inflow) == show_box + + outflow = BoundaryZone(; plane=([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], + plane_normal=(1.0, 0.0), density=1.0, open_boundary_layers=4, + boundary_type=OutFlow()) + + show_compact = "BoundaryZone{OutFlow}() with 80 particles" + @test repr(outflow) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone{OutFlow} │ + │ ═════════════════════ │ + │ #particles: ………………………………………………… 80 │ + │ prescribed velocity: ………………………… constant_vector │ + │ prescribed pressure: ………………………… constant_scalar │ + │ prescribed density: …………………………… constant_scalar │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", outflow) == show_box + end + + @testset verbose=true "Illegal Inputs" begin + plane = ([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, " * + "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) BoundaryZone(; plane, particle_spacing=0.1, + plane_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, " * + "a vector holding the pressure of each particle, or a scalar" + + reference_pressure = [1.0, 1.0] + + @test_throws ArgumentError(error_str) BoundaryZone(; plane, particle_spacing=0.1, + plane_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, " * + "a vector holding the density of each particle, or a scalar" + + reference_density = [1.0, 1.0] + @test_throws ArgumentError(error_str) BoundaryZone(; plane, particle_spacing=0.1, + plane_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 diff --git a/test/schemes/boundary/open_boundary/characteristic_variables.jl b/test/schemes/boundary/open_boundary/characteristic_variables.jl index 570818f8f..d25fcba5f 100644 --- a/test/schemes/boundary/open_boundary/characteristic_variables.jl +++ b/test/schemes/boundary/open_boundary/characteristic_variables.jl @@ -36,10 +36,12 @@ flow_direction = flow_directions[j] inflow = BoundaryZone(; plane=plane_points, particle_spacing, density, plane_normal=flow_direction, open_boundary_layers, - boundary_type=InFlow()) + boundary_type=InFlow(), reference_velocity, + reference_pressure, reference_density) outflow = BoundaryZone(; plane=plane_points, particle_spacing, density, plane_normal=(-flow_direction), open_boundary_layers, - boundary_type=OutFlow()) + boundary_type=OutFlow(), reference_velocity, + reference_pressure, reference_density) boundary_zones = [ inflow, @@ -62,10 +64,7 @@ boundary_system = OpenBoundarySPHSystem(boundary_zone; fluid_system, buffer_size=0, - boundary_model=BoundaryModelLastiwka(), - reference_velocity, - reference_pressure, - reference_density) + boundary_model=BoundaryModelLastiwka()) semi = Semidiscretization(fluid_system, boundary_system) diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 8f658a04a..ce0c7f454 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -66,6 +66,7 @@ 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) @@ -163,6 +162,7 @@ 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) @@ -236,6 +234,7 @@ 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,7 +245,7 @@ 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) @@ -288,6 +287,7 @@ # 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,8 +297,7 @@ 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]]) @@ -312,6 +311,7 @@ # 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 diff --git a/test/systems/open_boundary_system.jl b/test/systems/open_boundary_system.jl index 0fdf98f90..ecd620170 100644 --- a/test/systems/open_boundary_system.jl +++ b/test/systems/open_boundary_system.jl @@ -1,71 +1,19 @@ @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 "`show`" begin # Mock fluid system struct FluidSystemMock2 <: TrixiParticles.FluidSystem{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, 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()) - show_compact = "OpenBoundarySPHSystem{2}(InFlow) with 80 particles" + show_compact = "OpenBoundarySPHSystem{2}() with 80 particles" @test repr(system) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ @@ -73,28 +21,21 @@ │ ════════════════════════ │ │ #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 │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box - outflow = BoundaryZone(; plane=([0.0, 0.0], [0.0, 1.0]), particle_spacing=0.05, + outflow = BoundaryZone(; plane=([5.0, 0.0], [5.0, 1.0]), particle_spacing=0.05, plane_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], + boundary_model=BoundaryModelTafuni(), fluid_system=FluidSystemMock2()) - show_compact = "OpenBoundarySPHSystem{2}(OutFlow) with 80 particles" + show_compact = "OpenBoundarySPHSystem{2}() with 80 particles" @test repr(system) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ @@ -102,13 +43,28 @@ │ ════════════════════════ │ │ #particles: ………………………………………………… 80 │ │ #buffer_particles: ……………………………… 0 │ + │ #boundary_zones: …………………………………… 1 │ │ 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: ……………………………………… BoundaryModelTafuni │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", system) == show_box + + system = OpenBoundarySPHSystem(outflow, inflow; buffer_size=0, + boundary_model=BoundaryModelTafuni(), + fluid_system=FluidSystemMock2()) + + show_compact = "OpenBoundarySPHSystem{2}() with 160 particles" + @test repr(system) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ OpenBoundarySPHSystem{2} │ + │ ════════════════════════ │ + │ #particles: ………………………………………………… 160 │ + │ #buffer_particles: ……………………………… 0 │ + │ #boundary_zones: …………………………………… 2 │ + │ fluid system: …………………………………………… FluidSystemMock2 │ + │ boundary model: ……………………………………… BoundaryModelTafuni │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box From 28061d2c4db3aabad2ff61ab10cb0eb913868a2d Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 25 Jul 2025 10:00:23 +0200 Subject: [PATCH 29/54] adapt example file --- examples/fluid/pipe_flow_2d.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index 4e2e0820c..327b5859a 100644 --- a/examples/fluid/pipe_flow_2d.jl +++ b/examples/fluid/pipe_flow_2d.jl @@ -111,13 +111,8 @@ inflow = BoundaryZone(; plane=plane_in, plane_normal=flow_direction, open_bounda reference_density=reference_density_in, reference_pressure=reference_pressure_in, reference_velocity=reference_velocity_in, - # average_inflow_velocity=false, boundary_type=boundary_type_in) -# open_boundary_in = OpenBoundarySPHSystem(inflow; fluid_system, -# boundary_model=open_boundary_model, -# buffer_size=n_buffer_particles) - reference_velocity_out = velocity_function2d reference_pressure_out = pressure reference_density_out = fluid_density From 99f5eaf6d0220b238909e7a06f24922e19cc6426 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 25 Jul 2025 11:41:16 +0200 Subject: [PATCH 30/54] fix allocations --- src/general/semidiscretization.jl | 4 +- .../boundary/open_boundary/boundary_zones.jl | 33 ++++++++---- .../method_of_characteristics.jl | 52 ++++++++----------- .../boundary/open_boundary/mirroring.jl | 33 +++++++----- src/schemes/boundary/open_boundary/system.jl | 32 ++---------- test/examples/examples_fluid.jl | 4 +- .../boundary/open_boundary/boundary_zone.jl | 26 +++++----- .../open_boundary/characteristic_variables.jl | 10 ++-- 8 files changed, 92 insertions(+), 102 deletions(-) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 61fdee8a9..724113138 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -935,9 +935,9 @@ function check_configuration(system::OpenBoundarySPHSystem, systems, system.fluid_system_index[] = fluid_system_index if boundary_model isa BoundaryModelLastiwka && - any(zone -> zone isa BoundaryZone{BidirectionalFlow}, boundary_zones) + any(zone -> isnothing(zone.flow_direction), boundary_zones) throw(ArgumentError("`BoundaryModelLastiwka` needs a specific flow direction. " * - "Please specify inflow and outflow.")) + "Please specify `InFlow()` and `OutFlow()`.")) end if first(PointNeighbors.requires_update(neighborhood_search)) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 2aef1607e..c70d2738e 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -90,8 +90,7 @@ bidirectional_flow = BoundaryZone(; plane=plane_points, plane_normal, particle_s !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. """ -struct BoundaryZone{BT, IC, S, ZO, ZW, FD, PN, RD, RP, RV} - boundary_type :: BT +struct BoundaryZone{IC, S, ZO, ZW, FD, PN, RD, RP, RV} initial_condition :: IC spanning_set :: S zone_origin :: ZO @@ -187,18 +186,29 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, prescribed_pressure = isnothing(reference_pressure) ? false : true prescribed_velocity = isnothing(reference_velocity) ? false : true - return BoundaryZone(boundary_type, ic, spanning_set_, zone_origin, zone_width, + return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, flow_direction, plane_normal_, average_inflow_velocity, prescribed_density, prescribed_pressure, prescribed_velocity, reference_density_, reference_pressure_, reference_velocity_) end -boundary_type_name(::BoundaryZone{ZT}) where {ZT} = string(nameof(ZT)) +function boundary_type_name(boundary_zone::BoundaryZone) + (; flow_direction, plane_normal) = boundary_zone + + # Bidirectional flow + isnothing(flow_direction) && return "bidirectional_flow" + + # Outflow + signbit(dot(flow_direction, plane_normal)) && return "outflow" + + # Inflow + return "inflow" +end function Base.show(io::IO, boundary_zone::BoundaryZone) @nospecialize boundary_zone # reduce precompilation time - print(io, "BoundaryZone{", boundary_type_name(boundary_zone), "}(") + print(io, "BoundaryZone(") print(io, ") with ", nparticles(boundary_zone.initial_condition), " particles") end @@ -208,7 +218,8 @@ function Base.show(io::IO, ::MIME"text/plain", boundary_zone::BoundaryZone) if get(io, :compact, false) show(io, boundary_zone) else - summary_header(io, "BoundaryZone{$(boundary_type_name(boundary_zone))}") + 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, "prescribed velocity", type2string(boundary_zone.reference_velocity)) @@ -263,7 +274,7 @@ function set_up_boundary_zone(plane, plane_normal, flow_direction, density, throw(ArgumentError("`plane_normal` is not normal to the boundary plane")) end - if boundary_type isa InFlow + if boundary_type == InFlow() # First vector of `spanning_vectors` is normal to the boundary plane dot_flow = dot(normalize(spanning_set[:, 1]), flow_direction) @@ -271,7 +282,7 @@ function set_up_boundary_zone(plane, plane_normal, flow_direction, density, # Flip the normal vector to point in the opposite direction of `flow_direction`. spanning_set[:, 1] .*= -sign(dot_flow) - elseif boundary_type isa OutFlow + elseif boundary_type == OutFlow() # First vector of `spanning_vectors` is normal to the boundary plane dot_flow = dot(normalize(spanning_set[:, 1]), flow_direction) @@ -279,7 +290,7 @@ function set_up_boundary_zone(plane, plane_normal, flow_direction, density, # Flip the normal vector to point in `flow_direction`. spanning_set[:, 1] .*= sign(dot_flow) - elseif boundary_type isa BidirectionalFlow + elseif boundary_type == BidirectionalFlow() # Flip the normal vector to point opposite to fluid domain spanning_set[:, 1] .*= -sign(dot_plane_normal) end @@ -374,8 +385,8 @@ function update_boundary_zone_indices!(system, u, boundary_zones, semi) return system end -function current_boundary_zone(system, boundary_zones, particle) - return boundary_zones[system.boundary_zone_indices[particle]] +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) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index b31541f70..bfe21a0af 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -46,7 +46,7 @@ end # Update quantities based on the characteristic variables @threaded semi for particle in each_moving_particle(system) - boundary_zone = current_boundary_zone(system, boundary_zones, particle) + boundary_zone = current_boundary_zone(system, particle) (; reference_pressure, reference_density, reference_velocity, flow_direction) = boundary_zone @@ -125,7 +125,7 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) foreach_point_neighbor(system, fluid_system, system_coords, fluid_coords, semi; points=each_moving_particle(system)) do particle, neighbor, pos_diff, distance - boundary_zone = current_boundary_zone(system, boundary_zones, particle) + boundary_zone = current_boundary_zone(system, particle) (; reference_velocity, reference_pressure, reference_density, flow_direction) = boundary_zone @@ -165,8 +165,6 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) # 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) - boundary_zone = current_boundary_zone(system, boundary_zones, particle) - # Particle is outside of the influence of fluid particles if isapprox(volume[particle], 0) @@ -203,40 +201,36 @@ 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 - -@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 + boundary_zone = current_boundary_zone(system, particle) + (; flow_direction, plane_normal) = boundary_zone -@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)) + # Outflow + if signbit(dot(flow_direction, plane_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)) - return characteristics -end + 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 -function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, semi) - # Only apply averaging at the inflow - return v + return system end +# Only apply averaging at the inflow function average_velocity!(v, u, system, ::BoundaryModelLastiwka, - boundary_zone::BoundaryZone{InFlow}, semi) - (; boundary_zones) = system + boundary_zone::BoundaryZone, semi) + (; flow_direction, plane_normal) = boundary_zone + + # Outflow + signbit(dot(flow_direction, plane_normal)) && return v particles_in_zone = findall(particle -> boundary_zone == - current_boundary_zone(system, boundary_zones, - particle), + current_boundary_zone(system, particle), each_moving_particle(system)) # Division inside the `sum` closure to maintain GPU compatibility diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 3fd052c6f..2403b0c6f 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -91,7 +91,7 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni end @threaded semi for particle in each_moving_particle(system) - boundary_zone = current_boundary_zone(system, boundary_zones, particle) + boundary_zone = current_boundary_zone(system, particle) (; prescribed_pressure, prescribed_density, prescribed_velocity, reference_pressure, reference_density, reference_velocity) = boundary_zone @@ -139,7 +139,7 @@ function extrapolate_values!(system, # 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) - boundary_zone = current_boundary_zone(system, boundary_zones, particle) + boundary_zone = current_boundary_zone(system, particle) (; prescribed_pressure, prescribed_density, prescribed_velocity) = boundary_zone particle_coords = current_coords(u_open_boundary, system, particle) @@ -286,7 +286,7 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, # 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) - boundary_zone = current_boundary_zone(system, boundary_zones, particle) + boundary_zone = current_boundary_zone(system, particle) (; prescribed_pressure, prescribed_density, prescribed_velocity) = boundary_zone particle_coords = current_coords(u_open_boundary, system, particle) @@ -478,11 +478,15 @@ function mirror_position(particle_coords, boundary_zone) return particle_coords - 2 * dist * boundary_zone.plane_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) + (; plane_normal, zone_origin, initial_condition, flow_direction) = boundary_zone -function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, semi) - (; boundary_zones) = system - (; plane_normal, zone_origin, initial_condition) = boundary_zone + # Bidirectional flow + isnothing(flow_direction) && return v + + # Outflow + signbit(dot(flow_direction, plane_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 @@ -493,8 +497,7 @@ function average_velocity!(v, u, system, boundary_zone::BoundaryZone{InFlow}, se reinterpret(reshape, SVector{ndims(system), eltype(u)}, u)) particles_in_zone = findall(particle -> boundary_zone == - current_boundary_zone(system, boundary_zones, - particle), + current_boundary_zone(system, particle), each_moving_particle(system)) intersect!(candidates, particles_in_zone) @@ -514,10 +517,16 @@ 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_plane_normal!(v, system, particle, boundary_zone) + (; plane_normal, flow_direction) = boundary_zone + + # Bidirectional flow + isnothing(flow_direction) && return v + + # Outflow + signbit(dot(flow_direction, plane_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, diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 343c0d5aa..7268f77a8 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -225,7 +225,7 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) particle_coords = current_coords(u, system, particle) # Check if boundary particle is outside the boundary zone - boundary_zone = current_boundary_zone(system, boundary_zones, particle) + boundary_zone = current_boundary_zone(system, particle) if !is_in_boundary_zone(boundary_zone, particle_coords) boundary_candidates[particle] = true end @@ -241,7 +241,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, boundary_zones, particle) + boundary_zone = current_boundary_zone(system, particle) convert_particle!(system, fluid_system, boundary_zone, particle, particle_new, v, u, v_fluid, u_fluid) end @@ -286,35 +286,9 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) 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 - - return system -end - # Buffer particle is outside the boundary zone @inline function convert_particle!(system::OpenBoundarySPHSystem, fluid_system, - boundary_zone::BoundaryZone{BidirectionalFlow}, + boundary_zone::BoundaryZone, particle, particle_new, v, u, v_fluid, u_fluid) relative_position = current_coords(u, system, particle) - boundary_zone.zone_origin diff --git a/test/examples/examples_fluid.jl b/test/examples/examples_fluid.jl index ede9e6bbf..711182524 100644 --- a/test/examples/examples_fluid.jl +++ b/test/examples/examples_fluid.jl @@ -339,7 +339,7 @@ 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__, @@ -354,7 +354,7 @@ 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", diff --git a/test/schemes/boundary/open_boundary/boundary_zone.jl b/test/schemes/boundary/open_boundary/boundary_zone.jl index b1ff8d6fb..fba55be89 100644 --- a/test/schemes/boundary/open_boundary/boundary_zone.jl +++ b/test/schemes/boundary/open_boundary/boundary_zone.jl @@ -7,12 +7,13 @@ reference_velocity=[0.0, 0.0], open_boundary_layers=4, boundary_type=InFlow()) - show_compact = "BoundaryZone{InFlow}() with 80 particles" + show_compact = "BoundaryZone() with 80 particles" @test repr(inflow) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ BoundaryZone{InFlow} │ - │ ════════════════════ │ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… inflow │ │ #particles: ………………………………………………… 80 │ │ prescribed velocity: ………………………… constant_vector │ │ prescribed pressure: ………………………… constant_scalar │ @@ -29,12 +30,13 @@ plane_normal=(1.0, 0.0), density=1.0, open_boundary_layers=4, boundary_type=OutFlow()) - show_compact = "BoundaryZone{OutFlow}() with 80 particles" + show_compact = "BoundaryZone() with 80 particles" @test repr(outflow) == show_compact show_box = """ ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ BoundaryZone{OutFlow} │ - │ ═════════════════════ │ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… outflow │ │ #particles: ………………………………………………… 80 │ │ prescribed velocity: ………………………… constant_vector │ │ prescribed pressure: ………………………… constant_scalar │ @@ -130,8 +132,8 @@ 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 == @@ -194,8 +196,8 @@ 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 == @@ -232,7 +234,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" ? sqrt(eps()) : -sqrt(eps()) @@ -278,7 +280,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 diff --git a/test/schemes/boundary/open_boundary/characteristic_variables.jl b/test/schemes/boundary/open_boundary/characteristic_variables.jl index d25fcba5f..b854c302d 100644 --- a/test/schemes/boundary/open_boundary/characteristic_variables.jl +++ b/test/schemes/boundary/open_boundary/characteristic_variables.jl @@ -51,7 +51,7 @@ @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, density, pressure, @@ -100,13 +100,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)) @@ -120,13 +120,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))) From 8136ba12761c2ba1ffa55a3f3308b1f4a832fed6 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 25 Jul 2025 12:11:42 +0200 Subject: [PATCH 31/54] fix gpu --- src/general/semidiscretization.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 724113138..8eb71b503 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -966,10 +966,8 @@ function set_system_links(system::OpenBoundarySPHSystem, semi) system.pressure, system.boundary_candidates, system.fluid_candidates, - system.boundary_zone, - system.reference_velocity, - system.reference_pressure, - system.reference_density, + system.boundary_zone_indices, + system.boundary_zones, system.buffer, system.update_callback_used, system.cache) From 452b5c59f40256a2dc316ba5c510c466dae70b51 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 26 Jul 2025 10:52:21 +0200 Subject: [PATCH 32/54] first GPU working prototype --- src/TrixiParticles.jl | 2 +- .../boundary/open_boundary/boundary_zones.jl | 142 +++++++++++------- .../method_of_characteristics.jl | 41 ++--- .../boundary/open_boundary/mirroring.jl | 61 ++++---- src/schemes/boundary/open_boundary/system.jl | 33 ++-- src/util.jl | 18 ++- 6 files changed, 174 insertions(+), 123 deletions(-) diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index bad506163..477c71116 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -63,7 +63,7 @@ export Semidiscretization, semidiscretize, restart_with! export InitialCondition export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangianSPHSystem, BoundarySPHSystem, DEMSystem, BoundaryDEMSystem, OpenBoundarySPHSystem -export BoundaryZone, InFlow, OutFlow, BidirectionalFlow +export BoundaryZone, InFlow, OutFlow, BidirectionalFlow, ReferenceValues export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback, PostprocessCallback, StepsizeCallback, UpdateCallback, SteadyStateReachedCallback, ParticleShiftingCallback diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index c70d2738e..13d4d0366 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -90,7 +90,7 @@ bidirectional_flow = BoundaryZone(; plane=plane_points, plane_normal, particle_s !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. """ -struct BoundaryZone{IC, S, ZO, ZW, FD, PN, RD, RP, RV} +struct BoundaryZone{IC, S, ZO, ZW, FD, PN, PV} initial_condition :: IC spanning_set :: S zone_origin :: ZO @@ -98,19 +98,15 @@ struct BoundaryZone{IC, S, ZO, ZW, FD, PN, RD, RP, RV} flow_direction :: FD plane_normal :: PN average_inflow_velocity :: Bool - prescribed_density :: Bool - prescribed_pressure :: Bool - prescribed_velocity :: Bool - reference_density :: RD - reference_pressure :: RP - reference_velocity :: RV + prescribed_density :: PV + prescribed_pressure :: PV + prescribed_velocity :: PV end function BoundaryZone(; plane, plane_normal, density, particle_spacing, initial_condition=nothing, extrude_geometry=nothing, open_boundary_layers::Integer, average_inflow_velocity=true, - boundary_type=BidirectionalFlow(), reference_velocity=nothing, - reference_pressure=nothing, reference_density=nothing) + boundary_type=BidirectionalFlow()) if open_boundary_layers <= 0 throw(ArgumentError("`open_boundary_layers` must be positive and greater than zero")) end @@ -135,61 +131,81 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, particle_spacing, initial_condition, extrude_geometry, open_boundary_layers; boundary_type=boundary_type) - NDIMS = ndims(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 " * + + prescribed_density = Ref(false) + prescribed_pressure = Ref(false) + prescribed_velocity = Ref(false) + + return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, + flow_direction, plane_normal_, average_inflow_velocity, + prescribed_density, prescribed_pressure, prescribed_velocity) +end + +struct ReferenceValues{BZ, F1, F2, F3} + boundary_zone :: BZ + reference_density :: F1 + reference_pressure :: F2 + reference_velocity :: F3 +end + +function ReferenceValues(boundary_zone::BoundaryZone; + density=nothing, pressure=nothing, velocity=nothing) + NDIMS = ndims(boundary_zone.initial_condition) + if !(velocity isa Function || isnothing(velocity) || + (velocity isa Vector && length(velocity) == NDIMS)) + throw(ArgumentError("`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 velocity isa Function + test_result = velocity(zeros(NDIMS), 0.0) if length(test_result) != NDIMS - throw(ArgumentError("`reference_velocity` function must be of dimension $NDIMS")) + throw(ArgumentError("`velocity` function must be of dimension $NDIMS")) end + velocity_ = wrap_reference_function(velocity) + else + velocity_ = wrap_reference_function(SVector(velocity...)) 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 " * + if !(pressure isa Function || pressure isa Real || + isnothing(pressure)) + throw(ArgumentError("`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 pressure isa Function + test_result = pressure(zeros(NDIMS), 0.0) if length(test_result) != 1 - throw(ArgumentError("`reference_pressure` function must be a scalar function")) + throw(ArgumentError("`pressure` function must be a scalar function")) end end - reference_pressure_ = wrap_reference_function(reference_pressure, Val(NDIMS)) + + pressure_ = wrap_reference_function(pressure) end - if !(reference_density isa Function || reference_density isa Real || - isnothing(reference_density)) - throw(ArgumentError("`reference_density` must be either a function mapping " * + if !(density isa Function || density isa Real || + isnothing(density)) + throw(ArgumentError("`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 density isa Function + test_result = density(zeros(NDIMS), 0.0) if length(test_result) != 1 - throw(ArgumentError("`reference_density` function must be a scalar function")) + throw(ArgumentError("`density` function must be a scalar function")) end end - reference_density_ = wrap_reference_function(reference_density, Val(NDIMS)) + + density_ = wrap_reference_function(density) end - prescribed_density = isnothing(reference_density) ? false : true - prescribed_pressure = isnothing(reference_pressure) ? false : true - prescribed_velocity = isnothing(reference_velocity) ? false : true + boundary_zone.prescribed_pressure[] = isnothing(pressure) ? false : true + boundary_zone.prescribed_density[] = isnothing(density) ? false : true + boundary_zone.prescribed_velocity[] = isnothing(velocity) ? false : true - return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, - flow_direction, plane_normal_, average_inflow_velocity, - prescribed_density, prescribed_pressure, prescribed_velocity, - reference_density_, reference_pressure_, reference_velocity_) + return ReferenceValues(boundary_zone, density_, pressure_, velocity_) end function boundary_type_name(boundary_zone::BoundaryZone) @@ -221,11 +237,6 @@ function Base.show(io::IO, ::MIME"text/plain", boundary_zone::BoundaryZone) 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, "prescribed velocity", - type2string(boundary_zone.reference_velocity)) - summary_line(io, "prescribed pressure", - type2string(boundary_zone.reference_pressure)) - summary_line(io, "prescribed density", type2string(boundary_zone.reference_density)) summary_line(io, "width", round(boundary_zone.zone_width, digits=3)) summary_footer(io) end @@ -374,10 +385,10 @@ function update_boundary_zone_indices!(system, u, boundary_zones, semi) @threaded semi for particle in each_moving_particle(system) particle_coords = current_coords(u, system, particle) - for (i, boundary_zone) in enumerate(boundary_zones) + 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] = i + system.boundary_zone_indices[particle] = zone_id end end end @@ -405,28 +416,43 @@ function remove_outside_particles(initial_condition, spanning_set, zone_origin) particle_spacing) end -wrap_reference_function(::Nothing, ::Val) = nothing +wrap_reference_function(::Nothing) = @inline((coords, t) -> nothing) -function wrap_reference_function(function_::Function, ::Val) +function wrap_reference_function(function_::Function) # 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_ +function wrap_reference_function(constant_scalar_::Number) + return @inline((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_) +function wrap_reference_function(constant_vector_::SVector{NDIMS, ELTYPE}) where {NDIMS, + ELTYPE} + return @inline((coords, t)->SVector{NDIMS, ELTYPE}(constant_vector_)) end -function reference_value(value::Function, quantity, position, t) - return value(position, t) +function apply_reference_pressure(system, particle, pos, t) + (; pressure_references) = system.cache + + zone_id = system.boundary_zone_indices[particle] + + return apply_ith_function(pressure_references, zone_id, pos, 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 +function apply_reference_density(system, particle, pos, t) + (; density_references) = system.cache + + zone_id = system.boundary_zone_indices[particle] + + return apply_ith_function(density_references, zone_id, pos, t) +end + +function apply_reference_velocity(system, particle, pos, t) + (; velocity_references) = system.cache + + zone_id = system.boundary_zone_indices[particle] + + return apply_ith_function(velocity_references, zone_id, pos, t) +end diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index bfe21a0af..ab349ad9b 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -47,8 +47,7 @@ end # Update quantities based on the characteristic variables @threaded semi for particle in each_moving_particle(system) boundary_zone = current_boundary_zone(system, particle) - (; reference_pressure, reference_density, reference_velocity, - flow_direction) = boundary_zone + (; flow_direction) = boundary_zone particle_position = current_coords(u, system, particle) @@ -56,17 +55,17 @@ end J2 = cache.characteristics[2, particle] J3 = cache.characteristics[3, particle] - rho_ref = reference_value(reference_density, density[particle], - particle_position, t) + rho_ref = reference_value(apply_reference_density, density[particle], + 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_value(apply_reference_pressure, pressure[particle], + 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) + v_ref = reference_value(apply_reference_velocity, v_current, + system, particle, particle_position, t) rho = density[particle] v_ = v_ref + ((J2 - J3) / (2 * sound_speed * rho)) * flow_direction @@ -101,7 +100,7 @@ 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_zones, fluid_system, density, pressure) = system + (; volume, cache, fluid_system, density, pressure) = system (; characteristics, previous_characteristics) = cache @threaded semi for particle in eachparticle(system) @@ -126,25 +125,23 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) points=each_moving_particle(system)) do particle, neighbor, pos_diff, distance boundary_zone = current_boundary_zone(system, particle) - - (; reference_velocity, reference_pressure, reference_density, - flow_direction) = boundary_zone + (; 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_value(reference_density, density[particle], - neighbor_position, t) + rho_ref = reference_value(apply_reference_density, density[particle], + system, particle, neighbor_position, t) p_b = current_pressure(v_fluid, fluid_system, neighbor) - p_ref = reference_value(reference_pressure, pressure[particle], - neighbor_position, t) + p_ref = reference_value(apply_reference_pressure, pressure[particle], + system, particle, neighbor_position, t) v_b = current_velocity(v_fluid, fluid_system, neighbor) v_particle = current_velocity(v, system, particle) - v_neighbor_ref = reference_value(reference_velocity, v_particle, - neighbor_position, t) + v_neighbor_ref = reference_value(apply_reference_velocity, v_particle, + system, particle, neighbor_position, t) # Determine characteristic variables density_term = -sound_speed^2 * (rho_b - rho_ref) @@ -221,6 +218,14 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) return system end +function reference_value(value::Function, quantity, system, particle, position, t) + function_value = value(system, particle, position, t) + + isnothing(function_value) && return quantity + + return function_value +end + # Only apply averaging at the inflow function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone::BoundaryZone, semi) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 2403b0c6f..896675365 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -69,7 +69,7 @@ end function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) - (; boundary_zones, pressure, density, fluid_system) = system + (; boundary_zones, pressure, density, fluid_system, cache) = system @trixi_timeit timer() "extrapolate and correct values" begin v_fluid = wrap_v(v_ode, fluid_system, semi) @@ -80,9 +80,9 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni end for boundary_zone in boundary_zones - (; prescribed_velocity, average_inflow_velocity) = boundary_zone + (; average_inflow_velocity, prescribed_velocity) = boundary_zone - if !prescribed_velocity && average_inflow_velocity + 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. @@ -92,25 +92,22 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni @threaded semi for particle in each_moving_particle(system) boundary_zone = current_boundary_zone(system, particle) - (; prescribed_pressure, prescribed_density, prescribed_velocity, - reference_pressure, reference_density, reference_velocity) = boundary_zone + (; prescribed_density, prescribed_pressure, prescribed_velocity) = boundary_zone particle_coords = current_coords(u, system, particle) - if prescribed_pressure - pressure[particle] = reference_value(reference_pressure, pressure[particle], - particle_coords, t) + if prescribed_pressure[] + pressure[particle] = apply_reference_pressure(system, particle, + particle_coords, t) end - if prescribed_density - density[particle] = reference_value(reference_density, density[particle], - particle_coords, t) + if prescribed_density[] + density[particle] = apply_reference_density(system, particle, + particle_coords, t) end - if prescribed_velocity - v_particle = current_velocity(v, system, particle) - - v_ref = reference_value(reference_velocity, v_particle, particle_coords, t) + if prescribed_velocity[] + v_ref = apply_reference_velocity(system, particle, particle_coords, t) for dim in eachindex(v_ref) @inbounds v[dim, particle] = v_ref[dim] @@ -124,7 +121,7 @@ update_final!(system, ::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) = syst function extrapolate_values!(system, mirror_method::Union{FirstOrderMirroring, SimpleMirroring}, v_open_boundary, v_fluid, u_open_boundary, u_fluid, semi) - (; pressure, density, boundary_zones, fluid_system) = system + (; pressure, density, boundary_zones, fluid_system, cache) = system # Static indices to avoid allocations two_to_end = SVector{ndims(system)}(2:(ndims(system) + 1)) @@ -140,7 +137,7 @@ function extrapolate_values!(system, # of arbitrary positions (see `PointNeighbors.requires_update`). @threaded semi for particle in each_moving_particle(system) boundary_zone = current_boundary_zone(system, particle) - (; prescribed_pressure, prescribed_density, prescribed_velocity) = boundary_zone + (; 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) @@ -179,15 +176,15 @@ function extrapolate_values!(system, correction_matrix[] += L - if !prescribed_pressure + if !prescribed_pressure[] interpolated_pressure_correction[] += pressure_b * R end - if !prescribed_density + if !prescribed_density[] interpolated_density_correction[] += rho_b * R end - if !prescribed_velocity + if !prescribed_velocity[] interpolated_velocity_correction[] += v_b * R' end end @@ -199,21 +196,21 @@ function extrapolate_values!(system, L_inv = inv(correction_matrix[]) # Pressure - if !prescribed_pressure + if !prescribed_pressure[] first_order_scalar_extrapolation!(pressure, particle, L_inv, interpolated_pressure_correction[], two_to_end, pos_diff, mirror_method) end # Density - if !prescribed_density + if !prescribed_density[] first_order_scalar_extrapolation!(density, particle, L_inv, interpolated_density_correction[], two_to_end, pos_diff, mirror_method) end # Velocity - if !prescribed_velocity + if !prescribed_velocity[] first_order_velocity_extrapolation!(v_open_boundary, particle, L_inv, interpolated_velocity_correction[], two_to_end, pos_diff, mirror_method) @@ -233,7 +230,7 @@ function extrapolate_values!(system, shepard_coefficient = correction_matrix[][1, 1] # Pressure - if !prescribed_pressure + if !prescribed_pressure[] # Only the first entry is used, as the subsequent entries represent gradient # components that are not required for zeroth-order interpolation. interpolated_pressure = first(interpolated_pressure_correction[]) @@ -242,7 +239,7 @@ function extrapolate_values!(system, end # Density - if !prescribed_density + if !prescribed_density[] # Only the first entry is used, as the subsequent entries represent gradient # components that are not required for zeroth-order interpolation. interpolated_density = first(interpolated_density_correction[]) @@ -251,7 +248,7 @@ function extrapolate_values!(system, end # Velocity - if !prescribed_velocity + if !prescribed_velocity[] # Only the first column is used, as the subsequent entries represent gradient # components that are not required for zeroth-order interpolation. interpolated_velocity = interpolated_velocity_correction[][:, 1] @@ -313,15 +310,15 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, shepard_coefficient[] += volume_b * W_ab - if !prescribed_pressure + if !prescribed_pressure[] interpolated_pressure[] += pressure_b * volume_b * W_ab end - if !prescribed_density + if !prescribed_density[] interpolated_density[] += rho_b * volume_b * W_ab end - if !prescribed_velocity + if !prescribed_velocity[] interpolated_velocity[] += v_b * volume_b * W_ab end end @@ -329,19 +326,19 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, if shepard_coefficient[] > eps() pos_diff = particle_coords - ghost_node_position - if !prescribed_pressure + if !prescribed_pressure[] zeroth_order_scalar_extrapolation!(pressure, particle, shepard_coefficient[], interpolated_pressure[]) end - if !prescribed_density + if !prescribed_density[] zeroth_order_scalar_extrapolation!(density, particle, shepard_coefficient[], interpolated_density[]) end - if !prescribed_velocity + if !prescribed_velocity[] zeroth_order_velocity_interpolation!(v_open_boundary, particle, shepard_coefficient[], interpolated_velocity[]) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 7268f77a8..3cf7c3fb6 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -74,10 +74,11 @@ function OpenBoundarySPHSystem(boundary_model, initial_condition, fluid_system, boundary_zone, buffer, update_callback_used, cache) end -function OpenBoundarySPHSystem(boundary_zones::Union{BoundaryZone, Nothing}...; +function OpenBoundarySPHSystem(reference_values::Union{ReferenceValues, Nothing}...; fluid_system::FluidSystem, buffer_size::Integer, boundary_model) - boundary_zones = filter(boundary_zone -> !isnothing(boundary_zone), boundary_zones) + reference_values = filter(ref_values -> !isnothing(ref_values), reference_values) + boundary_zones = map(ref -> ref.boundary_zone, reference_values) initial_conditions = union((bz.initial_condition for bz in boundary_zones)...) @@ -90,7 +91,7 @@ function OpenBoundarySPHSystem(boundary_zones::Union{BoundaryZone, Nothing}...; density = copy(initial_conditions.density) volume = similar(initial_conditions.density) - cache = create_cache_open_boundary(boundary_model, initial_conditions) + cache = create_cache_open_boundary(boundary_model, initial_conditions, reference_values) # These will be set later update_callback_used = Ref(false) @@ -118,17 +119,27 @@ function initialize!(system::OpenBoundarySPHSystem, semi) return system end -create_cache_open_boundary(boundary_model, initial_condition) = (;) - -function create_cache_open_boundary(boundary_model::BoundaryModelLastiwka, - initial_condition) +function create_cache_open_boundary(boundary_model, initial_condition, reference_values) ELTYPE = eltype(initial_condition) - characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) - previous_characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) + pressure_references = map(ref -> ref.reference_pressure, reference_values) + density_references = map(ref -> ref.reference_density, reference_values) + velocity_references = map(ref -> ref.reference_velocity, reference_values) + + if boundary_model isa BoundaryModelLastiwka + characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) + previous_characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) - return (; characteristics=characteristics, - previous_characteristics=previous_characteristics) + return (; characteristics=characteristics, + previous_characteristics=previous_characteristics, + pressure_references=pressure_references, + density_references=density_references, + velocity_references=velocity_references) + else + return (; pressure_references=pressure_references, + density_references=density_references, + velocity_references=velocity_references) + end end timer_name(::OpenBoundarySPHSystem) = "open_boundary" diff --git a/src/util.jl b/src/util.jl index 88be2096a..5baa319c6 100644 --- a/src/util.jl +++ b/src/util.jl @@ -9,6 +9,16 @@ foreach_noalloc(func, remaining_collection) end +@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 + @inline foreach_noalloc(func, collection::Tuple{}) = nothing # Print informative message at startup @@ -144,8 +154,10 @@ struct ThreadedBroadcastArray{T, N, A <: AbstractArray{T, N}, P} <: AbstractArra parallelization_backend::P function ThreadedBroadcastArray(array::AbstractArray{T, N}; - parallelization_backend=default_backend(array)) where {T, - N} + parallelization_backend=default_backend(array)) where { + T, + N + } new{T, N, typeof(array), typeof(parallelization_backend)}(array, parallelization_backend) end @@ -192,7 +204,7 @@ function Base.copyto!(dest::ThreadedBroadcastArray, src::AbstractArray) else # Dual-iterator implementation @threaded dest.parallelization_backend for (Idest, Isrc) in zip(eachindex(dest), - eachindex(src)) + eachindex(src)) @inbounds dest.array[Idest] = src[Isrc] end end From df39f141b1372fd4860877911e763cb3156a143c Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 26 Jul 2025 12:21:27 +0200 Subject: [PATCH 33/54] improve API --- .../boundary/open_boundary/boundary_zones.jl | 104 +++++++----------- .../method_of_characteristics.jl | 3 +- src/schemes/boundary/open_boundary/system.jl | 34 ++++-- 3 files changed, 68 insertions(+), 73 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 13d4d0366..0138090c6 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -90,23 +90,26 @@ bidirectional_flow = BoundaryZone(; plane=plane_points, plane_normal, particle_s !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. """ -struct BoundaryZone{IC, S, ZO, ZW, FD, PN, PV} +struct BoundaryZone{IC, S, ZO, ZW, FD, PN, R} initial_condition :: IC spanning_set :: S zone_origin :: ZO zone_width :: ZW flow_direction :: FD plane_normal :: PN + reference_values :: R average_inflow_velocity :: Bool - prescribed_density :: PV - prescribed_pressure :: PV - prescribed_velocity :: PV + prescribed_density :: Bool + prescribed_pressure :: Bool + prescribed_velocity :: Bool end function BoundaryZone(; plane, plane_normal, density, particle_spacing, initial_condition=nothing, extrude_geometry=nothing, open_boundary_layers::Integer, average_inflow_velocity=true, - boundary_type=BidirectionalFlow()) + 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 @@ -132,80 +135,68 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, extrude_geometry, open_boundary_layers; boundary_type=boundary_type) - prescribed_density = Ref(false) - prescribed_pressure = Ref(false) - prescribed_velocity = Ref(false) - - return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, - flow_direction, plane_normal_, average_inflow_velocity, - prescribed_density, prescribed_pressure, prescribed_velocity) -end - -struct ReferenceValues{BZ, F1, F2, F3} - boundary_zone :: BZ - reference_density :: F1 - reference_pressure :: F2 - reference_velocity :: F3 -end - -function ReferenceValues(boundary_zone::BoundaryZone; - density=nothing, pressure=nothing, velocity=nothing) - NDIMS = ndims(boundary_zone.initial_condition) - if !(velocity isa Function || isnothing(velocity) || - (velocity isa Vector && length(velocity) == NDIMS)) - throw(ArgumentError("`velocity` must be either a function mapping " * + NDIMS = ndims(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, " * "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 velocity isa Function - test_result = velocity(zeros(NDIMS), 0.0) + 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 - velocity_ = wrap_reference_function(velocity) + velocity_ref = wrap_reference_function(reference_velocity) else - velocity_ = wrap_reference_function(SVector(velocity...)) + velocity_ref = wrap_reference_function(SVector(reference_velocity...)) end end - if !(pressure isa Function || pressure isa Real || - isnothing(pressure)) - throw(ArgumentError("`pressure` must be either a function mapping " * + 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 pressure isa Function - test_result = pressure(zeros(NDIMS), 0.0) + if reference_pressure isa Function + test_result = reference_pressure(zeros(NDIMS), 0.0) if length(test_result) != 1 - throw(ArgumentError("`pressure` function must be a scalar function")) + throw(ArgumentError("`reference_pressure` function must be a scalar function")) end end - pressure_ = wrap_reference_function(pressure) + pressure_ref = wrap_reference_function(reference_pressure) end - if !(density isa Function || density isa Real || - isnothing(density)) - throw(ArgumentError("`density` must be either a function mapping " * + 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 density isa Function - test_result = density(zeros(NDIMS), 0.0) + if reference_density isa Function + test_result = reference_density(zeros(NDIMS), 0.0) if length(test_result) != 1 - throw(ArgumentError("`density` function must be a scalar function")) + throw(ArgumentError("`reference_density` function must be a scalar function")) end end - density_ = wrap_reference_function(density) + density_ref = wrap_reference_function(reference_density) end - boundary_zone.prescribed_pressure[] = isnothing(pressure) ? false : true - boundary_zone.prescribed_density[] = isnothing(density) ? false : true - boundary_zone.prescribed_velocity[] = isnothing(velocity) ? false : true + 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) - return ReferenceValues(boundary_zone, density_, pressure_, velocity_) + return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, + flow_direction, plane_normal_, reference_values, + average_inflow_velocity, prescribed_density, prescribed_pressure, + prescribed_velocity) end function boundary_type_name(boundary_zone::BoundaryZone) @@ -344,18 +335,7 @@ function spanning_vectors(plane_points::NTuple{3}, zone_width) return hcat(c, edge1, edge2) end -@inline function is_in_boundary_zone(boundary_zones::NTuple{N, BoundaryZone}, - particle_coords) where {N} - for boundary_zone in boundary_zones - if is_in_boundary_zone(boundary_zone, particle_coords) - return true - end - end - - return false -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 @@ -416,7 +396,7 @@ function remove_outside_particles(initial_condition, spanning_set, zone_origin) particle_spacing) end -wrap_reference_function(::Nothing) = @inline((coords, t) -> nothing) +wrap_reference_function(::Nothing) = @inline((coords, t)->nothing) function wrap_reference_function(function_::Function) # Already a function diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index ab349ad9b..7418f4da2 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -227,8 +227,7 @@ function reference_value(value::Function, quantity, system, particle, position, end # Only apply averaging at the inflow -function average_velocity!(v, u, system, ::BoundaryModelLastiwka, - boundary_zone::BoundaryZone, semi) +function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, semi) (; flow_direction, plane_normal) = boundary_zone # Outflow diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 3cf7c3fb6..c78d09ee8 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -74,11 +74,11 @@ function OpenBoundarySPHSystem(boundary_model, initial_condition, fluid_system, boundary_zone, buffer, update_callback_used, cache) end -function OpenBoundarySPHSystem(reference_values::Union{ReferenceValues, Nothing}...; +function OpenBoundarySPHSystem(boundary_zones::Union{BoundaryZone, Nothing}...; fluid_system::FluidSystem, buffer_size::Integer, boundary_model) - reference_values = filter(ref_values -> !isnothing(ref_values), reference_values) - boundary_zones = map(ref -> ref.boundary_zone, reference_values) + boundary_zones_ = filter(ref_values -> !isnothing(ref_values), boundary_zones) + reference_values_ = map(bz -> bz.reference_values, boundary_zones_) initial_conditions = union((bz.initial_condition for bz in boundary_zones)...) @@ -91,7 +91,8 @@ function OpenBoundarySPHSystem(reference_values::Union{ReferenceValues, Nothing} density = copy(initial_conditions.density) volume = similar(initial_conditions.density) - cache = create_cache_open_boundary(boundary_model, initial_conditions, reference_values) + cache = create_cache_open_boundary(boundary_model, initial_conditions, + reference_values_) # These will be set later update_callback_used = Ref(false) @@ -104,10 +105,23 @@ function OpenBoundarySPHSystem(reference_values::Union{ReferenceValues, Nothing} boundary_zone_indices = zeros(UInt8, nparticles(initial_conditions)) + boundary_zones_new = map(zone -> BoundaryZone(zone.initial_condition, + zone.spanning_set, + zone.zone_origin, + zone.zone_width, + zone.flow_direction, + zone.plane_normal, + nothing, + zone.average_inflow_velocity, + zone.prescribed_density, + zone.prescribed_pressure, + zone.prescribed_velocity), + boundary_zones) + return OpenBoundarySPHSystem(boundary_model, initial_conditions, fluid_system, fluid_system_index, smoothing_length, mass, density, volume, pressure, boundary_candidates, fluid_candidates, - boundary_zone_indices, boundary_zones, buffer, + boundary_zone_indices, boundary_zones_new, buffer, update_callback_used, cache) end @@ -267,8 +281,10 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) fluid_coords = current_coords(u_fluid, fluid_system, fluid_particle) # Check if fluid particle is in any boundary zone - if is_in_boundary_zone(boundary_zones, fluid_coords) - fluid_candidates[fluid_particle] = true + for boundary_zone in boundary_zones + if is_in_boundary_zone(boundary_zone, fluid_coords) + fluid_candidates[fluid_particle] = true + end end end @@ -299,8 +315,8 @@ end # Buffer particle is outside the boundary zone @inline function convert_particle!(system::OpenBoundarySPHSystem, fluid_system, - boundary_zone::BoundaryZone, - particle, particle_new, v, u, v_fluid, u_fluid) + 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. From 5f99a1802a3b82213917b8ff87648f9a15052645 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 26 Jul 2025 12:36:04 +0200 Subject: [PATCH 34/54] fix tests --- src/schemes/boundary/open_boundary/boundary_zones.jl | 3 ++- src/util.jl | 12 +++++------- test/schemes/boundary/open_boundary/boundary_zone.jl | 6 ------ test/schemes/boundary/open_boundary/mirroring.jl | 2 +- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 0138090c6..d6046efed 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -150,7 +150,8 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, end velocity_ref = wrap_reference_function(reference_velocity) else - velocity_ref = wrap_reference_function(SVector(reference_velocity...)) + v = isnothing(reference_velocity) ? nothing : SVector(reference_velocity...) + velocity_ref = wrap_reference_function(v) end end diff --git a/src/util.jl b/src/util.jl index 5baa319c6..d551761ec 100644 --- a/src/util.jl +++ b/src/util.jl @@ -9,6 +9,8 @@ foreach_noalloc(func, remaining_collection) end +@inline foreach_noalloc(func, collection::Tuple{}) = nothing + @inline function apply_ith_function(functions, index, args...) if index == 1 # Found the function to apply, apply it and return @@ -19,8 +21,6 @@ end apply_ith_function(Base.tail(functions), index - 1, args...) end -@inline foreach_noalloc(func, collection::Tuple{}) = nothing - # Print informative message at startup function print_startup_message() s = """ @@ -154,10 +154,8 @@ struct ThreadedBroadcastArray{T, N, A <: AbstractArray{T, N}, P} <: AbstractArra parallelization_backend::P function ThreadedBroadcastArray(array::AbstractArray{T, N}; - parallelization_backend=default_backend(array)) where { - T, - N - } + parallelization_backend=default_backend(array)) where {T, + N} new{T, N, typeof(array), typeof(parallelization_backend)}(array, parallelization_backend) end @@ -204,7 +202,7 @@ function Base.copyto!(dest::ThreadedBroadcastArray, src::AbstractArray) else # Dual-iterator implementation @threaded dest.parallelization_backend for (Idest, Isrc) in zip(eachindex(dest), - eachindex(src)) + eachindex(src)) @inbounds dest.array[Idest] = src[Isrc] end end diff --git a/test/schemes/boundary/open_boundary/boundary_zone.jl b/test/schemes/boundary/open_boundary/boundary_zone.jl index fba55be89..7efbd9cc1 100644 --- a/test/schemes/boundary/open_boundary/boundary_zone.jl +++ b/test/schemes/boundary/open_boundary/boundary_zone.jl @@ -15,9 +15,6 @@ │ ════════════ │ │ boundary type: ………………………………………… inflow │ │ #particles: ………………………………………………… 80 │ - │ prescribed velocity: ………………………… constant_vector │ - │ prescribed pressure: ………………………… constant_scalar │ - │ prescribed density: …………………………… constant_scalar │ │ width: ……………………………………………………………… 0.2 │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @@ -38,9 +35,6 @@ │ ════════════ │ │ boundary type: ………………………………………… outflow │ │ #particles: ………………………………………………… 80 │ - │ prescribed velocity: ………………………… constant_vector │ - │ prescribed pressure: ………………………… constant_scalar │ - │ prescribed density: …………………………… constant_scalar │ │ width: ……………………………………………………………… 0.2 │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index ce0c7f454..7a6c63cc8 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -248,7 +248,7 @@ 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. From ecabe1e1aac562ace23350126ef8c1bffffca37c Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 26 Jul 2025 12:36:42 +0200 Subject: [PATCH 35/54] rm export --- src/TrixiParticles.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index 477c71116..bad506163 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -63,7 +63,7 @@ export Semidiscretization, semidiscretize, restart_with! export InitialCondition export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangianSPHSystem, BoundarySPHSystem, DEMSystem, BoundaryDEMSystem, OpenBoundarySPHSystem -export BoundaryZone, InFlow, OutFlow, BidirectionalFlow, ReferenceValues +export BoundaryZone, InFlow, OutFlow, BidirectionalFlow export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback, PostprocessCallback, StepsizeCallback, UpdateCallback, SteadyStateReachedCallback, ParticleShiftingCallback From ced1f862187e3e46f428fd3b34620dc4697d1a3a Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 26 Jul 2025 12:43:30 +0200 Subject: [PATCH 36/54] fix --- .../boundary/open_boundary/mirroring.jl | 38 +++++++++---------- src/schemes/boundary/open_boundary/system.jl | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 896675365..45b289023 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -82,7 +82,7 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni for boundary_zone in boundary_zones (; average_inflow_velocity, prescribed_velocity) = boundary_zone - if !prescribed_velocity[] && average_inflow_velocity + 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. @@ -96,17 +96,17 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni particle_coords = current_coords(u, system, particle) - if prescribed_pressure[] + if prescribed_pressure pressure[particle] = apply_reference_pressure(system, particle, particle_coords, t) end - if prescribed_density[] + if prescribed_density density[particle] = apply_reference_density(system, particle, particle_coords, t) end - if prescribed_velocity[] + if prescribed_velocity v_ref = apply_reference_velocity(system, particle, particle_coords, t) for dim in eachindex(v_ref) @@ -176,15 +176,15 @@ function extrapolate_values!(system, correction_matrix[] += L - if !prescribed_pressure[] + if !prescribed_pressure interpolated_pressure_correction[] += pressure_b * R end - if !prescribed_density[] + if !prescribed_density interpolated_density_correction[] += rho_b * R end - if !prescribed_velocity[] + if !prescribed_velocity interpolated_velocity_correction[] += v_b * R' end end @@ -196,21 +196,21 @@ function extrapolate_values!(system, L_inv = inv(correction_matrix[]) # Pressure - if !prescribed_pressure[] + if !prescribed_pressure first_order_scalar_extrapolation!(pressure, particle, L_inv, interpolated_pressure_correction[], two_to_end, pos_diff, mirror_method) end # Density - if !prescribed_density[] + if !prescribed_density first_order_scalar_extrapolation!(density, particle, L_inv, interpolated_density_correction[], two_to_end, pos_diff, mirror_method) end # Velocity - if !prescribed_velocity[] + if !prescribed_velocity first_order_velocity_extrapolation!(v_open_boundary, particle, L_inv, interpolated_velocity_correction[], two_to_end, pos_diff, mirror_method) @@ -230,7 +230,7 @@ function extrapolate_values!(system, shepard_coefficient = correction_matrix[][1, 1] # Pressure - if !prescribed_pressure[] + if !prescribed_pressure # Only the first entry is used, as the subsequent entries represent gradient # components that are not required for zeroth-order interpolation. interpolated_pressure = first(interpolated_pressure_correction[]) @@ -239,7 +239,7 @@ function extrapolate_values!(system, end # Density - if !prescribed_density[] + if !prescribed_density # Only the first entry is used, as the subsequent entries represent gradient # components that are not required for zeroth-order interpolation. interpolated_density = first(interpolated_density_correction[]) @@ -248,7 +248,7 @@ function extrapolate_values!(system, end # Velocity - if !prescribed_velocity[] + if !prescribed_velocity # Only the first column is used, as the subsequent entries represent gradient # components that are not required for zeroth-order interpolation. interpolated_velocity = interpolated_velocity_correction[][:, 1] @@ -310,15 +310,15 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, shepard_coefficient[] += volume_b * W_ab - if !prescribed_pressure[] + if !prescribed_pressure interpolated_pressure[] += pressure_b * volume_b * W_ab end - if !prescribed_density[] + if !prescribed_density interpolated_density[] += rho_b * volume_b * W_ab end - if !prescribed_velocity[] + if !prescribed_velocity interpolated_velocity[] += v_b * volume_b * W_ab end end @@ -326,19 +326,19 @@ function extrapolate_values!(system, mirror_method::ZerothOrderMirroring, if shepard_coefficient[] > eps() pos_diff = particle_coords - ghost_node_position - if !prescribed_pressure[] + if !prescribed_pressure zeroth_order_scalar_extrapolation!(pressure, particle, shepard_coefficient[], interpolated_pressure[]) end - if !prescribed_density[] + if !prescribed_density zeroth_order_scalar_extrapolation!(density, particle, shepard_coefficient[], interpolated_density[]) end - if !prescribed_velocity[] + if !prescribed_velocity zeroth_order_velocity_interpolation!(v_open_boundary, particle, shepard_coefficient[], interpolated_velocity[]) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index c78d09ee8..7bd696de7 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -77,7 +77,7 @@ end function OpenBoundarySPHSystem(boundary_zones::Union{BoundaryZone, Nothing}...; fluid_system::FluidSystem, buffer_size::Integer, boundary_model) - boundary_zones_ = filter(ref_values -> !isnothing(ref_values), boundary_zones) + boundary_zones_ = filter(bz -> !isnothing(bz), boundary_zones) reference_values_ = map(bz -> bz.reference_values, boundary_zones_) initial_conditions = union((bz.initial_condition for bz in boundary_zones)...) From 42c7b3ee8836638c35a84831c47757c7f57b5596 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 26 Jul 2025 15:28:57 +0200 Subject: [PATCH 37/54] fix type stability --- .../boundary/open_boundary/boundary_zones.jl | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index d6046efed..32aff6d3b 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -136,6 +136,7 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, boundary_type=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 " * @@ -148,11 +149,10 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, if length(test_result) != NDIMS throw(ArgumentError("`velocity` function must be of dimension $NDIMS")) end - velocity_ref = wrap_reference_function(reference_velocity) - else - v = isnothing(reference_velocity) ? nothing : SVector(reference_velocity...) - velocity_ref = wrap_reference_function(v) end + # We need this dummy for type stability reasons + velocity_dummy = SVector(ntuple(dim -> ELTYPE(Inf), NDIMS)) + velocity_ref = wrap_reference_function(reference_velocity, velocity_dummy) end if !(reference_pressure isa Function || reference_pressure isa Real || @@ -167,8 +167,9 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, throw(ArgumentError("`reference_pressure` function must be a scalar function")) end end - - pressure_ref = wrap_reference_function(reference_pressure) + # We need this dummy for type stability reasons + pressure_dummy = ELTYPE(Inf) + pressure_ref = wrap_reference_function(reference_pressure, pressure_dummy) end if !(reference_density isa Function || reference_density isa Real || @@ -183,8 +184,9 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, throw(ArgumentError("`reference_density` function must be a scalar function")) end end - - density_ref = wrap_reference_function(reference_density) + # We need this dummy for type stability reasons + density_dummy = ELTYPE(Inf) + density_ref = wrap_reference_function(reference_density, density_dummy) end prescribed_pressure = isnothing(reference_pressure) ? false : true @@ -397,20 +399,22 @@ function remove_outside_particles(initial_condition, spanning_set, zone_origin) particle_spacing) end -wrap_reference_function(::Nothing) = @inline((coords, t)->nothing) +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) +function wrap_reference_function(function_::Function, ref_dummy) # 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) +function wrap_reference_function(constant_scalar_::Number, ref_dummy) return @inline((coords, t)->constant_scalar_) end -function wrap_reference_function(constant_vector_::SVector{NDIMS, ELTYPE}) where {NDIMS, - ELTYPE} +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 From 70422b98ff3f907b4c4ca74913efa81e6c46570f Mon Sep 17 00:00:00 2001 From: LasNikas Date: Mon, 28 Jul 2025 12:52:12 +0200 Subject: [PATCH 38/54] implement suggestions --- src/schemes/boundary/open_boundary/mirroring.jl | 4 +++- test/schemes/boundary/open_boundary/mirroring.jl | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index c5f6082bb..3d9fde0af 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -37,7 +37,9 @@ end """ ZerothOrderMirroring() -Fluid properties are interpolated onto ghost nodes using Shepard interpolation [Shepard1968](@cite). +Fluid properties are interpolated onto ghost nodes using Shepard interpolation. +(See slide 6 from the 4th DualSPHysics Users Workshop: +[Tafuni, Lisbon 2018](https://dual.sphysics.org/4thusersworkshop/data/_uploaded/PDF_Talks_4thWorkshop/Tafuni_Lisbon2018.pdf)). The position of the ghost nodes is obtained by mirroring the boundary particles into the fluid along a direction that is normal to the open boundary. The interpolated values at the ghost nodes are then assigned to the corresponding boundary particles. diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 8f658a04a..b0592541b 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -367,6 +367,10 @@ pressure_func(pos) = cos(2pi * pos[1]) + + # The pressures are interpolated to obtain a unified vector of length 50, + # rather than handling three separate systems with numerous particles each. + # Additionally, it facilitates plotting for test validation purposes. pressures = interpolate_pressure.([ SimpleMirroring(), FirstOrderMirroring(), From 7e0d6185ce30abf538a70227affde9c31c944aaf Mon Sep 17 00:00:00 2001 From: LasNikas Date: Mon, 28 Jul 2025 12:53:36 +0200 Subject: [PATCH 39/54] rm ref --- docs/src/refs.bib | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/docs/src/refs.bib b/docs/src/refs.bib index b7711bc31..2f2a636dd 100644 --- a/docs/src/refs.bib +++ b/docs/src/refs.bib @@ -761,14 +761,7 @@ @book{Poling2001 publisher = {McGraw-Hill}, address = {New York} } -@article{Shepard1968, - author = {Shepard, Donald}, - booktitle = {Proceedings of the 1968 23rd ACM national conference on -}, - title = {A two-dimensional interpolation function for irregularly-spaced data}, - year = {1968}, - publisher = {ACM Press}, - doi = {10.1145/800186.810616}, -} + @article{Smagorinsky1963, author = {Smagorinsky, Joseph}, title = {General Circulation Experiments with the Primitive Equations. I. The Basic Experiment}, From fcfd47777be2ef16280af3c49acc6b7c3a00e476 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Mon, 28 Jul 2025 12:59:57 +0200 Subject: [PATCH 40/54] format --- test/schemes/boundary/open_boundary/mirroring.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index b0592541b..6e3fb9cd6 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -367,7 +367,6 @@ pressure_func(pos) = cos(2pi * pos[1]) - # The pressures are interpolated to obtain a unified vector of length 50, # rather than handling three separate systems with numerous particles each. # Additionally, it facilitates plotting for test validation purposes. From 08de26407f7e1d57d18e4f193f8a5642717e889f Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Fri, 13 Jun 2025 15:52:21 +0200 Subject: [PATCH 41/54] Rename 'tlsph' to 'place_on_shell' (#814) * rename * format * forgot some * format * naming * forgot some more * fix test * incorporate review comments * format --------- Co-authored-by: Niklas Neher <73897120+LasNikas@users.noreply.github.com> --- examples/dem/collapsing_sand_pile_3d.jl | 3 +- examples/fsi/dam_break_gate_2d.jl | 8 +-- examples/fsi/dam_break_plate_2d.jl | 8 +-- examples/preprocessing/packing_2d.jl | 9 +-- examples/preprocessing/packing_3d.jl | 2 +- examples/solid/oscillating_beam_2d.jl | 6 +- src/general/interpolation.jl | 5 +- .../particle_packing/signed_distance.jl | 2 +- src/preprocessing/particle_packing/system.jl | 35 ++++++----- src/setups/complex_shape.jl | 10 ++-- src/setups/extrude_geometry.jl | 60 +++++++++++-------- src/setups/rectangular_shape.jl | 19 +++--- src/setups/sphere_shape.jl | 45 +++++++------- test/setups/extrude_geometry.jl | 7 ++- test/setups/rectangular_shape.jl | 5 +- test/setups/sphere_shape.jl | 8 +-- test/systems/packing_system.jl | 6 +- 17 files changed, 128 insertions(+), 110 deletions(-) diff --git a/examples/dem/collapsing_sand_pile_3d.jl b/examples/dem/collapsing_sand_pile_3d.jl index aa9ba7a56..f463ac241 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/fsi/dam_break_gate_2d.jl b/examples/fsi/dam_break_gate_2d.jl index 529fd5652..44adf7bc5 100644 --- a/examples/fsi/dam_break_gate_2d.jl +++ b/examples/fsi/dam_break_gate_2d.jl @@ -83,18 +83,18 @@ solid_particle_spacing = thickness / (n_particles_x - 1) n_particles_y = round(Int, length_beam / solid_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 solids. 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, (n_particles_x, n_particles_y - 1), (plate_position, solid_particle_spacing), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) fixed_particles = RectangularShape(solid_particle_spacing, (n_particles_x, 1), (plate_position, 0.0), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) solid = union(plate, fixed_particles) diff --git a/examples/fsi/dam_break_plate_2d.jl b/examples/fsi/dam_break_plate_2d.jl index f5b42ecaa..d4f8b206f 100644 --- a/examples/fsi/dam_break_plate_2d.jl +++ b/examples/fsi/dam_break_plate_2d.jl @@ -57,15 +57,15 @@ solid_particle_spacing = thickness / (n_particles_x - 1) n_particles_y = round(Int, length_beam / solid_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 solids. We therefore need to pass `place_on_shell=true`. plate = RectangularShape(solid_particle_spacing, (n_particles_x, n_particles_y - 1), (2initial_fluid_size[1], solid_particle_spacing), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) fixed_particles = RectangularShape(solid_particle_spacing, (n_particles_x, 1), (2initial_fluid_size[1], 0.0), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) solid = union(plate, fixed_particles) diff --git a/examples/preprocessing/packing_2d.jl b/examples/preprocessing/packing_2d.jl index fc8b33026..0975b6e2a 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 cb15a255b..ede3433b3 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/solid/oscillating_beam_2d.jl index 8df52c28f..28d371634 100644 --- a/examples/solid/oscillating_beam_2d.jl +++ b/examples/solid/oscillating_beam_2d.jl @@ -38,7 +38,7 @@ fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing (0.0, elastic_beam.thickness / 2), material.density, cutout_min=(0.0, 0.0), cutout_max=(clamp_radius, elastic_beam.thickness), - tlsph=true) + place_on_shell=true) n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) @@ -48,9 +48,9 @@ n_particles_per_dimension = (round(Int, elastic_beam.length / particle_spacing) # 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`. +# 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) diff --git a/src/general/interpolation.jl b/src/general/interpolation.jl index 9b0ac40d7..6ca3fa16f 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, diff --git a/src/preprocessing/particle_packing/signed_distance.jl b/src/preprocessing/particle_packing/signed_distance.jl index 7e957e124..082deced5 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 1318f6354..77be7e0c5 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`. @@ -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 @@ -79,7 +80,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, update_callback_used, fixed_system, cache, @@ -93,7 +95,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, @@ -108,7 +110,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) @@ -147,12 +150,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)...) @@ -161,7 +164,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, Ref(false), fixed_system, cache, @@ -187,7 +190,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 @@ -349,8 +352,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 @@ -365,7 +368,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 diff --git a/src/setups/complex_shape.jl b/src/setups/complex_shape.jl index 2c515157d..594b3b4f3 100644 --- a/src/setups/complex_shape.jl +++ b/src/setups/complex_shape.jl @@ -79,7 +79,7 @@ end """ sample_boundary(signed_distance_field::SignedDistanceField; - boundary_thickness::Real, tlsph=true) + boundary_thickness::Real, place_on_shell=true) Sample boundary particles of a complex geometry by using the [`SignedDistanceField`](@ref) of the geometry. @@ -89,9 +89,9 @@ of the geometry. # Keywords - `boundary_thickness`: Thickness of the boundary -- `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. @@ -117,7 +117,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 @@ -157,6 +157,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 498266ecd..6d169762c 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 98160bcac..1c30ef5c1 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 0233fc899..3c6a7466d 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/test/setups/extrude_geometry.jl b/test/setups/extrude_geometry.jl index e695f2e17..5a927dbee 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 f3e77043b..7c2fd6875 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 57d904a37..c8b87d76d 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/packing_system.jl b/test/systems/packing_system.jl index d55afd559..b42e53427 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 From 0ed7dd5eabb003add539a7b12804dfccd23f3f61 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 29 Jul 2025 11:24:04 +0200 Subject: [PATCH 42/54] fix merge bugs --- .../method_of_characteristics.jl | 23 ------------------- .../boundary/open_boundary/mirroring.jl | 3 --- 2 files changed, 26 deletions(-) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index da9da12e2..7418f4da2 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -251,26 +251,3 @@ function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, return v end - -function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, semi) - # Only apply averaging at the inflow - return v -end - -function average_velocity!(v, u, system, ::BoundaryModelLastiwka, ::BoundaryZone{InFlow}, - semi) - - # 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[] - end - - @threaded semi for particle in each_moving_particle(system) - # 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] - end - end - - return v -end diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 66fffbfde..93730b60c 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -250,9 +250,6 @@ TrixiParticles.average_velocity!(v_open_boundary, u_open_boundary, open_boundary_in, first(open_boundary_in.boundary_zones), semi) - TrixiParticles.average_velocity!(v_open_boundary, u_open_boundary, open_boundary_in, - inflow, semi) - # Since the velocity profile increases linearly in positive x-direction, # we can use the first velocity entry as a representative value. v_x_fluid_first = v_fluid[1, 1] From 0150a1ffb86d081c3ffc7be9c595ab9b3a44c404 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 29 Jul 2025 12:06:46 +0200 Subject: [PATCH 43/54] supersede #852 --- .../boundary/open_boundary/boundary_zones.jl | 13 +++++++++++++ .../open_boundary/characteristic_variables.jl | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 32aff6d3b..6a314cf34 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -196,6 +196,19 @@ function BoundaryZone(; plane, plane_normal, density, particle_spacing, 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_, reference_values, average_inflow_velocity, prescribed_density, prescribed_pressure, diff --git a/test/schemes/boundary/open_boundary/characteristic_variables.jl b/test/schemes/boundary/open_boundary/characteristic_variables.jl index b854c302d..e83005b76 100644 --- a/test/schemes/boundary/open_boundary/characteristic_variables.jl +++ b/test/schemes/boundary/open_boundary/characteristic_variables.jl @@ -15,7 +15,8 @@ # 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]] From 95ee81197373f4123ccedef841a96f90588af142 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 29 Jul 2025 12:30:10 +0200 Subject: [PATCH 44/54] supersede #850 --- src/general/system.jl | 5 +++-- src/io/write_vtk.jl | 22 +++++++++---------- src/preprocessing/particle_packing/system.jl | 2 +- .../boundary/open_boundary/mirroring.jl | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/general/system.jl b/src/general/system.jl index 69141a8ff..31ee4a868 100644 --- a/src/general/system.jl +++ b/src/general/system.jl @@ -37,7 +37,8 @@ initialize!(system, semi) = system # 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 eachparticle(system) = Base.OneTo(nparticles(system)) +@inline eachparticle(system::System) = active_particles(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) @@ -47,7 +48,7 @@ initialize!(system, semi) = 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 active_particles(system, ::Nothing) = Base.OneTo(nparticles(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) diff --git a/src/io/write_vtk.jl b/src/io/write_vtk.jl index 9eddef59c..6455eed55 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -124,12 +124,12 @@ function trixi2vtk(system_, v_ode_, u_ode_, semi_, t, periodic_box; output_direc write2vtk!(vtk, v, u, t, system, write_meta_data=write_meta_data) # 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)] + for particle in eachparticle(system)] if write_meta_data vtk["solver_version"] = git_hash @@ -263,20 +263,20 @@ end function write2vtk!(vtk, v, u, t, system::DEMSystem; write_meta_data=true) 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) 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) @@ -378,7 +378,7 @@ function write2vtk!(vtk, v, u, t, system::TotalLagrangianSPHSystem; write_meta_d n_fixed_particles = nparticles(system) - n_moving_particles(system) 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)] @@ -410,11 +410,11 @@ end function write2vtk!(vtk, v, u, t, system::OpenBoundarySPHSystem; write_meta_data=true) 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)] + for particle in eachparticle(system)] return vtk end diff --git a/src/preprocessing/particle_packing/system.jl b/src/preprocessing/particle_packing/system.jl index 77be7e0c5..e47e3dc27 100644 --- a/src/preprocessing/particle_packing/system.jl +++ b/src/preprocessing/particle_packing/system.jl @@ -228,7 +228,7 @@ update_callback_used!(system::ParticlePackingSystem) = system.update_callback_us function write2vtk!(vtk, v, u, t, system::ParticlePackingSystem; write_meta_data=true) vtk["velocity"] = [advection_velocity(v, system, particle) - for particle in active_particles(system)] + for particle in eachparticle(system)] if write_meta_data vtk["signed_distances"] = system.signed_distances end diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index 93730b60c..b28b11057 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -334,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 From 76411ff62b85223175f195b2961c7716f0e7e855 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 29 Jul 2025 12:45:21 +0200 Subject: [PATCH 45/54] fix --- test/general/buffer.jl | 2 +- test/general/semidiscretization.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/general/buffer.jl b/test/general/buffer.jl index 41f827402..6b2194eb9 100644 --- a/test/general/buffer.jl +++ b/test/general/buffer.jl @@ -6,7 +6,7 @@ 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], - reference_density=0.0, reference_pressure=0.0, + reference_density=1.0, reference_pressure=0.0, reference_velocity=[0, 0], boundary_type=InFlow()) system = OpenBoundarySPHSystem(zone; fluid_system=FluidSystemMock3(), boundary_model=BoundaryModelLastiwka(), buffer_size=0) diff --git a/test/general/semidiscretization.jl b/test/general/semidiscretization.jl index dea8c356a..862f2ba37 100644 --- a/test/general/semidiscretization.jl +++ b/test/general/semidiscretization.jl @@ -140,7 +140,7 @@ v_ode = vcat(vec(v1), v2) # Avoid `SystemBuffer` barrier - TrixiParticles.each_moving_particle(system::Union{System1, System2}) = TrixiParticles.eachparticle(system) + TrixiParticles.each_moving_particle(system::Union{System1, System2}) = Base.OneTo(nparticles(system)) TrixiParticles.add_source_terms!(dv_ode, v_ode, u_ode, semi, 0.0) From 8ac88be592f08210751d4b4cd3c5b7cd4fca5486 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 29 Jul 2025 22:47:05 +0200 Subject: [PATCH 46/54] rename boundary models --- examples/fluid/pipe_flow_2d.jl | 2 +- src/TrixiParticles.jl | 3 ++- src/general/buffer.jl | 3 +++ src/general/semidiscretization.jl | 4 ++-- src/general/system.jl | 6 +++--- .../open_boundary/method_of_characteristics.jl | 15 +++++++++------ src/schemes/boundary/open_boundary/mirroring.jl | 12 ++++++------ src/schemes/boundary/open_boundary/system.jl | 8 +++++--- .../fluid/entropically_damped_sph/system.jl | 2 ++ .../fluid/weakly_compressible_sph/system.jl | 2 ++ .../solid/discrete_element_method/system.jl | 3 +-- src/schemes/solid/total_lagrangian_sph/system.jl | 3 +-- test/examples/examples_fluid.jl | 12 ++++++------ test/examples/gpu.jl | 14 +++++++------- test/general/buffer.jl | 5 +++-- .../open_boundary/characteristic_variables.jl | 2 +- test/schemes/boundary/open_boundary/mirroring.jl | 10 +++++----- test/systems/open_boundary_system.jl | 12 ++++++------ 18 files changed, 65 insertions(+), 53 deletions(-) diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index 327b5859a..85a133146 100644 --- a/examples/fluid/pipe_flow_2d.jl +++ b/examples/fluid/pipe_flow_2d.jl @@ -99,7 +99,7 @@ function velocity_function2d(pos, t) return SVector(prescribed_velocity, 0.0) end -open_boundary_model = BoundaryModelLastiwka() +open_boundary_model = BoundaryModelLastiwkaCharacteristics() reference_velocity_in = velocity_function2d reference_pressure_in = pressure diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index bad506163..6e4e22585 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -79,7 +79,8 @@ export DensityDiffusion, DensityDiffusionMolteniColagrossi, DensityDiffusionFerr DensityDiffusionAntuono export tensile_instability_control export BoundaryModelMonaghanKajtar, BoundaryModelDummyParticles, AdamiPressureExtrapolation, - PressureMirroring, PressureZeroing, BoundaryModelLastiwka, BoundaryModelTafuni, + PressureMirroring, PressureZeroing, BoundaryModelLastiwkaCharacteristics, + BoundaryModelTafuniMirroring, BernoulliPressureExtrapolation export FirstOrderMirroring, ZerothOrderMirroring, SimpleMirroring export HertzContactModel, LinearContactModel diff --git a/src/general/buffer.jl b/src/general/buffer.jl index 3e1c28e8c..6592035be 100644 --- a/src/general/buffer.jl +++ b/src/general/buffer.jl @@ -35,6 +35,9 @@ 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 diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 8eb71b503..a7cdf4c63 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -934,9 +934,9 @@ function check_configuration(system::OpenBoundarySPHSystem, systems, fluid_system_index = findfirst(==(system.fluid_system), systems) system.fluid_system_index[] = fluid_system_index - if boundary_model isa BoundaryModelLastiwka && + if boundary_model isa BoundaryModelLastiwkaCharacteristics && any(zone -> isnothing(zone.flow_direction), boundary_zones) - throw(ArgumentError("`BoundaryModelLastiwka` needs a specific flow direction. " * + throw(ArgumentError("`BoundaryModelLastiwkaCharacteristics` needs a specific flow direction. " * "Please specify `InFlow()` and `OutFlow()`.")) end diff --git a/src/general/system.jl b/src/general/system.jl index 31ee4a868..f496ae7f5 100644 --- a/src/general/system.jl +++ b/src/general/system.jl @@ -41,13 +41,13 @@ initialize!(system, semi) = 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) = each_moving_particle(system, buffer(system)) @inline each_moving_particle(system, ::Nothing) = Base.OneTo(n_moving_particles(system)) -@inline active_coordinates(u, system) = active_coordinates(u, system, system.buffer) +@inline active_coordinates(u, system) = 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) = active_particles(system, buffer(system)) @inline active_particles(system, ::Nothing) = Base.OneTo(nparticles(system)) # This should not be dispatched by system type. We always expect to get a column of `A`. diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 7418f4da2..8603267ae 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -1,5 +1,5 @@ @doc raw""" - BoundaryModelLastiwka(; extrapolate_reference_values=nothing) + BoundaryModelLastiwkaCharacteristics(; extrapolate_reference_values=nothing) Boundary model for [`OpenBoundarySPHSystem`](@ref). This model uses the characteristic variables to propagate the appropriate values @@ -19,16 +19,17 @@ 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 BoundaryModelLastiwkaCharacteristics{T} extrapolate_reference_values::T - function BoundaryModelLastiwka(; extrapolate_reference_values=nothing) + function BoundaryModelLastiwkaCharacteristics(; 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::BoundaryModelLastiwkaCharacteristics, v, u, v_ode, u_ode, semi, t) (; density, pressure, cache, boundary_zones, fluid_system) = system @@ -87,7 +88,8 @@ end end # Called from semidiscretization -function update_final!(system, ::BoundaryModelLastiwka, v, u, v_ode, u_ode, semi, t) +function update_final!(system, ::BoundaryModelLastiwkaCharacteristics, 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 @@ -227,7 +229,8 @@ function reference_value(value::Function, quantity, system, particle, position, end # Only apply averaging at the inflow -function average_velocity!(v, u, system, ::BoundaryModelLastiwka, boundary_zone, semi) +function average_velocity!(v, u, system, ::BoundaryModelLastiwkaCharacteristics, + boundary_zone, semi) (; flow_direction, plane_normal) = boundary_zone # Outflow diff --git a/src/schemes/boundary/open_boundary/mirroring.jl b/src/schemes/boundary/open_boundary/mirroring.jl index 8a20b9efb..b6780944f 100644 --- a/src/schemes/boundary/open_boundary/mirroring.jl +++ b/src/schemes/boundary/open_boundary/mirroring.jl @@ -47,7 +47,7 @@ The interpolated values at the ghost nodes are then assigned to the correspondin struct ZerothOrderMirroring end @doc raw""" - BoundaryModelTafuni(; mirror_method=FirstOrderMirroring()) + BoundaryModelTafuniMirroring(; mirror_method=FirstOrderMirroring()) Boundary model for the `OpenBoundarySPHSystem`. This model implements the method of [Tafuni et al. (2018)](@cite Tafuni2018) to extrapolate the properties from the fluid domain @@ -61,15 +61,15 @@ 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 BoundaryModelTafuniMirroring{MM} mirror_method::MM end -function BoundaryModelTafuni(; mirror_method=FirstOrderMirroring()) - return BoundaryModelTafuni(mirror_method) +function BoundaryModelTafuniMirroring(; mirror_method=FirstOrderMirroring()) + return BoundaryModelTafuniMirroring(mirror_method) end -function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni, +function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuniMirroring, v, u, v_ode, u_ode, semi, t) (; boundary_zones, pressure, density, fluid_system, cache) = system @@ -118,7 +118,7 @@ function update_boundary_quantities!(system, boundary_model::BoundaryModelTafuni end end -update_final!(system, ::BoundaryModelTafuni, v, u, v_ode, u_ode, semi, t) = system +update_final!(system, ::BoundaryModelTafuniMirroring, v, u, v_ode, u_ode, semi, t) = system function extrapolate_values!(system, mirror_method::Union{FirstOrderMirroring, SimpleMirroring}, diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 7bd696de7..631e52ee8 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -29,8 +29,8 @@ Open boundary system for in- and outflow 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)). + In this case, they will either be extrapolated from the fluid domain ([BoundaryModelTafuniMirroring](@ref BoundaryModelTafuniMirroring)) + or evolved using the characteristic flow variables ([BoundaryModelLastiwkaCharacteristics](@ref BoundaryModelLastiwkaCharacteristics)). !!! warning "Experimental Implementation" This is an experimental feature and may change in future releases. @@ -140,7 +140,7 @@ function create_cache_open_boundary(boundary_model, initial_condition, reference density_references = map(ref -> ref.reference_density, reference_values) velocity_references = map(ref -> ref.reference_velocity, reference_values) - if boundary_model isa BoundaryModelLastiwka + if boundary_model isa BoundaryModelLastiwkaCharacteristics characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) previous_characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) @@ -186,6 +186,8 @@ end return ELTYPE end +@inline buffer(system::OpenBoundarySPHSystem) = system.buffer + function reset_callback_flag!(system::OpenBoundarySPHSystem) system.update_callback_used[] = false diff --git a/src/schemes/fluid/entropically_damped_sph/system.jl b/src/schemes/fluid/entropically_damped_sph/system.jl index 044104916..5c50012e4 100644 --- a/src/schemes/fluid/entropically_damped_sph/system.jl +++ b/src/schemes/fluid/entropically_damped_sph/system.jl @@ -234,6 +234,8 @@ end return ndims(system) * factor_tvf(system) + 2 end +@inline buffer(system::EntropicallyDampedSPHSystem) = system.buffer + system_correction(system::EntropicallyDampedSPHSystem) = system.correction @inline function current_pressure(v, system::EntropicallyDampedSPHSystem, particle) diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index 690803960..a38b25348 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -245,6 +245,8 @@ end return ndims(system) * factor_tvf(system) + 1 end +@inline buffer(system::WeaklyCompressibleSPHSystem) = system.buffer + system_correction(system::WeaklyCompressibleSPHSystem) = system.correction @inline function current_velocity(v, system::WeaklyCompressibleSPHSystem) diff --git a/src/schemes/solid/discrete_element_method/system.jl b/src/schemes/solid/discrete_element_method/system.jl index 1b65c7e26..8398d03b2 100644 --- a/src/schemes/solid/discrete_element_method/system.jl +++ b/src/schemes/solid/discrete_element_method/system.jl @@ -37,7 +37,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 +64,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 diff --git a/src/schemes/solid/total_lagrangian_sph/system.jl b/src/schemes/solid/total_lagrangian_sph/system.jl index acf269d7b..f55011ba5 100644 --- a/src/schemes/solid/total_lagrangian_sph/system.jl +++ b/src/schemes/solid/total_lagrangian_sph/system.jl @@ -75,7 +75,6 @@ struct TotalLagrangianSPHSystem{BM, NDIMS, ELTYPE <: Real, IC, ARRAY1D, ARRAY2D, boundary_model :: BM penalty_force :: PF source_terms :: ST - buffer :: Nothing end function TotalLagrangianSPHSystem(initial_condition, @@ -119,7 +118,7 @@ function TotalLagrangianSPHSystem(initial_condition, n_moving_particles, young_modulus, poisson_ratio, lame_lambda, lame_mu, smoothing_kernel, smoothing_length, acceleration_, boundary_model, - penalty_force, source_terms, nothing) + penalty_force, source_terms) end function Base.show(io::IO, system::TotalLagrangianSPHSystem) diff --git a/test/examples/examples_fluid.jl b/test/examples/examples_fluid.jl index 711182524..f79a14971 100644 --- a/test/examples/examples_fluid.jl +++ b/test/examples/examples_fluid.jl @@ -289,7 +289,7 @@ @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelLastiwka (WCSPH)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelLastiwkaCharacteristics (WCSPH)" begin @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), @@ -298,7 +298,7 @@ @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 - BoundaryModelLastiwkaCharacteristics (EDAC)" begin @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl")) @@ -306,11 +306,11 @@ @test count_rhs_allocations(sol, semi) == 0 end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuni (EDAC)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuniMirroring (EDAC)" begin @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), - open_boundary_model=BoundaryModelTafuni(), + open_boundary_model=BoundaryModelTafuniMirroring(), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, @@ -321,12 +321,12 @@ @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 - BoundaryModelTafuniMirroring (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(), + open_boundary_model=BoundaryModelTafuniMirroring(), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index 0acdcb852..1d70bca4e 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -288,7 +288,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 - BoundaryModelLastiwkaCharacteristics (WCSPH)" begin @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, tspan=(0.0f0, 0.5f0), joinpath(examples_dir(), @@ -301,7 +301,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 - BoundaryModelLastiwkaCharacteristics (EDAC)" begin @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, tspan=(0.0f0, 0.5f0), joinpath(examples_dir(), @@ -313,13 +313,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 - BoundaryModelTafuniMirroring (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=BoundaryModelTafuniMirroring(), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, @@ -332,7 +332,7 @@ end @test backend == Main.parallelization_backend end - @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuni (WCSPH)" begin + @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuniMirroring (WCSPH)" begin @trixi_test_nowarn trixi_include_changeprecision(Float32, @__MODULE__, tspan=(0.0f0, 0.5f0), joinpath(examples_dir(), @@ -340,8 +340,8 @@ end "pipe_flow_2d.jl"), wcsph=true, sound_speed=20.0f0, pressure=0.0f0, - open_boundary_model=BoundaryModelTafuni(; - mirror_method=ZerothOrderMirroring()), + open_boundary_model=BoundaryModelTafuniMirroring(; + mirror_method=ZerothOrderMirroring()), boundary_type_in=BidirectionalFlow(), boundary_type_out=BidirectionalFlow(), reference_density_in=nothing, diff --git a/test/general/buffer.jl b/test/general/buffer.jl index 6b2194eb9..50f79ce33 100644 --- a/test/general/buffer.jl +++ b/test/general/buffer.jl @@ -9,9 +9,10 @@ reference_density=1.0, reference_pressure=0.0, reference_velocity=[0, 0], boundary_type=InFlow()) system = OpenBoundarySPHSystem(zone; fluid_system=FluidSystemMock3(), - boundary_model=BoundaryModelLastiwka(), buffer_size=0) + boundary_model=BoundaryModelLastiwkaCharacteristics(), + buffer_size=0) system_buffer = OpenBoundarySPHSystem(zone; buffer_size=5, - boundary_model=BoundaryModelLastiwka(), + boundary_model=BoundaryModelLastiwkaCharacteristics(), fluid_system=FluidSystemMock3()) n_particles = nparticles(system) diff --git a/test/schemes/boundary/open_boundary/characteristic_variables.jl b/test/schemes/boundary/open_boundary/characteristic_variables.jl index e83005b76..829e06c01 100644 --- a/test/schemes/boundary/open_boundary/characteristic_variables.jl +++ b/test/schemes/boundary/open_boundary/characteristic_variables.jl @@ -65,7 +65,7 @@ boundary_system = OpenBoundarySPHSystem(boundary_zone; fluid_system, buffer_size=0, - boundary_model=BoundaryModelLastiwka()) + boundary_model=BoundaryModelLastiwkaCharacteristics()) semi = Semidiscretization(fluid_system, boundary_system) diff --git a/test/schemes/boundary/open_boundary/mirroring.jl b/test/schemes/boundary/open_boundary/mirroring.jl index b28b11057..d50bbc25f 100644 --- a/test/schemes/boundary/open_boundary/mirroring.jl +++ b/test/schemes/boundary/open_boundary/mirroring.jl @@ -61,7 +61,7 @@ open_boundary_layers=10, density=1000.0, particle_spacing) open_boundary = OpenBoundarySPHSystem(inflow; fluid_system, - boundary_model=BoundaryModelTafuni(), + boundary_model=BoundaryModelTafuniMirroring(), buffer_size=0) semi = Semidiscretization(fluid_system, open_boundary) @@ -157,7 +157,7 @@ open_boundary_layers=10, density=1000.0, particle_spacing) open_boundary = OpenBoundarySPHSystem(inflow; fluid_system, - boundary_model=BoundaryModelTafuni(), + boundary_model=BoundaryModelTafuniMirroring(), buffer_size=0) semi = Semidiscretization(fluid_system, open_boundary) @@ -229,7 +229,7 @@ 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(), + boundary_model=BoundaryModelTafuniMirroring(), buffer_size=0) semi = Semidiscretization(fluid_system, open_boundary_in) @@ -281,7 +281,7 @@ open_boundary_layers=10, density=1000.0, particle_spacing) open_boundary_out = OpenBoundarySPHSystem(outflow; fluid_system, - boundary_model=BoundaryModelTafuni(), + boundary_model=BoundaryModelTafuniMirroring(), buffer_size=0) # Temporary semidiscretization just to extrapolate the pressure into the outflow system @@ -305,7 +305,7 @@ plane_normal=[1.0, 0.0], open_boundary_layers=10, density=1000.0, particle_spacing) open_boundary_in = OpenBoundarySPHSystem(inflow; fluid_system, - boundary_model=BoundaryModelTafuni(), + boundary_model=BoundaryModelTafuniMirroring(), buffer_size=0) # Temporary semidiscretization just to extrapolate the pressure into the outflow system diff --git a/test/systems/open_boundary_system.jl b/test/systems/open_boundary_system.jl index ecd620170..a31b5b366 100644 --- a/test/systems/open_boundary_system.jl +++ b/test/systems/open_boundary_system.jl @@ -10,7 +10,7 @@ plane_normal=(1.0, 0.0), density=1.0, open_boundary_layers=4, boundary_type=InFlow()) system = OpenBoundarySPHSystem(inflow; buffer_size=0, - boundary_model=BoundaryModelLastiwka(), + boundary_model=BoundaryModelLastiwkaCharacteristics(), fluid_system=FluidSystemMock2()) show_compact = "OpenBoundarySPHSystem{2}() with 80 particles" @@ -23,7 +23,7 @@ │ #buffer_particles: ……………………………… 0 │ │ #boundary_zones: …………………………………… 1 │ │ fluid system: …………………………………………… FluidSystemMock2 │ - │ boundary model: ……………………………………… BoundaryModelLastiwka │ + │ boundary model: ……………………………………… BoundaryModelLastiwkaCharacteristics │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box @@ -32,7 +32,7 @@ plane_normal=(1.0, 0.0), density=1.0, open_boundary_layers=4, boundary_type=OutFlow()) system = OpenBoundarySPHSystem(outflow; buffer_size=0, - boundary_model=BoundaryModelTafuni(), + boundary_model=BoundaryModelTafuniMirroring(), fluid_system=FluidSystemMock2()) show_compact = "OpenBoundarySPHSystem{2}() with 80 particles" @@ -45,13 +45,13 @@ │ #buffer_particles: ……………………………… 0 │ │ #boundary_zones: …………………………………… 1 │ │ fluid system: …………………………………………… FluidSystemMock2 │ - │ boundary model: ……………………………………… BoundaryModelTafuni │ + │ boundary model: ……………………………………… BoundaryModelTafuniMirroring │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box system = OpenBoundarySPHSystem(outflow, inflow; buffer_size=0, - boundary_model=BoundaryModelTafuni(), + boundary_model=BoundaryModelTafuniMirroring(), fluid_system=FluidSystemMock2()) show_compact = "OpenBoundarySPHSystem{2}() with 160 particles" @@ -64,7 +64,7 @@ │ #buffer_particles: ……………………………… 0 │ │ #boundary_zones: …………………………………… 2 │ │ fluid system: …………………………………………… FluidSystemMock2 │ - │ boundary model: ……………………………………… BoundaryModelTafuni │ + │ boundary model: ……………………………………… BoundaryModelTafuniMirroring │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", system) == show_box From 2b94538ab718f6877bc45230fcd5b440cedd33cd Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 30 Jul 2025 08:38:31 +0200 Subject: [PATCH 47/54] fix? --- examples/fluid/pipe_flow_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index 85a133146..adb2f3f91 100644 --- a/examples/fluid/pipe_flow_2d.jl +++ b/examples/fluid/pipe_flow_2d.jl @@ -56,7 +56,7 @@ pipe.boundary.coordinates[1, :] .-= particle_spacing * open_boundary_layers NDIMS = ndims(pipe.fluid) -n_buffer_particles = 5 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) +n_buffer_particles = 8 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) # ========================================================================================== # ==== Fluid From d45fbdd6b48f297475420230b7927954eebfab6c Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 31 Jul 2025 17:00:54 +0200 Subject: [PATCH 48/54] fix tests --- examples/fluid/pipe_flow_2d.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index adb2f3f91..2dd655e7b 100644 --- a/examples/fluid/pipe_flow_2d.jl +++ b/examples/fluid/pipe_flow_2d.jl @@ -56,7 +56,7 @@ pipe.boundary.coordinates[1, :] .-= particle_spacing * open_boundary_layers NDIMS = ndims(pipe.fluid) -n_buffer_particles = 8 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) +n_buffer_particles = 5 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) # ========================================================================================== # ==== Fluid @@ -130,7 +130,7 @@ open_boundary = OpenBoundarySPHSystem(inflow, outflow; fluid_system, buffer_size=n_buffer_particles) # ========================================================================================== # ==== Boundary -viscosity_boundary = ViscosityAdami(nu=1e-4) +viscosity_boundary = nothing boundary_model = BoundaryModelDummyParticles(pipe.boundary.density, pipe.boundary.mass, AdamiPressureExtrapolation(), state_equation=state_equation, From ce0ed1d8ca0c164516ce9fdcea7125c11984fb99 Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Fri, 13 Jun 2025 15:52:21 +0200 Subject: [PATCH 49/54] Rename 'tlsph' to 'place_on_shell' (#814) * rename * format * forgot some * format * naming * forgot some more * fix test * incorporate review comments * format --------- Co-authored-by: Niklas Neher <73897120+LasNikas@users.noreply.github.com> --- examples/dem/collapsing_sand_pile_3d.jl | 3 +- examples/fsi/dam_break_gate_2d.jl | 8 +-- examples/fsi/dam_break_plate_2d.jl | 8 +-- examples/preprocessing/packing_2d.jl | 9 +-- examples/preprocessing/packing_3d.jl | 2 +- examples/solid/oscillating_beam_2d.jl | 6 +- src/general/interpolation.jl | 5 +- .../particle_packing/signed_distance.jl | 2 +- src/preprocessing/particle_packing/system.jl | 35 ++++++----- src/setups/complex_shape.jl | 10 ++-- src/setups/extrude_geometry.jl | 60 +++++++++++-------- src/setups/rectangular_shape.jl | 19 +++--- src/setups/sphere_shape.jl | 45 +++++++------- test/setups/extrude_geometry.jl | 7 ++- test/setups/rectangular_shape.jl | 5 +- test/setups/sphere_shape.jl | 8 +-- test/systems/packing_system.jl | 6 +- 17 files changed, 128 insertions(+), 110 deletions(-) diff --git a/examples/dem/collapsing_sand_pile_3d.jl b/examples/dem/collapsing_sand_pile_3d.jl index aa9ba7a56..f463ac241 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/fsi/dam_break_gate_2d.jl b/examples/fsi/dam_break_gate_2d.jl index 529fd5652..44adf7bc5 100644 --- a/examples/fsi/dam_break_gate_2d.jl +++ b/examples/fsi/dam_break_gate_2d.jl @@ -83,18 +83,18 @@ solid_particle_spacing = thickness / (n_particles_x - 1) n_particles_y = round(Int, length_beam / solid_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 solids. 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, (n_particles_x, n_particles_y - 1), (plate_position, solid_particle_spacing), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) fixed_particles = RectangularShape(solid_particle_spacing, (n_particles_x, 1), (plate_position, 0.0), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) solid = union(plate, fixed_particles) diff --git a/examples/fsi/dam_break_plate_2d.jl b/examples/fsi/dam_break_plate_2d.jl index f5b42ecaa..d4f8b206f 100644 --- a/examples/fsi/dam_break_plate_2d.jl +++ b/examples/fsi/dam_break_plate_2d.jl @@ -57,15 +57,15 @@ solid_particle_spacing = thickness / (n_particles_x - 1) n_particles_y = round(Int, length_beam / solid_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 solids. We therefore need to pass `place_on_shell=true`. plate = RectangularShape(solid_particle_spacing, (n_particles_x, n_particles_y - 1), (2initial_fluid_size[1], solid_particle_spacing), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) fixed_particles = RectangularShape(solid_particle_spacing, (n_particles_x, 1), (2initial_fluid_size[1], 0.0), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) solid = union(plate, fixed_particles) diff --git a/examples/preprocessing/packing_2d.jl b/examples/preprocessing/packing_2d.jl index fc8b33026..0975b6e2a 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 cb15a255b..ede3433b3 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/solid/oscillating_beam_2d.jl index 8df52c28f..28d371634 100644 --- a/examples/solid/oscillating_beam_2d.jl +++ b/examples/solid/oscillating_beam_2d.jl @@ -38,7 +38,7 @@ fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing (0.0, elastic_beam.thickness / 2), material.density, cutout_min=(0.0, 0.0), cutout_max=(clamp_radius, elastic_beam.thickness), - tlsph=true) + place_on_shell=true) n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) @@ -48,9 +48,9 @@ n_particles_per_dimension = (round(Int, elastic_beam.length / particle_spacing) # 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`. +# 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) diff --git a/src/general/interpolation.jl b/src/general/interpolation.jl index 9b0ac40d7..6ca3fa16f 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, diff --git a/src/preprocessing/particle_packing/signed_distance.jl b/src/preprocessing/particle_packing/signed_distance.jl index 7e957e124..082deced5 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 1318f6354..77be7e0c5 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`. @@ -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 @@ -79,7 +80,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, update_callback_used, fixed_system, cache, @@ -93,7 +95,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, @@ -108,7 +110,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) @@ -147,12 +150,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)...) @@ -161,7 +164,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, Ref(false), fixed_system, cache, @@ -187,7 +190,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 @@ -349,8 +352,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 @@ -365,7 +368,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 diff --git a/src/setups/complex_shape.jl b/src/setups/complex_shape.jl index 2c515157d..594b3b4f3 100644 --- a/src/setups/complex_shape.jl +++ b/src/setups/complex_shape.jl @@ -79,7 +79,7 @@ end """ sample_boundary(signed_distance_field::SignedDistanceField; - boundary_thickness::Real, tlsph=true) + boundary_thickness::Real, place_on_shell=true) Sample boundary particles of a complex geometry by using the [`SignedDistanceField`](@ref) of the geometry. @@ -89,9 +89,9 @@ of the geometry. # Keywords - `boundary_thickness`: Thickness of the boundary -- `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. @@ -117,7 +117,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 @@ -157,6 +157,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 498266ecd..6d169762c 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 98160bcac..1c30ef5c1 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 0233fc899..3c6a7466d 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/test/setups/extrude_geometry.jl b/test/setups/extrude_geometry.jl index e695f2e17..5a927dbee 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 f3e77043b..7c2fd6875 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 57d904a37..c8b87d76d 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/packing_system.jl b/test/systems/packing_system.jl index d55afd559..b42e53427 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 From 3367db3d2fcd3244275344dd01e583f215aa3887 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 1 Aug 2025 13:12:11 +0200 Subject: [PATCH 50/54] fix characteristics --- .../method_of_characteristics.jl | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/src/schemes/boundary/open_boundary/method_of_characteristics.jl b/src/schemes/boundary/open_boundary/method_of_characteristics.jl index 8603267ae..30d945f43 100644 --- a/src/schemes/boundary/open_boundary/method_of_characteristics.jl +++ b/src/schemes/boundary/open_boundary/method_of_characteristics.jl @@ -48,7 +48,8 @@ end # Update quantities based on the characteristic variables @threaded semi for particle in each_moving_particle(system) boundary_zone = current_boundary_zone(system, particle) - (; flow_direction) = boundary_zone + (; prescribed_velocity, prescribed_density, prescribed_pressure, + flow_direction) = boundary_zone particle_position = current_coords(u, system, particle) @@ -56,18 +57,29 @@ end J2 = cache.characteristics[2, particle] J3 = cache.characteristics[3, particle] - rho_ref = reference_value(apply_reference_density, density[particle], - system, particle, particle_position, t) + if prescribed_density + rho_ref = apply_reference_density(system, particle, particle_position, t) + else + rho_ref = current_density(v, system, particle) + end + density[particle] = rho_ref + ((-J1 + (J2 + J3) / 2) / sound_speed^2) - p_ref = reference_value(apply_reference_pressure, pressure[particle], - system, particle, particle_position, t) + if prescribed_pressure + p_ref = apply_reference_pressure(system, particle, particle_position, t) + else + p_ref = current_pressure(v, system, particle) + end + pressure[particle] = p_ref + (J2 + J3) / 2 - v_current = current_velocity(v, system, particle) - v_ref = reference_value(apply_reference_velocity, v_current, - system, particle, particle_position, t) - rho = density[particle] + if prescribed_velocity + v_ref = apply_reference_velocity(system, particle, particle_position, t) + else + v_ref = current_velocity(v, system, particle) + end + + rho = current_density(v, system, particle) v_ = v_ref + ((J2 - J3) / (2 * sound_speed * rho)) * flow_direction for dim in 1:ndims(system) @@ -127,23 +139,36 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) points=each_moving_particle(system)) do particle, neighbor, pos_diff, distance boundary_zone = current_boundary_zone(system, particle) - (; flow_direction) = boundary_zone + (; prescribed_velocity, prescribed_density, prescribed_pressure, + 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_value(apply_reference_density, density[particle], - system, particle, neighbor_position, t) + + if prescribed_density + rho_ref = apply_reference_density(system, particle, neighbor_position, t) + else + rho_ref = current_density(v, system, particle) + end p_b = current_pressure(v_fluid, fluid_system, neighbor) - p_ref = reference_value(apply_reference_pressure, pressure[particle], - system, particle, neighbor_position, t) + + if prescribed_pressure + p_ref = apply_reference_pressure(system, particle, neighbor_position, t) + else + p_ref = current_pressure(v, system, particle) + end v_b = current_velocity(v_fluid, fluid_system, neighbor) - v_particle = current_velocity(v, system, particle) - v_neighbor_ref = reference_value(apply_reference_velocity, v_particle, - system, particle, neighbor_position, t) + + if prescribed_velocity + v_neighbor_ref = apply_reference_velocity(system, particle, neighbor_position, + t) + else + v_neighbor_ref = current_velocity(v, system, particle) + end # Determine characteristic variables density_term = -sound_speed^2 * (rho_b - rho_ref) @@ -220,14 +245,6 @@ function evaluate_characteristics!(system, v, u, v_ode, u_ode, semi, t) return system end -function reference_value(value::Function, quantity, system, particle, position, t) - function_value = value(system, particle, position, t) - - isnothing(function_value) && return quantity - - return function_value -end - # Only apply averaging at the inflow function average_velocity!(v, u, system, ::BoundaryModelLastiwkaCharacteristics, boundary_zone, semi) From fcb7675bfef8d53026268fccd6a08a111b2e7056 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 1 Aug 2025 13:15:56 +0200 Subject: [PATCH 51/54] revise pipe flow example --- examples/fluid/pipe_flow_2d.jl | 99 ++++++++++++++++++--------------- examples/fluid/pipe_flow_3d.jl | 24 ++++---- test/examples/examples_fluid.jl | 27 +++------ 3 files changed, 72 insertions(+), 78 deletions(-) diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index 5975a9709..4d5672362 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,63 +30,68 @@ 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 = 8 * pipe.n_particles_per_dimension[2]^(NDIMS - 1) +n_buffer_particles = 5 * 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, 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, + buffer_size=n_buffer_particles) end # ========================================================================================== @@ -96,14 +101,14 @@ 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 = BoundaryModelLastiwkaCharacteristics() +open_boundary_model = BoundaryModelTafuniMirroring(; mirror_method=ZerothOrderMirroring()) reference_velocity_in = velocity_function2d -reference_pressure_in = pressure -reference_density_in = fluid_density +reference_pressure_in = nothing +reference_density_in = nothing 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, @@ -111,44 +116,46 @@ inflow = BoundaryZone(; plane=plane_in, plane_normal=flow_direction, open_bounda reference_density=reference_density_in, reference_pressure=reference_pressure_in, reference_velocity=reference_velocity_in, - boundary_type=boundary_type_in) + initial_condition=inlet.fluid, boundary_type=boundary_type_in) -reference_velocity_out = velocity_function2d -reference_pressure_out = pressure -reference_density_out = fluid_density +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]]) +plane_out = ([pipe.fluid_size[1], 0.0], [pipe.fluid_size[1], domain_size[2]]) outflow = BoundaryZone(; plane=plane_out, plane_normal=(-flow_direction), open_boundary_layers, density=fluid_density, particle_spacing, reference_density=reference_density_out, reference_pressure=reference_pressure_out, reference_velocity=reference_velocity_out, - boundary_type=boundary_type_out) + initial_condition=outlet.fluid, boundary_type=boundary_type_out) open_boundary = OpenBoundarySPHSystem(inflow, outflow; fluid_system, boundary_model=open_boundary_model, buffer_size=n_buffer_particles) + # ========================================================================================== # ==== Boundary -viscosity_boundary = nothing -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 = BoundarySPHSystem(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, - boundary_system, neighborhood_search=nhs, +semi = Semidiscretization(fluid_system, open_boundary, boundary_system, + neighborhood_search=nhs, parallelization_backend=PolyesterBackend()) ode = semidiscretize(semi, tspan) diff --git a/examples/fluid/pipe_flow_3d.jl b/examples/fluid/pipe_flow_3d.jl index 10035f430..61e89da3c 100644 --- a/examples/fluid/pipe_flow_3d.jl +++ b/examples/fluid/pipe_flow_3d.jl @@ -24,26 +24,22 @@ 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 - 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, + tspan=tspan, prescribed_velocity=prescribed_velocity, + open_boundary_layers=open_boundary_layers, min_coords_inlet=min_coords_inlet, + min_coords_outlet=min_coords_outlet, 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], diff --git a/test/examples/examples_fluid.jl b/test/examples/examples_fluid.jl index f79a14971..67eb527c8 100644 --- a/test/examples/examples_fluid.jl +++ b/test/examples/examples_fluid.jl @@ -291,15 +291,16 @@ @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelLastiwkaCharacteristics (WCSPH)" begin @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), + open_boundary_model=BoundaryModelLastiwkaCharacteristics(), joinpath(examples_dir(), "fluid", - "pipe_flow_2d.jl"), - wcsph=true) + "pipe_flow_2d.jl")) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelLastiwkaCharacteristics (EDAC)" begin - @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), + @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), wcsph=false, + open_boundary_model=BoundaryModelLastiwkaCharacteristics(), joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl")) @test sol.retcode == ReturnCode.Success @@ -307,16 +308,11 @@ end @trixi_testset "fluid/pipe_flow_2d.jl - BoundaryModelTafuniMirroring (EDAC)" begin - @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), + @trixi_test_nowarn trixi_include(@__MODULE__, tspan=(0.0, 0.5), wcsph=false, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), - open_boundary_model=BoundaryModelTafuniMirroring(), 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 @@ -325,15 +321,8 @@ @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=BoundaryModelTafuniMirroring(), 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 @@ -345,6 +334,7 @@ @trixi_test_nowarn trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), + open_boundary_model=BoundaryModelLastiwkaCharacteristics(), extra_callback=steady_state_reached, tspan=(0.0, 1.5), viscosity_boundary=nothing) @@ -359,6 +349,7 @@ @trixi_test_nowarn trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "pipe_flow_2d.jl"), + open_boundary_model=BoundaryModelLastiwkaCharacteristics(), extra_callback=steady_state_reached, dtmax=2e-3, tspan=(0.0, 1.5), viscosity_boundary=nothing) From 49b10b3612f3ce7eb122a6ba2a5e3eda158f8147 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 1 Aug 2025 15:10:05 +0200 Subject: [PATCH 52/54] fix include bug --- examples/fluid/pipe_flow_3d.jl | 2 +- test/examples/examples_fluid.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fluid/pipe_flow_3d.jl b/examples/fluid/pipe_flow_3d.jl index 61e89da3c..595859aff 100644 --- a/examples/fluid/pipe_flow_3d.jl +++ b/examples/fluid/pipe_flow_3d.jl @@ -22,7 +22,7 @@ open_boundary_layers = 6 # ========================================================================================== # ==== Experiment Setup -tspan = (0.0, 2.0) +tspan = (0.0, 0.5) domain_size = (1.0, 0.4, 0.4) const prescribed_velocity = (1.0, 0.0, 0.0) diff --git a/test/examples/examples_fluid.jl b/test/examples/examples_fluid.jl index 67eb527c8..9d1386352 100644 --- a/test/examples/examples_fluid.jl +++ b/test/examples/examples_fluid.jl @@ -359,7 +359,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 From c1e858944662bf9880ae9884217bdf93df937b4c Mon Sep 17 00:00:00 2001 From: Sven Berger Date: Fri, 13 Jun 2025 15:52:21 +0200 Subject: [PATCH 53/54] Rename 'tlsph' to 'place_on_shell' (#814) * rename * format * forgot some * format * naming * forgot some more * fix test * incorporate review comments * format --------- Co-authored-by: Niklas Neher <73897120+LasNikas@users.noreply.github.com> --- examples/dem/collapsing_sand_pile_3d.jl | 3 +- examples/fsi/dam_break_gate_2d.jl | 8 +-- examples/fsi/dam_break_plate_2d.jl | 8 +-- examples/preprocessing/packing_2d.jl | 9 +-- examples/preprocessing/packing_3d.jl | 2 +- examples/solid/oscillating_beam_2d.jl | 6 +- src/general/interpolation.jl | 5 +- .../particle_packing/signed_distance.jl | 2 +- src/preprocessing/particle_packing/system.jl | 35 ++++++----- src/setups/complex_shape.jl | 10 ++-- src/setups/extrude_geometry.jl | 60 +++++++++++-------- src/setups/rectangular_shape.jl | 19 +++--- src/setups/sphere_shape.jl | 45 +++++++------- test/setups/extrude_geometry.jl | 7 ++- test/setups/rectangular_shape.jl | 5 +- test/setups/sphere_shape.jl | 8 +-- test/systems/packing_system.jl | 6 +- 17 files changed, 128 insertions(+), 110 deletions(-) diff --git a/examples/dem/collapsing_sand_pile_3d.jl b/examples/dem/collapsing_sand_pile_3d.jl index aa9ba7a56..f463ac241 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/fsi/dam_break_gate_2d.jl b/examples/fsi/dam_break_gate_2d.jl index 529fd5652..44adf7bc5 100644 --- a/examples/fsi/dam_break_gate_2d.jl +++ b/examples/fsi/dam_break_gate_2d.jl @@ -83,18 +83,18 @@ solid_particle_spacing = thickness / (n_particles_x - 1) n_particles_y = round(Int, length_beam / solid_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 solids. 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, (n_particles_x, n_particles_y - 1), (plate_position, solid_particle_spacing), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) fixed_particles = RectangularShape(solid_particle_spacing, (n_particles_x, 1), (plate_position, 0.0), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) solid = union(plate, fixed_particles) diff --git a/examples/fsi/dam_break_plate_2d.jl b/examples/fsi/dam_break_plate_2d.jl index f5b42ecaa..d4f8b206f 100644 --- a/examples/fsi/dam_break_plate_2d.jl +++ b/examples/fsi/dam_break_plate_2d.jl @@ -57,15 +57,15 @@ solid_particle_spacing = thickness / (n_particles_x - 1) n_particles_y = round(Int, length_beam / solid_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 solids. We therefore need to pass `place_on_shell=true`. plate = RectangularShape(solid_particle_spacing, (n_particles_x, n_particles_y - 1), (2initial_fluid_size[1], solid_particle_spacing), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) fixed_particles = RectangularShape(solid_particle_spacing, (n_particles_x, 1), (2initial_fluid_size[1], 0.0), - density=solid_density, tlsph=true) + density=solid_density, place_on_shell=true) solid = union(plate, fixed_particles) diff --git a/examples/preprocessing/packing_2d.jl b/examples/preprocessing/packing_2d.jl index fc8b33026..0975b6e2a 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 cb15a255b..ede3433b3 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/solid/oscillating_beam_2d.jl index 8df52c28f..28d371634 100644 --- a/examples/solid/oscillating_beam_2d.jl +++ b/examples/solid/oscillating_beam_2d.jl @@ -38,7 +38,7 @@ fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing (0.0, elastic_beam.thickness / 2), material.density, cutout_min=(0.0, 0.0), cutout_max=(clamp_radius, elastic_beam.thickness), - tlsph=true) + place_on_shell=true) n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) @@ -48,9 +48,9 @@ n_particles_per_dimension = (round(Int, elastic_beam.length / particle_spacing) # 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`. +# 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) diff --git a/src/general/interpolation.jl b/src/general/interpolation.jl index 9b0ac40d7..6ca3fa16f 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, diff --git a/src/preprocessing/particle_packing/signed_distance.jl b/src/preprocessing/particle_packing/signed_distance.jl index 7e957e124..082deced5 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 fb97501e4..3af6adc80 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`. @@ -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 @@ -79,7 +80,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, update_callback_used, fixed_system, cache, @@ -93,7 +95,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, @@ -108,7 +110,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) @@ -147,12 +150,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)...) @@ -161,7 +164,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, Ref(false), fixed_system, cache, @@ -187,7 +190,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 @@ -341,8 +344,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 @@ -357,7 +360,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 diff --git a/src/setups/complex_shape.jl b/src/setups/complex_shape.jl index cdb8e146b..032250ce5 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 498266ecd..6d169762c 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 98160bcac..1c30ef5c1 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 0233fc899..3c6a7466d 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/test/setups/extrude_geometry.jl b/test/setups/extrude_geometry.jl index e695f2e17..5a927dbee 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 f3e77043b..7c2fd6875 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 57d904a37..c8b87d76d 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/packing_system.jl b/test/systems/packing_system.jl index d55afd559..b42e53427 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 From b9d091ce38c2c70b3a7f4f4914f376d7649b1334 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:27:56 +0200 Subject: [PATCH 54/54] Combine PST and TVF into a unified framework (#884) * Combine PST and TVF into a unified framework * Require update callback for PST * Fix WCSPH * Update PST only in callback * Fix EDAC * Update docs * Fix alle example files * Fix tests * Fix periodic channel * Fix docs * Update news * Fix tests * Fix example file --- NEWS.md | 25 +- docs/src/systems/entropically_damped_sph.md | 61 ---- docs/src/systems/weakly_compressible_sph.md | 70 ++++- examples/fluid/lid_driven_cavity_2d.jl | 4 +- .../fluid/periodic_array_of_cylinders_2d.jl | 2 +- examples/fluid/periodic_channel_2d.jl | 3 +- examples/fluid/pipe_flow_2d.jl | 6 +- examples/fluid/taylor_green_vortex_2d.jl | 4 +- src/TrixiParticles.jl | 5 +- src/callbacks/callbacks.jl | 1 - src/callbacks/particle_shifting.jl | 148 --------- src/callbacks/update.jl | 42 ++- src/general/semidiscretization.jl | 2 +- src/general/system.jl | 3 - src/io/write_vtk.jl | 3 - .../fluid/entropically_damped_sph/rhs.jl | 13 +- .../fluid/entropically_damped_sph/system.jl | 28 +- src/schemes/fluid/fluid.jl | 2 +- src/schemes/fluid/shifting_techniques.jl | 291 ++++++++++++++++++ src/schemes/fluid/transport_velocity.jl | 158 ---------- .../fluid/weakly_compressible_sph/rhs.jl | 13 +- .../fluid/weakly_compressible_sph/system.jl | 25 +- test/examples/examples_fluid.jl | 19 +- test/systems/edac_system.jl | 8 +- test/systems/wcsph_system.jl | 4 +- 25 files changed, 479 insertions(+), 461 deletions(-) delete mode 100644 src/callbacks/particle_shifting.jl create mode 100644 src/schemes/fluid/shifting_techniques.jl delete mode 100644 src/schemes/fluid/transport_velocity.jl diff --git a/NEWS.md b/NEWS.md index f1940cbc4..d4e2ccdd7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,14 +4,27 @@ 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 + +- 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=ParticleShiftingTechnique()` to the system. + +- Renamed the keyword argument `tlsph` to `place_on_shell` for `ParticlePackingSystem`, + `sample_boundary`, `extrude_geometry`, `RectangularShape`, and `SphereShape`. + ## 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) @@ -21,17 +34,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). diff --git a/docs/src/systems/entropically_damped_sph.md b/docs/src/systems/entropically_damped_sph.md index 0db8d275e..d035fb6fc 100644 --- a/docs/src/systems/entropically_damped_sph.md +++ b/docs/src/systems/entropically_damped_sph.md @@ -45,64 +45,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/weakly_compressible_sph.md b/docs/src/systems/weakly_compressible_sph.md index 51355961f..62a1bc8d0 100644 --- a/docs/src/systems/weakly_compressible_sph.md +++ b/docs/src/systems/weakly_compressible_sph.md @@ -152,8 +152,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/examples/fluid/lid_driven_cavity_2d.jl b/examples/fluid/lid_driven_cavity_2d.jl index b6582a170..1b8b15cda 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(pressure)) else state_equation = nothing density_calculator = ContinuityDensity() @@ -73,7 +73,7 @@ else smoothing_length, density_calculator=density_calculator, sound_speed, viscosity=viscosity, - transport_velocity=TransportVelocityAdami(pressure)) + shifting_technique=TransportVelocityAdami(pressure)) end # ========================================================================================== diff --git a/examples/fluid/periodic_array_of_cylinders_2d.jl b/examples/fluid/periodic_array_of_cylinders_2d.jl index 850c9f2ff..648a98be1 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(pressure), acceleration=(acceleration_x, 0.0)) # ========================================================================================== diff --git a/examples/fluid/periodic_channel_2d.jl b/examples/fluid/periodic_channel_2d.jl index 74770c318..9cd4bd95a 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) # ========================================================================================== diff --git a/examples/fluid/pipe_flow_2d.jl b/examples/fluid/pipe_flow_2d.jl index 3544ac7a8..85e3461cc 100644 --- a/examples/fluid/pipe_flow_2d.jl +++ b/examples/fluid/pipe_flow_2d.jl @@ -74,6 +74,7 @@ viscosity = ViscosityAdami(nu=kinematic_viscosity) 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) # Alternatively the WCSPH scheme can be used @@ -86,6 +87,7 @@ if wcsph fluid_system = WeaklyCompressibleSPHSystem(pipe.fluid, fluid_density_calculator, state_equation, smoothing_kernel, smoothing_length, viscosity=viscosity, + shifting_technique=ParticleShiftingTechnique(), buffer_size=n_buffer_particles) end @@ -159,12 +161,10 @@ 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/taylor_green_vortex_2d.jl b/examples/fluid/taylor_green_vortex_2d.jl index e6e74fbc4..8a99c455c 100644 --- a/examples/fluid/taylor_green_vortex_2d.jl +++ b/examples/fluid/taylor_green_vortex_2d.jl @@ -86,13 +86,13 @@ 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/src/TrixiParticles.jl b/src/TrixiParticles.jl index 987e20875..2a53025d4 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -65,10 +65,9 @@ export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangian BoundarySPHSystem, DEMSystem, BoundaryDEMSystem, OpenBoundarySPHSystem 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 export SchoenbergCubicSplineKernel, SchoenbergQuarticSplineKernel, SchoenbergQuinticSplineKernel, GaussianKernel, WendlandC2Kernel, WendlandC4Kernel, WendlandC6Kernel, SpikyKernel, Poly6Kernel diff --git a/src/callbacks/callbacks.jl b/src/callbacks/callbacks.jl index 062a16e3a..e9eb048c3 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 921370f3b..000000000 --- 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/update.jl b/src/callbacks/update.jl index 77496a46c..8eecb8380 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/semidiscretization.jl b/src/general/semidiscretization.jl index a9c78b334..9532d812a 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -485,7 +485,7 @@ end @inline add_velocity!(du, v, particle, system::BoundarySPHSystem) = du @inline function add_velocity!(du, v, particle, system::FluidSystem) - # This is zero unless a transport velocity is used + # This is zero unless a shifting technique is used delta_v_ = delta_v(system, particle) for i in 1:ndims(system) diff --git a/src/general/system.jl b/src/general/system.jl index 929fb8b42..5b4d5aecb 100644 --- a/src/general/system.jl +++ b/src/general/system.jl @@ -147,9 +147,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/io/write_vtk.jl b/src/io/write_vtk.jl index b93e25f32..1aca73209 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -349,9 +349,6 @@ function write2vtk!(vtk, v, u, t, system::FluidSystem; write_meta_data=true) 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 diff --git a/src/schemes/fluid/entropically_damped_sph/rhs.jl b/src/schemes/fluid/entropically_damped_sph/rhs.jl index 08139e2ca..a4d7d280a 100644 --- a/src/schemes/fluid/entropically_damped_sph/rhs.jl +++ b/src/schemes/fluid/entropically_damped_sph/rhs.jl @@ -51,13 +51,12 @@ function interact!(dv, v_particle_system, u_particle_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), - 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) + # Extra terms in the momentum equation when using a shifting technique + dv_tvf = 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) dv_surface_tension = surface_tension_force(surface_tension_a, surface_tension_b, particle_system, neighbor_system, diff --git a/src/schemes/fluid/entropically_damped_sph/system.jl b/src/schemes/fluid/entropically_damped_sph/system.jl index 19e50c441..89d8a923f 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,10 +30,11 @@ 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). - `correction`: Correction method used for this system. (default: no correction, see [Corrections](@ref corrections)) @@ -67,7 +68,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 +84,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 +138,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 +162,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 +219,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) @@ -271,7 +271,7 @@ 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) average_pressure(system, system.average_pressure_reduction, particle) @@ -321,7 +321,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 diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index d3e6adf97..59703f9b6 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -193,7 +193,7 @@ 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 000000000..98775b3be --- /dev/null +++ b/src/schemes/fluid/shifting_techniques.jl @@ -0,0 +1,291 @@ +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, + 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 + +@doc raw""" + ParticleShiftingTechnique() + +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 [Particle Shifting Technique](@ref shifting) for more information on the method. + +!!! 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 <: AbstractShiftingTechnique end + +# Zero because PST is applied in a callback +@inline function delta_v(system, ::ParticleShiftingTechnique, particle) + return zero(SVector{ndims(system), eltype(system)}) +end + +function particle_shifting_from_callback!(u_ode, shifting::ParticleShiftingTechnique, + system, v_ode, semi, dt) + @trixi_timeit timer() "particle shifting" begin + v = wrap_v(v_ode, system, semi) + u = wrap_u(u_ode, system, semi) + + # Update the shifting velocity + update_shifting_from_callback!(system, shifting, v, u, v_ode, u_ode, semi) + + # Update the particle positions with the shifting velocity + particle_shifting!(u_ode, shifting, system, semi, dt) + end +end + +function update_shifting_from_callback!(system, ::ParticleShiftingTechnique, + v, u, v_ode, u_ode, semi) + (; cache) = system + (; delta_v) = cache + + set_zero!(delta_v) + + # 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_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 + +function particle_shifting!(u_ode, ::ParticleShiftingTechnique, 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 + +""" + 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. + +!!! 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{T <: Real} <: AbstractShiftingTechnique + background_pressure::T +end + +@inline function dv_shifting(::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_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_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/transport_velocity.jl b/src/schemes/fluid/transport_velocity.jl deleted file mode 100644 index fce7375dd..000000000 --- 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/rhs.jl b/src/schemes/fluid/weakly_compressible_sph/rhs.jl index af088e0a1..b9cd27f07 100644 --- a/src/schemes/fluid/weakly_compressible_sph/rhs.jl +++ b/src/schemes/fluid/weakly_compressible_sph/rhs.jl @@ -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), - 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) + # Extra terms in the momentum equation when using a shifting technique + dv_tvf = 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) dv_surface_tension = surface_tension_correction * surface_tension_force(surface_tension_a, surface_tension_b, diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index a5f56499b..dda2ead5f 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, @@ -36,8 +36,9 @@ See [Weakly Compressible SPH](@ref wcsph) for more details on the method. 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). - `correction`: Correction method used for this system. (default: no correction, see [Corrections](@ref corrections)) @@ -59,7 +60,7 @@ 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} <: FluidSystem{NDIMS} initial_condition :: IC mass :: MA # Array{ELTYPE, 1} pressure :: P # Array{ELTYPE, 1} @@ -71,7 +72,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 +90,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 +148,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 +163,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 +178,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 +209,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 @@ -278,7 +279,7 @@ 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 function update_quantities!(system::WeaklyCompressibleSPHSystem, v, u, v_ode, u_ode, semi, t) @@ -316,7 +317,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, diff --git a/test/examples/examples_fluid.jl b/test/examples/examples_fluid.jl index ede9e6bbf..a84a69317 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=ParticleShiftingTechnique(), + 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(50_000.0), + extra_callback=UpdateCallback()) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end @@ -283,8 +295,9 @@ joinpath(examples_dir(), "fluid", "periodic_channel_2d.jl"), tspan=(0.0, 0.2), - extra_callback=ParticleShiftingCallback(), - pressure_acceleration=tensile_instability_control) + shifting_technique=ParticleShiftingTechnique(), + pressure_acceleration=tensile_instability_control, + extra_callback=UpdateCallback()) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol, semi) == 0 end diff --git a/test/systems/edac_system.jl b/test/systems/edac_system.jl index 3d8cf9bab..ed37b9ea5 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 │ @@ -225,7 +225,7 @@ 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/wcsph_system.jl b/test/systems/wcsph_system.jl index cf6020d06..99f7152db 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] │