void TestSymplecticity(Integrator const& integrator, Energy const& expected_energy_error) { Length const q_initial = 1 * Metre; Speed const v_initial = 0 * Metre / Second; Instant const t_initial; Instant const t_final = t_initial + 500 * Second; Time const step = 0.2 * Second; Mass const m = 1 * Kilogram; Stiffness const k = SIUnit<Stiffness>(); Energy const initial_energy = 0.5 * m * Pow<2>(v_initial) + 0.5 * k * Pow<2>(q_initial); 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}; 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); std::size_t const length = solution.size(); std::vector<Energy> energy_error(length); std::vector<Time> time(length); Energy max_energy_error; for (std::size_t i = 0; i < length; ++i) { Length const q_i = solution[i].positions[0].value; Speed const v_i = solution[i].velocities[0].value; time[i] = solution[i].time.value - t_initial; energy_error[i] = AbsoluteError(initial_energy, 0.5 * m * Pow<2>(v_i) + 0.5 * k * Pow<2>(q_i)); max_energy_error = std::max(energy_error[i], max_energy_error); } double const correlation = PearsonProductMomentCorrelationCoefficient(time, energy_error); LOG(INFO) << "Correlation between time and energy error : " << correlation; EXPECT_THAT(correlation, Lt(1e-2)); Power const slope = Slope(time, energy_error); LOG(INFO) << "Slope : " << slope; EXPECT_THAT(Abs(slope), Lt(2e-6 * SIUnit<Power>())); LOG(INFO) << "Maximum energy error : " << max_energy_error; EXPECT_EQ(expected_energy_error, max_energy_error); }
TEST_P(SimpleHarmonicMotionTest, Symplecticity) { parameters_.initial.positions.emplace_back(SIUnit<Length>()); parameters_.initial.momenta.emplace_back(Speed()); parameters_.initial.time = Time(); Stiffness const k = SIUnit<Stiffness>(); Mass const m = SIUnit<Mass>(); Length const q0 = parameters_.initial.positions[0].value; Speed const v0 = parameters_.initial.momenta[0].value; Energy const initial_energy = 0.5 * m * Pow<2>(v0) + 0.5 * k * Pow<2>(q0); parameters_.tmax = 500.0 * SIUnit<Time>(); parameters_.Δt = 0.2 * Second; parameters_.sampling_period = 1; integrator_->SolveTrivialKineticEnergyIncrement<Length>( &ComputeHarmonicOscillatorAcceleration, parameters_, &solution_); std::size_t const length = solution_.size(); std::vector<Energy> energy_error(length); std::vector<Time> time_steps(length); Energy max_energy_error = 0 * SIUnit<Energy>(); for (std::size_t i = 0; i < length; ++i) { Length const q_i = solution_[i].positions[0].value; Speed const v_i = solution_[i].momenta[0].value; time_steps[i] = solution_[i].time.value; energy_error[i] = Abs(0.5 * m * Pow<2>(v_i) + 0.5 * k * Pow<2>(q_i) - initial_energy); max_energy_error = std::max(energy_error[i], max_energy_error); } #if 0 LOG(INFO) << "Energy error as a function of time:\n" << BidimensionalDatasetMathematicaInput(time_steps, energy_error); #endif double const correlation = PearsonProductMomentCorrelationCoefficient(time_steps, energy_error); LOG(INFO) << GetParam(); LOG(INFO) << "Correlation between time and energy error : " << correlation; EXPECT_THAT(correlation, Lt(2E-3)); Power const slope = Slope(time_steps, energy_error); LOG(INFO) << "Slope : " << slope; EXPECT_THAT(Abs(slope), Lt(2E-6 * SIUnit<Power>())); LOG(INFO) << "Maximum energy error : " << max_energy_error; EXPECT_EQ(GetParam().expected_energy_error, max_energy_error); }