Speed-Fuel Curve
How to run this?
Go here to instantly run all code below inside of your browser.
Use case
In the basics use case we demonstrated how to simulate a ship's performance in a single condition at a single speed.
We will extend this example and simulate a ship's performance over multiple speeds. This will allow us to construct a speed-fuel table and speed-fuel curve, showing the required fuel consumption for each speed of the ship.
Authorize
Let's first make sure we're authorized to use the API by running the following code.
If everything is alright, you should see a list of ships.
import json
import requests
API_URL = "https://api.toqua.ai"
url = "https://api.toqua.ai/ships/"
headers = {"accept": "application/json", "X-API-Key": API_KEY}
response = requests.get(url, headers=headers)
print(json.dumps(response.json(), indent=2))
[
{
"name": "Demo Vessel",
"imo_number": 9999999,
"type": "Tanker",
"country": "SC",
"build_year": 2015,
"shipyard": "Toqua Shipyard",
"dwt": 220000.0,
"beam": 55.0,
"loa": 300.0,
"mcr": 21900.0,
"max_rpm": 60.0,
"uuid": "eycrYbrzJNsJecGqKraUCn"
}
]
Fill in the IMO number of the ship you want to analyze.
IMO_NUMBER = 9999999
Conditioning parameters
Let's again define our conditions.
wind_speed = 6 # [m/s]
wind_direction = 180 # [degrees]
wave_height = 2 # [m]
wave_direction = 90 # [degrees]
current_speed = 0.5 # [m/s]
current_direction = 0 # [degrees]
mean_draft = 20 # [m]
trim = -1 # [m]
ship_heading = 0 # [degrees]
fuel_specific_energy = 41.5 # [MJ/kg]
Our entrypoint will this time be a list, rather than a single value. We will analyze the ship's fuel consumption in speeds ranging from a STW of 8 knots to 16 knots.
stw = list(range(8, 17))
print(stw)
[8, 9, 10, 11, 12, 13, 14, 15, 16]
Define the API input
Remember that the Toqua API expects the model input to look like this:
{
"date": "...",
"data": {
"stw": [...],
"draft_avg": [...],
"trim": [...],
"wave_direction": [...],
"wave_height": [...],
"wave_period": [...],
"current_speed": [...],
"current_direction": [...],
"wind_direction": [...],
"wind_speed": [...],
"ship_heading": [...],
"fuel_specific_energy": [...]
}
}
We will again ignore the date
parameter for now.
Each parameter expects a list of values and all lists must have exactly the same length.
The parameter value at each index $i$ corresponds to $$Condition_i = {stw_i, wave_direction_i, wave_speed_i, draft_avg_i, ...}$$
Only the stw
parameter is currently a list and we are only simulating a single condition, so we will have to duplicate the conditioning parameters once for each element in the stw
list.
length_input = len(stw)
api_input = {
"data": {
"stw": stw,
"wave_direction": [wave_direction]*length_input,
"wave_height": [wave_height]*length_input,
"wind_direction": [wind_direction]*length_input,
"wind_speed": [wind_speed]*length_input,
"current_direction": [current_direction]*length_input,
"current_speed": [current_speed]*length_input,
"draft_avg": [mean_draft]*length_input,
"trim": [trim]*length_input,
"ship_heading": [ship_heading]*length_input,
"fuel_specific_energy": [fuel_specific_energy]*length_input
}
}
Query the API
def query_api(imo_number, payload):
url = f"https://api.toqua.ai/ships/{imo_number}/models/latest/predict"
headers = {
"accept": "application/json",
"content-type": "application/json",
"X-API-Key": API_KEY,
}
response = requests.post(url, json=payload, headers=headers)
return response
Let's look at the values the model predicts.
response = query_api(IMO_NUMBER, api_input)
print(response.json())
{'sog': [7.02808, 8.02808, 9.02808, 10.02808, 11.02808, 12.02808, 13.02808, None, None], 'stw': [8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, None, None], 'me_rpm': [35.563477161420984, 38.86856315406255, 42.3286138994565, 45.958934292444695, 49.77248238124245, 53.780632616735986, 57.993745359998364, None, None], 'me_power': [4014.487664675082, 5034.96591338081, 6306.250909817854, 7883.151679670297, 9830.679471751078, 12225.665331559763, 15158.644854083537, None, None], 'me_fo_consumption': [18.24386889835794, 22.461921943399787, 27.57687777045103, 33.79875072031291, 41.47119688825557, 51.21728628254814, 64.2267638497258, None, None], 'me_fo_emission': [57.842186342243835, 71.21552352154902, 87.43249097121499, 107.15893915875206, 131.48442973421427, 162.38440615881885, 203.63095478555562, None, None], 'errors': [{'error_code': 'max_mcr_limit_exceeded', 'description': '90% Maximum MCR (19710 kW) exceeded.', 'indices': [8]}, {'error_code': 'max_rpm_limit_exceeded', 'description': 'Maximum RPM (60.0 RPM) exceeded.', 'indices': [7, 8]}]}
Speed-Fuel Table
Using the Pandas library we can transform this output into table format to make it easier on the eyes, and for future data transformations.
Let's see if any errors occurred during prediction.
import pandas as pd
output_json = response.json()
errors = output_json['errors']
print(errors)
[{'error_code': 'max_mcr_limit_exceeded', 'description': '90% Maximum MCR (19710 kW) exceeded.', 'indices': [8]}, {'error_code': 'max_rpm_limit_exceeded', 'description': 'Maximum RPM (60.0 RPM) exceeded.', 'indices': [7, 8]}]
We can see that on indices 7 and 8 the model failed to predict the fuel consumption. This is because for those conditions the predicted power would be above the ship's MCR or Max RPM.
On these indices, the output will contain null
values.
# remove the errors from the output json so pandas can read it
del output_json['errors']
df = pd.DataFrame(output_json)
df
sog | stw | me_rpm | me_power | me_fo_consumption | me_fo_emission | |
---|---|---|---|---|---|---|
0 | 7.02808 | 8.0 | 35.563477 | 4014.487665 | 18.243869 | 57.842186 |
1 | 8.02808 | 9.0 | 38.868563 | 5034.965913 | 22.461922 | 71.215524 |
2 | 9.02808 | 10.0 | 42.328614 | 6306.250910 | 27.576878 | 87.432491 |
3 | 10.02808 | 11.0 | 45.958934 | 7883.151680 | 33.798751 | 107.158939 |
4 | 11.02808 | 12.0 | 49.772482 | 9830.679472 | 41.471197 | 131.484430 |
5 | 12.02808 | 13.0 | 53.780633 | 12225.665332 | 51.217286 | 162.384406 |
6 | 13.02808 | 14.0 | 57.993745 | 15158.644854 | 64.226764 | 203.630955 |
7 | NaN | NaN | NaN | NaN | NaN | NaN |
8 | NaN | NaN | NaN | NaN | NaN | NaN |
There we have our speed-fuel table. For speeds ranging from 8 to 16 knots it tells us the predicted fuel consumption in the conditions we defined earlier.
Speed-Fuel Curve
Finally, to make things more tangible we can use the Plotly library to visualize our table as a speed-fuel curve.
import plotly.express as px
fig = px.line(df, x="stw", y="me_fo_consumption", title='Speed-Fuel Curve')
fig.update_layout(xaxis_title='Speed Through Water [kn]',
yaxis_title='Fuel Consumption [mt/day]')
fig.show()
fig.write_image("speed-fuel-curve.png")
import plotly.express as px
fig = px.line(df, x="stw", y="me_fo_consumption", title='Speed-Fuel Curve')
fig.update_layout(xaxis_title='Speed Through Water [kn]',
yaxis_title='Fuel Consumption [mt/day]')
fig.show()
Updated 12 months ago