Skip to content

Commit 3d2fb3d

Browse files
committed
Merge branch 'rigid_structures_v2' into collision
2 parents ec06968 + 8970a9b commit 3d2fb3d

File tree

36 files changed

+686
-343
lines changed

36 files changed

+686
-343
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ used in the Julia ecosystem. Notable changes will be documented in this file for
99
### Features
1010

1111
- Added `StateEquationAdaptiveCole` an adaptive sound speed version of the Cole state equation (#875)
12+
- Added `RigidSPHSystem` that supports rigid body dynamics for FSI (#1076)
1213

14+
### Validation cases
15+
- Added new validation case hydrostatic_water_column (#724)
1316

1417
## Version 0.4.3
1518

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ It offers intuitive configuration, robust pre- and post-processing, and vendor-a
3333
Implicit Incompressible SPH (IISPH)
3434
- Models: Surface Tension, Open Boundaries
3535
- Solid-body mechanics
36-
- Methods: Total Lagrangian SPH (TLSPH), Discrete Element Method (DEM)
36+
- Methods: Total Lagrangian SPH (TLSPH), Discrete Element Method (DEM), Rigid Body Dynamics (RBD)
3737
- Fluid-Structure Interaction
3838
- Particle sampling of complex geometries from `.stl` and `.asc` files.
3939
- Output formats:

docs/make.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ makedocs(sitename="TrixiParticles.jl",
120120
"implicit_incompressible_sph.md")
121121
],
122122
"Discrete Element Method (Solid)" => joinpath("systems", "dem.md"),
123+
"Rigid Body SPH (Rigid Structure)" => joinpath("systems",
124+
"rigid_body_sph.md"),
123125
"Total Lagrangian SPH (Elastic Structure)" => joinpath("systems",
124126
"total_lagrangian_sph.md"),
125127
"Boundary" => joinpath("systems", "boundary.md")

docs/src/systems/rigid_body_sph.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# [Rigid Body SPH](@id rigid_body_sph)
2+
3+
Rigid bodies in TrixiParticles.jl are represented by particles whose motion is evolved
4+
with rigid-body translation and rotation. This allows fluid-structure interaction while
5+
keeping the structure kinematics rigid.
6+
7+
## API
8+
9+
```@autodocs
10+
Modules = [TrixiParticles]
11+
Pages = [joinpath("schemes", "structure", "rigid_body_sph", "system.jl")]
12+
```

examples/fsi/falling_rotating_rigid_squares_2d.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ square2_angular_velocity = -7.5
6262
square1 = RectangularShape(structure_particle_spacing,
6363
(square1_nparticles_side, square1_nparticles_side),
6464
square1_bottom_left,
65-
density=material_properties.wood.density,
66-
angular_velocity=square1_angular_velocity)
65+
density=material_properties.wood.density)
6766
square2 = RectangularShape(structure_particle_spacing,
6867
(square2_nparticles_side, square2_nparticles_side),
6968
square2_bottom_left,
70-
density=material_properties.steel.density,
71-
angular_velocity=square2_angular_velocity)
69+
density=material_properties.steel.density)
70+
square1 = apply_angular_velocity(square1, square1_angular_velocity)
71+
square2 = apply_angular_velocity(square2, square2_angular_velocity)
7272

7373
# ==========================================================================================
7474
# ==== Fluid

src/TrixiParticles.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ include("io/io.jl")
6666
include("visualization/recipes_plots.jl")
6767

6868
export Semidiscretization, semidiscretize, restart_with!
69-
export InitialCondition
69+
export InitialCondition, apply_angular_velocity
7070
export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangianSPHSystem,
7171
RigidSPHSystem, WallBoundarySystem, DEMSystem, BoundaryDEMSystem, OpenBoundarySystem,
7272
ImplicitIncompressibleSPHSystem

