Event Correction

How to run this?

Go here to instantly run all code below inside of your browser.

Use case

This is a more advanced use case. If you have little coding experience we recommend following the "Basics" and "Speed-Fuel curve" use cases first.

When an event takes place that alters the performance of the vessel, like when it gets cleaned or an ESD is installed, the Toqua Ship Kernel might not reflect the new vessel condition immediately. This could be because the data has not been uploaded yet or there's not enough data yet for the Ship Kernel to be sufficiently accurate.

In such cases you can let the Ship Kernel know of any expected performance changes. The Ship Kernel will use that information to apply a correction to the predictions following the event. This correction will eventually be removed by itself once the model has seen enough data after the event.

We will show how you can add such an expected performance change, how to see which corrections are applied and how those affect the model.

Setup

Fill in the IMO number of your ship below.

IMO_NUMBER = "9999999"

Helper functions

Some helper functions to not clutter our code too much later on.

import requests

def make_api_call(method, url, payload=None):
    headers = {
        "accept": "application/json",
        "content-type": "application/json",
        "X-API-Key": API_KEY,
    }
    
    if method == 'GET':
        response = requests.get(url, headers=headers)
    elif method == 'POST':
        response = requests.post(url, json=payload, headers=headers)
    else:
        print("Error: Invalid method")
        return None

    if not str(response.status_code).startswith("2"):
        print(f"Error: API request failed with status code {response.status_code}")
        print(response.text)
        return response

    return response

def predict(imo_number, payload):
    url = f"https://api.toqua.ai/ships/{imo_number}/models/latest/predict"
    return make_api_call('POST', url, payload)

def get_metadata(imo_number):
    url = f"https://api.toqua.ai/ships/{imo_number}/models/latest/metadata"
    return make_api_call('GET', url)

def ingest_event(imo_number, payload):
    url = f"https://api.toqua.ai/ships/{imo_number}/data/events"
    return make_api_call('POST', url, payload)

Determining the current vessel performance

To see the effect of a correction, we must first know what the performance of the vessel was before the correction.
To find out, we'll predict what the fuel consumption is for a STW of 12 given a single set of conditioning parameters.

We'll call this prediction the baseline_prediction and save it for later when we want to compare against the corrected Ship Kernel.

api_input = {
    "data": {
        "stw": [12],                    # [knots]
        "wave_direction": [0],          # [degrees]
        "wave_height": [2],             # [m]
        "wind_direction": [0],          # [degrees]
        "wind_speed": [6],              # [m/s]
        "current_direction": [0],       # [degrees]
        "current_speed": [0],           # [m]
        "draft_avg": [12],              # [m]
        "trim": [0],                    # [degrees]
        "fuel_specific_energy": [41]    # [MJ/kg]
    }
}

baseline_prediction = predict(IMO_NUMBER, api_input).json()
del baseline_prediction["errors"]
print(baseline_prediction)
{'sog': [12.0], 'stw': [12.0], 'me_rpm': [44.690351382184815], 'me_power': [7323.964299889474], 'me_fo_consumption': [31.986373946738524], 'me_fo_emission': [100.82105068011984]}

Setting an expected performance change

Whenever there is an event that is expected to change the performance of the ship, you can provide an expected_performance_change key to convey the expected change. The value for expected_performance_change should fall within the range of -100 to 100. A value of 100 indicates that the ship's performance will increase by 100%, meaning it will use 100% less power, resulting in a power of 0 kW. On the other hand, a value of -100 means that the ship will use 100% more power, resulting in the doubling of required power. A value of 0 indicates no performance change.

Using the value for expected_performance_change, the Ship Kernel determines a correction factor. Only one correction factor can be present at a time, and any previous corrections will be overwritten when a new expected_performance_change is given. You can ensure that previous corrections remain applied by providing null or simply leaving out the expected_performance_change key.

