verification · oscad

A second CAD kernel is a second test oracle

2026-10-13 · punkfab/openscad-occt (MIT) · NVIDIA-Omniverse/usd-brep (Apache-2.0)

On 8 October NVIDIA published usd-brep, which is SMLib: the NURBS solid modelling kernel that Solid Modeling Solutions sold for twenty-five years, about a million lines of C++, now under the Apache 2.0 licence. Until last week there was exactly one open kernel with production booleans and fillets, OpenCASCADE. Now there are two.

We have a small interpreter, oscad, that reads OpenSCAD-style code and builds the part on OpenCASCADE instead of a mesh library, so the output is an exact STEP solid rather than triangles. The obvious question was whether to switch it to SMLib. We did not. We added SMLib beside OpenCASCADE and made the interpreter run on either, because a second kernel is worth more as a check than as a replacement.

Why a second kernel is a test

Boundary-representation booleans fail in two ways. One is loud: the operation reports an error and you get nothing. The other is quiet: it reports success and hands back a solid with a face missing, a sliver added, or an operand dropped. The quiet kind is the one that reaches the machine shop.

With one kernel the only outside check we had was OpenSCAD itself, whose output is a mesh. We compare bounding boxes against it across its own test corpus, and that catches gross errors. It cannot catch a fillet applied to the wrong edges, because the bounding box does not move.

Two kernels that share no code are a sharper instrument. Build the same part on both, compare bounding box and volume, and any disagreement is a finding: a bug in one kernel, a bug in our code on one side, or a difference in what an operation means. Agreement to a few parts per million is strong evidence that neither is quietly wrong, because two independent implementations would have to be wrong in the same way.

Four renders in two rows. Top row: a pipe flange, a flat disk with four bolt holes and a bored riser joined by a concave weld fillet, built by OpenCASCADE on the left and SMLib on the right. Bottom row: a square post mount with a central bored post and four corner holes, again OpenCASCADE on the left and SMLib on the right. Each pair is indistinguishable.

The flange is a disk and a tube joined by a concave fillet, with five bores subtracted. The post mount is a plate with a post seated on it by a face-to-face datum query, then a weld fillet. Each row is the same source file built by the two kernels. The volumes agree to within 0.004% at this mesh resolution, and to 0.001% when the meshes are made finer.

What we built

Results

CheckOpenCASCADESMLib
Our 16 sample and example parts, bbox and volume against the other kernel16 of 16 agree16 of 16 agree
OpenSCAD test corpus, 139 files in scope, bbox against OpenSCAD67 matched · 0 mismatched · 69 not supported56 matched · 0 mismatched · 81 not supported
STEP output passes OpenCASCADE's validity checkallall

The twelve extra files SMLib cannot run are declared limits of our backend, not wrong answers: exporting a bare 2D shape, mixing 2D and 3D, mirroring a 2D profile, and two extrude options. Sixteen boolean subtractions on the plate example take about forty milliseconds in total on SMLib. We have not benchmarked the two kernels against each other beyond that.

What disagreement found

Three things turned up as the kernels disagreeing, not as anyone reading code.

The rest of the bugs were ours, in the bridge: parts made of several separate bodies, a torus that is a single face closed on itself, and intersection curves whose ends miss their vertices by a millionth. The corpus gate and the volume check found each of them within a minute of the change that caused it.

What this does not show

Why it matters to us

Our pipeline is define, simulate, verify, build. The verify step is only as good as the oracle. For geometry the oracle has been the kernel that made the geometry, which is circular. Two independent kernels break the circle for the class of errors that matter most, the ones that report success. From here on, any kernel-facing change to oscad runs through both, and a part we hand to a shop has been built twice.

What's next

Map planes, cylinders, cones, spheres and tori to their analytic STEP entities in the bridge. Carry SMLib's own trimming curves across instead of projecting, which removes the pole limitation. Add 2D booleans on the SMLib side. Report the silent-union case upstream with the reduced file.

Who did what

We decided to try SMLib and asked whether it should be a branch or a pluggable backend. Claude did the spike, the standalone build, the bridge, the backend, the comparison scripts and this write-up. The kernels are the work of their authors: Open Cascade SAS, and Solid Modeling Solutions and NVIDIA.

The code is at punkfab/openscad-occt; the spike, results and remaining gaps are in issue #2. Got a part that needs to be right the first time? Start a project →