Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: df57b9d9b0c1
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2b8b67b1fbd2
Choose a head ref

Commits on Jul 5, 2019

  1. A new class.

    pleroy committed Jul 5, 2019
    Copy the full SHA
    c49e1b9 View commit details
  2. Start implementing the body.

    pleroy committed Jul 5, 2019

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    44caf92 View commit details
  3. Copy the full SHA
    41ce5af View commit details

Commits on Jul 6, 2019

  1. A better API.

    pleroy committed Jul 6, 2019
    Copy the full SHA
    e394483 View commit details
  2. Computation of the phase.

    pleroy committed Jul 6, 2019
    Copy the full SHA
    ea75f66 View commit details
  3. Copy the full SHA
    c8d2648 View commit details
  4. Computation at time t.

    pleroy committed Jul 6, 2019
    Copy the full SHA
    61586f0 View commit details
  5. Add a test.

    pleroy committed Jul 6, 2019
    Copy the full SHA
    5f5110e View commit details
  6. Copy the full SHA
    616f924 View commit details

Commits on Jul 7, 2019

  1. Copy the full SHA
    7442e5d View commit details
  2. Copy the full SHA
    087348f View commit details
  3. A test that passes.

    pleroy committed Jul 7, 2019
    Copy the full SHA
    5126d86 View commit details
  4. TODOs.

    pleroy committed Jul 7, 2019
    Copy the full SHA
    f0fc73c View commit details
  5. Test the symmetrical cases.

    pleroy committed Jul 7, 2019
    Copy the full SHA
    60befcd View commit details
  6. Useless Abs.

    pleroy committed Jul 7, 2019
    Copy the full SHA
    cbfe009 View commit details

Commits on Jul 9, 2019

  1. A test of the formulae.

    pleroy committed Jul 9, 2019
    Copy the full SHA
    d15b269 View commit details
  2. Avoid cancellations.

    pleroy committed Jul 9, 2019
    Copy the full SHA
    376f3d2 View commit details

Commits on Jul 10, 2019

  1. Copy the full SHA
    be6055f View commit details
  2. Copy the full SHA
    347ae47 View commit details
  3. Copy the full SHA
    607c4a8 View commit details
  4. Comments.

    pleroy committed Jul 10, 2019
    Copy the full SHA
    56d4afc View commit details
  5. Comments.

    pleroy committed Jul 10, 2019
    Copy the full SHA
    2688ac3 View commit details
  6. Copy the full SHA
    27e0723 View commit details
  7. Cleanups.

    pleroy committed Jul 10, 2019
    Copy the full SHA
    2d48012 View commit details
  8. More cleanups.

    pleroy committed Jul 10, 2019
    Copy the full SHA
    40e6b76 View commit details
  9. Lint.

    pleroy committed Jul 10, 2019
    Copy the full SHA
    6f59d11 View commit details

Commits on Jul 11, 2019

  1. After egg's review.

    pleroy committed Jul 11, 2019
    Copy the full SHA
    39e72da View commit details
  2. Stupid, stupid language.

    pleroy committed Jul 11, 2019
    Copy the full SHA
    73db4a8 View commit details
  3. Merge pull request #2241 from pleroy/Euler

    A simple solver for Euler's rotation equations
    pleroy authored Jul 11, 2019
    Copy the full SHA
    2b8b67b View commit details
24 changes: 24 additions & 0 deletions numerics/elliptic_integrals.cpp
Original file line number Diff line number Diff line change
@@ -1755,6 +1755,30 @@ void FukushimaEllipticBDJ(Angle const& φ,
}
}

double EllipticE(quantities::Angle const& φ, double const mc) {
double e;
double f;
double ᴨ;
EllipticEFΠ(φ, /*n=*/1.0, mc, e, f, ᴨ);
return e;
}

double EllipticF(quantities::Angle const& φ, double const mc) {
double e;
double f;
double ᴨ;
EllipticEFΠ(φ, /*n=*/1.0, mc, e, f, ᴨ);
return f;
}

double EllipticΠ(quantities::Angle const& φ, double const n, double const mc) {
double e;
double f;
double ᴨ;
EllipticEFΠ(φ, n, mc, e, f, ᴨ);
return ᴨ;
}

