Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transform simplifier #1131

Merged
merged 9 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/orange/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ list(APPEND SOURCES
transform/SignedPermutation.cc
transform/TransformHasher.cc
transform/TransformIO.cc
transform/TransformSimplifier.cc
transform/Transformation.cc
transform/VariantTransform.cc
)
Expand Down
63 changes: 62 additions & 1 deletion src/orange/MatrixUtils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,26 @@ namespace celeritas
template<class T>
T determinant(SquareMatrix<T, 3> const& mat)
{
return mat[0][0] * mat[1][1] * mat[2][2] + mat[1][0] * mat[2][1] * mat[0][2]
// clang-format off
return mat[0][0] * mat[1][1] * mat[2][2]
+ mat[1][0] * mat[2][1] * mat[0][2]
+ mat[2][0] * mat[0][1] * mat[1][2]
- mat[2][0] * mat[1][1] * mat[0][2]
- mat[1][0] * mat[0][1] * mat[2][2]
- mat[0][0] * mat[2][1] * mat[1][2];
// clang-format on
}

//---------------------------------------------------------------------------//
/*!
* Calculate the trace of a 3x3 matrix.
*
* The trace is just the sum of the diagonal elements.
*/
template<class T>
T trace(SquareMatrix<T, 3> const& mat)
{
return mat[0][0] + mat[1][1] + mat[2][2];
}

//---------------------------------------------------------------------------//
Expand Down Expand Up @@ -136,6 +151,48 @@ void orthonormalize(SquareMatrix<T, N>* mat)
CELER_ENSURE(soft_equal(std::fabs(determinant(*mat)), T{1}));
}

//---------------------------------------------------------------------------//
/*!
* Create a C-ordered rotation matrix from an arbitrary rotation.
*
* This is equation (38) in "Rotation Matrices in Two, Three, and Many
* Dimensions", Physics 116A, UC Santa Cruz,
* http://scipp.ucsc.edu/~haber/ph116A/.
*
* \param ax Axis of rotation (unit vector)
* \param theta Rotation
*/
Mat3 make_rotation(Real3 const& ax, Turn theta)
{
CELER_EXPECT(is_soft_unit_vector(ax));
CELER_EXPECT(theta >= Turn{0} && theta <= Turn{real_type(0.5)});

// Axis/direction enumeration
enum
{
X = 0,
Y = 1,
Z = 2
};

// Calculate sin and cosine with less precision loss using "turn" value
real_type cost;
real_type sint;
sincos(theta, &sint, &cost);

Mat3 r{Real3{cost + ipow<2>(ax[X]) * (1 - cost),
ax[X] * ax[Y] * (1 - cost) - ax[Z] * sint,
ax[X] * ax[Z] * (1 - cost) + ax[Y] * sint},
Real3{ax[X] * ax[Y] * (1 - cost) + ax[Z] * sint,
cost + ipow<2>(ax[Y]) * (1 - cost),
ax[Y] * ax[Z] * (1 - cost) - ax[X] * sint},
Real3{ax[X] * ax[Z] * (1 - cost) - ax[Y] * sint,
ax[Y] * ax[Z] * (1 - cost) + ax[X] * sint,
cost + ipow<2>(ax[Z]) * (1 - cost)}};
CELER_ENSURE(soft_equal(std::fabs(determinant(r)), real_type{1}));
return r;
}

//---------------------------------------------------------------------------//
/*!
* Create a C-ordered rotation matrix.
Expand Down Expand Up @@ -190,6 +247,10 @@ template int determinant(SquareMatrix<int, 3> const&);
template float determinant(SquareMatrix<float, 3> const&);
template double determinant(SquareMatrix<double, 3> const&);

template int trace(SquareMatrix<int, 3> const&);
template float trace(SquareMatrix<float, 3> const&);
template double trace(SquareMatrix<double, 3> const&);

template void orthonormalize(SquareMatrix<float, 3>*);
template void orthonormalize(SquareMatrix<double, 3>*);

Expand Down
9 changes: 8 additions & 1 deletion src/orange/MatrixUtils.hh
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ gemv(matrix::TransposePolicy, SquareMatrix<T, N> const& a, Array<T, N> const& x)
template<class T>
T determinant(SquareMatrix<T, 3> const& mat);

// Calculate the trace of a matrix
template<class T>
T trace(SquareMatrix<T, 3> const& mat);

// Perform a matrix-matrix multiply
template<class T, size_type N>
SquareMatrix<T, N>
Expand All @@ -89,7 +93,10 @@ SquareMatrix<T, N> gemm(matrix::TransposePolicy,
template<class T, size_type N>
void orthonormalize(SquareMatrix<T, N>* mat);

// Create a C-ordered rotation matrix
// Create a C-ordered rotation matrix about an arbitrary axis
SquareMatrixReal3 make_rotation(Real3 const& ax, Turn rev);

// Create a C-ordered rotation matrix about a cartesian axis
SquareMatrixReal3 make_rotation(Axis ax, Turn rev);

// Apply a rotation to an existing C-ordered rotation matrix
Expand Down
11 changes: 11 additions & 0 deletions src/orange/orangeinp/detail/CsgUnitBuilder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "corecel/io/Logger.hh"
#include "corecel/io/StreamableVariant.hh"
#include "orange/transform/TransformIO.hh"
#include "orange/transform/TransformSimplifier.hh"

namespace celeritas
{
Expand Down Expand Up @@ -49,6 +50,16 @@ BoundingZone const& CsgUnitBuilder::bounds(NodeId nid) const
return iter->second.bounds;
}

//---------------------------------------------------------------------------//
/*!
* Insert transform with simplification and deduplication.
*/
TransformId CsgUnitBuilder::insert_transform(VariantTransform const& vt)
{
auto simplified = std::visit(TransformSimplifier(tol_), vt);
return this->insert_transform_(std::move(simplified));
}

