MiniTensor Version of the Day
Loading...
Searching...
No Matches
MiniTensor_Quaternion.h
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MiniTensor Package
4//
5// Copyright 2016 NTESS and the MiniTensor contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#if !defined(MiniTensor_Quaternion_h)
11#define MiniTensor_Quaternion_h
12
13// Quaternions and conversions among rotation representations:
14// rotation matrix (rt), unit quaternion (q) and principal rotation
15// vector (rv). Ported from Norma.jl, which in turn adopted them from
16// the ONDAP FEM code. See: Object-oriented finite-element dynamic
17// simulation of geometrically nonlinear space structures, Victor
18// Balopoulos, Ph.D. dissertation, Cornell University, 1997.
20
21namespace minitensor {
22
23namespace impl {
24
25//
26// Compute sin(x)/x with asymptotic expansions near zero to avoid
27// division by small values. This function is frequently encountered
28// in rotational algebra when evaluating exp/log maps of
29// skew-symmetric matrices.
30//
31template<typename T>
33T
34sin_x_over_x(T const & x)
35{
36 using S = typename Sacado::ScalarType<T>::type;
37
38 T const
39 y = std::abs(x);
40
41 S const
42 y_val = Sacado::ScalarValue<T>::eval(y);
43
44 S const
45 epsilon2 = std::sqrt(machine_epsilon<T>());
46
47 S const
48 epsilon4 = std::sqrt(epsilon2);
49
50 if (y_val > epsilon4) {
51 return std::sin(y) / y;
52 } else if (y_val > epsilon2) {
53 return 1.0 - y * y / 6.0;
54 }
55
56 return T(1.0);
57}
58
59} // namespace impl
60
63
71template<typename T>
73{
74public:
75
81 {
82 }
83
90 Quaternion(T const & s, Vector<T, 3> const & v) : s_(s), v_(v)
91 {
92 }
93
102 Quaternion(T const & s, T const & v0, T const & v1, T const & v2) :
103 s_(s), v_(v0, v1, v2)
104 {
105 }
106
111 explicit
113 Quaternion(Vector<T, 4> const & q) : s_(q(0)), v_(q(1), q(2), q(3))
114 {
115 }
116
121 T const &
122 scalar() const
123 {
124 return s_;
125 }
126
131 T &
133 {
134 return s_;
135 }
136
141 Vector<T, 3> const &
142 vector() const
143 {
144 return v_;
145 }
146
153 {
154 return v_;
155 }
156
162 T const &
163 operator()(Index const i) const
164 {
165 assert(i < 4);
166 return i == 0 ? s_ : v_(i - 1);
167 }
168
174 T &
176 {
177 assert(i < 4);
178 return i == 0 ? s_ : v_(i - 1);
179 }
180
186 to_vector() const
187 {
189 q(4);
190
191 q(0) = s_;
192 q(1) = v_(0);
193 q(2) = v_(1);
194 q(3) = v_(2);
195
196 return q;
197 }
198
199private:
200
204 T
205 s_{};
206
212};
213
221template<typename T>
224operator*(Quaternion<T> const & p, Quaternion<T> const & q);
225
231template<typename T>
233bool
234operator==(Quaternion<T> const & p, Quaternion<T> const & q);
235
241template<typename T>
243bool
244operator!=(Quaternion<T> const & p, Quaternion<T> const & q);
245
251template<typename T>
254conjugate(Quaternion<T> const & q);
255
261template<typename T>
263T
264norm(Quaternion<T> const & q);
265
271template<typename T>
274inverse(Quaternion<T> const & q);
275
281template<typename T>
284unit(Quaternion<T> const & q);
285
292template<typename T>
293std::ostream &
294operator<<(std::ostream & os, Quaternion<T> const & q);
295
305template<typename T, Index N>
308q_of_rt(Tensor<T, N> const & R);
309
319template<typename T>
322rv_of_q(Quaternion<T> const & q);
323
333template<typename T, Index N>
336q_of_rv(Vector<T, N> const & rv);
337
344template<typename T>
347rt_of_q(Quaternion<T> const & q);
348
357template<typename T, Index N>
360rv_of_rt(Tensor<T, N> const & R);
361
368template<typename T, Index N>
371rt_of_rv(Vector<T, N> const & rv);
372
382template<typename T, Index N>
385rv_continue(Vector<T, N> const & old, Vector<T, N> const & prev);
386
387//
388// Hamilton product of two quaternions.
389//
390template<typename T>
394{
395 T const
396 s = p.scalar() * q.scalar() - dot(p.vector(), q.vector());
397
398 Vector<T, 3> const
399 v = p.scalar() * q.vector() + q.scalar() * p.vector() +
400 cross(p.vector(), q.vector());
401
402 return Quaternion<T>(s, v);
403}
404
405//
406// Quaternion equality.
407//
408template<typename T>
410bool
412{
413 return p.scalar() == q.scalar() && p.vector() == q.vector();
414}
415
416//
417// Quaternion inequality.
418//
419template<typename T>
421bool
423{
424 return !(p == q);
425}
426
427//
428// Quaternion conjugate.
429//
430template<typename T>
432Quaternion<T>
434{
435 return Quaternion<T>(q.scalar(), -q.vector());
436}
437
438//
439// Quaternion norm.
440//
441template<typename T>
443T
445{
446 return std::sqrt(
447 q.scalar() * q.scalar() + dot(q.vector(), q.vector()));
448}
449
450//
451// Quaternion inverse.
452//
453template<typename T>
455Quaternion<T>
457{
458 T const
459 norm_square = q.scalar() * q.scalar() + dot(q.vector(), q.vector());
460
461 return Quaternion<T>(q.scalar() / norm_square, -q.vector() / norm_square);
462}
463
464//
465// Quaternion normalized to unit norm.
466//
467template<typename T>
469Quaternion<T>
471{
472 T const
473 norm_q = norm(q);
474
475 return Quaternion<T>(q.scalar() / norm_q, q.vector() / norm_q);
476}
477
478//
479// Quaternion output.
480//
481template<typename T>
482std::ostream &
483operator<<(std::ostream & os, Quaternion<T> const & q)
484{
485 os << std::scientific << std::setprecision(17);
486
487 os << std::setw(24) << q.scalar();
488
489 for (Index i = 0; i < 3; ++i) {
490 os << "," << std::setw(24) << q.vector()(i);
491 }
492
493 return os;
494}
495
496//
497// Quaternion of a rotation matrix, Spurrier's algorithm.
498//
499template<typename T, Index N>
501Quaternion<T>
503{
504 assert(R.get_dimension() == 3);
505
506 //firewalls, make sure R \in SO(3)
507 assert(norm(dot_t(R, R) - eye<T, N>(3)) <
508 std::max(1.0e-12 * norm(R), 1.0e-12));
509 assert(std::abs(det(R) - 1.0) <
510 std::max(1.0e-12 * norm(R), 1.0e-12));
511
512 using S = typename Sacado::ScalarType<T>::type;
513
514 T const
515 trace_R = trace(R);
516
517 // Select the largest of the trace and the diagonal entries. The
518 // sentinel index 3 denotes the trace.
519 S
520 max_value = Sacado::ScalarValue<T>::eval(trace_R);
521
522 Index
523 max_index = 3;
524
525 for (Index i = 0; i < 3; ++i) {
526
527 S const
528 diagonal_value = Sacado::ScalarValue<T>::eval(R(i, i));
529
530 if (diagonal_value > max_value) {
531 max_value = diagonal_value;
532 max_index = i;
533 }
534 }
535
537 q;
538
539 switch (max_index) {
540
541 case 3:
542 {
543 T const
544 root = std::sqrt(trace_R + 1.0);
545
546 T const
547 factor = 0.5 / root;
548
549 q = Quaternion<T>(
550 0.5 * root,
551 factor * (R(2, 1) - R(1, 2)),
552 factor * (R(0, 2) - R(2, 0)),
553 factor * (R(1, 0) - R(0, 1)));
554 }
555 break;
556
557 case 2:
558 {
559 T const
560 root = std::sqrt(2.0 * R(2, 2) + 1.0 - trace_R);
561
562 T const
563 factor = 0.5 / root;
564
565 q = Quaternion<T>(
566 factor * (R(1, 0) - R(0, 1)),
567 factor * (R(0, 2) + R(2, 0)),
568 factor * (R(1, 2) + R(2, 1)),
569 0.5 * root);
570 }
571 break;
572
573 case 1:
574 {
575 T const
576 root = std::sqrt(2.0 * R(1, 1) + 1.0 - trace_R);
577
578 T const
579 factor = 0.5 / root;
580
581 q = Quaternion<T>(
582 factor * (R(0, 2) - R(2, 0)),
583 factor * (R(0, 1) + R(1, 0)),
584 0.5 * root,
585 factor * (R(1, 2) + R(2, 1)));
586 }
587 break;
588
589 case 0:
590 {
591 T const
592 root = std::sqrt(2.0 * R(0, 0) + 1.0 - trace_R);
593
594 T const
595 factor = 0.5 / root;
596
597 q = Quaternion<T>(
598 factor * (R(2, 1) - R(1, 2)),
599 0.5 * root,
600 factor * (R(0, 1) + R(1, 0)),
601 factor * (R(0, 2) + R(2, 0)));
602 }
603 break;
604
605 }
606
607 return q;
608}
609
610//
611// Principal rotation vector of a quaternion.
612//
613template<typename T>
615Vector<T, 3>
617{
618 // Normalize the sign so that the scalar part is non-negative,
619 // which selects the principal rotation |rv| <= pi.
620 bool const
621 negative = Sacado::ScalarValue<T>::eval(q.scalar()) < 0.0;
622
623 T const
624 qs = negative == true ? T(-q.scalar()) : q.scalar();
625
627 qv(q.vector()(0), q.vector()(1), q.vector()(2));
628
629 if (negative == true) {
630 for (Index i = 0; i < 3; ++i) {
631 qv(i) = -qv(i);
632 }
633 }
634
635 T const
636 qv_norm = norm(qv);
637
638 // Small rotation: rv = 2 qv to leading order.
639 if (Sacado::ScalarValue<T>::eval(qv_norm) <
640 std::sqrt(machine_epsilon<T>())) {
641 return 2.0 * qv;
642 }
643
644 T
645 rv_norm;
646
647 // asin is accurate for small vector parts, acos otherwise.
648 if (Sacado::ScalarValue<T>::eval(qv_norm) < std::sqrt(0.5)) {
649 rv_norm = 2.0 * std::asin(qv_norm);
650 } else {
651 rv_norm = 2.0 * std::acos(qs);
652 }
653
654 return rv_norm / qv_norm * qv;
655}
656
657//
658// Quaternion of a rotation vector.
659//
660template<typename T, Index N>
662Quaternion<T>
664{
665 assert(rv.get_dimension() == 3);
666
667 T const
668 half_norm = 0.5 * norm(rv);
669
670 T const
671 factor = 0.5 * impl::sin_x_over_x(half_norm);
672
673 return Quaternion<T>(
674 std::cos(half_norm),
675 factor * rv(0),
676 factor * rv(1),
677 factor * rv(2));
678}
679
680//
681// Rotation matrix of a quaternion.
682//
683template<typename T>
685Tensor<T, 3>
687{
688 T const &
689 qs = q.scalar();
690
691 Vector<T, 3> const &
692 qv = q.vector();
693
694 Tensor<T, 3> const
695 R = 2.0 * dyad(qv, qv) + 2.0 * qs * skew(qv) +
696 (2.0 * qs * qs - 1.0) * identity<T, 3>(3);
697
698 return R;
699}
700
701//
702// Principal rotation vector of a rotation matrix.
703//
704template<typename T, Index N>
706Vector<T, 3>
708{
709 return rv_of_q(q_of_rt(R));
710}
711
712//
713// Rotation matrix of a rotation vector.
714//
715template<typename T, Index N>
717Tensor<T, 3>
719{
720 return rt_of_q(q_of_rv(rv));
721}
722
723//
724// Rotation vector equivalent to old but closest to prev.
725//
726template<typename T, Index N>
728Vector<T, N>
729rv_continue(Vector<T, N> const & old, Vector<T, N> const & prev)
730{
731 assert(old.get_dimension() == 3);
732 assert(prev.get_dimension() == 3);
733
734 using S = typename Sacado::ScalarType<T>::type;
735
736 T const
737 norm_old = norm(old);
738
740 direction(old.get_dimension());
741
742 T
743 projection;
744
745 if (Sacado::ScalarValue<T>::eval(norm_old) > 0.0) {
746 direction = old / norm_old;
747 projection = dot(direction, prev);
748 } else {
749 projection = norm(prev);
750 if (Sacado::ScalarValue<T>::eval(projection) == 0.0) {
751 return old;
752 }
753 direction = prev / projection;
754 }
755
756 if (Sacado::ScalarValue<T>::eval(projection) == 0.0) {
757 return old;
758 }
759
760 S const
761 pi = std::acos(S(-1.0));
762
763 // The equivalent rotation vectors are (|old| + 2 pi k) direction;
764 // their signed coordinate along direction closest to that of prev
765 // determines the number of turns k.
766 S const
767 number_turns = std::round(
768 0.5 *
769 (Sacado::ScalarValue<T>::eval(projection) -
770 Sacado::ScalarValue<T>::eval(norm_old)) / pi);
771
772 if (number_turns == 0.0) {
773 return old;
774 }
775
776 return (2.0 * number_turns * pi + norm_old) * direction;
777}
778
780} // namespace minitensor
781
782#endif // MiniTensor_Quaternion_h
#define KOKKOS_INLINE_FUNCTION
KOKKOS_INLINE_FUNCTION T const & operator()(Index const i) const
KOKKOS_INLINE_FUNCTION T & scalar()
KOKKOS_INLINE_FUNCTION Quaternion(T const &s, T const &v0, T const &v1, T const &v2)
KOKKOS_INLINE_FUNCTION Vector< T, 3 > & vector()
KOKKOS_INLINE_FUNCTION T & operator()(Index const i)
KOKKOS_INLINE_FUNCTION Vector< T, 3 > const & vector() const
KOKKOS_INLINE_FUNCTION Vector< T, 4 > to_vector() const
KOKKOS_INLINE_FUNCTION Quaternion()
KOKKOS_INLINE_FUNCTION T const & scalar() const
KOKKOS_INLINE_FUNCTION Quaternion(Vector< T, 4 > const &q)
KOKKOS_INLINE_FUNCTION Quaternion(T const &s, Vector< T, 3 > const &v)
std::ostream & operator<<(std::ostream &os, Matrix< T, M, N > const &A)
KOKKOS_INLINE_FUNCTION Vector< typename Promote< S, T >::type, N > cross(Vector< S, N > const &u, Vector< T, N > const &v)
KOKKOS_INLINE_FUNCTION Tensor< typename Promote< S, T >::type, N > dyad(Vector< S, N > const &u, Vector< T, N > const &v)
KOKKOS_INLINE_FUNCTION Vector< typename Promote< S, T >::type, M > operator*(Matrix< T, M, N > const &A, Vector< S, N > const &u)
KOKKOS_INLINE_FUNCTION Index get_dimension() const
KOKKOS_INLINE_FUNCTION Index get_dimension() const
KOKKOS_INLINE_FUNCTION Matrix< typename Promote< S, T >::type, M, N > dot_t(Matrix< S, M, P > const &A, Matrix< T, N, P > const &B)
KOKKOS_INLINE_FUNCTION bool operator!=(Matrix< T, M, N > const &A, Matrix< T, M, N > const &B)
KOKKOS_INLINE_FUNCTION bool operator==(Matrix< T, M, N > const &A, Matrix< T, M, N > const &B)
KOKKOS_INLINE_FUNCTION Vector< typename Promote< S, T >::type, M > dot(Matrix< T, M, N > const &A, Vector< S, N > const &u)
KOKKOS_INLINE_FUNCTION T norm_square(Vector< T, N > const &u)
KOKKOS_INLINE_FUNCTION Tensor< T, N > skew(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION Tensor< T, N > inverse(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T norm(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T det(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION T trace(Tensor< T, N > const &A)
KOKKOS_INLINE_FUNCTION Vector< T, N > rv_continue(Vector< T, N > const &old, Vector< T, N > const &prev)
KOKKOS_INLINE_FUNCTION Vector< T, 3 > rv_of_rt(Tensor< T, N > const &R)
KOKKOS_INLINE_FUNCTION Quaternion< T > conjugate(Quaternion< T > const &q)
KOKKOS_INLINE_FUNCTION Vector< T, 3 > rv_of_q(Quaternion< T > const &q)
KOKKOS_INLINE_FUNCTION Tensor< T, 3 > rt_of_q(Quaternion< T > const &q)
KOKKOS_INLINE_FUNCTION Quaternion< T > q_of_rv(Vector< T, N > const &rv)
KOKKOS_INLINE_FUNCTION Tensor< T, 3 > rt_of_rv(Vector< T, N > const &rv)
KOKKOS_INLINE_FUNCTION Quaternion< T > q_of_rt(Tensor< T, N > const &R)
KOKKOS_INLINE_FUNCTION Quaternion< T > unit(Quaternion< T > const &q)
uint32_t Index
Indexing type.