Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility with pandas >= 1.3, reverse shoaling functions #176

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ dependencies:
- coverage ==5.*,>=5.0.0
- joblib ==0.*,>=0.14.1
- numpy ==1.*,>=1.16.0
- pandas ==0.*,>=0.25.3
- pandas ==1.*,>=1.3.0
- pre-commit ==1.*,>=1.18.0
- pytest ==5.*,>=5.3.0
- pytest-cov ==2.*,>=2.8.0
- pytest-runner ==5.*,>=5.1.0
- scikit-learn ==0.*,>=0.22.0
- scikit-learn ==0.*,>=0.24.0
- sphinx ==2.*,>=2.3.0
- sphinx-gallery ==0.*,>=0.5.0
- tox ==3.*,>=3.13.0
Expand Down
39 changes: 39 additions & 0 deletions examples/reverse_shoaling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"""
Script by: Mandi Thran
Date: 05/08/2021

This script demonstrates an example that requires reverse-shoaling before
run-up is calculated.
"""

#=========== Modules ===========#
import pandas as pd
import numpy as np
from py_wave_runup import models
from py_wave_runup import utils


#=========== Create a dummy data set ===========#
df = pd.DataFrame(columns=["water_depth","Tp","Hs"])
# Water depth of observed/measured/modeled significant wave height
# Depth is positive, in meters
# Depths between 90 and 100 m should yield a mix of deep and intermediate water waves
# For the following Hs, Tp wave parameter ranges
df['water_depth'] = np.random.uniform(90.,100.,5)
# Wave period in seconds
df['Tp'] = np.random.uniform(8,14,5)
# Significant wave height
df['Hs'] = np.random.uniform(3,8,5)
# Beach slope
df['beta'] = np.random.uniform(0.05,0.2)


#=========== Reverse Shoaling of Wave Forecasts ===========#
# Calculate the deep water wave length
df['Lo'] = utils.calculate_Lo(df)
# Evaluate whether in shallow, intermediate, or deep water
df['wave_depth'] = utils.determine_waveDepth(df)
# Compute the deep water wave height
df['Ho'] = utils.reverse_shoal(df)

print(df)
175 changes: 175 additions & 0 deletions py_wave_runup/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import math
import random
import numpy as np
import pandas as pd


class PerlinNoise(object):
Expand Down Expand Up @@ -72,3 +74,176 @@ def grad(self, hash, x):
if h & 8:
grad = -grad # Add a random sign
return grad * x


def calculate_Lo(df):
"""
Calculates the deep water wave length

Input:
df: pandas dataframe with at least the following columns:
- df.Tp: Wave period in seconds

Output:
- A pandas series containing the deep water wavelength Lo in meters

Example of use:
df['Lo'] = utils.calculate_Lo(df)
See examples/reverse_shoaling.py
"""
g = 9.81 # in m/s2
Lo = (g*df.Tp*df.Tp)/(2.*np.pi)
return Lo


def reverse_shoal_intermediate(waterDepthSeries=None, LoSeries=None,
TpSeries=None, HsSeries=None):
"""
Computes deep water wave height assuming an intermediate wave depth condition.

Input: pandas dataframe with at least the following columns:
- waterDepthSeries: Pandas series containing water depth where wave height was
measured/computed (meters)
- LoSeries: Pandas series containing deep water wave length (seconds)
- TpSeries: Pandas series containing wave period (s)
- HsSeries: Pandas series containing height at the time of measurement/computation
in intermediate water (m)

Output:
- Pandas series of reverse-shoaled deep water significant wave heights (m)

Example of use:
HoSeries = reverse_shoal_intermediate(waterDepthSeries=df.water_depth,
LoSeries=df.Lo,
TpSeries=df.Tp,
HsSeries=df.Hs)
"""

# Double-check that these are in fact intermediate water waves
if (waterDepthSeries > (1./2.)*LoSeries).any() == True:
errorMessage = str("ERROR: Deep water waves detected. " +
"Water depth should be less than " +
"1/2 * deep water wave length (Lo) " +
"to satisfy intermediate wave condition. " +
"For deep water waves, Hs = Ho")
print(errorMessage)
raise ValueError("Deep water waves detected")
if (waterDepthSeries < (1./50.)*LoSeries).any() == True:
errorMessage = str("ERROR: Shallow water waves detected. " +
"Water depth should be greater than " +
"1/50 * deep water wave length (Lo) " +
"to satisfy intermediate wave condition. ")
print(errorMessage)