//---------------------------------------------------------------------------//
/*!
* Set a bounding zone and transform for a node.
Expand Down
11 changes: 1 addition & 10 deletions src/orange/orangeinp/detail/CsgUnitBuilder.hh
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class CsgUnitBuilder
inline NodeInsertion insert_csg(Args&&... args);

// Insert a transform
inline TransformId insert_transform(VariantTransform&& vt);
TransformId insert_transform(VariantTransform const& vt);

// Insert node metadata
inline void insert_md(NodeId node, Metadata&& md);
Expand Down Expand Up @@ -173,15 +173,6 @@ auto CsgUnitBuilder::insert_csg(Args&&... args) -> NodeInsertion
return result;
}

//---------------------------------------------------------------------------//
/*!
* Insert transform with deduplication.
*/
TransformId CsgUnitBuilder::insert_transform(VariantTransform&& vt)
{
return this->insert_transform_(std::move(vt));
}

//---------------------------------------------------------------------------//
/*!
* Insert node metadata.
Expand Down
2 changes: 1 addition & 1 deletion src/orange/surf/SoftSurfaceEqual.hh
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class SoftSurfaceEqual
{
public:
// Construct with tolerance
inline SoftSurfaceEqual(Tolerance<> const& tol);
explicit inline SoftSurfaceEqual(Tolerance<> const& tol);

//! Construct with relative tolerance only
explicit SoftSurfaceEqual(real_type rel)
Expand Down
3 changes: 3 additions & 0 deletions src/orange/surf/SurfaceSimplifier.hh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ namespace celeritas
* was applied.
*
* The embedded sense may be flipped as part of the simplifications.
*
* \todo Use a \c Tolerance object instead of a single tolerance, and compare
* implementations with \c SoftSurfaceEqual for consistency.
*/
class SurfaceSimplifier
{
Expand Down
47 changes: 47 additions & 0 deletions src/orange/transform/TransformSimplifier.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/transform/TransformSimplifier.cc
//---------------------------------------------------------------------------//
#include "TransformSimplifier.hh"

#include "corecel/math/ArrayUtils.hh"
#include "orange/MatrixUtils.hh"

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Translation may simplify to no transformation.
*/
VariantTransform TransformSimplifier::operator()(Translation const& t)
{
if (norm(t.translation()) <= eps_)
{
// Distance to origin is less than tolerance
return NoTransformation{};
}
return t;
}

//---------------------------------------------------------------------------//
/*!
* Simplify, possibly to translation or no transform.
*
* See the derivation in the class documentation.
*/
VariantTransform TransformSimplifier::operator()(Transformation const& t)
{
real_type tr = trace(t.rotation());
if (tr >= 3 - ipow<2>(eps_))
{
// Rotation results in no more then epsilon movement
return (*this)(Translation{t.translation()});
}
return t;
}

//---------------------------------------------------------------------------//
} // namespace celeritas
77 changes: 77 additions & 0 deletions src/orange/transform/TransformSimplifier.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file orange/transform/TransformSimplifier.hh
//---------------------------------------------------------------------------//
#pragma once

#include "corecel/Macros.hh"
#include "orange/OrangeTypes.hh"

