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
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
*.jl.*.cov
*.jl.cov
*.jl.mem
/Manifest.toml
/test/Manifest.toml
/Manifest*.toml
/test/Manifest*.toml
2 changes: 2 additions & 0 deletions src/SimplexCellLists.jl
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,6 @@ let NL = NeighborLists{DefaultCollidePolicy, DefaultPairParams}
end
end

include("show.jl")

end
169 changes: 169 additions & 0 deletions src/show.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# `text/plain` `show` methods for the container types, printing a summary
# instead of the default dump of every field.

# Print `n` followed by the singular or plural form of a noun.
function _print_count(io::IO, n::Integer, one::AbstractString, many::AbstractString)
print(io, n, " ", isone(n) ? one : many)
end

function Base.show(io::IO, ::MIME"text/plain", pcl::PointCellList)
lens = Iterators.map(c -> c.len, pcl.cells)
summary(io, pcl)
print(io, " with ")
_print_count(io, sum(lens; init=0), "point", "points")
print(io, ":\n size: ", join(pcl.size, "×"), " cells")
print(io, "\n grid spacing: ")
show(IOContext(io, :compact => true), 2*inv(pcl.inv_half_grid_spacing))
print(io, "\n most points in one cell: ", maximum(lens; init=0))
end

function Base.show(io::IO, ::MIME"text/plain", scl::LineSegCellList)
lens = Iterators.map(c -> c.len, scl.cells)
# Each line segment has exactly one entry with the `is_end` flag.
nsegs = sum(scl.cells; init=0) do c
count(cell_line_seg_is_end, view(c.list, 1:c.len))
end
summary(io, scl)
print(io, " with ")
_print_count(io, nsegs, "line segment", "line segments")
print(io, ":\n size: ", join(scl.size, "×"), " cells")
print(io, "\n grid spacing: ")
show(IOContext(io, :compact => true), 2*inv(scl.inv_half_grid_spacing))
print(io, "\n cell entries: ", sum(lens; init=0))
print(io, "\n most entries in one cell: ", maximum(lens; init=0))
end

# Print the forces of all beads as type `F`, one bead per line, for the
# `text/plain` `show` of accumulators that store forces. Like printing a
# `Vector`, a limited `io` shows only the first and last beads so the output
# fits the display, including the `header_lines` already printed.
function _show_bead_forces(io::IO, fe::ForceEnergy, ::Type{F}, header_lines::Int) where F
n = get_nbeads(fe)
iszero(n) && return
print(io, "\n forces:")
max_lines = if get(io, :limit, false)
# Leave 5 lines: the "forces:" line, the "⋮" line, and 3 free lines
# for the prompt, the same as a long `Vector` leaves free.
max(displaysize(io)[1] - header_lines - 5, 2)
else
n
end
beads = if n ≤ max_lines
(1:n,)
else
(1:cld(max_lines, 2), n-fld(max_lines, 2)+1:n)
end
width = ndigits(n)
# The `:typeinfo` stops a `Rational{BigInt}[...]` style prefix.
compact_io = IOContext(io, :compact => true, :typeinfo => SVector{3, F})
for (k, range) in enumerate(beads)
isone(k) || print(io, "\n ", lpad("⋮", width))
for i in range
print(io, "\n ", lpad(i, width), ": ")
show(compact_io, get_bead_force(fe, i, F))
end
end
end

function Base.show(io::IO, ::MIME"text/plain", fe::ForceEnergyT{T}) where T
summary(io, fe)
print(io, " with ")
_print_count(io, get_nbeads(fe), "bead", "beads")
print(io, ":\n energy: ")
show(IOContext(io, :compact => true), fe.energy)
_show_bead_forces(io, fe, T, 2)
end

function Base.show(io::IO, ::MIME"text/plain", fe::ForceEnergyFixedPoint)
summary(io, fe)
print(io, " with ")
_print_count(io, get_nbeads(fe), "bead", "beads")
print(io, ":\n energy: ")
show(IOContext(io, :compact => true), get_energy(fe))
_show_bead_forces(io, fe, Float64, 2)
end

