// Check that the small body has the right degrees of freedom in the frame of
// the big body.
TEST_F(BodyCentredNonRotatingDynamicFrameTest, SmallBodyInBigFrame) {
  int const kSteps = 100;
  Bivector<double, ICRFJ2000Equator> const axis({0, 0, 1});

  RelativeDegreesOfFreedom<ICRFJ2000Equator> const initial_big_to_small =
      small_initial_state_ - big_initial_state_;
  ContinuousTrajectory<ICRFJ2000Equator>::Hint hint;
  for (Instant t = t0_; t < t0_ + 1 * period_; t += period_ / kSteps) {
    DegreesOfFreedom<ICRFJ2000Equator> const small_in_inertial_frame_at_t =
        solar_system_.trajectory(*ephemeris_, kSmall).
            EvaluateDegreesOfFreedom(t, &hint);

    auto const rotation_in_big_frame_at_t =
        Rotation<ICRFJ2000Equator, Big>(-2 * π * (t - t0_) * Radian / period_,
                                        axis);
    DegreesOfFreedom<Big> const small_in_big_frame_at_t(
        rotation_in_big_frame_at_t(initial_big_to_small.displacement()) +
            Big::origin,
        rotation_in_big_frame_at_t(initial_big_to_small.velocity()));

    auto const to_big_frame_at_t = big_frame_->ToThisFrameAtTime(t);
    EXPECT_THAT(AbsoluteError(
                    to_big_frame_at_t(small_in_inertial_frame_at_t).position(),
                    small_in_big_frame_at_t.position()),
                Lt(0.3 * Milli(Metre)));
    EXPECT_THAT(AbsoluteError(
                    to_big_frame_at_t(small_in_inertial_frame_at_t).velocity(),
                    small_in_big_frame_at_t.velocity()),
                Lt(4 * Milli(Metre) / Second));
  }
}
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));
}
TEST_F(EmbeddedExplicitRungeKuttaNyströmIntegratorTest, Serialization) {
  AdaptiveStepSizeIntegrator<ODE> const& integrator =
      EmbeddedExplicitRungeKuttaNyströmIntegrator<
          methods::DormandالمكاوىPrince1986RKN434FM,
          Length>();
  Length const x_initial = 1 * Metre;
  Speed const v_initial = 0 * Metre / Second;
  Time const period = 2 * π * Second;
  Instant const t_initial;
  Instant const t_final = t_initial + 10 * period;
  Length const length_tolerance = 1 * Milli(Metre);
  Speed const speed_tolerance = 1 * Milli(Metre) / Second;

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

  std::vector<ODE::SystemState> solution;
  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 = [&solution](ODE::SystemState const& state) {
    solution.push_back(state);
  };
  AdaptiveStepSizeIntegrator<ODE>::Parameters const parameters(
      /*first_time_step=*/t_final - t_initial,
      /*safety_factor=*/0.9);
  auto const tolerance_to_error_ratio =
      std::bind(HarmonicOscillatorToleranceRatio,
                _1, _2,
                length_tolerance,
                speed_tolerance,
                step_size_callback);

  auto const instance1 = integrator.NewInstance(problem,
                                                append_state,
                                                tolerance_to_error_ratio,
                                                parameters);
  serialization::IntegratorInstance message1;
  instance1->WriteToMessage(&message1);
  auto const instance2 =
      AdaptiveStepSizeIntegrator<ODE>::Instance::ReadFromMessage(
          message1,
          harmonic_oscillator,
          append_state,
          tolerance_to_error_ratio);
  serialization::IntegratorInstance message2;
  instance2->WriteToMessage(&message2);
  EXPECT_THAT(message1, EqualsProto(message2));
}
示例#4
0
 not_null<std::unique_ptr<Ephemeris<KSP>>> MakeEphemeris(
     std::vector<DegreesOfFreedom<KSP>> states) {
   return make_not_null_unique<Ephemeris<KSP>>(
              std::move(owned_bodies_),
              states,
              game_epoch_,
              /*fitting_tolerance=*/5 * Milli(Metre),
              Ephemeris<KSP>::FixedStepParameters(
                  McLachlanAtela1992Order5Optimal<Position<KSP>>(),
              45 * Minute));
 }
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
}
 BodyCentredNonRotatingDynamicFrameTest()
     : period_(10 * π * sqrt(5.0 / 7.0) * Second),
       centre_of_mass_initial_state_(Position<ICRFJ2000Equator>(),
                                     Velocity<ICRFJ2000Equator>()),
       big_initial_state_(Position<ICRFJ2000Equator>(),
                          Velocity<ICRFJ2000Equator>()),
       small_initial_state_(Position<ICRFJ2000Equator>(),
                            Velocity<ICRFJ2000Equator>()) {
   solar_system_.Initialize(
       SOLUTION_DIR / "astronomy" / "gravity_model_two_bodies_test.proto.txt",
       SOLUTION_DIR / "astronomy" / "initial_state_two_bodies_test.proto.txt");
   t0_ = solar_system_.epoch();
   ephemeris_ = solar_system_.MakeEphemeris(
                    integrators::McLachlanAtela1992Order4Optimal<
                        Position<ICRFJ2000Equator>>(),
                    10 * Milli(Second),
                    1 * Milli(Metre));
   ephemeris_->Prolong(t0_ + 2 * period_);
   big_initial_state_ = solar_system_.initial_state(kBig);
   big_gravitational_parameter_ = solar_system_.gravitational_parameter(kBig);
   big_frame_ = std::make_unique<
                    BodyCentredNonRotatingDynamicFrame<ICRFJ2000Equator, Big>>(
                        ephemeris_.get(),
                        solar_system_.massive_body(*ephemeris_, kBig));
   small_initial_state_ = solar_system_.initial_state(kSmall);
   small_gravitational_parameter_ =
       solar_system_.gravitational_parameter(kSmall);
   small_frame_ = std::make_unique<
                    BodyCentredNonRotatingDynamicFrame<ICRFJ2000Equator,
                                                       Small>>(
                        ephemeris_.get(),
                        solar_system_.massive_body(*ephemeris_, kSmall));
   centre_of_mass_initial_state_ =
       Barycentre<DegreesOfFreedom<ICRFJ2000Equator>, GravitationalParameter>(
           {big_initial_state_, small_initial_state_},
           {big_gravitational_parameter_, small_gravitational_parameter_});
 }
