Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ end

## Collide forces

Neighbor lists and collide forces are configured with a `CollisionPolicy`.
Neighbor lists and collide forces are configured with a `CollidePolicy`.
The policy decides which objects and pairs participate in the neighbor lists
(`filter_object`, `filter_pair`), how per-object parameters combine into
per-pair parameters (`mix_params`), and which force law applies to each edge
(`nl_edge_forces!`).

`DefaultCollisionPolicy` stores a stiffness and collision layer masks per
`DefaultCollidePolicy` stores a stiffness and collision layer masks per
object, and applies a soft repulsive potential `E = k/2 * (L - d)²` when two
objects overlap, where `L` is the sum of the two radii and `d` is the closest
distance between them. The pair stiffness `k` mixes the two per-object
Expand All @@ -53,7 +53,7 @@ stiffnesses like springs in series, `k = k₁*k₂/(k₁ + k₂)`, so the two
```julia
using SimplexCellLists, StaticArrays

policy = DefaultCollisionPolicy()
policy = DefaultCollidePolicy()

# Two overlapping spheres: radius 0.5, centers 0.5 apart
pos = [SA[0.0, 0.0, 0.0], SA[0.5, 0.0, 0.0]]
Expand All @@ -77,7 +77,7 @@ triangles, `no_collide_pairs` exclusions, and a `skin` distance so lists can be
reused across steps. `setup_neighbors_naive!` is a reference implementation of
`setup_neighbors_sort_sweep!` useful for testing.

A custom policy subtypes `CollisionPolicy{ObjectParams, PairParams}` with its
A custom policy subtypes `CollidePolicy{ObjectParams, PairParams}` with its
own parameter types and methods for `filter_object`, `filter_pair`, and
`mix_params`, and can define its own `nl_edge_forces!` methods to change the
force law.
2 changes: 1 addition & 1 deletion benchmark/ring-neighbor-bench.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ for k in 1:length(clines)-1
end
end

policy = DefaultCollisionPolicy()
policy = DefaultCollidePolicy()
inputs = NeighborListInputs(policy;
clines,
c_radius = fill(radius, length(clines)),
Expand Down
12 changes: 6 additions & 6 deletions src/SimplexCellLists.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,23 @@ export CollidePairs
export N_COLLIDE_PAIRS
export empty_no_collide_pairs
export CollideObjectTypes
public CollisionPolicy
public CollidePolicy
public filter_object
public filter_pair
public mix_params
export DefaultObjectParams
export DefaultPairParams
export DefaultCollisionPolicy
export DefaultCollidePolicy
export NeighborListInputs
export NeighborLists
export is_neighbor_list_subset
export setup_neighbors_naive!
export setup_neighbors_sort_sweep!

# Precompile the neighbor list builds for the default policy
precompile(NeighborLists, (DefaultCollisionPolicy,))
let NL = NeighborLists{DefaultCollisionPolicy, DefaultPairParams},
Inputs = NeighborListInputs{DefaultCollisionPolicy, DefaultObjectParams}
precompile(NeighborLists, (DefaultCollidePolicy,))
let NL = NeighborLists{DefaultCollidePolicy, DefaultPairParams},
Inputs = NeighborListInputs{DefaultCollidePolicy, DefaultObjectParams}
for T in (Float32, Float64)
for Pos in (
Vector{SVector{3, T}},
Expand All @@ -132,7 +132,7 @@ export collide_forces!
public nl_edge_forces!

# Precompile the collide forces for the default policy
let NL = NeighborLists{DefaultCollisionPolicy, DefaultPairParams}
let NL = NeighborLists{DefaultCollidePolicy, DefaultPairParams}
for FE in (ForceEnergyFloat64, ForceEnergyFixedPoint{30, 30}, ForceNoEnergyFixedPoint{30})
for T in (Float32, Float64)
for Pos in (
Expand Down
22 changes: 11 additions & 11 deletions src/collide-forces.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""
nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge, policy::CollisionPolicy, calc_type)::Nothing
nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge, policy::CollidePolicy, calc_type)::Nothing

Add the collide force and energy of a single neighbor-list edge to `force_energy`.

Methods dispatch on the index part types of `edge` and on `policy`, so a custom
`CollisionPolicy` with its own `PairParams` can define its own force laws.
`CollidePolicy` with its own `PairParams` can define its own force laws.
"""
function nl_edge_forces! end

Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{PointIdxPart, PointIdxPart, DefaultPairParams}, policy::DefaultCollisionPolicy, calc_type::T) where T
Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{PointIdxPart, PointIdxPart, DefaultPairParams}, policy::DefaultCollidePolicy, calc_type::T) where T
x1 = pos[edge.a.i]
x2 = pos[edge.b.i]
d = map(calc_type, x1 - x2)
Expand All @@ -27,7 +27,7 @@ Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos
nothing
end

Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{PointIdxPart, CLineIdxPart, DefaultPairParams}, policy::DefaultCollisionPolicy, calc_type::T) where T
Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{PointIdxPart, CLineIdxPart, DefaultPairParams}, policy::DefaultCollidePolicy, calc_type::T) where T
x1 = pos[edge.a.i]
y1 = pos[edge.b.i]
y2 = pos[edge.b.i + UInt32(1)]
Expand All @@ -51,7 +51,7 @@ Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos
nothing
end

Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{PointIdxPart, LineIdxPart, DefaultPairParams}, policy::DefaultCollisionPolicy, calc_type::T) where T
Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{PointIdxPart, LineIdxPart, DefaultPairParams}, policy::DefaultCollidePolicy, calc_type::T) where T
x1 = pos[edge.a.i]
y1 = pos[edge.b.i]
y2 = pos[edge.b.j]
Expand Down Expand Up @@ -196,7 +196,7 @@ Force on P0 can be computed as -(fp1+fq0+fq1)
end
end

Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{CLineIdxPart, CLineIdxPart, DefaultPairParams}, policy::DefaultCollisionPolicy, calc_type::T) where T
Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{CLineIdxPart, CLineIdxPart, DefaultPairParams}, policy::DefaultCollidePolicy, calc_type::T) where T
P0 = pos[edge.a.i]
P1 = pos[edge.a.i + UInt32(1)]
Q0 = pos[edge.b.i]
Expand All @@ -216,7 +216,7 @@ Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos
nothing
end

Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{CLineIdxPart, LineIdxPart, DefaultPairParams}, policy::DefaultCollisionPolicy, calc_type::T) where T
Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{CLineIdxPart, LineIdxPart, DefaultPairParams}, policy::DefaultCollidePolicy, calc_type::T) where T
P0 = pos[edge.a.i]
P1 = pos[edge.a.i + UInt32(1)]
Q0 = pos[edge.b.i]
Expand All @@ -236,7 +236,7 @@ Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos
nothing
end

Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{LineIdxPart, LineIdxPart, DefaultPairParams}, policy::DefaultCollisionPolicy, calc_type::T) where T
Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{LineIdxPart, LineIdxPart, DefaultPairParams}, policy::DefaultCollidePolicy, calc_type::T) where T
P0 = pos[edge.a.i]
P1 = pos[edge.a.j]
Q0 = pos[edge.b.i]
Expand Down Expand Up @@ -265,7 +265,7 @@ Based on https://www.geometrictools.com/Documentation/DistancePoint3Triangle3.pd
// https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
// Version: 6.0.2022.01.06
=#
Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{PointIdxPart, TriangleIdxPart, DefaultPairParams}, policy::DefaultCollisionPolicy, calc_type::T) where T
Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos, edge::NeighborListEdge{PointIdxPart, TriangleIdxPart, DefaultPairParams}, policy::DefaultCollidePolicy, calc_type::T) where T
P = pos[edge.a.i]
B = pos[edge.b.i]
E0 = map(calc_type, pos[edge.b.j] - B)
Expand Down Expand Up @@ -334,7 +334,7 @@ Base.@propagate_inbounds function nl_edge_forces!(force_energy::ForceEnergy, pos
nothing
end

function nl_forces!(force_energy::ForceEnergy, pos, nl, policy::CollisionPolicy, calc_type::T, chunk, nthreads) where T
function nl_forces!(force_energy::ForceEnergy, pos, nl, policy::CollidePolicy, calc_type::T, chunk, nthreads) where T
# Split 1:length(nl) into nthreads contiguous chunks with sizes differing
# by at most one; chunks past the end are empty.
q, r = divrem(length(nl), nthreads)
Expand All @@ -353,7 +353,7 @@ Add the collide forces and energy of every neighbor-list edge in `s` to
`force_energy`, doing the calculations in the floating-point type `calc_type`.

Each edge's contribution is computed by [`nl_edge_forces!`](@ref), so a custom
`CollisionPolicy` can change the force laws.
`CollidePolicy` can change the force laws.

For multithreading, split the work into `nthreads` chunks: thread `t` calls
`collide_forces!` with `chunk=t` and the same `nthreads`, accumulating into
Expand Down
36 changes: 18 additions & 18 deletions src/neighbor-lists.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,16 @@ Dispatch token and global configuration for neighbor-list construction.

`ObjectParams` is per-object. `PairParams` is per-pair.
"""
abstract type CollisionPolicy{ObjectParams, PairParams} end
abstract type CollidePolicy{ObjectParams, PairParams} end

object_params_type(::Type{<:CollisionPolicy{O, P}}) where {O, P} = O
pair_params_type(::Type{<:CollisionPolicy{O, P}}) where {O, P} = P
object_params_type(policy::CollisionPolicy) = object_params_type(typeof(policy))
pair_params_type(policy::CollisionPolicy) = pair_params_type(typeof(policy))
object_params_type(::Type{<:CollidePolicy{O, P}}) where {O, P} = O
pair_params_type(::Type{<:CollidePolicy{O, P}}) where {O, P} = P
object_params_type(policy::CollidePolicy) = object_params_type(typeof(policy))
pair_params_type(policy::CollidePolicy) = pair_params_type(typeof(policy))

"""
filter_object(
policy::CollisionPolicy{ObjectParams, PairParams},
policy::CollidePolicy{ObjectParams, PairParams},
object_params::ObjectParams,
object_type::CollideObjectTypes,
object_index::UInt32,
Expand All @@ -121,7 +121,7 @@ function filter_object end

"""
filter_pair(
policy::CollisionPolicy{ObjectParams, PairParams},
policy::CollidePolicy{ObjectParams, PairParams},
a::ObjectParams,
b::ObjectParams,
object_type_a::CollideObjectTypes,
Expand All @@ -144,7 +144,7 @@ function filter_pair end

"""
mix_params(
policy::CollisionPolicy{ObjectParams, PairParams},
policy::CollidePolicy{ObjectParams, PairParams},
a::ObjectParams,
b::ObjectParams,
object_type_a::CollideObjectTypes,
Expand All @@ -169,7 +169,7 @@ struct DefaultPairParams
k::Float32
end

Base.@kwdef struct DefaultCollisionPolicy <: CollisionPolicy{
Base.@kwdef struct DefaultCollidePolicy <: CollidePolicy{
DefaultObjectParams,
DefaultPairParams,
}
Expand All @@ -178,7 +178,7 @@ Base.@kwdef struct DefaultCollisionPolicy <: CollisionPolicy{
end

@inline function filter_object(
::DefaultCollisionPolicy,
::DefaultCollidePolicy,
params::DefaultObjectParams,
object_type::CollideObjectTypes,
object_index::UInt32,
Expand All @@ -192,21 +192,21 @@ end

Check if two objects can collide based on their collision layers and no-collide masks.

Each object has `collision_layers` (which layers it is on) and `no_collide_mask`
Each object has `layers` (which layers it is on) and `no_collide_mask`
(which layers it will not collide with). Two objects cannot collide if either one
has disabled collisions with the other's layer:

no_collide = (A.layers & B.no_collide_mask) ≠ 0 OR (B.layers & A.no_collide_mask) ≠ 0

Default `collision_layers = UInt32(1)` and `no_collide_mask = UInt32(0)` (collide with all layers),
Default `layers = UInt32(1)` and `no_collide_mask = UInt32(0)` (collide with all layers),
so all objects collide by default.
"""
@inline function can_collide(layers_a::UInt32, no_collide_mask_a::UInt32, layers_b::UInt32, no_collide_mask_b::UInt32)::Bool
iszero(layers_a & no_collide_mask_b) && iszero(layers_b & no_collide_mask_a)
end

@inline function filter_pair(
::DefaultCollisionPolicy,
::DefaultCollidePolicy,
a::DefaultObjectParams,
b::DefaultObjectParams,
object_type_a::CollideObjectTypes,
Expand All @@ -220,7 +220,7 @@ end
end

@inline function mix_params(
::DefaultCollisionPolicy,
::DefaultCollidePolicy,
a::DefaultObjectParams,
b::DefaultObjectParams,
object_type_a::CollideObjectTypes,
Expand Down Expand Up @@ -250,7 +250,7 @@ in the reverse order are silently ignored:
`CollideLine_Line` excludes `line_index_a => line_index_b` where
`line_index_a < line_index_b`.
"""
Base.@kwdef mutable struct NeighborListInputs{Policy <: CollisionPolicy, ObjectParams}
Base.@kwdef mutable struct NeighborListInputs{Policy <: CollidePolicy, ObjectParams}
policy::Policy

points::Vector{PointIdxPart} = PointIdxPart[]
Expand Down Expand Up @@ -287,7 +287,7 @@ Base.@kwdef mutable struct NeighborListInputs{Policy <: CollisionPolicy, ObjectP
nthreads::Int = 1
end

function NeighborListInputs(policy::Policy; kwargs...) where {Policy <: CollisionPolicy}
function NeighborListInputs(policy::Policy; kwargs...) where {Policy <: CollidePolicy}
ObjectParams = object_params_type(policy)
NeighborListInputs{Policy, ObjectParams}(; policy, kwargs...)
end
Expand Down Expand Up @@ -343,7 +343,7 @@ function swap_remove_active!(a::ActiveSoA, k::Integer)::UInt32
moved
end

mutable struct NeighborLists{Policy <: CollisionPolicy, PairParams}
mutable struct NeighborLists{Policy <: CollidePolicy, PairParams}
policy::Policy
PPNL::Vector{NeighborListEdge{PointIdxPart, PointIdxPart, PairParams}}
PCNL::Vector{NeighborListEdge{PointIdxPart, CLineIdxPart, PairParams}}
Expand All @@ -353,7 +353,7 @@ mutable struct NeighborLists{Policy <: CollisionPolicy, PairParams}
CLNL::Vector{NeighborListEdge{CLineIdxPart, LineIdxPart, PairParams}}
LLNL::Vector{NeighborListEdge{LineIdxPart, LineIdxPart, PairParams}}
end
function NeighborLists(policy::Policy) where {Policy <: CollisionPolicy}
function NeighborLists(policy::Policy) where {Policy <: CollidePolicy}
PairParams = pair_params_type(policy)
NeighborLists{Policy, PairParams}(
policy,
Expand Down
4 changes: 2 additions & 2 deletions test/test-collide-forces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Error if the collide force and energy violates a property
`a` and `b` are vectors of three vectors
"""
function check_collide_props(a, b)
policy = DefaultCollisionPolicy()
policy = DefaultCollidePolicy()
positions = SVector{3, Float64}[]
a_i, b_i = map((a,b)) do y
local idxs = UInt32[]
Expand Down Expand Up @@ -178,7 +178,7 @@ end
end

@testset "collide_forces! integration" begin
policy = DefaultCollisionPolicy()
policy = DefaultCollidePolicy()
# 3×3×4 grid, spacing 0.4: with radius 0.3 overlaps are guaranteed, but
# no two objects touch exactly, so all forces stay finite
positions = [0.4*SA[mod(i-1, 3), mod((i-1)÷3, 3), (i-1)÷9] for i in 1:30]
Expand Down
2 changes: 1 addition & 1 deletion test/test-neighbor-lists.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ using StaticArrays
using Random

@testset "neighborlists" begin
policy = DefaultCollisionPolicy()
policy = DefaultCollidePolicy()
@testset "Naive vs Sort-Sweep consistency" begin

for trial in 1:2000
Expand Down
Loading