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: 03acbbfa605b
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: ce7b57e8be81
Choose a head ref
  • 8 commits
  • 5 files changed
  • 1 contributor

Commits on Jul 24, 2021

  1. Declare a checkpointer.

    pleroy committed Jul 24, 2021
    Copy the full SHA
    0380bcc View commit details
  2. Copy the full SHA
    795e28c View commit details
  3. Implement checkpoint writing.

    pleroy committed Jul 24, 2021
    Copy the full SHA
    5ab4bed View commit details
  4. Copy the full SHA
    9f36718 View commit details

Commits on Aug 10, 2021

  1. Copy the full SHA
    6567bd6 View commit details

Commits on Aug 12, 2021

  1. A unit test that passes.

    pleroy committed Aug 12, 2021
    Copy the full SHA
    eb03676 View commit details

Commits on Aug 17, 2021

  1. Copy the full SHA
    0fbb879 View commit details
  2. Merge pull request #3090 from pleroy/Checkpointing3

    Write a discrete trajectory checkpoint when closing a non-collapsible segment
    pleroy authored Aug 17, 2021
    Copy the full SHA
    ce7b57e View commit details
Showing with 185 additions and 1 deletion.
  1. +32 −1 ksp_plugin/vessel.cpp
  2. +11 −0 ksp_plugin/vessel.hpp
  3. +133 −0 ksp_plugin_test/vessel_test.cpp
  4. +4 −0 physics/discrete_trajectory_body.hpp
  5. +5 −0 serialization/ksp_plugin.proto
