diff --git a/.gitignore b/.gitignore index 12ed1504f..2e89987ef 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ bin/ build/ develop-eggs/ dist/ +results/ eggs/ lib/ lib64/ diff --git a/.gitmodules b/.gitmodules index 209214e8c..bbabcd307 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "extern/plog"] path = extern/plog url = https://github.com/SergiusTheBest/plog.git +[submodule "extern/openGA"] + path = extern/openGA + url = https://github.com/Arash-codedev/openGA.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ba9c2b50b..cbac57cd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,19 @@ # set required cmake version -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION 3.24) # This avoids googletest complaining that this (IPO) policy is not set cmake_policy(SET CMP0069 NEW) set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) project( rail LANGUAGES CXX DESCRIPTION "") +# Settings for openGA header +add_definitions(-DOPENGA_EXTERN_LOCAL_VARS) + # check whether the submodule ``modulename`` is correctly cloned in the ``/extern`` directory. macro(CHECK_SUBMODULE_PRESENT modulename) if(NOT EXISTS "${PROJECT_SOURCE_DIR}/extern/${modulename}/CMakeLists.txt") @@ -22,6 +25,17 @@ macro(CHECK_SUBMODULE_PRESENT modulename) endif() endmacro() +# check whether openGA is present under modulename +macro(CHECK_OPENGA_PRESENT modulename) + if(NOT EXISTS "${PROJECT_SOURCE_DIR}/extern/${modulename}/src/${modulename}.hpp") + message( + FATAL_ERROR + "${modulename} submodule not cloned properly. \ + Please run `git submodule update --init --recursive` \ + from the main project directory") + endif() +endmacro() + include(cmake/StandardProjectSettings.cmake) if(NOT TARGET project_options) # Use the options specified in CompilerOptions.cmake @@ -41,6 +55,7 @@ check_submodule_present("json") check_submodule_present("tinyxml2") check_submodule_present("gsl") check_submodule_present("plog") +check_openga_present("openGA") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") diff --git a/README.md b/README.md index b86e4f593..5fd60df4b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ The tool is under active development, and more features will follow. #### System Requirements The tool has been tested under Windows 11 (64-bit) using the MSVC compiler. -It should also be compatible with any current version of g++ supporting C++17 and a minimum CMake version of 3.19. +It should also be compatible with any current version of g++ supporting C++20 and a minimum CMake version of 3.24. Moreover, the tool requires a local installation of a recent Gurobi [[8]](#references) version available at [https://www.gurobi.com/downloads/gurobi-software/](https://www.gurobi.com/downloads/gurobi-software/) as well as a valid [license](https://www.gurobi.com/solutions/licensing/). For academic purposes, Gurobi offers [free academic licenses](https://www.gurobi.com/academia/academic-program-and-licenses/). diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 9f8fbf024..6738f36cc 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -6,6 +6,11 @@ endmacro() add_sim_executable(vss_generation_timetable_mip_testing) add_sim_executable(vss_generation_timetable_mip_iterative_vss_testing) +add_sim_executable(vss_generation_timetable_simulator) +add_sim_executable(vss_generation_timetable_simulator_genetic_parameter_testing) +add_sim_executable(vss_generation_timetable_simulator_greedy_parameter_testing) +add_sim_executable(vss_generation_timetable_simulator_local_parameter_testing) +add_sim_executable(vss_generation_timetable_simulator_search_methods_testing) add_sim_executable(vss_generation_timetable_iterative_parameter_testing) add_sim_executable(vss_generation_timetable_using_mb_information_testing) add_sim_executable(gen_po_moving_block_lazy_vss_gen_testing) diff --git a/apps/vss_generation_timetable_simulator.cpp b/apps/vss_generation_timetable_simulator.cpp new file mode 100644 index 000000000..1e4a80fab --- /dev/null +++ b/apps/vss_generation_timetable_simulator.cpp @@ -0,0 +1,53 @@ +#include "simulation/RoutingSolver.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) + +int main(int argc, char** argv) { + // Only log to console using std::cerr and std::cout respectively unless + // initialized differently + if (plog::get() == nullptr) { + static plog::ColorConsoleAppender console_appender; + plog::init(plog::debug, &console_appender); + } + + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 || + argc != 3) { + PLOGI << "Usage: vss_generation_timetable_simulator [NETWORK PATH] [OUTPUT " + "PATH]" + << std::endl; + std::exit(0); + } + + auto args = gsl::span(argv, argc); + + const std::string model_path = args[1]; + const std::string output_path = args[2]; + + cda_rail::Network network = + cda_rail::Network::import_network(model_path + "/network"); + cda_rail::Timetable timetable = + cda_rail::Timetable::import_timetable(model_path + "/timetable", network); + + cda_rail::sim::SimulationInstance instance{network, timetable, false}; + + cda_rail::sim::RoutingSolver solver{instance}; + + auto res = solver.greedy_search(std::chrono::seconds{6}, {}, + {std::chrono::milliseconds{50}}); + + if (std::get<0>(res)) + std::get<0>(res).value().get_trajectories().export_csv(output_path + + "/result.csv"); +} + +// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) diff --git a/apps/vss_generation_timetable_simulator_genetic_parameter_testing.cpp b/apps/vss_generation_timetable_simulator_genetic_parameter_testing.cpp new file mode 100644 index 000000000..8d460b78b --- /dev/null +++ b/apps/vss_generation_timetable_simulator_genetic_parameter_testing.cpp @@ -0,0 +1,224 @@ +#include "simulation/RoutingSolver.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) + +int main(int argc, char** argv) { + auto args = gsl::span(argv, argc); + + const std::string model_path = args[1]; + const std::string output_path = args[2]; + const std::string model_name = + model_path.substr(model_path.find_last_of("/"), model_path.length()); + + cda_rail::Network network = + cda_rail::Network::import_network(model_path + "/network"); + cda_rail::Timetable timetable = + cda_rail::Timetable::import_timetable(model_path + "/timetable", network); + + cda_rail::sim::SimulationInstance instance{network, timetable, false}; + + size_t processor_count = std::thread::hardware_concurrency(); + if (processor_count == 0) + processor_count = 1; + + cda_rail::sim::GeneticParams ga_params{ + .is_multithread = false, + .population = 1000, + .gen_max = 20, + .stall_max = 5, + .n_elite = 10, + .xover_frac = 0.7, + .mut_rate = 0.1, + }; + + { + std::vector xover_fractions = {0.01, 0.025, 0.1, 0.5, 0.7, 0.99}; + + for (double xover_fraction : xover_fractions) { + cda_rail::sim::ScoreHistoryCollection score_coll; + std::mutex hist_mutex; + + ga_params.xover_frac = xover_fraction; + + std::vector workers; + for (size_t process = 0; process < processor_count; process++) { + workers.push_back(std::thread{[&]() { + cda_rail::sim::RoutingSolver solver{instance}; + + for (size_t sample_runs = 0; sample_runs < 3; sample_runs++) { + auto res = solver.genetic_search(ga_params); + + if (std::get<0>(res)) { + const std::lock_guard lock(hist_mutex); + score_coll.add(std::get<1>(res)); + } + } + }}); + } + + while (workers.size() > 0) { + workers.back().join(); + workers.pop_back(); + } + + auto save_path = + output_path + "/results/genetic_params/crossover/" + model_name; + cda_rail::is_directory_and_create(save_path); + score_coll.export_csv(save_path + "/score_hist_" + + std::to_string(xover_fraction).substr(0, 5) + + ".csv"); + } + } + + { + std::vector mut_rates = {0.01, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99}; + + for (double mut_rate : mut_rates) { + cda_rail::sim::ScoreHistoryCollection score_coll; + std::mutex hist_mutex; + + ga_params.mut_rate = mut_rate; + + std::vector workers; + for (size_t process = 0; process < processor_count; process++) { + workers.push_back(std::thread{[&]() { + cda_rail::sim::RoutingSolver solver{instance}; + + for (size_t sample_runs = 0; sample_runs < 3; sample_runs++) { + auto res = solver.genetic_search(ga_params); + + if (std::get<0>(res)) { + const std::lock_guard lock(hist_mutex); + score_coll.add(std::get<1>(res)); + } + } + }}); + } + + while (workers.size() > 0) { + workers.back().join(); + workers.pop_back(); + } + + auto save_path = + output_path + "/results/genetic_params/mut_rate/" + model_name; + cda_rail::is_directory_and_create(save_path); + score_coll.export_csv(save_path + "/score_hist_" + + std::to_string(mut_rate).substr(0, 5) + ".csv"); + } + } + + { + std::vector pops = {10, 100, 1000}; + + for (size_t pop : pops) { + cda_rail::sim::ScoreHistoryCollection score_coll; + std::mutex hist_mutex; + + ga_params.population = pop; + ga_params.n_elite = (size_t)std::round(0.1 * pop); + + std::vector workers; + for (size_t process = 0; process < processor_count; process++) { + workers.push_back(std::thread{[&]() { + cda_rail::sim::RoutingSolver solver{instance}; + + for (size_t sample_runs = 0; sample_runs < 3; sample_runs++) { + auto res = solver.genetic_search(ga_params); + + if (std::get<0>(res)) { + const std::lock_guard lock(hist_mutex); + score_coll.add(std::get<1>(res)); + } + } + }}); + } + + while (workers.size() > 0) { + workers.back().join(); + workers.pop_back(); + } + + auto save_path = + output_path + "/results/genetic_params/pop/" + model_name; + cda_rail::is_directory_and_create(save_path); + score_coll.export_csv(save_path + "/score_hist_" + + std::to_string(pop).substr(0, 5) + ".csv"); + } + } + + { + std::vector elites = {0.01, 0.05, 0.1, 0.25, 0.5}; + + for (double elite : elites) { + cda_rail::sim::ScoreHistoryCollection score_coll; + std::mutex hist_mutex; + + ga_params.n_elite = + (size_t)std::round(elite * (double)ga_params.population); + + std::vector workers; + for (size_t process = 0; process < processor_count; process++) { + workers.push_back(std::thread{[&]() { + cda_rail::sim::RoutingSolver solver{instance}; + + for (size_t sample_runs = 0; sample_runs < 3; sample_runs++) { + auto res = solver.genetic_search(ga_params); + + if (std::get<0>(res)) { + const std::lock_guard lock(hist_mutex); + score_coll.add(std::get<1>(res)); + } + } + }}); + } + + while (workers.size() > 0) { + workers.back().join(); + workers.pop_back(); + } + + auto save_path = + output_path + "/results/genetic_params/elite/" + model_name; + cda_rail::is_directory_and_create(save_path); + score_coll.export_csv(save_path + "/score_hist_" + + std::to_string(elite).substr(0, 5) + ".csv"); + } + } + + { + std::vector multithreads = {false, true}; + + for (bool multithread : multithreads) { + cda_rail::sim::ScoreHistoryCollection score_coll; + std::mutex hist_mutex; + + ga_params.is_multithread = multithread; + + cda_rail::sim::RoutingSolver solver{instance}; + + for (size_t sample_runs = 0; sample_runs < 5; sample_runs++) { + auto res = solver.genetic_search(ga_params); + score_coll.add(std::get<1>(res)); + } + + auto save_path = + output_path + "/results/genetic_params/multithread/" + model_name; + cda_rail::is_directory_and_create(save_path); + score_coll.export_csv(save_path + "/score_hist_" + + std::to_string(multithread) + ".csv"); + } + } +} + +// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) diff --git a/apps/vss_generation_timetable_simulator_greedy_parameter_testing.cpp b/apps/vss_generation_timetable_simulator_greedy_parameter_testing.cpp new file mode 100644 index 000000000..2c858a373 --- /dev/null +++ b/apps/vss_generation_timetable_simulator_greedy_parameter_testing.cpp @@ -0,0 +1,72 @@ +#include "simulation/RoutingSolver.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) + +int main(int argc, char** argv) { + auto args = gsl::span(argv, argc); + + const std::string model_path = args[1]; + const std::string output_path = args[2]; + const std::string model_name = + model_path.substr(model_path.find_last_of("/"), model_path.length()); + + cda_rail::Network network = + cda_rail::Network::import_network(model_path + "/network"); + cda_rail::Timetable timetable = + cda_rail::Timetable::import_timetable(model_path + "/timetable", network); + + cda_rail::sim::SimulationInstance instance{network, timetable, false}; + + size_t processor_count = std::thread::hardware_concurrency(); + if (processor_count == 0) + processor_count = 1; + + std::vector test_timeouts = {1, 2, 5, 10, 50, 100, 250}; + + for (size_t train_to : test_timeouts) { + cda_rail::sim::ScoreHistoryCollection score_coll; + std::mutex hist_mutex; + + std::vector workers; + for (size_t process = 0; process < processor_count; process++) { + workers.push_back(std::thread{[&]() { + cda_rail::sim::RoutingSolver solver{instance}; + + for (size_t sample = 0; sample < std::floor(100 / processor_count); + sample++) { + auto res = + solver.greedy_search(std::chrono::seconds{10}, {}, + {std::chrono::milliseconds{train_to}}); + + if (std::get<0>(res)) { + const std::lock_guard lock(hist_mutex); + score_coll.add(std::get<1>(res)); + } + } + }}); + } + + while (workers.size() > 0) { + workers.back().join(); + workers.pop_back(); + } + + auto save_path = + output_path + "/results/greedy_params/stall_time/" + model_name; + cda_rail::is_directory_and_create(save_path); + score_coll.export_csv(save_path + "/score_hist_" + + std::to_string(train_to).substr(0, 5) + ".csv"); + } +} + +// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) diff --git a/apps/vss_generation_timetable_simulator_local_parameter_testing.cpp b/apps/vss_generation_timetable_simulator_local_parameter_testing.cpp new file mode 100644 index 000000000..a9444b092 --- /dev/null +++ b/apps/vss_generation_timetable_simulator_local_parameter_testing.cpp @@ -0,0 +1,76 @@ +#include "simulation/RoutingSolver.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) + +int main(int argc, char** argv) { + auto args = gsl::span(argv, argc); + + const std::string model_path = args[1]; + const std::string output_path = args[2]; + const std::string model_name = + model_path.substr(model_path.find_last_of("/"), model_path.length()); + + cda_rail::Network network = + cda_rail::Network::import_network(model_path + "/network"); + cda_rail::Timetable timetable = + cda_rail::Timetable::import_timetable(model_path + "/timetable", network); + + cda_rail::sim::SimulationInstance instance{network, timetable, false}; + + size_t processor_count = std::thread::hardware_concurrency(); + if (processor_count == 0) + processor_count = 1; + + std::vector> params = { + {0.3, 0.01, 0.99}, {0.4, 0.01, 0.99}, {0.4, 0.001, 0.99}, + {0.6, 0.01, 0.99}, {0.4, 0.01, 0.99}, {0.4, 0.01, 0.999}, + {0.4, 0.0001, 0.99}}; + + for (std::tuple param : params) { + cda_rail::sim::ScoreHistoryCollection score_coll; + std::mutex hist_mutex; + + std::vector workers; + for (size_t process = 0; process < processor_count; process++) { + workers.push_back(std::thread{[&]() { + cda_rail::sim::RoutingSolver solver{instance}; + + for (size_t sample = 0; sample < 20; sample++) { + auto res = solver.random_local_search( + std::chrono::seconds{8}, + {std::get<0>(param), std::get<1>(param), std::get<2>(param)}); + + if (std::get<0>(res)) { + const std::lock_guard lock(hist_mutex); + score_coll.add(std::get<1>(res)); + } + } + }}); + } + + while (workers.size() > 0) { + workers.back().join(); + workers.pop_back(); + } + + auto save_path = output_path + "/results/local_params/multi/" + model_name; + cda_rail::is_directory_and_create(save_path); + score_coll.export_csv( + save_path + "/score_hist_" + + std::to_string(std::get<0>(param)).substr(0, 5) + "-" + + std::to_string(std::get<1>(param)).substr(0, 5) + "-" + + std::to_string((std::get<2>(param))).substr(0, 5) + ".csv"); + } +} + +// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) diff --git a/apps/vss_generation_timetable_simulator_search_methods_testing.cpp b/apps/vss_generation_timetable_simulator_search_methods_testing.cpp new file mode 100644 index 000000000..f34c7cb86 --- /dev/null +++ b/apps/vss_generation_timetable_simulator_search_methods_testing.cpp @@ -0,0 +1,119 @@ +#include "simulation/RoutingSolver.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) + +int main(int argc, char** argv) { + auto args = gsl::span(argv, argc); + + const std::string model_path = args[1]; + const std::string output_path = args[2]; + const std::string model_name = + model_path.substr(model_path.find_last_of("/"), model_path.length()); + + cda_rail::Network network = + cda_rail::Network::import_network(model_path + "/network"); + cda_rail::Timetable timetable = + cda_rail::Timetable::import_timetable(model_path + "/timetable", network); + + cda_rail::sim::SimulationInstance instance{network, timetable, false}; + + size_t processor_count = std::thread::hardware_concurrency(); + if (processor_count == 0) + processor_count = 1; + + cda_rail::sim::GeneticParams ga_params{ + .is_multithread = false, + .population = 1000, + .gen_max = 100, + .stall_max = 10, + .n_elite = 10, + .xover_frac = 0.7, + .mut_rate = 0.1, + }; + + cda_rail::sim::LocalParams loc_params{ + .start_sampling_range_fraction = 0.4, + .abort_sampling_range_fraction = 0.001, + .contraction_coeff = 0.99, + }; + + std::vector methods = {"random", "random+local", "greedy", + "grasp", "genetic"}; + + for (std::string method : methods) { + cda_rail::sim::ScoreHistoryCollection score_coll; + std::mutex hist_mutex; + + std::vector workers; + for (size_t process = 0; process < processor_count; process++) { + workers.push_back(std::thread{[&]() { + cda_rail::sim::RoutingSolver solver{instance}; + + int max_samples; + if (method == "random" || method == "greedy" || + method == "random+local" || method == "grasp") + max_samples = 10; + else + max_samples = 3; + + for (size_t sample = 0; sample < max_samples; sample++) { + // Method here + + std::tuple, + cda_rail::sim::ScoreHistory> + res; + if (method == "random") { + res = solver.random_search(std::chrono::seconds{400}, {}); + } else if (method == "greedy") { + res = solver.greedy_search(std::chrono::seconds{400}, {}, + {std::chrono::milliseconds{10}}); + } else if (method == "random+local") { + res = solver.random_local_search(std::chrono::seconds{400}, + loc_params); + } else if (method == "grasp") { + res = solver.grasp_search(std::chrono::seconds{400}, + {std::chrono::milliseconds{10}}, + loc_params); + } else if (method == "genetic") { + res = solver.genetic_search(ga_params); + } else if (method == "genetic+local") { + res = solver.genetic_search(ga_params, true); + } + + if (std::get<0>(res)) { + const std::lock_guard lock(hist_mutex); + score_coll.add(std::get<1>(res)); + std::get<0>(res).value().get_trajectories().export_csv( + output_path + "/results/methods/" + model_name + "/best_traj_" + + method + ".csv"); + } + + std::cout << "Sample completed." << std::endl; + } + }}); + } + + while (workers.size() > 0) { + workers.back().join(); + workers.pop_back(); + } + + cda_rail::is_directory_and_create(output_path + "/results/methods/" + + model_name); + score_coll.export_csv(output_path + "/results/methods/" + model_name + + "/score_hist_" + method + ".csv"); + } +} + +// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) diff --git a/extern/openGA b/extern/openGA new file mode 160000 index 000000000..26523ec71 --- /dev/null +++ b/extern/openGA @@ -0,0 +1 @@ +Subproject commit 26523ec71dd301586054896d3f9bbdeeac35b29b diff --git a/include/datastructure/GeneralTimetable.hpp b/include/datastructure/GeneralTimetable.hpp index 8746d7d03..f82ed9687 100644 --- a/include/datastructure/GeneralTimetable.hpp +++ b/include/datastructure/GeneralTimetable.hpp @@ -684,8 +684,8 @@ class GeneralTimetable : BaseTimetable { return get_schedule(train_list.get_train_index(train_name)); }; - virtual bool is_forced_to_stop(const std::string& train_name, - int time) const { + [[nodiscard]] bool is_forced_to_stop(const std::string& train_name, + int time) const override { return get_schedule(train_name).is_forced_to_stop(time); } diff --git a/include/datastructure/RailwayNetwork.hpp b/include/datastructure/RailwayNetwork.hpp index 91550d631..5a6b98901 100644 --- a/include/datastructure/RailwayNetwork.hpp +++ b/include/datastructure/RailwayNetwork.hpp @@ -545,6 +545,12 @@ class Network { [[nodiscard]] std::vector> all_edge_pairs_shortest_paths() const; + [[nodiscard]] std::vector> + all_vertex_pairs_shortest_paths() const; + + [[nodiscard]] std::vector> + all_vertex_pairs_shortest_paths_undirected() const; + [[nodiscard]] std::optional shortest_path(size_t source_edge_id, size_t target_vertex_id) const; diff --git a/include/datastructure/Route.hpp b/include/datastructure/Route.hpp index fccd51d93..01a81c2b4 100644 --- a/include/datastructure/Route.hpp +++ b/include/datastructure/Route.hpp @@ -106,6 +106,10 @@ class RouteMap { void add_empty_route(const std::string& train_name); void add_empty_route(const std::string& train_name, const TrainList& trains); + void set_route(const std::string& train_name, const Route& route) { + routes.insert_or_assign(train_name, route); + }; + void push_back_edge(const std::string& train_name, size_t edge_index, const Network& network); void push_back_edge(const std::string& train_name, size_t source, diff --git a/include/probleminstances/GeneralProblemInstance.hpp b/include/probleminstances/GeneralProblemInstance.hpp index e6b438747..bdf8d69d9 100644 --- a/include/probleminstances/GeneralProblemInstance.hpp +++ b/include/probleminstances/GeneralProblemInstance.hpp @@ -241,6 +241,10 @@ class GeneralProblemInstanceWithScheduleAndRoutes routes.add_empty_route(train_name, get_train_list()); }; + void set_route(const std::string& train_name, const Route& route) { + routes.set_route(train_name, route); + }; + void push_back_edge_to_route(const std::string& train_name, size_t edge_index) { routes.push_back_edge(train_name, edge_index, this->const_n()); @@ -691,6 +695,10 @@ class SolGeneralProblemInstanceWithScheduleAndRoutes this->instance.add_empty_route(train_name); }; + void set_route(const std::string& train_name, const Route& route) { + this->instance.set_route(train_name, route); + }; + void push_back_edge_to_route(const std::string& train_name, size_t edge_index) { this->instance.push_back_edge_to_route(train_name, edge_index); diff --git a/include/simulation/EdgeTrajectory.hpp b/include/simulation/EdgeTrajectory.hpp new file mode 100644 index 000000000..f51936ecc --- /dev/null +++ b/include/simulation/EdgeTrajectory.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include "simulation/RoutingSolution.hpp" +#include "simulation/SimulationInstance.hpp" + +#include +#include + +namespace cda_rail::sim { + +struct TrainState { + /** + * Single train state + */ + size_t timestep; // [0, n_timesteps - 1] + size_t edge; // [0, network.edges.size() - 1] + double position; // [0, 1] + bool orientation; // true, false = forward, backward + double speed; // (-Inf, Inf) +}; + +enum EdgeEntryOutcome { + NORMAL, + OVERSPEED, + DEADEND, + TIME_END, +}; + +struct EdgeEntry { + // TODO: should use getter functions since it has an invariant + EdgeEntryOutcome outcome; + std::optional new_state; + + EdgeEntry(EdgeEntryOutcome outcome, std::optional new_state); +}; + +struct EdgeTraversal { + size_t from_timestep; + size_t from_edge; + bool from_exit_point; // true, false = forward, backward + size_t vertex; // [0, network.edges.size() - 1] + bool crossing_orientation; // true, false = forward, backward + double leftover_movement; // (0, Inf) + double speed; // (-Inf, Inf) +}; + +class EdgeTrajectory { + /** + * Continuous train state on one edge + */ + std::reference_wrapper instance_r; + std::reference_wrapper train_r; + + size_t first_timestep; // [0, n_timesteps - 1] + size_t last_timestep; // [0, n_timesteps - 1] + size_t edge; // [0, network.edges.1ize() - 1] + bool orientation; // true, false = forward, backward + + std::vector positions; // [0, 1] + std::vector speeds; // (-Inf, Inf) + + std::optional traversal; + +public: + EdgeTrajectory() = delete; + // Simulate movement on edge from initial state and v_targets + EdgeTrajectory(const SimulationInstance& instance, const Train& train, + SpeedTargets& v_targets, TrainState initial_state); + + EdgeEntry enter_next_edge(double switch_direction) const; + + void check_speed_limits() const; + + cda_rail::ScheduledStop get_stop() const; + + size_t get_first_timestep() const; + size_t get_last_timestep() const; + size_t get_edge() const; + bool get_orientation() const; + const std::vector& get_positions() const; + const std::vector& get_speeds() const; + std::optional get_traversal() const; +}; + +std::optional determine_first_state(const Network& network, + EdgeTraversal exit, + double switch_direction); +EdgeTraversal determine_exit(const Network& network, TrainState overshot_state); + +}; // namespace cda_rail::sim diff --git a/include/simulation/Objectives.hpp b/include/simulation/Objectives.hpp new file mode 100644 index 000000000..c24072347 --- /dev/null +++ b/include/simulation/Objectives.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "simulation/TrainTrajectorySet.hpp" + +namespace cda_rail::sim { + +double combined_objective(const TrainTrajectorySet& traj_set); + +double collision_penalty(const TrainTrajectorySet& traj_set); + +double destination_penalty(const TrainTrajectorySet& traj_set); + +double destination_penalty(const TrainTrajectory& traj); + +double stop_penalty(const TrainTrajectorySet& traj_set); + +double stop_penalty(const TrainTrajectory& traj); + +double reciprocal_dist_penalty(double dist); + +} // namespace cda_rail::sim diff --git a/include/simulation/RoutingSolution.hpp b/include/simulation/RoutingSolution.hpp new file mode 100644 index 000000000..d5e810348 --- /dev/null +++ b/include/simulation/RoutingSolution.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "simulation/SimulationInstance.hpp" +#include "simulation/SpeedTargets.hpp" + +#include +#include +#include + +namespace cda_rail::sim { + +struct RoutingSolution { + /** + * Heuristic routing decision variables for a single train + * + * @param v_targets speed targets to accelerate towards + * (timestep [0, n_timesteps - 1], speed [-max_speed,+max_speed]) + * @param switch_directions directions to take at vertices + * direction [0,1] + * switch_direction selects from list of available next edges + */ + SpeedTargets v_targets; + std::vector switch_directions; + +public: + RoutingSolution() = delete; + // Stationary solution + RoutingSolution(const SimulationInstance& instance); + // Random solution + RoutingSolution(const SimulationInstance& instance, const Train& train, + std::ranlux24_base& rng_engine); + RoutingSolution(const SimulationInstance& instance, const Train& train, + const std::function& rnd01); + // Specific solution + RoutingSolution(const SimulationInstance& instance, + const SpeedTargets& targets, std::vector directions, + const Train& train); +}; + +}; // namespace cda_rail::sim diff --git a/include/simulation/RoutingSolutionSet.hpp b/include/simulation/RoutingSolutionSet.hpp new file mode 100644 index 000000000..d9181bb4b --- /dev/null +++ b/include/simulation/RoutingSolutionSet.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "simulation/RoutingSolution.hpp" +#include "simulation/SimulationInstance.hpp" + +#include + +namespace cda_rail::sim { + +struct RoutingSolutionSet { + /** + * Routing solutions for all trains in a timetable + * + * @param solutions Map containing train name and corresponding + * RoutingSolution + */ + + std::unordered_map solutions; + +public: + // No Solutions + RoutingSolutionSet(); + // Stationary solutions + RoutingSolutionSet(const SimulationInstance& instance); + // Random solutions + RoutingSolutionSet(const SimulationInstance& instance, + std::ranlux24_base& rng_engine); + RoutingSolutionSet(const SimulationInstance& instance, + const std::function& rnd01); + + void perturb(const SimulationInstance& instance, double fraction, + std::ranlux24_base& rng_engine); + void perturb(const SimulationInstance& instance, double fraction, + const std::function& rnd01); +}; + +}; // namespace cda_rail::sim diff --git a/include/simulation/RoutingSolver.hpp b/include/simulation/RoutingSolver.hpp new file mode 100644 index 000000000..13a5bb4f7 --- /dev/null +++ b/include/simulation/RoutingSolver.hpp @@ -0,0 +1,125 @@ +#pragma once + +#include "simulation/RoutingSolutionSet.hpp" +#include "simulation/SolverResult.hpp" +#include "simulation/TrainTrajectorySet.hpp" + +#include +#include +#include +#include +#include +#include + +namespace cda_rail::sim { + +struct ScoreHistory + : public std::vector> { + void export_csv(const std::filesystem::path& p) const; +}; + +class ScoreHistoryCollection : std::vector { +public: + void export_csv(const std::filesystem::path& p) const; + void add(ScoreHistory hist); +}; + +struct GeneticParams { + bool is_multithread; + size_t population; + int gen_max; + int stall_max; + int n_elite; + double xover_frac; + double mut_rate; +}; + +struct LocalParams { + double start_sampling_range_fraction; + double abort_sampling_range_fraction; + double contraction_coeff; +}; + +struct GreedyParams { + std::chrono::milliseconds per_train_stall_time; +}; + +class RoutingSolver { + /** + * Performs heuristic routing for a SimulationInstance + * + * @param instance contains constant parameters + * @param rng_engine used for solution generation + */ + + const SimulationInstance& instance; + std::ranlux24_base rng_engine; + +public: + RoutingSolver() = delete; + RoutingSolver(const SimulationInstance& instance); + + std::tuple + local_search(RoutingSolutionSet starting_solution, LocalParams params); + + std::tuple + local_search(RoutingSolutionSet starting_solution, LocalParams params, + const std::function& rng01); + + std::tuple, ScoreHistory> + grasp_search(std::chrono::seconds max_search_time, GreedyParams gre_params, + LocalParams loc_params); + + std::tuple, ScoreHistory> + random_search(std::optional max_search_time, + std::optional max_stall_time); + + std::tuple, ScoreHistory> + random_local_search(std::chrono::seconds max_search_time, LocalParams params); + + std::tuple, ScoreHistory> + greedy_search(std::optional max_search_time, + std::optional max_stall_time, + GreedyParams params); + + std::optional greedy_solution(GreedyParams params); + + std::tuple, ScoreHistory> + genetic_search(GeneticParams params, bool local_improv = false); + + // GA Helpers + struct MiddleCost { + double score; + }; + + typedef EA::Genetic GA_Type; + typedef EA::GenerationType Generation_Type; + + void init_genes(RoutingSolutionSet& p, + const std::function& rnd01); + + bool eval_solution(const RoutingSolutionSet& p, MiddleCost& c); + + RoutingSolutionSet mutate(const RoutingSolutionSet& X_base, + const std::function& rnd01, + double shrink_scale); + + RoutingSolutionSet crossover(const RoutingSolutionSet& X1, + const RoutingSolutionSet& X2, + const std::function& rnd01); + + RoutingSolutionSet + crossover_local_improv(const RoutingSolutionSet& X1, + const RoutingSolutionSet& X2, + const std::function& rnd01); + + double calculate_SO_total_fitness(const GA_Type::thisChromosomeType& X); + + void SO_report_generation( + const std::chrono::steady_clock::time_point starting_time, + ScoreHistory& hist, int generation_number, + const EA::GenerationType& last_generation, + const RoutingSolutionSet& best_genes); +}; + +}; // namespace cda_rail::sim diff --git a/include/simulation/SimulationInstance.hpp b/include/simulation/SimulationInstance.hpp new file mode 100644 index 000000000..e711fd0a4 --- /dev/null +++ b/include/simulation/SimulationInstance.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "datastructure/RailwayNetwork.hpp" +#include "datastructure/Timetable.hpp" + +#include + +namespace cda_rail::sim { + +struct SimulationInstance { + /** + * Environment for Microscopic Simulation + * + * Requires an unidirectional (non-doubled edges) network since + * trains can traverse edges in both directions + * even if allow_reversing=false + */ + + const Network network; + const Timetable timetable; + const std::vector> shortest_paths; + + const size_t n_timesteps; + const size_t n_switch_vars; + // Determines if trains can leave an edge in the direction they came from + const bool allow_reversing; + +public: + SimulationInstance() = delete; + // TODO: remove double edges from template networks + SimulationInstance(Network network, Timetable timetable, + bool allow_reversing); + + double get_max_train_speed() const; + double get_shortest_track() const; + size_t get_last_train_departure() const; +}; + +}; // namespace cda_rail::sim diff --git a/include/simulation/SolverResult.hpp b/include/simulation/SolverResult.hpp new file mode 100644 index 000000000..e3fdabb73 --- /dev/null +++ b/include/simulation/SolverResult.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "simulation/Objectives.hpp" +#include "simulation/RoutingSolutionSet.hpp" +#include "simulation/TrainTrajectorySet.hpp" + +namespace cda_rail::sim { + +struct ScoreSet { + std::unordered_map stop_scores; + std::unordered_map destination_scores; + double collision_score = 0; + + double get_score() const; + double get_collision_score() const; + double get_norm_stop_score() const; + double get_norm_destination_score() const; +}; + +class SolverResult { + RoutingSolutionSet solutions; + TrainTrajectorySet trajectories; + ScoreSet scores; + +public: + SolverResult(const SimulationInstance& instance); + SolverResult(const SimulationInstance& instance, + const RoutingSolutionSet& solutions); + + void insert_or_assign(const RoutingSolution& solution, + const TrainTrajectory& trajectory); + + const RoutingSolutionSet& get_solutions() const; + const TrainTrajectorySet& get_trajectories() const; + const ScoreSet& get_score_set() const; +}; + +}; // namespace cda_rail::sim diff --git a/include/simulation/SpeedTargets.hpp b/include/simulation/SpeedTargets.hpp new file mode 100644 index 000000000..83877192d --- /dev/null +++ b/include/simulation/SpeedTargets.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace cda_rail::sim { + +struct SpeedTargets { + /** + * Timestep-Speed tuples that train acceleration follows + * Speed target is determined by the previous tuple + */ + std::map targets; + +public: + SpeedTargets() = default; + SpeedTargets(std::vector timesteps, std::vector speeds); + + void limit_speed_from(double maximum, size_t timestep); + void insert(std::map add_targets); // does not replace + void delete_range(size_t start, size_t end); + void set_range(size_t start, size_t end, double value); + + double find_target_speed(size_t timestep) const; + std::optional find_next_reversal(size_t timestep) const; + std::map copy_range(size_t start, size_t end) const; + bool is_first_target(size_t timestep) const; + size_t size() const; +}; + +}; // namespace cda_rail::sim diff --git a/include/simulation/TrainTrajectory.hpp b/include/simulation/TrainTrajectory.hpp new file mode 100644 index 000000000..2637f0944 --- /dev/null +++ b/include/simulation/TrainTrajectory.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include "datastructure/Route.hpp" +#include "simulation/EdgeTrajectory.hpp" + +#include +#include +#include +#include + +namespace cda_rail::sim { + +using BrakingPeriod = std::tuple; + +class TrainTrajectory { + /** + * Path of a single train over entire timespan + * Repairs solution speeds to be feasible + */ + std::reference_wrapper instance_r; + std::reference_wrapper train_r; + + std::vector edge_trajs; + std::vector remaining_planned_stops; + RoutingSolution solution; + +private: + void backtrack_trajectory(size_t timestep); + + // Modify speed targets to reach velocity before last edge traversal + BrakingPeriod add_braking(double target_speed, + std::optional hold_until, + std::optional hold_at_least); + + std::optional + find_latest_braking_period(double target_speed) const; + + // Returns end of braking if braking is feasible + std::optional is_feasible_braking_point(size_t start_braking, + double target_speed) const; + + double distance_to_last_traversal(size_t timestep) const; + +public: + TrainTrajectory() = delete; + TrainTrajectory(const SimulationInstance& instance, const Train& train, + RoutingSolution solution); + + std::optional get_state(size_t timestep) const; + size_t find_traj_idx(size_t timestep) const; + size_t get_remaining_stop_amount() const; + size_t get_earliest_affected_trajectory(size_t timestep) const; + size_t get_first_timestep() const; + size_t get_last_timestep() const; + + const Train& get_train() const; + const SimulationInstance& get_instance() const; + + TrainState read_initial_train_state() const; + void check_speed_limits() const; + std::optional train_vertex_distance(size_t vertex, + size_t timestep) const; + + // Return route in bidirectional format and position on this route + std::tuple>> + convert_to_vss_format(const Network& network_bidirec) const; +}; + +}; // namespace cda_rail::sim diff --git a/include/simulation/TrainTrajectorySet.hpp b/include/simulation/TrainTrajectorySet.hpp new file mode 100644 index 000000000..957c10e08 --- /dev/null +++ b/include/simulation/TrainTrajectorySet.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "probleminstances/GeneralPerformanceOptimizationInstance.hpp" +#include "simulation/RoutingSolutionSet.hpp" +#include "simulation/TrainTrajectory.hpp" + +#include +#include + +namespace cda_rail::sim { + +class TrainTrajectorySet { + /** + * Trajectories for all trains in a timetable + * + * @param trajectories Map containing train name and corresponding + * TrainTrajectory + */ + std::reference_wrapper instance; + + std::unordered_map trajectories; + +public: + TrainTrajectorySet() = delete; + TrainTrajectorySet(const SimulationInstance& instance); + TrainTrajectorySet(const SimulationInstance& instance, + const RoutingSolutionSet& solution_set); + + void insert_or_assign(std::string name, TrainTrajectory traj); + + std::optional train_distance(std::string train1, std::string train2, + size_t timestep) const; + std::optional train_vertex_distance(std::string train, size_t vertex, + size_t timestep) const; + + void export_csv(const std::filesystem::path& p) const; + + void check_speed_limits() const; + + const std::unordered_map& get_map() const; + std::optional + get_traj(std::string train_name) const; + const SimulationInstance& get_instance() const; + size_t size() const; + bool contains(std::string train_name) const; + + instances::SolGeneralPerformanceOptimizationInstance< + instances::GeneralPerformanceOptimizationInstance> + to_vss_solution(const cda_rail::Network& bidirec_network) const; +}; + +}; // namespace cda_rail::sim diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 90d4c5476..d9bf8bf14 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,7 +34,27 @@ add_library( solver/mip-based/VSSGenTimetableSolver_MovingBlockInformation.cpp solver/mip-based/GenPOMovingBlockMIPSolver.cpp solver/mip-based/GenPOMovingBlockMIPSolver_SolutionExtraction.cpp - solver/mip-based/GenPOMovingBlockMIPSolver_Lazy.cpp) + solver/mip-based/GenPOMovingBlockMIPSolver_Lazy.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/SimulationInstance.hpp + simulation/SimulationInstance.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/RoutingSolution.hpp + simulation/RoutingSolution.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/RoutingSolutionSet.hpp + simulation/RoutingSolutionSet.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/RoutingSolver.hpp + simulation/RoutingSolver.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/SolverResult.hpp + simulation/SolverResult.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/EdgeTrajectory.hpp + simulation/EdgeTrajectory.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/TrainTrajectory.hpp + simulation/TrainTrajectory.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/TrainTrajectorySet.hpp + simulation/TrainTrajectorySet.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/SpeedTargets.hpp + simulation/SpeedTargets.cpp + ${PROJECT_SOURCE_DIR}/include/simulation/Objectives.hpp + simulation/Objectives.cpp) # set include directories target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include) @@ -62,6 +82,9 @@ target_link_libraries(${PROJECT_NAME} PUBLIC Microsoft.GSL::GSL) add_subdirectory(${PROJECT_SOURCE_DIR}/extern/plog extern/plog) target_link_libraries(${PROJECT_NAME} PUBLIC plog::plog) +# Include openGA +target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/extern/openGA/src) + # add gtest for FRIEND_TEST add_subdirectory(${PROJECT_SOURCE_DIR}/extern/googletest extern/googletest) target_link_libraries(${PROJECT_NAME} PUBLIC gtest) diff --git a/src/datastructure/RailwayNetwork.cpp b/src/datastructure/RailwayNetwork.cpp index 3c5b3f2ca..0814aa47c 100644 --- a/src/datastructure/RailwayNetwork.cpp +++ b/src/datastructure/RailwayNetwork.cpp @@ -304,7 +304,8 @@ void cda_rail::Network::add_successor(size_t edge_in, size_t edge_out) { if (!has_edge(edge_out)) { throw exceptions::EdgeNotExistentException(edge_out); } - if (edges[edge_in].target != edges[edge_out].source) { + if (edges[edge_in].target != edges[edge_out].source && + edges[edge_in].target != edges[edge_out].target) { throw exceptions::ConsistencyException("Edge " + std::to_string(edge_out) + " is not adjacent to " + std::to_string(edge_in)); @@ -1639,7 +1640,7 @@ cda_rail::Network::all_edge_pairs_shortest_paths() const { * Given e0 = (v0, v1) and e1 = (v2, v3), the distance refers to the distance * between v1 and v3 by only using valid successors. If v0 or v2 are of * interest the value has to be post-processed accordingly. The distance is - * std::numeric_limits::max()/3 if no path exists. This methods uses + * std::numeric_limits::max()/3 if no path exists. This method uses * the Floyd-Warshall algorithm. * * @return: Matrix of distances between all edges @@ -1669,6 +1670,76 @@ cda_rail::Network::all_edge_pairs_shortest_paths() const { return ret_val; } +std::vector> +cda_rail::Network::all_vertex_pairs_shortest_paths() const { + /** + * Calculates all shortest paths between all vertices. + * The distance is * std::numeric_limits::max()/3 if no path exists. + * This method uses the Floyd-Warshall algorithm. + * + * @return: Matrix of distances between all edges + */ + std::vector> ret_val( + number_of_vertices(), std::vector(number_of_vertices(), INF)); + + for (size_t e = 0; e < number_of_edges(); ++e) { + Edge edge = get_edge(e); + ret_val[edge.source][edge.target] = edge.length; + } + + for (size_t v = 0; v < number_of_vertices(); ++v) { + ret_val[v][v] = 0; + } + + // Floyd-Warshall iterations + for (int k = 0; k < number_of_vertices(); ++k) { + for (int i = 0; i < number_of_vertices(); ++i) { + for (int j = 0; j < number_of_vertices(); ++j) { + ret_val[i][j] = std::min(ret_val[i][j], ret_val[i][k] + ret_val[k][j]); + } + } + } + + return ret_val; +} + +std::vector> +cda_rail::Network::all_vertex_pairs_shortest_paths_undirected() const { + /** + * Calculates all shortest paths between all vertices. + * All edges are considered bidirectional. + * The distance is * std::numeric_limits::max()/3 if no path exists. + * This method uses the Floyd-Warshall algorithm. + * + * @return: Matrix of distances between all edges + */ + std::vector> ret_val( + number_of_vertices(), std::vector(number_of_vertices(), INF)); + + for (size_t e = 0; e < number_of_edges(); ++e) { + Edge edge = get_edge(e); + ret_val[edge.source][edge.target] = + std::min(edge.length, ret_val[edge.source][edge.target]); + ret_val[edge.target][edge.source] = + std::min(edge.length, ret_val[edge.target][edge.source]); + } + + for (size_t v = 0; v < number_of_vertices(); ++v) { + ret_val[v][v] = 0; + } + + // Floyd-Warshall iterations + for (int k = 0; k < number_of_vertices(); ++k) { + for (int i = 0; i < number_of_vertices(); ++i) { + for (int j = 0; j < number_of_vertices(); ++j) { + ret_val[i][j] = std::min(ret_val[i][j], ret_val[i][k] + ret_val[k][j]); + } + } + } + + return ret_val; +} + std::optional cda_rail::Network::shortest_path(size_t source_edge_id, size_t target_vertex_id) const { diff --git a/src/simulation/EdgeTrajectory.cpp b/src/simulation/EdgeTrajectory.cpp new file mode 100644 index 000000000..693f1e317 --- /dev/null +++ b/src/simulation/EdgeTrajectory.cpp @@ -0,0 +1,248 @@ +#include "simulation/EdgeTrajectory.hpp" + +#include + +cda_rail::sim::EdgeEntry::EdgeEntry( + cda_rail::sim::EdgeEntryOutcome outcome, + std::optional new_state) + : outcome(outcome), new_state(new_state) { + bool requires_state = (outcome == NORMAL || outcome == OVERSPEED); + if (requires_state && !new_state.has_value() || + !requires_state && new_state.has_value()) + throw std::logic_error("Improper result of edge entry."); +} + +cda_rail::sim::EdgeTrajectory::EdgeTrajectory( + const SimulationInstance& instance, const Train& train, + SpeedTargets& v_targets, TrainState initial_state) + : instance_r(std::ref(instance)), train_r(std::ref(train)), + first_timestep(initial_state.timestep), edge(initial_state.edge), + orientation(initial_state.orientation) { + double edge_length = instance_r.get().network.get_edge(edge).length; + double edge_length_divisor = 1 / edge_length; + // TODO: narrowing cast should be avoided + size_t exit_time = + instance_r.get().timetable.get_schedule(train_r.get().name).get_t_n(); + + speeds.push_back(initial_state.speed); + positions.push_back(initial_state.position); + + for (int timestep = first_timestep + 1; timestep <= exit_time; timestep++) { + double speed = speeds.back(); + double speed_disparity = v_targets.find_target_speed(timestep - 1) - speed; + double acceleration; + if (speed_disparity >= 0) { + acceleration = std::min(speed_disparity, train_r.get().acceleration); + } else { + acceleration = std::max(speed_disparity, -train_r.get().deceleration); + } + + speeds.push_back(speeds.back() + acceleration); + positions.push_back(positions.back() + ((2 * ((double)orientation) - 1) * + speed * edge_length_divisor)); + + if (positions.back() > 1 || positions.back() < 0) { + last_timestep = first_timestep + positions.size() - 2; + traversal = determine_exit(instance_r.get().network, + TrainState{ + .timestep = last_timestep + 1, + .edge = edge, + .position = positions.back(), + .orientation = orientation, + .speed = speeds.back(), + }); + positions.pop_back(); + speeds.pop_back(); + return; + } + } + + last_timestep = first_timestep + positions.size() - 1; + traversal = {}; +} + +cda_rail::sim::EdgeEntry +cda_rail::sim::EdgeTrajectory::enter_next_edge(double switch_direction) const { + if (!traversal.has_value()) { + return EdgeEntry{TIME_END, {}}; + } + + // traversal along edges until no longer overshooting on first step + // + // +----> EdgeTrajectory ---+ EdgeTraj() + // | | + // | v + // TrainState TrainState + // initial_state ---------> overshot_state + // ^ | + // | | + // +------- EdgeTraversal <-+ + // determine_first_state() determine_exit() + // + + EdgeTraversal edge_exit = traversal.value(); + TrainState new_state; + for (bool overshot = true; overshot != false;) { + // TODO: select separate direction for each iteration + // TODO: trains should be able to exit the network at their designated node + std::optional new_state_result = determine_first_state( + instance_r.get().network, edge_exit, switch_direction); + + if (!new_state_result.has_value()) { + return EdgeEntry{DEADEND, {}}; + } else { + new_state = new_state_result.value(); + } + + overshot = (new_state.position < 0 || new_state.position > 1); + + if (overshot) { + edge_exit = determine_exit(instance_r.get().network, new_state); + } + } + + EdgeEntryOutcome outcome; + if (std::abs(edge_exit.speed) > + instance_r.get().network.get_edge(new_state.edge).max_speed) { + outcome = OVERSPEED; + } else { + outcome = NORMAL; + } + + return EdgeEntry{outcome, new_state}; +} + +std::optional +cda_rail::sim::determine_first_state(const cda_rail::Network& network, + cda_rail::sim::EdgeTraversal traversal, + double switch_direction) { + std::vector viable_next_edges; + if (traversal.from_exit_point) { + viable_next_edges = network.get_successors(traversal.from_edge); + } else { + viable_next_edges = network.get_predecessors(traversal.from_edge); + } + + if (viable_next_edges.size() < 1) + return {}; + + size_t next_edge = viable_next_edges.at( + std::round(switch_direction * (viable_next_edges.size() - 1))); + + bool edge_entry_point = + (network.get_edge(next_edge).target == traversal.vertex); + + double edge_entry_position; + if (edge_entry_point) { + edge_entry_position = 1 - std::abs(traversal.leftover_movement / + network.get_edge(next_edge).length); + } else { + edge_entry_position = std::abs(traversal.leftover_movement / + network.get_edge(next_edge).length); + } + + return cda_rail::sim::TrainState{ + .timestep = traversal.from_timestep + 1, + .edge = next_edge, + .position = edge_entry_position, + .orientation = + !(edge_entry_point != !traversal.crossing_orientation), // XNOR + .speed = traversal.speed, + }; +} + +cda_rail::sim::EdgeTraversal +cda_rail::sim::determine_exit(const cda_rail::Network& network, + cda_rail::sim::TrainState overshot_state) { + double edge_length = network.get_edge(overshot_state.edge).length; + bool exit_point = (overshot_state.position > 1); + size_t vertex; + bool crossing_orientation; + double leftover_movement; + + if (exit_point) { + return cda_rail::sim::EdgeTraversal{ + // Forward exit in edge direction + .from_timestep = overshot_state.timestep - 1, + .from_edge = overshot_state.edge, + .from_exit_point = exit_point, + .vertex = network.get_edge(overshot_state.edge).target, + .crossing_orientation = overshot_state.orientation, + .leftover_movement = (overshot_state.position - 1) * edge_length, + .speed = overshot_state.speed, + }; + } else { + return cda_rail::sim::EdgeTraversal{ + // Backward exit in edge direction + .from_timestep = overshot_state.timestep - 1, + .from_edge = overshot_state.edge, + .from_exit_point = exit_point, + .vertex = network.get_edge(overshot_state.edge).source, + .crossing_orientation = !overshot_state.orientation, + .leftover_movement = -overshot_state.position * edge_length, + .speed = overshot_state.speed, + }; + } +} + +void cda_rail::sim::EdgeTrajectory::check_speed_limits() const { + double speed_limit = + std::max(instance_r.get().network.get_edge(edge).max_speed, + train_r.get().max_speed); + + if (positions.size() > 1) { + double edge_length = instance_r.get().network.get_edge(edge).length; + for (auto pos = positions.begin() + 1; pos != positions.end(); pos++) { + if (std::abs((*pos) - *(pos - 1)) * edge_length > speed_limit) + throw std::logic_error("Overspeed detected."); + } + } + + for (auto speed : speeds) { + if (std::abs(speed) > speed_limit) + throw std::logic_error("Overspeed detected."); + } +} + +cda_rail::ScheduledStop cda_rail::sim::EdgeTrajectory::get_stop() const { + for (auto stop : instance_r.get() + .timetable.get_schedule(train_r.get().name) + .get_stops()) { + auto stop_station = + instance_r.get().timetable.get_station_list().get_station( + stop.get_station_name()); + + if (std::find(stop_station.tracks.begin(), stop_station.tracks.end(), + edge) != stop_station.tracks.end()) + return stop; + } + throw std::invalid_argument("No associated scheduled stop found."); +} + +size_t cda_rail::sim::EdgeTrajectory::get_first_timestep() const { + return first_timestep; +} + +size_t cda_rail::sim::EdgeTrajectory::get_last_timestep() const { + return last_timestep; +} + +size_t cda_rail::sim::EdgeTrajectory::get_edge() const { return edge; } + +bool cda_rail::sim::EdgeTrajectory::get_orientation() const { + return orientation; +} + +const std::vector& +cda_rail::sim::EdgeTrajectory::get_positions() const { + return positions; +} + +const std::vector& cda_rail::sim::EdgeTrajectory::get_speeds() const { + return speeds; +} + +std::optional +cda_rail::sim::EdgeTrajectory::get_traversal() const { + return traversal; +} diff --git a/src/simulation/Objectives.cpp b/src/simulation/Objectives.cpp new file mode 100644 index 000000000..87ec75fb6 --- /dev/null +++ b/src/simulation/Objectives.cpp @@ -0,0 +1,165 @@ +#include "simulation/Objectives.hpp" + +double cda_rail::sim::combined_objective(const TrainTrajectorySet& traj_set) { + /** + * Combined objectives + */ + return collision_penalty(traj_set) + destination_penalty(traj_set) + + stop_penalty(traj_set); +} + +double cda_rail::sim::collision_penalty(const TrainTrajectorySet& traj_set) { + /** + * Check all trains for violation in minimum distances + * Train position is assumed to be the center of the train + * + * @param traj_set Set of train trajectories + * @return Penalty score, lower is better + */ + + constexpr double safety_distance = 100; + + const SimulationInstance& instance = traj_set.get_instance(); + const TrainList& train_list = instance.timetable.get_train_list(); + double score = 0; + + for (auto train1 = train_list.begin(); train1 != train_list.end(); train1++) { + size_t first_step1, last_step1; + if (const auto traj1 = traj_set.get_traj((*train1).name)) { + first_step1 = traj1.value().get_first_timestep(); + last_step1 = traj1.value().get_last_timestep(); + } else { + continue; + } + + for (auto train2 = train1 + 1; train2 != train_list.end(); train2++) { + size_t first_step2, last_step2; + if (const auto traj2 = traj_set.get_traj((*train2).name)) { + first_step2 = traj2.value().get_first_timestep(); + last_step2 = traj2.value().get_last_timestep(); + } else { + continue; + } + + if (last_step1 < first_step2 || last_step2 < first_step1) + continue; + + double required_dist = + 0.5 * (*train1).length + 0.5 * (*train2).length + safety_distance; + + if (2 * required_dist < (*train1).max_speed + (*train2).max_speed) + throw std::logic_error("Time resolution too low."); + + for (size_t timestep = std::max(first_step1, first_step2); + timestep <= std::min(last_step1, last_step2);) { + double dist = + traj_set.train_distance((*train1).name, (*train2).name, timestep) + .value(); + + if (dist >= required_dist) { + double max_approach_speed = (*train1).max_speed + (*train2).max_speed; + double min_time_to_collision = + std::max((dist - required_dist), 0.0) / max_approach_speed; + size_t guaranteed_safe_time = std::floor(min_time_to_collision); + timestep = timestep + std::max(guaranteed_safe_time, (size_t)1); + } else { + // TODO: normalization does not work since the amount of timesteps + // checked is changing + double penalty = 1 - (dist / required_dist); + score = score + penalty; + timestep++; + } + } + } + } + + size_t n_pairs = (train_list.size() * (train_list.size() - 1)) / 2; + return score / n_pairs; +} + +double cda_rail::sim::destination_penalty(const TrainTrajectorySet& traj_set) { + /** + * Penalize all trains for the distance from their scheduled exit at their + * final position Train position is assumed to be the center of the train + * + * @param traj_set Set of train trajectories + * @return Normalized penalty score from 0 to 1, lower is better + */ + + const SimulationInstance& instance = traj_set.get_instance(); + const TrainList& train_list = instance.timetable.get_train_list(); + double score = 0; + + for (auto const& [train_name, traj] : traj_set.get_map()) { + score = score + destination_penalty(traj); + } + + return score / traj_set.size(); +} + +double cda_rail::sim::destination_penalty(const TrainTrajectory& traj) { + /** + * Penalize a train for the distance from their scheduled exit at their + * final position Train position is assumed to be the center of the train + * + * @param traj train trajectory + * @return Normalized penalty score from 0 to 1, lower is better + */ + + const SimulationInstance& instance = traj.get_instance(); + const TrainList& train_list = instance.timetable.get_train_list(); + double score = 0; + + size_t dest_vertex = + instance.timetable.get_schedule(traj.get_train().name).get_exit(); + double max_dist = + *std::max_element(instance.shortest_paths.at(dest_vertex).begin(), + instance.shortest_paths.at(dest_vertex).end()); + double dist = + traj.train_vertex_distance(dest_vertex, traj.get_last_timestep()).value(); + + // TODO: Penalize wrong exit speed as well as position + return dist / max_dist; +} + +double cda_rail::sim::stop_penalty(const TrainTrajectorySet& traj_set) { + /** + * Penalize trains not visiting their scheduled stop + * + * @param traj_set Set of train trajectories + * @return Normalized penalty score from 0 to 1, lower is better + */ + + const SimulationInstance& instance = traj_set.get_instance(); + const TrainList& train_list = instance.timetable.get_train_list(); + double score = 0; + + if (traj_set.size() < 1) + throw std::invalid_argument("Cannot evaluate empty trajectory set."); + + for (auto const& [train_name, traj] : traj_set.get_map()) { + score = score + stop_penalty(traj); + } + + return score / (double)traj_set.size(); +} + +double cda_rail::sim::stop_penalty(const TrainTrajectory& traj) { + /** + * Penalize train not visiting their scheduled stop + * + * @param traj train trajectory + * @return Normalized penalty score from 0 to 1, lower is better + */ + size_t n_remaining_stops = traj.get_remaining_stop_amount(); + size_t n_scheduled_stops = traj.get_instance() + .timetable.get_schedule(traj.get_train().name) + .get_stops() + .size(); + + if (n_scheduled_stops == (size_t)0) { + return 0.0; + } + + return (double)n_remaining_stops / (double)n_scheduled_stops; +} diff --git a/src/simulation/RoutingSolution.cpp b/src/simulation/RoutingSolution.cpp new file mode 100644 index 000000000..44b4edfb7 --- /dev/null +++ b/src/simulation/RoutingSolution.cpp @@ -0,0 +1,93 @@ +#include "simulation/RoutingSolution.hpp" + +cda_rail::sim::RoutingSolution::RoutingSolution( + const SimulationInstance& instance) { + switch_directions.reserve(instance.n_switch_vars); + while (switch_directions.size() < instance.n_switch_vars) { + switch_directions.push_back(0.5); + } + v_targets.targets.insert({0, 0}); +} + +cda_rail::sim::RoutingSolution::RoutingSolution( + const SimulationInstance& instance, const Train& train, + std::ranlux24_base& rng_engine) { + std::uniform_real_distribution uniform(0, 1); + std::uniform_int_distribution uniform_timestep( + 0, instance.n_timesteps - 1); + std::uniform_int_distribution uniform_n_v_target_vars( + 1, instance.n_timesteps); + double min_speed = 0; + if (instance.allow_reversing) + min_speed = -train.max_speed; + std::uniform_real_distribution uniform_train_speed(min_speed, + train.max_speed); + + switch_directions.reserve(instance.n_switch_vars); + while (switch_directions.size() < instance.n_switch_vars) { + switch_directions.push_back(uniform(rng_engine)); + } + + // TODO: ignores duplicates and produces less than n_v_target_vars variables + while (v_targets.size() < uniform_n_v_target_vars(rng_engine)) { + v_targets.targets.insert( + {uniform_timestep(rng_engine), uniform_train_speed(rng_engine)}); + } +} + +cda_rail::sim::RoutingSolution::RoutingSolution( + const SimulationInstance& instance, const Train& train, + const std::function& rnd01) { + auto uniform_n_v_target_vars = [instance, rnd01]() { + return (size_t)std::round(1 + (rnd01() * (instance.n_timesteps - 1))); + }; + + auto uniform_timestep = [instance, rnd01]() { + return (size_t)std::round((rnd01() * (instance.n_timesteps - 1))); + }; + + std::function uniform_train_speed; + if (instance.allow_reversing) { + uniform_train_speed = [train, rnd01]() { + return (2 * rnd01() - 1) * train.max_speed; + }; + } else { + uniform_train_speed = [train, rnd01]() { + return rnd01() * train.max_speed; + }; + } + + switch_directions.reserve(instance.n_switch_vars); + while (switch_directions.size() < instance.n_switch_vars) { + switch_directions.push_back(rnd01()); + } + + // TODO: ignores duplicates and produces less than n_v_target_vars variables + while (v_targets.size() < uniform_n_v_target_vars()) { + v_targets.targets.insert({uniform_timestep(), uniform_train_speed()}); + } +} + +cda_rail::sim::RoutingSolution::RoutingSolution( + const SimulationInstance& instance, const SpeedTargets& targets, + std::vector directions, const Train& train) + : v_targets(targets), switch_directions(directions) { + if (targets.size() < 1 || directions.size() < instance.n_switch_vars) + throw std::invalid_argument("Routing solution is not consistent."); + + // Check speed targets + double min_speed = 0; + if (instance.allow_reversing) + min_speed = -train.max_speed; + + for (const auto& target : targets.targets) { + if (target.second < min_speed || target.second > train.max_speed || + target.first > instance.n_timesteps - 1) + throw std::invalid_argument("Routing solution is not consistent."); + } + + for (const auto& direction : directions) { + if (direction > 1 || direction < 0) + throw std::invalid_argument("Routing solution is not consistent."); + } +} diff --git a/src/simulation/RoutingSolutionSet.cpp b/src/simulation/RoutingSolutionSet.cpp new file mode 100644 index 000000000..7b5c0f175 --- /dev/null +++ b/src/simulation/RoutingSolutionSet.cpp @@ -0,0 +1,115 @@ +#include "simulation/RoutingSolutionSet.hpp" + +#include + +cda_rail::sim::RoutingSolutionSet::RoutingSolutionSet() {} + +cda_rail::sim::RoutingSolutionSet::RoutingSolutionSet( + const SimulationInstance& instance, std::ranlux24_base& rng_engine) { + for (const Train& train : instance.timetable.get_train_list()) { + RoutingSolution sol{instance, train, rng_engine}; + solutions.insert({train.name, sol}); + } +} + +cda_rail::sim::RoutingSolutionSet::RoutingSolutionSet( + const SimulationInstance& instance, + const std::function& rnd01) { + for (const Train& train : instance.timetable.get_train_list()) { + RoutingSolution sol{instance, train, rnd01}; + solutions.insert({train.name, sol}); + } +} + +cda_rail::sim::RoutingSolutionSet::RoutingSolutionSet( + const SimulationInstance& instance) { + for (const Train& train : instance.timetable.get_train_list()) { + RoutingSolution sol{instance}; + solutions.insert({train.name, sol}); + } +} + +void cda_rail::sim::RoutingSolutionSet::perturb( + const SimulationInstance& instance, double fraction, + std::ranlux24_base& rng_engine) { + std::uniform_real_distribution uniform_direction_perturbation( + -fraction, fraction); + + for (auto sol_it = solutions.begin(); sol_it != solutions.end(); sol_it++) { + double max_speed = instance.timetable.get_train_list() + .get_train((*sol_it).first) + .max_speed; + double max_speed_perturbation = fraction * 2 * max_speed; + size_t max_timestep_perturbation = + (size_t)std::ceil(fraction * instance.n_timesteps); + + std::uniform_real_distribution uniform_speed_perturbation( + -max_speed_perturbation, max_speed_perturbation); + std::uniform_int_distribution uniform_timestep_perturbation( + -max_timestep_perturbation, max_timestep_perturbation); + + SpeedTargets new_targets; + for (auto old_target : (*sol_it).second.v_targets.targets) { + new_targets.targets.insert_or_assign( + std::clamp(old_target.first + + uniform_timestep_perturbation(rng_engine), + static_cast(0), instance.n_timesteps - 1), + std::clamp(old_target.second + uniform_speed_perturbation(rng_engine), + -max_speed, max_speed)); + } + + std::vector new_switch_directions; + + for (auto old_direction : (*sol_it).second.switch_directions) { + new_switch_directions.push_back( + std::clamp(old_direction + uniform_direction_perturbation(rng_engine), + 0.0, 1.0)); + } + + (*sol_it).second.v_targets = new_targets; + (*sol_it).second.switch_directions = new_switch_directions; + } +} + +void cda_rail::sim::RoutingSolutionSet::perturb( + const SimulationInstance& instance, double fraction, + const std::function& rnd01) { + auto uniform_direction_perturbation = [fraction, rnd01]() { + return (rnd01() * 2 - 1) * fraction; + }; + + for (auto sol_it = solutions.begin(); sol_it != solutions.end(); sol_it++) { + double max_speed = instance.timetable.get_train_list() + .get_train((*sol_it).first) + .max_speed; + double max_speed_perturbation = fraction * 2 * max_speed; + size_t max_timestep_perturbation = + (size_t)std::ceil(fraction * instance.n_timesteps); + + auto uniform_speed_perturbation = [max_speed_perturbation, rnd01]() { + return (rnd01() * 2 - 1) * max_speed_perturbation; + }; + auto uniform_timestep_perturbation = [max_timestep_perturbation, rnd01]() { + return (int64_t)std::round((rnd01() * 2 - 1) * max_timestep_perturbation); + }; + + SpeedTargets new_targets; + for (auto old_target : (*sol_it).second.v_targets.targets) { + new_targets.targets.insert_or_assign( + std::clamp(old_target.first + uniform_timestep_perturbation(), + static_cast(0), instance.n_timesteps - 1), + std::clamp(old_target.second + uniform_speed_perturbation(), + -max_speed, max_speed)); + } + + std::vector new_switch_directions; + + for (auto old_direction : (*sol_it).second.switch_directions) { + new_switch_directions.push_back(std::clamp( + old_direction + uniform_direction_perturbation(), 0.0, 1.0)); + } + + (*sol_it).second.v_targets = new_targets; + (*sol_it).second.switch_directions = new_switch_directions; + } +} diff --git a/src/simulation/RoutingSolver.cpp b/src/simulation/RoutingSolver.cpp new file mode 100644 index 000000000..be1392cab --- /dev/null +++ b/src/simulation/RoutingSolver.cpp @@ -0,0 +1,513 @@ +#include "simulation/RoutingSolver.hpp" + +namespace EA { +std::mutex mtx_rand; +} + +void cda_rail::sim::ScoreHistory::export_csv( + const std::filesystem::path& p) const { + std::ofstream csvfile(p); + csvfile << "time,score,collision_score,destination_score,stop_score" + << std::endl; + + for (auto it = begin(); it != end(); it++) { + const ScoreSet& set = std::get<1>(*it); + csvfile << std::get<0>(*it).count() << "," << set.get_score() << "," + << set.get_collision_score() << "," + << set.get_norm_destination_score() << "," + << set.get_norm_stop_score() << "," << std::endl; + } + + csvfile.close(); +} + +void cda_rail::sim::ScoreHistoryCollection::export_csv( + const std::filesystem::path& p) const { + std::ofstream csvfile(p); + csvfile << "time,score,collision_score,destination_score,stop_score,sample" + << std::endl; + + for (auto hist_it = begin(); hist_it != end(); hist_it++) { + size_t i_sample = std::distance(begin(), hist_it); + for (auto it = (*hist_it).begin(); it != (*hist_it).end(); it++) { + const ScoreSet& set = std::get<1>(*it); + csvfile << std::get<0>(*it).count() << "," << set.get_score() << "," + << set.get_collision_score() << "," + << set.get_norm_destination_score() << "," + << set.get_norm_stop_score() << "," << i_sample << "," + << std::endl; + } + } + + csvfile.close(); +} + +void cda_rail::sim::ScoreHistoryCollection::add(ScoreHistory hist) { + push_back(hist); +} + +cda_rail::sim::RoutingSolver::RoutingSolver(const SimulationInstance& instance) + : instance(instance), + rng_engine(std::ranlux24_base( + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count())) {}; + +std::tuple, + cda_rail::sim::ScoreHistory> +cda_rail::sim::RoutingSolver::random_local_search( + std::chrono::seconds max_search_time, LocalParams params) { + double best_score = DBL_MAX; + std::optional best_result; + ScoreHistory hist; + + std::chrono::steady_clock::time_point initial_time = + std::chrono::steady_clock::now(); + + for (;;) { + auto res = local_search(RoutingSolutionSet{instance, rng_engine}, params); + ScoreSet round_score_set = std::get<0>(res).get_score_set(); + double round_score = round_score_set.get_score(); + + std::chrono::steady_clock::time_point round_time = + std::chrono::steady_clock::now(); + + if (round_score < best_score) { + best_score = round_score; + best_result.reset(); + best_result.emplace(std::get<0>(res)); + hist.push_back({std::chrono::duration_cast( + round_time - initial_time), + round_score_set}); + } + + if (std::chrono::duration_cast( + round_time - initial_time) > max_search_time) + break; + } + + return {best_result, hist}; +} + +std::tuple, + cda_rail::sim::ScoreHistory> +cda_rail::sim::RoutingSolver::grasp_search(std::chrono::seconds max_search_time, + GreedyParams gre_params, + LocalParams loc_params) { + double best_score = DBL_MAX; + std::optional best_result; + ScoreHistory hist; + + std::chrono::steady_clock::time_point initial_time = + std::chrono::steady_clock::now(); + + for (;;) { + std::optional greedy_sol_opt = + greedy_solution(gre_params); + if (!greedy_sol_opt.has_value()) + continue; + + auto res = + local_search(RoutingSolutionSet{instance, rng_engine}, loc_params); + ScoreSet round_score_set = std::get<0>(res).get_score_set(); + double round_score = round_score_set.get_score(); + + std::chrono::steady_clock::time_point round_time = + std::chrono::steady_clock::now(); + + if (round_score < best_score) { + best_score = round_score; + best_result.reset(); + best_result.emplace(std::get<0>(res)); + hist.push_back({std::chrono::duration_cast( + round_time - initial_time), + round_score_set}); + } + + if (std::chrono::duration_cast( + round_time - initial_time) > max_search_time) + break; + } + + return {best_result, hist}; +} + +std::tuple, + cda_rail::sim::ScoreHistory> +cda_rail::sim::RoutingSolver::random_search( + std::optional max_search_time, + std::optional max_stall_time) { + if (!max_search_time.has_value() && !max_stall_time.has_value()) + throw std::invalid_argument( + "Need at least one abort criterium for search."); + + double best_score = DBL_MAX; + std::optional best_result; + ScoreHistory hist; + + std::chrono::steady_clock::time_point initial_time = + std::chrono::steady_clock::now(); + + std::chrono::steady_clock::time_point last_time = initial_time; + for (;;) { + SolverResult round_result{instance, + RoutingSolutionSet{instance, rng_engine}}; + ScoreSet round_score_set = round_result.get_score_set(); + double round_score = round_score_set.get_score(); + + std::chrono::steady_clock::time_point round_time = + std::chrono::steady_clock::now(); + auto interval = std::chrono::duration_cast( + round_time - last_time); + auto total_time = std::chrono::duration_cast( + round_time - initial_time); + + if (round_score < best_score) { + last_time = round_time; + best_score = round_score; + best_result.reset(); + best_result.emplace(round_result); + hist.push_back({std::chrono::duration_cast( + round_time - initial_time), + round_score_set}); + } + + if (max_stall_time.has_value()) { + if (interval > max_stall_time.value()) + break; + } + + if (max_search_time.has_value()) { + if (total_time > max_search_time.value()) + break; + } + } + + return {best_result, hist}; +} + +std::tuple, + cda_rail::sim::ScoreHistory> +cda_rail::sim::RoutingSolver::greedy_search( + std::optional max_search_time, + std::optional max_stall_time, GreedyParams params) { + if (!max_search_time.has_value() && !max_stall_time.has_value()) + throw std::invalid_argument( + "Need at least one abort criterium for search."); + + double best_score = DBL_MAX; + std::optional best_result; + ScoreHistory hist; + + std::chrono::steady_clock::time_point initial_time = + std::chrono::steady_clock::now(); + std::chrono::steady_clock::time_point last_time = initial_time; + + for (;;) { + std::optional round_result_opt = + greedy_solution(params); + + if (!round_result_opt.has_value()) + continue; + + ScoreSet round_score_set = round_result_opt.value().get_score_set(); + double round_score = round_score_set.get_score(); + + std::chrono::steady_clock::time_point round_time = + std::chrono::steady_clock::now(); + auto interval = std::chrono::duration_cast( + round_time - last_time); + auto total_time = std::chrono::duration_cast( + round_time - initial_time); + + if (round_score < best_score) { + last_time = round_time; + best_score = round_score; + best_result.reset(); + best_result.emplace(round_result_opt.value()); + auto total_time_spent = + std::chrono::duration_cast(round_time - + initial_time); + hist.push_back({total_time_spent, round_score_set}); + } + + if (max_stall_time.has_value()) { + if (interval > max_stall_time.value()) + break; + } + + if (max_search_time.has_value()) { + if (total_time > max_search_time.value()) + break; + } + + if (!round_result_opt) + continue; + } + + return {best_result, hist}; +} + +std::tuple +cda_rail::sim::RoutingSolver::local_search( + cda_rail::sim::RoutingSolutionSet starting_solution, LocalParams params) { + /** + * Luus–Jaakola Local Search + */ + std::chrono::steady_clock::time_point initial_time = + std::chrono::steady_clock::now(); + + ScoreHistory hist; + double sampling_range_fraction = params.start_sampling_range_fraction; + SolverResult last_result{instance, starting_solution}; + ScoreSet last_score_set = last_result.get_score_set(); + double last_score = last_score_set.get_score(); + hist.push_back({std::chrono::duration_cast( + std::chrono::steady_clock::now() - initial_time), + last_score_set}); + + while (sampling_range_fraction > params.abort_sampling_range_fraction) { + RoutingSolutionSet new_sol = last_result.get_solutions(); + new_sol.perturb(instance, sampling_range_fraction, rng_engine); + SolverResult new_result{instance, new_sol}; + ScoreSet new_score_set = new_result.get_score_set(); + + if (double new_score = new_score_set.get_score(); new_score < last_score) { + last_result = new_result; + last_score = new_score; + hist.push_back({std::chrono::duration_cast( + std::chrono::steady_clock::now() - initial_time), + new_score_set}); + } else { + sampling_range_fraction = + sampling_range_fraction * params.contraction_coeff; + } + } + + return {last_result, hist}; +} + +std::tuple +cda_rail::sim::RoutingSolver::local_search( + RoutingSolutionSet starting_solution, LocalParams params, + const std::function& rng01) { + std::chrono::steady_clock::time_point initial_time = + std::chrono::steady_clock::now(); + + ScoreHistory hist; + double sampling_range_fraction = params.start_sampling_range_fraction; + SolverResult last_result{instance, starting_solution}; + ScoreSet last_score_set = last_result.get_score_set(); + double last_score = last_score_set.get_score(); + hist.push_back({std::chrono::duration_cast( + std::chrono::steady_clock::now() - initial_time), + last_score_set}); + + while (sampling_range_fraction > params.abort_sampling_range_fraction) { + RoutingSolutionSet new_sol = last_result.get_solutions(); + new_sol.perturb(instance, sampling_range_fraction, rng01); + SolverResult new_result{instance, new_sol}; + ScoreSet new_score_set = new_result.get_score_set(); + + if (double new_score = new_score_set.get_score(); new_score < last_score) { + last_result = new_result; + last_score = new_score; + hist.push_back({std::chrono::duration_cast( + std::chrono::steady_clock::now() - initial_time), + new_score_set}); + } else { + sampling_range_fraction = + sampling_range_fraction * params.contraction_coeff; + } + } + + return {last_result, hist}; +} + +std::optional +cda_rail::sim::RoutingSolver::greedy_solution(GreedyParams params) { + std::chrono::steady_clock::time_point last_time = + std::chrono::steady_clock::now(); + + SolverResult result{instance}; + + TrainList train_list = instance.timetable.get_train_list(); + double score = 0; + std::vector train_idxs(train_list.size()); + std::iota(std::begin(train_idxs), std::end(train_idxs), 0); + std::shuffle(train_idxs.begin(), train_idxs.end(), rng_engine); + + for (auto train_idx = train_idxs.begin(); train_idx != train_idxs.end(); + train_idx++) { + const auto train = train_list.get_train(*train_idx); + std::chrono::steady_clock::time_point last_time = + std::chrono::steady_clock::now(); + + double best_score = DBL_MAX; + RoutingSolution best_sol{instance}; + TrainTrajectory best_traj{instance, train, best_sol}; + + for (;;) { + RoutingSolution round_sol{instance, train, rng_engine}; + TrainTrajectory round_traj{instance, train, round_sol}; + result.insert_or_assign(round_sol, round_traj); + ScoreSet round_score_set = result.get_score_set(); + double round_score = round_score_set.get_score(); + + std::chrono::steady_clock::time_point round_time = + std::chrono::steady_clock::now(); + auto interval = std::chrono::duration_cast( + round_time - last_time); + + if (round_score < best_score) { + last_time = round_time; + best_score = round_score; + best_sol = round_sol; + best_traj = round_traj; + } + + if (interval > params.per_train_stall_time) { + if (!result.get_trajectories().get_traj(train.name)) + return {}; + + result.insert_or_assign(best_sol, best_traj); + break; + } + } + } + + return result; +}; + +std::tuple, + cda_rail::sim::ScoreHistory> +cda_rail::sim::RoutingSolver::genetic_search(GeneticParams params, + bool local_improv) { + /** + * Genetic algorithm for entire solution sets + */ + ScoreHistory hist; + + GA_Type ga_obj; + ga_obj.problem_mode = EA::GA_MODE::SOGA; + ga_obj.multi_threading = params.is_multithread; + ga_obj.idle_delay_us = 0; // switch between threads quickly + ga_obj.verbose = false; + ga_obj.population = params.population; + ga_obj.generation_max = params.gen_max; + ga_obj.calculate_SO_total_fitness = std::bind( + &RoutingSolver::calculate_SO_total_fitness, this, std::placeholders::_1); + ga_obj.init_genes = std::bind(&RoutingSolver::init_genes, this, + std::placeholders::_1, std::placeholders::_2); + ga_obj.eval_solution = + std::bind(&RoutingSolver::eval_solution, this, std::placeholders::_1, + std::placeholders::_2); + ga_obj.mutate = std::bind(&RoutingSolver::mutate, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + + if (local_improv) { + ga_obj.crossover = std::bind(&RoutingSolver::crossover_local_improv, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3); + } else { + ga_obj.crossover = + std::bind(&RoutingSolver::crossover, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3); + } + + std::chrono::steady_clock::time_point starting_time = + std::chrono::steady_clock::now(); + ga_obj.SO_report_generation = std::bind( + &RoutingSolver::SO_report_generation, this, starting_time, std::ref(hist), + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + + ga_obj.best_stall_max = params.stall_max; + ga_obj.elite_count = params.n_elite; + ga_obj.crossover_fraction = params.xover_frac; + ga_obj.mutation_rate = params.mut_rate; + ga_obj.solve(); + + const RoutingSolutionSet& best_solution = + ga_obj.last_generation.chromosomes + .at(ga_obj.last_generation.best_chromosome_index) + .genes; + + return {SolverResult{instance, best_solution}, hist}; +} + +void cda_rail::sim::RoutingSolver::init_genes( + RoutingSolutionSet& p, const std::function& rnd01) { + p = RoutingSolutionSet{instance, rnd01}; +} + +bool cda_rail::sim::RoutingSolver::eval_solution(const RoutingSolutionSet& p, + MiddleCost& c) { + SolverResult round_result{instance, p}; + c.score = round_result.get_score_set().get_score(); + return true; +} + +cda_rail::sim::RoutingSolutionSet +cda_rail::sim::RoutingSolver::mutate(const RoutingSolutionSet& X_base, + const std::function& rnd01, + double shrink_scale) { + RoutingSolutionSet X_new(X_base); + X_new.perturb(instance, shrink_scale, rnd01); + return X_new; +} + +cda_rail::sim::RoutingSolutionSet cda_rail::sim::RoutingSolver::crossover( + const RoutingSolutionSet& X1, const RoutingSolutionSet& X2, + const std::function& rnd01) { + RoutingSolutionSet X_new; + + // Randomly select individual train solutions + for (auto sol_it = X1.solutions.begin(); sol_it != X1.solutions.end(); + sol_it++) { + if (rnd01() > 0.5) { + X_new.solutions.insert_or_assign((*sol_it).first, (*sol_it).second); + } else { + X_new.solutions.insert_or_assign((*sol_it).first, + X2.solutions.at((*sol_it).first)); + } + } + + return X_new; +} + +cda_rail::sim::RoutingSolutionSet +cda_rail::sim::RoutingSolver::crossover_local_improv( + const RoutingSolutionSet& X1, const RoutingSolutionSet& X2, + const std::function& rnd01) { + auto X_new = crossover(X1, X2, rnd01); + // TODO: pass localsearch parameters without modifying openGA.hpp + auto res = local_search(X_new, {0.05, 0.01, 0.9}, rnd01); + + return std::get<0>(res).get_solutions(); +} + +double cda_rail::sim::RoutingSolver::calculate_SO_total_fitness( + const GA_Type::thisChromosomeType& X) { + return X.middle_costs.score; +} + +void cda_rail::sim::RoutingSolver::SO_report_generation( + const std::chrono::steady_clock::time_point starting_time, + ScoreHistory& hist, int generation_number, + const EA::GenerationType& last_generation, + const RoutingSolutionSet& best_genes) { + std::cout << "Generation [" << generation_number << "], " + << "Best=" << last_generation.best_total_cost << ", " + << "Average=" << last_generation.average_cost << ", " + << "Exe_time=" << last_generation.exe_time << std::endl; + + std::chrono::steady_clock::time_point round_time = + std::chrono::steady_clock::now(); + std::chrono::milliseconds past_time = + std::chrono::duration_cast(round_time - + starting_time); + auto best_individual = + last_generation.chromosomes[last_generation.best_chromosome_index]; + SolverResult best_result{instance, best_individual.genes}; + hist.push_back({past_time, best_result.get_score_set()}); +} diff --git a/src/simulation/SimulationInstance.cpp b/src/simulation/SimulationInstance.cpp new file mode 100644 index 000000000..ff4befb8b --- /dev/null +++ b/src/simulation/SimulationInstance.cpp @@ -0,0 +1,42 @@ +#include "simulation/SimulationInstance.hpp" + +cda_rail::sim::SimulationInstance::SimulationInstance(Network network, + Timetable timetable, + bool allow_reversing) + : network(network), timetable(timetable), + shortest_paths(network.all_vertex_pairs_shortest_paths_undirected()), + n_timesteps(get_last_train_departure()), + n_switch_vars(std::ceil((get_max_train_speed() * n_timesteps) / + get_shortest_track())), + allow_reversing(allow_reversing) {} + +double cda_rail::sim::SimulationInstance::get_max_train_speed() const { + double max_speed = 0; + for (const auto& train : timetable.get_train_list()) { + if (train.max_speed > max_speed) { + max_speed = train.max_speed; + } + } + return max_speed; +} + +double cda_rail::sim::SimulationInstance::get_shortest_track() const { + double shortest_edge_length = DBL_MAX; + for (const auto& edge : network.get_edges()) { + if (edge.length < shortest_edge_length) { + shortest_edge_length = edge.length; + } + } + return shortest_edge_length; +} + +size_t cda_rail::sim::SimulationInstance::get_last_train_departure() const { + size_t last_departure = 1; + for (auto train : timetable.get_train_list()) { + // TODO: narrowing cast should be avoided + size_t departure = timetable.get_schedule(train.name).get_t_n(); + if (departure > last_departure) + last_departure = departure; + } + return last_departure; +} diff --git a/src/simulation/SolverResult.cpp b/src/simulation/SolverResult.cpp new file mode 100644 index 000000000..49159dbce --- /dev/null +++ b/src/simulation/SolverResult.cpp @@ -0,0 +1,87 @@ + +#include "simulation/SolverResult.hpp" + +cda_rail::sim::SolverResult::SolverResult(const SimulationInstance& instance) + : solutions(RoutingSolutionSet{}), + trajectories(TrainTrajectorySet(instance)) {} + +cda_rail::sim::SolverResult::SolverResult(const SimulationInstance& instance, + const RoutingSolutionSet& solutions) + : solutions(solutions), + trajectories(TrainTrajectorySet(instance, solutions)) { + if (solutions.solutions.size() != trajectories.size()) + throw std::invalid_argument( + "Solutions and Trajectories are not the same size"); + + for (auto const& [train_name, traj] : trajectories.get_map()) { + scores.stop_scores.insert_or_assign(train_name, stop_penalty(traj)); + } + + for (auto const& [train_name, traj] : trajectories.get_map()) { + scores.destination_scores.insert_or_assign(train_name, + destination_penalty(traj)); + } + + scores.collision_score = collision_penalty(trajectories); +} + +void cda_rail::sim::SolverResult::insert_or_assign( + const RoutingSolution& solution, const TrainTrajectory& trajectory) { + solutions.solutions.insert_or_assign(trajectory.get_train().name, solution); + trajectories.insert_or_assign(trajectory.get_train().name, trajectory); + + scores.stop_scores.insert_or_assign(trajectory.get_train().name, + stop_penalty(trajectory)); + scores.destination_scores.insert_or_assign(trajectory.get_train().name, + destination_penalty(trajectory)); + + // TODO: don't need to recompute all pairs for new train + scores.collision_score = collision_penalty(trajectories); +} + +const cda_rail::sim::RoutingSolutionSet& +cda_rail::sim::SolverResult::get_solutions() const { + return solutions; +} + +const cda_rail::sim::TrainTrajectorySet& +cda_rail::sim::SolverResult::get_trajectories() const { + return trajectories; +} + +double cda_rail::sim::ScoreSet::get_score() const { + double score_sum = 0; + + score_sum += get_collision_score(); + score_sum += get_norm_destination_score(); + score_sum += get_norm_stop_score(); + + return score_sum; +} + +double cda_rail::sim::ScoreSet::get_collision_score() const { + return collision_score; +} + +double cda_rail::sim::ScoreSet::get_norm_stop_score() const { + double score_sum = 0; + for (auto const& [train_name, score] : stop_scores) { + score_sum = score_sum + score; + } + + return score_sum / (double)stop_scores.size(); +} + +double cda_rail::sim::ScoreSet::get_norm_destination_score() const { + double score_sum = 0; + for (auto const& [train_name, score] : destination_scores) { + score_sum = score_sum + score; + } + + return score_sum / (double)stop_scores.size(); +} + +const cda_rail::sim::ScoreSet& +cda_rail::sim::SolverResult::get_score_set() const { + return scores; +} diff --git a/src/simulation/SpeedTargets.cpp b/src/simulation/SpeedTargets.cpp new file mode 100644 index 000000000..48777218c --- /dev/null +++ b/src/simulation/SpeedTargets.cpp @@ -0,0 +1,97 @@ +#include "simulation/SpeedTargets.hpp" + +#include +#include +#include + +cda_rail::sim::SpeedTargets::SpeedTargets(std::vector timesteps, + std::vector speeds) { + for (size_t i = 0; i < timesteps.size(); i++) { + targets.insert({timesteps.at(i), speeds.at(i)}); + }; +}; + +void cda_rail::sim::SpeedTargets::limit_speed_from(double maximum, + size_t timestep) { + // Cap targets in range + for (auto it = targets.lower_bound(timestep); it != targets.end(); it++) { + if (std::abs((*it).second) > maximum) { + (*it).second = std::copysign(maximum, (*it).second); + } + } + + // Ensure carried over targets are not too high + double previous_target = find_target_speed(timestep); + if (std::abs(previous_target) > maximum) + targets.insert_or_assign(timestep, std::copysign(maximum, previous_target)); +} + +void cda_rail::sim::SpeedTargets::insert(std::map add_targets) { + targets.insert(add_targets.begin(), add_targets.end()); +} + +void cda_rail::sim::SpeedTargets::delete_range(size_t start, size_t end) { + if (start > end) + throw std::invalid_argument("Invalid timestep range."); + + for (auto it = targets.lower_bound(start); it != targets.end();) { + if ((*it).first <= end) { + it = targets.erase(it); + } else { + break; + } + } +} + +std::optional +cda_rail::sim::SpeedTargets::find_next_reversal(size_t timestep) const { + bool previous_direction = !std::signbit(find_target_speed(timestep)); + for (auto it = targets.upper_bound(timestep); it != targets.end(); it++) { + if (!std::signbit((*it).second) != previous_direction) + return (*it).first; + } + return {}; +} + +void cda_rail::sim::SpeedTargets::set_range(size_t start, size_t end, + double value) { + if (start > end) + throw std::invalid_argument("Invalid timestep range."); + + delete_range(start, end); + targets.insert_or_assign(start, value); +} + +double cda_rail::sim::SpeedTargets::find_target_speed(size_t timestep) const { + std::map::const_iterator it = targets.upper_bound(timestep); + if (it != targets.begin()) { + --it; + } + + if (it == targets.end()) + throw std::out_of_range("Failed to find relevant speed target."); + + return it->second; +}; + +std::map +cda_rail::sim::SpeedTargets::copy_range(size_t start, size_t end) const { + if (start > end) + throw std::invalid_argument("Invalid timestep range."); + + std::map new_map; + for (auto it = targets.lower_bound(start); + (*it).first <= end && it != targets.end(); it++) { + new_map.insert({(*it).first, (*it).second}); + } + return new_map; +} + +bool cda_rail::sim::SpeedTargets::is_first_target(size_t timestep) const { + std::map::const_iterator it = targets.upper_bound(timestep); + if (it == targets.begin() || it-- == targets.begin()) + return true; + return false; +} + +size_t cda_rail::sim::SpeedTargets::size() const { return targets.size(); } diff --git a/src/simulation/TrainTrajectory.cpp b/src/simulation/TrainTrajectory.cpp new file mode 100644 index 000000000..49f2d9c1c --- /dev/null +++ b/src/simulation/TrainTrajectory.cpp @@ -0,0 +1,395 @@ +#include "simulation/TrainTrajectory.hpp" + +cda_rail::sim::TrainTrajectory::TrainTrajectory( + const SimulationInstance& instance, const Train& train, + RoutingSolution init_solution) + : instance_r(std::ref(instance)), train_r(std::ref(train)), + solution(init_solution), + remaining_planned_stops( + instance.timetable.get_schedule(train.name).get_stops()) { + for (size_t abort = 0;; abort++) { + TrainState initial_edge_state; + if (edge_trajs.size() == 0) { + initial_edge_state = read_initial_train_state(); + } else { + double switch_direction = + solution.switch_directions.at(edge_trajs.size()); + + const EdgeEntry entry = + edge_trajs.back().enter_next_edge(switch_direction); + + if (entry.outcome == TIME_END) { + return; + } else if (entry.outcome == OVERSPEED) { + double prev_speed_limit = + instance_r.get() + .network.get_edge(entry.new_state.value().edge) + .max_speed; + BrakingPeriod braking_period = add_braking(prev_speed_limit, {}, {}); + backtrack_trajectory(std::get<0>(braking_period)); + continue; + } else if (entry.outcome == DEADEND) { + std::optional reversal_time = + solution.v_targets.find_next_reversal( + edge_trajs.back().get_last_timestep()); + + BrakingPeriod braking_period = add_braking( + 0, reversal_time.value_or(instance_r.get().n_timesteps - 1), {}); + backtrack_trajectory(std::get<0>(braking_period)); + continue; + } else if (entry.outcome == NORMAL) { + // Check for scheduled stop + bool found_stop = false; + for (auto stop = remaining_planned_stops.begin(); + stop != remaining_planned_stops.end() && !found_stop; stop++) { + auto stop_station = + instance_r.get().timetable.get_station_list().get_station( + (*stop).get_station_name()); + + if (std::find(stop_station.tracks.begin(), stop_station.tracks.end(), + edge_trajs.back().get_edge()) != + stop_station.tracks.end()) { + BrakingPeriod braking_period = + add_braking(0, {}, (*stop).get_min_stopping_time()); + backtrack_trajectory(std::get<0>(braking_period)); + remaining_planned_stops.erase(stop); + found_stop = true; + } + } + + if (found_stop) { + continue; + } else { + initial_edge_state = entry.new_state.value(); + } + } else { + throw std::logic_error("Invalid EdgeEntry outcome."); + } + } + + SpeedTargets previous_targets(solution.v_targets); + + double curr_speed_limit = + instance_r.get().network.get_edge(initial_edge_state.edge).max_speed; + solution.v_targets.limit_speed_from(curr_speed_limit, + initial_edge_state.timestep); + + edge_trajs.push_back(EdgeTrajectory( + instance_r, train_r.get(), solution.v_targets, initial_edge_state)); + + // Restore original targets after leaving edge + // TODO: Braking can shift traversal forwards when changing direction + // so we sometimes constrain speeds unnecessarily + if (size_t last_step = edge_trajs.back().get_last_timestep(); + last_step < instance_r.get().n_timesteps - 1) { + solution.v_targets.delete_range(last_step + 1, + instance_r.get().n_timesteps - 1); + solution.v_targets.insert(previous_targets.copy_range( + last_step + 1, instance_r.get().n_timesteps - 1)); + } + + if (abort > 1000) + throw std::logic_error("Trajectory construction did not terminate."); + } +} + +void cda_rail::sim::TrainTrajectory::backtrack_trajectory(size_t timestep) { + size_t i = get_earliest_affected_trajectory(timestep); + + while (edge_trajs.size() > i) { + edge_trajs.pop_back(); + } +} + +cda_rail::sim::BrakingPeriod cda_rail::sim::TrainTrajectory::add_braking( + double abs_target_speed, std::optional hold_until, + std::optional hold_at_least) { + if (!edge_trajs.back().get_traversal().has_value()) + throw std::logic_error("No traversal to brake for."); + + EdgeTraversal traversal = edge_trajs.back().get_traversal().value(); + double target_speed = + std::copysign(abs_target_speed, traversal.crossing_orientation); + + std::optional braking_period = + find_latest_braking_period(target_speed); + + if (!braking_period.has_value()) + throw std::logic_error("No feasible braking period found."); + + size_t start_braking, end_braking; + std::tie(start_braking, end_braking) = braking_period.value(); + size_t end_hold = end_braking; + + if (hold_until.has_value() && hold_until.value() > end_hold) + end_hold = hold_until.value(); + if (hold_at_least.has_value() && + hold_at_least.value() + start_braking > end_hold) + end_hold = hold_at_least.value() + start_braking; + + if (solution.v_targets.is_first_target(start_braking)) + start_braking = 0; + + solution.v_targets.set_range(start_braking, end_hold, target_speed); + + return std::tuple(start_braking, end_hold); +} + +std::optional +cda_rail::sim::TrainTrajectory::find_latest_braking_period( + double target_speed) const { + size_t last_timestep = edge_trajs.back().get_last_timestep(); + size_t first_timestep = edge_trajs.front().get_first_timestep(); + + for (size_t start_braking = last_timestep; start_braking >= first_timestep; + start_braking--) { + std::optional end_braking = + is_feasible_braking_point(start_braking, target_speed); + + if (end_braking.has_value()) + return std::tuple(start_braking, end_braking.value()); + + if (start_braking == 0) + break; + } + return {}; +} + +std::optional cda_rail::sim::TrainTrajectory::is_feasible_braking_point( + size_t start_braking, double target_speed) const { + TrainState start_state = get_state(start_braking).value(); + double abs_diff_to_target_speed = std::abs(start_state.speed - target_speed); + if (start_braking > edge_trajs.back().get_last_timestep() || + start_braking < edge_trajs.front().get_first_timestep()) + throw std::out_of_range("Timestep out of range."); + + double starting_speed = start_state.speed; + double speed_diff = target_speed - starting_speed; + double speed_diff_abs = std::abs(speed_diff); + bool accel_direction = !std::signbit(speed_diff); + + double accel; + if (accel_direction) { + accel = train_r.get().acceleration; + } else { + accel = train_r.get().deceleration; + } + + size_t required_braking_time = std::ceil(speed_diff_abs / accel); + + // This is the definite integral under the braking curve + // As defined in EdgeTrajectory speed changes are done at maximum acceleration + // Zero-crossing and bidirectionality need to be considered + double braking_dist = + starting_speed * required_braking_time + + (0.5 * std::copysign((required_braking_time - 1) * + (required_braking_time * accel), + speed_diff)); + + size_t end_braking = start_braking + required_braking_time; + + if (end_braking <= edge_trajs.back().get_last_timestep()) { + return end_braking; + } else { + return {}; + } +} + +std::optional +cda_rail::sim::TrainTrajectory::get_state(size_t timestep) const { + if (timestep > edge_trajs.back().get_last_timestep() || + timestep < edge_trajs.front().get_first_timestep()) + return {}; + + const EdgeTrajectory& relevant_trajectory = + edge_trajs.at(find_traj_idx(timestep)); + size_t trajectory_idx = timestep - relevant_trajectory.get_first_timestep(); + + return TrainState{ + .timestep = timestep, + .edge = relevant_trajectory.get_edge(), + .position = relevant_trajectory.get_positions().at(trajectory_idx), + .orientation = relevant_trajectory.get_orientation(), + .speed = relevant_trajectory.get_speeds().at(trajectory_idx), + }; +} + +size_t cda_rail::sim::TrainTrajectory::find_traj_idx(size_t timestep) const { + for (auto it = edge_trajs.begin(); it != edge_trajs.end(); it++) { + if ((*it).get_first_timestep() <= timestep && + (*it).get_last_timestep() >= timestep) { + return (size_t)std::distance(edge_trajs.begin(), it); + } + } + throw std::out_of_range("Timestep not contained in any trajectory."); +} + +size_t cda_rail::sim::TrainTrajectory::get_remaining_stop_amount() const { + return remaining_planned_stops.size(); +} + +size_t cda_rail::sim::TrainTrajectory::get_earliest_affected_trajectory( + size_t timestep) const { + for (auto it = edge_trajs.begin(); it != edge_trajs.end(); it++) { + if ((*it).get_last_timestep() >= timestep) { + return (size_t)std::distance(edge_trajs.begin(), it); + } + } + throw std::out_of_range("No affected trajectory found."); +} + +size_t cda_rail::sim::TrainTrajectory::get_first_timestep() const { + return edge_trajs.front().get_first_timestep(); +} + +size_t cda_rail::sim::TrainTrajectory::get_last_timestep() const { + return edge_trajs.back().get_last_timestep(); +} + +const cda_rail::Train& cda_rail::sim::TrainTrajectory::get_train() const { + return train_r.get(); +} + +const cda_rail::sim::SimulationInstance& +cda_rail::sim::TrainTrajectory::get_instance() const { + return instance_r.get(); +} + +cda_rail::sim::TrainState +cda_rail::sim::TrainTrajectory::read_initial_train_state() const { + cda_rail::Schedule train_schedule = + instance_r.get().timetable.get_schedule(train_r.get().name); + + size_t entry_vertex = train_schedule.get_entry(); + size_t entry_edge; + if (!instance_r.get().network.out_edges(entry_vertex).empty()) { + entry_edge = instance_r.get().network.out_edges(entry_vertex).front(); + } else if (!instance_r.get().network.in_edges(entry_vertex).empty()) { + entry_edge = instance_r.get().network.in_edges(entry_vertex).front(); + } else { + throw std::logic_error("Entry vertex has no adjacent edges."); + } + + double position; + bool orientation; + if (instance_r.get().network.get_edge(entry_edge).target == entry_vertex) { + position = 1.0; + orientation = false; + } else { + position = 0.0; + orientation = true; + } + + return TrainState{.timestep = (size_t)train_schedule.get_t_0(), + .edge = entry_edge, + .position = position, + .orientation = orientation, + .speed = train_schedule.get_v_0()}; +} + +void cda_rail::sim::TrainTrajectory::check_speed_limits() const { + for (auto traj : edge_trajs) { + traj.check_speed_limits(); + } +} + +std::optional +cda_rail::sim::TrainTrajectory::train_vertex_distance(size_t vertex, + size_t timestep) const { + const std::optional state_opt = get_state(timestep); + + if (!state_opt.has_value()) + return {}; + + const TrainState state = state_opt.value(); + const Edge& edge = instance_r.get().network.get_edge(state.edge); + + const double remain_dist_fw = (1 - state.position) * edge.length; + const double remain_dist_bw = state.position * edge.length; + + if (edge.target == vertex) + return remain_dist_fw; + if (edge.source == vertex) + return remain_dist_bw; + + const double dist_fw = + remain_dist_fw + instance_r.get().shortest_paths[edge.target][vertex]; + const double dist_bw = + remain_dist_bw + instance_r.get().shortest_paths[edge.source][vertex]; + + const double min_dist = std::min(dist_fw, dist_bw); + + if (min_dist < 0) + throw std::logic_error("Distance calculation failed."); + + return min_dist; +} + +std::tuple>> +cda_rail::sim::TrainTrajectory::convert_to_vss_format( + const Network& network_bidirec) const { + if (instance_r.get().allow_reversing) + throw std::runtime_error( + "Cannot export to VSS compatible solution. " + "Converting solutions that allow reversing is not yet supported. " + "Position can't be easily represented as covered distance."); + + if (instance_r.get().get_max_train_speed() > + instance_r.get().get_shortest_track()) + throw std::runtime_error( + "Cannot export to VSS compatible solution. " + "Max train speed is higher than smallest track size. " + "Trains can skip tracks which is not representable as a 'Route'."); + + const Network& network_unidirec = instance_r.get().network; + + if (network_unidirec.number_of_vertices() != + network_bidirec.number_of_vertices()) + throw std::invalid_argument( + "Cannot export to VSS compatible solution. " + "Networks must be the same except in uni/bidirectional format."); + + Route route; + std::vector> route_pos; + double passed_edges_dist = 0; + + for (auto edge_traj : edge_trajs) { + Edge edge_unidirec = network_unidirec.get_edge(edge_traj.get_edge()); + bool orientation = edge_traj.get_orientation(); + + // Find corresponding edge in bidirectional network and add to route + if (orientation) { + if (!network_bidirec.has_edge(edge_unidirec.source, edge_unidirec.target)) + throw std::logic_error("Bidirectional network does not correspond to " + "unidirectional network."); + route.push_back_edge(network_bidirec.get_edge_index(edge_unidirec.source, + edge_unidirec.target), + network_bidirec); + } else { + if (!network_bidirec.has_edge(edge_unidirec.target, edge_unidirec.source)) + throw std::logic_error("Bidirectional network does not correspond to " + "unidirectional network."); + route.push_back_edge(network_bidirec.get_edge_index(edge_unidirec.target, + edge_unidirec.source), + network_bidirec); + } + + // Record distances from route origin + size_t time = edge_traj.get_first_timestep(); + for (double position : edge_traj.get_positions()) { + double edge_trav_dist; + if (orientation) { + edge_trav_dist = edge_unidirec.length * position; + } else { + edge_trav_dist = edge_unidirec.length * (1 - position); + } + + route_pos.push_back({time, passed_edges_dist + edge_trav_dist}); + time++; + } + + passed_edges_dist = passed_edges_dist + edge_unidirec.length; + } + + return std::make_tuple(route, route_pos); +} diff --git a/src/simulation/TrainTrajectorySet.cpp b/src/simulation/TrainTrajectorySet.cpp new file mode 100644 index 000000000..66445ca1a --- /dev/null +++ b/src/simulation/TrainTrajectorySet.cpp @@ -0,0 +1,204 @@ +#include "simulation/TrainTrajectorySet.hpp" + +cda_rail::sim::TrainTrajectorySet::TrainTrajectorySet( + const SimulationInstance& instance) + : instance(instance) {} + +cda_rail::sim::TrainTrajectorySet::TrainTrajectorySet( + const SimulationInstance& instance, const RoutingSolutionSet& solution_set) + : instance(instance) { + const TrainList& trainlist = instance.timetable.get_train_list(); + for (auto solution : solution_set.solutions) { + TrainTrajectory traj{instance, trainlist.get_train(solution.first), + solution.second}; + trajectories.insert({solution.first, traj}); + } +} + +void cda_rail::sim::TrainTrajectorySet::insert_or_assign( + std::string name, cda_rail::sim::TrainTrajectory traj) { + const TrainList& trainlist = instance.get().timetable.get_train_list(); + if (!trainlist.has_train(name)) + throw std::invalid_argument("No train with such name in timetable."); + + trajectories.insert_or_assign(name, traj); +} + +std::optional cda_rail::sim::TrainTrajectorySet::train_distance( + std::string train1, std::string train2, size_t timestep) const { + if (!contains(train1) || !contains(train2)) { + return {}; + } + + const std::optional state_opt1 = + trajectories.at(train1).get_state(timestep); + const std::optional state_opt2 = + trajectories.at(train2).get_state(timestep); + + if (!state_opt1.has_value() || !state_opt2.has_value()) { + return {}; + } + + const TrainState state1 = state_opt1.value(); + const TrainState state2 = state_opt2.value(); + const Edge& edge1 = instance.get().network.get_edge(state1.edge); + const Edge& edge2 = instance.get().network.get_edge(state2.edge); + + if (state1.edge == state2.edge) + return std::abs(state1.position - state2.position) * edge1.length; + + const double remain_dist_1_fw = (1 - state1.position) * edge1.length; + const double remain_dist_1_bw = state1.position * edge1.length; + const double remain_dist_2_fw = (1 - state2.position) * edge2.length; + const double remain_dist_2_bw = state2.position * edge2.length; + + const double dist_fw_fw = + remain_dist_1_fw + remain_dist_2_fw + + instance.get().shortest_paths[edge1.target][edge2.target]; + const double dist_fw_bw = + remain_dist_1_fw + remain_dist_2_bw + + instance.get().shortest_paths[edge1.target][edge2.source]; + const double dist_bw_bw = + remain_dist_1_bw + remain_dist_2_bw + + instance.get().shortest_paths[edge1.source][edge2.source]; + const double dist_bw_fw = + remain_dist_1_bw + remain_dist_2_fw + + instance.get().shortest_paths[edge1.source][edge2.target]; + + const double min_dist = std::min( + dist_fw_fw, std::min(dist_fw_bw, std::min(dist_bw_bw, dist_bw_fw))); + + if (min_dist < 0) + throw std::logic_error("Distance calculation failed."); + + return min_dist; +} + +std::optional cda_rail::sim::TrainTrajectorySet::train_vertex_distance( + std::string train, size_t vertex, size_t timestep) const { + if (!contains(train)) + return {}; + + const std::optional state_opt = + trajectories.at(train).get_state(timestep); + + if (!state_opt.has_value()) + return {}; + + const TrainState state = state_opt.value(); + const Edge& edge = instance.get().network.get_edge(state.edge); + + const double remain_dist_fw = (1 - state.position) * edge.length; + const double remain_dist_bw = state.position * edge.length; + + if (edge.target == vertex) + return remain_dist_fw; + if (edge.source == vertex) + return remain_dist_bw; + + const double dist_fw = + remain_dist_fw + instance.get().shortest_paths[edge.target][vertex]; + const double dist_bw = + remain_dist_bw + instance.get().shortest_paths[edge.source][vertex]; + + const double min_dist = std::min(dist_fw, dist_bw); + + if (min_dist < 0) + throw std::logic_error("Distance calculation failed."); + + return min_dist; +} + +void cda_rail::sim::TrainTrajectorySet::export_csv( + const std::filesystem::path& p) const { + std::ofstream csvfile(p); + csvfile << "train_idx,train_name,timestep,edge_idx,edge_src_node,edge_dst_" + "node,edge_pos,speed\n"; + + const TrainList& train_list = instance.get().timetable.get_train_list(); + + for (auto traj : trajectories) { + for (size_t timestep = traj.second.get_first_timestep(); + timestep <= traj.second.get_last_timestep(); timestep++) { + TrainState state = traj.second.get_state(timestep).value(); + const Edge& edge = instance.get().network.get_edge(state.edge); + + csvfile << train_list.get_train_index(traj.first) << ","; + csvfile << traj.first << ","; + csvfile << timestep << ","; + csvfile << state.edge << ","; + csvfile << instance.get().network.get_vertex(edge.source).name << ","; + csvfile << instance.get().network.get_vertex(edge.target).name << ","; + csvfile << state.position << ","; + csvfile << state.speed << "\n"; + } + } + + csvfile.close(); +} + +void cda_rail::sim::TrainTrajectorySet::check_speed_limits() const { + for (auto traj : trajectories) { + traj.second.check_speed_limits(); + } +} + +const std::unordered_map& +cda_rail::sim::TrainTrajectorySet::get_map() const { + return trajectories; +} + +std::optional +cda_rail::sim::TrainTrajectorySet::get_traj(std::string train_name) const { + if (contains(train_name)) { + return trajectories.at(train_name); + } else { + return {}; + } +} + +const cda_rail::sim::SimulationInstance& +cda_rail::sim::TrainTrajectorySet::get_instance() const { + return instance; +} + +size_t cda_rail::sim::TrainTrajectorySet::size() const { + return trajectories.size(); +} + +bool cda_rail::sim::TrainTrajectorySet::contains(std::string train_name) const { + return trajectories.count(train_name); +} + +cda_rail::instances::SolGeneralPerformanceOptimizationInstance< + cda_rail::instances::GeneralPerformanceOptimizationInstance> +cda_rail::sim::TrainTrajectorySet::to_vss_solution( + const cda_rail::Network& network_bidirec) const { + cda_rail::instances::GeneralPerformanceOptimizationInstance general_instance{ + network_bidirec, instance.get().timetable.parse_to_general_timetable(), + RouteMap{}}; + cda_rail::instances::SolGeneralPerformanceOptimizationInstance< + cda_rail::instances::GeneralPerformanceOptimizationInstance> + general_sol_instance{general_instance}; + + for (auto [tr_name, traj] : trajectories) { + Route route; + std::vector> route_positions; + std::tie(route, route_positions) = + traj.convert_to_vss_format(network_bidirec); + + for (std::pair route_position : route_positions) { + general_sol_instance.add_train_pos(tr_name, route_position.first, + route_position.second); + general_sol_instance.add_train_speed( + tr_name, route_position.first, + traj.get_state(route_position.first).value().speed); + } + + general_sol_instance.set_route(tr_name, route); + general_sol_instance.set_train_routed(tr_name); + } + + general_sol_instance.check_consistency(); + return general_sol_instance; +} diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 000000000..a0cbf8aa1 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1 @@ +*tmp* diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a5112c649..7fc3de517 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,4 +20,5 @@ package_add_test( ${CMAKE_CURRENT_SOURCE_DIR}/test_abstract_data_structure.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_general_performance_optimization_instances.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_gen_po_movingblock_mip_solver.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/test_gurobi_vss_gen_using_mb_information.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/test_gurobi_vss_gen_using_mb_information.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_simulation.cpp) diff --git a/test/example-networks-sim-bidirec/Overtake/network/successors_cpp.json b/test/example-networks-sim-bidirec/Overtake/network/successors_cpp.json new file mode 100644 index 000000000..a43840166 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake/network/successors_cpp.json @@ -0,0 +1,98 @@ +{ + "('switch_center', 'v6')": [["v6", "v7"]], + "('v1', 'v2')": [["v2", "v3"]], + "('v10', 'v11')": [], + "('v2', 'v3')": [ + ["v3", "v4"], + ["v3", "v4b"] + ], + "('v3', 'v4')": [["v4", "v5"]], + "('v3', 'v4b')": [["v4b", "v5b"]], + "('v4', 'v5')": [["v5", "v6"]], + "('v4b', 'v5b')": [["v5b", "v6b"]], + "('v5', 'v6')": [["v6", "v7"]], + "('v5b', 'v6b')": [ + ["v6b", "v7b"], + ["v6b", "switch_center"] + ], + "('v6', 'v7')": [["v7", "v8"]], + "('v6b', 'switch_center')": [["switch_center", "v6"]], + "('v6b', 'v7b')": [["v7b", "v8b"]], + "('v7', 'v8')": [["v8", "v9"]], + "('v7b', 'v8b')": [["v8b", "v9"]], + "('v8', 'v9')": [["v9", "v10"]], + "('v8b', 'v9')": [["v9", "v10"]], + "('v9', 'v10')": [["v10", "v11"]], + "('v3', 'v2')": [ + ["v2", "v1"], + ["v2", "v1"] + ], + "('v4', 'v3')": [ + ["v3", "v2"], + ["v3", "v2"] + ], + "('v4b', 'v3')": [ + ["v3", "v2"], + ["v3", "v2"] + ], + "('v5', 'v4')": [ + ["v4", "v3"], + ["v4", "v3"] + ], + "('v5b', 'v4b')": [ + ["v4b", "v3"], + ["v4b", "v3"] + ], + "('v6', 'v5')": [ + ["v5", "v4"], + ["v5", "v4"] + ], + "('v7', 'v6')": [ + ["v6", "v5"], + ["v6", "switch_center"], + ["v6", "v5"], + ["v6", "switch_center"] + ], + "('v8', 'v7')": [ + ["v7", "v6"], + ["v7", "v6"] + ], + "('v9', 'v8')": [ + ["v8", "v7"], + ["v8", "v7"] + ], + "('v10', 'v9')": [ + ["v9", "v8"], + ["v9", "v8"], + ["v9", "v8b"], + ["v9", "v8b"] + ], + "('v11', 'v10')": [ + ["v10", "v9"], + ["v10", "v9"] + ], + "('v6b', 'v5b')": [ + ["v5b", "v4b"], + ["v5b", "v4b"] + ], + "('v7b', 'v6b')": [ + ["v6b", "v5b"], + ["v6b", "v5b"] + ], + "('switch_center', 'v6b')": [ + ["v6b", "v5b"], + ["v6b", "v5b"] + ], + "('v8b', 'v7b')": [ + ["v7b", "v6b"], + ["v7b", "v6b"] + ], + "('v6', 'switch_center')": [ + ["switch_center", "v6b"], + ["switch_center", "v6b"] + ], + "('v9', 'v8b')": [ + ["v8b", "v7b"], + ["v8b", "v7b"] + ] +} diff --git a/test/example-networks-sim-bidirec/Overtake/network/tracks.graphml b/test/example-networks-sim-bidirec/Overtake/network/tracks.graphml new file mode 100644 index 000000000..08bbef86b --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake/network/tracks.graphml @@ -0,0 +1,277 @@ + + + + + + + + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + True + 4475.0 + 80.0 + 100.0 + + + False + 25.0 + 80.0 + 1.0 + + + 100.0 + 80.0 + 4475.0 + True + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + 1.0 + 80.0 + 25.0 + False + + + True + 675.0 + 80.0 + 100.0 + + + 1.0 + 80.0 + 25.0 + False + + + False + 25.0 + 80.0 + 1.0 + + + 100.0 + 80.0 + 675.0 + True + + + False + 25.0 + 80.0 + 1.0 + + + 1.0 + 80.0 + 25.0 + False + + + 1.0 + 25.0 + 13.0 + False + + + True + 225.0 + 80.0 + 100.0 + + + 1.0 + 80.0 + 25.0 + False + + + False + 25.0 + 80.0 + 1.0 + + + 100.0 + 80.0 + 225.0 + True + + + False + 25.0 + 80.0 + 1.0 + + + 1.0 + 80.0 + 25.0 + False + + + 1.0 + 25.0 + 25.0 + False + + + True + 4475.0 + 80.0 + 100.0 + + + 1.0 + 80.0 + 25.0 + False + + + 100.0 + 80.0 + 4475.0 + True + + + True + 650.0 + 25.0 + 100.0 + + + 1.0 + 25.0 + 25.0 + False + + + False + 25.0 + 25.0 + 1.0 + + + 100.0 + 25.0 + 650.0 + True + + + False + 25.0 + 25.0 + 1.0 + + + False + 13.0 + 25.0 + 1.0 + + + 1.0 + 25.0 + 25.0 + False + + + True + 250.0 + 25.0 + 100.0 + + + 1.0 + 25.0 + 25.0 + False + + + False + 25.0 + 25.0 + 1.0 + + + 100.0 + 25.0 + 250.0 + True + + + False + 13.0 + 25.0 + 1.0 + + + 1.0 + 25.0 + 13.0 + False + + + diff --git a/test/example-networks-sim-bidirec/Overtake/routes/routes.json b/test/example-networks-sim-bidirec/Overtake/routes/routes.json new file mode 100644 index 000000000..01fd1d9b0 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake/routes/routes.json @@ -0,0 +1,40 @@ +{ + "tr1": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4b"], + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "v7b"], + ["v7b", "v8b"], + ["v8b", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ], + "tr2": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4b"], + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "switch_center"], + ["switch_center", "v6"], + ["v6", "v7"], + ["v7", "v8"], + ["v8", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ], + "tr3": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4"], + ["v4", "v5"], + ["v5", "v6"], + ["v6", "v7"], + ["v7", "v8"], + ["v8", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ] +} diff --git a/test/example-networks-sim-bidirec/Overtake/timetable/schedules.json b/test/example-networks-sim-bidirec/Overtake/timetable/schedules.json new file mode 100644 index 000000000..2b4c9ea43 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake/timetable/schedules.json @@ -0,0 +1,29 @@ +{ + "tr1": { + "entry": "v1", + "exit": "v11", + "stops": [{ "begin": 270, "end": 330, "station": "Station" }], + "t_0": 0, + "t_n": 585, + "v_0": 25.0, + "v_n": 25.0 + }, + "tr2": { + "entry": "v1", + "exit": "v11", + "stops": null, + "t_0": 30, + "t_n": 465, + "v_0": 25.0, + "v_n": 25.0 + }, + "tr3": { + "entry": "v1", + "exit": "v11", + "stops": null, + "t_0": 165, + "t_n": 315, + "v_0": 25.0, + "v_n": 80.0 + } +} diff --git a/test/example-networks-sim-bidirec/Overtake/timetable/stations.json b/test/example-networks-sim-bidirec/Overtake/timetable/stations.json new file mode 100644 index 000000000..929feff08 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake/timetable/stations.json @@ -0,0 +1,8 @@ +{ + "Station": [ + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "v7b"], + ["v7b", "v8b"] + ] +} diff --git a/test/example-networks-sim-bidirec/Overtake/timetable/trains.json b/test/example-networks-sim-bidirec/Overtake/timetable/trains.json new file mode 100644 index 000000000..68aeec9d4 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake/timetable/trains.json @@ -0,0 +1,23 @@ +{ + "tr1": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 200, + "max_speed": 25.0, + "tim": true + }, + "tr2": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 150, + "max_speed": 25.0, + "tim": true + }, + "tr3": { + "acceleration": 2.0, + "deceleration": 2.0, + "length": 300, + "max_speed": 80.0, + "tim": true + } +} diff --git a/test/example-networks-sim-bidirec/Overtake_slow/network/successors_cpp.json b/test/example-networks-sim-bidirec/Overtake_slow/network/successors_cpp.json new file mode 100644 index 000000000..a43840166 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake_slow/network/successors_cpp.json @@ -0,0 +1,98 @@ +{ + "('switch_center', 'v6')": [["v6", "v7"]], + "('v1', 'v2')": [["v2", "v3"]], + "('v10', 'v11')": [], + "('v2', 'v3')": [ + ["v3", "v4"], + ["v3", "v4b"] + ], + "('v3', 'v4')": [["v4", "v5"]], + "('v3', 'v4b')": [["v4b", "v5b"]], + "('v4', 'v5')": [["v5", "v6"]], + "('v4b', 'v5b')": [["v5b", "v6b"]], + "('v5', 'v6')": [["v6", "v7"]], + "('v5b', 'v6b')": [ + ["v6b", "v7b"], + ["v6b", "switch_center"] + ], + "('v6', 'v7')": [["v7", "v8"]], + "('v6b', 'switch_center')": [["switch_center", "v6"]], + "('v6b', 'v7b')": [["v7b", "v8b"]], + "('v7', 'v8')": [["v8", "v9"]], + "('v7b', 'v8b')": [["v8b", "v9"]], + "('v8', 'v9')": [["v9", "v10"]], + "('v8b', 'v9')": [["v9", "v10"]], + "('v9', 'v10')": [["v10", "v11"]], + "('v3', 'v2')": [ + ["v2", "v1"], + ["v2", "v1"] + ], + "('v4', 'v3')": [ + ["v3", "v2"], + ["v3", "v2"] + ], + "('v4b', 'v3')": [ + ["v3", "v2"], + ["v3", "v2"] + ], + "('v5', 'v4')": [ + ["v4", "v3"], + ["v4", "v3"] + ], + "('v5b', 'v4b')": [ + ["v4b", "v3"], + ["v4b", "v3"] + ], + "('v6', 'v5')": [ + ["v5", "v4"], + ["v5", "v4"] + ], + "('v7', 'v6')": [ + ["v6", "v5"], + ["v6", "switch_center"], + ["v6", "v5"], + ["v6", "switch_center"] + ], + "('v8', 'v7')": [ + ["v7", "v6"], + ["v7", "v6"] + ], + "('v9', 'v8')": [ + ["v8", "v7"], + ["v8", "v7"] + ], + "('v10', 'v9')": [ + ["v9", "v8"], + ["v9", "v8"], + ["v9", "v8b"], + ["v9", "v8b"] + ], + "('v11', 'v10')": [ + ["v10", "v9"], + ["v10", "v9"] + ], + "('v6b', 'v5b')": [ + ["v5b", "v4b"], + ["v5b", "v4b"] + ], + "('v7b', 'v6b')": [ + ["v6b", "v5b"], + ["v6b", "v5b"] + ], + "('switch_center', 'v6b')": [ + ["v6b", "v5b"], + ["v6b", "v5b"] + ], + "('v8b', 'v7b')": [ + ["v7b", "v6b"], + ["v7b", "v6b"] + ], + "('v6', 'switch_center')": [ + ["switch_center", "v6b"], + ["switch_center", "v6b"] + ], + "('v9', 'v8b')": [ + ["v8b", "v7b"], + ["v8b", "v7b"] + ] +} diff --git a/test/example-networks-sim-bidirec/Overtake_slow/network/tracks.graphml b/test/example-networks-sim-bidirec/Overtake_slow/network/tracks.graphml new file mode 100644 index 000000000..e222cc3d7 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake_slow/network/tracks.graphml @@ -0,0 +1,277 @@ + + + + + + + + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + True + 4475.0 + 80.0 + 100.0 + + + False + 25.0 + 80.0 + 1.0 + + + 100.0 + 80.0 + 4475.0 + True + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + 1.0 + 80.0 + 25.0 + False + + + True + 675.0 + 80.0 + 100.0 + + + 1.0 + 80.0 + 25.0 + False + + + False + 25.0 + 80.0 + 1.0 + + + 100.0 + 80.0 + 675.0 + True + + + False + 25.0 + 80.0 + 1.0 + + + 1.0 + 80.0 + 25.0 + False + + + 1.0 + 25.0 + 25.0 + False + + + True + 225.0 + 80.0 + 100.0 + + + 1.0 + 80.0 + 25.0 + False + + + False + 25.0 + 80.0 + 1.0 + + + 100.0 + 80.0 + 225.0 + True + + + False + 25.0 + 80.0 + 1.0 + + + 1.0 + 80.0 + 25.0 + False + + + 1.0 + 25.0 + 25.0 + False + + + True + 4475.0 + 80.0 + 100.0 + + + 1.0 + 80.0 + 25.0 + False + + + 100.0 + 80.0 + 4475.0 + True + + + True + 650.0 + 25.0 + 100.0 + + + 1.0 + 25.0 + 25.0 + False + + + False + 25.0 + 25.0 + 1.0 + + + 100.0 + 25.0 + 650.0 + True + + + False + 25.0 + 25.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + 1.0 + 25.0 + 25.0 + False + + + True + 250.0 + 25.0 + 100.0 + + + 1.0 + 25.0 + 25.0 + False + + + False + 25.0 + 25.0 + 1.0 + + + 100.0 + 25.0 + 250.0 + True + + + False + 25.0 + 25.0 + 1.0 + + + 1.0 + 25.0 + 25.0 + False + + + diff --git a/test/example-networks-sim-bidirec/Overtake_slow/readme.md b/test/example-networks-sim-bidirec/Overtake_slow/readme.md new file mode 100644 index 000000000..12df08c80 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake_slow/readme.md @@ -0,0 +1,3 @@ +## Explanation Overtake_slow Network + +Shortest track length has been increased and max train speed decreasted to avoid trains skipping tracks. diff --git a/test/example-networks-sim-bidirec/Overtake_slow/routes/routes.json b/test/example-networks-sim-bidirec/Overtake_slow/routes/routes.json new file mode 100644 index 000000000..01fd1d9b0 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake_slow/routes/routes.json @@ -0,0 +1,40 @@ +{ + "tr1": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4b"], + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "v7b"], + ["v7b", "v8b"], + ["v8b", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ], + "tr2": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4b"], + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "switch_center"], + ["switch_center", "v6"], + ["v6", "v7"], + ["v7", "v8"], + ["v8", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ], + "tr3": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4"], + ["v4", "v5"], + ["v5", "v6"], + ["v6", "v7"], + ["v7", "v8"], + ["v8", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ] +} diff --git a/test/example-networks-sim-bidirec/Overtake_slow/timetable/schedules.json b/test/example-networks-sim-bidirec/Overtake_slow/timetable/schedules.json new file mode 100644 index 000000000..da41de5d4 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake_slow/timetable/schedules.json @@ -0,0 +1,29 @@ +{ + "tr1": { + "entry": "v1", + "exit": "v11", + "stops": [{ "begin": 270, "end": 330, "station": "Station" }], + "t_0": 0, + "t_n": 585, + "v_0": 24.0, + "v_n": 24.0 + }, + "tr2": { + "entry": "v1", + "exit": "v11", + "stops": null, + "t_0": 30, + "t_n": 465, + "v_0": 24.0, + "v_n": 24.0 + }, + "tr3": { + "entry": "v1", + "exit": "v11", + "stops": null, + "t_0": 165, + "t_n": 315, + "v_0": 24.0, + "v_n": 24.0 + } +} diff --git a/test/example-networks-sim-bidirec/Overtake_slow/timetable/stations.json b/test/example-networks-sim-bidirec/Overtake_slow/timetable/stations.json new file mode 100644 index 000000000..929feff08 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake_slow/timetable/stations.json @@ -0,0 +1,8 @@ +{ + "Station": [ + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "v7b"], + ["v7b", "v8b"] + ] +} diff --git a/test/example-networks-sim-bidirec/Overtake_slow/timetable/trains.json b/test/example-networks-sim-bidirec/Overtake_slow/timetable/trains.json new file mode 100644 index 000000000..a0f01e850 --- /dev/null +++ b/test/example-networks-sim-bidirec/Overtake_slow/timetable/trains.json @@ -0,0 +1,23 @@ +{ + "tr1": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 200, + "max_speed": 24.0, + "tim": true + }, + "tr2": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 150, + "max_speed": 24.0, + "tim": true + }, + "tr3": { + "acceleration": 2.0, + "deceleration": 2.0, + "length": 300, + "max_speed": 24.0, + "tim": true + } +} diff --git a/test/example-networks-sim-bidirec/SimpleNetwork/network/successors_cpp.json b/test/example-networks-sim-bidirec/SimpleNetwork/network/successors_cpp.json new file mode 100644 index 000000000..36fb59f51 --- /dev/null +++ b/test/example-networks-sim-bidirec/SimpleNetwork/network/successors_cpp.json @@ -0,0 +1,74 @@ +{ + "('v10', 'v11')": [["v11", "v12"]], + "('v10', 'v9')": [ + ["v9", "v8a"], + ["v9", "v8b"] + ], + "('v11', 'v10')": [["v10", "v9"]], + "('v11', 'v12')": [ + ["v12", "v13b"], + ["v12", "v13c"], + ["v12", "v13a"] + ], + "('v12', 'v11')": [["v11", "v10"]], + "('v12', 'v13b')": [["v13b", "v14b"]], + "('v12', 'v13c')": [["v13c", "v14c"]], + "('v13a', 'v12')": [["v12", "v11"]], + "('v13b', 'v12')": [["v12", "v11"]], + "('v13b', 'v14b')": [], + "('v13c', 'v14c')": [], + "('v14a', 'v13a')": [["v13a", "v12"]], + "('v14b', 'v13b')": [["v13b", "v12"]], + "('v1b', 'v2b')": [["v2b", "v3"]], + "('v1c', 'v2c')": [["v2c", "v3"]], + "('v2a', 'v1a')": [], + "('v2b', 'v1b')": [], + "('v2b', 'v3')": [["v3", "v4"]], + "('v2c', 'v3')": [["v3", "v4"]], + "('v3', 'v2a')": [["v2a", "v1a"]], + "('v3', 'v2b')": [["v2b", "v1b"]], + "('v3', 'v4')": [["v4", "v5"]], + "('v4', 'v3')": [ + ["v3", "v2a"], + ["v3", "v2b"], + ["v3", "v2c"] + ], + "('v4', 'v5')": [["v5", "v6"]], + "('v5', 'v4')": [["v4", "v3"]], + "('v5', 'v6')": [ + ["v6", "v7a"], + ["v6", "v7b"] + ], + "('v6', 'v5')": [["v5", "v4"]], + "('v6', 'v7a')": [["v7a", "v8a"]], + "('v6', 'v7b')": [["v7b", "v8b"]], + "('v7a', 'v6')": [["v6", "v5"]], + "('v7a', 'v8a')": [["v8a", "v9"]], + "('v7b', 'v6')": [["v6", "v5"]], + "('v7b', 'v8b')": [["v8b", "v9"]], + "('v8a', 'v7a')": [["v7a", "v6"]], + "('v8a', 'v9')": [["v9", "v10"]], + "('v8b', 'v7b')": [["v7b", "v6"]], + "('v8b', 'v9')": [["v9", "v10"]], + "('v9', 'v10')": [["v10", "v11"]], + "('v9', 'v8a')": [["v8a", "v7a"]], + "('v9', 'v8b')": [["v8b", "v7b"]], + "('v3', 'v2c')": [ + ["v2c", "v1c"], + ["v2c", "v1c"] + ], + "('v1a', 'v2a')": [ + ["v2a", "v3"], + ["v2a", "v3"] + ], + "('v2a', 'v3')": [["v3", "v4"]], + "('v14c', 'v13c')": [ + ["v13c", "v12"], + ["v13c", "v12"] + ], + "('v13c', 'v12')": [["v12", "v11"]], + "('v12', 'v13a')": [ + ["v13a", "v14a"], + ["v13a", "v14a"] + ] +} diff --git a/test/example-networks-sim-bidirec/SimpleNetwork/network/tracks.graphml b/test/example-networks-sim-bidirec/SimpleNetwork/network/tracks.graphml new file mode 100644 index 000000000..464e3aa1e --- /dev/null +++ b/test/example-networks-sim-bidirec/SimpleNetwork/network/tracks.graphml @@ -0,0 +1,370 @@ + + + + + + + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 100.0 + 25.0 + 475.0 + True + + + True + 475.0 + 75.0 + 100.0 + + + True + 475.0 + 25.0 + 100.0 + + + True + 475.0 + 25.0 + 100.0 + + + 1.0 + 25.0 + 25.0 + False + + + False + 25.0 + 75.0 + 1.0 + + + True + 475.0 + 75.0 + 100.0 + + + False + 25.0 + 25.0 + 1.0 + + + 100.0 + 25.0 + 475.0 + True + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + 1.0 + 25.0 + 25.0 + False + + + True + 9950.0 + 75.0 + 100.0 + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 75.0 + 1.0 + + + True + 9950.0 + 75.0 + 100.0 + + + False + 25.0 + 25.0 + 1.0 + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 75.0 + 1.0 + + + True + 1950.0 + 25.0 + 100.0 + + + False + 25.0 + 25.0 + 1.0 + + + True + 1950.0 + 75.0 + 100.0 + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + True + 1950.0 + 25.0 + 100.0 + + + False + 25.0 + 75.0 + 1.0 + + + True + 1950.0 + 75.0 + 100.0 + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + True + 9950.0 + 75.0 + 100.0 + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 75.0 + 1.0 + + + True + 9950.0 + 75.0 + 100.0 + + + False + 25.0 + 75.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + False + 25.0 + 75.0 + 1.0 + + + 1.0 + 25.0 + 25.0 + False + + + False + 25.0 + 25.0 + 1.0 + + + 100.0 + 25.0 + 475.0 + True + + + True + 475.0 + 75.0 + 100.0 + + + False + 25.0 + 75.0 + 1.0 + + + True + 475.0 + 25.0 + 100.0 + + + 1.0 + 25.0 + 25.0 + False + + + True + 475.0 + 25.0 + 100.0 + + + True + 475.0 + 75.0 + 100.0 + + + 100.0 + 25.0 + 475.0 + True + + + diff --git a/test/example-networks-sim-bidirec/SimpleNetwork/routes/routes.json b/test/example-networks-sim-bidirec/SimpleNetwork/routes/routes.json new file mode 100644 index 000000000..f0939e2c3 --- /dev/null +++ b/test/example-networks-sim-bidirec/SimpleNetwork/routes/routes.json @@ -0,0 +1,62 @@ +{ + "tr1lr": [ + ["v1b", "v2b"], + ["v2b", "v3"], + ["v3", "v4"], + ["v4", "v5"], + ["v5", "v6"], + ["v6", "v7b"], + ["v7b", "v8b"], + ["v8b", "v9"], + ["v9", "v10"], + ["v10", "v11"], + ["v11", "v12"], + ["v12", "v13b"], + ["v13b", "v14b"] + ], + "tr1rl": [ + ["v14b", "v13b"], + ["v13b", "v12"], + ["v12", "v11"], + ["v11", "v10"], + ["v10", "v9"], + ["v9", "v8a"], + ["v8a", "v7a"], + ["v7a", "v6"], + ["v6", "v5"], + ["v5", "v4"], + ["v4", "v3"], + ["v3", "v2b"], + ["v2b", "v1b"] + ], + "tr2lr": [ + ["v1c", "v2c"], + ["v2c", "v3"], + ["v3", "v4"], + ["v4", "v5"], + ["v5", "v6"], + ["v6", "v7b"], + ["v7b", "v8b"], + ["v8b", "v9"], + ["v9", "v10"], + ["v10", "v11"], + ["v11", "v12"], + ["v12", "v13c"], + ["v13c", "v14c"] + ], + "tr2rl": [ + ["v14a", "v13a"], + ["v13a", "v12"], + ["v12", "v11"], + ["v11", "v10"], + ["v10", "v9"], + ["v9", "v8a"], + ["v8a", "v7a"], + ["v7a", "v6"], + ["v6", "v5"], + ["v5", "v4"], + ["v4", "v3"], + ["v3", "v2a"], + ["v2a", "v1a"] + ] +} diff --git a/test/example-networks-sim-bidirec/SimpleNetwork/timetable/schedules.json b/test/example-networks-sim-bidirec/SimpleNetwork/timetable/schedules.json new file mode 100644 index 000000000..5e165d6a5 --- /dev/null +++ b/test/example-networks-sim-bidirec/SimpleNetwork/timetable/schedules.json @@ -0,0 +1,44 @@ +{ + "tr1lr": { + "entry": "v1b", + "exit": "v14b", + "stops": null, + "t_0": 180, + "t_n": 960, + "v_0": 50.0, + "v_n": 50.0 + }, + "tr1rl": { + "entry": "v14b", + "exit": "v1b", + "stops": null, + "t_0": 180, + "t_n": 960, + "v_0": 50.0, + "v_n": 50.0 + }, + "tr2lr": { + "entry": "v1c", + "exit": "v14c", + "stops": [ + { "begin": 30, "end": 90, "station": "StationLeft" }, + { "begin": 900, "end": 960, "station": "StationRight" } + ], + "t_0": 0, + "t_n": 990, + "v_0": 25.0, + "v_n": 25.0 + }, + "tr2rl": { + "entry": "v14a", + "exit": "v1a", + "stops": [ + { "begin": 30, "end": 90, "station": "StationRight" }, + { "begin": 900, "end": 960, "station": "StationLeft" } + ], + "t_0": 0, + "t_n": 990, + "v_0": 25.0, + "v_n": 25.0 + } +} diff --git a/test/example-networks-sim-bidirec/SimpleNetwork/timetable/stations.json b/test/example-networks-sim-bidirec/SimpleNetwork/timetable/stations.json new file mode 100644 index 000000000..ed2310455 --- /dev/null +++ b/test/example-networks-sim-bidirec/SimpleNetwork/timetable/stations.json @@ -0,0 +1,14 @@ +{ + "StationLeft": [ + ["v1b", "v2b"], + ["v1c", "v2c"], + ["v2b", "v1b"], + ["v2a", "v1a"] + ], + "StationRight": [ + ["v13b", "v14b"], + ["v13c", "v14c"], + ["v14b", "v13b"], + ["v14a", "v13a"] + ] +} diff --git a/test/example-networks-sim-bidirec/SimpleNetwork/timetable/trains.json b/test/example-networks-sim-bidirec/SimpleNetwork/timetable/trains.json new file mode 100644 index 000000000..625124d08 --- /dev/null +++ b/test/example-networks-sim-bidirec/SimpleNetwork/timetable/trains.json @@ -0,0 +1,30 @@ +{ + "tr1lr": { + "acceleration": 2.0, + "deceleration": 2.0, + "length": 400, + "max_speed": 50.0, + "tim": true + }, + "tr1rl": { + "acceleration": 2.0, + "deceleration": 2.0, + "length": 400, + "max_speed": 50.0, + "tim": true + }, + "tr2lr": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 400, + "max_speed": 34.0, + "tim": true + }, + "tr2rl": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 400, + "max_speed": 34.0, + "tim": true + } +} diff --git a/test/example-networks-sim-bidirec/Stammstrecke16Trains/network/successors_cpp.json b/test/example-networks-sim-bidirec/Stammstrecke16Trains/network/successors_cpp.json new file mode 100644 index 000000000..4ae4f8792 --- /dev/null +++ b/test/example-networks-sim-bidirec/Stammstrecke16Trains/network/successors_cpp.json @@ -0,0 +1,394 @@ +{ + "('DonnersbergerEntry', 'HackerbrueckeSwitchEntry')": [ + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"] + ], + "('Donnersbergerbruecke1L', 'Donnersbergerbruecke1R')": [ + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"] + ], + "('Donnersbergerbruecke1R', 'HackerbrueckeSwitchEntry')": [ + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"] + ], + "('Donnersbergerbruecke2L', 'Hirschgarten2R')": [ + ["Hirschgarten2R", "Hirschgarten2L"] + ], + "('Donnersbergerbruecke2R', 'Donnersbergerbruecke2L')": [ + ["Donnersbergerbruecke2L", "Hirschgarten2R"] + ], + "('Hackerbruecke1L', 'Hackerbruecke1R')": [["Hackerbruecke1R", "Hbf1L"]], + "('Hackerbruecke1R', 'Hbf1L')": [["Hbf1L", "Hbf1R"]], + "('Hackerbruecke2L', 'HackerbrueckeSwitch3')": [ + ["HackerbrueckeSwitch3", "HackerbrueckeSwitchC"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"] + ], + "('Hackerbruecke2R', 'Hackerbruecke2L')": [ + ["Hackerbruecke2L", "HackerbrueckeSwitch3"] + ], + "('HackerbrueckeSwitch1', 'HackerbrueckeSwitch2')": [ + ["HackerbrueckeSwitch2", "Hackerbruecke1L"] + ], + "('HackerbrueckeSwitch1', 'HackerbrueckeSwitchC')": [ + ["HackerbrueckeSwitchC", "HackerbrueckeSwitch3"] + ], + "('HackerbrueckeSwitch2', 'Hackerbruecke1L')": [ + ["Hackerbruecke1L", "Hackerbruecke1R"] + ], + "('HackerbrueckeSwitch2', 'HackerbrueckeSwitchC')": [ + ["HackerbrueckeSwitchC", "HackerbrueckeSwitch4"] + ], + "('HackerbrueckeSwitch3', 'HackerbrueckeSwitch4')": [ + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"] + ], + "('HackerbrueckeSwitch3', 'HackerbrueckeSwitchC')": [ + ["HackerbrueckeSwitchC", "HackerbrueckeSwitch1"] + ], + "('HackerbrueckeSwitch4', 'HackerbrueckeSwitchC')": [ + ["HackerbrueckeSwitchC", "HackerbrueckeSwitch2"] + ], + "('HackerbrueckeSwitch4', 'HackerbrueckeSwitchExit')": [ + ["HackerbrueckeSwitchExit", "DonnersbergerExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"] + ], + "('HackerbrueckeSwitchC', 'HackerbrueckeSwitch1')": [ + ["HackerbrueckeSwitch1", "HackerbrueckeSwitchEntry"] + ], + "('HackerbrueckeSwitchC', 'HackerbrueckeSwitch2')": [ + ["HackerbrueckeSwitch2", "Hackerbruecke1L"] + ], + "('HackerbrueckeSwitchC', 'HackerbrueckeSwitch3')": [ + ["HackerbrueckeSwitch3", "Hackerbruecke2L"] + ], + "('HackerbrueckeSwitchC', 'HackerbrueckeSwitch4')": [ + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"] + ], + "('HackerbrueckeSwitchEntry', 'HackerbrueckeSwitch1')": [ + ["HackerbrueckeSwitch1", "HackerbrueckeSwitchC"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"] + ], + "('HackerbrueckeSwitchExit', 'DonnersbergerExit')": [], + "('HackerbrueckeSwitchExit', 'Donnersbergerbruecke2R')": [ + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"] + ], + "('Hbf1L', 'Hbf1R')": [["Hbf1R", "Karlsplatz1L"]], + "('Hbf1R', 'Karlsplatz1L')": [["Karlsplatz1L", "Karlsplatz1R"]], + "('Hbf2L', 'Hackerbruecke2R')": [["Hackerbruecke2R", "Hackerbruecke2L"]], + "('Hbf2R', 'Hbf2L')": [["Hbf2L", "Hackerbruecke2R"]], + "('Hirschgarten1L', 'Hirschgarten1R')": [ + ["Hirschgarten1R", "Donnersbergerbruecke1L"] + ], + "('Hirschgarten1R', 'Donnersbergerbruecke1L')": [ + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"] + ], + "('Hirschgarten2L', 'Laim3R')": [["Laim3R", "Laim3L"]], + "('Hirschgarten2R', 'Hirschgarten2L')": [["Hirschgarten2L", "Laim3R"]], + "('Isartor1L', 'Isartor1R')": [["Isartor1R", "IsartorSwitchR_LR"]], + "('Isartor1L', 'IsartorSwitchLR')": [ + ["IsartorSwitchLR", "IsartorSwitchRL"], + ["IsartorSwitchLR", "Marienplatz1R"] + ], + "('Isartor1R', 'Isartor1L')": [["Isartor1L", "IsartorSwitchLR"]], + "('Isartor1R', 'IsartorSwitchR_LR')": [ + ["IsartorSwitchR_LR", "IsartorSwitchR_RL"], + ["IsartorSwitchR_LR", "Rosenheimer1L"] + ], + "('Isartor2L', 'IsartorSwitchRL')": [["IsartorSwitchRL", "Marienplatz2R"]], + "('Isartor2R', 'Isartor2L')": [["Isartor2L", "IsartorSwitchRL"]], + "('IsartorSwitchLR', 'Isartor1L')": [["Isartor1L", "Isartor1R"]], + "('IsartorSwitchLR', 'IsartorSwitchRL')": [], + "('IsartorSwitchRL', 'IsartorSwitchLR')": [["IsartorSwitchLR", "Isartor1L"]], + "('IsartorSwitchRL', 'Marienplatz2R')": [["Marienplatz2R", "Marienplatz2L"]], + "('IsartorSwitchR_LR', 'Isartor1R')": [["Isartor1R", "Isartor1L"]], + "('IsartorSwitchR_LR', 'IsartorSwitchR_RL')": [ + ["IsartorSwitchR_RL", "Rosenheimer2L"] + ], + "('IsartorSwitchR_LR', 'Rosenheimer1L')": [ + ["Rosenheimer1L", "Rosenheimer1R"] + ], + "('IsartorSwitchR_RL', 'Isartor2R')": [["Isartor2R", "Isartor2L"]], + "('IsartorSwitchR_RL', 'IsartorSwitchR_LR')": [ + ["IsartorSwitchR_LR", "Isartor1R"] + ], + "('Karlsplatz1L', 'Karlsplatz1R')": [["Karlsplatz1R", "Marienplatz1L"]], + "('Karlsplatz1R', 'Marienplatz1L')": [["Marienplatz1L", "Marienplatz1R"]], + "('Karlsplatz2L', 'Hbf2R')": [["Hbf2R", "Hbf2L"]], + "('Karlsplatz2R', 'Karlsplatz2L')": [["Karlsplatz2L", "Hbf2R"]], + "('Laim1L', 'Laim1R')": [["Laim1R", "LaimSwitchHirschgarten"]], + "('Laim1R', 'LaimSwitchHirschgarten')": [ + ["LaimSwitchHirschgarten", "Hirschgarten1L"] + ], + "('Laim3L', 'LaimSwitchNymphenburg')": [ + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["LaimSwitchNymphenburg", "LaimExitNymphenburg"] + ], + "('Laim3R', 'Laim3L')": [["Laim3L", "LaimSwitchNymphenburg"]], + "('LaimEntry', 'LaimSwitchHirschgarten')": [ + ["LaimSwitchHirschgarten", "Hirschgarten1L"] + ], + "('LaimSwitchHirschgarten', 'Hirschgarten1L')": [ + ["Hirschgarten1L", "Hirschgarten1R"] + ], + "('LaimSwitchNymphenburg', 'LaimExitNymphenburg')": [], + "('LaimSwitchNymphenburg', 'PasingSwitch2')": [ + ["PasingSwitch2", "PasingExit"] + ], + "('Marienplatz1L', 'Marienplatz1R')": [["Marienplatz1R", "IsartorSwitchLR"]], + "('Marienplatz1R', 'IsartorSwitchLR')": [["IsartorSwitchLR", "Isartor1L"]], + "('Marienplatz2L', 'Karlsplatz2R')": [["Karlsplatz2R", "Karlsplatz2L"]], + "('Marienplatz2R', 'Marienplatz2L')": [["Marienplatz2L", "Karlsplatz2R"]], + "('Ost1Entry', 'OstSwitch1_RL')": [["OstSwitch1_RL", "Rosenheimer2R"]], + "('Ost2Entry', 'OstSwitch2_RL')": [["OstSwitch2_RL", "OstSwitch1_RL"]], + "('Ost3Entry', 'OstSwitch3_RL')": [["OstSwitch3_RL", "OstSwitch2_RL"]], + "('OstSwitch1_RL', 'Rosenheimer2R')": [["Rosenheimer2R", "Rosenheimer2L"]], + "('OstSwitch2_RL', 'OstSwitch1_RL')": [["OstSwitch1_RL", "Rosenheimer2R"]], + "('OstSwitch3_RL', 'OstSwitch2_RL')": [["OstSwitch2_RL", "OstSwitch1_RL"]], + "('OstSwitch4_LR', 'Ost4Exit')": [], + "('OstSwitch5_LR', 'Ost5Exit')": [], + "('OstSwitch5_LR', 'OstSwitch4_LR')": [["OstSwitch4_LR", "Ost4Exit"]], + "('PasingEntry', 'PasingSwitch1')": [["PasingSwitch1", "Laim1L"]], + "('PasingSwitch1', 'Laim1L')": [["Laim1L", "Laim1R"]], + "('PasingSwitch1', 'PasingSwitch2')": [["PasingSwitch2", "PasingExit"]], + "('PasingSwitch2', 'PasingExit')": [], + "('PasingSwitch2', 'PasingSwitch1')": [["PasingSwitch1", "Laim1L"]], + "('Rosenheimer1L', 'Rosenheimer1R')": [["Rosenheimer1R", "OstSwitch5_LR"]], + "('Rosenheimer1R', 'OstSwitch5_LR')": [ + ["OstSwitch5_LR", "OstSwitch4_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "('Rosenheimer2L', 'IsartorSwitchR_RL')": [ + ["IsartorSwitchR_RL", "IsartorSwitchR_LR"], + ["IsartorSwitchR_RL", "Isartor2R"] + ], + "('Rosenheimer2R', 'Rosenheimer2L')": [ + ["Rosenheimer2L", "IsartorSwitchR_RL"] + ], + "('Laim1L', 'PasingSwitch1')": [ + ["PasingSwitch1", "PasingEntry"], + ["PasingSwitch1", "PasingEntry"], + ["PasingSwitch1", "PasingSwitch2"] + ], + "('Laim1R', 'Laim1L')": [ + ["Laim1L", "PasingSwitch1"], + ["Laim1L", "PasingSwitch1"] + ], + "('PasingExit', 'PasingSwitch2')": [ + ["PasingSwitch2", "LaimSwitchNymphenburg"], + ["PasingSwitch2", "PasingSwitch1"], + ["PasingSwitch2", "LaimSwitchNymphenburg"] + ], + "('LaimSwitchHirschgarten', 'Laim1R')": [ + ["Laim1R", "Laim1L"], + ["Laim1R", "Laim1L"] + ], + "('PasingSwitch2', 'LaimSwitchNymphenburg')": [ + ["LaimSwitchNymphenburg", "Laim3L"], + ["LaimSwitchNymphenburg", "Laim3L"] + ], + "('LaimExitNymphenburg', 'LaimSwitchNymphenburg')": [ + ["LaimSwitchNymphenburg", "Laim3L"], + ["LaimSwitchNymphenburg", "Laim3L"] + ], + "('LaimSwitchNymphenburg', 'Laim3L')": [ + ["Laim3L", "Laim3R"], + ["Laim3L", "Laim3R"] + ], + "('Hirschgarten1L', 'LaimSwitchHirschgarten')": [ + ["LaimSwitchHirschgarten", "Laim1R"], + ["LaimSwitchHirschgarten", "LaimEntry"], + ["LaimSwitchHirschgarten", "Laim1R"], + ["LaimSwitchHirschgarten", "LaimEntry"] + ], + "('Laim3L', 'Laim3R')": [ + ["Laim3R", "Hirschgarten2L"], + ["Laim3R", "Hirschgarten2L"] + ], + "('Hirschgarten1R', 'Hirschgarten1L')": [ + ["Hirschgarten1L", "LaimSwitchHirschgarten"], + ["Hirschgarten1L", "LaimSwitchHirschgarten"] + ], + "('Donnersbergerbruecke1L', 'Hirschgarten1R')": [ + ["Hirschgarten1R", "Hirschgarten1L"], + ["Hirschgarten1R", "Hirschgarten1L"] + ], + "('Laim3R', 'Hirschgarten2L')": [ + ["Hirschgarten2L", "Hirschgarten2R"], + ["Hirschgarten2L", "Hirschgarten2R"] + ], + "('Donnersbergerbruecke1R', 'Donnersbergerbruecke1L')": [ + ["Donnersbergerbruecke1L", "Hirschgarten1R"], + ["Donnersbergerbruecke1L", "Hirschgarten1R"] + ], + "('Hirschgarten2L', 'Hirschgarten2R')": [ + ["Hirschgarten2R", "Donnersbergerbruecke2L"], + ["Hirschgarten2R", "Donnersbergerbruecke2L"] + ], + "('HackerbrueckeSwitchEntry', 'Donnersbergerbruecke1R')": [ + ["Donnersbergerbruecke1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1R", "Donnersbergerbruecke1L"] + ], + "('Hirschgarten2R', 'Donnersbergerbruecke2L')": [ + ["Donnersbergerbruecke2L", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2L", "Donnersbergerbruecke2R"] + ], + "('HackerbrueckeSwitch1', 'HackerbrueckeSwitchEntry')": [ + ["HackerbrueckeSwitchEntry", "Donnersbergerbruecke1R"], + ["HackerbrueckeSwitchEntry", "DonnersbergerEntry"], + ["HackerbrueckeSwitchEntry", "Donnersbergerbruecke1R"], + ["HackerbrueckeSwitchEntry", "DonnersbergerEntry"] + ], + "('Donnersbergerbruecke2L', 'Donnersbergerbruecke2R')": [ + ["Donnersbergerbruecke2R", "HackerbrueckeSwitchExit"], + ["Donnersbergerbruecke2R", "HackerbrueckeSwitchExit"] + ], + "('Hackerbruecke1L', 'HackerbrueckeSwitch2')": [ + ["HackerbrueckeSwitch2", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch2", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch2", "HackerbrueckeSwitchC"] + ], + "('HackerbrueckeSwitch2', 'HackerbrueckeSwitch1')": [ + ["HackerbrueckeSwitch1", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitchEntry"] + ], + "('Hackerbruecke1R', 'Hackerbruecke1L')": [ + ["Hackerbruecke1L", "HackerbrueckeSwitch2"], + ["Hackerbruecke1L", "HackerbrueckeSwitch2"] + ], + "('HackerbrueckeSwitchExit', 'HackerbrueckeSwitch4')": [ + ["HackerbrueckeSwitch4", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchC"] + ], + "('HackerbrueckeSwitch4', 'HackerbrueckeSwitch3')": [ + ["HackerbrueckeSwitch3", "Hackerbruecke2L"], + ["HackerbrueckeSwitch3", "Hackerbruecke2L"] + ], + "('DonnersbergerExit', 'HackerbrueckeSwitchExit')": [ + ["HackerbrueckeSwitchExit", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitchExit", "HackerbrueckeSwitch4"] + ], + "('Donnersbergerbruecke2R', 'HackerbrueckeSwitchExit')": [ + ["HackerbrueckeSwitchExit", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitchExit", "HackerbrueckeSwitch4"] + ], + "('Hbf1L', 'Hackerbruecke1R')": [ + ["Hackerbruecke1R", "Hackerbruecke1L"], + ["Hackerbruecke1R", "Hackerbruecke1L"] + ], + "('HackerbrueckeSwitch3', 'Hackerbruecke2L')": [ + ["Hackerbruecke2L", "Hackerbruecke2R"], + ["Hackerbruecke2L", "Hackerbruecke2R"] + ], + "('Hbf1R', 'Hbf1L')": [ + ["Hbf1L", "Hackerbruecke1R"], + ["Hbf1L", "Hackerbruecke1R"] + ], + "('Hackerbruecke2L', 'Hackerbruecke2R')": [ + ["Hackerbruecke2R", "Hbf2L"], + ["Hackerbruecke2R", "Hbf2L"] + ], + "('Karlsplatz1L', 'Hbf1R')": [ + ["Hbf1R", "Hbf1L"], + ["Hbf1R", "Hbf1L"] + ], + "('Hackerbruecke2R', 'Hbf2L')": [ + ["Hbf2L", "Hbf2R"], + ["Hbf2L", "Hbf2R"] + ], + "('Karlsplatz1R', 'Karlsplatz1L')": [ + ["Karlsplatz1L", "Hbf1R"], + ["Karlsplatz1L", "Hbf1R"] + ], + "('Hbf2L', 'Hbf2R')": [ + ["Hbf2R", "Karlsplatz2L"], + ["Hbf2R", "Karlsplatz2L"] + ], + "('Marienplatz1L', 'Karlsplatz1R')": [ + ["Karlsplatz1R", "Karlsplatz1L"], + ["Karlsplatz1R", "Karlsplatz1L"] + ], + "('Hbf2R', 'Karlsplatz2L')": [ + ["Karlsplatz2L", "Karlsplatz2R"], + ["Karlsplatz2L", "Karlsplatz2R"] + ], + "('Marienplatz1R', 'Marienplatz1L')": [ + ["Marienplatz1L", "Karlsplatz1R"], + ["Marienplatz1L", "Karlsplatz1R"] + ], + "('Karlsplatz2L', 'Karlsplatz2R')": [ + ["Karlsplatz2R", "Marienplatz2L"], + ["Karlsplatz2R", "Marienplatz2L"] + ], + "('IsartorSwitchLR', 'Marienplatz1R')": [ + ["Marienplatz1R", "Marienplatz1L"], + ["Marienplatz1R", "Marienplatz1L"] + ], + "('Karlsplatz2R', 'Marienplatz2L')": [ + ["Marienplatz2L", "Marienplatz2R"], + ["Marienplatz2L", "Marienplatz2R"] + ], + "('Marienplatz2L', 'Marienplatz2R')": [ + ["Marienplatz2R", "IsartorSwitchRL"], + ["Marienplatz2R", "IsartorSwitchRL"] + ], + "('Marienplatz2R', 'IsartorSwitchRL')": [ + ["IsartorSwitchRL", "Isartor2L"], + ["IsartorSwitchRL", "Isartor2L"] + ], + "('IsartorSwitchRL', 'Isartor2L')": [ + ["Isartor2L", "Isartor2R"], + ["Isartor2L", "Isartor2R"] + ], + "('Isartor2L', 'Isartor2R')": [ + ["Isartor2R", "IsartorSwitchR_RL"], + ["Isartor2R", "IsartorSwitchR_RL"] + ], + "('Rosenheimer1R', 'Rosenheimer1L')": [ + ["Rosenheimer1L", "IsartorSwitchR_LR"], + ["Rosenheimer1L", "IsartorSwitchR_LR"] + ], + "('Rosenheimer1L', 'IsartorSwitchR_LR')": [ + ["IsartorSwitchR_LR", "Isartor1R"] + ], + "('Isartor2R', 'IsartorSwitchR_RL')": [ + ["IsartorSwitchR_RL", "Rosenheimer2L"], + ["IsartorSwitchR_RL", "Rosenheimer2L"] + ], + "('OstSwitch5_LR', 'Rosenheimer1R')": [ + ["Rosenheimer1R", "Rosenheimer1L"], + ["Rosenheimer1R", "Rosenheimer1L"] + ], + "('IsartorSwitchR_RL', 'Rosenheimer2L')": [ + ["Rosenheimer2L", "Rosenheimer2R"], + ["Rosenheimer2L", "Rosenheimer2R"] + ], + "('OstSwitch4_LR', 'OstSwitch5_LR')": [ + ["OstSwitch5_LR", "Rosenheimer1R"], + ["OstSwitch5_LR", "Rosenheimer1R"] + ], + "('Ost5Exit', 'OstSwitch5_LR')": [ + ["OstSwitch5_LR", "Rosenheimer1R"], + ["OstSwitch5_LR", "Rosenheimer1R"] + ], + "('Rosenheimer2L', 'Rosenheimer2R')": [ + ["Rosenheimer2R", "OstSwitch1_RL"], + ["Rosenheimer2R", "OstSwitch1_RL"] + ], + "('Ost4Exit', 'OstSwitch4_LR')": [ + ["OstSwitch4_LR", "OstSwitch5_LR"], + ["OstSwitch4_LR", "OstSwitch5_LR"] + ], + "('Rosenheimer2R', 'OstSwitch1_RL')": [ + ["OstSwitch1_RL", "Ost1Entry"], + ["OstSwitch1_RL", "OstSwitch2_RL"], + ["OstSwitch1_RL", "OstSwitch2_RL"], + ["OstSwitch1_RL", "Ost1Entry"] + ], + "('OstSwitch1_RL', 'OstSwitch2_RL')": [ + ["OstSwitch2_RL", "Ost2Entry"], + ["OstSwitch2_RL", "OstSwitch3_RL"], + ["OstSwitch2_RL", "OstSwitch3_RL"], + ["OstSwitch2_RL", "Ost2Entry"] + ], + "('OstSwitch2_RL', 'OstSwitch3_RL')": [ + ["OstSwitch3_RL", "Ost3Entry"], + ["OstSwitch3_RL", "Ost3Entry"] + ] +} diff --git a/test/example-networks-sim-bidirec/Stammstrecke16Trains/network/tracks.graphml b/test/example-networks-sim-bidirec/Stammstrecke16Trains/network/tracks.graphml new file mode 100644 index 000000000..6c8d11370 --- /dev/null +++ b/test/example-networks-sim-bidirec/Stammstrecke16Trains/network/tracks.graphml @@ -0,0 +1,1063 @@ + + + + + + + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + True + 280.0 + 33.3333 + 50.0 + + + 50.0 + 33.3333 + 160.0 + True + + + True + 2812.0 + 33.3333 + 50.0 + + + False + 120.0 + 22.2222 + 50.0 + + + 50.0 + 33.3333 + 280.0 + True + + + True + 160.0 + 33.3333 + 50.0 + + + False + 120.0 + 22.2222 + 50.0 + + + 50.0 + 33.3333 + 2562.0 + True + + + True + 210.0 + 33.3333 + 25.0 + + + 50.0 + 33.3333 + 2812.0 + True + + + True + 370.0 + 33.3333 + 50.0 + + + 25.0 + 33.3333 + 210.0 + True + + + True + 200.0 + 27.7778 + 50.0 + + + 25.0 + 33.3333 + 210.0 + True + + + True + 200.0 + 27.7778 + 50.0 + + + True + 210.0 + 33.3333 + 25.0 + + + 50.0 + 27.7778 + 892.0 + True + + + True + 2562.0 + 33.3333 + 50.0 + + + False + 30.0 + 27.7778 + 50.0 + + + 50.0 + 33.3333 + 370.0 + True + + + 50.0 + 27.7778 + 30.0 + False + + + True + 692.0 + 27.7778 + 50.0 + + + 50.0 + 27.7778 + 200.0 + True + + + 50.0 + 27.7778 + 200.0 + True + + + True + 205.0 + 27.7778 + 25.0 + + + 50.0 + 27.7778 + 692.0 + True + + + True + 892.0 + 27.7778 + 50.0 + + + 25.0 + 27.7778 + 205.0 + True + + + True + 1095.0 + 27.7778 + 50.0 + + + 25.0 + 27.7778 + 205.0 + True + + + True + 205.0 + 27.7778 + 25.0 + + + 50.0 + 27.7778 + 1095.0 + True + + + True + 205.0 + 27.7778 + 25.0 + + + 50.0 + 27.7778 + 1095.0 + True + + + True + 1095.0 + 27.7778 + 50.0 + + + 25.0 + 27.7778 + 205.0 + True + + + True + 210.0 + 27.7778 + 50.0 + + + 25.0 + 27.7778 + 205.0 + True + + + True + 205.0 + 27.7778 + 25.0 + + + 50.0 + 27.7778 + 210.0 + True + + + False + 150.0 + 27.7778 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + 50.0 + 27.7778 + 294.0 + True + + + False + 40.0 + 27.7778 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + 50.0 + 27.7778 + 150.0 + False + + + False + 150.0 + 27.7778 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + 50.0 + 27.7778 + 40.0 + False + + + True + 294.0 + 27.7778 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + 50.0 + 27.7778 + 150.0 + False + + + False + 75.0 + 22.2222 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + True + 207.0 + 27.7778 + 25.0 + + + 50.0 + 27.7778 + 40.0 + False + + + False + 40.0 + 27.7778 + 50.0 + + + 25.0 + 27.7778 + 207.0 + True + + + True + 591.0 + 22.2222 + 50.0 + + + 25.0 + 27.7778 + 207.0 + True + + + True + 207.0 + 27.7778 + 25.0 + + + 50.0 + 22.2222 + 591.0 + True + + + True + 294.0 + 27.7778 + 50.0 + + + 50.0 + 27.7778 + 210.0 + True + + + 50.0 + 27.7778 + 210.0 + True + + + True + 210.0 + 27.7778 + 50.0 + + + True + 210.0 + 27.7778 + 50.0 + + + 50.0 + 27.7778 + 294.0 + True + + + True + 210.0 + 27.7778 + 50.0 + + + 50.0 + 27.7778 + 210.0 + True + + + True + 210.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 591.0 + True + + + True + 591.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 210.0 + True + + + True + 292.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 210.0 + True + + + True + 210.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 292.0 + True + + + True + 206.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 292.0 + True + + + True + 292.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 206.0 + True + + + True + 494.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 206.0 + True + + + True + 206.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 494.0 + True + + + True + 205.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 494.0 + True + + + True + 494.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 205.0 + True + + + True + 393.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 205.0 + True + + + True + 205.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 343.0 + True + + + False + 100.0 + 22.2222 + 50.0 + + + False + 50.0 + 16.6667 + 50.0 + + + 50.0 + 22.2222 + 393.0 + True + + + True + 343.0 + 22.2222 + 50.0 + + + False + 50.0 + 16.6667 + 50.0 + + + 50.0 + 22.2222 + 150.0 + False + + + True + 209.0 + 22.2222 + 25.0 + + + False + 100.0 + 22.2222 + 50.0 + + + False + 150.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 209.0 + True + + + False + 100.0 + 22.2222 + 50.0 + + + True + 209.0 + 22.2222 + 25.0 + + + True + 209.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 150.0 + False + + + True + 592.0 + 22.2222 + 50.0 + + + False + 50.0 + 16.6667 + 50.0 + + + False + 100.0 + 22.2222 + 50.0 + + + False + 150.0 + 22.2222 + 50.0 + + + False + 50.0 + 16.6667 + 50.0 + + + 50.0 + 22.2222 + 542.0 + True + + + True + 206.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 592.0 + True + + + True + 542.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 206.0 + True + + + True + 792.0 + 22.2222 + 50.0 + + + 25.0 + 22.2222 + 206.0 + True + + + True + 206.0 + 22.2222 + 25.0 + + + 50.0 + 22.2222 + 752.0 + True + + + False + 40.0 + 22.2222 + 50.0 + + + 50.0 + 22.2222 + 60.0 + False + + + False + 60.0 + 22.2222 + 50.0 + + + False + 100.0 + 22.2222 + 50.0 + + + 50.0 + 22.2222 + 792.0 + True + + + True + 752.0 + 22.2222 + 50.0 + + + 50.0 + 22.2222 + 40.0 + False + + + 50.0 + 22.2222 + 140.0 + False + + + False + 40.0 + 22.2222 + 50.0 + + + 50.0 + 22.2222 + 60.0 + False + + + 50.0 + 22.2222 + 100.0 + False + + + False + 60.0 + 22.2222 + 50.0 + + + 50.0 + 22.2222 + 40.0 + False + + + False + 140.0 + 22.2222 + 50.0 + + + False + 100.0 + 22.2222 + 50.0 + + + False + 40.0 + 22.2222 + 50.0 + + + 50.0 + 22.2222 + 40.0 + False + + + 50.0 + 22.2222 + 100.0 + False + + + diff --git a/test/example-networks-sim-bidirec/Stammstrecke16Trains/routes/routes.json b/test/example-networks-sim-bidirec/Stammstrecke16Trains/routes/routes.json new file mode 100644 index 000000000..8d5907c32 --- /dev/null +++ b/test/example-networks-sim-bidirec/Stammstrecke16Trains/routes/routes.json @@ -0,0 +1,448 @@ +{ + "S1Freising": [ + ["Ost2Entry", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "LaimExitNymphenburg"] + ], + "S1Leuchtenbergring": [ + ["LaimEntry", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["IsartorSwitchLR", "Isartor1L"], + ["Isartor1L", "Isartor1R"], + ["Isartor1R", "IsartorSwitchR_LR"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "OstSwitch4_LR"], + ["OstSwitch4_LR", "Ost4Exit"] + ], + "S2Dachau": [ + ["Ost2Entry", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "LaimExitNymphenburg"] + ], + "S2Erding": [ + ["LaimEntry", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["IsartorSwitchLR", "Isartor1L"], + ["Isartor1L", "Isartor1R"], + ["Isartor1R", "IsartorSwitchR_LR"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S2Ost": [ + ["LaimEntry", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["IsartorSwitchLR", "Isartor1L"], + ["Isartor1L", "Isartor1R"], + ["Isartor1R", "IsartorSwitchR_LR"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S2Petershausen": [ + ["Ost2Entry", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "LaimExitNymphenburg"] + ], + "S3Deisenhofen": [ + ["PasingEntry", "PasingSwitch1"], + ["PasingSwitch1", "Laim1L"], + ["Laim1L", "Laim1R"], + ["Laim1R", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["IsartorSwitchLR", "Isartor1L"], + ["Isartor1L", "Isartor1R"], + ["Isartor1R", "IsartorSwitchR_LR"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "OstSwitch4_LR"], + ["OstSwitch4_LR", "Ost4Exit"] + ], + "S3Mammendorf": [ + ["Ost3Entry", "OstSwitch3_RL"], + ["OstSwitch3_RL", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["PasingSwitch2", "PasingExit"] + ], + "S4Geltendorf": [ + ["Ost1Entry", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["PasingSwitch2", "PasingExit"] + ], + "S4Grafing": [ + ["PasingEntry", "PasingSwitch1"], + ["PasingSwitch1", "Laim1L"], + ["Laim1L", "Laim1R"], + ["Laim1R", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["IsartorSwitchLR", "Isartor1L"], + ["Isartor1L", "Isartor1R"], + ["Isartor1R", "IsartorSwitchR_LR"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S6Ebersberg": [ + ["PasingEntry", "PasingSwitch1"], + ["PasingSwitch1", "Laim1L"], + ["Laim1L", "Laim1R"], + ["Laim1R", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["IsartorSwitchLR", "Isartor1L"], + ["Isartor1L", "Isartor1R"], + ["Isartor1R", "IsartorSwitchR_LR"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S6Tutzing": [ + ["Ost1Entry", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["PasingSwitch2", "PasingExit"] + ], + "S7Aying": [ + ["DonnersbergerEntry", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["IsartorSwitchLR", "Isartor1L"], + ["Isartor1L", "Isartor1R"], + ["Isartor1R", "IsartorSwitchR_LR"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "OstSwitch4_LR"], + ["OstSwitch4_LR", "Ost4Exit"] + ], + "S7Wolfratshausen": [ + ["Ost3Entry", "OstSwitch3_RL"], + ["OstSwitch3_RL", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "DonnersbergerExit"] + ], + "S8Airport": [ + ["PasingEntry", "PasingSwitch1"], + ["PasingSwitch1", "Laim1L"], + ["Laim1L", "Laim1R"], + ["Laim1R", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["IsartorSwitchLR", "Isartor1L"], + ["Isartor1L", "Isartor1R"], + ["Isartor1R", "IsartorSwitchR_LR"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S8Germering": [ + ["Ost1Entry", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["PasingSwitch2", "PasingExit"] + ] +} diff --git a/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/schedules.json b/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/schedules.json new file mode 100644 index 000000000..e93c25d9c --- /dev/null +++ b/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/schedules.json @@ -0,0 +1,297 @@ +{ + "S1Freising": { + "entry": "Ost2Entry", + "exit": "LaimExitNymphenburg", + "stops": [ + { "begin": 690, "end": 720, "station": "RosenheimerPlatz" }, + { "begin": 810, "end": 840, "station": "Isartor" }, + { "begin": 915, "end": 945, "station": "Marienplatz" }, + { "begin": 1020, "end": 1050, "station": "Karlsplatz" }, + { "begin": 1140, "end": 1170, "station": "Hbf" }, + { "begin": 1245, "end": 1275, "station": "Hackerbruecke" }, + { "begin": 1350, "end": 1380, "station": "Donnersbergerbruecke" }, + { "begin": 1470, "end": 1500, "station": "Hirschgarten" }, + { "begin": 1590, "end": 1620, "station": "Laim" } + ], + "t_0": 600, + "t_n": 1665, + "v_0": 0.0, + "v_n": 20.0 + }, + "S1Leuchtenbergring": { + "entry": "LaimEntry", + "exit": "Ost4Exit", + "stops": [ + { "begin": 615, "end": 645, "station": "Hirschgarten" }, + { "begin": 735, "end": 765, "station": "Donnersbergerbruecke" }, + { "begin": 840, "end": 870, "station": "Hackerbruecke" }, + { "begin": 960, "end": 990, "station": "Hbf" }, + { "begin": 1080, "end": 1110, "station": "Karlsplatz" }, + { "begin": 1185, "end": 1215, "station": "Marienplatz" }, + { "begin": 1290, "end": 1320, "station": "Isartor" }, + { "begin": 1410, "end": 1440, "station": "RosenheimerPlatz" } + ], + "t_0": 525, + "t_n": 1530, + "v_0": 0.0, + "v_n": 0.0 + }, + "S2Dachau": { + "entry": "Ost2Entry", + "exit": "LaimExitNymphenburg", + "stops": [ + { "begin": 510, "end": 540, "station": "RosenheimerPlatz" }, + { "begin": 630, "end": 660, "station": "Isartor" }, + { "begin": 735, "end": 765, "station": "Marienplatz" }, + { "begin": 840, "end": 870, "station": "Karlsplatz" }, + { "begin": 960, "end": 990, "station": "Hbf" }, + { "begin": 1065, "end": 1095, "station": "Hackerbruecke" }, + { "begin": 1170, "end": 1200, "station": "Donnersbergerbruecke" }, + { "begin": 1290, "end": 1320, "station": "Hirschgarten" }, + { "begin": 1410, "end": 1440, "station": "Laim" } + ], + "t_0": 420, + "t_n": 1485, + "v_0": 0.0, + "v_n": 20.0 + }, + "S2Erding": { + "entry": "LaimEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 360, "end": 390, "station": "Hirschgarten" }, + { "begin": 480, "end": 510, "station": "Donnersbergerbruecke" }, + { "begin": 585, "end": 615, "station": "Hackerbruecke" }, + { "begin": 705, "end": 735, "station": "Hbf" }, + { "begin": 825, "end": 855, "station": "Karlsplatz" }, + { "begin": 930, "end": 960, "station": "Marienplatz" }, + { "begin": 1035, "end": 1065, "station": "Isartor" }, + { "begin": 1155, "end": 1185, "station": "RosenheimerPlatz" } + ], + "t_0": 270, + "t_n": 1275, + "v_0": 0.0, + "v_n": 0.0 + }, + "S2Ost": { + "entry": "LaimEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 795, "end": 825, "station": "Hirschgarten" }, + { "begin": 915, "end": 945, "station": "Donnersbergerbruecke" }, + { "begin": 1020, "end": 1050, "station": "Hackerbruecke" }, + { "begin": 1140, "end": 1170, "station": "Hbf" }, + { "begin": 1260, "end": 1290, "station": "Karlsplatz" }, + { "begin": 1365, "end": 1395, "station": "Marienplatz" }, + { "begin": 1470, "end": 1500, "station": "Isartor" }, + { "begin": 1590, "end": 1620, "station": "RosenheimerPlatz" } + ], + "t_0": 705, + "t_n": 1710, + "v_0": 0.0, + "v_n": 0.0 + }, + "S2Petershausen": { + "entry": "Ost2Entry", + "exit": "LaimExitNymphenburg", + "stops": [ + { "begin": 90, "end": 120, "station": "RosenheimerPlatz" }, + { "begin": 210, "end": 240, "station": "Isartor" }, + { "begin": 315, "end": 345, "station": "Marienplatz" }, + { "begin": 420, "end": 450, "station": "Karlsplatz" }, + { "begin": 510, "end": 540, "station": "Hbf" }, + { "begin": 615, "end": 645, "station": "Hackerbruecke" }, + { "begin": 720, "end": 750, "station": "Donnersbergerbruecke" }, + { "begin": 840, "end": 870, "station": "Hirschgarten" }, + { "begin": 960, "end": 990, "station": "Laim" } + ], + "t_0": 0, + "t_n": 1035, + "v_0": 0.0, + "v_n": 20.0 + }, + "S3Deisenhofen": { + "entry": "PasingEntry", + "exit": "Ost4Exit", + "stops": [ + { "begin": 315, "end": 345, "station": "Laim" }, + { "begin": 435, "end": 465, "station": "Hirschgarten" }, + { "begin": 555, "end": 585, "station": "Donnersbergerbruecke" }, + { "begin": 660, "end": 690, "station": "Hackerbruecke" }, + { "begin": 780, "end": 810, "station": "Hbf" }, + { "begin": 900, "end": 930, "station": "Karlsplatz" }, + { "begin": 1005, "end": 1035, "station": "Marienplatz" }, + { "begin": 1110, "end": 1140, "station": "Isartor" }, + { "begin": 1230, "end": 1260, "station": "RosenheimerPlatz" } + ], + "t_0": 165, + "t_n": 1350, + "v_0": 0.0, + "v_n": 0.0 + }, + "S3Mammendorf": { + "entry": "Ost3Entry", + "exit": "PasingExit", + "stops": [ + { "begin": 420, "end": 450, "station": "RosenheimerPlatz" }, + { "begin": 540, "end": 570, "station": "Isartor" }, + { "begin": 645, "end": 675, "station": "Marienplatz" }, + { "begin": 750, "end": 780, "station": "Karlsplatz" }, + { "begin": 870, "end": 900, "station": "Hbf" }, + { "begin": 975, "end": 1005, "station": "Hackerbruecke" }, + { "begin": 1080, "end": 1110, "station": "Donnersbergerbruecke" }, + { "begin": 1200, "end": 1230, "station": "Hirschgarten" }, + { "begin": 1320, "end": 1350, "station": "Laim" } + ], + "t_0": 330, + "t_n": 1500, + "v_0": 0.0, + "v_n": 0.0 + }, + "S4Geltendorf": { + "entry": "Ost1Entry", + "exit": "PasingExit", + "stops": [ + { "begin": 600, "end": 630, "station": "RosenheimerPlatz" }, + { "begin": 720, "end": 750, "station": "Isartor" }, + { "begin": 825, "end": 855, "station": "Marienplatz" }, + { "begin": 930, "end": 960, "station": "Karlsplatz" }, + { "begin": 1050, "end": 1080, "station": "Hbf" }, + { "begin": 1155, "end": 1185, "station": "Hackerbruecke" }, + { "begin": 1260, "end": 1290, "station": "Donnersbergerbruecke" }, + { "begin": 1380, "end": 1410, "station": "Hirschgarten" }, + { "begin": 1500, "end": 1530, "station": "Laim" } + ], + "t_0": 510, + "t_n": 1680, + "v_0": 0.0, + "v_n": 0.0 + }, + "S4Grafing": { + "entry": "PasingEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 585, "end": 615, "station": "Laim" }, + { "begin": 705, "end": 735, "station": "Hirschgarten" }, + { "begin": 825, "end": 855, "station": "Donnersbergerbruecke" }, + { "begin": 930, "end": 960, "station": "Hackerbruecke" }, + { "begin": 1050, "end": 1080, "station": "Hbf" }, + { "begin": 1170, "end": 1200, "station": "Karlsplatz" }, + { "begin": 1275, "end": 1305, "station": "Marienplatz" }, + { "begin": 1380, "end": 1410, "station": "Isartor" }, + { "begin": 1500, "end": 1530, "station": "RosenheimerPlatz" } + ], + "t_0": 435, + "t_n": 1620, + "v_0": 0.0, + "v_n": 0.0 + }, + "S6Ebersberg": { + "entry": "PasingEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 150, "end": 180, "station": "Laim" }, + { "begin": 270, "end": 300, "station": "Hirschgarten" }, + { "begin": 390, "end": 420, "station": "Donnersbergerbruecke" }, + { "begin": 495, "end": 525, "station": "Hackerbruecke" }, + { "begin": 615, "end": 645, "station": "Hbf" }, + { "begin": 735, "end": 765, "station": "Karlsplatz" }, + { "begin": 840, "end": 870, "station": "Marienplatz" }, + { "begin": 945, "end": 975, "station": "Isartor" }, + { "begin": 1065, "end": 1095, "station": "RosenheimerPlatz" } + ], + "t_0": 0, + "t_n": 1185, + "v_0": 0.0, + "v_n": 0.0 + }, + "S6Tutzing": { + "entry": "Ost1Entry", + "exit": "PasingExit", + "stops": [ + { "begin": 180, "end": 210, "station": "RosenheimerPlatz" }, + { "begin": 300, "end": 330, "station": "Isartor" }, + { "begin": 405, "end": 435, "station": "Marienplatz" }, + { "begin": 510, "end": 540, "station": "Karlsplatz" }, + { "begin": 600, "end": 630, "station": "Hbf" }, + { "begin": 705, "end": 735, "station": "Hackerbruecke" }, + { "begin": 810, "end": 840, "station": "Donnersbergerbruecke" }, + { "begin": 930, "end": 960, "station": "Hirschgarten" }, + { "begin": 1050, "end": 1080, "station": "Laim" } + ], + "t_0": 90, + "t_n": 1230, + "v_0": 0.0, + "v_n": 0.0 + }, + "S7Aying": { + "entry": "DonnersbergerEntry", + "exit": "Ost4Exit", + "stops": [ + { "begin": 420, "end": 450, "station": "Hackerbruecke" }, + { "begin": 540, "end": 570, "station": "Hbf" }, + { "begin": 660, "end": 690, "station": "Karlsplatz" }, + { "begin": 765, "end": 795, "station": "Marienplatz" }, + { "begin": 870, "end": 900, "station": "Isartor" }, + { "begin": 990, "end": 1020, "station": "RosenheimerPlatz" } + ], + "t_0": 345, + "t_n": 1110, + "v_0": 0.0, + "v_n": 0.0 + }, + "S7Wolfratshausen": { + "entry": "Ost3Entry", + "exit": "DonnersbergerExit", + "stops": [ + { "begin": 255, "end": 285, "station": "RosenheimerPlatz" }, + { "begin": 375, "end": 405, "station": "Isartor" }, + { "begin": 480, "end": 510, "station": "Marienplatz" }, + { "begin": 585, "end": 615, "station": "Karlsplatz" }, + { "begin": 675, "end": 705, "station": "Hbf" }, + { "begin": 780, "end": 810, "station": "Hackerbruecke" } + ], + "t_0": 165, + "t_n": 885, + "v_0": 0.0, + "v_n": 0.0 + }, + "S8Airport": { + "entry": "PasingEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 405, "end": 435, "station": "Laim" }, + { "begin": 525, "end": 555, "station": "Hirschgarten" }, + { "begin": 645, "end": 675, "station": "Donnersbergerbruecke" }, + { "begin": 750, "end": 780, "station": "Hackerbruecke" }, + { "begin": 870, "end": 900, "station": "Hbf" }, + { "begin": 990, "end": 1020, "station": "Karlsplatz" }, + { "begin": 1095, "end": 1125, "station": "Marienplatz" }, + { "begin": 1200, "end": 1230, "station": "Isartor" }, + { "begin": 1320, "end": 1350, "station": "RosenheimerPlatz" } + ], + "t_0": 255, + "t_n": 1440, + "v_0": 0.0, + "v_n": 0.0 + }, + "S8Germering": { + "entry": "Ost1Entry", + "exit": "PasingExit", + "stops": [ + { "begin": 345, "end": 375, "station": "RosenheimerPlatz" }, + { "begin": 465, "end": 495, "station": "Isartor" }, + { "begin": 570, "end": 600, "station": "Marienplatz" }, + { "begin": 675, "end": 705, "station": "Karlsplatz" }, + { "begin": 765, "end": 795, "station": "Hbf" }, + { "begin": 870, "end": 915, "station": "Hackerbruecke" }, + { "begin": 990, "end": 1020, "station": "Donnersbergerbruecke" }, + { "begin": 1110, "end": 1140, "station": "Hirschgarten" }, + { "begin": 1230, "end": 1260, "station": "Laim" } + ], + "t_0": 255, + "t_n": 1410, + "v_0": 0.0, + "v_n": 0.0 + } +} diff --git a/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/stations.json b/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/stations.json new file mode 100644 index 000000000..2bf77e4b1 --- /dev/null +++ b/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/stations.json @@ -0,0 +1,38 @@ +{ + "Donnersbergerbruecke": [ + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"] + ], + "Hackerbruecke": [ + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke2R", "Hackerbruecke2L"] + ], + "Hbf": [ + ["Hbf1L", "Hbf1R"], + ["Hbf2R", "Hbf2L"] + ], + "Hirschgarten": [ + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten2R", "Hirschgarten2L"] + ], + "Isartor": [ + ["Isartor1L", "Isartor1R"], + ["Isartor2R", "Isartor2L"] + ], + "Karlsplatz": [ + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz2R", "Karlsplatz2L"] + ], + "Laim": [ + ["Laim1L", "Laim1R"], + ["Laim3R", "Laim3L"] + ], + "Marienplatz": [ + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz2R", "Marienplatz2L"] + ], + "RosenheimerPlatz": [ + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer2R", "Rosenheimer2L"] + ] +} diff --git a/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/trains.json b/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/trains.json new file mode 100644 index 000000000..29f0ce1f5 --- /dev/null +++ b/test/example-networks-sim-bidirec/Stammstrecke16Trains/timetable/trains.json @@ -0,0 +1,114 @@ +{ + "S1Freising": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S1Leuchtenbergring": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S2Dachau": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S2Erding": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S2Ost": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S2Petershausen": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S3Deisenhofen": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S3Mammendorf": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S4Geltendorf": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S4Grafing": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S6Ebersberg": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S6Tutzing": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S7Aying": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S7Wolfratshausen": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S8Airport": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S8Germering": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + } +} diff --git a/test/example-networks-sim-bidirec/readme.md b/test/example-networks-sim-bidirec/readme.md new file mode 100644 index 000000000..cf3c100bb --- /dev/null +++ b/test/example-networks-sim-bidirec/readme.md @@ -0,0 +1,6 @@ +## Explanation Bidirectional Networks + +The continuous simulator cannot export solutions back to the original network used to generate its unidirectional representation (from convertToUnidirec.py), +since information about directional edges is lost. +Thus we create a bidirectional version (has all reciprocal edges) of the original with convertToBidirec.py. +We can then export the simulator solution (TrainTrajectorySet) to a SolGeneralPerformanceOptimizationInstance for the bidirectional network using to_vss_solution(). diff --git a/test/example-networks-sim-unidirec/Overtake/network/successors_cpp.json b/test/example-networks-sim-unidirec/Overtake/network/successors_cpp.json new file mode 100644 index 000000000..120c85879 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake/network/successors_cpp.json @@ -0,0 +1,26 @@ +{ + "('switch_center', 'v6')": [["v6", "v7"]], + "('v1', 'v2')": [["v2", "v3"]], + "('v10', 'v11')": [], + "('v2', 'v3')": [ + ["v3", "v4"], + ["v3", "v4b"] + ], + "('v3', 'v4')": [["v4", "v5"]], + "('v3', 'v4b')": [["v4b", "v5b"]], + "('v4', 'v5')": [["v5", "v6"]], + "('v4b', 'v5b')": [["v5b", "v6b"]], + "('v5', 'v6')": [["v6", "v7"]], + "('v5b', 'v6b')": [ + ["v6b", "v7b"], + ["v6b", "switch_center"] + ], + "('v6', 'v7')": [["v7", "v8"]], + "('v6b', 'switch_center')": [["switch_center", "v6"]], + "('v6b', 'v7b')": [["v7b", "v8b"]], + "('v7', 'v8')": [["v8", "v9"]], + "('v7b', 'v8b')": [["v8b", "v9"]], + "('v8', 'v9')": [["v9", "v10"]], + "('v8b', 'v9')": [["v9", "v10"]], + "('v9', 'v10')": [["v10", "v11"]] +} diff --git a/test/example-networks-sim-unidirec/Overtake/network/tracks.graphml b/test/example-networks-sim-unidirec/Overtake/network/tracks.graphml new file mode 100644 index 000000000..a3775b3dd --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake/network/tracks.graphml @@ -0,0 +1,169 @@ + + + + + + + + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + True + 4475.0 + 80.0 + 100.0 + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + True + 675.0 + 80.0 + 100.0 + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 80.0 + 1.0 + + + True + 225.0 + 80.0 + 100.0 + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 80.0 + 1.0 + + + True + 4475.0 + 80.0 + 100.0 + + + True + 650.0 + 25.0 + 100.0 + + + False + 25.0 + 25.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + False + 13.0 + 25.0 + 1.0 + + + True + 250.0 + 25.0 + 100.0 + + + False + 25.0 + 25.0 + 1.0 + + + False + 13.0 + 25.0 + 1.0 + + + diff --git a/test/example-networks-sim-unidirec/Overtake/routes/routes.json b/test/example-networks-sim-unidirec/Overtake/routes/routes.json new file mode 100644 index 000000000..01fd1d9b0 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake/routes/routes.json @@ -0,0 +1,40 @@ +{ + "tr1": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4b"], + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "v7b"], + ["v7b", "v8b"], + ["v8b", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ], + "tr2": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4b"], + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "switch_center"], + ["switch_center", "v6"], + ["v6", "v7"], + ["v7", "v8"], + ["v8", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ], + "tr3": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4"], + ["v4", "v5"], + ["v5", "v6"], + ["v6", "v7"], + ["v7", "v8"], + ["v8", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ] +} diff --git a/test/example-networks-sim-unidirec/Overtake/timetable/schedules.json b/test/example-networks-sim-unidirec/Overtake/timetable/schedules.json new file mode 100644 index 000000000..2b4c9ea43 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake/timetable/schedules.json @@ -0,0 +1,29 @@ +{ + "tr1": { + "entry": "v1", + "exit": "v11", + "stops": [{ "begin": 270, "end": 330, "station": "Station" }], + "t_0": 0, + "t_n": 585, + "v_0": 25.0, + "v_n": 25.0 + }, + "tr2": { + "entry": "v1", + "exit": "v11", + "stops": null, + "t_0": 30, + "t_n": 465, + "v_0": 25.0, + "v_n": 25.0 + }, + "tr3": { + "entry": "v1", + "exit": "v11", + "stops": null, + "t_0": 165, + "t_n": 315, + "v_0": 25.0, + "v_n": 80.0 + } +} diff --git a/test/example-networks-sim-unidirec/Overtake/timetable/stations.json b/test/example-networks-sim-unidirec/Overtake/timetable/stations.json new file mode 100644 index 000000000..929feff08 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake/timetable/stations.json @@ -0,0 +1,8 @@ +{ + "Station": [ + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "v7b"], + ["v7b", "v8b"] + ] +} diff --git a/test/example-networks-sim-unidirec/Overtake/timetable/trains.json b/test/example-networks-sim-unidirec/Overtake/timetable/trains.json new file mode 100644 index 000000000..68aeec9d4 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake/timetable/trains.json @@ -0,0 +1,23 @@ +{ + "tr1": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 200, + "max_speed": 25.0, + "tim": true + }, + "tr2": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 150, + "max_speed": 25.0, + "tim": true + }, + "tr3": { + "acceleration": 2.0, + "deceleration": 2.0, + "length": 300, + "max_speed": 80.0, + "tim": true + } +} diff --git a/test/example-networks-sim-unidirec/Overtake_slow/network/successors_cpp.json b/test/example-networks-sim-unidirec/Overtake_slow/network/successors_cpp.json new file mode 100644 index 000000000..120c85879 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake_slow/network/successors_cpp.json @@ -0,0 +1,26 @@ +{ + "('switch_center', 'v6')": [["v6", "v7"]], + "('v1', 'v2')": [["v2", "v3"]], + "('v10', 'v11')": [], + "('v2', 'v3')": [ + ["v3", "v4"], + ["v3", "v4b"] + ], + "('v3', 'v4')": [["v4", "v5"]], + "('v3', 'v4b')": [["v4b", "v5b"]], + "('v4', 'v5')": [["v5", "v6"]], + "('v4b', 'v5b')": [["v5b", "v6b"]], + "('v5', 'v6')": [["v6", "v7"]], + "('v5b', 'v6b')": [ + ["v6b", "v7b"], + ["v6b", "switch_center"] + ], + "('v6', 'v7')": [["v7", "v8"]], + "('v6b', 'switch_center')": [["switch_center", "v6"]], + "('v6b', 'v7b')": [["v7b", "v8b"]], + "('v7', 'v8')": [["v8", "v9"]], + "('v7b', 'v8b')": [["v8b", "v9"]], + "('v8', 'v9')": [["v9", "v10"]], + "('v8b', 'v9')": [["v9", "v10"]], + "('v9', 'v10')": [["v10", "v11"]] +} diff --git a/test/example-networks-sim-unidirec/Overtake_slow/network/tracks.graphml b/test/example-networks-sim-unidirec/Overtake_slow/network/tracks.graphml new file mode 100644 index 000000000..06b93d00f --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake_slow/network/tracks.graphml @@ -0,0 +1,169 @@ + + + + + + + + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + True + 4475.0 + 80.0 + 100.0 + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + True + 675.0 + 80.0 + 100.0 + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 80.0 + 1.0 + + + True + 225.0 + 80.0 + 100.0 + + + False + 25.0 + 80.0 + 1.0 + + + False + 25.0 + 80.0 + 1.0 + + + True + 4475.0 + 80.0 + 100.0 + + + True + 650.0 + 25.0 + 100.0 + + + False + 25.0 + 25.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + True + 250.0 + 25.0 + 100.0 + + + False + 25.0 + 25.0 + 1.0 + + + False + 25.0 + 25.0 + 1.0 + + + diff --git a/test/example-networks-sim-unidirec/Overtake_slow/readme.md b/test/example-networks-sim-unidirec/Overtake_slow/readme.md new file mode 100644 index 000000000..12df08c80 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake_slow/readme.md @@ -0,0 +1,3 @@ +## Explanation Overtake_slow Network + +Shortest track length has been increased and max train speed decreasted to avoid trains skipping tracks. diff --git a/test/example-networks-sim-unidirec/Overtake_slow/routes/routes.json b/test/example-networks-sim-unidirec/Overtake_slow/routes/routes.json new file mode 100644 index 000000000..01fd1d9b0 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake_slow/routes/routes.json @@ -0,0 +1,40 @@ +{ + "tr1": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4b"], + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "v7b"], + ["v7b", "v8b"], + ["v8b", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ], + "tr2": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4b"], + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "switch_center"], + ["switch_center", "v6"], + ["v6", "v7"], + ["v7", "v8"], + ["v8", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ], + "tr3": [ + ["v1", "v2"], + ["v2", "v3"], + ["v3", "v4"], + ["v4", "v5"], + ["v5", "v6"], + ["v6", "v7"], + ["v7", "v8"], + ["v8", "v9"], + ["v9", "v10"], + ["v10", "v11"] + ] +} diff --git a/test/example-networks-sim-unidirec/Overtake_slow/timetable/schedules.json b/test/example-networks-sim-unidirec/Overtake_slow/timetable/schedules.json new file mode 100644 index 000000000..da41de5d4 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake_slow/timetable/schedules.json @@ -0,0 +1,29 @@ +{ + "tr1": { + "entry": "v1", + "exit": "v11", + "stops": [{ "begin": 270, "end": 330, "station": "Station" }], + "t_0": 0, + "t_n": 585, + "v_0": 24.0, + "v_n": 24.0 + }, + "tr2": { + "entry": "v1", + "exit": "v11", + "stops": null, + "t_0": 30, + "t_n": 465, + "v_0": 24.0, + "v_n": 24.0 + }, + "tr3": { + "entry": "v1", + "exit": "v11", + "stops": null, + "t_0": 165, + "t_n": 315, + "v_0": 24.0, + "v_n": 24.0 + } +} diff --git a/test/example-networks-sim-unidirec/Overtake_slow/timetable/stations.json b/test/example-networks-sim-unidirec/Overtake_slow/timetable/stations.json new file mode 100644 index 000000000..929feff08 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake_slow/timetable/stations.json @@ -0,0 +1,8 @@ +{ + "Station": [ + ["v4b", "v5b"], + ["v5b", "v6b"], + ["v6b", "v7b"], + ["v7b", "v8b"] + ] +} diff --git a/test/example-networks-sim-unidirec/Overtake_slow/timetable/trains.json b/test/example-networks-sim-unidirec/Overtake_slow/timetable/trains.json new file mode 100644 index 000000000..a0f01e850 --- /dev/null +++ b/test/example-networks-sim-unidirec/Overtake_slow/timetable/trains.json @@ -0,0 +1,23 @@ +{ + "tr1": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 200, + "max_speed": 24.0, + "tim": true + }, + "tr2": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 150, + "max_speed": 24.0, + "tim": true + }, + "tr3": { + "acceleration": 2.0, + "deceleration": 2.0, + "length": 300, + "max_speed": 24.0, + "tim": true + } +} diff --git a/test/example-networks-sim-unidirec/SimpleNetwork/network/successors_cpp.json b/test/example-networks-sim-unidirec/SimpleNetwork/network/successors_cpp.json new file mode 100644 index 000000000..9482da0f0 --- /dev/null +++ b/test/example-networks-sim-unidirec/SimpleNetwork/network/successors_cpp.json @@ -0,0 +1,32 @@ +{ + "('v10', 'v11')": [["v11", "v12"]], + "('v11', 'v12')": [ + ["v12", "v13b"], + ["v12", "v13c"] + ], + "('v12', 'v13b')": [["v13b", "v14b"]], + "('v12', 'v13c')": [["v13c", "v14c"]], + "('v13a', 'v12')": [["v11", "v12"]], + "('v13b', 'v14b')": [], + "('v13c', 'v14c')": [], + "('v14a', 'v13a')": [["v13a", "v12"]], + "('v1b', 'v2b')": [["v2b", "v3"]], + "('v1c', 'v2c')": [["v2c", "v3"]], + "('v2a', 'v1a')": [], + "('v2b', 'v3')": [["v3", "v4"]], + "('v2c', 'v3')": [["v3", "v4"]], + "('v3', 'v2a')": [["v2a", "v1a"]], + "('v3', 'v4')": [["v4", "v5"]], + "('v4', 'v5')": [["v5", "v6"]], + "('v5', 'v6')": [ + ["v6", "v7a"], + ["v6", "v7b"] + ], + "('v6', 'v7a')": [["v7a", "v8a"]], + "('v6', 'v7b')": [["v7b", "v8b"]], + "('v7a', 'v8a')": [["v8a", "v9"]], + "('v7b', 'v8b')": [["v8b", "v9"]], + "('v8a', 'v9')": [["v9", "v10"]], + "('v8b', 'v9')": [["v9", "v10"]], + "('v9', 'v10')": [["v10", "v11"]] +} diff --git a/test/example-networks-sim-unidirec/SimpleNetwork/network/tracks.graphml b/test/example-networks-sim-unidirec/SimpleNetwork/network/tracks.graphml new file mode 100644 index 000000000..f81e1ffc4 --- /dev/null +++ b/test/example-networks-sim-unidirec/SimpleNetwork/network/tracks.graphml @@ -0,0 +1,226 @@ + + + + + + + + + +2 + + +2 + + +2 + + +2 + + +2 + + +2 + + +0 + + +2 + + +2 + + +0 + + +2 + + +2 + + +2 + + +2 + + +0 + + +2 + + +2 + + +0 + + +2 + + +2 + + +2 + + +2 + + +2 + + +2 + + +true +475 +75 +100 + + +true +475 +25 +100 + + +false +25 +75 +1 + + +false +25 +25 +1 + + +false +25 +75 +1 + + +true +9950 +75 +100 + + +false +25 +75 +1 + + +false +25 +25 +1 + + +false +25 +75 +1 + + +true +1950 +25 +100 + + +true +1950 +75 +100 + + +false +25 +25 +1 + + +false +25 +75 +1 + + +false +25 +75 +1 + + +true +9950 +75 +100 + + +false +25 +75 +1 + + +false +25 +75 +1 + + +false +25 +25 +1 + + +true +475 +75 +100 + + +true +475 +25 +100 + + +true +475 +25 +100 + + +false +25 +25 +1 + + +false +25 +25 +1 + + +true +475 +25 +100 + + + diff --git a/test/example-networks-sim-unidirec/SimpleNetwork/routes/routes.json b/test/example-networks-sim-unidirec/SimpleNetwork/routes/routes.json new file mode 100644 index 000000000..f0939e2c3 --- /dev/null +++ b/test/example-networks-sim-unidirec/SimpleNetwork/routes/routes.json @@ -0,0 +1,62 @@ +{ + "tr1lr": [ + ["v1b", "v2b"], + ["v2b", "v3"], + ["v3", "v4"], + ["v4", "v5"], + ["v5", "v6"], + ["v6", "v7b"], + ["v7b", "v8b"], + ["v8b", "v9"], + ["v9", "v10"], + ["v10", "v11"], + ["v11", "v12"], + ["v12", "v13b"], + ["v13b", "v14b"] + ], + "tr1rl": [ + ["v14b", "v13b"], + ["v13b", "v12"], + ["v12", "v11"], + ["v11", "v10"], + ["v10", "v9"], + ["v9", "v8a"], + ["v8a", "v7a"], + ["v7a", "v6"], + ["v6", "v5"], + ["v5", "v4"], + ["v4", "v3"], + ["v3", "v2b"], + ["v2b", "v1b"] + ], + "tr2lr": [ + ["v1c", "v2c"], + ["v2c", "v3"], + ["v3", "v4"], + ["v4", "v5"], + ["v5", "v6"], + ["v6", "v7b"], + ["v7b", "v8b"], + ["v8b", "v9"], + ["v9", "v10"], + ["v10", "v11"], + ["v11", "v12"], + ["v12", "v13c"], + ["v13c", "v14c"] + ], + "tr2rl": [ + ["v14a", "v13a"], + ["v13a", "v12"], + ["v12", "v11"], + ["v11", "v10"], + ["v10", "v9"], + ["v9", "v8a"], + ["v8a", "v7a"], + ["v7a", "v6"], + ["v6", "v5"], + ["v5", "v4"], + ["v4", "v3"], + ["v3", "v2a"], + ["v2a", "v1a"] + ] +} diff --git a/test/example-networks-sim-unidirec/SimpleNetwork/timetable/schedules.json b/test/example-networks-sim-unidirec/SimpleNetwork/timetable/schedules.json new file mode 100644 index 000000000..5e165d6a5 --- /dev/null +++ b/test/example-networks-sim-unidirec/SimpleNetwork/timetable/schedules.json @@ -0,0 +1,44 @@ +{ + "tr1lr": { + "entry": "v1b", + "exit": "v14b", + "stops": null, + "t_0": 180, + "t_n": 960, + "v_0": 50.0, + "v_n": 50.0 + }, + "tr1rl": { + "entry": "v14b", + "exit": "v1b", + "stops": null, + "t_0": 180, + "t_n": 960, + "v_0": 50.0, + "v_n": 50.0 + }, + "tr2lr": { + "entry": "v1c", + "exit": "v14c", + "stops": [ + { "begin": 30, "end": 90, "station": "StationLeft" }, + { "begin": 900, "end": 960, "station": "StationRight" } + ], + "t_0": 0, + "t_n": 990, + "v_0": 25.0, + "v_n": 25.0 + }, + "tr2rl": { + "entry": "v14a", + "exit": "v1a", + "stops": [ + { "begin": 30, "end": 90, "station": "StationRight" }, + { "begin": 900, "end": 960, "station": "StationLeft" } + ], + "t_0": 0, + "t_n": 990, + "v_0": 25.0, + "v_n": 25.0 + } +} diff --git a/test/example-networks-sim-unidirec/SimpleNetwork/timetable/stations.json b/test/example-networks-sim-unidirec/SimpleNetwork/timetable/stations.json new file mode 100644 index 000000000..59b806ff8 --- /dev/null +++ b/test/example-networks-sim-unidirec/SimpleNetwork/timetable/stations.json @@ -0,0 +1,12 @@ +{ + "StationLeft": [ + ["v1b", "v2b"], + ["v1c", "v2c"], + ["v2a", "v1a"] + ], + "StationRight": [ + ["v13b", "v14b"], + ["v13c", "v14c"], + ["v14a", "v13a"] + ] +} diff --git a/test/example-networks-sim-unidirec/SimpleNetwork/timetable/trains.json b/test/example-networks-sim-unidirec/SimpleNetwork/timetable/trains.json new file mode 100644 index 000000000..625124d08 --- /dev/null +++ b/test/example-networks-sim-unidirec/SimpleNetwork/timetable/trains.json @@ -0,0 +1,30 @@ +{ + "tr1lr": { + "acceleration": 2.0, + "deceleration": 2.0, + "length": 400, + "max_speed": 50.0, + "tim": true + }, + "tr1rl": { + "acceleration": 2.0, + "deceleration": 2.0, + "length": 400, + "max_speed": 50.0, + "tim": true + }, + "tr2lr": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 400, + "max_speed": 34.0, + "tim": true + }, + "tr2rl": { + "acceleration": 1.5, + "deceleration": 1.5, + "length": 400, + "max_speed": 34.0, + "tim": true + } +} diff --git a/test/example-networks-sim-unidirec/Stammstrecke16Trains/network/successors_cpp.json b/test/example-networks-sim-unidirec/Stammstrecke16Trains/network/successors_cpp.json new file mode 100644 index 000000000..ef0b02510 --- /dev/null +++ b/test/example-networks-sim-unidirec/Stammstrecke16Trains/network/successors_cpp.json @@ -0,0 +1,131 @@ +{ + "('DonnersbergerEntry', 'HackerbrueckeSwitchEntry')": [ + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"] + ], + "('Donnersbergerbruecke1L', 'Donnersbergerbruecke1R')": [ + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"] + ], + "('Donnersbergerbruecke1R', 'HackerbrueckeSwitchEntry')": [ + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"] + ], + "('Donnersbergerbruecke2L', 'Hirschgarten2R')": [ + ["Hirschgarten2R", "Hirschgarten2L"] + ], + "('Donnersbergerbruecke2R', 'Donnersbergerbruecke2L')": [ + ["Donnersbergerbruecke2L", "Hirschgarten2R"] + ], + "('Hackerbruecke1L', 'Hackerbruecke1R')": [["Hackerbruecke1R", "Hbf1L"]], + "('Hackerbruecke1R', 'Hbf1L')": [["Hbf1L", "Hbf1R"]], + "('Hackerbruecke2L', 'HackerbrueckeSwitch3')": [ + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitchC", "HackerbrueckeSwitch3"] + ], + "('Hackerbruecke2R', 'Hackerbruecke2L')": [ + ["Hackerbruecke2L", "HackerbrueckeSwitch3"] + ], + "('HackerbrueckeSwitch1', 'HackerbrueckeSwitch2')": [ + ["HackerbrueckeSwitch2", "Hackerbruecke1L"] + ], + "('HackerbrueckeSwitch2', 'Hackerbruecke1L')": [ + ["Hackerbruecke1L", "Hackerbruecke1R"] + ], + "('HackerbrueckeSwitch3', 'HackerbrueckeSwitch4')": [ + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"] + ], + "('HackerbrueckeSwitch4', 'HackerbrueckeSwitchExit')": [ + ["HackerbrueckeSwitchExit", "DonnersbergerExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"] + ], + "('HackerbrueckeSwitchC', 'HackerbrueckeSwitch1')": [], + "('HackerbrueckeSwitchC', 'HackerbrueckeSwitch2')": [ + ["HackerbrueckeSwitch2", "Hackerbruecke1L"] + ], + "('HackerbrueckeSwitchC', 'HackerbrueckeSwitch3')": [], + "('HackerbrueckeSwitchC', 'HackerbrueckeSwitch4')": [ + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"] + ], + "('HackerbrueckeSwitchEntry', 'HackerbrueckeSwitch1')": [ + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitchC", "HackerbrueckeSwitch1"] + ], + "('HackerbrueckeSwitchExit', 'DonnersbergerExit')": [], + "('HackerbrueckeSwitchExit', 'Donnersbergerbruecke2R')": [ + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"] + ], + "('Hbf1L', 'Hbf1R')": [["Hbf1R", "Karlsplatz1L"]], + "('Hbf1R', 'Karlsplatz1L')": [["Karlsplatz1L", "Karlsplatz1R"]], + "('Hbf2L', 'Hackerbruecke2R')": [["Hackerbruecke2R", "Hackerbruecke2L"]], + "('Hbf2R', 'Hbf2L')": [["Hbf2L", "Hackerbruecke2R"]], + "('Hirschgarten1L', 'Hirschgarten1R')": [ + ["Hirschgarten1R", "Donnersbergerbruecke1L"] + ], + "('Hirschgarten1R', 'Donnersbergerbruecke1L')": [ + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"] + ], + "('Hirschgarten2L', 'Laim3R')": [["Laim3R", "Laim3L"]], + "('Hirschgarten2R', 'Hirschgarten2L')": [["Hirschgarten2L", "Laim3R"]], + "('Isartor1L', 'IsartorSwitchLR')": [["IsartorSwitchRL", "IsartorSwitchLR"]], + "('Isartor1R', 'Isartor1L')": [["Isartor1L", "IsartorSwitchLR"]], + "('Isartor2L', 'IsartorSwitchRL')": [["IsartorSwitchRL", "Marienplatz2R"]], + "('Isartor2R', 'Isartor2L')": [["Isartor2L", "IsartorSwitchRL"]], + "('IsartorSwitchRL', 'IsartorSwitchLR')": [["Isartor1L", "IsartorSwitchLR"]], + "('IsartorSwitchRL', 'Marienplatz2R')": [["Marienplatz2R", "Marienplatz2L"]], + "('IsartorSwitchR_LR', 'Isartor1R')": [["Isartor1R", "Isartor1L"]], + "('IsartorSwitchR_LR', 'Rosenheimer1L')": [ + ["Rosenheimer1L", "Rosenheimer1R"] + ], + "('IsartorSwitchR_RL', 'Isartor2R')": [["Isartor2R", "Isartor2L"]], + "('IsartorSwitchR_RL', 'IsartorSwitchR_LR')": [ + ["IsartorSwitchR_LR", "Isartor1R"] + ], + "('Karlsplatz1L', 'Karlsplatz1R')": [["Karlsplatz1R", "Marienplatz1L"]], + "('Karlsplatz1R', 'Marienplatz1L')": [["Marienplatz1L", "Marienplatz1R"]], + "('Karlsplatz2L', 'Hbf2R')": [["Hbf2R", "Hbf2L"]], + "('Karlsplatz2R', 'Karlsplatz2L')": [["Karlsplatz2L", "Hbf2R"]], + "('Laim1L', 'Laim1R')": [["Laim1R", "LaimSwitchHirschgarten"]], + "('Laim1R', 'LaimSwitchHirschgarten')": [ + ["LaimSwitchHirschgarten", "Hirschgarten1L"] + ], + "('Laim3L', 'LaimSwitchNymphenburg')": [ + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["LaimSwitchNymphenburg", "LaimExitNymphenburg"] + ], + "('Laim3R', 'Laim3L')": [["Laim3L", "LaimSwitchNymphenburg"]], + "('LaimEntry', 'LaimSwitchHirschgarten')": [ + ["LaimSwitchHirschgarten", "Hirschgarten1L"] + ], + "('LaimSwitchHirschgarten', 'Hirschgarten1L')": [ + ["Hirschgarten1L", "Hirschgarten1R"] + ], + "('LaimSwitchNymphenburg', 'LaimExitNymphenburg')": [], + "('LaimSwitchNymphenburg', 'PasingSwitch2')": [ + ["PasingSwitch2", "PasingExit"] + ], + "('Marienplatz1L', 'Marienplatz1R')": [["Marienplatz1R", "IsartorSwitchLR"]], + "('Marienplatz1R', 'IsartorSwitchLR')": [["Isartor1L", "IsartorSwitchLR"]], + "('Marienplatz2L', 'Karlsplatz2R')": [["Karlsplatz2R", "Karlsplatz2L"]], + "('Marienplatz2R', 'Marienplatz2L')": [["Marienplatz2L", "Karlsplatz2R"]], + "('Ost1Entry', 'OstSwitch1_RL')": [["OstSwitch1_RL", "Rosenheimer2R"]], + "('Ost2Entry', 'OstSwitch2_RL')": [["OstSwitch2_RL", "OstSwitch1_RL"]], + "('Ost3Entry', 'OstSwitch3_RL')": [["OstSwitch3_RL", "OstSwitch2_RL"]], + "('OstSwitch1_RL', 'Rosenheimer2R')": [["Rosenheimer2R", "Rosenheimer2L"]], + "('OstSwitch2_RL', 'OstSwitch1_RL')": [["OstSwitch1_RL", "Rosenheimer2R"]], + "('OstSwitch3_RL', 'OstSwitch2_RL')": [["OstSwitch2_RL", "OstSwitch1_RL"]], + "('OstSwitch4_LR', 'Ost4Exit')": [], + "('OstSwitch5_LR', 'Ost5Exit')": [], + "('OstSwitch5_LR', 'OstSwitch4_LR')": [["OstSwitch4_LR", "Ost4Exit"]], + "('PasingEntry', 'PasingSwitch1')": [["PasingSwitch1", "Laim1L"]], + "('PasingSwitch1', 'Laim1L')": [["Laim1L", "Laim1R"]], + "('PasingSwitch2', 'PasingExit')": [], + "('PasingSwitch2', 'PasingSwitch1')": [["PasingSwitch1", "Laim1L"]], + "('Rosenheimer1L', 'Rosenheimer1R')": [["Rosenheimer1R", "OstSwitch5_LR"]], + "('Rosenheimer1R', 'OstSwitch5_LR')": [ + ["OstSwitch5_LR", "OstSwitch4_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "('Rosenheimer2L', 'IsartorSwitchR_RL')": [ + ["IsartorSwitchR_RL", "IsartorSwitchR_LR"], + ["IsartorSwitchR_RL", "Isartor2R"] + ], + "('Rosenheimer2R', 'Rosenheimer2L')": [["Rosenheimer2L", "IsartorSwitchR_RL"]] +} diff --git a/test/example-networks-sim-unidirec/Stammstrecke16Trains/network/tracks.graphml b/test/example-networks-sim-unidirec/Stammstrecke16Trains/network/tracks.graphml new file mode 100644 index 000000000..7011dd8c7 --- /dev/null +++ b/test/example-networks-sim-unidirec/Stammstrecke16Trains/network/tracks.graphml @@ -0,0 +1,637 @@ + + + + + + + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 0 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + True + 280.0 + 33.3333 + 50.0 + + + True + 2812.0 + 33.3333 + 50.0 + + + True + 160.0 + 33.3333 + 50.0 + + + False + 120.0 + 22.2222 + 50.0 + + + True + 210.0 + 33.3333 + 25.0 + + + True + 370.0 + 33.3333 + 50.0 + + + True + 200.0 + 27.7778 + 50.0 + + + True + 200.0 + 27.7778 + 50.0 + + + True + 210.0 + 33.3333 + 25.0 + + + True + 2562.0 + 33.3333 + 50.0 + + + False + 30.0 + 27.7778 + 50.0 + + + True + 692.0 + 27.7778 + 50.0 + + + True + 205.0 + 27.7778 + 25.0 + + + True + 892.0 + 27.7778 + 50.0 + + + True + 1095.0 + 27.7778 + 50.0 + + + True + 205.0 + 27.7778 + 25.0 + + + True + 205.0 + 27.7778 + 25.0 + + + True + 1095.0 + 27.7778 + 50.0 + + + True + 210.0 + 27.7778 + 50.0 + + + True + 205.0 + 27.7778 + 25.0 + + + False + 150.0 + 27.7778 + 50.0 + + + False + 40.0 + 27.7778 + 50.0 + + + False + 150.0 + 27.7778 + 50.0 + + + True + 294.0 + 27.7778 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + False + 75.0 + 22.2222 + 50.0 + + + True + 207.0 + 27.7778 + 25.0 + + + False + 40.0 + 27.7778 + 50.0 + + + True + 591.0 + 22.2222 + 50.0 + + + True + 207.0 + 27.7778 + 25.0 + + + True + 294.0 + 27.7778 + 50.0 + + + True + 210.0 + 27.7778 + 50.0 + + + True + 210.0 + 27.7778 + 50.0 + + + True + 210.0 + 27.7778 + 50.0 + + + True + 210.0 + 22.2222 + 25.0 + + + True + 591.0 + 22.2222 + 50.0 + + + True + 292.0 + 22.2222 + 50.0 + + + True + 210.0 + 22.2222 + 25.0 + + + True + 206.0 + 22.2222 + 25.0 + + + True + 292.0 + 22.2222 + 50.0 + + + True + 494.0 + 22.2222 + 50.0 + + + True + 206.0 + 22.2222 + 25.0 + + + True + 205.0 + 22.2222 + 25.0 + + + True + 494.0 + 22.2222 + 50.0 + + + True + 393.0 + 22.2222 + 50.0 + + + True + 205.0 + 22.2222 + 25.0 + + + True + 343.0 + 22.2222 + 50.0 + + + False + 50.0 + 16.6667 + 50.0 + + + False + 100.0 + 22.2222 + 50.0 + + + False + 150.0 + 22.2222 + 50.0 + + + True + 209.0 + 22.2222 + 25.0 + + + True + 209.0 + 22.2222 + 25.0 + + + True + 592.0 + 22.2222 + 50.0 + + + False + 100.0 + 22.2222 + 50.0 + + + False + 150.0 + 22.2222 + 50.0 + + + False + 50.0 + 16.6667 + 50.0 + + + True + 206.0 + 22.2222 + 25.0 + + + True + 542.0 + 22.2222 + 50.0 + + + True + 792.0 + 22.2222 + 50.0 + + + True + 206.0 + 22.2222 + 25.0 + + + False + 40.0 + 22.2222 + 50.0 + + + False + 60.0 + 22.2222 + 50.0 + + + False + 100.0 + 22.2222 + 50.0 + + + True + 752.0 + 22.2222 + 50.0 + + + False + 40.0 + 22.2222 + 50.0 + + + False + 60.0 + 22.2222 + 50.0 + + + False + 140.0 + 22.2222 + 50.0 + + + False + 100.0 + 22.2222 + 50.0 + + + False + 40.0 + 22.2222 + 50.0 + + + diff --git a/test/example-networks-sim-unidirec/Stammstrecke16Trains/routes/routes.json b/test/example-networks-sim-unidirec/Stammstrecke16Trains/routes/routes.json new file mode 100644 index 000000000..ad1a6eb52 --- /dev/null +++ b/test/example-networks-sim-unidirec/Stammstrecke16Trains/routes/routes.json @@ -0,0 +1,448 @@ +{ + "S1Freising": [ + ["Ost2Entry", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "LaimExitNymphenburg"] + ], + "S1Leuchtenbergring": [ + ["LaimEntry", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["Isartor1L", "IsartorSwitchLR"], + ["Isartor1R", "Isartor1L"], + ["IsartorSwitchR_LR", "Isartor1R"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "OstSwitch4_LR"], + ["OstSwitch4_LR", "Ost4Exit"] + ], + "S2Dachau": [ + ["Ost2Entry", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "LaimExitNymphenburg"] + ], + "S2Erding": [ + ["LaimEntry", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["Isartor1L", "IsartorSwitchLR"], + ["Isartor1R", "Isartor1L"], + ["IsartorSwitchR_LR", "Isartor1R"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S2Ost": [ + ["LaimEntry", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["Isartor1L", "IsartorSwitchLR"], + ["Isartor1R", "Isartor1L"], + ["IsartorSwitchR_LR", "Isartor1R"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S2Petershausen": [ + ["Ost2Entry", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "LaimExitNymphenburg"] + ], + "S3Deisenhofen": [ + ["PasingEntry", "PasingSwitch1"], + ["PasingSwitch1", "Laim1L"], + ["Laim1L", "Laim1R"], + ["Laim1R", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["Isartor1L", "IsartorSwitchLR"], + ["Isartor1R", "Isartor1L"], + ["IsartorSwitchR_LR", "Isartor1R"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "OstSwitch4_LR"], + ["OstSwitch4_LR", "Ost4Exit"] + ], + "S3Mammendorf": [ + ["Ost3Entry", "OstSwitch3_RL"], + ["OstSwitch3_RL", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["PasingSwitch2", "PasingExit"] + ], + "S4Geltendorf": [ + ["Ost1Entry", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["PasingSwitch2", "PasingExit"] + ], + "S4Grafing": [ + ["PasingEntry", "PasingSwitch1"], + ["PasingSwitch1", "Laim1L"], + ["Laim1L", "Laim1R"], + ["Laim1R", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["Isartor1L", "IsartorSwitchLR"], + ["Isartor1R", "Isartor1L"], + ["IsartorSwitchR_LR", "Isartor1R"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S6Ebersberg": [ + ["PasingEntry", "PasingSwitch1"], + ["PasingSwitch1", "Laim1L"], + ["Laim1L", "Laim1R"], + ["Laim1R", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["Isartor1L", "IsartorSwitchLR"], + ["Isartor1R", "Isartor1L"], + ["IsartorSwitchR_LR", "Isartor1R"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S6Tutzing": [ + ["Ost1Entry", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["PasingSwitch2", "PasingExit"] + ], + "S7Aying": [ + ["DonnersbergerEntry", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["Isartor1L", "IsartorSwitchLR"], + ["Isartor1R", "Isartor1L"], + ["IsartorSwitchR_LR", "Isartor1R"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "OstSwitch4_LR"], + ["OstSwitch4_LR", "Ost4Exit"] + ], + "S7Wolfratshausen": [ + ["Ost3Entry", "OstSwitch3_RL"], + ["OstSwitch3_RL", "OstSwitch2_RL"], + ["OstSwitch2_RL", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "DonnersbergerExit"] + ], + "S8Airport": [ + ["PasingEntry", "PasingSwitch1"], + ["PasingSwitch1", "Laim1L"], + ["Laim1L", "Laim1R"], + ["Laim1R", "LaimSwitchHirschgarten"], + ["LaimSwitchHirschgarten", "Hirschgarten1L"], + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten1R", "Donnersbergerbruecke1L"], + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke1R", "HackerbrueckeSwitchEntry"], + ["HackerbrueckeSwitchEntry", "HackerbrueckeSwitch1"], + ["HackerbrueckeSwitch1", "HackerbrueckeSwitch2"], + ["HackerbrueckeSwitch2", "Hackerbruecke1L"], + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke1R", "Hbf1L"], + ["Hbf1L", "Hbf1R"], + ["Hbf1R", "Karlsplatz1L"], + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz1R", "Marienplatz1L"], + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz1R", "IsartorSwitchLR"], + ["Isartor1L", "IsartorSwitchLR"], + ["Isartor1R", "Isartor1L"], + ["IsartorSwitchR_LR", "Isartor1R"], + ["IsartorSwitchR_LR", "Rosenheimer1L"], + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer1R", "OstSwitch5_LR"], + ["OstSwitch5_LR", "Ost5Exit"] + ], + "S8Germering": [ + ["Ost1Entry", "OstSwitch1_RL"], + ["OstSwitch1_RL", "Rosenheimer2R"], + ["Rosenheimer2R", "Rosenheimer2L"], + ["Rosenheimer2L", "IsartorSwitchR_RL"], + ["IsartorSwitchR_RL", "Isartor2R"], + ["Isartor2R", "Isartor2L"], + ["Isartor2L", "IsartorSwitchRL"], + ["IsartorSwitchRL", "Marienplatz2R"], + ["Marienplatz2R", "Marienplatz2L"], + ["Marienplatz2L", "Karlsplatz2R"], + ["Karlsplatz2R", "Karlsplatz2L"], + ["Karlsplatz2L", "Hbf2R"], + ["Hbf2R", "Hbf2L"], + ["Hbf2L", "Hackerbruecke2R"], + ["Hackerbruecke2R", "Hackerbruecke2L"], + ["Hackerbruecke2L", "HackerbrueckeSwitch3"], + ["HackerbrueckeSwitch3", "HackerbrueckeSwitch4"], + ["HackerbrueckeSwitch4", "HackerbrueckeSwitchExit"], + ["HackerbrueckeSwitchExit", "Donnersbergerbruecke2R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"], + ["Donnersbergerbruecke2L", "Hirschgarten2R"], + ["Hirschgarten2R", "Hirschgarten2L"], + ["Hirschgarten2L", "Laim3R"], + ["Laim3R", "Laim3L"], + ["Laim3L", "LaimSwitchNymphenburg"], + ["LaimSwitchNymphenburg", "PasingSwitch2"], + ["PasingSwitch2", "PasingExit"] + ] +} diff --git a/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/schedules.json b/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/schedules.json new file mode 100644 index 000000000..e93c25d9c --- /dev/null +++ b/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/schedules.json @@ -0,0 +1,297 @@ +{ + "S1Freising": { + "entry": "Ost2Entry", + "exit": "LaimExitNymphenburg", + "stops": [ + { "begin": 690, "end": 720, "station": "RosenheimerPlatz" }, + { "begin": 810, "end": 840, "station": "Isartor" }, + { "begin": 915, "end": 945, "station": "Marienplatz" }, + { "begin": 1020, "end": 1050, "station": "Karlsplatz" }, + { "begin": 1140, "end": 1170, "station": "Hbf" }, + { "begin": 1245, "end": 1275, "station": "Hackerbruecke" }, + { "begin": 1350, "end": 1380, "station": "Donnersbergerbruecke" }, + { "begin": 1470, "end": 1500, "station": "Hirschgarten" }, + { "begin": 1590, "end": 1620, "station": "Laim" } + ], + "t_0": 600, + "t_n": 1665, + "v_0": 0.0, + "v_n": 20.0 + }, + "S1Leuchtenbergring": { + "entry": "LaimEntry", + "exit": "Ost4Exit", + "stops": [ + { "begin": 615, "end": 645, "station": "Hirschgarten" }, + { "begin": 735, "end": 765, "station": "Donnersbergerbruecke" }, + { "begin": 840, "end": 870, "station": "Hackerbruecke" }, + { "begin": 960, "end": 990, "station": "Hbf" }, + { "begin": 1080, "end": 1110, "station": "Karlsplatz" }, + { "begin": 1185, "end": 1215, "station": "Marienplatz" }, + { "begin": 1290, "end": 1320, "station": "Isartor" }, + { "begin": 1410, "end": 1440, "station": "RosenheimerPlatz" } + ], + "t_0": 525, + "t_n": 1530, + "v_0": 0.0, + "v_n": 0.0 + }, + "S2Dachau": { + "entry": "Ost2Entry", + "exit": "LaimExitNymphenburg", + "stops": [ + { "begin": 510, "end": 540, "station": "RosenheimerPlatz" }, + { "begin": 630, "end": 660, "station": "Isartor" }, + { "begin": 735, "end": 765, "station": "Marienplatz" }, + { "begin": 840, "end": 870, "station": "Karlsplatz" }, + { "begin": 960, "end": 990, "station": "Hbf" }, + { "begin": 1065, "end": 1095, "station": "Hackerbruecke" }, + { "begin": 1170, "end": 1200, "station": "Donnersbergerbruecke" }, + { "begin": 1290, "end": 1320, "station": "Hirschgarten" }, + { "begin": 1410, "end": 1440, "station": "Laim" } + ], + "t_0": 420, + "t_n": 1485, + "v_0": 0.0, + "v_n": 20.0 + }, + "S2Erding": { + "entry": "LaimEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 360, "end": 390, "station": "Hirschgarten" }, + { "begin": 480, "end": 510, "station": "Donnersbergerbruecke" }, + { "begin": 585, "end": 615, "station": "Hackerbruecke" }, + { "begin": 705, "end": 735, "station": "Hbf" }, + { "begin": 825, "end": 855, "station": "Karlsplatz" }, + { "begin": 930, "end": 960, "station": "Marienplatz" }, + { "begin": 1035, "end": 1065, "station": "Isartor" }, + { "begin": 1155, "end": 1185, "station": "RosenheimerPlatz" } + ], + "t_0": 270, + "t_n": 1275, + "v_0": 0.0, + "v_n": 0.0 + }, + "S2Ost": { + "entry": "LaimEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 795, "end": 825, "station": "Hirschgarten" }, + { "begin": 915, "end": 945, "station": "Donnersbergerbruecke" }, + { "begin": 1020, "end": 1050, "station": "Hackerbruecke" }, + { "begin": 1140, "end": 1170, "station": "Hbf" }, + { "begin": 1260, "end": 1290, "station": "Karlsplatz" }, + { "begin": 1365, "end": 1395, "station": "Marienplatz" }, + { "begin": 1470, "end": 1500, "station": "Isartor" }, + { "begin": 1590, "end": 1620, "station": "RosenheimerPlatz" } + ], + "t_0": 705, + "t_n": 1710, + "v_0": 0.0, + "v_n": 0.0 + }, + "S2Petershausen": { + "entry": "Ost2Entry", + "exit": "LaimExitNymphenburg", + "stops": [ + { "begin": 90, "end": 120, "station": "RosenheimerPlatz" }, + { "begin": 210, "end": 240, "station": "Isartor" }, + { "begin": 315, "end": 345, "station": "Marienplatz" }, + { "begin": 420, "end": 450, "station": "Karlsplatz" }, + { "begin": 510, "end": 540, "station": "Hbf" }, + { "begin": 615, "end": 645, "station": "Hackerbruecke" }, + { "begin": 720, "end": 750, "station": "Donnersbergerbruecke" }, + { "begin": 840, "end": 870, "station": "Hirschgarten" }, + { "begin": 960, "end": 990, "station": "Laim" } + ], + "t_0": 0, + "t_n": 1035, + "v_0": 0.0, + "v_n": 20.0 + }, + "S3Deisenhofen": { + "entry": "PasingEntry", + "exit": "Ost4Exit", + "stops": [ + { "begin": 315, "end": 345, "station": "Laim" }, + { "begin": 435, "end": 465, "station": "Hirschgarten" }, + { "begin": 555, "end": 585, "station": "Donnersbergerbruecke" }, + { "begin": 660, "end": 690, "station": "Hackerbruecke" }, + { "begin": 780, "end": 810, "station": "Hbf" }, + { "begin": 900, "end": 930, "station": "Karlsplatz" }, + { "begin": 1005, "end": 1035, "station": "Marienplatz" }, + { "begin": 1110, "end": 1140, "station": "Isartor" }, + { "begin": 1230, "end": 1260, "station": "RosenheimerPlatz" } + ], + "t_0": 165, + "t_n": 1350, + "v_0": 0.0, + "v_n": 0.0 + }, + "S3Mammendorf": { + "entry": "Ost3Entry", + "exit": "PasingExit", + "stops": [ + { "begin": 420, "end": 450, "station": "RosenheimerPlatz" }, + { "begin": 540, "end": 570, "station": "Isartor" }, + { "begin": 645, "end": 675, "station": "Marienplatz" }, + { "begin": 750, "end": 780, "station": "Karlsplatz" }, + { "begin": 870, "end": 900, "station": "Hbf" }, + { "begin": 975, "end": 1005, "station": "Hackerbruecke" }, + { "begin": 1080, "end": 1110, "station": "Donnersbergerbruecke" }, + { "begin": 1200, "end": 1230, "station": "Hirschgarten" }, + { "begin": 1320, "end": 1350, "station": "Laim" } + ], + "t_0": 330, + "t_n": 1500, + "v_0": 0.0, + "v_n": 0.0 + }, + "S4Geltendorf": { + "entry": "Ost1Entry", + "exit": "PasingExit", + "stops": [ + { "begin": 600, "end": 630, "station": "RosenheimerPlatz" }, + { "begin": 720, "end": 750, "station": "Isartor" }, + { "begin": 825, "end": 855, "station": "Marienplatz" }, + { "begin": 930, "end": 960, "station": "Karlsplatz" }, + { "begin": 1050, "end": 1080, "station": "Hbf" }, + { "begin": 1155, "end": 1185, "station": "Hackerbruecke" }, + { "begin": 1260, "end": 1290, "station": "Donnersbergerbruecke" }, + { "begin": 1380, "end": 1410, "station": "Hirschgarten" }, + { "begin": 1500, "end": 1530, "station": "Laim" } + ], + "t_0": 510, + "t_n": 1680, + "v_0": 0.0, + "v_n": 0.0 + }, + "S4Grafing": { + "entry": "PasingEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 585, "end": 615, "station": "Laim" }, + { "begin": 705, "end": 735, "station": "Hirschgarten" }, + { "begin": 825, "end": 855, "station": "Donnersbergerbruecke" }, + { "begin": 930, "end": 960, "station": "Hackerbruecke" }, + { "begin": 1050, "end": 1080, "station": "Hbf" }, + { "begin": 1170, "end": 1200, "station": "Karlsplatz" }, + { "begin": 1275, "end": 1305, "station": "Marienplatz" }, + { "begin": 1380, "end": 1410, "station": "Isartor" }, + { "begin": 1500, "end": 1530, "station": "RosenheimerPlatz" } + ], + "t_0": 435, + "t_n": 1620, + "v_0": 0.0, + "v_n": 0.0 + }, + "S6Ebersberg": { + "entry": "PasingEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 150, "end": 180, "station": "Laim" }, + { "begin": 270, "end": 300, "station": "Hirschgarten" }, + { "begin": 390, "end": 420, "station": "Donnersbergerbruecke" }, + { "begin": 495, "end": 525, "station": "Hackerbruecke" }, + { "begin": 615, "end": 645, "station": "Hbf" }, + { "begin": 735, "end": 765, "station": "Karlsplatz" }, + { "begin": 840, "end": 870, "station": "Marienplatz" }, + { "begin": 945, "end": 975, "station": "Isartor" }, + { "begin": 1065, "end": 1095, "station": "RosenheimerPlatz" } + ], + "t_0": 0, + "t_n": 1185, + "v_0": 0.0, + "v_n": 0.0 + }, + "S6Tutzing": { + "entry": "Ost1Entry", + "exit": "PasingExit", + "stops": [ + { "begin": 180, "end": 210, "station": "RosenheimerPlatz" }, + { "begin": 300, "end": 330, "station": "Isartor" }, + { "begin": 405, "end": 435, "station": "Marienplatz" }, + { "begin": 510, "end": 540, "station": "Karlsplatz" }, + { "begin": 600, "end": 630, "station": "Hbf" }, + { "begin": 705, "end": 735, "station": "Hackerbruecke" }, + { "begin": 810, "end": 840, "station": "Donnersbergerbruecke" }, + { "begin": 930, "end": 960, "station": "Hirschgarten" }, + { "begin": 1050, "end": 1080, "station": "Laim" } + ], + "t_0": 90, + "t_n": 1230, + "v_0": 0.0, + "v_n": 0.0 + }, + "S7Aying": { + "entry": "DonnersbergerEntry", + "exit": "Ost4Exit", + "stops": [ + { "begin": 420, "end": 450, "station": "Hackerbruecke" }, + { "begin": 540, "end": 570, "station": "Hbf" }, + { "begin": 660, "end": 690, "station": "Karlsplatz" }, + { "begin": 765, "end": 795, "station": "Marienplatz" }, + { "begin": 870, "end": 900, "station": "Isartor" }, + { "begin": 990, "end": 1020, "station": "RosenheimerPlatz" } + ], + "t_0": 345, + "t_n": 1110, + "v_0": 0.0, + "v_n": 0.0 + }, + "S7Wolfratshausen": { + "entry": "Ost3Entry", + "exit": "DonnersbergerExit", + "stops": [ + { "begin": 255, "end": 285, "station": "RosenheimerPlatz" }, + { "begin": 375, "end": 405, "station": "Isartor" }, + { "begin": 480, "end": 510, "station": "Marienplatz" }, + { "begin": 585, "end": 615, "station": "Karlsplatz" }, + { "begin": 675, "end": 705, "station": "Hbf" }, + { "begin": 780, "end": 810, "station": "Hackerbruecke" } + ], + "t_0": 165, + "t_n": 885, + "v_0": 0.0, + "v_n": 0.0 + }, + "S8Airport": { + "entry": "PasingEntry", + "exit": "Ost5Exit", + "stops": [ + { "begin": 405, "end": 435, "station": "Laim" }, + { "begin": 525, "end": 555, "station": "Hirschgarten" }, + { "begin": 645, "end": 675, "station": "Donnersbergerbruecke" }, + { "begin": 750, "end": 780, "station": "Hackerbruecke" }, + { "begin": 870, "end": 900, "station": "Hbf" }, + { "begin": 990, "end": 1020, "station": "Karlsplatz" }, + { "begin": 1095, "end": 1125, "station": "Marienplatz" }, + { "begin": 1200, "end": 1230, "station": "Isartor" }, + { "begin": 1320, "end": 1350, "station": "RosenheimerPlatz" } + ], + "t_0": 255, + "t_n": 1440, + "v_0": 0.0, + "v_n": 0.0 + }, + "S8Germering": { + "entry": "Ost1Entry", + "exit": "PasingExit", + "stops": [ + { "begin": 345, "end": 375, "station": "RosenheimerPlatz" }, + { "begin": 465, "end": 495, "station": "Isartor" }, + { "begin": 570, "end": 600, "station": "Marienplatz" }, + { "begin": 675, "end": 705, "station": "Karlsplatz" }, + { "begin": 765, "end": 795, "station": "Hbf" }, + { "begin": 870, "end": 915, "station": "Hackerbruecke" }, + { "begin": 990, "end": 1020, "station": "Donnersbergerbruecke" }, + { "begin": 1110, "end": 1140, "station": "Hirschgarten" }, + { "begin": 1230, "end": 1260, "station": "Laim" } + ], + "t_0": 255, + "t_n": 1410, + "v_0": 0.0, + "v_n": 0.0 + } +} diff --git a/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/stations.json b/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/stations.json new file mode 100644 index 000000000..885a7fab0 --- /dev/null +++ b/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/stations.json @@ -0,0 +1,35 @@ +{ + "Donnersbergerbruecke": [ + ["Donnersbergerbruecke1L", "Donnersbergerbruecke1R"], + ["Donnersbergerbruecke2R", "Donnersbergerbruecke2L"] + ], + "Hackerbruecke": [ + ["Hackerbruecke1L", "Hackerbruecke1R"], + ["Hackerbruecke2R", "Hackerbruecke2L"] + ], + "Hbf": [ + ["Hbf1L", "Hbf1R"], + ["Hbf2R", "Hbf2L"] + ], + "Hirschgarten": [ + ["Hirschgarten1L", "Hirschgarten1R"], + ["Hirschgarten2R", "Hirschgarten2L"] + ], + "Isartor": [["Isartor2R", "Isartor2L"]], + "Karlsplatz": [ + ["Karlsplatz1L", "Karlsplatz1R"], + ["Karlsplatz2R", "Karlsplatz2L"] + ], + "Laim": [ + ["Laim1L", "Laim1R"], + ["Laim3R", "Laim3L"] + ], + "Marienplatz": [ + ["Marienplatz1L", "Marienplatz1R"], + ["Marienplatz2R", "Marienplatz2L"] + ], + "RosenheimerPlatz": [ + ["Rosenheimer1L", "Rosenheimer1R"], + ["Rosenheimer2R", "Rosenheimer2L"] + ] +} diff --git a/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/trains.json b/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/trains.json new file mode 100644 index 000000000..29f0ce1f5 --- /dev/null +++ b/test/example-networks-sim-unidirec/Stammstrecke16Trains/timetable/trains.json @@ -0,0 +1,114 @@ +{ + "S1Freising": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S1Leuchtenbergring": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S2Dachau": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S2Erding": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S2Ost": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S2Petershausen": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S3Deisenhofen": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S3Mammendorf": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S4Geltendorf": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S4Grafing": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S6Ebersberg": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S6Tutzing": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S7Aying": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S7Wolfratshausen": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + }, + "S8Airport": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 202, + "max_speed": 38.888888888888886, + "tim": true + }, + "S8Germering": { + "acceleration": 1.0, + "deceleration": 0.9, + "length": 135, + "max_speed": 38.888888888888886, + "tim": true + } +} diff --git a/test/example-networks-sim-unidirec/readme.md b/test/example-networks-sim-unidirec/readme.md new file mode 100644 index 000000000..226721255 --- /dev/null +++ b/test/example-networks-sim-unidirec/readme.md @@ -0,0 +1,4 @@ +## Explanation Unidirectional Networks + +The continuous simulator takes an undirected graph as input (we call it unidirectional since its still represented as a digraph). +Unidirectional networks can be generated from normal networks using convertToUnidirec.py. diff --git a/test/test_railwaynetwork.cpp b/test/test_railwaynetwork.cpp index 28744e822..34f2f2889 100644 --- a/test/test_railwaynetwork.cpp +++ b/test/test_railwaynetwork.cpp @@ -2810,6 +2810,34 @@ TEST(Functionality, ShortestPaths) { EXPECT_EQ(shortest_paths_4_val.value(), 0); EXPECT_EQ(shortest_paths_4_path.size(), 1); EXPECT_EQ(shortest_paths_4_path, std::vector({v1_v2})); + + // Shortest paths between vertices + const auto vertex_shortest_paths = network.all_vertex_pairs_shortest_paths(); + + EXPECT_EQ(vertex_shortest_paths[4][4], 0); + EXPECT_EQ(vertex_shortest_paths[2][2], 0); + EXPECT_EQ(vertex_shortest_paths[5][4], 1000); + EXPECT_EQ(vertex_shortest_paths[4][5], 1000); + EXPECT_EQ(vertex_shortest_paths[3][0], 500); + EXPECT_EQ(vertex_shortest_paths[0][3], 600); + EXPECT_EQ(vertex_shortest_paths[5][0], 1900); + EXPECT_EQ(vertex_shortest_paths[2][5], 1500); + + const auto vertex_shortest_paths_undir = + network.all_vertex_pairs_shortest_paths_undirected(); + + EXPECT_EQ(vertex_shortest_paths_undir[4][4], 0); + EXPECT_EQ(vertex_shortest_paths_undir[2][2], 0); + EXPECT_EQ(vertex_shortest_paths_undir[5][4], 1000); + EXPECT_EQ(vertex_shortest_paths_undir[4][5], 1000); + EXPECT_EQ(vertex_shortest_paths_undir[3][0], 500); + EXPECT_EQ(vertex_shortest_paths_undir[0][3], 500); + EXPECT_EQ(vertex_shortest_paths_undir[5][0], 1800); + EXPECT_EQ(vertex_shortest_paths_undir[2][5], 1500); + EXPECT_EQ(vertex_shortest_paths_undir[4][2], 500); + EXPECT_EQ(vertex_shortest_paths_undir[2][4], 500); + EXPECT_EQ(vertex_shortest_paths_undir[2][0], 300); + EXPECT_EQ(vertex_shortest_paths_undir[0][2], 300); } TEST(Functionality, ReadTrains) { diff --git a/test/test_simulation.cpp b/test/test_simulation.cpp new file mode 100644 index 000000000..219c022ca --- /dev/null +++ b/test/test_simulation.cpp @@ -0,0 +1,433 @@ +#include "datastructure/RailwayNetwork.hpp" +#include "datastructure/Timetable.hpp" +#include "simulation/Objectives.hpp" +#include "simulation/RoutingSolver.hpp" +#include "simulation/SimulationInstance.hpp" +#include "simulation/TrainTrajectorySet.hpp" + +#include +#include + +using namespace cda_rail; + +#include "gtest/gtest.h" +#include +#include +#include +#include + +TEST(Simulation, RandomSolution) { + const Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + const Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + std::uniform_int_distribution random_train_index( + 0, timetable.get_train_list().size() - 1); + + for (int i = 0; i <= 100; i++) { + const Train& train = + timetable.get_train_list().get_train(random_train_index(rng_engine)); + + sim::SimulationInstance instance(network, timetable, true); + const sim::RoutingSolution sol{instance, train, rng_engine}; + for (auto target : sol.v_targets.targets) { + ASSERT_GE(target.first, 0); + ASSERT_LT(target.first, instance.n_timesteps); + ASSERT_GE(target.second, -(instance.allow_reversing * train.max_speed)); + ASSERT_LE(target.second, train.max_speed); + } + ASSERT_GE(sol.v_targets.size(), 1); + ASSERT_LE(sol.v_targets.size(), instance.n_timesteps); + ASSERT_EQ(sol.switch_directions.size(), instance.n_switch_vars); + } +} + +TEST(Simulation, SpeedTargets) { + std::vector timesteps = {3, 20, 50, 75, 87}; + std::vector speeds = {0.4, 0.6, 0.5, -0.2, -0.5}; + + sim::SpeedTargets v_targets(timesteps, speeds); + ASSERT_EQ(v_targets.find_target_speed(2), 0.4); + ASSERT_EQ(v_targets.find_target_speed(11), 0.4); + ASSERT_EQ(v_targets.find_target_speed(21), 0.6); + ASSERT_EQ(v_targets.find_target_speed(50), 0.5); + ASSERT_EQ(v_targets.find_target_speed(74), 0.5); + ASSERT_EQ(v_targets.find_target_speed(84), -0.2); + ASSERT_EQ(v_targets.find_target_speed(89), -0.5); + ASSERT_EQ(v_targets.find_next_reversal(0), 75); + ASSERT_EQ(v_targets.find_next_reversal(10), 75); + ASSERT_EQ(v_targets.find_next_reversal(21), 75); + + v_targets.limit_speed_from(0.3, 45); + ASSERT_EQ(v_targets.targets.at(3), 0.4); + ASSERT_EQ(v_targets.targets.at(20), 0.6); + ASSERT_EQ(v_targets.targets.at(50), 0.3); + ASSERT_EQ(v_targets.targets.at(75), -0.2); + ASSERT_EQ(v_targets.targets.at(87), -0.3); + + std::map cop = v_targets.copy_range(20, 50); + ASSERT_EQ(cop.size(), 3); + ASSERT_EQ(cop.at(20), 0.6); + ASSERT_EQ(cop.at(45), 0.3); + ASSERT_EQ(cop.at(50), 0.3); + + sim::SpeedTargets v_targets_original = v_targets; + v_targets.delete_range(20, 50); + ASSERT_EQ(v_targets.find_target_speed(35), 0.4); + ASSERT_EQ(v_targets.size(), 3); + + v_targets.insert(cop); + ASSERT_EQ(v_targets_original.targets, v_targets.targets); +} + +TEST(Simulation, SimulationInstance) { + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance(network, timetable, true); + + ASSERT_EQ(instance.get_max_train_speed(), 50); + ASSERT_EQ(instance.get_shortest_track(), 25); +} + +TEST(Simulation, EdgeTrajectory) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance(network, timetable, true); + std::uniform_int_distribution random_train_index( + 0, timetable.get_train_list().size() - 1); + + for (int i = 0; i < 500; i++) { + Train train = + timetable.get_train_list().get_train(random_train_index(rng_engine)); + + sim::SimulationInstance instance(network, timetable, true); + sim::RoutingSolution solution(instance, train, rng_engine); + + Schedule train_schedule = instance.timetable.get_schedule(train.name); + + size_t entry = train_schedule.get_entry(); + auto outedges = instance.network.get_successors(entry); + if (outedges.empty()) + outedges = instance.network.get_predecessors(entry); + if (outedges.empty()) + throw std::logic_error("Train entry vertex is has no connected edges."); + + sim::TrainState init_state{.timestep = (size_t)train_schedule.get_t_0(), + .edge = outedges.front(), + .position = 0, + .orientation = true, + .speed = train_schedule.get_v_0()}; + + sim::EdgeTrajectory edge_traj(instance, train, solution.v_targets, + init_state); + sim::EdgeEntry transition = edge_traj.enter_next_edge(0.3); + + edge_traj.check_speed_limits(); + } +} + +TEST(Simulation, TrainTrajectory) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance(network, timetable, true); + std::uniform_int_distribution random_train_index( + 0, timetable.get_train_list().size() - 1); + + for (int i = 0; i < 100; i++) { + size_t train_idx = random_train_index(rng_engine); + + Train train = timetable.get_train_list().get_train(train_idx); + sim::SimulationInstance instance(network, timetable, true); + sim::RoutingSolution solution(instance, train, rng_engine); + + sim::TrainTrajectory traj(instance, train, solution); + + traj.check_speed_limits(); + } +} + +TEST(Simulation, TrainTrajectorySet) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance(network, timetable, true); + std::uniform_int_distribution random_train_index( + 0, timetable.get_train_list().size() - 1); + std::uniform_int_distribution random_target_amount(1, 100); + + for (int i = 0; i < 100; i++) { + sim::RoutingSolutionSet solution_set{instance, rng_engine}; + sim::TrainTrajectorySet traj{instance, solution_set}; + + ASSERT_EQ(solution_set.solutions.size(), 4); + ASSERT_EQ(traj.size(), 4); + traj.check_speed_limits(); + + const auto old_solution_set = solution_set; + solution_set.perturb(instance, 0.01, rng_engine); + for (auto old_solution : old_solution_set.solutions) { + ASSERT_FALSE( + old_solution.second.v_targets.targets == + solution_set.solutions.at(old_solution.first).v_targets.targets); + ASSERT_FALSE( + old_solution.second.switch_directions == + solution_set.solutions.at(old_solution.first).switch_directions); + } + } +} + +TEST(Simulation, TrainDistance) { + // Generate a stationary solution set + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance(network, timetable, true); + sim::RoutingSolutionSet solution_set{instance}; + sim::TrainTrajectorySet traj{instance, solution_set}; + const TrainList& list = instance.timetable.get_train_list(); + + // Starting positions + ASSERT_EQ(traj.train_distance("tr2lr", "tr2lr", 0), 0); + ASSERT_EQ(traj.train_distance("tr2rl", "tr2rl", 0), 0); + + ASSERT_EQ(traj.train_distance("tr2lr", "tr2rl", 0), 23000); + ASSERT_EQ(traj.train_distance("tr2rl", "tr2lr", 0), 23000); + ASSERT_EQ(traj.train_distance("tr1lr", "tr1rl", 180), 23000); + ASSERT_EQ(traj.train_distance("tr1rl", "tr1lr", 180), 23000); + ASSERT_FALSE(traj.train_distance("tr1rl", "tr2lr", 0).has_value()); + ASSERT_FALSE(traj.train_distance("tr2rl", "tr1lr", 0).has_value()); +} + +TEST(Simulation, Penalties) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance(network, timetable, true); + + for (int i = 0; i < 100; i++) { + sim::RoutingSolutionSet solution_set{instance, rng_engine}; + sim::TrainTrajectorySet traj{instance, solution_set}; + sim::SolverResult res{instance, solution_set}; + sim::ScoreSet scores = res.get_score_set(); + ASSERT_DOUBLE_EQ(sim::collision_penalty(traj), + scores.get_collision_score()); + ASSERT_DOUBLE_EQ(sim::destination_penalty(traj), + scores.get_norm_destination_score()); + ASSERT_DOUBLE_EQ(sim::stop_penalty(traj), scores.get_norm_stop_score()); + ASSERT_DOUBLE_EQ(sim::combined_objective(traj), scores.get_score()); + traj.export_csv("tmp/test_traj_penalty.csv"); + } +} + +TEST(Simulation, SolverResult) { + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + + sim::SimulationInstance instance{network, timetable, false}; + + TrainList train_list = instance.timetable.get_train_list(); + + for (size_t i = 0; i < 100; i++) { + sim::SolverResult result{instance}; + + std::vector train_idxs(train_list.size()); + std::iota(std::begin(train_idxs), std::end(train_idxs), 0); + std::shuffle(train_idxs.begin(), train_idxs.end(), rng_engine); + + for (auto train_idx = train_idxs.begin(); train_idx != train_idxs.end(); + train_idx++) { + const auto train = train_list.get_train(*train_idx); + double previous_score = result.get_score_set().get_score(); + sim::RoutingSolution round_sol{instance, train, rng_engine}; + sim::TrainTrajectory round_traj{instance, train, round_sol}; + result.insert_or_assign(round_sol, round_traj); + } + } +} + +TEST(Simulation, GreedySolution) { + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance{network, timetable, false}; + sim::RoutingSolver solver{instance}; + std::optional sol = + solver.greedy_solution({std::chrono::milliseconds{10}}); +} + +TEST(Simulation, RandomSearch) { + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance{network, timetable, false}; + sim::RoutingSolver solver{instance}; + + auto res = + solver.random_search(std::chrono::seconds{1}, std::chrono::seconds{1}); + + if (std::get<0>(res)) { + cda_rail::is_directory_and_create("tmp"); + std::get<0>(res).value().get_trajectories().export_csv( + "tmp/test_traj_random.csv"); + std::get<1>(res).export_csv("tmp/test_hist_random.csv"); + } +} + +TEST(Simulation, GreedySearch) { + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance{network, timetable, false}; + sim::RoutingSolver solver{instance}; + + auto res = + solver.greedy_search(std::chrono::seconds{1}, std::chrono::seconds{1}, + {std::chrono::milliseconds{50}}); + + if (std::get<0>(res)) { + cda_rail::is_directory_and_create("tmp"); + std::get<0>(res).value().get_trajectories().export_csv( + "tmp/test_traj_greedy.csv"); + std::get<1>(res).export_csv("tmp/test_hist_greedy.csv"); + } +} + +TEST(Simulation, LocalSearch) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance{network, timetable, false}; + sim::RoutingSolver solver{instance}; + + sim::RoutingSolutionSet solution_set{instance, rng_engine}; + auto res = solver.local_search(solution_set, {0.1, 0.01, 0.95}); +} + +TEST(Simulation, RandomLocalSearch) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance{network, timetable, false}; + sim::RoutingSolver solver{instance}; + + sim::RoutingSolutionSet solution_set{instance, rng_engine}; + auto res = + solver.random_local_search(std::chrono::seconds{1}, {0.1, 1e-3, 0.95}); +} + +TEST(Simulation, GraspSearch) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + sim::SimulationInstance instance{network, timetable, false}; + sim::RoutingSolver solver{instance}; + + sim::RoutingSolutionSet solution_set{instance, rng_engine}; + auto res = + solver.grasp_search(std::chrono::seconds{1}, + {std::chrono::milliseconds{50}}, {0.1, 1e-3, 0.95}); +} + +TEST(Simulation, GeneticSearch) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/SimpleNetwork/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/SimpleNetwork/timetable/", network); + + cda_rail::sim::GeneticParams ga_params{ + .is_multithread = true, + .population = 10, + .gen_max = 3, + .stall_max = 2, + .n_elite = 2, + .xover_frac = 0.7, + .mut_rate = 0.1, + }; + + sim::SimulationInstance instance{network, timetable, false}; + sim::RoutingSolver solver{instance}; + + sim::RoutingSolutionSet solution_set{instance, rng_engine}; + auto res = solver.genetic_search(ga_params); + auto res2 = solver.genetic_search(ga_params, true); +} + +TEST(Simulation, ExportVSSSolution) { + const size_t seed = + std::chrono::high_resolution_clock::now().time_since_epoch().count(); + std::ranlux24_base rng_engine(seed); + Network network = Network::import_network( + "./example-networks-sim-unidirec/Overtake_slow/network/"); + Network network_bidirec = Network::import_network( + "./example-networks-sim-bidirec/Overtake_slow/network/"); + Timetable timetable = Timetable::import_timetable( + "./example-networks-sim-unidirec/Overtake_slow/timetable/", network); + sim::SimulationInstance instance{network, timetable, false}; + + for (size_t i = 0; i < 100; i++) { + sim::RoutingSolutionSet sol{instance, rng_engine}; + sim::TrainTrajectorySet traj{instance, sol}; + auto converted_sol = traj.to_vss_solution(network_bidirec); + } +} + +// TODO: test for invariance of solution after being repaired and used again