flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
contact_capacity_gate.py
Go to the documentation of this file.
1"""Regression for the contact-buffer capacity bug (found via peclet-examples/pall-ring-packing).
2
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.
8
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
11 straight through it.
12 B. Even with that call, the buffer did not follow the per-step capacity growth.
13
14Claim: composed-tree particles dropped onto a plane come to rest ON it. Run at OMP_NUM_THREADS=1.
15"""
16import os
17import numpy as np
18
19assert os.environ.get("OMP_NUM_THREADS") == "1", "run with OMP_NUM_THREADS=1"
20
21from peclet import geom
22from peclet.dem import scene_particle
23from peclet import dem as pdem
24
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)
31
32N = 6
33s = pdem.Simulation(64) # deliberately a SMALL construction capacity
34s.set_gravity(0.0, -9.81, 0.0)
35s.set_global_scale(1.0)
36sid = sp.register(s)
37s.add_plane(0.0, 0.0, 0.0, 0.0, 1.0, 0.0) # floor at y = 0, normal +y
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)
40s.set_positions(pos)
41s.set_shape_ids(np.full(N, sid, dtype=np.int32)) # AFTER set_positions
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)))
44s.set_dt(2e-3)
45for _ in range(2500):
46 s.step(2e-3)
47p = np.asarray(s.get_positions())
48ymin = float(p[:, 1].min())
49lo = float(sp.shell[:, 1].min()) # the shell's lowest point in the body frame
50print("shell lowest point (body frame) %.4f -> a resting centre cannot go below ~%.3f"
51 % (lo, -lo))
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"))