graph LR;
A(SOG) --> B(Step 1)
Z(Power) -->B
B --> C(Step 2)
C --> D(Step 3)
D --> E(Step 4)
E --> L{Convergence?}
L -- No --> C
L -- Yes --> M[Return STW]
Current Correction (current)
This module represents the Appendix H of the ITTC, and provides the functionality to correct for current.
The current correction approaches can only really be used if the ITTC speed power process has been followed closely. The corrections require that there are multiple runs for several speed settings. This allows a regression to be made. If there is only one speed setting then the methods described in appendix H will not be applicable. If multiple double runs or runs at different speeds have not been performed then ship current sensors, met-ocean data, or other methods will have to be used.
1 Which to choose?
Although the two methods are not mutually exlusive, they do have different minimum data gathering requirements in order to be used.
- The iterative method can be used when the seatrial has performed a SINGLE set of double runs at DIFFERENT power settings
- The Mean of Means method can be performed when there have been TWO sets of double-runs at the SAME power setting
2 Iterative Method
The iterative method assumes a current that varies with a semidurational period. This period is typically 12 hours, 25 minutes and 12 seconds, which can also be described as 0.51753 of a day. The function can use any speed unit and any unit of power although, the original ITTC documentation uses Knots and kWh respectively.
The process for estimating the current and the true speed through water is described below.
2.1 Step 1: Estimate the speed-power curve
Using the known speed over ground (\(V_g\)) values and observed power (\(P_{obs}(V_s)\)) values for each power/speed setting, find the coefficients of the equation
\[P(V_s) =a+ bV_s^q\]
Where:
- P(V_s) = the engine power at speed V_s
- a,b, q = unknown coefficients
2.2 Step 2: Estimate speed through water from observed power and speed-power model
using the coefficients of the speed power mode and the observed power calculate and estimate of the necessary speed through water using
\[V_s = \sqrt[q]{\frac{P(V_s)-a}{b}}\]
2.3 Step 3: Estimate Current and current coefficients
Using \(V_c = V_G - V_s\), get an estimate of \(V_c\) for each speed setting and use it to find the coefficients of the following equation using leasts-squares
\[V_c = V_{C,C}\textrm{cos}(\frac{2\pi}{T_c}t) + V_{C,S}\textrm{sin}(\frac{2\pi}{T_c}t) + V_{C,T}t + V_{C,0}\]
Where:
- V_c = the current speed in knots
- T_c = is the period of variation of current speed, it is measured in hours
- t = the time for each run relative to the first run, it is measured in hours.
- \(V_{C,C}, V_{C,S}, V_{C,T}, V_{C,0} =\) are unknown factors which found through iteration
Once the coefficients have been calculated the value of \(V_c\) can be updated
2.4 Step 4: Re-calculate the speed-power curve coefficients
Using the updated value of \(V_c\) update the value of \(V_s\) and re-calculate the parameters a.b and q for the speed power curve.
Using the new speed power curve and the estimated values of \(V_s\) create the predicted values of engine power.
2.5 Step 5: Measure the error between the observed and predicted Enginer power
Using the equation
\[SSE =\sum (P_{pred}(V_s)-P_{obs}(V_s))^2\]
measure the sum square error of the estimates. if the error is larger than tolerance go to Step 2, otherwise if the process has converged and the error minimised, finish and return the final values of \(V_c\) and \(V_s\)
When setting the tolerance any small value is acceptable. typically a tolerance of \(10^{-6}\) is used however values as large as \(10^{-2}\) would probably have negligable impact on the final analysis
2.6 Diagram
The below diagram provides a schematic of the process described above
2.7 Note:
This algorithm is not guarenteed to converge, nor is it guarenteed to reduce the square error each iteration. As such the function returns the current and power law parameters which produced the lowest error. It also returns a dataframe containing the error for each iteration
ITTC equations: H-1 to H-5
This equation is not vectorised
2.8 estimate_speed_through_water
estimate_speed_through_water (power:float, sog:float, t:float, T_c:float, tolerance:float=1, max_iter:float=1000, p0:list=[0, 1, 3], bounds:tuple=([0, 0, 2.5], [5000, 20, 3.5]))
| Type | Default | Details | |
|---|---|---|---|
| power | float | The engine power, typically the ‘ideal condition’ is used [W] | |
| sog | float | Speed over ground of the vessel [m/s] | |
| t | float | Time difference from current run to first run [hours] | |
| T_c | float | Time duration period of the tide typically 12.42 hours [hours] | |
| tolerance | float | 1 | The process terminates when the error falls below the specified threshold in Watts |
| max_iter | float | 1000 | Max imum number of iterations before process terminates |
| p0 | list | [0, 1, 3] | the initial guess for the power law coefficients they represent the expeonents a + bX^c [None] |
| bounds | tuple | ([0, 0, 2.5], [5000, 20, 3.5]) | Bounds the power law equation to within realistic values |
| Returns | tuple | Outputs a tuple of the stw, current, current coefficients, and speed power coefficints that minimised the error. Also returns a dataframe or the error per iteration |
To help gain an intuitive understanding of the concept of the semidiurnal current, an example is plotted below. Not that the last terms in the current model (i.e. ‘\(V_{C,T}t + V_{C,0}\)’) has no periodic component. This means that the iterative method can produce increasingly incorrect results over longer periods.
T_c = 12
V_c_C=-2
V_c_S=-2
V_c_T=0.1
V_c_0=0.1
times = np.linspace(0, 24, 200)
current_speed = V_c_C * np.cos((2 * np.pi / T_c) * times) + V_c_S * np.sin((2 * np.pi / T_c) * times) + V_c_T * times + V_c_0
plt.plot(times, current_speed)
plt.xlabel('Time (hours)')
plt.ylabel('Current speed')
plt.title('Current speed over 24 hours using the iterative method')
plt.grid(True)
plt.show()
The example below demonstrates the process of identifying current speed using speed overground and the current equation. It should be noted that this method is an estimate only and as can be seen by the example can still produce a significant error.
T_c = 12
V_c_C=-2
V_c_S=-2
V_c_T=0.1
V_c_0=0.1
t = np.array([0, 1, 2, 3, 4]) # Time values relative to the first run
sog = np.array([5, 10, 15, 20, 25])
V_c_true = V_c_C * np.cos((2 * np.pi / T_c) * t) + V_c_S * np.sin((2 * np.pi / T_c) * t) + V_c_T * t + V_c_0
V_s_true = V_c_true + sog
a_true = 2
b_true = 3
c_true = 3
power = a_true + b_true * V_s_true ** c_true
stw, current_speed, _, _, error_df = estimate_speed_through_water(power, sog, t, T_c)
print("Estimated speed through water:", stw)
print("The error on the estimated speed through water is: "+str(stw-V_s_true) )
plt.plot(t, V_s_true)
plt.scatter(t, stw, color = 'red')
plt.xlabel('Time (hours)')
plt.ylabel('Speed Through Water')
plt.title('True STW (blue) and estimated STW (red)')
plt.grid(True)
plt.show()Estimated speed through water: [ 3.36403099 7.8889879 13.11458325 19.03103214 25.44343601]
The error on the estimated speed through water is: [0.26403099 0.42103871 0.54663406 0.63103214 0.67548682]