function Base.show(io::IO, ::MIME"text/plain", fe::ForceNoEnergyFixedPoint)
summary(io, fe)
print(io, " with ")
_print_count(io, get_nbeads(fe), "bead", "beads")
print(io, " and no energy:")
_show_bead_forces(io, fe, Float64, 1)
end

function Base.show(io::IO, ::MIME"text/plain", fe::DebugForceEnergy{T}) where T
summary(io, fe)
print(io, " with ")
_print_count(io, get_nbeads(fe), "bead", "beads")
print(io, ":\n ")
_print_count(io, length(fe.added_energy), "energy contribution", "energy contributions")
print(io, ", ")
_print_count(io, length(fe.added_bead_force), "force contribution", "force contributions")
print(io, "\n energy: ")
show(IOContext(io, :compact => true), get_energy(fe, T))
_show_bead_forces(io, fe, T, 3)
end

function Base.show(io::IO, ::MIME"text/plain", fe::NullForceEnergy)
summary(io, fe)
print(io, " with ")
_print_count(io, get_nbeads(fe), "bead", "beads")
print(io, ", discarding all forces and energy")
end

function Base.show(io::IO, ::MIME"text/plain", inputs::NeighborListInputs)
compact_io = IOContext(io, :compact => true)
summary(io, inputs)
print(io, ":\n policy: ")
show(compact_io, inputs.policy)
# Name and lengths of the object, radius, and params vectors of each
# object type, which must match for the inputs to be valid.
objects = (
("points", "p", length(inputs.points), length(inputs.p_radius), length(inputs.p_params)),
("clines", "c", length(inputs.clines), length(inputs.c_radius), length(inputs.c_params)),
("lines", "l", length(inputs.lines), length(inputs.l_radius), length(inputs.l_params)),
("line_points", "lp", length(inputs.line_points), length(inputs.lp_radius), length(inputs.lp_params)),
("triangles", "t", length(inputs.triangles), length(inputs.t_radius), length(inputs.t_params)),
("triangle_lines", "tl", length(inputs.triangle_lines), length(inputs.tl_radius), length(inputs.tl_params)),
("triangle_points", "tp", length(inputs.triangle_points), length(inputs.tp_radius), length(inputs.tp_params)),
)
name_width = maximum(o -> length(o[1]), objects) + 1
count_width = maximum(o -> ndigits(o[3]), objects)
for (name, prefix, n, n_radius, n_params) in objects
print(io, "\n ", rpad(name * ":", name_width), " ", lpad(n, count_width))
if n_radius != n || n_params != n
print(io, ", but ", n_radius, " ", prefix, "_radius and ", n_params, " ", prefix, "_params")
end
end
print(io, "\n no_collide_pairs: ", sum(length, inputs.no_collide_pairs))
for pair_type in instances(CollidePairs)
n = length(inputs.no_collide_pairs[Int(pair_type)])
iszero(n) || print(io, "\n ", pair_type, ": ", n)
end
print(io, "\n skin: ")
show(compact_io, inputs.skin)
print(io, "\n nthreads: ", inputs.nthreads)
end

function Base.show(io::IO, ::MIME"text/plain", s::NeighborLists)
lists = (
("PPNL", "point–point", length(s.PPNL)),
("PCNL", "point–cline", length(s.PCNL)),
("PLNL", "point–line", length(s.PLNL)),
("PTNL", "point–triangle", length(s.PTNL)),
("CCNL", "cline–cline", length(s.CCNL)),
("CLNL", "cline–line", length(s.CLNL)),
("LLNL", "line–line", length(s.LLNL)),
)
summary(io, s)
print(io, " with ")
_print_count(io, sum(l -> l[3], lists), "edge", "edges")
print(io, ":\n policy: ")
show(IOContext(io, :compact => true), s.policy)
desc_width = maximum(l -> textwidth(l[2]), lists) + 1
count_width = maximum(l -> ndigits(l[3]), lists)
for (name, desc, n) in lists
# `rpad` pads by `textwidth`, so the en dashes align.
print(io, "\n ", name, " ", rpad(desc * ":", desc_width), " ", lpad(n, count_width))
end
end
84 changes: 84 additions & 0 deletions test/test-force-energy.jl
Original file line number Diff line number Diff line change
Expand Up @@ -335,5 +335,89 @@ using StaticArrays
@test_throws MethodError get_force!(fe, zeros(SVector{3, Float64}, nbeads))
@test_throws MethodError get_force(fe)
end