# Main calculation
g = 9.81 #in m/s2
y = 4.03 * waterDepthSeries / (TpSeries ** 2)
kd2 = y ** 2 + y / (
1
+ (0.666 * y)
+ (0.355 * y ** 2)
+ (0.161 * y ** 3)
+ (0.0632 * y ** 4)
+ (0.0218 * y ** 5)
+ (0.00564 * y ** 6)
)
kh = np.sqrt(kd2)
Cg = (
g
* TpSeries
/ (2 * np.pi)
* (np.tanh(kh))
* (0.5 * (1 + 2 * kh / np.sinh(2 * kh)))
)
Cgo = 1 / 4 * g * TpSeries / np.pi
Ks = np.sqrt(Cgo / Cg)
Hs0 = HsSeries / Ks
return Hs0


def determine_waveDepth(df):
"""
Determines whether a wave is in deep, intermediate, or shallow water

Input:
df: pandas dataframe containing at least the following columns:
- df.water_depth: water depth where significant wave height was measured
or computed (in meters, depth is positive)
- df.Lo: deep water wave length in meters

Output:
- A pandas series containing the wave depth classification

Example of use:
df['wave_depth'] = utils.determine_waveDepth(df)
See examples/reverse_shoaling.py

"""
# create a list of conditions
conditions = [
# Shallow water
(df.water_depth < (1./50.)*df.Lo),
# Intermediate water
(df.water_depth >= (1./50.)*df.Lo) & (df.water_depth <= (1./2.)*df.Lo),
# Deep water
(df.water_depth > (1./2.)*df.Lo)]

# create a list of the values to assign for each condition
values = ['shallow', 'intermediate', 'deep']

# create a new column and use np.select to assign values to it using our lists as arguments
df['wave_depth'] = np.select(conditions, values)

return df.wave_depth



def reverse_shoal(df_wave = None):
"""
Calculates the deep water wave height given an inshore wave height at a
particular depth

For theory, additional code, and useful tools, refer to:
https://github.com/csherwood-usgs/jsed/blob/master/Runup%20and%20Reverse%20Shoaling%2C%20USGS.html
And: https://csherwood-usgs.github.io/jsed/Runup%20and%20Reverse%20Shoaling,%20USGS.html

Args:
df_wave: Input dataframe with column for water_depth, Tp, Hs, and wave_depth
- "wave_depth" column: Indicates whether wave is in deep, intermediate, or shallow water.
To generate a "wave_depth" column, see the function determine_waveDepth in utils.py

Returns:
Pandas series of reverse shoalled wave heights

Note: only deals with intermediate and deep water wave conditions. Will return NaN if the wave meets a shallow water condition.

Uses an approximation to solve dispersion eq.

"""

df = df_wave.copy()

# New column
df['Ho'] = np.nan

# Evaluate in deep water
# Find rows that are labeled as having deep water waves
# Set deep water wave height to Hs (since it's already in deep water)
df.loc[df['wave_depth'] == 'deep', 'Ho'] = df.Hs

# Evaluate in intermediate water
# Make a new dataframe with only intermediate water waves by masking original dataframe
# To avoid copy warning in pandas
mask_int = (df.wave_depth== "intermediate")
df_int = df[mask_int]
HoSeries = reverse_shoal_intermediate(waterDepthSeries=df_int.water_depth,
LoSeries=df_int.Lo,
TpSeries=df_int.Tp,
HsSeries=df_int.Hs)

# Assign values in series to original dataframe
df.loc[df['wave_depth'] == 'intermediate', 'Ho'] = HoSeries

# Return series of values that matches the number
# of rows in the original dataframe
return df['Ho'].values
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ codecov==2.*,>=2.0.0
coverage==5.*,>=5.0.0
joblib==0.*,>=0.14.1
numpy==1.*,>=1.16.0
pandas==0.*,>=0.25.3
pandas==1.*,>=1.3.0
pre-commit==1.*,>=1.18.0
pytest==5.*,>=5.3.0
pytest-cov==2.*,>=2.8.0
pytest-runner==5.*,>=5.1.0
scikit-learn==0.*,>=0.22.0
scikit-learn==0.*,>=0.24.0
sphinx==2.*,>=2.3.0
sphinx-gallery==0.*,>=0.5.0
tomlkit==0.*,>=0.5.8
Expand Down