TEST_F(EmbeddedExplicitRungeKuttaNyströmIntegratorTest, Singularity) {
  // Integrating the position of an ideal rocket,
  //   x"(t) = m' I_sp / m(t),
  //   x'(0) = 0, x(0) = 0,
  // where m(t) = m₀ - t m'.
  // The solution is
  //   x(t)  = I_sp (t + (t - m₀ / m') log(m₀ / m(t))
  //   x'(t) = I_sp log(m₀ / m(t)) (Циолко́вский's equation).
  // There is a singularity at t = m₀ / m'.
  Variation<Mass> const mass_flow = 1 * Kilogram / Second;
  Mass const initial_mass = 1 * Kilogram;
  SpecificImpulse const specific_impulse = 1 * Newton * Second / Kilogram;
  Instant const t_initial;
  Instant const t_singular = t_initial + initial_mass / mass_flow;
  // After the singularity.
  Instant const t_final = t_initial + 2 * initial_mass / mass_flow;
  auto const mass = [initial_mass, t_initial, mass_flow](Instant const& t) {
    return initial_mass - (t - t_initial) * mass_flow;
  };
  ODE::SystemState initial_state =
      {{0 * Metre}, {0 * Metre / Second}, t_initial};

  Length const length_tolerance = 1 * Milli(Metre);
  Speed const speed_tolerance = 1 * Milli(Metre) / Second;

  std::vector<ODE::SystemState> solution;
  ODE rocket_equation;
  rocket_equation.compute_acceleration = [&mass, specific_impulse, mass_flow](
      Instant const& t,
      std::vector<Length> const& position,
      not_null<std::vector<Acceleration>*> acceleration) {
    acceleration->back() = mass_flow * specific_impulse / mass(t);
  };
  IntegrationProblem<ODE> problem;
  problem.append_state = [&solution](ODE::SystemState const& state) {
    solution.push_back(state);
  };
  problem.equation = rocket_equation;
  problem.initial_state = &initial_state;
  problem.t_final = t_final;
  AdaptiveStepSize<ODE> adaptive_step_size;
  adaptive_step_size.first_time_step = t_final - t_initial;
  adaptive_step_size.safety_factor = 0.9;
  adaptive_step_size.tolerance_to_error_ratio = [length_tolerance,
                                                 speed_tolerance](
      Time const& h, ODE::SystemStateError const& error) {
    return std::min(length_tolerance / Abs(error.position_error[0]),
                    speed_tolerance / Abs(error.velocity_error[0]));
  };

  AdaptiveStepSizeIntegrator<ODE> const& integrator =
      DormandElMikkawyPrince1986RKN434FM<Length>();
  auto const outcome = integrator.Solve(problem, adaptive_step_size);
  EXPECT_EQ(termination_condition::VanishingStepSize, outcome.error());
  EXPECT_EQ(130, solution.size());
  EXPECT_THAT(solution.back().time.value - t_initial,
              AlmostEquals(t_singular - t_initial, 20));
  EXPECT_THAT(solution.back().positions.back().value,
              AlmostEquals(specific_impulse * initial_mass / mass_flow, 711));
}
示例#2
0
TEST_F(RotationTest, AppliedToTrivector) {
  EXPECT_THAT(rotation_a_(trivector_),
              AlmostEquals(Trivector<quantities::Length, World>(
                  4.0 * Metre), 0));
  EXPECT_THAT(rotation_b_(trivector_),
              AlmostEquals(Trivector<quantities::Length, World>(
                  4.0 * Metre), 0));
  EXPECT_THAT(rotation_c_(trivector_),
              AlmostEquals(Trivector<quantities::Length, World>(
                  4.0 * Metre), 0));
}
TEST_F(BodyCentredNonRotatingDynamicFrameTest, Inverse) {
  int const kSteps = 100;
  for (Instant t = t0_; t < t0_ + 1 * period_; t += period_ / kSteps) {
    auto const from_big_frame_at_t = big_frame_->FromThisFrameAtTime(t);
    auto const to_big_frame_at_t = big_frame_->ToThisFrameAtTime(t);
    auto const small_initial_state_transformed_and_back =
        from_big_frame_at_t(to_big_frame_at_t(small_initial_state_));
    EXPECT_THAT(small_initial_state_transformed_and_back.position(),
                AlmostEquals(small_initial_state_.position(), 0, 1));
    EXPECT_THAT(small_initial_state_transformed_and_back.velocity(),
                AlmostEquals(small_initial_state_.velocity(), 0, 1));
  }
}
示例#4
0
TEST_F(RotationTest, ToQuaternion4) {
  R3Element<double> const v1 = {-2, -5, -6};
  R3Element<double> const v2 =
      R3Element<double>({-3, 4, 1}).OrthogonalizationAgainst(v1);
  R3Element<double> v3 = Cross(v1, v2);
  R3Element<double> const w1 = Normalize(v1);
  R3Element<double> const w2 = Normalize(v2);
  R3Element<double> const w3 = Normalize(v3);
  R3x3Matrix m = {w1, w2, w3};
  Rot rotation(ToQuaternion(m.Transpose()));
  EXPECT_THAT(rotation(e1_).coordinates(), AlmostEquals(w1, 6));
  EXPECT_THAT(rotation(e2_).coordinates(), AlmostEquals(w2, 5));
  EXPECT_THAT(rotation(e3_).coordinates(), AlmostEquals(w3, 1));
}
示例#5
0
TEST_F(PermutationTest, Compose) {
  struct World3;
  using Orth12 = OrthogonalMap<World1, World2>;
  using Orth13 = OrthogonalMap<World1, World3>;
  using Orth23 = OrthogonalMap<World2, World3>;
  using Perm12 = Permutation<World1, World2>;
  using Perm13 = Permutation<World1, World3>;
  using Perm23 = Permutation<World2, World3>;
  std::vector<Perm12> const all12 =
      {Perm12(Perm12::XYZ), Perm12(Perm12::YZX), Perm12(Perm12::ZXY),
       Perm12(Perm12::XZY), Perm12(Perm12::ZYX), Perm12(Perm12::YXZ)};
  std::vector<Perm23> const all23 =
      {Perm23(Perm23::XYZ), Perm23(Perm23::YZX), Perm23(Perm23::ZXY),
       Perm23(Perm23::XZY), Perm23(Perm23::ZYX), Perm23(Perm23::YXZ)};
  for (Perm12 const& p12 : all12) {
    Orth12 const o12 = p12.Forget();
    for (Perm23 const& p23 : all23) {
      Orth23 const o23 = p23.Forget();
      Perm13 const p13 = p23 * p12;
      Orth13 const o13 = o23 * o12;
      for (Length l = 1 * Metre; l < 4 * Metre; l += 1 * Metre) {
        Vector<quantities::Length, World1> modified_vector(
            {l, vector_.coordinates().y, vector_.coordinates().z});
        EXPECT_THAT(p13(modified_vector),
                    AlmostEquals(o13(modified_vector), 0, 12));
      }
    }
  }
}
示例#6
0
TEST_F(RotationTest, AppliedToBivector) {
  EXPECT_THAT(rotation_a_(bivector_),
              AlmostEquals(Bivector<quantities::Length, World>(
                  R3Element<quantities::Length>(3.0 * Metre,
                                                1.0 * Metre,
                                                2.0 * Metre)), 4));
  EXPECT_THAT(rotation_b_(bivector_),
              AlmostEquals(Bivector<quantities::Length, World>(
                  R3Element<quantities::Length>(1.0 * Metre,
                                                -3.0 * Metre,
                                                2.0 * Metre)), 1, 2));
  EXPECT_THAT(rotation_c_(bivector_),
              AlmostEquals(Bivector<quantities::Length, World>(
                  R3Element<quantities::Length>((0.5 + sqrt(3.0)) * Metre,
                                                (1.0 - 0.5 * sqrt(3.0)) * Metre,
                                                3.0 * Metre)), 0));
}
示例#7
0
TEST_F(RotationTest, Forget) {
  Orth const orthogonal_a = rotation_a_.Forget();
  EXPECT_THAT(orthogonal_a(vector_),
              AlmostEquals(Vector<quantities::Length, World>(
                  R3Element<quantities::Length>(3.0 * Metre,
                                                1.0 * Metre,
                                                2.0 * Metre)), 4));
}
示例#8
0
TEST_F(RotationTest, Composition) {
  Rot const rotation_ab = rotation_a_ * rotation_b_;
  EXPECT_THAT(rotation_ab(vector_),
              AlmostEquals(Vector<quantities::Length, World>(
                  R3Element<quantities::Length>(2.0 * Metre,
                                                1.0 * Metre,
                                                -3.0 * Metre)), 4, 6));
}
示例#9
0
TEST_F(RotationTest, Inverse) {
  EXPECT_THAT(rotation_a_.Inverse()(vector_),
              AlmostEquals(Vector<quantities::Length, World>(
                  R3Element<quantities::Length>(2.0 * Metre,
                                                3.0 * Metre,
                                                1.0 * Metre)), 2));
  EXPECT_THAT(rotation_b_.Inverse()(vector_),
              AlmostEquals(Vector<quantities::Length, World>(
                  R3Element<quantities::Length>(1.0 * Metre,
                                                3.0 * Metre,
                                                -2.0 * Metre)), 1, 2));
  EXPECT_THAT(rotation_c_.Inverse()(vector_),
              AlmostEquals(Vector<quantities::Length, World>(
                  R3Element<quantities::Length>((0.5 - sqrt(3.0)) * Metre,
                                                (1.0 + 0.5 * sqrt(3.0)) * Metre,
                                                3.0 * Metre)), 0));
}
示例#10
0
TEST_F(R3ElementTest, MixedProduct) {
  testing_utilities::TestBilinearMap(
      std::multiplies<>(), 1 * Second, 1 * JulianYear, u_, v_, 42.0, 0, 1);
  testing_utilities::TestBilinearMap(
      std::multiplies<>(), w_, a_,
      -1 * Day, 1 * Parsec / SpeedOfLight, -π, 0, 1);
  Time const t = -3 * Second;
  EXPECT_EQ(t * u_, u_ * t);
  EXPECT_THAT((u_ * t) / t, AlmostEquals(u_, 1));
}
void Test1000SecondsAt1Millisecond(
    Integrator const& integrator,
    Length const& expected_position_error,
    Speed const& expected_velocity_error) {
  Length const q_initial = 1 * Metre;
  Speed const v_initial = 0 * Metre / Second;
  Speed const v_amplitude = 1 * Metre / Second;
  AngularFrequency const ω = 1 * Radian / Second;
  Instant const t_initial;
#if defined(_DEBUG)
  Instant const t_final = t_initial + 1 * Second;
#else
  Instant const t_final = t_initial + 1000 * Second;
#endif
  Time const step = 1 * Milli(Second);
  int const steps = static_cast<int>((t_final - t_initial) / step) - 1;

  int evaluations = 0;

  std::vector<ODE::SystemState> solution;
  ODE harmonic_oscillator;
  harmonic_oscillator.compute_acceleration =
      std::bind(ComputeHarmonicOscillatorAcceleration,
                _1, _2, _3, &evaluations);
  IntegrationProblem<ODE> problem;
  problem.equation = harmonic_oscillator;
  ODE::SystemState const initial_state = {{q_initial}, {v_initial}, t_initial};
  problem.initial_state = &initial_state;
  auto append_state = [&solution](ODE::SystemState const& state) {
    solution.push_back(state);
  };

  auto const instance =
      integrator.NewInstance(problem, std::move(append_state), step);
  integrator.Solve(t_final, *instance);

  EXPECT_EQ(steps, solution.size());
  Length q_error;
  Speed v_error;
  for (int i = 0; i < steps; ++i) {
    Length const q = solution[i].positions[0].value;
    Speed const v = solution[i].velocities[0].value;
    Time const t = solution[i].time.value - t_initial;
    EXPECT_THAT(t, AlmostEquals((i + 1) * step, 0));
    // TODO(egg): we may need decent trig functions for this sort of thing.
    q_error = std::max(q_error, AbsoluteError(q_initial * Cos(ω * t), q));
    v_error = std::max(v_error, AbsoluteError(-v_amplitude * Sin(ω * t), v));
  }
#if !defined(_DEBUG)
  EXPECT_EQ(expected_position_error, q_error);
  EXPECT_EQ(expected_velocity_error, v_error);
#endif
}
示例#12
0
TEST_F(R3ElementTest, Dumb3Vector) {
  EXPECT_EQ((e * 42) * v_, e * (42 * v_));
  EXPECT_THAT(303.492345479576 * Metre / Second, AlmostEquals(a_.Norm(), 8));
  testing_utilities::TestEquality(42 * v_, 43 * v_);
  testing_utilities::TestVectorSpace<R3Element<Speed>, double>(
      null_velocity_, u_, v_, w_, 0.0, 1.0, e, 42.0, 0, 1);
  testing_utilities::TestAlternatingBilinearMap(
      Cross<Speed, Speed>, u_, v_, w_, a_, 42.0, 0, 1);
  EXPECT_EQ(Cross(R3Element<double>(1, 0, 0),
                  R3Element<double>(0, 1, 0)),
            R3Element<double>(0, 0, 1));
  testing_utilities::TestSymmetricPositiveDefiniteBilinearMap(
      Dot<Speed, Speed>, u_, v_, w_, a_, 42.0, 0, 1);
}
// The test point is at the origin and in motion.  The acceleration is purely
// due to Coriolis.
TEST_F(BodySurfaceDynamicFrameTest, CoriolisAcceleration) {
  Instant const t = t0_ + 0 * Second;
  // The velocity is opposed to the motion and away from the centre.
  DegreesOfFreedom<MockFrame> const point_dof =
      {Displacement<MockFrame>({0 * Metre, 0 * Metre, 0 * Metre}) +
           MockFrame::origin,
       Velocity<MockFrame>({10 * Metre / Second,
                            20 * Metre / Second,
                            30 * Metre / Second})};
  DegreesOfFreedom<ICRFJ2000Equator> const centre_dof =
      {Displacement<ICRFJ2000Equator>({0 * Metre, 0 * Metre, 0 * Metre}) +
           ICRFJ2000Equator::origin,
       Velocity<ICRFJ2000Equator>()};

  EXPECT_CALL(mock_centre_trajectory_, EvaluateDegreesOfFreedom(t, _))
      .Times(2)
      .WillRepeatedly(Return(centre_dof));
  {
    InSequence s;
    EXPECT_CALL(*mock_ephemeris_,
                ComputeGravitationalAccelerationOnMassiveBody(
                    check_not_null(massive_centre_), t))
        .WillOnce(Return(Vector<Acceleration, ICRFJ2000Equator>({
                             0 * Metre / Pow<2>(Second),
                             0 * Metre / Pow<2>(Second),
                             0 * Metre / Pow<2>(Second)})));
    EXPECT_CALL(*mock_ephemeris_,
                ComputeGravitationalAccelerationOnMasslessBody(_, t))
        .WillOnce(Return(Vector<Acceleration, ICRFJ2000Equator>()));
  }

  // The Coriolis acceleration is towards the centre and opposed to the motion.
  EXPECT_THAT(mock_frame_->GeometricAcceleration(t, point_dof).coordinates(),
              Componentwise(AlmostEquals(400 * Metre / Pow<2>(Second), 1),
                            AlmostEquals(-200 * Metre / Pow<2>(Second), 0),
                            VanishesBefore(1 * Metre / Pow<2>(Second), 110)));
}
示例#14
0
TEST_P(SRKNTest, ConsistentWeights) {
  // Check that the time argument of the force computation is correct by
  // integrating uniform linear motion.
  // We check this for all schemes.
  Speed const v = 1 * Metre / Second;
  Mass const m = 1 * Kilogram;
  auto compute_acceleration = [v](
      Time const& t,
      std::vector<Length> const& q,
      not_null<std::vector<Acceleration>*> const result) {
    EXPECT_THAT(q[0], AlmostEquals(v * t, 0, 4096));
    (*result)[0] = 0 * Metre / Pow<2>(Second);
  };
  parameters_.initial.positions.emplace_back(0 * Metre);
  parameters_.initial.momenta.emplace_back(v);
  parameters_.initial.time = 0 * Second;
  parameters_.tmax = 16 * Second;
  parameters_.Δt = 1 * Second;
  parameters_.sampling_period = 5;
  integrator_->SolveTrivialKineticEnergyIncrement<Length>(
      compute_acceleration, parameters_, &solution_);
  EXPECT_THAT(solution_.back().positions.back().value,
              AlmostEquals(v * parameters_.tmax, 0, 4));
}
void TestTermination(
    Integrator const& integrator) {
  Length const q_initial = 1 * Metre;
  Speed const v_initial = 0 * Metre / Second;
  Instant const t_initial;
  Instant const t_final = t_initial + 163 * Second;
  Time const step = 42 * Second;
  int const steps = static_cast<int>(std::floor((t_final - t_initial) / step));

  int evaluations = 0;

  std::vector<ODE::SystemState> solution;
  ODE harmonic_oscillator;
  harmonic_oscillator.compute_acceleration =
      std::bind(ComputeHarmonicOscillatorAcceleration,
                _1, _2, _3, &evaluations);
  IntegrationProblem<ODE> problem;
  problem.equation = harmonic_oscillator;
  ODE::SystemState const initial_state = {{q_initial}, {v_initial}, t_initial};
  problem.initial_state = &initial_state;
  problem.t_final = t_final;
  problem.append_state = [&solution](ODE::SystemState const& state) {
    solution.push_back(state);
  };

  integrator.Solve(problem, step);

  EXPECT_EQ(steps, solution.size());
  EXPECT_THAT(solution.back().time.value,
              AllOf(Gt(t_final - step), Le(t_final)));
  switch (integrator.composition) {
    case BA:
    case ABA:
      EXPECT_EQ(steps * integrator.evaluations, evaluations);
      break;
    case BAB:
      EXPECT_EQ(steps * integrator.evaluations + 1, evaluations);
      break;
    default:
      LOG(FATAL) << "Invalid composition";
  }
  Length q_error;
  Speed v_error;
  for (int i = 0; i < steps; ++i) {
    Time const t = solution[i].time.value - t_initial;
    EXPECT_THAT(t, AlmostEquals((i + 1) * step, 0));
  }
}
TEST_F(BodySurfaceDynamicFrameTest, GeometricAcceleration) {
  Instant const t = t0_ + period_;
  DegreesOfFreedom<BigSmallFrame> const point_dof =
      {Displacement<BigSmallFrame>({10 * Metre, 20 * Metre, 30 * Metre}) +
           BigSmallFrame::origin,
       Velocity<BigSmallFrame>({3 * Metre / Second,
                                2 * Metre / Second,
                                1 * Metre / Second})};
  // We trust the functions to compute the values correctly, but this test
  // ensures that we don't get NaNs.
  EXPECT_THAT(big_frame_->GeometricAcceleration(t, point_dof),
              AlmostEquals(Vector<Acceleration, BigSmallFrame>({
                  -9.54504983899937710e5 * Metre / Pow<2>(Second),
                  -1.90900569196062256e6 * Metre / Pow<2>(Second),
                  -2.86351379198155506e6 * Metre / Pow<2>(Second)}), 0, 2));
}
void TestTimeReversibility(Integrator const& integrator) {
  Length const q_initial = 1 * Metre;
  Speed const v_initial = 0 * Metre / Second;
  Speed const v_amplitude = 1 * Metre / Second;
  Instant const t_initial;
  Instant const t_final = t_initial + 100 * Second;
  Time const step = 1 * Second;

  std::vector<ODE::SystemState> solution;
  ODE harmonic_oscillator;
  harmonic_oscillator.compute_acceleration =
      std::bind(ComputeHarmonicOscillatorAcceleration,
                _1, _2, _3, /*evaluations=*/nullptr);
  IntegrationProblem<ODE> problem;
  problem.equation = harmonic_oscillator;
  ODE::SystemState const initial_state = {{q_initial}, {v_initial}, t_initial};
  ODE::SystemState final_state;
  problem.initial_state = &initial_state;
  problem.t_final = t_final;
  problem.append_state = [&final_state](ODE::SystemState const& state) {
    final_state = state;
  };

  integrator.Solve(problem, step);

  problem.initial_state = &final_state;
  problem.t_final = t_initial;

  integrator.Solve(problem, -step);

  EXPECT_EQ(t_initial, final_state.time.value);
  if (integrator.time_reversible) {
    EXPECT_THAT(final_state.positions[0].value,
                AlmostEquals(q_initial, 0, 8));
    EXPECT_THAT(final_state.velocities[0].value,
                VanishesBefore(v_amplitude, 0, 16));
  } else {
    EXPECT_THAT(AbsoluteError(q_initial,
                              final_state.positions[0].value),
                Gt(1e-4 * Metre));
    EXPECT_THAT(AbsoluteError(v_initial,
                              final_state.velocities[0].value),
                Gt(1e-4 * Metre / Second));
  }
}
示例#18
0
// Solving Δt * Δt == n.
TEST_F(RootFindersTest, SquareRoots) {
  Instant const t_0;
  Instant const t_max = t_0 + 10 * Second;
  Length const n_max = Pow<2>(t_max - t_0) * SIUnit<Acceleration>();
  for (Length n = 1 * Metre; n < n_max; n += 1 * Metre) {
    int evaluations = 0;
    auto const equation = [t_0, n, &evaluations](Instant const& t) {
      ++evaluations;
      return Pow<2>(t - t_0) * SIUnit<Acceleration>() - n;
    };
    EXPECT_THAT(Bisect(equation, t_0, t_max) - t_0,
                AlmostEquals(Sqrt(n / SIUnit<Acceleration>()), 0, 1));
    if (n == 25 * Metre) {
      EXPECT_EQ(3, evaluations);
    } else {
      EXPECT_THAT(evaluations, AllOf(Ge(49), Le(58)));
    }
  }
}
// A linear acceleration identical for both bodies.  The test point doesn't
// move.  The resulting acceleration combines centrifugal and linear.
TEST_F(BodySurfaceDynamicFrameTest, LinearAcceleration) {
  Instant const t = t0_ + 0 * Second;
  DegreesOfFreedom<MockFrame> const point_dof =
      {Displacement<MockFrame>({10 * Metre, 20 * Metre, 30 * Metre}) +
           MockFrame::origin,
       Velocity<MockFrame>({0 * Metre / Second,
                            0 * Metre / Second,
                            0 * Metre / Second})};
  DegreesOfFreedom<ICRFJ2000Equator> const centre_dof =
      {Displacement<ICRFJ2000Equator>({0 * Metre, 0 * Metre, 0 * Metre}) +
           ICRFJ2000Equator::origin,
       Velocity<ICRFJ2000Equator>({0 * Metre / Second,
                                   0 * Metre / Second,
                                   0 * Metre / Second})};

  EXPECT_CALL(mock_centre_trajectory_, EvaluateDegreesOfFreedom(t, _))
      .Times(2)
      .WillRepeatedly(Return(centre_dof));
  {
    // The acceleration is linear + centripetal.
    InSequence s;
    EXPECT_CALL(*mock_ephemeris_,
                ComputeGravitationalAccelerationOnMassiveBody(
                    check_not_null(massive_centre_), t))
        .WillOnce(Return(Vector<Acceleration, ICRFJ2000Equator>({
                             -160 * Metre / Pow<2>(Second),
                             120 * Metre / Pow<2>(Second),
                             300 * Metre / Pow<2>(Second)})));
    EXPECT_CALL(*mock_ephemeris_,
                ComputeGravitationalAccelerationOnMasslessBody(_, t))
        .WillOnce(Return(Vector<Acceleration, ICRFJ2000Equator>()));
  }

  // The acceleration is linear + centrifugal.
  EXPECT_THAT(mock_frame_->GeometricAcceleration(t, point_dof),
              AlmostEquals(Vector<Acceleration, MockFrame>({
                               (-120 + 1e3) * Metre / Pow<2>(Second),
                               (-160 + 2e3) * Metre / Pow<2>(Second),
                               -300 * Metre / Pow<2>(Second)}), 2));
}
void TestTermination(Integrator const& integrator) {
  Length const q_initial = 1 * Metre;
  Speed const v_initial = 0 * Metre / Second;
  Instant const t_initial;
  Instant const t_final = t_initial + 1630 * Second;
  Time const step = 42 * Second;
  int const steps = static_cast<int>(std::floor((t_final - t_initial) / step));

  int evaluations = 0;

  std::vector<ODE::SystemState> solution;
  ODE harmonic_oscillator;
  harmonic_oscillator.compute_acceleration =
      std::bind(ComputeHarmonicOscillatorAcceleration,
                _1, _2, _3, &evaluations);
  IntegrationProblem<ODE> problem;
  problem.equation = harmonic_oscillator;
  ODE::SystemState const initial_state = {{q_initial}, {v_initial}, t_initial};
  problem.initial_state = &initial_state;
  auto append_state = [&solution](ODE::SystemState const& state) {
    solution.push_back(state);
  };

  auto const instance =
      integrator.NewInstance(problem, std::move(append_state), step);
  integrator.Solve(t_final, *instance);

  EXPECT_EQ(steps, solution.size());
  EXPECT_THAT(solution.back().time.value,
              AllOf(Gt(t_final - step), Le(t_final)));
  Length q_error;
  Speed v_error;
  for (int i = 0; i < steps; ++i) {
    Time const t = solution[i].time.value - t_initial;
    EXPECT_THAT(t, AlmostEquals((i + 1) * step, 0));
  }
}
示例#21
0
// Creates an interleaved vertex array.
void Mesh::Interleave()
{
    VertexInd newFace;

    for (std::size_t f = 0; f < face.size(); ++f)
    {
        // vertind 0
        ae3d::Vec3 tvertex = vertex [ face[ f ].vInd [ 0 ] ];
        ae3d::Vec3 tnormal = vnormal[ face[ f ].vnInd[ 0 ] ];
        TexCoord ttcoord = tcoord.empty() ? TexCoord() : tcoord[ face[ f ].uvInd[ 0 ] ];
        ae3d::Vec4 tcolor = colors.empty() ? ae3d::Vec4( 0, 0, 0, 1 ) : colors[ face[ f ].colInd[ 0 ] ];

        // Searches face f's vertex a from the vertex list.
        // If it's not found, it's added.
        bool found = false;
        
        for (std::size_t i = 0; i < indices.size(); ++i)
        {
            if (AlmostEquals( interleavedVertices[ indices[ i ].a ].position, tvertex ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].a ].normal,   tnormal ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].a ].texCoord, ttcoord ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].a ].color, tcolor ))
            {
                found = true;
                newFace.a = indices[ i ].a;
                break;
            }
        }

        if (!found)
        {
            Vertex newVertex;
            newVertex.position = tvertex;
            newVertex.normal   = tnormal;
            newVertex.texCoord = ttcoord;
            newVertex.color    = tcolor;

            interleavedVertices.push_back( newVertex );

            newFace.a = (unsigned short)(interleavedVertices.size() - 1);
        }

        // vertind 1
        tvertex = vertex [ face[ f ].vInd [ 1 ] ];
        tnormal = vnormal[ face[ f ].vnInd[ 1 ] ];
        ttcoord = tcoord.empty() ? TexCoord() : tcoord [ face[ f ].uvInd[ 1 ] ];
        tcolor = colors.empty() ? ae3d::Vec4( 0, 0, 0, 1 ) : colors[ face[ f ].colInd[ 1 ] ];
        
        found = false;

        for (std::size_t i = 0; i < indices.size(); ++i)
        {
            if (AlmostEquals( interleavedVertices[ indices[ i ].b ].position, tvertex ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].b ].normal,   tnormal ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].b ].texCoord, ttcoord ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].b ].color, tcolor ))
                
            {
                found = true;

                newFace.b = indices[ i ].b;
                break;
            }
        }

        if (!found)
        {
            Vertex newVertex;
            newVertex.position = tvertex;
            newVertex.normal   = tnormal;
            newVertex.texCoord = ttcoord;
            newVertex.color    = tcolor;

            interleavedVertices.push_back( newVertex );

            newFace.b = (unsigned short)(interleavedVertices.size() - 1);
        }

        // vertind 2
        tvertex = vertex [ face[ f ].vInd [ 2 ] ];
        tnormal = vnormal[ face[ f ].vnInd[ 2 ] ];
        ttcoord = tcoord.empty() ? TexCoord() :  tcoord [ face[ f ].uvInd[ 2 ] ];
        tcolor = colors.empty() ? ae3d::Vec4( 0, 0, 0, 1 ) : colors[ face[ f ].colInd[ 2 ] ];

        found = false;

        for (std::size_t i = 0; i < indices.size(); ++i)
        {
            if (AlmostEquals( interleavedVertices[ indices[ i ].c ].position, tvertex ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].c ].normal,   tnormal ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].c ].texCoord, ttcoord ) &&
                AlmostEquals( interleavedVertices[ indices[ i ].c ].color, tcolor ))
            {
                found = true;

                newFace.c = indices[ i ].c;
                break;
            }
        }

        if (!found)
        {
            Vertex newVertex;
            newVertex.position = tvertex;
            newVertex.normal   = tnormal;
            newVertex.texCoord = ttcoord;
            newVertex.color    = tcolor;

            interleavedVertices.push_back( newVertex );

            newFace.c = (unsigned short)(interleavedVertices.size() - 1);
        }

        indices.push_back( newFace );
    }

    if (interleavedVertices.size() > 65536)
    {
        std::cerr << "Mesh " << name << " has more than 65536 vertices!" << std::endl;
        exit( 1 );
    }
}
TEST_F(EmbeddedExplicitRungeKuttaNyströmIntegratorTest, Restart) {
  AdaptiveStepSizeIntegrator<ODE> const& integrator =
      EmbeddedExplicitRungeKuttaNyströmIntegrator<
          methods::DormandالمكاوىPrince1986RKN434FM,
          Length>();
  Length const x_initial = 1 * Metre;
  Speed const v_initial = 0 * Metre / Second;
  Speed const v_amplitude = 1 * Metre / Second;
  Time const period = 2 * π * Second;
  AngularFrequency const ω = 1 * Radian / Second;
  Instant const t_initial;
  Time const duration = 10 * period;
  Length const length_tolerance = 1 * Milli(Metre);
  Speed const speed_tolerance = 1 * Milli(Metre) / Second;
  // The number of steps if no step limit is set.
  std::int64_t const steps_forward = 132;

  auto const step_size_callback = [](bool tolerable) {};

  std::vector<ODE::SystemState> solution1;
  {
    ODE harmonic_oscillator;
    harmonic_oscillator.compute_acceleration =
        std::bind(ComputeHarmonicOscillatorAcceleration1D,
                  _1, _2, _3, /*evaluations=*/nullptr);
    IntegrationProblem<ODE> problem;
    problem.equation = harmonic_oscillator;
    problem.initial_state = {{x_initial}, {v_initial}, t_initial};
    auto const append_state = [&solution1](ODE::SystemState const& state) {
      solution1.push_back(state);
    };

    AdaptiveStepSizeIntegrator<ODE>::Parameters const parameters(
        /*first_time_step=*/duration,
        /*safety_factor=*/0.9,
        /*max_steps=*/std::numeric_limits<std::int64_t>::max(),
        /*last_step_is_exact=*/false);
    auto const tolerance_to_error_ratio =
        std::bind(HarmonicOscillatorToleranceRatio,
                  _1, _2,
                  length_tolerance,
                  speed_tolerance,
                  step_size_callback);

    auto const instance = integrator.NewInstance(problem,
                                                 append_state,
                                                 tolerance_to_error_ratio,
                                                 parameters);
    auto outcome = instance->Solve(t_initial + duration);
    EXPECT_EQ(termination_condition::Done, outcome.error());

    // Check that the time step has been updated.
    EXPECT_EQ(131, solution1.size());
    EXPECT_THAT(
        solution1[solution1.size() - 1].time.value -
            solution1[solution1.size() - 2].time.value,
        AlmostEquals(0.509'363'975'335'290'320 * Second, 0));

    // Restart the integration.
    outcome = instance->Solve(t_initial + 2.0 * duration);
    EXPECT_EQ(termination_condition::Done, outcome.error());

    // Check that the time step has been updated again.
    EXPECT_EQ(261, solution1.size());
    EXPECT_THAT(
        solution1[solution1.size() - 1].time.value -
            solution1[solution1.size() - 2].time.value,
        AlmostEquals(0.506'410'259'195'249'068 * Second, 0));
  }

  // Do it again in one call to |Solve| and check associativity.
  std::vector<ODE::SystemState> solution2;
  {
    ODE harmonic_oscillator;
    harmonic_oscillator.compute_acceleration =
        std::bind(ComputeHarmonicOscillatorAcceleration1D,
                  _1, _2, _3, /*evaluations=*/nullptr);
    IntegrationProblem<ODE> problem;
    problem.equation = harmonic_oscillator;
    problem.initial_state = {{x_initial}, {v_initial}, t_initial};
    auto const append_state = [&solution2](ODE::SystemState const& state) {
      solution2.push_back(state);
    };

    AdaptiveStepSizeIntegrator<ODE>::Parameters const parameters(
        /*first_time_step=*/duration,
        /*safety_factor=*/0.9,
        /*max_steps=*/std::numeric_limits<std::int64_t>::max(),
        /*last_step_is_exact=*/false);
    auto const tolerance_to_error_ratio =
        std::bind(HarmonicOscillatorToleranceRatio,
                  _1, _2,
                  length_tolerance,
                  speed_tolerance,
                  step_size_callback);

    auto const instance = integrator.NewInstance(problem,
                                                 append_state,
                                                 tolerance_to_error_ratio,
                                                 parameters);
    auto outcome = instance->Solve(t_initial + 2.0 * duration);
    EXPECT_EQ(termination_condition::Done, outcome.error());
  }

  EXPECT_THAT(solution2, ElementsAreArray(solution1));
}