The error on the calculation can also be visualised.
plt.plot(error_df['Iteration'], error_df['Power_Error'])
plt.xlabel('Iteration')
plt.ylabel('Sum of squared errors')
plt.title('Change in error per iteration')
plt.grid(True)
plt.show()
3 Mean of means method
Another approach to finding the current
\[V_s = \frac{V_{G1} + 3V_{G2}+ 3V_{G3} + V_{G4}}{8}\]
Where \(V_s\) is the speed through water and \(V_{G1}\) to \(V_{G4}\) are the mean speed over-ground for each double run at a given speed.
The current is assumed to have a parabolic form and as such can be expressed as
\[V_c = V_{C,2} t^2 + V_{C,1} t +V_{C,1}\]
Where \(V_c\) is the current speed, and \(V_{C,2}\), \(V_{C,1}\), \(V_{C,0}\) are unknown factors.
The solution to the current equation can therefore be found by treating it as a system of linear equations of the form \(y = X\beta\). The system of linear equations is shown below.
\[\begin{bmatrix} V_{G1} - V_s \\ V_{G2} - V_s \\ V_{G3} - V_s \\ V_{G4} - V_s \\ \end{bmatrix} = \begin{bmatrix} (t + 3\Delta t)^2 & -(t + 3\Delta t) & 1 \\ (t + \Delta t)^2 & -(t + \Delta t) & 1 \\ (t - \Delta t)^2 & -(t - \Delta t) & 1 \\ (t - 3\Delta t)^2 & -(t - 3\Delta t) & 1 \\ \end{bmatrix} \cdot \begin{bmatrix} V_{C,2}\\ V_{C,1} \\ V_{C,0} \\ \end{bmatrix} \]
This equation demonstrates the need to perform two double runs, i.e. 4 runs as this will result in four equations with the 3 uknowns of \(V_{C,2}\), \(V_{C,1}\), \(V_{C,0}\). It should be noted that as current is not a parabola the values found should be used for interpolation not extrapolation. Setting the example below to 24 hours will produce terrifying torrents, which are obviously not realistic.
ITTC equations: H-6 to H-11
This equation is not vectorised
3.1 current_mean_of_means
current_mean_of_means (sog:numpy.ndarray, start_time:float, time_between_runs:float)
| Type | Details | |
|---|---|---|
| sog | ndarray | The mean speed over ground across a double run, |
| start_time | float | Time in decimal hours when the first run took place, |
| time_between_runs | float | Time in decimal hours between each run. Note the time difference must be consistent between all runs |
| Returns | float | Outputs a tuple of the stw vector, the current vector and the coefficients |
sog = np.array([10, 12.6, 12.6, 11]) # Mean speed over ground across a double run
start_time = 0.0 # Time in decimal hours when the first run took place
time_between_runs = 1 # Time in decimal hours between each run
stw, current, coefs= current_mean_of_means(sog, start_time, time_between_runs)
print("Current speed m/s:", current)Current speed m/s: [-0.525 -0.405 -0.525 -0.885]
def quadratic_function(t, coeffs):
a, b, c = coeffs
return a * t**2 + b * t + c
# Calculate the current speed throughout the day
times = np.linspace(0, 4, 100)
current_strength = quadratic_function(times, coefs)
# Plot the current speed over the 24-hour period
plt.plot(times, current_strength)
plt.xlabel('Time (hours)')
plt.ylabel('Current speed')
plt.title('Current speed over four hours using the mean of means method')
plt.grid(True)
plt.show()
import nbdev; nbdev.nbdev_export()