CadQuery / OCP

Apache License 2.0
83 stars 27 forks source link

Floating point inconsistencies across OS #114

Open SDI8 opened 1 year ago

SDI8 commented 1 year ago

When comparing the surface area of solids between the win-64 and linux-64 version, small differences can be observed.

The following snippet is run on the same machine on both windows 11 and Ubuntu 22. It's run in a clean conda environment using ocp-7.7.0.0 and Python 3.10.

import math
import platform

import OCP

print(OCP.__version__)
# Windows 11:   7.7.0.0
# Ubuntu 22:    7.7.0.0dev
print(platform.python_version())
# Windows 11:   3.10.10
# Ubuntu 22:    3.10.9
print(platform.uname())
# Windows 11:   uname_result(system='Windows', node='DESKTOP-DQ5VM60', release='10', version='10.0.22621', machine='AMD64')
# Ubuntu 22:    uname_result(system='Linux', node='sdi-work', release='5.14.0-1036-oem', version='#40-Ubuntu SMP Mon May 9 09:15:08 UTC 2022', machine='x86_64')

print("Ideal sphere surface area: ", 4 * math.pi)
# Windows 11:   12.566370614359172
# Ubuntu 22:    12.566370614359172

sphere = OCP.BRepPrimAPI.BRepPrimAPI_MakeSphere(1).Shape()

sys = OCP.GProp.GProp_GProps()
OCP.BRepGProp.BRepGProp.SurfaceProperties_s(sphere, sys)
print("OCP sphere surface area", sys.Mass())
# Windows 11:   12.566370614359172
# Ubuntu 22:    12.566370614359172

# Cut the sphere
trf = OCP.gp.gp_Trsf()
trf.SetTranslation(OCP.gp.gp_Vec(0, 0, 1))
sphere2 = sphere.Moved(OCP.TopLoc.TopLoc_Location(trf))
cut_sphere = OCP.BRepAlgoAPI.BRepAlgoAPI_Cut(sphere, sphere2).Shape()

sys = OCP.GProp.GProp_GProps()
OCP.BRepGProp.BRepGProp.SurfaceProperties_s(cut_sphere, sys)
print("Cut Sphere surface area", sys.Mass())
# Windows 11:   12.566370614359094
# Ubuntu 22:    12.566370614359096

The difference might seem insignificant in this example, but I can tell from experience that this error can get greatly amplified on more complex geometries. We have seen differences in surface area in the 1e-2 range. This issue is particularly troublesome for us, as we have a suite of unit tests and snapshot tests that only evaluate successfully on one of the platforms (windows, in our case).

This issue is not recent at all. In fact, our unit tests have never worked well for this reason.

jmwright commented 1 year ago

You can use assertAlmostEqual if you know the number of decimal places the numbers need to match to.

https://github.com/CadQuery/cadquery/blob/7143eadb8e69bd9c3cecdf28b3cb77e7eaf974c0/tests/__init__.py#L52

There is a lot we don't have control over in this situation. The error in the mass property is likely coming from OpenCASCADE kernel and not from CadQuery.

adam-urbanczyk commented 1 year ago

This is to be expected, numerical calculations are not exact and hardware/platform/compiler/compiler settings dependent.

SDI8 commented 1 year ago

I understand that the issue is to be expected across different hardware and architectures and that we don't have much control over OpenCASCADE at this stage. However, I believe that the compiler settings can play a big role in this, too. Especially different optimization settings. So let me rephrase my question: Do we even have sufficient control over the build process in order to alter the behavior, and possibly even align the results across the platforms?

adam-urbanczyk commented 1 year ago

I'm not sure what you mean TBH. OCP is built with gcc/msvc/clang on, respectively, linux, win, osx. We can change some flags, but do you know a set of them that will make the results bit for bit identical?