1"""Regression for the contact-buffer capacity bug (found via peclet-examples/pall-ring-packing).
3TWO independent symptoms of one cause -- every maxContacts-sized collision buffer was sized from
4the particle capacity AT THE TIME a shape or wall was registered, while `demStep` grows the
5particle capacity every step (ghost headroom = numReal + estGhosts + 4096). Buffers frozen at the
6smaller size are an out-of-bounds write, and the narrow phase's boundary contacts -- appended AFTER
7the body-body ones -- are the first thing to fall off the end.
9 A. add_scene_shape never called ensureContactCapacity() at all (every sibling adder does), so a
10 composed-tree particle's contacts against an add_plane floor were dropped and the grains fell
12 B. Even with that call, the buffer did not follow the per-step capacity growth.
14Claim: composed-tree particles dropped onto a plane come to rest ON it. Run at OMP_NUM_THREADS=1.
19assert os.environ.get(
"OMP_NUM_THREADS") ==
"1",
"run with OMP_NUM_THREADS=1"
21from peclet
import geom
23from peclet
import dem
as pdem
25R1, R2, OFF = 0.30, 0.22, 0.35
26b = geom.SceneBuilder()
27db = b.add_union(b.add_leaf(
"sphere", [R1], translation=[-OFF, 0, 0]),
28 b.add_leaf(
"sphere", [R2], translation=[+OFF, 0, 0]))
29sp = scene_particle.build(b, db, bounds=([-0.8, -0.5, -0.5], [0.8, 0.5, 0.5]),
30 n=40, shell_resolution=96, target_shell_points=400)
33s = pdem.Simulation(64)
34s.set_gravity(0.0, -9.81, 0.0)
35s.set_global_scale(1.0)
37s.add_plane(0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
38s.set_material_params(0.2, 0.0, 0.5)
39pos = np.array([[1.4 * i - 3.5, 2.0 + 0.3 * i, 0.0]
for i
in range(N)], dtype=np.float32)
41s.set_shape_ids(np.full(N, sid, dtype=np.int32))
42s.set_inv_mass(np.full(N, 1.0 / sp.mass, dtype=np.float32))
43s.set_inv_inertia(np.tile(np.asarray(sp.inv_inertia_unit, dtype=np.float32), (N, 1)))
47p = np.asarray(s.get_positions())
48ymin = float(p[:, 1].min())
49lo = float(sp.shell[:, 1].min())
50print(
"shell lowest point (body frame) %.4f -> a resting centre cannot go below ~%.3f"
52print(
"lowest particle centre after 2500 steps: %.4f" % ymin)
53print(
"max overlap %.4f contacts %d" % (s.max_overlap(), s.num_contacts()))
54ok = ymin > 0.5 * (-lo)
55print(
"GATE %s [composed-tree particles rest ON the plane, not through it]"
56 % (
"PASS" if ok
else "FAIL"))