Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F92945871
geometry.hh
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Nov 25, 01:18
Size
16 KB
Mime Type
text/x-c
Expires
Wed, Nov 27, 01:18 (1 d, 21 h)
Engine
blob
Format
Raw Data
Handle
22543313
Attached To
rMUSPECTRE µSpectre
geometry.hh
View Options
/**
* @file geometry.hh
*
* @author Till Junge <till.junge@altermail.ch>
*
* @date 18 Apr 2018
*
* @brief Geometric calculation helpers
*
* Copyright © 2018 Till Junge
*
* µSpectre 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, or (at
* your option) any later version.
*
* µSpectre 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 GNU Emacs; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "common/muSpectre_common.hh"
#include <libmugrid/tensor_algebra.hh>
#include <libmugrid/eigen_tools.hh>
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <array>
#ifndef SRC_COMMON_GEOMETRY_HH_
#define SRC_COMMON_GEOMETRY_HH_
namespace muSpectre {
/**
* The rotation matrices depend on the order in which we rotate
* around different axes. See [[
* https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix ]] to
* find the matrices
*/
enum class RotationOrder {
Z,
XZXEuler,
XYXEuler,
YXYEuler,
YZYEuler,
ZYZEuler,
ZXZEuler,
XZYTaitBryan,
XYZTaitBryan,
YXZTaitBryan,
YZXTaitBryan,
ZYXTaitBryan,
ZXYTaitBryan
};
namespace internal {
template <Dim_t Dim>
struct DefaultOrder {
constexpr static RotationOrder value{RotationOrder::ZXYTaitBryan};
};
template <>
struct DefaultOrder<twoD> {
constexpr static RotationOrder value{RotationOrder::Z};
};
} // namespace internal
template <Dim_t Dim>
class RotatorBase {
public:
using RotMat_t = Eigen::Matrix<Real, Dim, Dim>;
//! Default constructor
RotatorBase() = delete;
explicit RotatorBase(const RotMat_t rotation_matrix_input)
: rot_mat{rotation_matrix_input} {}
//! Copy constructor
RotatorBase(const RotatorBase & other) = default;
//! Move constructor
RotatorBase(RotatorBase && other) = default;
//! Destructor
virtual ~RotatorBase() = default;
//! Copy assignment operator
RotatorBase & operator=(const RotatorBase & other) = default;
//! Move assignment operator
RotatorBase & operator=(RotatorBase && other) = default;
/**
* Applies the rotation into the frame defined by the rotation
* matrix
*
* @param input is a first-, second-, or fourth-rank tensor
* (column vector, square matrix, or T4Matrix, or a Eigen::Map of
* either of these, or an expression that evaluates into any of
* these)
*/
template <class In_t>
inline decltype(auto) rotate(In_t && input);
/**
* Applies the rotation back out from the frame defined by the
* rotation matrix
*
* @param input is a first-, second-, or fourth-rank tensor
* (column vector, square matrix, or T4Matrix, or a Eigen::Map of
* either of these, or an expression that evaluates into any of
* these)
*/
template <class In_t>
inline decltype(auto) rotate_back(In_t && input);
const RotMat_t & get_rot_mat() const { return this->rot_mat; }
void set_rot_mat(const Eigen::Ref<RotMat_t> & mat_inp) {
this->rot_mat = mat_inp;
}
protected:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
RotMat_t rot_mat;
};
template <Dim_t Dim, RotationOrder Order = internal::DefaultOrder<Dim>::value>
class RotatorAngle : public RotatorBase<Dim> {
static_assert(((Dim == twoD) and (Order == RotationOrder::Z)) or
((Dim == threeD) and (Order != RotationOrder::Z)),
"In 2d, only order 'Z' makes sense. In 3d, it doesn't");
public:
using Parent = RotatorBase<Dim>;
using Angles_t = Eigen::Matrix<Real, (Dim == twoD) ? 1 : 3, 1>;
using RotMat_t = Eigen::Matrix<Real, Dim, Dim>;
//! Default constructor
RotatorAngle() = delete;
explicit RotatorAngle(const Eigen::Ref<const Angles_t> & angles_inp)
: Parent(this->compute_rotation_matrix_angle(angles_inp)),
angles{angles_inp} {}
//! Copy constructor
RotatorAngle(const RotatorAngle & other) = default;
//! Move constructor
RotatorAngle(RotatorAngle && other) = default;
//! Destructor
virtual ~RotatorAngle() = default;
//! Copy assignment operator
RotatorAngle & operator=(const RotatorAngle & other) = default;
//! Move assignment operator
RotatorAngle & operator=(RotatorAngle && other) = default;
protected:
inline RotMat_t compute_rotation_matrix_angle(Angles_t angles);
inline RotMat_t compute_this_rotation_matrix_angle() {
return compute_rotation_matrix(this->angles);
}
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
Angles_t angles;
private:
};
/* ---------------------------------------------------------------------- */
namespace internal {
template <RotationOrder Order, Dim_t Dim>
struct RotationMatrixComputerAngle {};
template <RotationOrder Order>
struct RotationMatrixComputerAngle<Order, twoD> {
constexpr static Dim_t Dim{twoD};
using RotMat_t = typename RotatorAngle<Dim, Order>::RotMat_t;
using Angles_t = typename RotatorAngle<Dim, Order>::Angles_t;
inline static decltype(auto)
compute(const Eigen::Ref<Angles_t> & angles) {
static_assert(Order == RotationOrder::Z,
"Two-d rotations can only be around the z axis");
return RotMat_t(Eigen::Rotation2Dd(angles(0)));
}
};
template <RotationOrder Order>
struct RotationMatrixComputerAngle<Order, threeD> {
constexpr static Dim_t Dim{threeD};
using RotMat_t = typename RotatorAngle<Dim, Order>::RotMat_t;
using Angles_t = typename RotatorAngle<Dim, Order>::Angles_t;
inline static decltype(auto)
compute(const Eigen::Ref<Angles_t> & angles) {
static_assert(Order != RotationOrder::Z,
"three-d rotations cannot only be around the z axis");
switch (Order) {
case RotationOrder::ZXZEuler: {
return RotMat_t(
(Eigen::AngleAxisd(angles(0), Eigen::Vector3d::UnitZ()) *
Eigen::AngleAxisd(angles(1), Eigen::Vector3d::UnitX()) *
Eigen::AngleAxisd(angles(2), Eigen::Vector3d::UnitZ())));
break;
}
case RotationOrder::ZXYTaitBryan: {
return RotMat_t(
(Eigen::AngleAxisd(angles(0), Eigen::Vector3d::UnitZ()) *
Eigen::AngleAxisd(angles(1), Eigen::Vector3d::UnitX()) *
Eigen::AngleAxisd(angles(2), Eigen::Vector3d::UnitY())));
}
default: { throw std::runtime_error("not yet implemented."); }
}
}
};
} // namespace internal
/* ---------------------------------------------------------------------- */
template <Dim_t Dim, RotationOrder Order>
auto RotatorAngle<Dim, Order>::compute_rotation_matrix_angle(Angles_t angles)
-> RotMat_t {
return internal::RotationMatrixComputerAngle<Order, Dim>::compute(angles);
}
/* ---------------------------------------------------------------------- */
/**
* this class is used to make the vector a aligned to the vec b by means of a
rotation system, the input for the constructor is the vector itself and the
functions
rotate and rotate back would be available as they exist in the parent class
(RotatorBase) and can be used in order to do the functionality of the class
*/
template <Dim_t Dim>
class RotatorTwoVec : public RotatorBase<Dim> {
public:
using Parent = RotatorBase<Dim>;
using Vec_t = Eigen::Matrix<Real, (Dim == twoD) ? 2 : 3, 1>;
using RotMat_t = Eigen::Matrix<Real, Dim, Dim>;
//! Default constructor
RotatorTwoVec() = delete;
RotatorTwoVec(Vec_t vec_a_inp, Vec_t vec_b_inp)
: Parent(this->compute_rotation_matrix_TwoVec(vec_a_inp, vec_b_inp)),
vec_ref{vec_a_inp}, vec_des{vec_b_inp} {}
//! Copy constructor
RotatorTwoVec(const RotatorTwoVec & other) = default;
//! Move constructor
RotatorTwoVec(RotatorTwoVec && other) = default;
//! Destructor
virtual ~RotatorTwoVec() = default;
//! Copy assignment operator
RotatorTwoVec & operator=(const RotatorTwoVec & other) = default;
//! Move assignment operator
RotatorTwoVec & operator=(RotatorTwoVec && other) = default;
protected:
inline RotMat_t compute_rotation_matrix_TwoVec(Vec_t vec_ref,
Vec_t vec_des);
inline RotMat_t compute_this_rotation_matrix_TwoVec() {
return compute_rotation_matrix(this->vec_ref, this->vec_des);
}
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
Vec_t vec_ref, vec_des;
private:
};
/* ---------------------------------------------------------------------- */
namespace internal {
template <Dim_t Dim>
struct RotationMatrixComputerTwoVec {};
template <>
struct RotationMatrixComputerTwoVec<twoD> {
constexpr static Dim_t Dim{twoD};
using RotMat_t = typename RotatorTwoVec<Dim>::RotMat_t;
using Vec_t = typename RotatorTwoVec<Dim>::Vec_t;
inline static RotMat_t compute(Vec_t vec_ref, Vec_t vec_des) {
Real v_ref_norm =
sqrt(vec_ref(0) * vec_ref(0) + vec_ref(1) * vec_ref(1));
Real v_des_norm =
sqrt(vec_des(0) * vec_des(0) + vec_des(1) * vec_des(1));
RotMat_t ret_mat;
ret_mat(0, 0) = ret_mat(1, 1) =
(((vec_ref(0) / v_ref_norm) * (vec_des(0) / v_des_norm)) +
((vec_des(1) / v_des_norm) * (vec_ref(1) / v_ref_norm)));
ret_mat(1, 0) =
(((vec_ref(0) / v_ref_norm) * (vec_des(1) / v_des_norm)) -
((vec_des(0) / v_des_norm) * (vec_ref(1) / v_ref_norm)));
ret_mat(0, 1) = -ret_mat(1, 0);
return ret_mat;
}
};
template <>
struct RotationMatrixComputerTwoVec<threeD> {
constexpr static Dim_t Dim{threeD};
using RotMat_t = typename RotatorTwoVec<Dim>::RotMat_t;
using Vec_t = typename RotatorTwoVec<Dim>::Vec_t;
inline static RotMat_t compute(Vec_t vec_ref, Vec_t vec_des) {
return Eigen::Quaternion<double>::FromTwoVectors(vec_ref, vec_des)
.normalized()
.toRotationMatrix();
}
};
} // namespace internal
/* ---------------------------------------------------------------------- */
template <Dim_t Dim>
auto RotatorTwoVec<Dim>::compute_rotation_matrix_TwoVec(Vec_t vec_ref,
Vec_t vec_des)
-> RotMat_t {
return internal::RotationMatrixComputerTwoVec<Dim>::compute(vec_ref,
vec_des);
}
/* ---------------------------------------------------------------------- */
/**
* this class is used to make a vector aligned to x-axis of the coordinate
system, the input for the constructor is the vector itself and the functions
rotate and rotate back would be available as they exist in the parent class
(RotatorBase) nad can be used in order to do the functionality of the class
*/
template <Dim_t Dim>
class RotatorNormal : public RotatorBase<Dim> {
public:
using Parent = RotatorBase<Dim>;
using Vec_t = Eigen::Matrix<Real, Dim, 1>;
using RotMat_t = Eigen::Matrix<Real, Dim, Dim>;
//! Default constructor
RotatorNormal() = delete;
explicit RotatorNormal(Vec_t vec)
: Parent(this->compute_rotation_matrix_normal(vec)), vec{vec} {}
//! Copy constructor
RotatorNormal(const RotatorNormal & other) = default;
//! Move constructor
RotatorNormal(RotatorNormal && other) = default;
//! Destructor
virtual ~RotatorNormal() = default;
//! Copy assignment operator
RotatorNormal & operator=(const RotatorNormal & other) = default;
//! Move assignment operator
RotatorNormal & operator=(RotatorNormal && other) = default;
protected:
inline RotMat_t compute_rotation_matrix_normal(Vec_t vec);
inline RotMat_t compute_this_rotation_matrix_normal() {
return compute_rotation_matrix_normal(this->vec);
}
EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
Vec_t vec;
private:
};
/* ---------------------------------------------------------------------- */
namespace internal {
template <Dim_t Dim>
struct RotationMatrixComputerNormal {};
template <>
struct RotationMatrixComputerNormal<twoD> {
constexpr static Dim_t Dim{twoD};
using RotMat_t = typename RotatorTwoVec<Dim>::RotMat_t;
using Vec_t = typename RotatorTwoVec<Dim>::Vec_t;
inline static RotMat_t compute(Vec_t vec) {
Real vec_norm = sqrt(vec(0) * vec(0) + vec(1) * vec(1));
Vec_t x;
x << 1.0, 0.0;
RotMat_t ret_mat;
ret_mat(0, 0) = ret_mat(1, 1) = ((vec(0) / vec_norm) * x(0));
ret_mat(1, 0) = -(-(vec(1) / vec_norm) * x(0));
ret_mat(0, 1) = -ret_mat(1, 0);
return ret_mat;
}
};
template <>
struct RotationMatrixComputerNormal<threeD> {
constexpr static Dim_t Dim{threeD};
using RotMat_t = typename RotatorTwoVec<Dim>::RotMat_t;
using Vec_t = typename RotatorTwoVec<Dim>::Vec_t;
inline static RotMat_t compute(Vec_t vec) {
Real eps = 0.1;
Vec_t vec1 = vec / vec.norm();
Vec_t x(Vec_t::UnitX());
Vec_t y(Vec_t::UnitY());
Vec_t n_x = vec1.cross(x);
Vec_t vec2 = ((n_x.norm() > eps) * n_x +
(1 - (n_x.norm() > eps)) * (vec1.cross(y)));
Vec_t vec3 = vec1.cross(vec2);
RotMat_t ret_mat;
ret_mat << vec1(0), vec2(0) / vec2.norm(), vec3(0) / vec3.norm(),
vec1(1), vec2(1) / vec2.norm(), vec3(1) / vec3.norm(), vec1(2),
vec2(2) / vec2.norm(), vec3(2) / vec3.norm();
return ret_mat;
}
};
} // namespace internal
/* ---------------------------------------------------------------------- */
template <Dim_t Dim>
auto RotatorNormal<Dim>::compute_rotation_matrix_normal(Vec_t vec)
-> RotMat_t {
return internal::RotationMatrixComputerNormal<Dim>::compute(vec);
}
/* ---------------------------------------------------------------------- */
namespace internal {
template <Dim_t Rank>
struct RotationHelper {};
/* ---------------------------------------------------------------------- */
template <>
struct RotationHelper<firstOrder> {
template <class In_t, class Rot_t>
inline static decltype(auto) rotate(In_t && input, Rot_t && R) {
return R * input;
}
};
/* ---------------------------------------------------------------------- */
template <>
struct RotationHelper<secondOrder> {
template <class In_t, class Rot_t>
inline static decltype(auto) rotate(In_t && input, Rot_t && R) {
return R * input * R.transpose();
}
};
/* ---------------------------------------------------------------------- */
template <>
struct RotationHelper<fourthOrder> {
template <class In_t, class Rot_t>
inline static decltype(auto) rotate(In_t && input, Rot_t && R) {
constexpr Dim_t Dim{muGrid::EigenCheck::tensor_dim<Rot_t>::value};
auto && rotator_forward{
Matrices::outer_under(R.transpose(), R.transpose())};
auto && rotator_back = Matrices::outer_under(R, R);
// Clarification. When I return this value as an
// expression, clang segfaults or returns an uninitialised
// tensor, hence the explicit cast into a T4Mat.
return muGrid::T4Mat<Real, Dim>(rotator_back * input * rotator_forward);
}
};
} // namespace internal
/* ---------------------------------------------------------------------- */
template <Dim_t Dim>
template <class In_t>
auto RotatorBase<Dim>::rotate(In_t && input) -> decltype(auto) {
constexpr Dim_t tensor_rank{
muGrid::EigenCheck::tensor_rank<In_t, Dim>::value};
return internal::RotationHelper<tensor_rank>::rotate(
std::forward<In_t>(input), this->rot_mat);
}
/* ---------------------------------------------------------------------- */
template <Dim_t Dim>
template <class In_t>
auto RotatorBase<Dim>::rotate_back(In_t && input) -> decltype(auto) {
constexpr Dim_t tensor_rank{
muGrid::EigenCheck::tensor_rank<In_t, Dim>::value};
return internal::RotationHelper<tensor_rank>::rotate(
std::forward<In_t>(input), this->rot_mat.transpose());
}
} // namespace muSpectre
#endif // SRC_COMMON_GEOMETRY_HH_
Event Timeline
Log In to Comment