From 652b35def236e4b701ed0f36d381ea30d8eb0cf9 Mon Sep 17 00:00:00 2001 From: Rudolf Weeber Date: Mon, 15 Jun 2026 10:46:10 +0200 Subject: [PATCH] collision_detection: enforce pair-bond precondition in GlueToSurface (bug-sweep #40) GlueToSurface::initialize never validated the arity of bond_centers or bond_vs, unlike the sibling protocols BindCenters and BindAtPointOfCollision. A non-pair (e.g. angle) bond was accepted at set time; at the first collision handle_collisions() stores it with a single-partner list, so it gets misclassified as a pair bond and calc_bond_pair_force throws BondUnknownTypeError inside a Kokkos parallel region, leading to std::terminate far from the misconfiguration. handle_collisions() inserts a single-partner list for both bond_centers and bond_vs, so both must be strict pair bonds (exactly one partner). Add the arity guards in initialize() at the same point the sibling protocols do, raising a clear RuntimeError. Existence is already guaranteed upstream by Protocol::find_bond_id, so at() is safe. Extend test_glue_to_surface in collision_detection_interface.py with the two assertRaisesRegex blocks mirroring the existing bind_centers and bind_at_point_of_collision checks. Co-Authored-By: Claude Opus 4.8 --- src/core/collision_detection/GlueToSurface.cpp | 14 ++++++++++++++ testsuite/python/collision_detection_interface.py | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/core/collision_detection/GlueToSurface.cpp b/src/core/collision_detection/GlueToSurface.cpp index 8390d2f5d2..a9a3b6f309 100644 --- a/src/core/collision_detection/GlueToSurface.cpp +++ b/src/core/collision_detection/GlueToSurface.cpp @@ -58,6 +58,20 @@ void GlueToSurface::initialize(System::System &system) { // Cache square of cutoff distance_sq = Utils::sqr(distance); + // Check that the bonds have the right number of partners. Both bonds are + // stored with a single-partner list in handle_collisions(), so they must + // be strict pair bonds (existence is already guaranteed at the seam). + assert(system.bonded_ias->contains(bond_centers)); + if (number_of_partners(*system.bonded_ias->at(bond_centers)) != 1) { + throw std::runtime_error("The bond type to be used for binding particle " + "centers needs to be a pair bond"); + } + assert(system.bonded_ias->contains(bond_vs)); + if (number_of_partners(*system.bonded_ias->at(bond_vs)) != 1) { + throw std::runtime_error("The bond type to be used for binding virtual " + "sites needs to be a pair bond"); + } + if (part_type_vs < 0) { throw std::domain_error("Collision detection particle type for virtual " "sites needs to be >=0"); diff --git a/testsuite/python/collision_detection_interface.py b/testsuite/python/collision_detection_interface.py index bd629c8d1a..a5988d49f1 100644 --- a/testsuite/python/collision_detection_interface.py +++ b/testsuite/python/collision_detection_interface.py @@ -153,6 +153,10 @@ def test_glue_to_surface(self): self.set_coldet("glue_to_surface", part_type_to_attach_vs_to=-1) with self.assertRaisesRegex(ValueError, "type after gluing needs to be >=0"): self.set_coldet("glue_to_surface", part_type_after_glueing=-1) + with self.assertRaisesRegex(RuntimeError, "The bond type to be used for binding particle centers needs to be a pair bond"): + self.set_coldet("glue_to_surface", bond_centers=self.bond_angle) + with self.assertRaisesRegex(RuntimeError, "The bond type to be used for binding virtual sites needs to be a pair bond"): + self.set_coldet("glue_to_surface", bond_vs=self.bond_angle) # check if original parameters have been preserved self.check_stored_parameters("glue_to_surface", distance=0.5)