From 559e9a8f681d26a3a4c529cf2ba47926972215c9 Mon Sep 17 00:00:00 2001
From: Sebastian Eibl <sebastian.eibl@fau.de>
Date: Mon, 10 Jul 2017 10:35:57 +0200
Subject: [PATCH] tried to numerically stabilize geometric function

---
 src/geometry/GeometricalFunctions.cpp | 26 +++++++-------
 tests/geometry/CMakeLists.txt         |  2 ++
 tests/geometry/Functions.cpp          | 52 +++++++++++++++++++++++++++
 3 files changed, 67 insertions(+), 13 deletions(-)
 create mode 100644 tests/geometry/Functions.cpp

diff --git a/src/geometry/GeometricalFunctions.cpp b/src/geometry/GeometricalFunctions.cpp
index 52f3c7cb9..ec8e02b82 100644
--- a/src/geometry/GeometricalFunctions.cpp
+++ b/src/geometry/GeometricalFunctions.cpp
@@ -259,7 +259,7 @@ void getClosestLineSegmentPoints( const Vector3<real_t>& a1, const Vector3<real_
    const Vector3<real_t> a1b1( b1 - a1 );
    const real_t da1 ( a1a2 * a1b1 );
    const real_t db1 ( b1b2 * a1b1 );
-   if( da1 <= real_t(0) && db1 >= real_t(0) ) {
+   if( da1 <= +Limits<real_t>::fpuAccuracy() && db1 >= -Limits<real_t>::fpuAccuracy() ) {
       cp1 = a1;
       cp2 = b1;
       return;
@@ -268,7 +268,7 @@ void getClosestLineSegmentPoints( const Vector3<real_t>& a1, const Vector3<real_
    const Vector3<real_t> a1b2( b2 - a1 );
    const real_t da2 ( a1a2 * a1b2 );
    const real_t db2 ( b1b2 * a1b2 );
-   if( da2 <= real_t(0) && db2 <= real_t(0) ) {
+   if( da2 <= +Limits<real_t>::fpuAccuracy() && db2 <= +Limits<real_t>::fpuAccuracy() ) {
       cp1 = a1;
       cp2 = b2;
       return;
@@ -277,7 +277,7 @@ void getClosestLineSegmentPoints( const Vector3<real_t>& a1, const Vector3<real_
    const Vector3<real_t> a2b1( b1 - a2 );
    const real_t da3 ( a1a2 * a2b1 );
    const real_t db3 ( b1b2 * a2b1 );
-   if( da3 >= real_t(0) && db3 >= real_t(0) ) {
+   if( da3 >= -Limits<real_t>::fpuAccuracy() && db3 >= -Limits<real_t>::fpuAccuracy() ) {
       cp1 = a2;
       cp2 = b1;
       return;
@@ -286,7 +286,7 @@ void getClosestLineSegmentPoints( const Vector3<real_t>& a1, const Vector3<real_
    const Vector3<real_t> a2b2( b2 - a2 );
    const real_t da4 ( a1a2 * a2b2 );
    const real_t db4 ( b1b2 * a2b2 );
-   if( da4 >= real_t(0) && db4 <= real_t(0) ) {
+   if( da4 >= -Limits<real_t>::fpuAccuracy() && db4 <= +Limits<real_t>::fpuAccuracy() ) {
       cp1 = a2;
       cp2 = b2;
       return;
@@ -300,20 +300,20 @@ void getClosestLineSegmentPoints( const Vector3<real_t>& a1, const Vector3<real_
    Vector3<real_t> n, k;
 
    const real_t la( a1a2 * a1a2 );
-   if( da1 >= real_t(0) && da3 <= real_t(0) ) {
+   if( da1 >= -Limits<real_t>::fpuAccuracy() && da3 <= +Limits<real_t>::fpuAccuracy() ) {
       k = (da1 / la) * a1a2;
       n = a1b1 - k;
-      if( b1b2 * n >= real_t(0) ) {
+      if( b1b2 * n >= -Limits<real_t>::fpuAccuracy() ) {
          cp1 = a1 + k;
          cp2 = b1;
          return;
       }
    }
 
-   if( da2 >= real_t(0) && da4 <= real_t(0) ) {
+   if( da2 >= -Limits<real_t>::fpuAccuracy() && da4 <= +Limits<real_t>::fpuAccuracy() ) {
       k = (da2 / la) * a1a2;
       n = a1b2 - k;
-      if( b1b2 * n <= real_t(0) ) {
+      if( b1b2 * n <= +Limits<real_t>::fpuAccuracy() ) {
          cp1 = a1 + k;
          cp2 = b2;
          return;
@@ -321,20 +321,20 @@ void getClosestLineSegmentPoints( const Vector3<real_t>& a1, const Vector3<real_
    }
 
    const real_t lb( b1b2 * b1b2 );
-   if( db1 <= real_t(0) && db2 >= real_t(0) ) {
+   if( db1 <= +Limits<real_t>::fpuAccuracy() && db2 >= -Limits<real_t>::fpuAccuracy() ) {
       k = (-db1 / lb) * b1b2;
       n = -a1b1 - k;
-      if( a1a2 * n >= real_t(0) ) {
+      if( a1a2 * n >= -Limits<real_t>::fpuAccuracy() ) {
          cp1 = a1;
          cp2= b1 + k;
          return;
       }
    }
 
-   if( db3 <= real_t(0) && db4 >= real_t(0) ) {
+   if( db3 <= +Limits<real_t>::fpuAccuracy() && db4 >= -Limits<real_t>::fpuAccuracy() ) {
       k = (-db3 / lb) * b1b2;
       n = -a2b1 - k;
-      if( a1a2 * n <= real_t(0) ) {
+      if( a1a2 * n <= +Limits<real_t>::fpuAccuracy() ) {
          cp1 = a2;
          cp2 = b1 + k;
          return;
@@ -347,7 +347,7 @@ void getClosestLineSegmentPoints( const Vector3<real_t>& a1, const Vector3<real_
    const real_t scale( a1a2 * b1b2 );
    real_t div( la * lb - math::sq(scale) );
 
-   WALBERLA_ASSERT_GREATER( div, real_t(0), "Invalid division" );
+   WALBERLA_ASSERT_GREATER( div, real_t(0), std::setprecision(16) << "Invalid division\n" << a1 << "\n" << a2 << "\n" << b1 << "\n" << b2 );
    div = real_t(1) / div;
 
    const real_t s( ( lb    * da1 - scale * db1 ) * div );
diff --git a/tests/geometry/CMakeLists.txt b/tests/geometry/CMakeLists.txt
index 2bedbd39e..789e44395 100644
--- a/tests/geometry/CMakeLists.txt
+++ b/tests/geometry/CMakeLists.txt
@@ -8,6 +8,8 @@
 waLBerla_compile_test( FILES CellAABBTest.cpp )
 waLBerla_execute_test( NAME CellAABBTest COMMAND $<TARGET_FILE:CellAABBTest> )
 
+waLBerla_compile_test( FILES Functions.cpp )
+waLBerla_execute_test( NAME Functions )
 
 waLBerla_compile_test( FILES VoxelFileTest.cpp )
 waLBerla_execute_test( NAME VoxelFileTest     COMMAND $<TARGET_FILE:VoxelFileTest> )
diff --git a/tests/geometry/Functions.cpp b/tests/geometry/Functions.cpp
new file mode 100644
index 000000000..f4341fae6
--- /dev/null
+++ b/tests/geometry/Functions.cpp
@@ -0,0 +1,52 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file Functions.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <core/DataTypes.h>
+#include <core/debug/TestSubsystem.h>
+#include <core/logging/Logging.h>
+#include <core/mpi/MPIManager.h>
+#include <geometry/GeometricalFunctions.h>
+
+using namespace walberla;
+using namespace walberla::geometry;
+
+typedef math::Vector3<real_t> Vec3;
+
+int main( int argc, char** argv )
+{
+    debug::enterTestMode();
+
+    MPIManager::instance()->initializeMPI( &argc, &argv );
+
+    Vec3 a,b;
+    geometry::getClosestLineSegmentPoints(Vec3(real_c(7.41657),real_c( 9.82461),real_c(17.28197)),
+                                          Vec3(real_c(7.21657),real_c( 9.82461),real_c(17.28197)),
+                                          Vec3(real_c(7.36474),real_c(10.20490),real_c(17.45072)),
+                                          Vec3(real_c(7.16474),real_c(10.20490),real_c(17.45072)), a, b);
+    WALBERLA_LOG_DEVEL(a << "\n" << b);
+    // might fail due to numerically instable algorithm
+    geometry::getClosestLineSegmentPoints(Vec3(real_c(7.416573195643714),real_c(9.82461330720222),real_c(17.28197302209214)),
+                                          Vec3(real_c(7.216573195643715),real_c(9.82461330720222),real_c(17.28197302209214)),
+                                          Vec3(real_c(7.364742740420613),real_c(10.2049088375177),real_c(17.45072353608904)),
+                                          Vec3(real_c(7.164742740420614),real_c(10.2049088375177),real_c(17.45072353608904)), a, b);
+    WALBERLA_LOG_DEVEL(a << "\n" << b);
+
+    return EXIT_SUCCESS;
+}
-- 
GitLab