33 changes: 32 additions & 1 deletion ksp_plugin/vessel.cpp
Original file line number Diff line number Diff line change
@@ -71,6 +71,9 @@ Vessel::Vessel(GUID guid,
return FlowPrognostication(parameters);
},
20ms), // 50 Hz.
checkpointer_(make_not_null_unique<Checkpointer<serialization::Vessel>>(
MakeCheckpointerWriter(),
MakeCheckpointerReader())),
prehistory_(make_not_null_unique<DiscreteTrajectory<Barycentric>>()) {
// Can't create the |history_|, |psychohistory_| and |prediction_| here
// because |prehistory_| is empty;
@@ -169,7 +172,15 @@ void Vessel::DetectCollapsibilityChange() {
// segment (but not vice-versa). If the segment being closed is a very
// short collapsible one (e.g., no downsampling took place) we could
// consider merging it with its predecessor and avoiding the creation of a
// new segment.
// new segment. The checkpointing code below would remain correct.

if (!is_collapsible_) {
// If the segment that is being closed is not collapsible, we have no way
// to reconstruct it, so we must serialize it in a checkpoint. Note that
// the last point of the history specifies the initial conditions of the
// next (collapsible) segment.
checkpointer_->WriteToCheckpoint(history_->Fork()->time);
}
auto psychohistory = psychohistory_->DetachFork();
history_ = history_->NewForkAtLast();
history_->SetDownsampling(MaxDenseIntervals, DownsamplingTolerance);
@@ -583,6 +594,23 @@ void Vessel::FillContainingPileUpsFromMessage(
}
}

Checkpointer<serialization::Vessel>::Writer Vessel::MakeCheckpointerWriter() {
return [this](not_null<serialization::Vessel::Checkpoint*> const message) {
auto const penultimate_history_segment = history_->parent();
auto last_history_segment = history_->DetachFork();
last_history_segment->WriteToMessage(message->mutable_segment(),
/*excluded=*/{psychohistory_},
/*tracked=*/{});
penultimate_history_segment->AttachFork(std::move(last_history_segment));
};
}

Checkpointer<serialization::Vessel>::Reader Vessel::MakeCheckpointerReader() {
return [this](serialization::Vessel::Checkpoint const& message) {
return absl::OkStatus();
};
}

void Vessel::MakeAsynchronous() {
synchronous_ = false;
}
@@ -596,6 +624,9 @@ Vessel::Vessel()
prediction_adaptive_step_parameters_(DefaultPredictionParameters()),
parent_(testing_utilities::make_not_null<Celestial const*>()),
ephemeris_(testing_utilities::make_not_null<Ephemeris<Barycentric>*>()),
checkpointer_(make_not_null_unique<Checkpointer<serialization::Vessel>>(
/*reader=*/nullptr,
/*writer=*/nullptr)),
prehistory_(make_not_null_unique<DiscreteTrajectory<Barycentric>>()),
prognosticator_(nullptr, 20ms) {}

11 changes: 11 additions & 0 deletions ksp_plugin/vessel.hpp
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
#include "ksp_plugin/orbit_analyser.hpp"
#include "ksp_plugin/part.hpp"
#include "ksp_plugin/pile_up.hpp"
#include "physics/checkpointer.hpp"
#include "physics/discrete_trajectory.hpp"
#include "physics/ephemeris.hpp"
#include "physics/massless_body.hpp"
@@ -31,6 +32,7 @@ using base::not_null;
using base::RecurringThread;
using geometry::Instant;
using geometry::Vector;
using physics::Checkpointer;
using physics::DegreesOfFreedom;
using physics::DiscreteTrajectory;
using physics::Ephemeris;
@@ -213,6 +215,13 @@ class Vessel {
PileUp::PileUpForSerializationIndex const&
pile_up_for_serialization_index);

// Return functions that can be passed to a |Checkpointer| to write this
// vessel to a checkpoint or read it back.
Checkpointer<serialization::Vessel>::Writer
MakeCheckpointerWriter();
Checkpointer<serialization::Vessel>::Reader
MakeCheckpointerReader();

static void MakeAsynchronous();
static void MakeSynchronous();

@@ -271,6 +280,8 @@ class Vessel {
std::map<PartId, not_null<std::unique_ptr<Part>>> parts_;
std::set<PartId> kept_parts_;

not_null<std::unique_ptr<Checkpointer<serialization::Vessel>>> checkpointer_;

// See the comments in pile_up.hpp for an explanation of the terminology.

// The |prehistory_| contains exactly one point, added by
133 changes: 133 additions & 0 deletions ksp_plugin_test/vessel_test.cpp
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
#include "physics/rigid_motion.hpp"
#include "physics/rotating_body.hpp"
#include "physics/mock_ephemeris.hpp"
#include "quantities/elementary_functions.hpp"
#include "quantities/named_quantities.hpp"
#include "quantities/quantities.hpp"
#include "quantities/si.hpp"
@@ -43,6 +44,7 @@ using physics::MockEphemeris;
using physics::RigidMotion;
using physics::RotatingBody;
using quantities::MomentOfInertia;
using quantities::Pow;
using quantities::Torque;
using quantities::si::Degree;
using quantities::si::Kilogram;
@@ -106,6 +108,11 @@ class VesselTest : public testing::Test {
return vessel_.IsCollapsible();
}

void WriteCheckpointToMessage(
not_null<serialization::Vessel*> const message) const {
vessel_.checkpointer_->WriteToMessage(message->mutable_checkpoint());
}

MockEphemeris<Barycentric> ephemeris_;
RotatingBody<Barycentric> const body_;
Celestial const celestial_;
@@ -486,6 +493,132 @@ TEST_F(VesselTest, IsCollapsible) {
}
}

TEST_F(VesselTest, Checkpointing) {
EXPECT_CALL(ephemeris_, t_max())
.WillRepeatedly(Return(astronomy::J2000 + 30 * Second));
EXPECT_CALL(
ephemeris_,
FlowWithAdaptiveStep(_, _, astronomy::InfiniteFuture, _, _))
.Times(AnyNumber());
EXPECT_CALL(
ephemeris_,
FlowWithAdaptiveStep(_, _, astronomy::J2000 + 30 * Second, _, _))
.Times(AnyNumber());
vessel_.CreatePrehistoryIfNeeded(astronomy::J2000);

auto const pile_up =
std::make_shared<PileUp>(/*parts=*/std::list<not_null<Part*>>{p1_, p2_},
Instant{},
DefaultPsychohistoryParameters(),
DefaultHistoryParameters(),
&ephemeris_,
/*deletion_callback=*/nullptr);
p1_->set_containing_pile_up(pile_up);
p2_->set_containing_pile_up(pile_up);

// Free-fall trajectory. This creates a checkpoint because the prehistory is
// not collapsible.
Time t;
for (; t <= 10 * Second; t += 1 * Second) {
p1_->AppendToHistory(
astronomy::J2000 + t,
DegreesOfFreedom<Barycentric>(
Barycentric::origin +
Displacement<Barycentric>({t * 1 * Metre / Second,
t * 2 * Metre / Second,
t * 3 * Metre / Second}),
Velocity<Barycentric>({1 * Metre / Second,
2 * Metre / Second,
3 * Metre / Second})));
p2_->AppendToHistory(
astronomy::J2000 + t,
DegreesOfFreedom<Barycentric>(
Barycentric::origin +
Displacement<Barycentric>({t * 4 * Metre / Second,
t * 5 * Metre / Second,
t * 6 * Metre / Second}),
Velocity<Barycentric>({4 * Metre / Second,
5 * Metre / Second,
6 * Metre / Second})));
}

vessel_.DetectCollapsibilityChange();
vessel_.AdvanceTime();

// Apply a force. This segment is not collapsible.
p1_->apply_intrinsic_force(
Vector<Force, Barycentric>({1 * Newton, 0 * Newton, 0 * Newton}));
// TODO(phl): The trajectory is not continuous and not consistent with the
// force because everything is hard in tests.
for (; t <= 25 * Second; t += 1 * Second) {
p1_->AppendToHistory(
astronomy::J2000 + t,
DegreesOfFreedom<Barycentric>(
Barycentric::origin +
Displacement<Barycentric>(
{Pow<2>(t) * 1 * Metre / Second / Second,
Pow<2>(t) * 2 * Metre / Second / Second,
Pow<2>(t) * 3 * Metre / Second / Second}),
Velocity<Barycentric>({2 * t * 1 * Metre / Second / Second,
2 * t * 2 * Metre / Second / Second,
2 * t * 3 * Metre / Second / Second})));
p2_->AppendToHistory(
astronomy::J2000 + t,
DegreesOfFreedom<Barycentric>(
Barycentric::origin +
Displacement<Barycentric>(
{Pow<2>(t) * 4 * Metre / Second / Second,
Pow<2>(t) * 5 * Metre / Second / Second,
Pow<2>(t) * 6 * Metre / Second / Second}),
Velocity<Barycentric>({2 * t * 4 * Metre / Second / Second,
2 * t * 5 * Metre / Second / Second,
2 * t * 6 * Metre / Second / Second})));
}

vessel_.DetectCollapsibilityChange();
vessel_.AdvanceTime();

// Remove the force. This creates a checkpoint because we closed a non-
// collapsible segment.
p1_->clear_intrinsic_force();
// TODO(phl): Again, no continuity.
for (; t <= 30 * Second; t += 1 * Second) {
p1_->AppendToHistory(
astronomy::J2000 + t,
DegreesOfFreedom<Barycentric>(
Barycentric::origin +
Displacement<Barycentric>({t * 1 * Metre / Second,
t * 2 * Metre / Second,
t * 3 * Metre / Second}),
Velocity<Barycentric>({1 * Metre / Second,
2 * Metre / Second,
3 * Metre / Second})));
p2_->AppendToHistory(
astronomy::J2000 + t,
DegreesOfFreedom<Barycentric>(
Barycentric::origin +
Displacement<Barycentric>({t * 4 * Metre / Second,
t * 5 * Metre / Second,
t * 6 * Metre / Second}),
Velocity<Barycentric>({4 * Metre / Second,
5 * Metre / Second,
6 * Metre / Second})));
}

vessel_.DetectCollapsibilityChange();
vessel_.AdvanceTime();

serialization::Vessel message;
WriteCheckpointToMessage(&message);
CHECK_EQ(2, message.checkpoint_size());
CHECK_EQ(0, message.checkpoint(0).time().scalar().magnitude());
CHECK_EQ(0, message.checkpoint(0).segment().children_size());
CHECK_EQ(1, message.checkpoint(0).segment().zfp().timeline_size());
CHECK_EQ(10, message.checkpoint(1).time().scalar().magnitude());
CHECK_EQ(0, message.checkpoint(1).segment().children_size());
CHECK_EQ(16, message.checkpoint(1).segment().zfp().timeline_size());
}

TEST_F(VesselTest, SerializationSuccess) {
MockFunction<int(not_null<PileUp const*>)>
serialization_index_for_pile_up;
4 changes: 4 additions & 0 deletions physics/discrete_trajectory_body.hpp
Original file line number Diff line number Diff line change
@@ -124,6 +124,10 @@ void DiscreteTrajectory<Frame>::AttachFork(
not_null<std::unique_ptr<DiscreteTrajectory<Frame>>> fork) {
CHECK(fork->is_root());
CHECK(!this->Empty());
// It is easy to mess up forks and to end up with a trajectory which is its
// own parent. This check catches that problem. It's not foolproof (there
// are more complicated anomalies that can arise) but it's still useful.
CHECK(fork.get() != this);

auto& fork_timeline = fork->timeline_;
auto const this_last = --this->end();
5 changes: 5 additions & 0 deletions serialization/ksp_plugin.proto
Original file line number Diff line number Diff line change
@@ -161,6 +161,10 @@ message Renderer {
}

message Vessel {
message Checkpoint {
required Point time = 1;
required DiscreteTrajectory segment = 2;
}
required string guid = 13;
required string name = 19;
required MasslessBody body = 1;
@@ -173,6 +177,7 @@ message Vessel {
optional bool psychohistory_is_authoritative = 17; // Pre-Cesàro.
optional DiscreteTrajectory prediction = 18; // Pre-Chasles.
optional FlightPlan flight_plan = 4;
repeated Checkpoint checkpoint = 21; // Added in Grothendieck/Haar.

// Pre-Буняковский.
reserved 2, 3, 5;