@testset "show" begin
plain(x; kwargs...) = repr(MIME"text/plain"(), x; kwargs...)
fe = ForceEnergyFloat64(3)
add_bead_force!(fe, 2, SA[1.0, -2/3, 0.0])
add_energy!(fe, 0.5)
@test plain(fe) == """
ForceEnergyFloat64 with 3 beads:
energy: 0.5
forces:
1: [0.0, 0.0, 0.0]
2: [1.0, -0.666667, 0.0]
3: [0.0, 0.0, 0.0]"""
@test plain(ForceEnergyFloat32(0)) == """
ForceEnergyFloat32 with 0 beads:
energy: 0.0"""

# A limited display shows only the first and last beads.
limited = IOContext(devnull, :limit => true, :displaysize => (12, 80))
@test plain(ForceEnergyFloat64(1000); context=limited) == """
ForceEnergyFloat64 with 1000 beads:
energy: 0.0
forces:
1: [0.0, 0.0, 0.0]
2: [0.0, 0.0, 0.0]
3: [0.0, 0.0, 0.0]
⋮
999: [0.0, 0.0, 0.0]
1000: [0.0, 0.0, 0.0]"""

# Fixed point shows converted values, not the raw integers.
fx = ForceEnergyFixedPoint{30, 30}(2)
add_bead_force!(fx, 1, SA[0.5, 0.25, -1.0])
add_energy!(fx, 2.5)
@test plain(fx) == """
ForceEnergyFixedPoint{30, 30} with 2 beads:
energy: 2.5
forces:
1: [0.5, 0.25, -1.0]
2: [0.0, 0.0, 0.0]"""

nfx = ForceNoEnergyFixedPoint{30}(1)
add_bead_force!(nfx, 1, SA[0.5, 0.25, -1.0])
@test plain(nfx) == """
ForceNoEnergyFixedPoint{30} with 1 bead and no energy:
forces:
1: [0.5, 0.25, -1.0]"""
# With one header line, one more bead fits than above.
@test plain(ForceNoEnergyFixedPoint{30}(1000); context=limited) == """
ForceNoEnergyFixedPoint{30} with 1000 beads and no energy:
forces:
1: [0.0, 0.0, 0.0]
2: [0.0, 0.0, 0.0]
3: [0.0, 0.0, 0.0]
⋮
998: [0.0, 0.0, 0.0]
999: [0.0, 0.0, 0.0]
1000: [0.0, 0.0, 0.0]"""

