Skip to content

Commit

Permalink
Merge pull request #282 from antarctica/0.5.x
Browse files Browse the repository at this point in the history
0.5.x Updating to include requirements for PolarRoute paper
  • Loading branch information
Ulvetanna authored Jul 10, 2024
2 parents de8a0e1 + 87092cc commit aea3046
Show file tree
Hide file tree
Showing 26 changed files with 353,081 additions and 2,211 deletions.
4 changes: 2 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ This includes a collection of test files which should be run according to which
>
> The files which have been changed during this PR can be listed using the command
git diff --name-only 0.4.x
git diff --name-only 0.5.x

- [ ] My changes require one or more test files to be updated for all regression tests to pass.

Expand All @@ -38,6 +38,6 @@ This includes a collection of test files which should be run according to which
- [ ] I have commented my code, particularly in hard-to-understand areas.
- [ ] I have updated the documentation of the codebase where required.
- [ ] My changes generate no new warnings.
- [ ] My PR has been made to the `0.4.x` branch (**DO NOT SUBMIT A PR TO MAIN**)
- [ ] My PR has been made to the `0.5.x` branch (**DO NOT SUBMIT A PR TO MAIN**)


3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ TestingNotebook.ipynb
/venv/
/data/
/resources/
/debugging/
*.output.*json
*output.vessel*
*output.route*
setup.cfg
pip.sh
datastore
Expand Down
1 change: 1 addition & 0 deletions examples/route_config/fuel.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"variable_speed": true,
"time_unit": "days",
"early_stopping_criterion": true,
"waypoints_splitting": false,
"save_dijkstra_graphs": true,
"smooth_path": {
"max_iteration_number": 1000,
Expand Down
1 change: 1 addition & 0 deletions examples/route_config/traveltime.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"variable_speed": true,
"time_unit": "days",
"early_stopping_criterion": true,
"waypoints_splitting": false,
"save_dijkstra_graphs": true,
"smooth_path": {
"max_iteration_number": 1000,
Expand Down
2 changes: 1 addition & 1 deletion polar_route/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.4.0"
__version__ = "0.5.0"
__description__ = "PolarRoute: Long-distance maritime polar route planning taking into account complex changing environmental conditions"
__license__ = "MIT"
__author__ = "Autonomous Marine Operations Planning (AMOP) Team, AI Lab, British Antarctic Survey"
Expand Down
34 changes: 30 additions & 4 deletions polar_route/crossing_smoothing.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,32 @@ def _traveltime_in_cell(self,xdist,ydist,U,V,S):
traveltime = np.inf
return self._unit_time(traveltime), dist

def _rhumb_line_distance(self,start_waypoint,end_waypoint):
'''
Defining the rhumbline distance from a given waypoint start and end point
Inputs:
start_waypoints - (list([Long,lat])) Start Waypoint location with long lat
end_waypoints - (list([Long,lat])) End Waypoint location with long lat
'''

# Defining a corrected distance based on rhumb lines
Xs,Ys = start_waypoint
Xe,Ye = end_waypoint

R = 6371.1*1000.
dY_corrected = np.log(np.tan((np.pi/4) + (Ye*(np.pi/180))/2)/np.tan((np.pi/4) + (Ys*(np.pi/180))/2))
dX = (Xe-Xs)*(np.pi/180)
dY = (Ye-Ys)*(np.pi/180)
if dY_corrected==0 and dY==0:
q = np.cos(Ye*(np.pi/180))
else:
q = dY/dY_corrected

distance = np.sqrt(dY**2 + (q**2)*(dX**2))*R
return distance


def _waypoint_correction(self,path_requested_variables,source_graph,Wp,Cp):
'''
Applies an in-cell correction to a path segments to determine 'path_requested_variables'
Expand Down Expand Up @@ -247,7 +273,8 @@ def _waypoint_correction(self,path_requested_variables,source_graph,Wp,Cp):
Su = source_graph['Vector_x']
Sv = source_graph['Vector_y']
Ssp = self._unit_speed(source_graph['speed'][case])
traveltime, distance = self._traveltime_in_cell(x,y,Su,Sv,Ssp)
traveltime, _ = self._traveltime_in_cell(x,y,Su,Sv,Ssp)
distance = self._rhumb_line_distance(Wp,Cp)

# Given the traveltime and distance between the two waypoints
# determine the path related variables (e.g. fuel usage, traveltime)
Expand Down Expand Up @@ -1049,12 +1076,11 @@ def blocked(self,new_cell,cell_a,cell_b):

percentage_diff1 = (max_new-start)*100
percentage_diff2 = (max_new-end)*100

if (percentage_diff1 <= self.blocked_sic*start) or (percentage_diff2 <= self.blocked_sic*end) or (max_new<=self.blocked_sic):
if (percentage_diff1 <= self.blocked_sic*start) or (percentage_diff2 <= self.blocked_sic*end) or max_new<=self.blocked_sic:
return False
else:
return True


def clip(self,cell_a,cell_b,case,x):
'''
Expand Down
88 changes: 62 additions & 26 deletions polar_route/route_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from polar_route.config_validation.config_validator import validate_waypoints
from meshiphi import Boundary
from meshiphi.utils import longitude_domain
from meshiphi.mesh_generation.environment_mesh import EnvironmentMesh


def _flattenCases(id,mesh):
neighbour_case = []
Expand Down Expand Up @@ -299,11 +301,14 @@ def __init__(self, mesh, config, waypoints, cost_func=NewtonianDistance):
# Load in the current cell structure & Optimisation Info̦
self.mesh = _json_str(mesh)
self.config = _json_str(config)
waypoints_df = _pandas_dataframe_str(waypoints)
self.waypoints_df = _pandas_dataframe_str(waypoints)

# #Splitting around waypoints
self._splitting_around_waypoints()

mesh_boundary = _mesh_boundary_polygon(self.mesh)
# Move waypoint to closest accessible cellbox if it isn't in one already
for idx, row in waypoints_df.iterrows():
for idx, row in self.waypoints_df.iterrows():
point = Point([row['Long'], row['Lat']])
# Only allow waypoints within an existing mesh
assert(point.within(mesh_boundary)), \
Expand All @@ -313,11 +318,11 @@ def __init__(self, mesh, config, waypoints, cost_func=NewtonianDistance):

adjusted_point = _adjust_waypoints(point, self.mesh['cellboxes'])

waypoints_df.loc[idx, 'Long'] = adjusted_point.x
waypoints_df.loc[idx, 'Lat'] = adjusted_point.y
self.waypoints_df.loc[idx, 'Long'] = adjusted_point.x
self.waypoints_df.loc[idx, 'Lat'] = adjusted_point.y

source_waypoints_df = waypoints_df[waypoints_df['Source'] == "X"]
des_waypoints_df = waypoints_df[waypoints_df['Destination'] == "X"]
source_waypoints_df = self.waypoints_df[self.waypoints_df['Source'] == "X"]
des_waypoints_df = self.waypoints_df[self.waypoints_df['Destination'] == "X"]

self.source_waypoints = list(source_waypoints_df['Name'])
self.end_waypoints = list(des_waypoints_df['Name'])
Expand All @@ -330,14 +335,10 @@ def __init__(self, mesh, config, waypoints, cost_func=NewtonianDistance):
self.smoothed_paths = None
self.dijkstra_info = {}






# ====== Loading Mesh & Neighbour Graph ======
# Zeroing currents if vectors names are not defined or zero_currents is defined
self.mesh = self._zero_currents(self.mesh)
self.mesh = self._fixed_speed(self.mesh)

# Formatting the Mesh and Neighbour Graph to the right form
self.neighbour_graph = pd.DataFrame(self.mesh['cellboxes']).set_index('id')
Expand Down Expand Up @@ -399,7 +400,7 @@ def __init__(self, mesh, config, waypoints, cost_func=NewtonianDistance):
self.mesh['cellboxes'] = cbxs.to_dict('records')

# ====== Waypoints ======
self.mesh['waypoints'] = waypoints_df
self.mesh['waypoints'] = self.waypoints_df
# Initialising Waypoints positions and cell index
wpts = self.mesh['waypoints']
wpts['index'] = np.nan
Expand All @@ -426,21 +427,24 @@ def __init__(self, mesh, config, waypoints, cost_func=NewtonianDistance):
# ==== Printing Configuration and Information
self.mesh['waypoints'] = pd.read_json(StringIO(self.mesh['waypoints']))

# # ===== Running the route planner for the given information
# if ("dijkstra_only" in self.config) and self.config['dijkstra_only']:
# self.compute_routes()
# else:
# self.compute_routes()
# self.compute_smoothed_routes()

def _splitting_around_waypoints(self):
"""
Applying splitting around waypoints if this is defined in config. This is applied
inplace.
Appied to terms:
self.mesh - MeshiPhi Vehicle Mesh in JSON format
self.config - PolarRoute config file
self.waypoints_df - Pandas DataFrame of Waypoint locations
# # === Saving file to output or saving it to variable output
# output = self.to_json()
# if ('output' in self.config) and (type(self.config['output']) == str):
# with open(self.config['output'], 'w') as f:
# json.dump(output,f)
# else:
# self.output = output
"""
if ('waypoint_splitting' in self.config) and (self.config['waypoint_splitting']):
logging.info(' Splitting around waypoints !')
msh = EnvironmentMesh.load_from_json(self.mesh)
wps_points = [(entry['Lat'],entry['Long']) for _,entry in self.waypoints_df.iterrows()]
msh.split_points(wps_points)
mesh = msh.to_json()
self.mesh['cellboxes'] = mesh['cellboxes']
self.mesh['neighbour_graph'] = mesh['neighbour_graph']

def _zero_currents(self,mesh):
'''
Expand Down Expand Up @@ -471,13 +475,45 @@ def _zero_currents(self,mesh):
mesh['cellboxes'][idx] = cell

return mesh

def _fixed_speed(self,mesh):
'''
Applying max speed for all cellboxes that are accessible
Input
mesh (JSON) - MeshiPhi Mesh input
Output:
mesh (JSON) - MeshiPhi Mesh Corrected
'''

# Zeroing currents if both vectors are defined and zeroed
if ('fixed_speed' in self.config):
if self.config['fixed_speed']:
logging.info('Setting all speeds max speed for Mesh !')
max_speed = mesh['config']['vessel_info']['max_speed']
for idx,cell in enumerate(mesh['cellboxes']):
# print(cell.keys())
if 'speed' in cell.keys():
cell['speed'] = [max_speed,
max_speed,
max_speed,
max_speed,
max_speed,
max_speed,
max_speed,
max_speed]
mesh['cellboxes'][idx] = cell
else:
continue

return mesh

def to_json(self):
'''
Outputting the information in JSON format
'''
mesh = copy.copy(self.mesh)
mesh['config']['route_info'] = self.config
mesh['waypoints'] = mesh['waypoints'].to_dict()
output_json = json.loads(json.dumps(mesh))
del mesh
Expand Down
6 changes: 6 additions & 0 deletions polar_route/vessel_performance/vessel_factory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from polar_route.vessel_performance.vessels.SDA import SDA
from polar_route.vessel_performance.vessels.slocum import SlocumGlider
from polar_route.vessel_performance.vessels.boatymcboatface import BoatyMcBoatFace
from polar_route.vessel_performance.vessels.twin_otter import TwinOtter
from polar_route.vessel_performance.vessels.windracer import Windracer
from polar_route.vessel_performance.vessels.example_ship import ExampleShip

class VesselFactory:
Expand All @@ -20,6 +23,9 @@ def get_vessel(cls, config):
vessel_requirements = {"SDA": (SDA, ["max_speed", "unit", "beam", "hull_type", "force_limit", "max_ice_conc",
"min_depth"]),
"Slocum": (SlocumGlider, ["max_speed", "unit", "max_ice_conc", "min_depth"]),
"BoatyMcBoatFace": (BoatyMcBoatFace, ["max_speed", "unit", "max_ice_conc", "min_depth"]),
"TwinOtter": (TwinOtter, ["max_speed", "unit", "max_elevation"]),
"Windracer": (Windracer, ["max_speed", "unit","max_ice_conc", "max_elevation"]),
"example_ship": (ExampleShip, ["max_speed", "unit", "beam", "hull_type", "force_limit",
"max_ice_conc", "min_depth"])
}
Expand Down
Loading

0 comments on commit aea3046

Please sign in to comment.