diff --git a/python/mesa_pd/templates/common/ParticleFunctions.templ.h b/python/mesa_pd/templates/common/ParticleFunctions.templ.h index 807a1291949b644f6f17b637d008c19688cd9467..599c8d58b45939d9ced44fee0326531f55191800 100644 --- a/python/mesa_pd/templates/common/ParticleFunctions.templ.h +++ b/python/mesa_pd/templates/common/ParticleFunctions.templ.h @@ -27,10 +27,6 @@ #pragma once #include <mesa_pd/data/DataTypes.h> -#include <mesa_pd/domain/IDomain.h> - -#include <core/logging/Logging.h> -#include <core/mpi/MPIManager.h> namespace walberla { namespace mesa_pd { @@ -44,6 +40,33 @@ inline Vec3 getVelocityAtWFPoint(const size_t p_idx, Accessor& ac, const Vec3& w return ac.getLinearVelocity(p_idx) + cross(ac.getAngularVelocity(p_idx), ( wf_pt - ac.getPosition(p_idx) )); } +/** + * Transformations between world frame (WF) and body frame (BF) coordinates + */ +template <typename Accessor> +inline Vec3 transformPositionFromWFtoBF(const size_t p_idx, Accessor& ac, const Vec3& positionWF) +{ + return ac.getRotation(p_idx).getMatrix().getTranspose() * ( positionWF - ac.getPosition(p_idx) ); +} + +template <typename Accessor> +inline Vec3 transformVectorFromWFtoBF(const size_t p_idx, Accessor& ac, const Vec3& vectorWF) +{ + return ac.getRotation(p_idx).getMatrix().getTranspose() * vectorWF; +} + +template <typename Accessor> +inline Vec3 transformPositionFromBFtoWF(const size_t p_idx, Accessor& ac, const Vec3& positionBF) +{ + return ac.getPosition(p_idx) + ac.getRotation(p_idx).getMatrix() * positionBF; +} + +template <typename Accessor> +inline Vec3 transformVectorFromBFtoWF(const size_t p_idx, Accessor& ac, const Vec3& vectorBF) +{ + return ac.getRotation(p_idx).getMatrix() * vectorBF; +} + /** * Force is applied at the center of mass. */ diff --git a/src/mesa_pd/common/Contains.h b/src/mesa_pd/common/Contains.h index 2b13cd351a59c5d4b58605b7e69380dfec657832..6f3bd1f1a4fa8b42da9fd69f7a1822ed634b7c63 100644 --- a/src/mesa_pd/common/Contains.h +++ b/src/mesa_pd/common/Contains.h @@ -23,6 +23,12 @@ #include <mesa_pd/data/DataTypes.h> #include <mesa_pd/data/Flags.h> #include <mesa_pd/data/IAccessor.h> + +#include <mesa_pd/common/ParticleFunctions.h> + +#include <mesa_pd/data/shape/Box.h> +#include <mesa_pd/data/shape/CylindricalBoundary.h> +#include <mesa_pd/data/shape/Ellipsoid.h> #include <mesa_pd/data/shape/HalfSpace.h> #include <mesa_pd/data/shape/Sphere.h> @@ -30,22 +36,44 @@ namespace walberla { namespace mesa_pd { /* - * contains functionality + * "contains point" functionality + * can either be formulated in world frame coordinates (then the rotation of the geometry is not taken into account) + * or in body frame coordinates (BF) which requires the point to be first transformed */ -bool isPointInsideSphere(const Vector3<real_t>& point, - const Vector3<real_t>& spherePosition, const real_t sphereRadius ) +bool isPointInsideSphere(const Vec3& point, + const Vec3& spherePosition, const real_t sphereRadius ) { return !((point - spherePosition).sqrLength() > sphereRadius * sphereRadius); } -bool isPointInsideHalfSpace(const Vector3<real_t>& point, - const Vector3<real_t>& halfSpacePosition, const Vector3<real_t>& halfSpaceNormal ) +bool isPointInsideHalfSpace(const Vec3& point, + const Vec3& halfSpacePosition, const Vec3& halfSpaceNormal ) { return !((point - halfSpacePosition) * halfSpaceNormal > real_t(0)); } -//TODO add ellipsoids +bool isPointInsideBoxBF(const Vec3& pointBF, + const Vec3& edgeLengths ) +{ + return std::fabs(pointBF[0]) <= real_t(0.5)*edgeLengths[0] && + std::fabs(pointBF[1]) <= real_t(0.5)*edgeLengths[1] && + std::fabs(pointBF[2]) <= real_t(0.5)*edgeLengths[2]; +} + +bool isPointInsideEllipsoidBF(const Vec3& pointBF, + const Vec3& semiAxes ) +{ + return ( (pointBF[0] * pointBF[0])/(semiAxes[0] * semiAxes[0]) + (pointBF[1] * pointBF[1])/(semiAxes[1] * semiAxes[1]) + + (pointBF[2] * pointBF[2])/(semiAxes[2] * semiAxes[2]) <= 1_r ); +} + +bool isPointInsideCylindricalBoundary(const Vec3& point, + const Vec3& cylindricalBoundaryPosition, const real_t radius, const Vec3& axis ) +{ + const Vec3 distanceFromCylinderCenterLine = (point - cylindricalBoundaryPosition) - ((point - cylindricalBoundaryPosition) * axis) * axis; + return distanceFromCylinderCenterLine.sqrLength() >= radius*radius; +} struct ContainsPointFunctor { @@ -71,6 +99,30 @@ struct ContainsPointFunctor return isPointInsideHalfSpace(point, ac.getPosition(particleIdx), halfSpace.getNormal() ); } + template<typename ParticleAccessor_T> + bool operator()(const size_t particleIdx, const mesa_pd::data::Box& box, const ParticleAccessor_T& ac, const Vector3<real_t>& point ) + { + static_assert(std::is_base_of<mesa_pd::data::IAccessor, ParticleAccessor_T>::value, "Provide a valid accessor as template"); + + return isPointInsideBoxBF(mesa_pd::transformPositionFromWFtoBF(particleIdx, ac, point), box.getEdgeLength()); + } + + template<typename ParticleAccessor_T> + bool operator()(const size_t particleIdx, const mesa_pd::data::Ellipsoid& ellipsoid, const ParticleAccessor_T& ac, const Vector3<real_t>& point ) + { + static_assert(std::is_base_of<mesa_pd::data::IAccessor, ParticleAccessor_T>::value, "Provide a valid accessor as template"); + + return isPointInsideEllipsoidBF(mesa_pd::transformPositionFromWFtoBF(particleIdx, ac, point), ellipsoid.getSemiAxes()); + } + + template<typename ParticleAccessor_T> + bool operator()(const size_t particleIdx, const mesa_pd::data::CylindricalBoundary& cylindricalBoundary, const ParticleAccessor_T& ac, const Vector3<real_t>& point ) + { + static_assert(std::is_base_of<mesa_pd::data::IAccessor, ParticleAccessor_T>::value, "Provide a valid accessor as template"); + + return isPointInsideCylindricalBoundary(point, ac.getPosition(particleIdx), cylindricalBoundary.getRadius(), cylindricalBoundary.getAxis()); + } + }; diff --git a/src/mesa_pd/common/ParticleFunctions.h b/src/mesa_pd/common/ParticleFunctions.h index bb1c89880afbce2932545b36709808b01db619b8..52b043d1289ae7fdd3778512f5b93f4e697a827c 100644 --- a/src/mesa_pd/common/ParticleFunctions.h +++ b/src/mesa_pd/common/ParticleFunctions.h @@ -27,10 +27,6 @@ #pragma once #include <mesa_pd/data/DataTypes.h> -#include <mesa_pd/domain/IDomain.h> - -#include <core/logging/Logging.h> -#include <core/mpi/MPIManager.h> namespace walberla { namespace mesa_pd { @@ -44,6 +40,33 @@ inline Vec3 getVelocityAtWFPoint(const size_t p_idx, Accessor& ac, const Vec3& w return ac.getLinearVelocity(p_idx) + cross(ac.getAngularVelocity(p_idx), ( wf_pt - ac.getPosition(p_idx) )); } +/** + * Transformations between world frame (WF) and body frame (BF) coordinates + */ +template <typename Accessor> +inline Vec3 transformPositionFromWFtoBF(const size_t p_idx, Accessor& ac, const Vec3& positionWF) +{ + return ac.getRotation(p_idx).getMatrix().getTranspose() * ( positionWF - ac.getPosition(p_idx) ); +} + +template <typename Accessor> +inline Vec3 transformVectorFromWFtoBF(const size_t p_idx, Accessor& ac, const Vec3& vectorWF) +{ + return ac.getRotation(p_idx).getMatrix().getTranspose() * vectorWF; +} + +template <typename Accessor> +inline Vec3 transformPositionFromBFtoWF(const size_t p_idx, Accessor& ac, const Vec3& positionBF) +{ + return ac.getPosition(p_idx) + ac.getRotation(p_idx).getMatrix() * positionBF; +} + +template <typename Accessor> +inline Vec3 transformVectorFromBFtoWF(const size_t p_idx, Accessor& ac, const Vec3& vectorBF) +{ + return ac.getRotation(p_idx).getMatrix() * vectorBF; +} + /** * Force is applied at the center of mass. */ diff --git a/src/mesa_pd/common/RayParticleIntersection.h b/src/mesa_pd/common/RayParticleIntersection.h index d1c9ce912dd325b931ecfa5e52021e0fe4ccbdc1..b62306aa5dd6014d301ddbd6943a8a0f45b700b9 100644 --- a/src/mesa_pd/common/RayParticleIntersection.h +++ b/src/mesa_pd/common/RayParticleIntersection.h @@ -24,6 +24,7 @@ #include <mesa_pd/data/DataTypes.h> #include <mesa_pd/data/Flags.h> #include <mesa_pd/data/IAccessor.h> +#include <mesa_pd/data/shape/Ellipsoid.h> #include <mesa_pd/data/shape/HalfSpace.h> #include <mesa_pd/data/shape/Sphere.h> #include <mesa_pd/kernel/SingleCast.h> @@ -33,10 +34,12 @@ namespace mesa_pd { /* * ray - particle intersection ratio functionality + * can either be formulated in world frame coordinates (then the rotation of the geometry is not taken into account) + * or in body frame coordinates (BF) which requires the point to be first transformed */ -real_t raySphereIntersectionRatio( const Vector3<real_t> & rayOrigin, const Vector3<real_t> & rayDirection, - const Vector3<real_t> & spherePosition, const real_t sphereRadius ) +real_t raySphereIntersectionRatio( const Vec3& rayOrigin, const Vec3& rayDirection, + const Vec3& spherePosition, const real_t sphereRadius ) { WALBERLA_ASSERT( !isPointInsideSphere(rayOrigin, spherePosition, sphereRadius ), "rayOrigin: " << rayOrigin ); WALBERLA_ASSERT( isPointInsideSphere(rayOrigin + rayDirection, spherePosition, sphereRadius ), "rayOrigin + rayDirection: " << rayOrigin + rayDirection ); @@ -53,14 +56,14 @@ real_t raySphereIntersectionRatio( const Vector3<real_t> & rayOrigin, const Vect real_t delta = ( lsqr > rsqr ) ? s - q : s + q; delta /= dirLength; - WALBERLA_ASSERT_GREATER_EQUAL( delta, real_t( 0 ) ); - WALBERLA_ASSERT_LESS_EQUAL( delta, real_t( 1 ) ); + WALBERLA_ASSERT_GREATER_EQUAL( delta, 0_r ); + WALBERLA_ASSERT_LESS_EQUAL( delta, 1_r ); return delta; } -real_t rayHalfSpaceIntersectionRatio( const Vector3<real_t> & rayOrigin, const Vector3<real_t> & rayDirection, - const Vector3<real_t> & halfSpacePosition, const Vector3<real_t> & halfSpaceNormal) +real_t rayHalfSpaceIntersectionRatio( const Vec3& rayOrigin, const Vec3& rayDirection, + const Vec3& halfSpacePosition, const Vec3& halfSpaceNormal) { WALBERLA_ASSERT( !isPointInsideHalfSpace( rayOrigin, halfSpacePosition, halfSpaceNormal ), "rayOrigin: " << rayOrigin ); WALBERLA_ASSERT( isPointInsideHalfSpace( rayOrigin + rayDirection, halfSpacePosition, halfSpaceNormal ), "rayOrigin + rayDirection: " << rayOrigin + rayDirection ); @@ -69,21 +72,49 @@ real_t rayHalfSpaceIntersectionRatio( const Vector3<real_t> & rayOrigin, const V auto diff = halfSpacePosition - rayOrigin; - WALBERLA_ASSERT_FLOAT_UNEQUAL(denom, real_t(0)); + WALBERLA_ASSERT_FLOAT_UNEQUAL(denom, 0_r); real_t delta = diff * halfSpaceNormal / denom; - WALBERLA_ASSERT_GREATER_EQUAL( delta, real_t( 0 ) ); - WALBERLA_ASSERT_LESS_EQUAL( delta, real_t( 1 ) ); + WALBERLA_ASSERT_GREATER_EQUAL( delta, 0_r ); + WALBERLA_ASSERT_LESS_EQUAL( delta, 1_r ); return delta; } -//TODO add ellipsoids from pe_coupling/geometry/PeIntersectionRatio.cpp +real_t rayEllipsoidIntersectionRatioBF( const Vec3& rayOriginBF, const Vec3& rayDirectionBF, + const Vec3& ellipsoidSemiAxes) +{ + WALBERLA_ASSERT( !isPointInsideEllipsoidBF( rayOriginBF, ellipsoidSemiAxes ), "rayOriginBF: " << rayOriginBF ); + WALBERLA_ASSERT( isPointInsideEllipsoidBF( rayOriginBF + rayDirectionBF, ellipsoidSemiAxes ), "rayOriginBF + rayDirectionBF: " << rayOriginBF + rayDirectionBF ); + + Matrix3<real_t> M = Matrix3<real_t>::makeDiagonalMatrix(1_r/ellipsoidSemiAxes[0], 1_r/ellipsoidSemiAxes[1], 1_r/ellipsoidSemiAxes[2]); + + Vec3 P_M = M*rayOriginBF; + Vec3 d_M = M*rayDirectionBF; + + const real_t a = d_M*d_M; + const real_t b = 2_r*P_M*d_M; + const real_t c = P_M*P_M - 1_r; + + const real_t discriminant = b*b - 4_r*a*c; + + WALBERLA_ASSERT_GREATER_EQUAL(discriminant, 0_r, "No intersection possible!"); + WALBERLA_ASSERT_FLOAT_UNEQUAL(a, 0_r); + + const real_t root = std::sqrt(discriminant); + real_t delta = (-b - root) / (2_r * a); + + WALBERLA_ASSERT_GREATER_EQUAL( delta, 0_r ); + WALBERLA_ASSERT_LESS_EQUAL( delta - 1_r, math::Limits<real_t>::accuracy(), delta ); + + return std::min(std::max(delta, real_c(0)), real_c(1)); + +} template <typename ParticleAccessor_T> real_t intersectionRatioBisection( const size_t particleIdx, const ParticleAccessor_T& ac, - const Vector3<real_t>& rayOrigin, const Vector3<real_t>& rayDirection, + const Vec3& rayOrigin, const Vec3& rayDirection, real_t epsilon) { mesa_pd::kernel::SingleCast singleCast; @@ -100,8 +131,8 @@ real_t intersectionRatioBisection( const size_t particleIdx, const ParticleAcces while( qDelta * qDelta * sqDirectionLength >= sqEpsilon ) { - qDelta *= real_t( 0.5 ); - Vector3<real_t> p = rayOrigin + q * rayDirection; + qDelta *= 0.5_r; + Vec3 p = rayOrigin + q * rayDirection; if( singleCast(particleIdx, ac, containsPointFctr, ac, p) ) { q -= qDelta; @@ -112,8 +143,8 @@ real_t intersectionRatioBisection( const size_t particleIdx, const ParticleAcces } } - WALBERLA_ASSERT_GREATER_EQUAL( q, real_t( 0 ) ); - WALBERLA_ASSERT_LESS_EQUAL( q, real_t( 1 ) ); + WALBERLA_ASSERT_GREATER_EQUAL( q, 0_r ); + WALBERLA_ASSERT_LESS_EQUAL( q, 1_r ); return q; } @@ -121,7 +152,7 @@ real_t intersectionRatioBisection( const size_t particleIdx, const ParticleAcces struct RayParticleIntersectionRatioFunctor { template<typename ParticleAccessor_T, typename Shape_T> - real_t operator()(const size_t particleIdx, const Shape_T& /*shape*/, const ParticleAccessor_T& ac, const Vector3<real_t>& rayOrigin, const Vector3<real_t>& rayDirection, real_t epsilon ) + real_t operator()(const size_t particleIdx, const Shape_T& /*shape*/, const ParticleAccessor_T& ac, const Vec3& rayOrigin, const Vec3& rayDirection, real_t epsilon ) { static_assert(std::is_base_of<mesa_pd::data::IAccessor, ParticleAccessor_T>::value, "Provide a valid accessor as template"); @@ -129,7 +160,7 @@ struct RayParticleIntersectionRatioFunctor } template<typename ParticleAccessor_T> - real_t operator()(const size_t particleIdx, const mesa_pd::data::Sphere& sphere, const ParticleAccessor_T& ac, const Vector3<real_t>& rayOrigin, const Vector3<real_t>& rayDirection, real_t /*epsilon*/ ) + real_t operator()(const size_t particleIdx, const mesa_pd::data::Sphere& sphere, const ParticleAccessor_T& ac, const Vec3& rayOrigin, const Vec3& rayDirection, real_t /*epsilon*/ ) { static_assert(std::is_base_of<mesa_pd::data::IAccessor, ParticleAccessor_T>::value, "Provide a valid accessor as template"); @@ -137,13 +168,21 @@ struct RayParticleIntersectionRatioFunctor } template<typename ParticleAccessor_T> - real_t operator()(const size_t particleIdx, const mesa_pd::data::HalfSpace& halfSpace, const ParticleAccessor_T& ac, const Vector3<real_t>& rayOrigin, const Vector3<real_t>& rayDirection, real_t /*epsilon*/ ) + real_t operator()(const size_t particleIdx, const mesa_pd::data::HalfSpace& halfSpace, const ParticleAccessor_T& ac, const Vec3& rayOrigin, const Vec3& rayDirection, real_t /*epsilon*/ ) { static_assert(std::is_base_of<mesa_pd::data::IAccessor, ParticleAccessor_T>::value, "Provide a valid accessor as template"); return rayHalfSpaceIntersectionRatio(rayOrigin, rayDirection, ac.getPosition(particleIdx), halfSpace.getNormal() ); } + template<typename ParticleAccessor_T> + real_t operator()(const size_t particleIdx, const mesa_pd::data::Ellipsoid& ellipsoid, const ParticleAccessor_T& ac, const Vec3& rayOrigin, const Vec3& rayDirection, real_t /*epsilon*/ ) + { + static_assert(std::is_base_of<mesa_pd::data::IAccessor, ParticleAccessor_T>::value, "Provide a valid accessor as template"); + + return rayEllipsoidIntersectionRatioBF(mesa_pd::transformPositionFromWFtoBF(particleIdx, ac, rayOrigin), mesa_pd::transformVectorFromWFtoBF(particleIdx, ac, rayDirection), ellipsoid.getSemiAxes() ); + } + }; } //namespace mesa_pd diff --git a/tests/mesa_pd/CMakeLists.txt b/tests/mesa_pd/CMakeLists.txt index e55d03d2543789625502526388ebb716159e0e67..d2bf597b215594b57fa9f568cc9306135172c448 100644 --- a/tests/mesa_pd/CMakeLists.txt +++ b/tests/mesa_pd/CMakeLists.txt @@ -34,6 +34,9 @@ waLBerla_execute_test( NAME MESA_PD_COLLISIONDETECTION_SphereSupport ) waLBerla_compile_test( NAME MESA_PD_COMMON_IntersectionRatio FILES common/IntersectionRatio.cpp DEPENDS core ) waLBerla_execute_test( NAME MESA_PD_COMMON_IntersectionRatio ) +waLBerla_compile_test( NAME MESA_PD_COMMON_ContainsPoint FILES common/ContainsPoint.cpp DEPENDS core ) +waLBerla_execute_test( NAME MESA_PD_COMMON_ContainsPoint ) + waLBerla_compile_test( NAME MESA_PD_ContactDetection FILES ContactDetection.cpp DEPENDS blockforest core pe) waLBerla_execute_test( NAME MESA_PD_ContactDetection PROCESSES 8 ) diff --git a/tests/mesa_pd/common/ContainsPoint.cpp b/tests/mesa_pd/common/ContainsPoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..be5b07996aca428761943d5c30ea0ad3adad6a5e --- /dev/null +++ b/tests/mesa_pd/common/ContainsPoint.cpp @@ -0,0 +1,230 @@ +//====================================================================================================================== +// +// 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 +//! \author Christoph Rettinger <christoph.rettinger@fau.de> +// +//====================================================================================================================== + +#include "core/DataTypes.h" +#include "core/Environment.h" +#include "core/debug/TestSubsystem.h" +#include "core/math/all.h" + +#include "mesa_pd/common/Contains.h" +#include "mesa_pd/data/ParticleAccessorWithShape.h" +#include "mesa_pd/data/ParticleStorage.h" +#include "mesa_pd/data/ShapeStorage.h" +#include "mesa_pd/data/DataTypes.h" +#include <mesa_pd/data/shape/Box.h> +#include <mesa_pd/data/shape/CylindricalBoundary.h> +#include <mesa_pd/data/shape/Ellipsoid.h> +#include <mesa_pd/data/shape/HalfSpace.h> +#include <mesa_pd/data/shape/Sphere.h> +#include "mesa_pd/kernel/SingleCast.h" + +namespace contains_point_test +{ +using namespace walberla; +using mesa_pd::Vec3; + + +/*!\brief Tests the contains pint functionality implemented in mesa_pd/common/Contains.h + * + * Currently the following shapes are tested: + * - sphere + * - halfspace + * - box (default and rotated) + * - ellipsoid (default and rotated) + * - cylindrical boundary + */ + +////////// +// MAIN // +////////// +int main( int argc, char **argv ) +{ + debug::enterTestMode(); + + mpi::Environment env( argc, argv ); + + auto ps = std::make_shared<mesa_pd::data::ParticleStorage>(1); + auto shapeStorage = std::make_shared<mesa_pd::data::ShapeStorage>(); + using ParticleAccessor = mesa_pd::data::ParticleAccessorWithShape; + ParticleAccessor accessor(ps, shapeStorage); + + + mesa_pd::kernel::SingleCast singleCast; + mesa_pd::ContainsPointFunctor containsPointFctr; + + //////////// + // SPHERE // + //////////// + { + real_t sphereRadius = 1_r; + auto sphereShape = shapeStorage->create<mesa_pd::data::Sphere>( sphereRadius ); + + Vec3 position(1_r, 0_r, 0_r); + + mesa_pd::data::Particle&& p = *ps->create(); + p.setPosition(position); + p.setShapeID(sphereShape); + auto idx = p.getIdx(); + + std::vector<Vec3> testPositions {position, position + Vec3(0,0,1.01_r*sphereRadius)}; + std::vector<bool> shouldBeContained {true, false}; + + for(size_t i = 0; i < testPositions.size(); ++i) + { + bool isContained = singleCast(idx, accessor, containsPointFctr, accessor, testPositions[i] ); + WALBERLA_CHECK_EQUAL(isContained, shouldBeContained[i], "Sphere check, case " << i << ": Wrong containment info for position " << testPositions[i] ); + } + } + + /////////////// + // HALFSPACE // + /////////////// + { + Vec3 position(1_r, 0_r, 0_r); + Vec3 normal(0_r, 1_r, 1_r); + + auto planeShape = shapeStorage->create<mesa_pd::data::HalfSpace>( normal.getNormalized() ); + + mesa_pd::data::Particle&& p = *ps->create(true); + p.setPosition(position); + p.setShapeID(planeShape); + auto idx = p.getIdx(); + + std::vector<Vec3> testPositions {position - normal, position + normal}; + std::vector<bool> shouldBeContained {true, false}; + + for(size_t i = 0; i < testPositions.size(); ++i) + { + bool isContained = singleCast(idx, accessor, containsPointFctr, accessor, testPositions[i] ); + WALBERLA_CHECK_EQUAL(isContained, shouldBeContained[i], "Halfspace check, case " << i << ": Wrong containment info for position " << testPositions[i] ); + } + } + + ///////// + // BOX // + ///////// + { + Vec3 position(1_r, 0_r, 0_r); + Vec3 edgeLength{1_r}; + + auto boxShape = shapeStorage->create<mesa_pd::data::Box>( edgeLength ); + + mesa_pd::data::Particle&& p = *ps->create(); + p.setPosition(position); + p.setShapeID(boxShape); + auto idx = p.getIdx(); + + std::vector<Vec3> testPositions {position, position + 0.51_r*Vec3(0_r,0_r,edgeLength[2])}; + std::vector<bool> shouldBeContained {true, false}; + + for(size_t i = 0; i < testPositions.size(); ++i) + { + bool isContained = singleCast(idx, accessor, containsPointFctr, accessor, testPositions[i] ); + WALBERLA_CHECK_EQUAL(isContained, shouldBeContained[i], "Box check, case " << i << ": Wrong containment info for position " << testPositions[i] ); + } + + auto rotation = p.getRotation(); + rotation.rotate( Vec3(1_r,0_r,0_r), math::pi / 4_r ); // rotate by 45° around x axis + p.setRotation(rotation); + + shouldBeContained[1] = true; + + for(size_t i = 0; i < testPositions.size(); ++i) + { + bool isContained = singleCast(idx, accessor, containsPointFctr, accessor, testPositions[i] ); + WALBERLA_CHECK_EQUAL(isContained, shouldBeContained[i], "Box rotation check, case " << i << ": Wrong containment info for position " << testPositions[i] ); + } + } + + /////////////// + // ELLIPSOID // + /////////////// + { + Vec3 position(1_r, 0_r, 0_r); + Vec3 semiAxes{2_r,1_r, 1_r}; + + auto ellipsoidShape = shapeStorage->create<mesa_pd::data::Ellipsoid>( semiAxes ); + + mesa_pd::data::Particle&& p = *ps->create(); + p.setPosition(position); + p.setShapeID(ellipsoidShape); + auto idx = p.getIdx(); + + std::vector<Vec3> testPositions {position, + position + 0.9_r*Vec3(semiAxes[0],0_r,0_r), + position + 1.1_r*Vec3(0_r,semiAxes[1], 0_r), + position + 1.1_r*Vec3(0_r,0_r,semiAxes[2])}; + std::vector<bool> shouldBeContained {true, true, false, false}; + + for(size_t i = 0; i < testPositions.size(); ++i) + { + bool isContained = singleCast(idx, accessor, containsPointFctr, accessor, testPositions[i] ); + WALBERLA_CHECK_EQUAL(isContained, shouldBeContained[i], "Ellipsoid check, case " << i << ": Wrong containment info for position " << testPositions[i] ); + } + + auto rotation = p.getRotation(); + rotation.rotate( Vec3(0_r,1_r,0_r), math::pi / 2_r ); // rotate by 90° around y axis + p.setRotation(rotation); + + shouldBeContained[1] = false; + shouldBeContained[3] = true; + + for(size_t i = 0; i < testPositions.size(); ++i) + { + bool isContained = singleCast(idx, accessor, containsPointFctr, accessor, testPositions[i] ); + WALBERLA_CHECK_EQUAL(isContained, shouldBeContained[i], "Ellipsoid rotation check, case " << i << ": Wrong containment info for position " << testPositions[i] ); + } + } + + ////////////////////////// + // CYLINDRICAL BOUNDARY // + ////////////////////////// + { + Vec3 position(1_r, 0_r, 0_r); + Vec3 axis(0_r, 1_r, 1_r); + real_t radius = 1_r; + + auto cylindricalBoundaryShape = shapeStorage->create<mesa_pd::data::CylindricalBoundary>( radius, axis.getNormalized() ); + + mesa_pd::data::Particle&& p = *ps->create(true); + p.setPosition(position); + p.setShapeID(cylindricalBoundaryShape); + auto idx = p.getIdx(); + + Vec3 orthogonalAxis(0_r, -1_r, 1_r); + std::vector<Vec3> testPositions {position, position + axis, position + 1.1_r*radius*orthogonalAxis.getNormalized()}; + std::vector<bool> shouldBeContained {false, false, true}; + + for(size_t i = 0; i < testPositions.size(); ++i) + { + bool isContained = singleCast(idx, accessor, containsPointFctr, accessor, testPositions[i] ); + WALBERLA_CHECK_EQUAL(isContained, shouldBeContained[i], "Cylindrical boundary check, case " << i << ": Wrong containment info for position " << testPositions[i] ); + } + } + + return 0; + +} + +} //namespace contains_point_test + +int main( int argc, char **argv ){ + contains_point_test::main(argc, argv); +} diff --git a/tests/mesa_pd/common/IntersectionRatio.cpp b/tests/mesa_pd/common/IntersectionRatio.cpp index 71db12ae58ce1e05ea246c0cc9a3e670d8752f99..edb34250e266717a1ea60fa1c65cd9a43d185cb2 100644 --- a/tests/mesa_pd/common/IntersectionRatio.cpp +++ b/tests/mesa_pd/common/IntersectionRatio.cpp @@ -24,36 +24,26 @@ #include "core/math/all.h" #include "mesa_pd/common/RayParticleIntersection.h" -#include "mesa_pd/data/ParticleAccessor.h" +#include "mesa_pd/data/ParticleAccessorWithShape.h" #include "mesa_pd/data/ParticleStorage.h" #include "mesa_pd/data/ShapeStorage.h" #include "mesa_pd/data/DataTypes.h" +#include <mesa_pd/data/shape/Ellipsoid.h> +#include <mesa_pd/data/shape/HalfSpace.h> +#include <mesa_pd/data/shape/Sphere.h> #include "mesa_pd/kernel/SingleCast.h" namespace intersection_ratio_test { using namespace walberla; +using mesa_pd::Vec3; - -class ParticleAccessorWithShape : public mesa_pd::data::ParticleAccessor -{ -public: - ParticleAccessorWithShape(std::shared_ptr<mesa_pd::data::ParticleStorage>& ps, std::shared_ptr<mesa_pd::data::ShapeStorage>& ss) - : ParticleAccessor(ps) - , ss_(ss) - {} - - mesa_pd::data::BaseShape* getShape(const size_t p_idx) const {return ss_->shapes[ps_->getShapeID(p_idx)].get();} -private: - std::shared_ptr<mesa_pd::data::ShapeStorage> ss_; -}; - - -/*!\brief Tests the ray-particle intersection ratio functionality of the RPDUtility.h in the lbm_rpd_coupling module +/*!\brief Tests the ray-particle intersection ratio functionality implemented in mesa_pd/common/RayParticleIntersection.h * * Currently the following shapes are tested: * - sphere - * - halfspace ( default and rotated ) + * - halfspace + * - ellipsoid (default and rotated) * * Additionally, the default variant with the bisection line search is tested with the help of a sphere. */ @@ -69,10 +59,10 @@ int main( int argc, char **argv ) auto ps = std::make_shared<mesa_pd::data::ParticleStorage>(1); auto shapeStorage = std::make_shared<mesa_pd::data::ShapeStorage>(); - using ParticleAccessor = ParticleAccessorWithShape; + using ParticleAccessor = mesa_pd::data::ParticleAccessorWithShape; ParticleAccessor accessor(ps, shapeStorage); - const real_t epsilon( real_t(1e-4) ); + const real_t epsilon( 1e-4_r ); mesa_pd::kernel::SingleCast singleCast; mesa_pd::RayParticleIntersectionRatioFunctor intersectionRatioFctr; @@ -81,33 +71,33 @@ int main( int argc, char **argv ) // SPHERE // //////////// { - real_t sphereRadius = real_t(1); + real_t sphereRadius = 1_r; auto sphereShape = shapeStorage->create<mesa_pd::data::Sphere>( sphereRadius ); - Vector3<real_t> position(real_t(1), real_t(0), real_t(0)); + Vec3 position(1_r, 0_r, 0_r); mesa_pd::data::Particle&& p = *ps->create(); p.setPosition(position); p.setShapeID(sphereShape); auto idx = p.getIdx(); - Vector3<real_t> pos1(real_t(-0.5), real_t(0), real_t(0)); - Vector3<real_t> dir1(real_t(1), real_t(0), real_t(0)); + Vec3 pos1(-0.5_r, 0_r, 0_r); + Vec3 dir1(1_r, 0_r, 0_r); real_t delta1 = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos1, dir1, epsilon ); - WALBERLA_CHECK_FLOAT_EQUAL(delta1, real_t(0.5), "Intersection ratio 1 with sphere wrong!"); + WALBERLA_CHECK_FLOAT_EQUAL(delta1, 0.5_r, "Intersection ratio 1 with sphere wrong!"); - Vector3<real_t> pos2(real_t(1), real_t(1), real_t(1)); - Vector3<real_t> dir2(real_t(0), -real_t(1), -real_t(1)); + Vec3 pos2(1_r, 1_r, 1_r); + Vec3 dir2(0_r, -1_r, -1_r); real_t delta2 = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos2, dir2, epsilon ); - WALBERLA_CHECK_FLOAT_EQUAL(delta2, (std::sqrt(2) - real_t(1)) / std::sqrt(2), "Intersection ratio 2 with sphere wrong!"); + WALBERLA_CHECK_FLOAT_EQUAL(delta2, (std::sqrt(2_r) - 1_r) / std::sqrt(2_r), "Intersection ratio 2 with sphere wrong!"); } /////////////// // HALFSPACE // /////////////// { - Vector3<real_t> position(real_t(1), real_t(0), real_t(0)); - Vector3<real_t> normal(real_t(0), real_t(1), real_t(1)); + Vec3 position(1_r, 0_r, 0_r); + Vec3 normal(0_r, 1_r, 1_r); auto planeShape = shapeStorage->create<mesa_pd::data::HalfSpace>( normal.getNormalized() ); @@ -116,25 +106,58 @@ int main( int argc, char **argv ) p.setShapeID(planeShape); auto idx = p.getIdx(); - Vector3<real_t> pos1(real_t(1), real_t(0.5), real_t(0.5)); - Vector3<real_t> dir1(real_t(0), -real_t(1), -real_t(1)); + Vec3 pos1(1_r, 0.5_r, 0.5_r); + Vec3 dir1(0_r, -1_r, -1_r); real_t delta1 = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos1, dir1, epsilon ); - WALBERLA_CHECK_FLOAT_EQUAL(delta1, real_t(0.5), "Intersection ratio 1 with half space wrong!"); + WALBERLA_CHECK_FLOAT_EQUAL(delta1, 0.5_r, "Intersection ratio 1 with half space wrong!"); - Vector3<real_t> dir2(real_t(0), real_t(0), -real_t(2)); + Vec3 dir2(0_r, 0_r, -2_r); real_t delta2 = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos1, dir2, epsilon ); - WALBERLA_CHECK_FLOAT_EQUAL(delta2, real_t(0.5), "Intersection ratio 2 with half space wrong!"); + WALBERLA_CHECK_FLOAT_EQUAL(delta2, 0.5_r, "Intersection ratio 2 with half space wrong!"); - Vector3<real_t> dir3(real_t(0), -real_t(3), real_t(0)); + Vec3 dir3(0_r, -3_r, 0_r); real_t delta3 = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos1, dir3, epsilon ); - WALBERLA_CHECK_FLOAT_EQUAL(delta3, real_t(1)/real_t(3), "Intersection ratio 3 with half space wrong!"); + WALBERLA_CHECK_FLOAT_EQUAL(delta3, 1_r/3_r, "Intersection ratio 3 with half space wrong!"); } - ///////////////////////// - // HALFSPACE (rotated) // - ///////////////////////// + /////////////// + // ELLIPSOID // + /////////////// + { + Vec3 semiAxes{1_r, 1_r, 2_r}; + auto ellipsoidShape = shapeStorage->create<mesa_pd::data::Ellipsoid>( semiAxes ); + + Vec3 position(1_r, 0_r, 0_r); + + mesa_pd::data::Particle&& p = *ps->create(); + p.setPosition(position); + p.setShapeID(ellipsoidShape); + auto idx = p.getIdx(); + + Vec3 pos1(-0.5_r, 0_r, 0_r); + Vec3 dir1(1_r, 0_r, 0_r); + real_t delta1 = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos1, dir1, epsilon ); + WALBERLA_CHECK_FLOAT_EQUAL(delta1, 0.5_r, "Intersection ratio 1 with ellipsoid wrong!"); + + Vec3 pos2(1_r, 1.5_r, 0_r); + Vec3 dir2(0_r, -1_r, 0_r); + real_t delta2 = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos2, dir2, epsilon ); + WALBERLA_CHECK_FLOAT_EQUAL(delta2, 0.5_r, "Intersection ratio 2 with ellipsoid wrong!"); + + auto rotation = p.getRotation(); + rotation.rotate( Vec3(1_r,0_r,0_r), math::pi / 2_r ); // rotate by 90° around x axis + p.setRotation(rotation); + + real_t delta1Rot = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos1, dir1, epsilon ); + WALBERLA_CHECK_FLOAT_EQUAL(delta1Rot, 0.5_r, "Intersection ratio 1 with rotated ellipsoid wrong!"); + + Vec3 pos2Rot(1_r, 0_r, -1.5_r); + Vec3 dir2Rot(0_r, 0_r, 1_r); + real_t delta2Rot = singleCast(idx, accessor, intersectionRatioFctr, accessor, pos2Rot, dir2Rot, epsilon ); + WALBERLA_CHECK_FLOAT_EQUAL(delta2Rot, 0.5_r, "Intersection ratio 2 with rotated ellipsoid wrong!"); + + } - // removed because rotation is not supported by half space, see half space docu /////////////////////////// // Bisection Line Search // @@ -142,25 +165,25 @@ int main( int argc, char **argv ) { // note: here tested with a sphere and therefore called explicitly because otherwise the sphere specialization would be selected - real_t sphereRadius = real_t(1); + real_t sphereRadius = 1_r; auto sphereShape = shapeStorage->create<mesa_pd::data::Sphere>( sphereRadius ); - Vector3<real_t> position(real_t(1), real_t(0), real_t(0)); + Vec3 position(1_r, 0_r, 0_r); mesa_pd::data::Particle&& p = *ps->create(); p.setPosition(position); p.setShapeID(sphereShape); auto idx = p.getIdx(); - Vector3<real_t> pos1(real_t(-0.5), real_t(0), real_t(0)); - Vector3<real_t> dir1(real_t(1), real_t(0), real_t(0)); + Vec3 pos1(-0.5_r, 0_r, 0_r); + Vec3 dir1(1_r, 0_r, 0_r); real_t delta1 = mesa_pd::intersectionRatioBisection(idx, accessor, pos1, dir1, epsilon); - WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(delta1, real_t(0.5), epsilon, "Intersection ratio 1 with bisection line search for sphere wrong!"); + WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(delta1, 0.5_r, epsilon, "Intersection ratio 1 with bisection line search for sphere wrong!"); - Vector3<real_t> pos2(real_t(1), real_t(1), real_t(1)); - Vector3<real_t> dir2(real_t(0), -real_t(1), -real_t(1)); + Vec3 pos2(1_r, 1_r, 1_r); + Vec3 dir2(0_r, -1_r, -1_r); real_t delta2 = mesa_pd::intersectionRatioBisection(idx, accessor, pos2, dir2, epsilon); - WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(delta2, (std::sqrt(2) - real_t(1)) / std::sqrt(2), epsilon, "Intersection ratio 2 with bisection line search for sphere wrong!"); + WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(delta2, (std::sqrt(2_r) - 1_r) / std::sqrt(2_r), epsilon, "Intersection ratio 2 with bisection line search for sphere wrong!"); } return 0;