#include "VariantTransform.hh"

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Return a simplified version of a transformation.
*
* Like surface simplification, we want to consider whether two different
* transformations will result in a distance change of \f$\epsilon\f$ for a
* point that's at the length scale from the origin. Setting the length scale
* to unity (the default), we use the relative tolerance.
*
* A *translation* can be deleted if its magnitude is less than epsilon.
*
* For a translation, we use the fact that the trace (sum of diagonal elements)
* of any proper (non-reflecting) rotation matrix has an angle of rotation \f[
\mathrm{Tr}[R] = 2 \cos \theta + 1
* \f] about an axis of rotation. Applying the rotation to a point
* at a unit distance will yield an iscoceles triangle with sides 1 and inner
* angle \f$\theta\f$. For the displacement to be no more than \f$\epsilon\f$
* then the angle must be \f[
\sin \theta/2 \le \epsilon/2
\f]
* which with some manipulation means that a "soft zero" rotation has a trace
* \f[
\mathrm{Tr}[R] \ge 3 - \epsilon^2 \,.
\f]
*
* Note that this means no rotational simplifications may be performed when the
* geometry tolerance is less than the square root of machine precision.
*/
class TransformSimplifier
{
public:
// Construct with tolerance
explicit inline TransformSimplifier(Tolerance<> const& tol);

//! No simplification can be applied to a null transformation
VariantTransform operator()(NoTransformation const& nt) { return nt; }

// Translation may simplify to no transformation
VariantTransform operator()(Translation const& nt);

// Simplify, possibly to translation or no transform
VariantTransform operator()(Transformation const& nt);

private:
real_type eps_;
};

//---------------------------------------------------------------------------//
// INLINE DEFINITIONS
//---------------------------------------------------------------------------//
/*!
* Construct with tolerance.
*/
TransformSimplifier::TransformSimplifier(Tolerance<> const& tol)
: eps_{tol.rel}
{
CELER_EXPECT(tol);
}

//---------------------------------------------------------------------------//
} // namespace celeritas
53 changes: 32 additions & 21 deletions test/corecel/math/Algorithms.test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -363,27 +363,38 @@ TEST(MathTest, sincospi)
EXPECT_DOUBLE_EQ(std::sin(m_pi * 0.1), sinpi(0.1));
EXPECT_DOUBLE_EQ(std::cos(m_pi * 0.1), cospi(0.1));

double s{0}, c{0};
sincospi(0.123, &s, &c);
EXPECT_DOUBLE_EQ(std::sin(m_pi * 0.123), s);
EXPECT_DOUBLE_EQ(std::cos(m_pi * 0.123), c);

// Test special cases
sincospi(0, &s, &c);
EXPECT_EQ(double(0.0), s);
EXPECT_EQ(double(1.0), c);

sincospi(0.5, &s, &c);
EXPECT_EQ(double(1.0), s);
EXPECT_EQ(double(0.0), c);

sincospi(1.0, &s, &c);
EXPECT_EQ(double(0.0), s);
EXPECT_EQ(double(-1.0), c);

sincospi(1.5, &s, &c);
EXPECT_EQ(double(-1.0), s);
EXPECT_EQ(double(0.0), c);
{
double s{0}, c{0};
sincospi(0.123, &s, &c);
EXPECT_DOUBLE_EQ(std::sin(m_pi * 0.123), s);
EXPECT_DOUBLE_EQ(std::cos(m_pi * 0.123), c);

// Test special cases
sincospi(0, &s, &c);
EXPECT_EQ(double(0.0), s);
EXPECT_EQ(double(1.0), c);

sincospi(0.5, &s, &c);
EXPECT_EQ(double(1.0), s);
EXPECT_EQ(double(0.0), c);

sincospi(1.0, &s, &c);
EXPECT_EQ(double(0.0), s);
EXPECT_EQ(double(-1.0), c);

sincospi(1.5, &s, &c);
EXPECT_EQ(double(-1.0), s);
EXPECT_EQ(double(0.0), c);
}
{
// This is about the threshold where sin(x) == 1.0f
float inp{0.000233115f};
float s{0}, c{0};
sincospi(inp, &s, &c);
EXPECT_FLOAT_EQ(1.0f, c);
EXPECT_FLOAT_EQ(std::sin(static_cast<float>(m_pi * inp)), s);
EXPECT_FLOAT_EQ(std::cos(static_cast<float>(m_pi * inp)), c);
}
}

//---------------------------------------------------------------------------//
Expand Down
5 changes: 3 additions & 2 deletions test/orange/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ celeritas_add_library(testcel_orange
)
celeritas_target_link_libraries(testcel_orange
PUBLIC
Celeritas::orange Celeritas::testcel_harness
Celeritas::orange Celeritas::testcel_harness Celeritas::testcel_core
PRIVATE
Celeritas::testcel_geocel Celeritas::testcel_core Celeritas::orange ${nlohmann_json_LIBRARIES}
Celeritas::testcel_geocel Celeritas::orange ${nlohmann_json_LIBRARIES}
)

#-----------------------------------------------------------------------------#
Expand Down Expand Up @@ -69,6 +69,7 @@ celeritas_add_test(orangeinp/detail/TransformInserter.test.cc)
# Transforms
set(CELERITASTEST_PREFIX orange/transform)
celeritas_add_test(transform/SignedPermutation.test.cc)
celeritas_add_test(transform/TransformSimplifier.test.cc)
celeritas_add_test(transform/Transformation.test.cc)
celeritas_add_test(transform/Translation.test.cc)
celeritas_add_test(transform/VariantTransform.test.cc)
Expand Down