void EllipticEFΠ(quantities::Angle const& φ,
double const n,
double const mc,
13 changes: 13 additions & 0 deletions numerics/elliptic_integrals.hpp
Original file line number Diff line number Diff line change
@@ -22,6 +22,16 @@ void FukushimaEllipticBDJ(quantities::Angle const& φ,
double& d,
double& j);

double EllipticE(quantities::Angle const& φ,
double mc);

double EllipticF(quantities::Angle const& φ,
double mc);

double EllipticΠ(quantities::Angle const& φ,
double n,
double mc);

void EllipticEFΠ(quantities::Angle const& φ,
double n,
double mc,
@@ -33,6 +43,9 @@ double EllipticK(double mc);

} // namespace internal_elliptic_integrals

using internal_elliptic_integrals::EllipticE;
using internal_elliptic_integrals::EllipticF;
using internal_elliptic_integrals::EllipticΠ;
using internal_elliptic_integrals::EllipticEFΠ;
using internal_elliptic_integrals::EllipticK;
using internal_elliptic_integrals::FukushimaEllipticBDJ;
146 changes: 146 additions & 0 deletions physics/euler_solver.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@

#include "physics/euler_solver.hpp"

#include <algorithm>

#include "numerics/elliptic_functions.hpp"
#include "numerics/elliptic_integrals.hpp"
#include "quantities/elementary_functions.hpp"
#include "quantities/si.hpp"

namespace principia {
namespace physics {
namespace internal_euler_solver {

using geometry::Vector;
using numerics::EllipticF;
using numerics::JacobiSNCNDN;
using quantities::Abs;
using quantities::ArcTan;
using quantities::ArcTanh;
using quantities::Cosh;
using quantities::Tanh;
using quantities::Energy;
using quantities::Inverse;
using quantities::Sqrt;
using quantities::Square;
using quantities::SquareRoot;
using quantities::SIUnit;
using quantities::Time;
using quantities::si::Joule;
using quantities::si::Radian;

EulerSolver::EulerSolver(
R3Element<MomentOfInertia> const& moments_of_inertia,
AngularMomentumBivector const& initial_angular_momentum,
Instant const& initial_time)
: initial_angular_momentum_(initial_angular_momentum),
initial_time_(initial_time) {
auto const& I₁ = moments_of_inertia.x;
auto const& I₂ = moments_of_inertia.y;
auto const& I₃ = moments_of_inertia.z;
CHECK_LE(I₁, I₂);
CHECK_LE(I₂, I₃);

auto const& m = initial_angular_momentum.coordinates();

auto const I₁₂ = I₁ - I₂;
auto const I₁₃ = I₁ - I₃;
auto const I₂₁ = -I₁₂;
auto const I₂₃ = I₂ - I₃;
auto const I₃₁ = -I₁₃;
auto const I₃₂ = -I₂₃;

// The formulæ for the Δs in Celledoni cannot be used directly because of
// cancellations.
auto const Δ₁ = m.y * m.y * I₂₁ / I₂ + m.z * m.z * I₃₁ / I₃;
auto const Δ₂ = m.x * m.x * I₁₂ / I₁ + m.z * m.z * I₃₂ / I₃;
auto const Δ₃ = m.x * m.x * I₃₁ / I₁ + m.y * m.y * I₃₂ / I₂;
DCHECK_LE(Square<AngularMomentum>(), Δ₁);
DCHECK_LE(Square<AngularMomentum>(), Δ₃);

B₁₃_ = Sqrt(I₁ * Δ₃ / I₃₁);
B₃₁_ = Sqrt(I₃ * Δ₁ / I₃₁);
λ₃_ = Sqrt(Δ₃ * I₂₁ / (I₁ * I₂ * I₃));

// Note that Celledoni et al. give k, but we need mc = 1 - k^2. We write mc
// in a way that reduces cancellations when k is close to 1.
if (Δ₂ < Square<AngularMomentum>()) {
B₂₁_ = Sqrt(I₂ * Δ₁ / I₂₁);
mc_ = -Δ₂ * I₃₁ / (Δ₃ * I₂₁);
ν_ = EllipticF(ArcTan(m.y / B₂₁_, m.z / B₃₁_), mc_) * Radian;
if (m.x < AngularMomentum()) {
λ₃_ = -λ₃_;
B₁₃_ = -B₁₃_;
}
formula_ = Formula::i;
} else if (Square<AngularMomentum>() < Δ₂) {
B₂₃_ = Sqrt(I₂ * Δ₃ / I₃₂);
mc_ = Δ₂ * I₃₁ / (Δ₁ * I₃₂);
ν_ = EllipticF(ArcTan(m.y / B₂₃_, m.x / B₁₃_), mc_) * Radian;
λ₁_ = Sqrt(Δ₁ * I₃₂ / (I₁ * I₂ * I₃));
if (m.z < AngularMomentum()) {
λ₁_ = -λ₁_;
B₃₁_ = -B₃₁_;
}
formula_ = Formula::ii;
} else {
// Δ₂ == Square<AngularMomentum>()
if (I₃₁ == MomentOfInertia()) {
// The degenerate case of a sphere. It would create NaNs.
DCHECK_EQ(MomentOfInertia(), I₂₁);
DCHECK_EQ(AngularFrequency(), λ₃_);
formula_ = Formula::Sphere;
} else {
G_ = initial_angular_momentum_.Norm();
ν_ = -ArcTanh(m.y / G_);
// NOTE(phl): The sign adjustments on this path are unclear.
if (m.x < AngularMomentum()) {
B₁₃_ = -B₁₃_;
}
if (m.z < AngularMomentum()) {
B₃₁_ = -B₃₁_;
}
formula_ = Formula::iii;
}
}
}

EulerSolver::AngularMomentumBivector EulerSolver::AngularMomentumAt(
Instant const& time) const {
Time const Δt = time - initial_time_;
switch (formula_) {
case Formula::i: {
double sn;
double cn;
double dn;
JacobiSNCNDN((λ₃_ * Δt - ν_) / Radian, mc_, sn, cn, dn);
return AngularMomentumBivector({B₁₃_ * dn, -B₂₁_ * sn, B₃₁_ * cn});
}
case Formula::ii: {
double sn;
double cn;
double dn;
JacobiSNCNDN((λ₁_ * Δt - ν_) / Radian, mc_, sn, cn, dn);
return AngularMomentumBivector({B₁₃_ * cn, -B₂₃_ * sn, B₃₁_ * dn});
}
case Formula::iii: {
Angle const angle = λ₃_ * Δt - ν_;
double const sech = 1.0 / Cosh(angle);
return AngularMomentumBivector(
{B₁₃_ * sech, G_ * Tanh(angle), B₃₁_ * sech});
}
case Formula::Sphere : {
// NOTE(phl): It's unclear how the formulæ degenerate in this case, but
// surely λ₃_ becomes 0, so the dependency in time disappears, so this is
// my best guess.
return initial_angular_momentum_;
}
default:
LOG(FATAL) << "Unexpected formula " << static_cast<int>(formula_);
};
}

} // namespace internal_euler_solver
} // namespace physics
} // namespace principia
80 changes: 80 additions & 0 deletions physics/euler_solver.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@

#pragma once

#include "geometry/frame.hpp"
#include "geometry/grassmann.hpp"
#include "geometry/named_quantities.hpp"
#include "geometry/r3_element.hpp"
#include "quantities/named_quantities.hpp"
#include "serialization/geometry.pb.h"

namespace principia {
namespace physics {
namespace internal_euler_solver {

using geometry::Bivector;
using geometry::Frame;
using geometry::Instant;
using geometry::R3Element;
using quantities::Angle;
using quantities::AngularFrequency;
using quantities::AngularMomentum;
using quantities::MomentOfInertia;
using quantities::NaN;

// A solver for Euler's rotation equations. It follows Celledoni, Fassò,
// Säfström and Zanna, 2007, The exact computation of the free rigid body motion
// and its use in splitting method.
class EulerSolver {
public:
using PrincipalAxesFrame = Frame<serialization::Frame::PhysicsTag,
serialization::Frame::PRINCIPAL_AXES,
/*frame_is_inertial*/ false>;
using AngularMomentumBivector = Bivector<AngularMomentum, PrincipalAxesFrame>;

// Constructs a solver for a body with the given moments_of_inertia in its
// principal axes frame. The moments must be in increasing order. At
// initial_time the angular momentum is initial_angular_momentum.
EulerSolver(R3Element<MomentOfInertia> const& moments_of_inertia,
AngularMomentumBivector const& initial_angular_momentum,
Instant const& initial_time);

// Computes the angular momentum at the given time.
AngularMomentumBivector AngularMomentumAt(Instant const& time) const;

private:
// The formula to use, following Cellodoni et al., Section 2.2. They don't
// have a formula for the spherical case.
enum class Formula {
i,
ii,
iii,
Sphere
};

// Construction parameters.
AngularMomentumBivector const initial_angular_momentum_;
Instant const initial_time_;

// Amusingly, the formula to use is a constant of motion.
Formula formula_;

// Only the parameters needed for the selected formula are non-NaN after
// construction.
AngularMomentum B₁₃_ = NaN<AngularMomentum>();
AngularMomentum B₃₁_ = NaN<AngularMomentum>();
AngularMomentum B₂₁_ = NaN<AngularMomentum>();
AngularMomentum B₂₃_ = NaN<AngularMomentum>();
AngularMomentum G_ = NaN<AngularMomentum>();
AngularFrequency λ₁_ = NaN<AngularFrequency>();
AngularFrequency λ₃_ = NaN<AngularFrequency>();
double mc_ = NaN<double>();
Angle ν_ = NaN<Angle>();
};

} // namespace internal_euler_solver

using internal_euler_solver::EulerSolver;

} // namespace physics
} // namespace principia
205 changes: 205 additions & 0 deletions physics/euler_solver_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@

#include "physics/euler_solver.hpp"

#include <algorithm>
#include <array>
#include <random>

#include "geometry/named_quantities.hpp"
#include "geometry/r3_element.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/elementary_functions.hpp"
#include "quantities/quantities.hpp"
#include "testing_utilities/almost_equals.hpp"

namespace principia {
namespace physics {

using geometry::Instant;
using geometry::R3Element;
using quantities::AngularMomentum;
using quantities::MomentOfInertia;
using quantities::SIUnit;
using quantities::Sqrt;
using testing_utilities::AlmostEquals;

class EulerSolverTest : public ::testing::Test {
};

// Check that we are able to retrieve the initial state for random choices of
// the moments of inertia and the angular momentum.
TEST_F(EulerSolverTest, InitialStateRandom) {
std::mt19937_64 random(42);
std::uniform_real_distribution<> moment_of_inertia_distribution(0.0, 10.0);
std::uniform_real_distribution<> angular_momentum_distribution(-10.0, 10.0);
for (int i = 0; i < 1000; ++i) {
// Make sure that the moments of inertia are properly ordered.
std::array<double, 3> randoms{moment_of_inertia_distribution(random),
moment_of_inertia_distribution(random),
moment_of_inertia_distribution(random)};
std::sort(randoms.begin(), randoms.end());
R3Element<MomentOfInertia> const moments_of_inertia{
randoms[0] * SIUnit<MomentOfInertia>(),
randoms[1] * SIUnit<MomentOfInertia>(),
randoms[2] * SIUnit<MomentOfInertia>()};

EulerSolver::AngularMomentumBivector initial_angular_momentum(
{angular_momentum_distribution(random) * SIUnit<AngularMomentum>(),
angular_momentum_distribution(random) * SIUnit<AngularMomentum>(),
angular_momentum_distribution(random) * SIUnit<AngularMomentum>()});

EulerSolver const solver(
moments_of_inertia, initial_angular_momentum, Instant());
auto const computed_initial_angular_momentum =
solver.AngularMomentumAt(Instant());

EXPECT_THAT(computed_initial_angular_momentum,
AlmostEquals(initial_angular_momentum, 0, 209))
<< moments_of_inertia << " " << initial_angular_momentum;
}
}

// Same as above, but exercises the symmetrical cases where at least two moments
// of inertia are equal.
TEST_F(EulerSolverTest, InitialStateSymmetrical) {
std::mt19937_64 random(42);
std::uniform_real_distribution<> angular_momentum_distribution(-10.0, 10.0);

R3Element<MomentOfInertia> const moments_of_inertia1{
2 * SIUnit<MomentOfInertia>(),
2 * SIUnit<MomentOfInertia>(),
3 * SIUnit<MomentOfInertia>()};
R3Element<MomentOfInertia> const moments_of_inertia2{
2 * SIUnit<MomentOfInertia>(),
3 * SIUnit<MomentOfInertia>(),
3 * SIUnit<MomentOfInertia>()};
R3Element<MomentOfInertia> const moments_of_inertia3{
3 * SIUnit<MomentOfInertia>(),
3 * SIUnit<MomentOfInertia>(),
3 * SIUnit<MomentOfInertia>()};

for (int i = 0; i < 100; ++i) {
EulerSolver::AngularMomentumBivector initial_angular_momentum(
{angular_momentum_distribution(random) * SIUnit<AngularMomentum>(),
angular_momentum_distribution(random) * SIUnit<AngularMomentum>(),
angular_momentum_distribution(random) * SIUnit<AngularMomentum>()});
{
EulerSolver const solver1(
moments_of_inertia1, initial_angular_momentum, Instant());
auto const computed_initial_angular_momentum1 =
solver1.AngularMomentumAt(Instant());

EXPECT_THAT(computed_initial_angular_momentum1,
AlmostEquals(initial_angular_momentum, 0, 87))
<< moments_of_inertia1 << " " << initial_angular_momentum;
}
{
EulerSolver const solver2(
moments_of_inertia2, initial_angular_momentum, Instant());
auto const computed_initial_angular_momentum2 =
solver2.AngularMomentumAt(Instant());

EXPECT_THAT(computed_initial_angular_momentum2,
AlmostEquals(initial_angular_momentum, 0, 50))
<< moments_of_inertia2 << " " << initial_angular_momentum;
}
{
EulerSolver const solver3(
moments_of_inertia3, initial_angular_momentum, Instant());
auto const computed_initial_angular_momentum3 =
solver3.AngularMomentumAt(Instant());

EXPECT_THAT(computed_initial_angular_momentum3,
AlmostEquals(initial_angular_momentum, 0, 0))
<< moments_of_inertia3 << " " << initial_angular_momentum;
}
}
}

// Same as above, but exercises all the formulæ. We compute an angular
// momentum by fixing its first coordinate and picking the third coordinate so
// that it falls in the right interval. (The second coordinate turns out to be
// irrelevant.)
TEST_F(EulerSolverTest, InitialStateFormulæ) {
std::mt19937_64 random(42);
std::uniform_real_distribution<> moment_of_inertia_distribution(0.0, 10.0);
std::uniform_real_distribution<> angular_momentum_distribution(-10.0, 10.0);
for (int i = 0; i < 1000; ++i) {
// Make sure that the moments of inertia are properly ordered.
std::array<double, 3> randoms{moment_of_inertia_distribution(random),
moment_of_inertia_distribution(random),
moment_of_inertia_distribution(random)};
std::sort(randoms.begin(), randoms.end());
auto const I₁ = randoms[0] * SIUnit<MomentOfInertia>();
auto const I₂ = randoms[1] * SIUnit<MomentOfInertia>();
auto const I₃ = randoms[2] * SIUnit<MomentOfInertia>();
R3Element<MomentOfInertia> const moments_of_inertia{I₁, I₂, I₃};

// G² = T * (I₁ + I₂)
{
auto const mx =
angular_momentum_distribution(random) * SIUnit<AngularMomentum>();
auto mz = mx * Sqrt(((I₂ - I₁) * I₃) / ((2.0 * I₃ - I₂ - I₁) * I₁));
if (i % 2 == 0) {
mz = -mz;
}
EulerSolver::AngularMomentumBivector initial_angular_momentum(
{mx, SIUnit<AngularMomentum>(), mz});
EulerSolver const solver(
moments_of_inertia, initial_angular_momentum, Instant());

auto const computed_initial_angular_momentum =
solver.AngularMomentumAt(Instant());
EXPECT_THAT(computed_initial_angular_momentum,
AlmostEquals(initial_angular_momentum, 0, 356))
<< moments_of_inertia << " " << initial_angular_momentum;
}

// G² = 2 * T * I₂
{
auto const mx =
angular_momentum_distribution(random) * SIUnit<AngularMomentum>();
auto mz = mx * Sqrt(((I₂ - I₁) * I₃) / ((I₃ - I₂) * I₁));
if (i % 2 == 0) {
mz = -mz;
}
EulerSolver::AngularMomentumBivector initial_angular_momentum(
{mx, SIUnit<AngularMomentum>(), mz});
EulerSolver const solver(
moments_of_inertia, initial_angular_momentum, Instant());

auto const computed_initial_angular_momentum =
solver.AngularMomentumAt(Instant());
// NOTE(phl): The largest error happens to actually go through
// Formula::ii and is on the z component (x and y are fine). That's
// probably related to the fact that Δ₂ is very small.
EXPECT_THAT(computed_initial_angular_momentum,
AlmostEquals(initial_angular_momentum, 0, 11126))
<< moments_of_inertia << " " << initial_angular_momentum;
}

// G² = T * (I₂ + I₃)
{
auto const mx =
angular_momentum_distribution(random) * SIUnit<AngularMomentum>();
auto mz = mx * Sqrt(((I₂ + I₃ - 2.0 * I₁) * I₃) / ((I₃ - I₂) * I₁));
if (i % 2 == 0) {
mz = -mz;
}
EulerSolver::AngularMomentumBivector initial_angular_momentum(
{mx, SIUnit<AngularMomentum>(), mz});
EulerSolver const solver(
moments_of_inertia, initial_angular_momentum, Instant());

auto const computed_initial_angular_momentum =
solver.AngularMomentumAt(Instant());
EXPECT_THAT(computed_initial_angular_momentum,
AlmostEquals(initial_angular_momentum, 0, 2711))
<< moments_of_inertia << " " << initial_angular_momentum;
}
}
}

} // namespace physics
} // namespace principia
5 changes: 5 additions & 0 deletions physics/physics.vcxproj
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@
<ClInclude Include="discrete_trajectory_body.hpp" />
<ClInclude Include="dynamic_frame.hpp" />
<ClInclude Include="dynamic_frame_body.hpp" />
<ClInclude Include="euler_solver.hpp" />
<ClInclude Include="geopotential.hpp" />
<ClInclude Include="geopotential_body.hpp" />
<ClInclude Include="protector.hpp" />
@@ -65,6 +66,8 @@
<ItemGroup>
<ClCompile Include="..\base\status.cpp" />
<ClCompile Include="..\numerics\cbrt.cpp" />
<ClCompile Include="..\numerics\elliptic_functions.cpp" />
<ClCompile Include="..\numerics\elliptic_integrals.cpp" />
<ClCompile Include="apsides_test.cpp" />
<ClCompile Include="barycentric_rotating_dynamic_frame_test.cpp" />
<ClCompile Include="body_centred_non_rotating_dynamic_frame_test.cpp" />
@@ -77,6 +80,8 @@
<ClCompile Include="degrees_of_freedom_test.cpp" />
<ClCompile Include="discrete_trajectory_test.cpp" />
<ClCompile Include="dynamic_frame_test.cpp" />
<ClCompile Include="euler_solver.cpp" />
<ClCompile Include="euler_solver_test.cpp" />
<ClCompile Include="geopotential_test.cpp" />
<ClCompile Include="hierarchical_system_test.cpp" />
<ClCompile Include="jacobi_coordinates_test.cpp" />
15 changes: 15 additions & 0 deletions physics/physics.vcxproj.filters
Original file line number Diff line number Diff line change
@@ -179,6 +179,9 @@
<ClInclude Include="protector.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="euler_solver.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="degrees_of_freedom_test.cpp">
@@ -253,5 +256,17 @@
<ClCompile Include="protector_test.cpp">
<Filter>Test Files</Filter>
</ClCompile>
<ClCompile Include="euler_solver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="euler_solver_test.cpp">
<Filter>Test Files</Filter>
</ClCompile>
<ClCompile Include="..\numerics\elliptic_functions.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\numerics\elliptic_integrals.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions serialization/geometry.proto
Original file line number Diff line number Diff line change
@@ -158,6 +158,7 @@ message Frame {
// The frame tags used in the physics library.
enum PhysicsTag {
FRENET = 1;
PRINCIPAL_AXES = 2;
}

// The frame tags used in the astronomical simulations.