Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions examples/slow_charge_rb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ int main(int argc, char** argv) {

std::cout << "Creating dynamics...\n";

Dynamics dynamics{graph, true, SEED, 0.6};
Dynamics dynamics{graph, false, SEED, 0.6};

{
std::vector<dsf::Id> destinationNodes;
Expand All @@ -123,7 +123,6 @@ int main(int argc, char** argv) {

std::cout << "Done." << std::endl;
std::cout << "Running simulation...\n";

#ifdef PRINT_FLOWS
std::ofstream streetFlow(OUT_FOLDER + "flows.csv");
streetFlow << "time";
Expand Down
85 changes: 16 additions & 69 deletions examples/slow_charge_tl.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <dsf/dsf.hpp>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <fstream>
Expand Down Expand Up @@ -45,13 +46,13 @@ void printLoadingBar(int const i, int const n) {
}

int main(int argc, char** argv) {
auto const start = std::chrono::high_resolution_clock::now();
if (argc != 6) {
std::cerr
<< "Usage: " << argv[0]
<< " <SEED> <ERROR_PROBABILITY> <OUT_FOLDER_BASE> <OPTIMIZE> <INIT_NAGENTS>\n";
return 1;
}
spdlog::set_level(spdlog::level::err); // turn off spdlog logging

const int SEED = std::stoi(argv[1]); // seed for random number generator
const double ERROR_PROBABILITY{std::stod(argv[2])};
Expand All @@ -60,12 +61,6 @@ int main(int argc, char** argv) {
BASE_OUT_FOLDER += OPTIMIZE ? "_op/" : "/";
auto nAgents{std::stoul(argv[5])};

std::string OUT_FOLDER{std::format("{}output_sctl_{}_{}/",
BASE_OUT_FOLDER,
ERROR_PROBABILITY,
std::to_string(SEED))}; // output folder
constexpr auto MAX_TIME{static_cast<unsigned int>(5e5)}; // maximum time of simulation

std::cout << "-------------------------------------------------\n";
std::cout << "Input parameters:\n";
std::cout << "Seed: " << SEED << '\n';
Expand All @@ -76,7 +71,11 @@ int main(int argc, char** argv) {
std::cout << "Traffic light optimization ENABLED.\n";
}
std::cout << "-------------------------------------------------\n";

const std::string OUT_FOLDER{std::format("{}output_sctl_{}_{}/",
BASE_OUT_FOLDER,
ERROR_PROBABILITY,
std::to_string(SEED))}; // output folder
constexpr auto MAX_TIME{static_cast<unsigned int>(5e5)}; // maximum time of simulation
// Clear output folder or create it if it doesn't exist
if (!fs::exists(BASE_OUT_FOLDER)) {
fs::create_directory(BASE_OUT_FOLDER);
Expand Down Expand Up @@ -151,81 +150,25 @@ int main(int argc, char** argv) {
for (const auto& pair : graph.edges()) {
graph.makeSpireStreet(pair.first);
}
// create gaussian random number generator
std::random_device rd;
std::mt19937 gen(rd());
gen.seed(64313);
std::normal_distribution d(60., 10.);
std::array<uint8_t, 2> sda{0, 0};
auto random = [&d, &gen]() { return std::round(d(gen)); };
std::cout << "Adjusting node capacities...\n";
graph.adjustNodeCapacities();
std::cout << "Setting traffic light parameters..." << '\n';
for (const auto& [nodeId, pNode] : graph.nodes()) {
if (!pNode->isTrafficLight()) {
continue;
}
auto& tl = dynamic_cast<TrafficLight&>(*pNode);
double value = -1.;
while (value < 0.) {
value = random();
}
const auto& ingoingEdges = pNode->ingoingEdges();
if (ingoingEdges.empty()) {
spdlog::error("Node {} has no ingoing edges.", nodeId);
continue;
}
std::set<dsf::Id> streets;
if (!pNode->geometry().has_value()) {
std::cerr << "Warning: Node " << nodeId << " has no geometry, skipping.\n";
continue;
}
const auto& refLat = (*pNode->geometry()).y();
for (const auto& inEdgeId : ingoingEdges) {
auto const& pEdge{graph.edge(inEdgeId)};
const auto& edgeGeometry = pEdge->geometry();
if (edgeGeometry.empty()) {
std::cerr << "Warning: Edge " << inEdgeId << " has empty geometry, skipping.\n";
continue;
}
const auto& lat = edgeGeometry.front().y();
// std::cout << "Lat: " << lat << " RefLat: " << refLat << '\n';
if (std::abs(lat - refLat) < std::numeric_limits<double>::epsilon()) {
streets.emplace(inEdgeId);
}
}
for (auto const& streetId : streets) {
tl.setCycle(streetId, dsf::Direction::ANY, {static_cast<dsf::Delay>(value), 0});
}
for (const auto& streetId : ingoingEdges) {
if (!streets.contains(streetId)) {
tl.setComplementaryCycle(streetId, *streets.begin());
}
}
++sda[streets.size() - 1];
// std::cout << "Node id: " << nodeId << " has " << streets.size()
// << "streets.\n";
}
std::cout << "Nodes with one street: " << static_cast<int>(sda[0]) << '\n';
std::cout << "Nodes with two streets: " << static_cast<int>(sda[1]) << '\n';
graph.initTrafficLights();
std::cout << "Done." << std::endl;

std::cout << "Creating dynamics...\n";

Dynamics dynamics{graph, true, SEED, 0.6};
std::size_t n{0};
Dynamics dynamics{graph, false, SEED, 0.6};
{
std::vector<dsf::Id> destinationNodes;
for (auto const& [nodeId, pNode] : dynamics.graph().nodes()) {
if (pNode->outgoingEdges().size() < 4) {
destinationNodes.push_back(pNode->id());
++n;
destinationNodes.push_back(nodeId);
}
}
dynamics.setDestinationNodes(destinationNodes);
std::cout << "Number of exits: " << destinationNodes.size() << '\n';
}
dynamics.updatePaths();
std::cout << "Number of exits: " << n << '\n';

dynamics.setErrorProbability(ERROR_PROBABILITY);
// dynamics.setMaxFlowPercentage(0.69);
Expand Down Expand Up @@ -429,6 +372,10 @@ int main(int argc, char** argv) {
#ifdef __APPLE__
t.join();
#endif

std::cout << "Total elapsed time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - start)
.count()
<< " milliseconds\n";
return 0;
}
43 changes: 37 additions & 6 deletions src/dsf/base/Dynamics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,34 +69,64 @@
class Dynamics {
private:
network_t m_graph;
std::string m_name;
std::time_t m_timeInit, m_timeStep;

protected:
tbb::task_arena m_taskArena;
std::time_t m_timeInit, m_timeStep;
std::mt19937_64 m_generator;

protected:
void m_evolve() { ++m_timeStep; };
inline void m_evolve() { ++m_timeStep; };

/// @brief Get a safe date-time string for filenames (YYYYMMDD_HHMMSS)
/// @return std::string, The safe date-time string
inline auto m_safeDateTime() const {
#ifdef __APPLE__
std::time_t const t = time();

Check notice

Code scanning / Cppcheck (reported by Codacy)

time is Y2038-unsafe Note

time is Y2038-unsafe
std::ostringstream oss;
oss << std::put_time(std::localtime(&t), "%Y%m%d_%H%M%S");

Check notice

Code scanning / Cppcheck (reported by Codacy)

localtime is Y2038-unsafe Note

localtime is Y2038-unsafe
return oss.str();
#else
return std::format(
"{:%Y%m%d_%H%M%S}",
std::chrono::floor<std::chrono::seconds>(std::chrono::current_zone()->to_local(
std::chrono::system_clock::from_time_t(time()))));
#endif
}
/// @brief Get a safe name string for filenames (spaces replaced by underscores)
/// @return std::string, The safe name string
inline auto m_safeName() const {
std::string safeName = m_name;
std::replace(safeName.begin(), safeName.end(), ' ', '_');
return safeName;
}

public:
/// @brief Construct a new Dynamics object
/// @param graph The graph representing the network
/// @param seed The seed for the random number generator (default is std::nullopt)
Dynamics(network_t& graph, std::optional<unsigned int> seed = std::nullopt);

/// @brief Set the name of the simulation
/// @param name The name of the simulation

Check notice

Code scanning / Cppcheck (reported by Codacy)

time is Y2038-unsafe Note

time is Y2038-unsafe
inline void setName(const std::string& name) { m_name = name; };
/// @brief Set the initial time as epoch time
/// @param timeEpoch The initial time as epoch time
void setInitTime(std::time_t timeEpoch) { m_timeInit = timeEpoch; };
inline void setInitTime(std::time_t timeEpoch) { m_timeInit = timeEpoch; };

/// @brief Get the graph
/// @return const network_t&, The graph
inline const network_t& graph() const { return m_graph; };
inline const auto& graph() const { return m_graph; };
/// @brief Get the name of the simulation
/// @return const std::string&, The name of the simulation
inline const auto& name() const { return m_name; };
/// @brief Get the current simulation time as epoch time
/// @return std::time_t, The current simulation time as epoch time
inline std::time_t time() const { return m_timeInit + m_timeStep; }
inline auto time() const { return m_timeInit + m_timeStep; }

Check notice

Code scanning / Cppcheck (reported by Codacy)

time is Y2038-unsafe Note

time is Y2038-unsafe
/// @brief Get the current simulation time-step
/// @return std::time_t, The current simulation time-step
inline std::time_t time_step() const { return m_timeStep; }
inline auto time_step() const { return m_timeStep; }
/// @brief Get the current simulation time as formatted string (YYYY-MM-DD HH:MM:SS)
/// @return std::string, The current simulation time as formatted string
inline auto strDateTime() const {
Expand All @@ -117,6 +147,7 @@
template <typename network_t>
Dynamics<network_t>::Dynamics(network_t& graph, std::optional<unsigned int> seed)
: m_graph{std::move(graph)},
m_name{"unnamed simulation"},
m_timeInit{0},
m_timeStep{0},
m_generator{std::random_device{}()} {
Expand Down
4 changes: 4 additions & 0 deletions src/dsf/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ PYBIND11_MODULE(dsf_cpp, m) {
dsf::g_docstrings.at("dsf::mobility::FirstOrderDynamics::FirstOrderDynamics")
.c_str())
// Note: Constructors with std::function parameters are not exposed to avoid stub generation issues
.def("setName",
&dsf::mobility::FirstOrderDynamics::setName,
pybind11::arg("name"),
dsf::g_docstrings.at("dsf::Dynamics::setName").c_str())
.def("setInitTime",
&dsf::mobility::FirstOrderDynamics::setInitTime,
pybind11::arg("timeEpoch"),
Expand Down
2 changes: 1 addition & 1 deletion src/dsf/dsf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

static constexpr uint8_t DSF_VERSION_MAJOR = 4;
static constexpr uint8_t DSF_VERSION_MINOR = 3;
static constexpr uint8_t DSF_VERSION_PATCH = 0;
static constexpr uint8_t DSF_VERSION_PATCH = 1;

static auto const DSF_VERSION =
std::format("{}.{}.{}", DSF_VERSION_MAJOR, DSF_VERSION_MINOR, DSF_VERSION_PATCH);
Expand Down
39 changes: 26 additions & 13 deletions src/dsf/mobility/RoadDynamics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,9 @@
Measurement<double> meanSpireOutputFlow(bool resetValue = true);

/// @brief Save the street densities in csv format
/// @param filename The name of the file
/// @param filename The name of the file (default is "{datetime}_{simulation_name}_street_densities.csv")
/// @param normalized If true, the densities are normalized in [0, 1]
void saveStreetDensities(const std::string& filename,
void saveStreetDensities(std::string filename = std::string(),

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.8 rule Note

MISRA 17.8 rule
bool normalized = true,
char const separator = ';') const;
/// @brief Save the street input counts in csv format
Expand All @@ -357,11 +357,11 @@
/// - distances: the travel distances of the agents
/// - times: the travel times of the agents
/// - speeds: the travel speeds of the agents
/// @param filename The name of the file
/// @param filename The name of the file (default is "{datetime}_{simulation_name}_travel_data.csv")
/// @param reset If true, the travel speeds are cleared after the computation
void saveTravelData(const std::string& filename, bool reset = false);
void saveTravelData(std::string filename = std::string(), bool reset = false);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.8 rule Note

MISRA 17.8 rule
/// @brief Save the main macroscopic observables in csv format
/// @param filename The name of the file
/// @param filename The name of the file (default is "{datetime}_{simulation_name}_macroscopic_observables.csv")
/// @param separator The separator character (default is ';')
/// @details The file contains the following columns:
/// - time: the time of the simulation
Expand All @@ -375,7 +375,7 @@
/// - mean_travelspeed - mean_travelspeed_std (km/h): the mean travel speed of the agents
///
/// NOTE: the mean density is normalized in [0, 1] and reset is true for all observables which have such parameter
void saveMacroscopicObservables(const std::string& filename,
void saveMacroscopicObservables(std::string filename = std::string(),

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.8 rule Note

MISRA 17.8 rule
char const separator = ';');
};
Expand Down Expand Up @@ -2160,9 +2160,13 @@

template <typename delay_t>
requires(is_numeric_v<delay_t>)
void RoadDynamics<delay_t>::saveStreetDensities(const std::string& filename,
void RoadDynamics<delay_t>::saveStreetDensities(std::string filename,
bool normalized,
char const separator) const {
if (filename.empty()) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 14.4 rule Note

MISRA 14.4 rule
filename =

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.8 rule Note

MISRA 17.8 rule
this->m_safeDateTime() + '_' + this->m_safeName() + "_street_densities.csv";

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 18.4 rule Note

MISRA 18.4 rule
}
bool bEmptyFile{false};
{
std::ifstream file(filename);
Expand Down Expand Up @@ -2276,7 +2280,10 @@
}
template <typename delay_t>
requires(is_numeric_v<delay_t>)
void RoadDynamics<delay_t>::saveTravelData(const std::string& filename, bool reset) {
void RoadDynamics<delay_t>::saveTravelData(std::string filename, bool reset) {
if (filename.empty()) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 14.4 rule Note

MISRA 14.4 rule
filename = this->m_safeDateTime() + '_' + this->m_safeName() + "_travel_data.csv";

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 18.4 rule Note

MISRA 18.4 rule
}
bool bEmptyFile{false};
{
std::ifstream file(filename);
Expand Down Expand Up @@ -2331,8 +2338,12 @@
}
template <typename delay_t>
requires(is_numeric_v<delay_t>)
void RoadDynamics<delay_t>::saveMacroscopicObservables(const std::string& filename,
void RoadDynamics<delay_t>::saveMacroscopicObservables(std::string filename,
char const separator) {
if (filename.empty()) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 14.4 rule Note

MISRA 14.4 rule
filename = this->m_safeDateTime() + '_' + this->m_safeName() +

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 18.4 rule Note

MISRA 18.4 rule
"_macroscopic_observables.csv";
}
bool bEmptyFile{false};
{
std::ifstream file(filename);
Expand All @@ -2343,10 +2354,12 @@
throw std::runtime_error("Error opening file \"" + filename + "\" for writing.");
}
if (bEmptyFile) {
file << "datetime;time_step;n_ghost_agents;n_agents;mean_speed_kph;std_speed_kph;"
"mean_density_vpk;std_density_vpk;mean_flow_vph;std_flow_vph;mean_"
"traveltime_m;std_traveltime_m;mean_traveldistance_km;std_traveldistance_"
"km;mean_travelspeed_kph;std_travelspeed_kph\n";
constexpr auto strHeader{
"datetime;time_step;n_ghost_agents;n_agents;mean_speed_kph;std_speed_kph;"
"mean_density_vpk;std_density_vpk;mean_flow_vph;std_flow_vph;mean_"
"traveltime_m;std_traveltime_m;mean_traveldistance_km;std_traveldistance_"
"km;mean_travelspeed_kph;std_travelspeed_kph\n"};
file << strHeader;
}
double mean_speed{0.}, mean_density{0.}, mean_flow{0.}, mean_travel_distance{0.},
mean_travel_time{0.}, mean_travel_speed{0.};
Expand Down
Loading
Loading