Repository navigation
# Allow user-provided vertex/edge areas in CollisionMesh (e.g. for axisymmetric models) #266
Description
Motivation
We use ipctk (1.6.0, Python bindings) in fedoo, a finite element library, including for 2D axisymmetric models. There, the collision mesh is the planar (r, z) outline of a solid of revolution, and each planar collision stands for a ring of contact. The area a vertex represents is therefore 2*pi*R * ds rather than the planar barycentric length ds computed by CollisionMesh.
The same need arises whenever the quadrature weight of a surface vertex is not its barycentric area in the collision mesh: plane strain/stress with a thickness, periodic or symmetric sectors, shells with a custom lumping, etc.
Current workaround
After each NormalCollisions.build, we rescale the weight of every collision from Python:
collisions.build(mesh, vertices, dhat, broad_phase=broad_phase)
edges, faces = mesh.edges, mesh.faces
for i in range(len(collisions)):
c = collisions[i]
ids = c.vertex_ids(edges, faces)
n = c.num_vertices()
R = radius[ids[0]] if n == 3 else radius[ids[:n]].mean()
c.weight *= 2 * np.pi * RThis gives a consistent potential, gradient and hessian, and the tangential collisions inherit the weight. It has three drawbacks:
- Cost: a Python loop over all collisions at every Newton iteration and line-search trial (about 16 µs per collision; 3 s for 170k collisions, versus 0.04 s for the build itself).
- OGC:
ogc.TrustRegion.update_if_neededrebuilds the collision set internally, so the rescaled weights are lost and we cannot use OGC in this setting. - Merged terms: with
use_area_weightingandIMPROVED_MAX_APPROX, a vertex-vertex term merges contributions from both vertices (-(A_p/2 + A_v/2)). From the merged weight alone, the exact value-(w_p*A_p/2 + w_v*A_v/2)cannot be recovered, so we use a mean radius.
Proposal
Let the user provide (or scale) the vertex and edge areas of a CollisionMesh, for instance:
mesh.vertex_areas = 2 * np.pi * R * np.asarray(mesh.vertex_areas) # setter
# or
mesh.set_vertex_areas(areas); mesh.set_edge_areas(areas)vertex_areas / edge_areas are currently read-only (AttributeError: property of 'CollisionMesh' object has no setter). Since NormalCollisions.build already takes the weights from these areas when use_area_weighting is on, a setter would solve the three points above without any axisymmetric-specific code in ipctk.
Related question
initial_barrier_stiffness computes its lower bound from average_mass, bbox_diagonal and the barrier only. When collision weights are not of order one (area weighting, or the scaling above), the effective stiffness kappa * weight of that lower bound depends on the weights and on the unit system. Is scaling average_mass by 1 / mean(weight) the intended way to handle this, or would a weight-aware variant make sense?
I am happy to open a PR for the setters if this direction suits you.
Activity
zfergus commentedon Oct 13, 2026
Thanks for the detailed write-up, @chemiskyy. This direction makes sense, and I've opened #273 with the setters.
CollisionMesh::set_vertex_areas/set_edge_areasin C++, andmesh.vertex_areas = .../mesh.edge_areas = ...in Python.NormalCollisions::buildalready takes its weights from these areas, so it covers all three of your points: no per-collision loop, the weights survive OGC's internal rebuilds, and merged vertex-vertex terms stay exact.mesh.vertex_areas = 2 * np.pi * R * mesh.vertex_areasrather than*=.init_area_jacobians()already ran, andinit_area_jacobians()throws afterwards, so you can't end up with gradients that silently disagree with the weights.On
initial_barrier_stiffness: adaptive stiffness was never designed for area weighting. With area weighting and a physical barrier, κ becomes a material parameter in the same units as Young's modulus, so picking a fixed value relative to the elastic stiffness should work across many cases. If you still want the adaptive range,average_mass / mean(weight)is a reasonable adjustment, and I documented it on the function. I didn't add a weight-aware variant, since it would only multiply the mass.If you can try the branch on your axisymmetric setup, I'd appreciate hearing whether it works for you.
chemiskyy commentedon Oct 15, 2026
Dear @zfergus
I have tested the branch relative to PR #273 and it works very well, thanks.
I have left detailed comments in the PR along with a suggestion about 0-valuated weight along the axis
Thank you very much for having taken into account this issue and request