Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved documentation of code and fixed perf bug #22

Merged
merged 1 commit into from
May 22, 2023
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
29 changes: 23 additions & 6 deletions include/flight_planner.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ class IdCity {
bool operator!=(const IdCity& other) const { return !(*this == other); }
bool operator<(const IdCity& other) const { return id_ < other.id_; }

/// Returns the id of the city
int id() const { return id_; }

private:
int id_;
int id_; // Id of the city
};


Expand All @@ -46,6 +47,8 @@ class StateAircraft {
id_city_(id_city), battery_level_hours_(battery_level_hours) {}
StateAircraft() : id_city_(IdCity()), battery_level_hours_(-1.0) {}

/// Calculates the change in battery flying time from this state to another state
/// DeltaBattery = BatteryOther - BatteryThis
double CalcBatteryDelta(const StateAircraft& other) const {
assert(id_city_ == other.id_city_);
return other.battery_level_hours_ - battery_level_hours_;
Expand All @@ -63,12 +66,15 @@ class StateAircraft {
}
}

/// Returns the id of the city
IdCity id_city() const { return id_city_; }

/// Returns the remaining battery flight time in hours
double battery_level_hours() const { return battery_level_hours_; }

private:
IdCity id_city_;
double battery_level_hours_;
IdCity id_city_; // Id of the city
double battery_level_hours_; // Remaining battery flight time in hours
};


Expand Down Expand Up @@ -105,6 +111,9 @@ class FlightPlannerBase : public GraphDirected<StateAircraft> {
}

static constexpr double MinBatteryHours() { return 0.0; }

/// Returns the maximum battery flytime in hours. The maximum flytime is the
/// maximum flight distance 320km divided by the speed of the aircraft.
static constexpr double MaxBatteryHours() { return 320.0 / SpeedKmPerHr(); }
static constexpr double MaxFlightTimeHours() { return MaxBatteryHours() - MinBatteryHours(); }

Expand Down Expand Up @@ -137,7 +146,7 @@ class FlightPlannerBase : public GraphDirected<StateAircraft> {
double CalcChargeRateKmPerHr(const IdCity& id_city) const { return GetAirport(id_city).rate; }

static constexpr double RadEarth() { return 6356.752; } // Radius of Earth in km
static constexpr double SpeedKmPerHr() { return 105.0; } // Speed of aircraft in km/hr
static constexpr double SpeedKmPerHr() { return 105.0; } // Speed of aircraft in km/hr from the problem statement

const std::array<row, 303> airports_; // List of airports copied from the non-constant global in airports.h
};
Expand All @@ -153,6 +162,7 @@ class FlightPlannerExact : public FlightPlannerBase {
}

private:
// Adds edges for this graph construction approach
void AddEdgesFlightsExact();
};

Expand All @@ -170,10 +180,14 @@ class FlightPlannerGrid : public FlightPlannerBase {
AddEdgesCharge();
}

/// Returns the number of battery levels in the grid
int n_levels() const { return n_levels_; }

/// Returns the vector of evenly spaced battery levels
const vector<double>& v_battery_levels() const { return v_battery_levels_; }

private:
// Creates a vector of evenly spaced battery levels between the minimum and maximum battery levels
static vector<double> CreateGridBatteryLevels(int n_levels) {
const double delta_battery = (MaxBatteryHours() - MinBatteryHours()) / (n_levels - 1);

Expand All @@ -186,14 +200,17 @@ class FlightPlannerGrid : public FlightPlannerBase {
return v_battery_levels;
}

// Adds vertices linearly spaced between the minimum and maximum battery levels
void AddVerticesGrid();

// Finds the battery level in the grid that is closest to the given battery level
int FindBatteryLevel(const double battery_level) const;

// Adds approximate edges for this graph construction approach
void AddEdgesDrivingGrid();

const int n_levels_;
const vector<double> v_battery_levels_;
const int n_levels_; // Number of evenly spaced battery levels in the grid
const vector<double> v_battery_levels_; // Vector of evenly spaced battery levels
};

}; // namespace std
3 changes: 3 additions & 0 deletions include/graph_directed.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,10 @@ class GraphDirected {
return make_pair(path, dist[dst]); // Return the shortest path and its cost
}

/// Returns the set of vertices in the graph
const set<VertexType>& vertices() const { return vertices_; }

/// Returns the adjacency list of the graph
const map<VertexType, map<VertexType, double>>& adjacency_list() const { return adjacency_list_; }

