Skip to content

Commit a643f8e

Browse files
authored
Fix greedy simulator route-end detection and early station departures (#149)
* update comment * add greedy simulator test * fix test * fix starting too early from station * add better braking check for route ending * refactor test case with new return type * add more debug output * compare with speed for correctness * special case, end on stop * add comment * fix test cases
1 parent 061d2b0 commit a643f8e

4 files changed

Lines changed: 594 additions & 103 deletions

File tree

include/simulator/GeneralSimulator.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ struct SimulatorResults {
3636
std::vector<double>
3737
braking_distances; // distance before route end at which each train had to
3838
// brake due to approaching their route end
39-
std::vector<double>
40-
vertex_headways; // final vertex headways that have to be respected after
41-
// the simulation until a next train can pass a vertex
39+
std::vector<double> vertex_headways; // for every vertex, earliest time at
40+
// which next train can enter
4241
std::vector<std::map<double, PosVel>>
4342
train_trajectories; // For every train, a map of time to position and
4443
// velocity at that time

include/simulator/GreedySimulator.hpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ class GreedySimulator
7979
FRIEND_TEST(::GreedySimulator, FutureSpeedRestrictionConstraintsAfterLeaving);
8080
#endif
8181

82+
struct MaAndMaxVResult {
83+
double ma;
84+
double ma_without_route_end;
85+
double max_v;
86+
double max_v_without_route_end;
87+
};
88+
8289
enum class TTDOccupationType : std::uint8_t {
8390
OnlyOccupied,
8491
OnlyBehind,
@@ -155,7 +162,7 @@ class GreedySimulator
155162
const std::unordered_set<size_t>& trains_left,
156163
const std::vector<std::unordered_set<size_t>>& tr_on_edges) const;
157164

158-
[[nodiscard]] std::pair<double, double>
165+
[[nodiscard]] MaAndMaxVResult
159166
get_future_max_speed_constraints(size_t tr, const Train& train, double pos,
160167
double v_0, double max_displacement, int dt,
161168
bool also_limit_by_leaving_edges) const;
@@ -182,7 +189,7 @@ class GreedySimulator
182189
time_to_exit_objective(double v_0, double v_1, double v_e, double s, double a,
183190
double d, int dt);
184191

185-
[[nodiscard]] std::pair<double, double>
192+
[[nodiscard]] MaAndMaxVResult
186193
get_ma_and_maxv(size_t tr, const std::vector<double>& train_velocities,
187194
std::optional<size_t> next_stop, int h, int dt,
188195
const std::vector<std::pair<double, double>>& train_positions,

src/simulator/GreedySimulator.cpp

Lines changed: 87 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ cda_rail::simulator::GreedySimulator::simulate(
148148
for (const auto& tr : trains_in_network) {
149149
const auto& train_object = instance->get_train_list().get_train(tr);
150150

151-
if (trains_finished_simulating.contains(tr) || t < tr_stop_until.at(tr)) {
151+
if (trains_finished_simulating.contains(tr) ||
152+
t < tr_stop_until.at(tr) + dt) {
152153
PLOGV << train_object.name << " skipped.";
153154
continue;
154155
}
@@ -162,36 +163,54 @@ cda_rail::simulator::GreedySimulator::simulate(
162163
}
163164
assert(h % dt == 0);
164165

165-
const auto [tr_ma, tr_v1] =
166+
// const auto [tr_ma, tr_v1] =
167+
const auto tr_ma_data =
166168
get_ma_and_maxv(tr, train_velocities, tr_next_stop_id.at(tr), h, dt,
167169
train_positions, trains_in_network, trains_left,
168170
trains_on_edges, limit_speed_by_leaving_edges);
169171
PLOGV << train_object.name << " positioned at "
170172
<< train_positions.at(tr).second
171-
<< " has MA: " << train_positions.at(tr).second + tr_ma
172-
<< " and max velocity: " << tr_v1;
173+
<< " has MA: " << train_positions.at(tr).second + tr_ma_data.ma
174+
<< " (without route end this would be: "
175+
<< train_positions.at(tr).second + tr_ma_data.ma_without_route_end
176+
<< ")"
177+
<< " and max velocity: " << tr_ma_data.max_v
178+
<< " (without route end this would be: "
179+
<< tr_ma_data.max_v_without_route_end << ")";
173180
const auto tr_edge_len = train_edge_length(tr);
181+
182+
PLOGV << "h = " << h;
183+
auto tr_new_speed =
184+
std::min(tr_ma_data.max_v,
185+
get_v1_from_ma(train_velocities.at(tr), tr_ma_data.ma,
186+
train_object.deceleration, dt));
187+
if (tr_new_speed < V_MIN) {
188+
tr_new_speed = 0.0; // Train is stopped
189+
}
190+
auto tr_new_speed_without_route_end =
191+
std::min(tr_ma_data.max_v_without_route_end,
192+
get_v1_from_ma(train_velocities.at(tr),
193+
tr_ma_data.ma_without_route_end,
194+
train_object.deceleration, dt));
195+
if (tr_new_speed_without_route_end < V_MIN) {
196+
tr_new_speed_without_route_end = 0.0;
197+
}
198+
199+
PLOGV << "tr_new_speed = " << tr_new_speed
200+
<< " (without route end this would be: "
201+
<< tr_new_speed_without_route_end << ")";
202+
174203
if ((braking_distances.at(tr) < 0) &&
175-
(instance->const_n().get_edge(train_edges.at(tr).back()).target !=
176-
tr_schedule.get_exit()) &&
177-
(train_positions.at(tr).second + tr_ma + STOP_TOLERANCE >=
178-
tr_edge_len)) {
204+
(tr_new_speed < tr_new_speed_without_route_end)) {
179205
PLOGV << train_object.name
180206
<< " starts braking due to end of route constraint.";
181207
braking_times.at(tr) = t - dt;
182208
braking_distances.at(tr) = tr_edge_len - train_positions.at(tr).second;
183209
}
184-
PLOGV << "h = " << h;
185-
auto tr_new_speed =
186-
std::min(tr_v1, get_v1_from_ma(train_velocities.at(tr), tr_ma,
187-
train_object.deceleration, dt));
188-
if (tr_new_speed < V_MIN) {
189-
tr_new_speed = 0.0; // Train is stopped
190-
}
191210

192211
// Move trains
193-
if (move_train(tr, train_velocities.at(tr), tr_new_speed, tr_ma, dt,
194-
train_positions)) {
212+
if (move_train(tr, train_velocities.at(tr), tr_new_speed, tr_ma_data.ma,
213+
dt, train_positions)) {
195214
movement_detected = true;
196215
}
197216
train_velocities.at(tr) = tr_new_speed;
@@ -249,6 +268,11 @@ cda_rail::simulator::GreedySimulator::simulate(
249268
stop_times.at(tr).push_back(static_cast<double>(t));
250269
tr_next_stop_id.at(tr) = {};
251270
trains_finished_simulating.insert(tr);
271+
// set braking_times to the route end. Only after stopping at the
272+
// station, the underdefined route is the only cause for the train not
273+
// to continue
274+
braking_times.at(tr) = exit_times.at(tr);
275+
braking_distances.at(tr) = 0;
252276
PLOGV << "At time " << t << ", "
253277
<< instance->get_train_list().get_train(tr).name
254278
<< " reached the end of its route at station "
@@ -378,8 +402,8 @@ cda_rail::simulator::GreedySimulator::simulate(
378402
})) {
379403
PLOGV << "Vertex headway constraint prevents movement.";
380404
reason_found = true;
381-
} else if (std::ranges::any_of(tr_stop_until, [t](int stop_time) {
382-
return stop_time > t;
405+
} else if (std::ranges::any_of(tr_stop_until, [t, dt](int stop_time) {
406+
return stop_time + dt > t;
383407
})) {
384408
PLOGV << "Train stop constraint prevents movement.";
385409
reason_found = true;
@@ -849,7 +873,7 @@ double cda_rail::simulator::GreedySimulator::get_absolute_distance_ma(
849873
// range
850874
}
851875

852-
std::pair<double, double>
876+
cda_rail::simulator::GreedySimulator::MaAndMaxVResult
853877
cda_rail::simulator::GreedySimulator::get_future_max_speed_constraints(
854878
size_t tr, const cda_rail::Train& train, double pos, double v_0,
855879
double max_displacement, int dt, bool also_limit_by_leaving_edges) const {
@@ -890,26 +914,6 @@ cda_rail::simulator::GreedySimulator::get_future_max_speed_constraints(
890914
double ma = max_displacement;
891915

892916
const auto milestones = edge_milestones(tr);
893-
894-
// Check exit
895-
const auto& last_edge_id = train_edges.at(tr).back();
896-
const auto& last_edge = instance->const_n().get_edge(last_edge_id);
897-
const auto tr_schedule = instance->get_schedule(tr);
898-
const bool last_edge_leaves_network =
899-
(last_edge.target == tr_schedule.get_exit());
900-
const auto relevant_last_pos =
901-
milestones.back() + (last_edge_leaves_network
902-
? train.length
903-
: 0); // + train.length because train needs to
904-
// fully leave the network
905-
if (pos + max_displacement >= relevant_last_pos) {
906-
const double last_edge_exit_restriction =
907-
last_edge_leaves_network ? tr_schedule.get_v_n() : 0;
908-
std::tie(ma, max_v) = speed_restriction_helper(
909-
ma, max_v, pos, relevant_last_pos, v_0, last_edge_exit_restriction,
910-
train.deceleration, dt);
911-
}
912-
913917
for (size_t i = 0; i < train_edges.at(tr).size() &&
914918
milestones.at(i) + EPS < pos + max_displacement;
915919
++i) {
@@ -933,7 +937,35 @@ cda_rail::simulator::GreedySimulator::get_future_max_speed_constraints(
933937
edge.max_speed, train.deceleration, dt);
934938
}
935939
}
936-
return {ma, max_v};
940+
941+
// Check exit
942+
const auto& last_edge_id = train_edges.at(tr).back();
943+
const auto& last_edge = instance->const_n().get_edge(last_edge_id);
944+
const auto tr_schedule = instance->get_schedule(tr);
945+
const bool last_edge_leaves_network =
946+
(last_edge.target == tr_schedule.get_exit());
947+
const auto relevant_last_pos =
948+
milestones.back() + (last_edge_leaves_network
949+
? train.length
950+
: 0); // + train.length because train needs to
951+
// fully leave the network
952+
double ma_without_route_end = ma;
953+
double max_v_without_route_end = max_v;
954+
if (pos + max_displacement >= relevant_last_pos) {
955+
const double last_edge_exit_restriction =
956+
last_edge_leaves_network ? tr_schedule.get_v_n() : 0;
957+
std::tie(ma, max_v) = speed_restriction_helper(
958+
ma, max_v, pos, relevant_last_pos, v_0, last_edge_exit_restriction,
959+
train.deceleration, dt);
960+
}
961+
if (last_edge_leaves_network) {
962+
ma_without_route_end = ma;
963+
max_v_without_route_end = max_v;
964+
}
965+
return {.ma = ma,
966+
.ma_without_route_end = ma_without_route_end,
967+
.max_v = max_v,
968+
.max_v_without_route_end = max_v_without_route_end};
937969
}
938970

939971
std::pair<double, double>
@@ -1134,7 +1166,8 @@ cda_rail::simulator::GreedySimulator::time_to_exit_objective(
11341166
static_cast<double>(dt)};
11351167
}
11361168

1137-
std::pair<double, double> cda_rail::simulator::GreedySimulator::get_ma_and_maxv(
1169+
cda_rail::simulator::GreedySimulator::MaAndMaxVResult
1170+
cda_rail::simulator::GreedySimulator::get_ma_and_maxv(
11381171
size_t tr, const std::vector<double>& train_velocities,
11391172
std::optional<size_t> next_stop, int h, int dt,
11401173
const std::vector<std::pair<double, double>>& train_positions,
@@ -1153,14 +1186,20 @@ std::pair<double, double> cda_rail::simulator::GreedySimulator::get_ma_and_maxv(
11531186
double max_v = NAN;
11541187
ma = get_absolute_distance_ma(tr, ma, train_positions, train_velocities,
11551188
trains_in_network, trains_left, tr_on_edges);
1156-
std::tie(ma, max_v) = get_future_max_speed_constraints(
1189+
const auto tmp_ma_data = get_future_max_speed_constraints(
11571190
tr, train, train_positions.at(tr).second, train_velocities.at(tr), ma, dt,
11581191
also_limit_speed_by_leaving_edges);
1159-
max_v = std::min(max_v, get_max_speed_exit_headway(
1160-
tr, train, train_positions.at(tr).second,
1161-
train_velocities.at(tr), h, dt));
1162-
1163-
return {ma, max_v};
1192+
ma = tmp_ma_data.ma;
1193+
const double max_speed_exit_h = get_max_speed_exit_headway(
1194+
tr, train, train_positions.at(tr).second, train_velocities.at(tr), h, dt);
1195+
max_v = std::min(tmp_ma_data.max_v, max_speed_exit_h);
1196+
const double max_v_without_route_end =
1197+
std::min(tmp_ma_data.max_v_without_route_end, max_speed_exit_h);
1198+
1199+
return {.ma = ma,
1200+
.ma_without_route_end = tmp_ma_data.ma_without_route_end,
1201+
.max_v = max_v,
1202+
.max_v_without_route_end = max_v_without_route_end};
11641203
}
11651204

11661205
double cda_rail::simulator::GreedySimulator::get_v1_from_ma(double v_0,

0 commit comments

Comments
 (0)