In the following example, we will show how to ingest a hull cleaning event that occurred yesterday of which we expect it will result in a 15% improvement in ship performance.

import datetime
import json


yesterday = datetime.datetime.now(tz=datetime.timezone.utc) - datetime.timedelta(days=1)
event_data = {
    "type": ["hull_cleaning", "esd"],
    "description": ["Partial hull cleaning, flat bottom only", "retrofit of bulbous bow"],
    "expected_performance_change": [15, None],
    "datetime_end": [
        yesterday.isoformat(),
        "2024-05-14T12:00:00+00:00"
    ]
}
print("Ingesting the following events:")
print(json.dumps(event_data, indent=4))

response = ingest_event(IMO_NUMBER, event_data)
print(f"Event ingested. Response status code: {response.status_code}")
Ingesting the following events:
{
    "type": [
        "hull_cleaning",
        "esd"
    ],
    "description": [
        "Partial hull cleaning, flat bottom only",
        "retrofit of bulbous bow"
    ],
    "expected_performance_change": [
        15,
        null
    ],
    "datetime_end": [
        "2023-12-07T13:35:09.504421+00:00",
        "2024-05-14T12:00:00+00:00"
    ]
}
Event ingested. Response status code: 201

Viewing the applied corrections

You can check if there are any corrections currently applied by inspecting the corrections array in the model metadata.

The correction_date key in the corrections array indicates the date on which the correction started to be applied. The creation_date is the date at which the event was uploaded. The correction_factor indicates the factor with which the Main Engine Power will be multiplied.

metadata = get_metadata(IMO_NUMBER).json()

metadata["corrections"]
[{'correction_date': '2023-12-07T13:35:09.504421+00:00',
  'correction_factor': 0.85,
  'creation_date': '2023-12-08T13:35:12.998456+00:00'}]

We can see that the event we uploaded is already being applied. This makes sense, as the event took place yesterday.

The correction_factor is set to 0.85 as we expected a performance increase of 15%.

Determining the effect of a correction

To see how the correction affects our Ship Kernel, we will do a new prediction and compare it to the old prediction we have saved.

print("Prediction before correction:")
print(baseline_prediction)

corrected_prediction = predict(IMO_NUMBER, api_input).json()
del corrected_prediction["errors"]

print("Prediction after correction:")
print(corrected_prediction)
Prediction before correction:
{'sog': [12.0], 'stw': [12.0], 'me_rpm': [44.690351382184815], 'me_power': [7323.964299889474], 'me_fo_consumption': [31.986373946738524], 'me_fo_emission': [100.82105068011984]}
Prediction after correction:
{'sog': [12.0], 'stw': [12.0], 'me_rpm': [44.690351382184815], 'me_power': [7323.964299889474], 'me_fo_consumption': [31.986373946738524], 'me_fo_emission': [100.82105068011984]}

The me_rpm, me_power and me_fo_consumption have increased.

Using the Pandas libray we can easily determine the exact ratio of the corrected predictions to the baseline predictions.

import pandas as pd

# Create a DataFrame with corrected and baseline predictions as rows
df = pd.concat([pd.DataFrame(corrected_prediction), pd.DataFrame(baseline_prediction)]).set_index([['corrected', 'baseline']])
# Calculate the ratio of corrected values to baseline values and add a new row 'ratio' to the DataFrame
df.loc['ratio', :] = df.loc['corrected', :] / df.loc['baseline', :]
df
sog stw me_rpm me_power me_fo_consumption me_fo_emission
corrected 12.0 12.0 44.690351 7323.9643 31.986374 100.821051
baseline 12.0 12.0 44.690351 7323.9643 31.986374 100.821051
ratio 1.0 1.0 1.000000 1.0000 1.000000 1.000000

As expected, the Main Engine power has decreased by 15%.

The Main Engine RPM and Main Engine Fuel Oil Consumption have decreased too, but by a different magnitude. Given the nature of how these variables relate to one another the decrease this is as expected.