src/general/initial_condition.jl

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
@doc raw"""
22
InitialCondition(; coordinates, density, velocity=zeros(size(coordinates, 1)),
3-
mass=nothing, pressure=0.0, particle_spacing=-1.0,
4-
angular_velocity=nothing)
3+
mass=nothing, pressure=0.0, particle_spacing=-1.0)
54
65
Struct to hold the initial configuration of the particles.
76
@@ -39,15 +38,9 @@ with `TrixiParticles.TriangleMesh` and `TrixiParticles.Polygon` returned by [`lo
3938
- `particle_spacing`: The spacing between the particles. This is a scalar, as the spacing
4039
is assumed to be uniform. This is only needed when using
4140
set operations on the `InitialCondition` or for automatic mass calculation.
42-
- `angular_velocity`: Initial angular velocity `ω` of the shape (not angular momentum).
43-
It adds a rotational contribution to `velocity` around the center of mass of the shape.
44-
If both `velocity` and `angular_velocity` are set, they are added.
45-
In 2D, pass a scalar angular speed in rad/s.
46-
In 3D, pass a vector of length 3: its direction is the rotation axis
47-
(right-hand rule), its norm `|ω|` is the angular speed in rad/s.
48-
The resulting local rotational velocity is `v = ω × r`, i.e., motion in
49-
planes normal to the rotation axis.
50-
By default, angular velocity is zero.
41+
42+
To add a rotational contribution to an existing initial condition, use
43+
[`apply_angular_velocity`](@ref).
5144
5245
# Examples
5346
```jldoctest; output = false
@@ -101,33 +94,28 @@ initial_condition = InitialCondition(; coordinates, velocity=x -> 2x, mass=1.0,
10194
└──────────────────────────────────────────────────────────────────────────────────────────────────┘
10295
```
10396
"""
104-
struct InitialCondition{ELTYPE, C, MATRIX, VECTOR, AV}
97+
struct InitialCondition{ELTYPE, C, MATRIX, VECTOR}
10598
particle_spacing :: ELTYPE
10699
coordinates :: C # Array{coordinates_eltype, 2}
107100
velocity :: MATRIX # Array{ELTYPE, 2}
108101
mass :: VECTOR # Array{ELTYPE, 1}
109102
density :: VECTOR # Array{ELTYPE, 1}
110103
pressure :: VECTOR # Array{ELTYPE, 1}
111-
angular_velocity :: AV
112104
end
113105

114106
# The default constructor needs to be accessible for Adapt.jl to work with this struct.
115107
# See the comments in general/gpu.jl for more details.
116108
function InitialCondition(; coordinates, density, velocity=zeros(size(coordinates, 1)),
117-
mass=nothing, pressure=0.0, particle_spacing=-1.0,
118-
angular_velocity=nothing, apply_angular_velocity=true)
109+
mass=nothing, pressure=0.0, particle_spacing=-1.0)
119110
NDIMS = size(coordinates, 1)
120111

121112
return InitialCondition{NDIMS}(coordinates, velocity, mass, density,
122-
pressure, particle_spacing, angular_velocity;
123-
apply_angular_velocity)
113+
pressure, particle_spacing)
124114
end
125115

