Source code for deltares_wave_toolbox.cores.core_time

# SPDX-License-Identifier: GPL-3.0-or-later
import numpy as np
from numpy import float64
from numpy.typing import NDArray

import deltares_wave_toolbox.cores.core_engine as core_engine


[docs] def sort_wave_params( hWave: NDArray[float64], tWave: NDArray[float64] ) -> tuple[NDArray[float64], NDArray[float64]]: """Sorts the wave height and wave period This functions sorts the wave heights and wave periods in arrays hWave and tWave, and stores them in arrays hWaveSorted and tWaveSorted. The sorting is done such that in hWaveSorted the wave heights of hWave are sorted in descending order. This same sorting is applied to tWave, with the result in tWaveSorted. This means that hWaveSorted(i) and hWaveSorted(i) correspond to the wave height and wave period of the same wave. Parameters ---------- hWave : NDArray[float64] 1D array containing the wave heights of the individual waves [m] tWave : NDArray[float64] 1D array containing the periods of the individual waves [s] Returns ------- tuple[NDArray[float64], NDArray[float64]] hWaveSorted : NDArray[float64] 1D array containing wave heights of the individual waves, sorted in descending order [m] tWaveSorted : NDArray[float64] 1D array containing wave periods of the individual waves, using the same sorting (re-arranging) as applied to hWave [s] Raises ------ ValueError Input error, Input arrays hWave is not 1D ValueError Input error, Input array tWave is not 1D ValueError Input arrays hWave and tWave have different length Example ------- >>> import numpy as np >>> hWave = np.asrange([2,4,3]) >>> tWave = np.asrange([6,7,8]) >>> # Then we get: >>> [hWaveSorted, tWaveSorted] = sort_wave_params(hWave,tWave) >>> #hWaveSorted = np.asrange([4,3,2]) >>> #tWaveSorted = np.asrange([7,8,6]) (and not: tWaveSorted = [8 7 6]) """ # --- ensure input is of type ndarray hWave = core_engine.convert_to_array_type(hWave) tWave = core_engine.convert_to_array_type(tWave) # Check on input arguments # --- Check whether the size of the input arguments is identical is1d_array = core_engine.is1darray(hWave) if not is1d_array: raise ValueError("sort_wave_params: Input error, Input arrays hWave is not 1D") is1d_array = core_engine.is1darray(tWave) if not is1d_array: raise ValueError("sort_wave_params: Input error, Input array tWave is not 1D") # --- Check whether number of elements in input arguments is identical isequal_length = len(hWave) == len(tWave) if not isequal_length: raise ValueError( "InputError", "Input arrays hWave and tWave have different length" ) # --- Number of waves nWave = len(hWave) # --- If there are no waves, return to calling function hWaveSorted = np.empty((0, 0)) tWaveSorted = np.empty((0, 0)) if nWave < 1: return hWaveSorted, tWaveSorted # Computational core # --- Sort the wave height in descending order iSorted = np.argsort(hWave)[ ::-1 ] # term [::-1] reverses the array, meaning descending order. hWaveSorted = hWave[iSorted] # --- Sort the wave periods tWaveSorted = tWave[iSorted] # return hWaveSorted, tWaveSorted
[docs] def determine_zero_crossing( t: NDArray[float64], xTime: NDArray[float64], typeCross: str = "down", ) -> tuple[int, NDArray[float64]]: """Determines zero-crossings (number and positions) of signal. This function determines the zero-crossings of a given time signal xTime = xTime(t). Here, t stands for time, and xTime for a given signal (for example, time series of measured surface elevation). The type of zero-crossing can be either up-crossing or down-crossing. Parameters ---------- t : NDArray[float64] 1D array containing time axis. The numbers in the array t must be increasing and uniformly spaced (uniform time step) [s] xTime : NDArray[float64] 1D array containing signal values, i.e. the time series of the signal. The value xxTime(i) must be the signal value at time t(i). Usually water surface elevation [m] typeCross : str, optional Search for up- or down-crossings, by default "down" Returns ------- tuple[int, NDArray[float64]] nWave : int Number of waves in the signal, where one wave corresponds to two successive zero-crossings. Wave i starts at time tCross(i), and end at time tCross(i+1) [-] tCross : NDArray[float64] 1D array of length (nWave+1), containing the time of all zero-crossings. The time of the zero-crossings is determined by linear interpolation. Note that in case of no zero-crossing, the array tCross is empty. Note that in case of one zero-crossing, the number of waves is zero. [s] Raises ------ ValueError Input error: Input arrays t is not 1D ValueError Input error: Input arrays x is not 1D ValueError Input error: Input arrays t and x have different length ValueError Input error: Wrong input argument for typeCross """ # --- ensure input is of type ndarray t = core_engine.convert_to_array_type(t) xTime = core_engine.convert_to_array_type(xTime) # Perform checks on the input arguments is1d_array = core_engine.is1darray(t) if not is1d_array: raise ValueError("sort_wave_params: Input error: Input arrays t is not 1D") is1d_array = core_engine.is1darray(xTime) if not is1d_array: raise ValueError("sort_wave_params: Input error: Input arrays x is not 1D") # --- Check whether the length of arrays t and x the same isequal_length = len(t) == len(xTime) if not isequal_length: raise ValueError( "sort_wave_params: Input error: Input arrays t and x have different length" ) # Computational core nWave = -1 tCross = np.empty((0, 0)) dt = t[1] - t[0] nTime = len(t) # --- Loop over all time levels, to determine zero-crossings if typeCross.lower() == "up": # --- Upcrossings for iTime in np.arange(0, nTime - 1): if xTime[iTime] <= 0 and xTime[iTime + 1] > 0: # --- Add 1 to counter number of zero-crossings nWave = nWave + 1 # --- Determine time level of zero-crossing by means of linear # interpolation tCrossing = t[iTime] + dt * xTime[iTime] / ( xTime[iTime] - xTime[iTime + 1] ) tCross = np.append(tCross, tCrossing) elif typeCross.lower() == "down": # --- Downcrossings for iTime in np.arange(0, nTime - 1): if xTime[iTime] >= 0 and xTime[iTime + 1] < 0: # --- Add 1 to counter number of zero-crossings nWave = nWave + 1 # --- Determine time level of zero-crossing by means of linear # interpolation tCrossing = t[iTime] + dt * xTime[iTime] / ( xTime[iTime] - xTime[iTime + 1] ) tCross = np.append(tCross, tCrossing) else: raise ValueError( "sort_wave_params: Input error: Wrong input argument for typeCross " ) # --- If no zero-crossings are found, put number of waves to zero (the # value -1 may be considered inappropriate) if nWave == -1: nWave = 0 return nWave, tCross
[docs] def highest_waves_params( hWaveSorted: NDArray[float64], tWaveSorted: NDArray[float64], fracP: float, ) -> tuple[float, float]: """Computes wave parameters of selection largest waves This function computes the wave height hFracP and wave period tFracP by taking the average of the fraction fracP of the highest waves. When fracP = 1/3, then hFracP is equal to the significant wave height and tFracP is equal to the significant wave period. Parameters ---------- hWaveSorted : NDArray[float64] 1D array containing wave heights of the individual waves, sorted in descending order [m] tWaveSorted : NDArray[float64] 1D array containing wave periods of the individual waves, using the same sorting (re-arranging) as applied to hWave [s] fracP : float fraction. Should be between 0 and 1 [-] Returns ------- tuple[float, float] hFracP : float average of the wave heights of the highest fracP waves [m] tFracP : float average of the wave periods of the highest fracP waves [s] Raises ------ ValueError Input error: Input arrays hWaveSorted is not 1D ValueError Input error: Input arrays tWaveSorted is not 1D ValueError Input arrays tWaveSorted and hWaveSorted have different length ValueError Input error: Input parameter fracP should be between 0 and 1 ValueError Input error:Input array hWaveSorted is not correctly sorted Example ------- >>> import numpy as np >>> hWaveSorted = [7.1, 6.0, 5.8, 4.4, 3.1, 2.2, 1.4] >>> tWaveSorted = [5.1, 3.2, 3.7, 0.0, 2.0, 3.0, 1.1] >>> [Hsig,Tsig] = highest_waves_params(hWaveSorted,tWaveSorted,1/3) """ # --- ensure input is of type ndarray hWaveSorted = core_engine.convert_to_array_type(hWaveSorted) tWaveSorted = core_engine.convert_to_array_type(tWaveSorted) # Perform checks on the input arguments is1d_array = core_engine.is1darray(hWaveSorted) if not is1d_array: raise ValueError( "highest_waves_params: Input error: Input arrays hWaveSorted is not 1D" ) is1d_array = core_engine.is1darray(tWaveSorted) if not is1d_array: raise ValueError( "highest_waves_params: Input error: Input arrays tWaveSorted is not 1D" ) # --- Check whether the length of arrays t and x the same isequal_length = len(tWaveSorted) == len(hWaveSorted) if not isequal_length: raise ValueError( "highest_waves_params: Input error: Input arrays tWaveSorted and hWaveSorted have different length" ) # --- Input parameter fracP should be between 0 and 1 if fracP < 0 or fracP > 1: raise ValueError( "highest_waves_params: Input error: Input parameter fracP should be between 0 and 1 " ) # --- If there are no waves, return to calling function hFracP = 0.0 tFracP = 0.0 # --- Number of waves nWave = len(hWaveSorted) if nWave < 1: return hFracP, tFracP # --- Check whether hWaveSorted is indeed sorted issorted = (hWaveSorted == np.sort(hWaveSorted)[::-1]).all() if not issorted: raise ValueError( "highest_waves_params: Input error:Input array hWaveSorted is not correctly sorted " ) # Computational core # --- Number of waves in the fraction that is to be considered nWaveP = int(np.floor(nWave * fracP)) if nWaveP > 0: hFracP = float(np.mean(hWaveSorted[0:nWaveP])) tFracP = float(np.mean(tWaveSorted[0:nWaveP])) return hFracP, tFracP
[docs] def exceedance_wave_height(hWaveSorted: NDArray[float64], excPerc: float) -> float: """Computes wave height with given exceedance probability This function computes the wave height hExcPerc with given exceedance probability percentage excPerc. Parameters ---------- hWaveSorted : NDArray[float64] 1D array containing wave heights of the individual waves, sorted in descending order [m] excPerc : float exceedance probability percentage. excPerc = 2 means an exceedance percentage of 2%. The value of excPerc should not exceed 100, or be smaller than 0 [%] Returns ------- float hExcPerc : float wave height with given exceedance probability [m] Raises ------ ValueError Input error: Input arrays hWaveSorted is not 1D ValueError Input error: Input parameter excPerc should be between 0 and 100 ValueError Input error:Input array hWaveSorted is not correctly sorted Example ------- >>> import numpy as np >>> hWaveSorted = [7.1, 6.0, 5.8, 4.4, 3.1, 2.2, 1.4] >>> [hExcPerc_33perc] = exceedance_wave_height(hWaveSorted,33) """ hWaveSorted = core_engine.convert_to_array_type(hWaveSorted) # Perform checks on the input arguments is1d_array = core_engine.is1darray(hWaveSorted) if not is1d_array: raise ValueError( "exceedance_wave_height: Input error: Input arrays hWaveSorted is not 1D" ) # Check on input arguments # --- Input parameter excPerc should not exceed 100 (100%) or be smaller than 0 isvalid_range = excPerc > 100 or excPerc < 0 if isvalid_range: raise ValueError( "exceedance_wave_height: Input error: Input parameter excPerc should be between 0 and 100 " ) # --- If there are no waves, return to calling function hExcPerc = 0.0 # --- Number of waves nWave = len(hWaveSorted) if nWave < 1: return hExcPerc # --- Check whether hWaveSorted is indeed sorted issorted = (hWaveSorted == np.sort(hWaveSorted)[::-1]).all() if not issorted: raise ValueError( "exceedance_wave_height: Input error:Input array hWaveSorted is not correctly sorted " ) # Computational core # --- Index of the wave corresponding to the given exceedance probability percentage iWaveP = int(np.floor(nWave * excPerc / 100.0)) - 1 # --- Compute wave height with exceedance probability if not (iWaveP < 0): hExcPerc = float(hWaveSorted[iWaveP]) return hExcPerc
[docs] def determine_params_individual_waves( tCross: NDArray[float64], t: NDArray[float64], xTime: NDArray[float64], ) -> tuple[ NDArray[float64], NDArray[float64], NDArray[float64], NDArray[float64], NDArray[float64], NDArray[float64], ]: """Determines parameters per individual wave This function determines several wave properties (wave period, wave height, crest amplitude, trough amplitude, and the time at which the maximum crest and trough amplitudes occur) of all individual waves in a wave train. The wave train is given by time series xTime = xTime(t), and the zero-crossings occur at time levels given in tCross Parameters ---------- tCross : NDArray[float64] 1D array of length (nWave+1), containing the time of all zero-crossings. The time of the zero-crossings is determined by linear interpolation. Note that in case of no zero-crossing, the array tCross is empty. Note that in case of one zero-crossing, the number of waves is zero. [s] t : NDArray[float64] 1D array containing time axis. The numbers in the array t must be increasing and uniformly spaced (uniform time step) [s] xTime : NDArray[float64] 1D array containing signal values, i.e. the time series of the signal. The value xTime(i) must be the signal value at time t(i). Usually water surface elevation [m] Returns ------- tuple[ NDArray[float64], NDArray[float64], NDArray[float64], NDArray[float64], NDArray[float64], NDArray[float64], ] tWave : NDArray[float64] 1D array containing the periods of the individual waves [s] hWave : NDArray[float64] 1D array containing the wave heights of the individual waves [m] aCrest : NDArray[float64] 1D array containing the maximum amplitude of the crests of the individual waves [m] aTrough : NDArray[float64] 1D array containing the maximum amplitude of the troughs of the individual waves [m] tCrest : NDArray[float64] 1D array containing the time at which maximum crest amplitude of the individual waves occurs [s] tTrough : NDArray[float64] 1D array containing the time at which maximum trough amplitude of the individual waves occurs [s] Notes ----- * All these arrays have a length equal to nWave, which is the number of waves in the wave train * The values of aTrough are always smaller than zero * hWave = aCrest - aTrough Raises ------ ValueError Input error: Input arrays tCross is not 1D ValueError Input error: Input arrays t is not 1D ValueError Input error: Input arrays x is not 1D ValueError Length of input arrays t and x not identical """ # --- ensure input is of type ndarray tCross = core_engine.convert_to_array_type(tCross) t = core_engine.convert_to_array_type(t) xTime = core_engine.convert_to_array_type(xTime) # Perform checks on input arguments # --- In the possible situation of no zero-crossings, the array tCross is # empty. In this situation, make output variables empty arrays and # return tWave = np.empty((0, 0)) hWave = np.empty((0, 0)) aCrest = np.empty((0, 0)) aTrough = np.empty((0, 0)) tCrest = np.empty((0, 0)) tTrough = np.empty((0, 0)) if tCross.size == 0: return hWave, tWave, aCrest, aTrough, tCrest, tTrough # Perform checks on the input arguments is1d_array = core_engine.is1darray(tCross) if not is1d_array: raise ValueError( "determine_params_individual_waves: Input error: Input arrays tCross is not 1D" ) is1d_array = core_engine.is1darray(t) if not is1d_array: raise ValueError( "determine_params_individual_waves: Input error: Input arrays t is not 1D" ) is1d_array = core_engine.is1darray(xTime) if not is1d_array: raise ValueError( "determine_params_individual_waves: Input error: Input arrays x is not 1D" ) # --- Check whether the length of arrays t and x the same isequal_length = len(t) == len(xTime) if not isequal_length: raise ValueError( "determine_params_individual_waves: Length of input arrays t and x not identical " ) # Computational core # --- Determine number of waves nWave = len(tCross) - 1 # --- Initialize output arrays (could have used append also, without initializing array) tWave = np.zeros((1, nWave), dtype=float)[0] hWave = np.zeros((1, nWave), dtype=float)[0] aCrest = np.zeros((1, nWave), dtype=float)[0] aTrough = np.zeros((1, nWave), dtype=float)[0] tCrest = np.zeros((1, nWave), dtype=float)[0] tTrough = np.zeros((1, nWave), dtype=float)[0] # --- Do a loop over all waves for iWave in np.arange(0, nWave): # --- Determine initial and end time of wave, and corresponding # positions in time-array tIni = tCross[iWave] # Initial time tEnd = tCross[iWave + 1] # End time iIni = np.where(t >= tIni)[0][0] iEnd = np.where(t <= tEnd)[0][-1] # --- Find position where maximum and minimum occurs, for the # individual wave # Argument 'first' is included, to account for the possible # situation of two extrema with the same function # value in the given wave tIwave = t[iIni : iEnd + 1] xIwave = xTime[iIni : iEnd + 1] iMax = np.where(xIwave == max(xIwave))[0][0] iMin = np.where(xIwave == min(xIwave))[0][0] # --- Fill the output arrays tWave[iWave] = tEnd - tIni aCrest[iWave] = xIwave[iMax] aTrough[iWave] = xIwave[iMin] tCrest[iWave] = tIwave[iMax] tTrough[iWave] = tIwave[iMin] hWave[iWave] = aCrest[iWave] - aTrough[iWave] return hWave, tWave, aCrest, aTrough, tCrest, tTrough