private:
Expand Down
97 changes: 51 additions & 46 deletions src/flight_planner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@ namespace std {
// FlightPlannerBase

pair<vector<StateAircraft>, double> FlightPlannerBase::SolvePath(const char* char_src, const char* char_dst) const {
IdCity id_src = GetIdCity(char_src);
IdCity id_dst = GetIdCity(char_dst);

// Calculate shortest distance from vertex src to every other vertex
StateAircraft src{id_src, MaxBatteryHours()}; // Source city with fully battery
StateAircraft dst{id_dst, MinBatteryHours()}; // Destination city
return CalcMinCostPathDijkstra(src, dst);
StateAircraft src{GetIdCity(char_src), MaxBatteryHours()}; // Source city with fully battery
StateAircraft dst{GetIdCity(char_dst), MinBatteryHours()}; // Destination city
return CalcMinCostPathDijkstra(src, dst); // Solve for the minimum cost path
}

void FlightPlannerBase::AddEdgesCharge() {
Expand All @@ -39,7 +35,7 @@ void FlightPlannerBase::AddEdgesCharge(const IdCity& id_city, const set<StateAir
if (next_it != vertex_states.end()) {
StateAircraft next_vertex_state = *next_it;

double charge_time = CalcChargeTime(vertex_state, next_vertex_state);
const double charge_time = CalcChargeTime(vertex_state, next_vertex_state);

// Represents charging edge
AddDirectedEdge(vertex_state, next_vertex_state, charge_time);
Expand All @@ -60,40 +56,43 @@ IdCity FlightPlannerBase::GetIdCity(const string& name) const {
}

void FlightPlannerBase::PrintPath(const vector<StateAircraft>& v_path) const {
IdCity id_src = v_path[0].id_city();
IdCity id_dst = v_path[v_path.size() - 1].id_city();
IdCity id_src = v_path[0].id_city(); // Id of the first city
IdCity id_dst = v_path[v_path.size() - 1].id_city(); // Id of the last city

cout << endl;
PrintCity(id_src); cout << ", " << endl; // Print first city
PrintChargingCitiesAndTimes(v_path, id_dst); // Print out each city the plane charges at
PrintChargingCitiesAndTimes(v_path, id_dst); // Print out each intermediate city and charging time
PrintCity(id_dst); // Print last city
cout << endl;
}

void FlightPlannerBase::PrintChargingCitiesAndTimes(const vector<StateAircraft>& v_path, const IdCity& id_dst) const {
auto fn_find_last_index_of_city = [this](vector<StateAircraft> v_path, int i) {
int j = i;
while (j < v_path.size() && v_path[i].id_city() == v_path[j].id_city()) {
j++;
// Finds the index where charging stops
auto fn_find_i_stop = [this](const vector<StateAircraft>& v_path, int i_start) {
int i_stop = i_start;
while (v_path[i_start].id_city() == v_path[i_stop].id_city()) { // Is same city
i_stop++;
if (i_stop == v_path.size()) { break; } // Reached the end of the path
}
return j - 1;

return i_stop - 1; // The last index before the city changes
};

// Prints out the change time
auto fn_print_charge_time = [this](vector<StateAircraft> v_path, int i, int j) {
PrintCity(v_path[i]);
double charge_time_hours = CalcChargeTime(v_path[i], v_path[j]);
auto fn_print_charge_time = [this](vector<StateAircraft> v_path, int i_arrive, int i_depart) {
PrintCity(v_path[i_arrive]);
double charge_time_hours = CalcChargeTime(v_path[i_arrive], v_path[i_depart]);
cout << ", " << setprecision(16) << charge_time_hours << "," << endl;
};

// Runs the lambda on each city the plane charges at
int ind_arrive = 1;
int ind_depart = -1;
int i_charge_start = 1;
int i_charge_stop = -1;

while (v_path[ind_arrive].id_city() != id_dst) {
ind_depart = fn_find_last_index_of_city(v_path, ind_arrive);
fn_print_charge_time(v_path, ind_arrive, ind_depart);
ind_arrive = ind_depart + 1;
while (v_path[i_charge_start].id_city() != id_dst) {
i_charge_stop = fn_find_i_stop(v_path, i_charge_start);
fn_print_charge_time(v_path, i_charge_start, i_charge_stop);
i_charge_start = i_charge_stop + 1; // Start at the next city
}
}

Expand All @@ -105,23 +104,25 @@ pair<double, double> FlightPlannerBase::GetLatLonRadians(const IdCity& id_city)
}

double FlightPlannerBase::CalcDistKm(const IdCity& src, const IdCity& dst) const {
double lat1r, lon1r, lat2r, lon2r, u, v;
const auto lat1r_lon1r = GetLatLonRadians(src);
const double lat1r = lat1r_lon1r.first;
const double lon1r = lat1r_lon1r.second;

// Get the latitude and longitude of the source and destination cities in radians
tie(lat1r, lon1r) = GetLatLonRadians(src);
tie(lat2r, lon2r) = GetLatLonRadians(dst);
const auto lat2r_lon2r = GetLatLonRadians(dst);
const double lat2r = lat2r_lon2r.first;
const double lon2r = lat2r_lon2r.second;

// Copilot auto-generated code
u = sin((lat2r - lat1r) / 2);
v = sin((lon2r - lon1r) / 2);
const double u = sin((lat2r - lat1r) / 2);
const double v = sin((lon2r - lon1r) / 2);
return 2.0 * RadEarth() * asin(sqrt(u * u + cos(lat1r) * cos(lat2r) * v * v));
}

double FlightPlannerBase::CalcChargeTime(const StateAircraft& state_lo, const StateAircraft& state_hi) const {
const IdCity& id = state_lo.id_city();
assert(id == state_hi.id_city()); // Charging needs to happen at the same city
assert(id == state_hi.id_city()); // Aircraft can't change cities while charging

const double flight_time_per_charge_hr = CalcChargeRateKmPerHr(id) / SpeedKmPerHr();
const double flight_time_per_charge_hr = CalcChargeRateKmPerHr(id) / SpeedKmPerHr(); // Called R in the readme
return state_lo.CalcBatteryDelta(state_hi) / flight_time_per_charge_hr;
}

Expand All @@ -138,13 +139,16 @@ void FlightPlannerExact::AddEdgesFlightsExact() {

double flight_time = CalcFlightTimeHours(src, dst);
if (flight_time < MaxFlightTimeHours()) {
// Flight that departs with full charge
double max_battery = MaxBatteryHours();
AddDirectedEdge(StateAircraft{src, max_battery}, StateAircraft{dst, max_battery - flight_time}, flight_time);

// Flight that arrives with zero charge
double min_battery = MinBatteryHours();
AddDirectedEdge(StateAircraft{src, flight_time + min_battery}, StateAircraft{dst, min_battery}, flight_time);
{
// Flight that departs with full charge
double max_battery = MaxBatteryHours();
AddDirectedEdge(StateAircraft{src, max_battery}, StateAircraft{dst, max_battery - flight_time}, flight_time);
}
{
// Flight that arrives with zero charge
double min_battery = MinBatteryHours();
AddDirectedEdge(StateAircraft{src, flight_time + min_battery}, StateAircraft{dst, min_battery}, flight_time);
}
}
}
}
Expand All @@ -161,6 +165,7 @@ void FlightPlannerGrid::AddVerticesGrid() {
}
}

// Finds the nearest battery level without going over
int FlightPlannerGrid::FindBatteryLevel(const double battery_level) const {
const vector<double>& v_bat = v_battery_levels();

Expand All @@ -177,14 +182,14 @@ void FlightPlannerGrid::AddEdgesDrivingGrid() {
for (auto vertex_i : vertices()) {
for (int j = 0; j < NumAirports(); j++) {
if (vertex_i.id_city() == IdCity{j}) { continue; }
double flight_time_ij = CalcFlightTimeHours(vertex_i.id_city(), IdCity{j});
double battery_i = vertex_i.battery_level_hours();
double battery_post = battery_i - flight_time_ij;
double battery_min = MinBatteryHours();
const double flight_time_ij = CalcFlightTimeHours(vertex_i.id_city(), IdCity{j});
const double battery_i = vertex_i.battery_level_hours();
const double battery_post = battery_i - flight_time_ij;
const double battery_min = MinBatteryHours();

if (battery_post < battery_min) { continue; }
int ind_lo = FindBatteryLevel(battery_post);
double battery_level_lo = v_battery_levels()[ind_lo];
const int ind_lo = FindBatteryLevel(battery_post);
const double battery_level_lo = v_battery_levels()[ind_lo];
StateAircraft vertex_j_lo{IdCity{j}, battery_level_lo};
AddDirectedEdge(vertex_i, vertex_j_lo, flight_time_ij);
}
Expand Down
4 changes: 2 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ int main(int argc, char** argv) {
return 1;
}

FlightPlannerExact(airports).SolvePathAndPrint(argv[1], argv[2]);
// FlightPlannerGrid(airports, 4).SolvePathAndPrint(argv[1], argv[2]);
FlightPlannerExact(airports).SolvePathAndPrint(argv[1], argv[2]); // Run the exact planner
// FlightPlannerGrid(airports, 4).SolvePathAndPrint(argv[1], argv[2]); // Run the grid planner

return 0;
};