dfe = DebugForceEnergy{Rational{Int}}(2)
add_bead_force!(dfe, 1, SA[1//3, 0, 0])
add_bead_force!(dfe, 1, SA[1//3, 0, 0])
add_energy!(dfe, 1//7)
@test plain(dfe) == """
DebugForceEnergy{Rational{Int64}} with 2 beads:
1 energy contribution, 2 force contributions
energy: 1//7
forces:
1: [2//3, 0, 0]
2: [0, 0, 0]"""
# With three header lines, one less bead fits than above.
@test plain(DebugForceEnergy{Float64}(1000); context=limited) == """
DebugForceEnergy{Float64} with 1000 beads:
0 energy contributions, 0 force contributions
energy: 0.0
forces:
1: [0.0, 0.0, 0.0]
2: [0.0, 0.0, 0.0]
⋮
999: [0.0, 0.0, 0.0]
1000: [0.0, 0.0, 0.0]"""

@test plain(NullForceEnergy(1)) == "NullForceEnergy with 1 bead, discarding all forces and energy"
end
end
nothing
17 changes: 17 additions & 0 deletions test/test-linesegcelllist.jl
Original file line number Diff line number Diff line change
Expand Up @@ -369,5 +369,22 @@ end
end
end
end

@testset "show" begin
scl = LineSegCellList{Int,Float32}((4,4,4), 1f0)
# Crosses x = -1, 0, and 1, so has entries in 4 cells.
cell_line_seg_add!(scl, SA[-1.5f0, 0.5f0, 0.5f0], SA[1.5f0, 0.5f0, 0.5f0], 1)
# Inside one of those cells.
cell_line_seg_add!(scl, SA[0.2f0, 0.5f0, 0.5f0], SA[0.3f0, 0.5f0, 0.5f0], 2)
@test repr(MIME"text/plain"(), scl) == """
LineSegCellList{Int64, Float32} with 2 line segments:
size: 4×4×4 cells
grid spacing: 1.0
cell entries: 5
most entries in one cell: 2"""
cell_line_segs_clear!(scl)
cell_line_seg_add!(scl, SA[0.2f0, 0.5f0, 0.5f0], SA[0.3f0, 0.5f0, 0.5f0], 2)
@test startswith(repr(MIME"text/plain"(), scl), "LineSegCellList{Int64, Float32} with 1 line segment:\n")
end
end
nothing
57 changes: 57 additions & 0 deletions test/test-neighbor-lists.jl
Original file line number Diff line number Diff line change
Expand Up @@ -320,5 +320,62 @@ using Random
end
end
end

@testset "show" begin
plain(x) = repr(MIME"text/plain"(), x)
params = DefaultObjectParams(1.0f0, UInt32(1), UInt32(0))
inputs = NeighborListInputs(policy;
points = PointIdxPart.(UInt32.(1:3)),
p_radius = fill(0.3f0, 3),
p_params = fill(params, 3),
skin = 0.25f0,
)
push!(inputs.no_collide_pairs[Int(CollidePoint_Point)], UInt32(1) => UInt32(3))
@test plain(inputs) == """
NeighborListInputs{DefaultCollidePolicy, DefaultObjectParams}:
policy: DefaultCollidePolicy(8.0)
points: 3
clines: 0
lines: 0
line_points: 0
triangles: 0
triangle_lines: 0
triangle_points: 0
no_collide_pairs: 1
CollidePoint_Point: 1
skin: 0.25
nthreads: 1"""

# Only the first two points are close enough to collide.
pos = [SA[0.0, 0.0, 0.0], SA[0.5, 0.0, 0.0], SA[10.0, 0.0, 0.0]]
nl = NeighborLists(policy)
setup_neighbors_naive!(nl, pos, inputs)
@test plain(nl) == """
NeighborLists{DefaultCollidePolicy, DefaultPairParams} with 1 edge:
policy: DefaultCollidePolicy(8.0)
PPNL point–point: 1
PCNL point–cline: 0
PLNL point–line: 0
PTNL point–triangle: 0
CCNL cline–cline: 0
CLNL cline–line: 0
LLNL line–line: 0"""

# Mismatched lengths, which setup rejects, are pointed out.
push!(inputs.c_radius, 1.0f0)
@test contains(plain(inputs), "\n clines: 0, but 1 c_radius and 0 c_params\n")

# The count column widens to fit the largest count.
inputs = NeighborListInputs(policy;
lines = fill(LineIdxPart(UInt32(1), UInt32(2)), 12345),
l_radius = fill(0.3f0, 12345),
l_params = fill(params, 12345),
)
@test contains(plain(inputs), """
points: 0
clines: 0
lines: 12345
""")
end
end
nothing
18 changes: 18 additions & 0 deletions test/test-pointcelllist.jl
Original file line number Diff line number Diff line change
Expand Up @@ -322,5 +322,23 @@ total_entries(pcl::PointCellList) = sum(pcl.cells[i].len for i in 1:prod(pcl.siz
end
end
end

@testset "show" begin
pcl = PointCellList{Int,Float32}((4,3,2), 0.5f0)
cell_point_add!(pcl, SA[0.1f0, 0.1f0, 0.1f0], 1)
cell_point_add!(pcl, SA[0.2f0, 0.1f0, 0.1f0], 2)
cell_point_add!(pcl, SA[-0.6f0, 0.1f0, 0.1f0], 3)
@test repr(MIME"text/plain"(), pcl) == """
PointCellList{Int64, Float32} with 3 points:
size: 4×3×2 cells
grid spacing: 0.5
most points in one cell: 2"""
cell_points_clear!(pcl)
@test repr(MIME"text/plain"(), pcl) == """
PointCellList{Int64, Float32} with 0 points:
size: 4×3×2 cells
grid spacing: 0.5
most points in one cell: 0"""
end
end
nothing
Loading