示例#7
0
void BM_BarycentricRotatingDynamicFrame(
    benchmark::State& state) {  // NOLINT(runtime/references)
  Time const Δt = 5 * Minute;
  int const steps = state.range_x();

  SolarSystem<ICRFJ2000Equator> solar_system;
  solar_system.Initialize(
      SOLUTION_DIR / "astronomy" / "gravity_model.proto.txt",
      SOLUTION_DIR / "astronomy" /
          "initial_state_jd_2433282_500000000.proto.txt");
  auto const ephemeris = solar_system.MakeEphemeris(
      McLachlanAtela1992Order5Optimal<Position<ICRFJ2000Equator>>(),
      45 * Minute,
      5 * Milli(Metre));
  ephemeris->Prolong(solar_system.epoch() + steps * Δt);

  not_null<MassiveBody const*> const earth =
      solar_system.massive_body(*ephemeris, "Earth");
  not_null<MassiveBody const*> const venus =
      solar_system.massive_body(*ephemeris, "Venus");

  MasslessBody probe;
  Position<ICRFJ2000Equator> probe_initial_position =
      ICRFJ2000Equator::origin + Displacement<ICRFJ2000Equator>(
                                     {0.5 * AstronomicalUnit,
                                      -1 * AstronomicalUnit,
                                      0 * AstronomicalUnit});
  Velocity<ICRFJ2000Equator> probe_velocity =
      Velocity<ICRFJ2000Equator>({0 * SIUnit<Speed>(),
                                  100 * Kilo(Metre) / Second,
                                  0 * SIUnit<Speed>()});
  DiscreteTrajectory<ICRFJ2000Equator> probe_trajectory;
  FillLinearTrajectory<ICRFJ2000Equator, DiscreteTrajectory>(
      probe_initial_position,
      probe_velocity,
      solar_system.epoch(),
      Δt,
      steps,
      &probe_trajectory);

  BarycentricRotatingDynamicFrame<ICRFJ2000Equator, Rendering>
      dynamic_frame(ephemeris.get(), earth, venus);
  while (state.KeepRunning()) {
    auto v = ApplyDynamicFrame(&probe,
                               &dynamic_frame,
                               probe_trajectory.Begin(),
                               probe_trajectory.End());
  }
}
TEST_F(EmbeddedExplicitRungeKuttaNyströmIntegratorTest,
       MaxSteps) {
  AdaptiveStepSizeIntegrator<ODE> const& integrator =
      DormandElMikkawyPrince1986RKN434FM<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;
  Instant const t_final = t_initial + 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;

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

  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 = {{x_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);
  };
  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 =
      std::bind(HarmonicOscillatorToleranceRatio,
                _1, _2, length_tolerance, speed_tolerance, step_size_callback);
  adaptive_step_size.max_steps = 100;

  auto const outcome = integrator.Solve(problem, adaptive_step_size);
  EXPECT_EQ(termination_condition::ReachedMaximalStepCount, outcome.error());
  EXPECT_THAT(AbsoluteError(
                  x_initial * Cos(ω * (solution.back().time.value - t_initial)),
                      solution.back().positions[0].value),
              AllOf(Ge(8e-4 * Metre), Le(9e-4 * Metre)));
  EXPECT_THAT(AbsoluteError(
                  -v_amplitude *
                      Sin(ω * (solution.back().time.value - t_initial)),
                  solution.back().velocities[0].value),
              AllOf(Ge(1e-3 * Metre / Second), Le(2e-3 * Metre / Second)));
  EXPECT_THAT(solution.back().time.value, Lt(t_final));
  EXPECT_EQ(100, solution.size());

  // Check that a |max_steps| greater than or equal to the unconstrained number
  // of steps has no effect.
  for (std::int64_t const max_steps :
       {steps_forward, steps_forward + 1234}) {
    solution.clear();
    adaptive_step_size.max_steps = steps_forward;
    auto const outcome = integrator.Solve(problem, adaptive_step_size);
    EXPECT_EQ(termination_condition::Done, outcome.error());
    EXPECT_THAT(AbsoluteError(x_initial, solution.back().positions[0].value),
                AllOf(Ge(3e-4 * Metre), Le(4e-4 * Metre)));
    EXPECT_THAT(AbsoluteError(v_initial, solution.back().velocities[0].value),
                AllOf(Ge(2e-3 * Metre / Second), Le(3e-3 * Metre / Second)));
    EXPECT_EQ(t_final, solution.back().time.value);
    EXPECT_EQ(steps_forward, solution.size());
  }
}
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));
}