126116
# Function barrier to make `NDIMS` static and therefore SVectors type-stable
127117
function InitialCondition{NDIMS}(coordinates, velocity, mass, density,
128-
pressure, particle_spacing,
129-
angular_velocity;
130-
apply_angular_velocity=true) where {NDIMS}
118+
pressure, particle_spacing) where {NDIMS}
131119
if !(particle_spacing isa AbstractFloat)
132120
throw(ArgumentError("particle spacing must be a floating point number. " *
133121
"The type of the particle spacing determines the eltype " *
@@ -137,13 +125,10 @@ function InitialCondition{NDIMS}(coordinates, velocity, mass, density,
137125
# The arguments can all be functions, so use `particle_spacing` for the eltype
138126
ELTYPE = typeof(particle_spacing)
139127
n_particles = size(coordinates, 2)
140-
angular_velocity_ = convert_initial_angular_velocity(angular_velocity, Val(NDIMS),
141-
ELTYPE)
142128

143129
if n_particles == 0
144130
return InitialCondition(particle_spacing, coordinates, zeros(ELTYPE, NDIMS, 0),
145-
zeros(ELTYPE, 0), zeros(ELTYPE, 0), zeros(ELTYPE, 0),
146-
angular_velocity_)
131+
zeros(ELTYPE, 0), zeros(ELTYPE, 0), zeros(ELTYPE, 0))
147132
end
148133

149134
# SVector of coordinates to pass to functions.
@@ -216,14 +201,9 @@ function InitialCondition{NDIMS}(coordinates, velocity, mass, density,
216201
end
217202

218203
velocities_ = ELTYPE.(velocities)
219-
if apply_angular_velocity
220-
add_initial_angular_velocity!(velocities_, coordinates, masses, angular_velocity_,
221-
Val(NDIMS), ELTYPE)
222-
end
223204

224205
return InitialCondition(particle_spacing, coordinates, velocities_,
225-
ELTYPE.(masses), ELTYPE.(densities), ELTYPE.(pressures),
226-
angular_velocity_)
206+
ELTYPE.(masses), ELTYPE.(densities), ELTYPE.(pressures))
227207
end
228208

229209
@inline function convert_initial_angular_velocity(::Nothing, ::Val{NDIMS},
@@ -343,6 +323,33 @@ function add_initial_angular_velocity!(velocities, coordinates, mass,
343323
return velocities
344324
end
345325

326+
@doc raw"""
327+
apply_angular_velocity(initial_condition::InitialCondition, angular_velocity)
328+
329+
Return a new [`InitialCondition`](@ref) whose velocity includes the rotational
330+
contribution `v = ω × r` around the center of mass of `initial_condition`.
331+
332+
In 2D, pass a scalar angular speed in rad/s.
333+
In 3D, pass a vector of length 3 whose direction gives the rotation axis
334+
(right-hand rule) and whose norm `|ω|` gives the angular speed in rad/s.
335+
"""
336+
function apply_angular_velocity(initial_condition::InitialCondition, angular_velocity)
337+
NDIMS = ndims(initial_condition)
338+
ELTYPE = eltype(initial_condition)
339+
angular_velocity_ = convert_initial_angular_velocity(angular_velocity, Val(NDIMS),
340+
ELTYPE)
341+
velocity = copy(initial_condition.velocity)
342+
343+
add_initial_angular_velocity!(velocity, initial_condition.coordinates,
344+
initial_condition.mass, angular_velocity_,
345+
Val(NDIMS), ELTYPE)
346+
347+
return InitialCondition(initial_condition.particle_spacing,
348+
initial_condition.coordinates, velocity,
349+
initial_condition.mass, initial_condition.density,
350+
initial_condition.pressure)
351+
end
352+
346353
function Base.show(io::IO, ic::InitialCondition)
347354
@nospecialize ic # reduce precompilation time
348355

@@ -423,9 +430,7 @@ function Base.union(initial_condition::InitialCondition, initial_conditions...)
423430
pressure = vcat(initial_condition.pressure, ic.pressure[valid_particles])
424431

425432
result = InitialCondition{ndims(ic)}(coordinates, velocity, mass, density, pressure,
426-
particle_spacing,
427-
initial_condition.angular_velocity;
428-
apply_angular_velocity=false)
433+
particle_spacing)
429434

430435
return union(result, Base.tail(initial_conditions)...)
431436
end
@@ -460,9 +465,7 @@ function Base.setdiff(initial_condition::InitialCondition, initial_conditions...
460465
pressure = initial_condition.pressure[valid_particles]
461466

462467
result = InitialCondition{ndims(ic)}(coordinates, velocity, mass, density, pressure,
463-
particle_spacing,
464-
initial_condition.angular_velocity;
465-
apply_angular_velocity=false)
468+
particle_spacing)
466469

467470
return setdiff(result, Base.tail(initial_conditions)...)
468471
end
@@ -496,9 +499,7 @@ function Base.intersect(initial_condition::InitialCondition, initial_conditions.
496499
pressure = initial_condition.pressure[too_close]
497500

498501
result = InitialCondition{ndims(ic)}(coordinates, velocity, mass, density, pressure,
499-
particle_spacing,
500-
initial_condition.angular_velocity;
501-
apply_angular_velocity=false)
502+
particle_spacing)
502503

503504
return intersect(result, Base.tail(initial_conditions)...)
504505
end
@@ -533,8 +534,7 @@ function InitialCondition(sol::ODESolution, system, semi; use_final_velocity=fal
533534
end
534535

535536
return InitialCondition{ndims(ic)}(coordinates, velocity, mass, density, pressure,
536-
ic.particle_spacing, ic.angular_velocity;
537-
apply_angular_velocity=false)
537+
ic.particle_spacing)
538538
end
539539

540540
# Find particles in `coords1` that are closer than `max_distance` to any particle in `coords2`

src/general/neighborhood_search.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ function update_nhs!(neighborhood_search,
285285
end
286286

287287
function update_nhs!(neighborhood_search,
288-
system::OpenBoundarySystem{<:BoundaryModelDynamicalPressureZhang},
288+
system::OpenBoundarySystem,
289289
neighbor::RigidSPHSystem,
290290
u_system, u_neighbor, semi)
291291
update!(neighborhood_search,
@@ -306,7 +306,7 @@ end
306306

307307
function update_nhs!(neighborhood_search,
308308
system::RigidSPHSystem,
309-
neighbor::OpenBoundarySystem{<:BoundaryModelDynamicalPressureZhang},
309+
neighbor::OpenBoundarySystem,
310310
u_system, u_neighbor, semi)
311311
update!(neighborhood_search,
312312
current_coordinates(u_system, system),
@@ -317,14 +317,14 @@ end
317317
# -- Open boundary combinations that are never used
318318
function update_nhs!(neighborhood_search,
319319
system::OpenBoundarySystem,
320-
neighbor::Union{TotalLagrangianSPHSystem, RigidSPHSystem},
320+
neighbor::TotalLagrangianSPHSystem,
321321
u_system, u_neighbor, semi)
322322
# Don't update. This NHS is never used.
323323
return neighborhood_search
324324
end
325325

326326
function update_nhs!(neighborhood_search,
327-
system::Union{TotalLagrangianSPHSystem, RigidSPHSystem},
327+
system::TotalLagrangianSPHSystem,
328328
neighbor::OpenBoundarySystem,
329329
u_system, u_neighbor, semi)
330330
# Don't update. This NHS is never used.

src/general/semidiscretization.jl

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -709,16 +709,26 @@ end
709709
check_configuration(system::AbstractSystem, systems, nhs) = nothing
710710

711711
function check_system_color(systems)
712-
if any(system isa AbstractFluidSystem && !(system isa ParticlePackingSystem) &&
713-
!isnothing(system.surface_tension)
714-
for system in systems)
712+
requires_color_check = any(systems) do system
713+
system isa AbstractFluidSystem || return false
714+
system isa ParticlePackingSystem && return false
715715

716-
# System indices of all systems that are either a fluid or a boundary system
717-
system_ids = findall(system isa Union{AbstractFluidSystem, WallBoundarySystem}
718-
for system in systems)
716+
return !isnothing(system.surface_tension) ||
717+
system.surface_normal_method isa ColorfieldSurfaceNormal
718+
end
719+
720+
if requires_color_check
721+
722+
# Systems that contribute to the colorfield/contact logic.
723+
system_ids = findall(system -> (system isa AbstractFluidSystem &&
724+
!(system isa ParticlePackingSystem)) ||
725+
system isa WallBoundarySystem ||
726+
system isa
727+
RigidSPHSystem{<:BoundaryModelDummyParticles},
728+
systems)
719729

720730
if length(system_ids) > 1 && sum(i -> systems[i].cache.color, system_ids) == 0
721-
throw(ArgumentError("If a surface tension model is used the values of at least one system needs to have a color different than 0."))
731+
throw(ArgumentError("If `ColorfieldSurfaceNormal` or a surface tension model is used, at least one participating system must have a color different from 0."))
722732
end
723733
end
724734
end

src/io/io.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ function add_system_data!(system_data, system::RigidSPHSystem)
125125
system_data["system_type"] = type2string(system)
126126
system_data["particle_spacing"] = particle_spacing(system, 1)
127127
system_data["acceleration"] = system.acceleration
128+
system_data["adhesion_coefficient"] = system.adhesion_coefficient
129+
system_data["color"] = system.cache.color
128130
add_system_data!(system_data, system.boundary_model)
129131
add_system_data!(system_data, system.boundary_contact_model)
130132
end

0 commit comments

Comments
 (0)