CoastalCodeBook
  1. Week 7
  2. 7: Alongshore transport
  • Introduction
  • About the Codebook
    • Introduction
    • Course structure
    • Notebook overview
    • Acknowledgements
  • Usage
    • Installation Guide
    • Getting Started
    • Troubleshooting
    • Building the book
  • Week 1
    • 1a: Tectonic classification
    • 1b: Process-based coastal classification
  • Week 2
    • 2a: Pre-knowledge waves
    • 2b: Wave dispersion and grouping
    • 2c: Generation of the tide
    • 2d: Tidal constituents
  • Week 3
    • 3a: Tidal Environments
    • 3b: Wave asymmetry and skewness
    • 3c: Radiation stresses and wave forces
  • Week 4
    • 4a: Bound long waves
    • 4b: Shallow-water Tides
  • Week 5
    • 5: Coastal Classification and Beach States
  • Week 6
    • 6: Cross-shore transport
  • Week 7
    • 7: Alongshore transport
  • Week 8
    • 8a: Escoffier curve
    • 8b: Tide-induced residual transport (Van de Kreeke and Robaczewska)
    • 8c: Equilibrium concentration versus lag effects
    • 8d: Interventions and natural changes
  1. Week 7
  2. 7: Alongshore transport
image

7: Alongshore transport

Welcome to the notebook of week 7! Just as in week 6, we only have a single notebook this week. The main topic of this notebook is alongshore sediment transport, which is described in Chapter 8 of the Coastal Dynamics Open Textbook. This notebook consists of three parts: 1. In part 1, we will look into the CERC formula, and how to use it to obtain an (S, \(\phi\))-curve (Sections 8.2.3-8.2.4). 2. Afterwards, in part 2, we will use the (S, \(\phi\))-curve to determine coastal evolution around a breakwater (Sections 8.3.2-8.3.3). 3. In part 3, we will consider a transport curve as computed in a coastline model (Intermezzo 8.2)

You do not have to code yourself in this notebook. Instead, the challenge for you is to read through the notebook - including the code - and see if you can follow along. There are eleven multiple-choice / -selection questions, and six numerical questions for you to check your understanding. The code uses the dispersion relationship, for which we provide an efficient solver. You can also replace it by your own dispersion relation, as you have coded it in previous notebooks.

Import the required packages and questions

Run the below two cells to import all necessary packages and question files required in this notebook

import logging
from pathlib import Path
import warnings

import holoviews as hv
import hvplot.pandas  # noqa: API import
import numpy as np
import pandas as pd
import panel as pn

import coastal_dynamics as cd

# Activate Panel extension to make interactive visualizations
pn.extension()
import sys

sys.path.append('../')

from modules import mod_7
# Read questions locally
questions = cd.read_questions(Path("../hashed_questions/7_alongshore_transport_hashed.json"))

question_industry = cd.QuestionIndustry(questions)

cd.UseAnswersApp("Q7").serve()

Part 1: CERC formula and (\(S\),\(\phi\))-curve

Theory

Many different formulas exist to calculate bulk longshore sediment transport. One widely used formula is the CERC formula, which in its most general form is shown in the textbook as Equation 8.4 in the book. Under the assumption of shallow-water wave breaking, we can write the CERC formula as follows (Equation 8.5 in the textbook):

\[S = \frac{K}{16(s-1)(1-p)} \sqrt{\frac{g}{\gamma}} \sin{2 \phi_b} H_b^2 \]

For a complete description of each parameter, see Section 8.2.3 of the book. As you can see the alongshore transport is here written in terms of the wave angle \(\phi_b\) and wave height at breaking \(H_b\). If all parameters - including the wave height at breaking \(H_b\) - are kept constant, the transport magnitude is maximum at \(\phi_b\) = 45°. In practice \(\phi_b\) is in the range -20° to -20° due to refraction.

Sometimes it is more practical to use deep-water wave parameters in the equations. For straight and parallel depth contours, the textbook presents the following version of the CERC formula (Equation 8.10 in the book):

\[S = \frac{K}{32(s-1)(1-p)} c_b \sin{2 \phi_0} H_0^2 \]

You can see that it uses deep-water values for the wave height \(H_0\) and wave angle \(\phi_0\), but needs the wave celerity at breaking \(c_b\).

Example 8.1 in the textbook

Example 8.1 in the book examines the effect of the angle of wave incidence on longshore sediment transport for straight and parallel depth contours. It does so by using Equation 8.10 for a constant offshore wave height and period and a range of deep water angles of attack \(\phi_0\). In this notebook, you can discover in more detail how the calculations are done.

In line with Example 8.1 in the book, we will assume that the offshore wave height \(H_0\) is equal to the deep-water root-mean-square wave height \(H_{rms,0}\), and can be represented by a single value of 2 m, with a period T of 7 s. The corresponding value for \(K\) is given by \(K_{rms} \approx 0.7\). We further use a porosity \(p = 0.4\) and relative density \(s = 2.65\). We use a breaker index of \(\gamma = H_{rms,b}/h_b = 0.8\), where \(H_{rms,b}\) is the root-mean-square wave height at breaking and \(h_b\) is the water depth at the breaking point.

Given the constant offshore wave height, the only variables left in the CERC formula are the wave celerity at breaking \(c_b\) and the offshore wave angle \(\phi_0\). Starting from our values for \(H_0\) and \(T\), we can use linear wave theory to compute values for \(c_b\) as a function of \(\phi_0\). In order to determine \(c_b\), we need to know \(h_b\). This requires an iterative computation procedure, which is described in Example 8.1 in the book. In short, the steps in a by-hand computation would be:

  1. Guess \(h_b\)
  2. Compute \(c_b\) from linear wave theory (this requires solving the dispersion relationship, see notebooks 2a and 2b)
  3. Compute the shoaling and refraction coefficients and, from those, \(H_b\)
  4. Compute \(h_b=H_b/\gamma\) and check whether this is equal to the guess for \(h_b\).
  5. If not, make a better estimate for \(h_b\) and repeat

Solver for Dispersion Relationship

As described above, the computation requires solving the dispersion relationship. We use an efficient solver for the dispersion relationship (© Gert Klopman, 1994; conversion to Python by M. van der Lugt). You could also use the dispersion relationship that you coded during the Waves course, or in the notebooks of week 2. If you would like to do so, change the below function by replacing the code between hashtags (‘#’) with your own code.

def disper(w, h, g):
    """
    Input:
    w = 2*pi/T, where T is the wave period
    h = water depth
    g = gravity constant

    Output:
    k = wave number
    """

    k = None

    ### you can add code here with your own code to compute k 

    # k = ...

   
    ### the end of your code

    # efficient dispersion solver
    if not k:
        k = mod_7.disper(w, h, g=g)

    return k

Computation of Wave Celerity at Breaking (\(c_b\))

Now that we have defined the dispersion relationship, we can set up the computation of \(c_b\). The function below takes care of this computation. Each step in the function is explained and it is instructive to see if you can follow the function step-by-step. Note that the function is neither elegant nor efficient. It simply computes \(c_b\) and \(H_b\) for a wide range of potential \(h_b\)-values and only afterwards checks which of these potential \(h_b\)-values is the actual \(h_b\)-value satisfying \(h_b=H_b/\gamma\).

The result of the function is \(c_b\) for a certain deep water wave angle of incidence \(\phi_0\). Once \(c_b\) is known, we can compute the transport \(S\). Note that the function also outputs \(\phi_b\), \(h_b\) and \(H_b\).

def find_cb_phib_Hb_hb(phi0, H0, T, gamma, g=9.81, hb=np.arange(0.1, 5.0, 0.01),  disper=disper):
    """
    Returns breaking wave celerity cb [m/s], angle of incidence at breaking phib [degrees], 
    wave height at breaking Hb [m] and water depth hb at breaking hb [m] for given: 
    - phi0 : angle of incidence [degrees]
    - H0   : deep water wave height [m]
    - T    : period [s]

    The parameter hb is used as guessed values for the breaking depth.
    From this array, the best-fitting value is chosen in the end. You can adjust this
    array to make estimates more accurate at the cost of computational efficiency.
    Note that for different H0 or T the current range may not satisfy (you will notice if you find hb = 5 m as output).
    You will then have to adjust the range. 
    """
    # First convert the angle of incidence to radians
    phi_rad = phi0 / 360 * 2 * np.pi

    # We start with calculating deep water celerity, wavelength, and angular frequency
    c0 = g * T / (2 * np.pi)
    L0 = c0 * T
    w = (2 * np.pi) / T
    
    # For every potential value of hb, the wavenumber k is determined using the dispersion relation
    # Feel free to use your own implementation from week 2!
    k = disper(w, hb, g=g)

    # Next we calculate the celerity and group celerity for each breaking depth
    c = np.sqrt(g / k * np.tanh(k * hb))
    n = 1 / 2 * (1 + (2 * k * hb) / (np.sinh(2 * k * hb)))
    cg = n * c

    # In order to correctly shoal the waves, we also need the deep water group celerity
    n0 = 1 / 2
    cg0 = n0 * c0

    # And to account for refraction we need the angle of incidence at breaking using Snell's law
    phi = np.arcsin(np.sin(phi_rad) / c0 * c)

    # Shoaling & refraction coefficients
    Ksh = np.sqrt(cg0 / cg)
    Kref = np.sqrt(np.cos(phi_rad) / np.cos(phi))

    # Wave heights Hb at depth hb
    Hb = Ksh * Kref * H0

    # We are looking for an hb where the breaker parameter is 0.8
    # We can determine which value of hb in our array gets closest using the
    # following line of code:
    i = np.argmin(np.abs(Hb / hb - gamma))

    return c[i], phi[i] / (2 * np.pi) * 360, Hb[i], hb[i]

Computation of the transport

With \(c_b\) known for the given deep water wave height \(H_0\), period \(T\) and angle of incidence \(\phi_0\), we can calculate the transport \(S\) using the CERC formulation of Equation 8.10. To do so, you can use the function below. Check for yourself that the function below indeed corresponds to Equation 8.10. Note that we use the above-mentioned typical values of \(K=0.7\), \(p=0.4\), and \(s=2.65\). Note that we will get slightly higher values than in Example 8.1, since the there used Equation 8.13 rounds \(\frac{K}{32(s-1)(1-p)}\) to 0.02. You can check that by changing the code below.

def CERC(cb, phi0, H0, K=0.7, s=2.65, p=0.4):
    """
    cb:   celerity at breaking
    phi0: offshore angle of incidence (degrees)
    H0:   offshore wave height

    K:    coefficient
    s:    relative density
    p:    porosity
    """

    return K / (32 * (s - 1) * (1 - p)) * cb * np.sin(2 * (phi0 / 360 * 2 * np.pi)) * H0**2
    # use the below statement instead to get exactly the same values as in Example 8.1
    # return 0.02 * cb * np.sin(2 * (phi0 / 360 * 2 * np.pi)) * H0**2    

Check the Computation of \(c_b\) and \(S\)

Let’s check that our calculations work correctly. The cell below shows that for an offshore wave height \(H_0\) of 2 m, a period \(T\) of 7 s, a deep water angle of incidence \(\phi_0\) of 5°, and \(\gamma\) = 0.8, we get a \(c_b\) of 4.92 m/s and an alongshore transport \(S\) of 0.0763 m\(^3\)/s. You can check that this is in reasonable agreement with Example 8.1 in the book (about 10% higher for the reasons mentioned above). Also, check the other values that the cell prints.

If you want to make the computation of \(c_b\) and \(S\) for a different \(\phi_0\), you can simply change the input value for \(\phi_0\) in the cell.

# Define the conditions

H0 = 2 # m
T = 7 # s
gamma = 0.8 # -   

# Change phi0 for a different deep water angle between -90 and 90 degrees
phi0 = 5  # degrees

# Compute cb, phib, Hb, and hb
cb, phib, Hb, hb = find_cb_phib_Hb_hb(phi0, H0, T, gamma)

# Compute transport using the CERC code above
S = CERC(cb,phi0,H0)  

# print some informative values
print(f"Deep water wave height H₀: {H0:.2f} m")
print(f"Deep water angle of incidence φ₀: {phi0:.2f}°")
print(f"Period T: {T:.2f} s")
print(f"Breaker parameter γ: {gamma:.1f}")
print(f"Wave celerity at breaking cb: {cb:.2f} m/s")
print(f"Depth at breaking hb: {hb:.2f} m")
print(f"Wave height at breaking Hb: {Hb:.2f} m")
print(f"Wave angle at breaking φb: {phib:.2f}°")
print(f"Longshore transport S: {S:.4f} m3/s")
Deep water wave height H₀: 2.00 m
Deep water angle of incidence φ₀: 5.00°
Period T: 7.00 s
Breaker parameter γ: 0.8
Wave celerity at breaking cb: 4.97 m/s
Depth at breaking hb: 2.72 m
Wave height at breaking Hb: 2.17 m
Wave angle at breaking φb: 2.27°
Longshore transport S: 0.0763 m3/s

Construct the (\(S\),\(\phi\))-curve

The above-computed transport is in reasonable agreement with Example 8.1 from the book!

For the given deep water wave height and period, we are now able to compute \(c_b\) and the transport \(S\) as a function of \(\phi_0\), which means we can generate an (\(S\),\(\phi\))-curve! To do so, we use the above-defined functions to calculate a breaking wave celerity and, subsequently, the transport \(S\) for each angle of incidence. We use a range from negative to positive angles here, but this is not strictly necessary since the transport S for negative angles only differs in sign from the result for positive angles.

Run the cell below. It does the computations for \(c_b\) and \(S\) for the full range of \(\phi_0\)-values. Note that in the cell we again define the wave conditions, so that you can use this computation independently of the above-defined conditions.

Note You can also experiment with different deep water conditions by choosing different values for \(H_0\) or \(T\). Please be aware that the current range for \(h_b\) used in the computation of \(c_b\) may not be satisfied. You will notice this if you find \(h_b\) = 5 m as output. You will then have to adjust the range.

# Define the conditions again
H0 = 2 # m 
T = 7  # s  
gamma = 0.8  # -

# Set the range for which we want to calculate cb
phi0_array = np.arange(-85, 86, 1) 

# Initialize cb array
cb_array = np.zeros(phi0_array.shape)
phib_array = np.zeros(phi0_array.shape)
Hb_array = np.zeros(phi0_array.shape)
hb_array = np.zeros(phi0_array.shape)
phi2_array=np.zeros(phi0_array.shape)
sinphi2_array=np.zeros(phi0_array.shape)

phi2_array=2 * (phi0_array / 360 * 2 * np.pi)
sinphi2_array=np.sin(phi2_array)

# Loop through each phi and compute the associated value for cb, phib, Hb, and hb
for i in range(len(phi0_array)):
     cb_array[i], phib_array[i], Hb_array[i],hb_array[i] = find_cb_phib_Hb_hb(phi0_array[i], H0, T,gamma)

# Finally, compute associated transport
S = CERC(cb_array, phi0_array, H0)

Present the results

The below two cells present the results in the form of: 1. A table that you can compare with Table 8.1 in the book. As compared to the book we have added \(H_b\), sin(2\(\phi_0\)) and 2\(\phi_0\); 2. The so-called (\(S\), \(\phi\))-curve that you can compare with Figure 8.4 in the book.

# collect results
data = np.transpose([phi0_array, hb_array, Hb_array, phib_array,cb_array, S, sinphi2_array, phi2_array])

# create dataframe
df = pd.DataFrame(data)

# only keep rows with phi0 that are a multiple of 5
df = df[df.index % 5== 0]

# round to the same amount of decimals places used in the book
for key in df.keys():
    if key in [0, 1, 2, 3, 4]:
        df.loc[:,key] = np.around(df[key].values, 2)
    elif key in [5,6,7]:
        df.loc[:,key] = np.around(df[key].values, 3)

# change column names
df.columns = ['φ₀ (°)', 'hb (m)', 'Hb (m)', 'φb (°)','cb (m/s)','S (m3/s)','sin(2φ₀)','2φ₀ (rad)']

# print some informative values
print(f"Deep water wave height  H₀: {H0:.2f} m")
print(f"Deep water angle of incidence φ₀: {phi0:.2f}°")
print(f"Period T: {T:.2f} s")
print(f"Breaker parameter γ: {gamma:.1f}")

# display table
display(df)
Deep water wave height  H₀: 2.00 m
Deep water angle of incidence φ₀: 5.00°
Period T: 7.00 s
Breaker parameter γ: 0.8
φ₀ (°) hb (m) Hb (m) φb (°) cb (m/s) S (m3/s) sin(2φ₀) 2φ₀ (rad)
0 -85.0 1.01 0.81 -16.44 3.10 -0.048 -0.174 -2.967
5 -80.0 1.35 1.08 -18.77 3.57 -0.108 -0.342 -2.793
10 -75.0 1.59 1.28 -19.96 3.86 -0.171 -0.500 -2.618
15 -70.0 1.79 1.43 -20.58 4.09 -0.232 -0.643 -2.443
20 -65.0 1.95 1.56 -20.67 4.26 -0.288 -0.766 -2.269
25 -60.0 2.09 1.68 -20.40 4.40 -0.337 -0.866 -2.094
30 -55.0 2.21 1.77 -19.78 4.51 -0.375 -0.940 -1.920
35 -50.0 2.31 1.85 -18.85 4.61 -0.401 -0.985 -1.745
40 -45.0 2.40 1.92 -17.67 4.69 -0.415 -1.000 -1.571
45 -40.0 2.48 1.98 -16.27 4.76 -0.415 -0.985 -1.396
50 -35.0 2.54 2.03 -14.65 4.82 -0.400 -0.940 -1.222
55 -30.0 2.59 2.07 -12.85 4.86 -0.372 -0.866 -1.047
60 -25.0 2.63 2.10 -10.91 4.90 -0.331 -0.766 -0.873
65 -20.0 2.66 2.13 -8.86 4.92 -0.280 -0.643 -0.698
70 -15.0 2.69 2.15 -6.73 4.95 -0.219 -0.500 -0.524
75 -10.0 2.71 2.16 -4.52 4.96 -0.150 -0.342 -0.349
80 -5.0 2.72 2.17 -2.27 4.97 -0.076 -0.174 -0.175
85 0.0 2.72 2.18 0.00 4.97 0.000 0.000 0.000
90 5.0 2.72 2.17 2.27 4.97 0.076 0.174 0.175
95 10.0 2.71 2.16 4.52 4.96 0.150 0.342 0.349
100 15.0 2.69 2.15 6.73 4.95 0.219 0.500 0.524
105 20.0 2.66 2.13 8.86 4.92 0.280 0.643 0.698
110 25.0 2.63 2.10 10.91 4.90 0.331 0.766 0.873
115 30.0 2.59 2.07 12.85 4.86 0.372 0.866 1.047
120 35.0 2.54 2.03 14.65 4.82 0.400 0.940 1.222
125 40.0 2.48 1.98 16.27 4.76 0.415 0.985 1.396
130 45.0 2.40 1.92 17.67 4.69 0.415 1.000 1.571
135 50.0 2.31 1.85 18.85 4.61 0.401 0.985 1.745
140 55.0 2.21 1.77 19.78 4.51 0.375 0.940 1.920
145 60.0 2.09 1.68 20.40 4.40 0.337 0.866 2.094
150 65.0 1.95 1.56 20.67 4.26 0.288 0.766 2.269
155 70.0 1.79 1.43 20.58 4.09 0.232 0.643 2.443
160 75.0 1.59 1.28 19.96 3.86 0.171 0.500 2.618
165 80.0 1.35 1.08 18.77 3.57 0.108 0.342 2.793
170 85.0 1.01 0.81 16.44 3.10 0.048 0.174 2.967
# The S-phi curve
warnings.filterwarnings("ignore")
logging.getLogger().setLevel(logging.ERROR)

(
    hv.Curve((phi0_array, S))
    * hv.HLine(0).opts(color="black")
    * hv.VLine(0).opts(color="black")
).opts(
    xlabel="deep water angle [°]",
    ylabel="S [m3/s]",
    title="(S, φ₀)-curve",
    height = 400,
    width = 600,
    show_grid=True,

)

Questions about part 1

Using the table, plot and the code above, try to answer the questions below. Make sure to set the offshore wave height \(H_0\) at 2 m and the period \(T\) at 7 s as the default in the computations.

q = [
    "Q7-breaking_wave_height",
    "Q7-parameters_monotonically_decrease",
    "Q7-variation_ub",
    "Q7-angle_max_transport",
    "Q7-symmetrical_transport",
    "Q7-CERC_grain-size",
    "Q7-transport_errr_angle",
    "Q7-increase_alongshore_transport",
]

question_industry.serve(q)

Part 2: Coastal evolution around a shore-normal breakwater

Now that we know how to construct an (\(S\),\(\phi\))-curve we will continue with predicting the coastal evolution updrift of shore-normal breakwater using single-line theory. It is handy to read up on Section 8.3.2 and Section 8.3.3 of the book.

Draw breakwater

We use an alonghore \(x\)-axis and a \(y\)-axis representing the offshore position. For the initial coastline we have \(y=0\). Let’s place the breakwater at \(x=0\), and consider the domain \(x \leq 0\). We will look at a stretch of coast with a length of order 10 km. The breakwater extends into the water from \(y=0\) m to \(y=500\) m and is represented by a thin black line. An overview of the situation is provided by running the cell below.

breakwater = hv.Curve(((0, 0), (0, 500)), label="Breakwater").opts(color="black")
shoreline = hv.Curve(((-10000, 500), (0, 0)), label="Initial shoreline").opts(color="#D2B48C")

curves = (breakwater * shoreline).opts(
    height=300,
    legend_position="top_left",
    xlabel="alongshore position x [m]",
    ylabel="cross-shore position y [m]",
    responsive = True,
)

pn.Column(curves)

Compute shoreline change from continuity equation

According to the single-line theory described in Section 8.3.2, we know the coastline will evolve by (Equation 8.16):

\[\frac{\partial Y}{\partial t} + \frac{1}{d} \frac{\partial S_x}{\partial x}=0\]

with \(S_x\) the sediment transport in the \(x\)-direction (the alongshore coordinate), \(y = Y\) the coastline position and \(d\) the active profile height. We define the transport \(S_x\) positive in positive x-direction. This equation shows that the temporal evolution of the coastline position \(Y\) is determined by the alongshore gradient of the alongshore transport. The equation requires that \(S_x\) is defined as a bulk volume transport of deposited material, hence including pores (verify this!). Our formulation of the CERC formula satisfies this requirement.

For any location \(x\) along the coastline and a given active profile height \(d\), we can determine the coastline evolution through the following steps: 1. Determine the local deep water angle of wave attack relative to the coastline \(\phi\) 2. Determine the sediment transport, which is a function of this local angle \(\phi\) (see Part 1) 3. Determine the alongshore gradient of the alongshore sediment transport \(\partial S_x/\partial x\) 4. Determine the expected morphological change in a certain short time interval \(\partial Y/\partial t\)

However, when the coastline orientation changes, the angle of wave attack relative to the coastline changes as well and so too does the transport. We thus need to iterate through these steps. This is called the morphological feedback loop. When repeated, for example through a numerical model, this strategy can help predict morphological changes. This is useful for predicting the shoreline evolution due to for instance human interventions, like the construction of harbours or coastal protection works.

Let us for example consider a breakwater perpendicular to the shore. We will try to use a numerical model to determine the shoreline evolution. Don’t worry too much about the numerical model – for now, we will focus on the physical concepts. Therefore, and because it may take a little while to run, we have run the model already for you, and will simply load the results. You will learn more about the numerical modelling part in the Coastal Modelling unit of the Coastal Engineering B-module (CIEM3210) and get the chance to also do this yourself.

Assumptions

First, some assumptions need to be made. For simplicity, we set \(d\) to an assumed depth of closure of \(d=7\) m. Further, let’s assume that the wave climate can be represented by a single offshore wave condition \(H_0\), again given as the root-mean-square wave height \(H_{rms,0}\):

  • \(H_0=H_{rms,0}=1.2\)
  • \(T=7\)
  • \(\phi_0=10°\)

Assume again that the breaker parameter \(\gamma=0.8\). This wave condition is assumed to occur roughly 10% of the time. Note that this means that for 90% of the time, no significant transport occurs! The angle is quite small, but it simplifies our notebook and helps us make a comparison below between a numerical and analytical solution. In the analytical solution we assume small angles, such that we can approximate \(\sin2\phi\) by \(2\phi\) (in rad!). We assume this approximation is valid as \(-20°<2\phi<20°\). With this knowledge, review the above question about the relative error in S due to this approximation!

Remember, the angle \(\phi_0\) is defined as positive when it induces positive transport (to the right), the same as the image in Table 8.2 of the textbook. Thus, the stretch of coast we are looking at is the updrift side of the breakwater.

For the initial, straight and undisturbed coastline, this wave condition leads to an alongshore yearly transport \(S\), which we will compute shortly after.

Initial and boundary conditions

We also need to impose initial and boundary conditions. These are thoroughly described by Equations 8.21 - 8.23. Briefly, for initial conditions, we assume a straight coastline (i.e \(y=0\) along the coast). The following two boundary conditions are imposed: * \(S_x = S\), for \(x=-\infty\) and for all \(t\) (Equation 8.22) * \(S_x = 0\), for \(x=0\) and for all \(t\) (Equation 8.23)

Determine alongshore transport S

Let’s now determine the average yearly transport for the initial updrift coastline. Outside the influence of this breakwater the transport remains constant in time at \(S\) (see the first boundary condition above). We now calculate the transport as above, and convert it to \(m^3/year\), taking the frequency of occurrence (10%) into account:

# define values
H0 = 1.2
T = 7
phi0 = 10
gamma = 0.8 

# compute cb, phib, Hb, and hb
cb, phib, Hb, hb = find_cb_phib_Hb_hb(phi0, H0, T, gamma)

# compute transport
S_m3s = CERC(cb, phi0, H0)

# This transports S is given in m3/s. Remember that the wave climate occurs 10% of the year. For 1 year, the contribution is:
S_total = (0.1 * S_m3s) * 365.25 * 24 * 3600

# print values
print(f"Deep water wave height H₀: {H0:.2f} m")
print(f"Deep water angle of incidence φ₀: {phi0:.2f}°") 
print(f"Period T: {T:.2f} s")
print(f"Breaker parameter γ: {gamma:.1f}")
print(f"Wave celerity at breaking cb: {cb:.2f} m/s")  
print(f"Depth at breaking hb: {hb:.2f} m")   
print(f"Wave height at breaking Hb: {Hb:.2f} m") 
print(f"Wave angle at breaking φb: {phib:.2f}°")
print(f"Longshore transport S per second: {S_m3s:.4f} m³/s")
print(f"Total yearly transport S per year : {S_total:.0f} m³/year")
Deep water wave height H₀: 1.20 m
Deep water angle of incidence φ₀: 10.00°
Period T: 7.00 s
Breaker parameter γ: 0.8
Wave celerity at breaking cb: 4.07 m/s
Depth at breaking hb: 1.77 m
Wave height at breaking Hb: 1.42 m
Wave angle at breaking φb: 3.70°
Longshore transport S per second: 0.0442 m³/s
Total yearly transport S per year : 139630 m³/year

Load the numerical solution and plot the results

As mentioned, we have already run the numerical model for you. Using the cells below, you can simply load the results and plot them.

The plot shows the results for a selection of years, which you can modify. We plot the total yearly transport (in the top plot), as well as the coastline (in the bottom plot). Each line represents the coastline after a certain amount of years.

# local file paths
fp_X = Path("../database/7_alongshore_transport/7_X.txt")
fp_T = Path("../database/7_alongshore_transport/7_T.txt")
fp_Y = Path("../database/7_alongshore_transport/7_Y_t.txt")
fp_S = Path("../database/7_alongshore_transport/7_S_t.txt")


# load data
X = np.loadtxt(fp_X)
T = np.loadtxt(fp_T)
Y_t = np.loadtxt(fp_Y)
S_t = np.loadtxt(fp_S)
# Plot the results
############ Modify years here ############

# standard plotted years
years = [0, 5, 10, 15, 20]

# years = np.arange(0, 21, 1)   # Uncomment this line if you want a visualization for every year!

############ End of your code #############

# initialize curves
transport_plot = hv.Curve(((), ()))
coastline_plot = hv.Curve(((), ()))

# create curves for every year
for year in years:

    id = np.argmin(np.abs(T - year))

    transport_plot *= hv.Curve((X, S_t[id]), label=f"Year = {year}")
    coastline_plot *= hv.Curve((X, Y_t[id]), label=f"Year = {year}")

# breakwater and shoreline curves
breakwater = hv.Curve(((0, 0), (0, 500)), label="Breakwater").opts(color="black")
shoreline = hv.Curve(((np.min(X), 500), (0, 0)), label="Initial shoreline").opts(
    color="#D2B48C"
)

# add plots to same figure
(
    transport_plot.opts(
        height=200,
        responsive=True,
        xlabel="alongshore position x [m]",
        ylabel="transport S [m³/year]",
        show_grid=True,
        xlim=(-10000, 500),
        legend_position="right",
    )
    + (coastline_plot * breakwater * shoreline).opts(
        height=250,
        responsive=True,
        xlabel="alongshore position x [m]",
        ylabel="cross-shore position y [m]",
        show_grid=True,
        xlim=(-10000, 500),
        legend_position="right",
    )
).opts(shared_axes=False).cols(1)

Discussion of numerical solution

This numerical solution looks quite nice. You can verify the transport magnitude far away from the breakwater and at the breakwater from the boundary conditions and the above-computed value for the total yearly transport. Have a look at how the shoreline and transport changes over the years, and compare this to the more qualitative Figures 8.11 and 8.13 in the book. You can see that the development of the shoreline slows down in time. You can also see the difference between the initial transport at t = 0 and the transport after some time.

We could extend this model with all sorts of additional functionality. Perhaps a first step would be to include the down-drift zone into the model. You will learn all about this in the Coastal Modelling unit of CIEM3210!

In the plot with the morphological evolution through time, we see that the angle the shoreline makes with the breakwater is fairly constant throughout the years. This is the angle that leads to zero alongshore transport for the given conditions at this location. Can you estimate the shoreline angle at the breakwater from the figure? Why did you find this value, and why it is constant in time? You can verify your answer in one of the questions below.

Note that in the textbook, lectures and exercises, we often only consider the initial transport and the morphological change as a consequence of this initial transport. This would correspond to the first morphodynamic time step only.

Accretion length and region of influence from numerical and analytical solution

Above we have considered a simplified situation, for which the textbook also presents some analytical solutions, for 1) the accretion length \(L(t)= 2 \sqrt{\frac{\phi'St}{\pi d}}\) at the breakwater, and 2) the region of influence of the breakwater \(X = 2.5 \sqrt{\pi}\frac{L}{\phi'}\) (see Figure 8.12 and Equation 8.25). Note that the angle is in radians in these equations. Let’s see how they compare with the numerical solution. In the first cell below we get the accretion length and region of influence for the modelled time. In the second cell, we calculate these same values analytically. Then in the third cell we plot both to compare.

Note that the values from the numerical solution are influenced by a chosen threshold value of 0.01% of the accretion length at the breakwater at each time. This is the minimum accretion length before we count it as change. You can change it if you’d like and see how it affects the results.

For the analytical computation of the accretion length and region of influence, we need \(\phi\)’, the angle of incidence at the closure depth of \(d = 7\) m (see Equation 8.25). Here we will use \(\phi\)’\(≈ \phi_0\), where \(\phi_0\) is a small angle, in line with the assumptions behind the analytical solution. We also need the yearly transport \(S\) in the undisturbed zone (where the coastline still has its original orientation). We already computed this above as follows: \(S\) (in m\(^3\)/s) = CERC(\(c_b\), 10, 1.2). Remember that the wave climate occurs 10% of the year. For 1 year, the contribution is: \(S\) (in \(m^3\)/year) \(= (0.1 * S) * 365.25 * 24 * 3600\).

# Get accretion length and region of influence from the numerical solution

# minimal accretion to include it in region of influence
threshold = 0.0001  # times accretion length at breakwater

acc_num = Y_t[:, -1]
roi_num = np.zeros(T.shape)

for it in range(len(T)):
    X_influence = X[Y_t[it, :] > threshold * acc_num[it]]
    roi_num[it] = np.max(X_influence) - np.min(X_influence)
# Get accretion length and region of influence from the analytical solution
# We need to use the angle in radians in the formulas!
phi_prime = phi0

# For the depth of closure, we have previously assumed d=7
d = 7  #  m

# We can calculate our accretion length analytically
acc_ana = 2 * np.sqrt((phi_prime / 360 * 2 * np.pi) * S_total * T / (np.pi * d))

# And from that we can calculate our region of influence analytically
roi_ana = 2.5 * np.sqrt(np.pi) * acc_ana / (phi_prime / 360 * 2 * np.pi)
# Plot the accretion length and region of influence from both the numerical and the analytical solution
acc_plot = (
    hv.Curve((T, acc_num), label="numerical")
    * hv.Curve((T, acc_ana), label="analytical")
).opts(
    height=400,
    legend_position="bottom_right",
    xlabel="time [years]",
    ylabel="Accretion length at breakwater L [m]",
    show_grid=True,
    responsive=True,
)
roi_plot = (
    hv.Curve((T, roi_num), label="numerical")
    * hv.Curve((T, roi_ana), label="analytical")
).opts(
    height=400,
    legend_position="bottom_right",
    xlabel="time [years]",
    ylabel="Region of influence X of breakwater [m]",
    show_grid=True,
    responsive=True,
)

(acc_plot + roi_plot).opts(shared_axes=False)

Questions about part 2

Using these plots, try to answer the questions below. The first questions deal directly with the numerical and analytical solution as described above and shown in the plots.

We will also look beyond our current model, which uses very simple wave conditions and boundary conditions. We could improve and extend this model further by using more wave conditions and different boundary conditions. The last two questions below concern this extension.

q = [
    "Q7-accretion_length_and_ROI",
    "Q7-outward_growth",
    "Q7-angle_at_breakwater",
    "Q7-yearly_transport",
    "Q7-depth_of_closure",
    "Q7-shadow_effects",
    "Q7-bypass_effect",
]

question_industry.serve(q)

Part 3: Transport for a wave climate as a function of coastline orientation

Previously, we plotted the (\(S\),\(\phi\))-curve for a single wave condition only. We will now include multiple wave conditions and will determine the transport as a function of the coastline orientation. This will result in a different type of curve, as you will see.

Such a curve generally underlies numerical coastline models. In such models, a full wave climate is often taken into account. Further, the transport is computed by changing the coastline orientation relative to the waves. First, the transport for the full climate is computed for the initial coastline orientation of zero. Then, the coastline orientation is changed a bit and the transport for the full climate is recomputed. This procedure for constructing a \(S\)-curve as a function of the coastline orientation is quite similar to constructing an (\(S\),\(\phi\))-curve. The procedure is further explained in Intermezzo 8.2 in the textbook.

We will now step through the example computations as shown in Intermezzo 8.2. We will use the condensed wave climate as presented in Table 8.2. Each wave height and offshore angle of incidence is associated with a period \(T\) assumed to be 7 seconds. Following the above described procedure, we can plot the transport as a function of the shoreline orientation, recreating Figure 8.10.

Wave input

First, we define the dataset containing the wave climate as in Table 8.2. Run the cell below.

Hs_array = np.array([0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4])

phi_array = np.array(
    [-45, -15, 15, 45, -45, -15, 15, 45, -45, -15, 15, 45, -45, -15, 15, 45]
)

days_array = np.array([10, 10, 25, 8, 8, 15, 35, 16, 4, 10, 21, 11, 1, 1, 4, 2])

df = pd.DataFrame(
    data=zip(Hs_array, phi_array, days_array), columns=["Hs", "phi", "days"]
)

Determine the transport for the input wave climate as a function of coastline orientation

We construct the \(S\)-curve as a function of the coastline orientation in three steps using the three cells below: 1. The first cell defines a function that calculates the transport S for that wave climate, given a coastline orientation 2. The second cell loops through different values of coastal orientation, and calculates the total yearly transport for each of these orientations 3. The third cell plots the results

We should get a figure quite similar to Figure 8.10 in the book, which was created using the coastline model Unibest-CL+. Make sure to compare the two.

You will obtain more insight in how such a transport curve is used in a coastline model in the B1 Coastal Engineering module.

def get_S_coastline(coastline_orientation, wave_climate):
    """
    Returns yearly transport for angle phi [degrees]

    Transport is already scaled for the relative occurrence of the conditions.
    """
    total_transport = 0
    T = 7
    gamma = 0.8
    
    for index, row in df.iterrows():

        Hs, angle, days = row

        # Our formulation of the CERC formula (the choice of K) was based on Hrms, so we determine Hrms from Hs
        Hrms = Hs / np.sqrt(2)

        # use our function to compute wave characteristics at breaking, based on Hrms
        cb, phib, Hb, hb = find_cb_phib_Hb_hb(angle - coastline_orientation, Hrms, T, gamma)

        # compute transport
        S = CERC(cb, angle - coastline_orientation, Hrms)

        # compute total transport
        total_transport += days / 365.25 * S
        
    return total_transport * 365.25 * 24 * 3600
orientations = np.linspace(-45, 45, 200)
transports = np.zeros(orientations.shape)

for i in range(len(orientations)):
    transports[i] = get_S_coastline(orientations[i], df)
# Plot the curve of S as a function of coastline orientation
fig = (
    hv.Curve((orientations, transports))
    * hv.HLine(0).opts(color="black")
    * hv.VLine(0).opts(color="black")
).opts(
    xlabel="coastline orientation [°]",
    ylabel="S [m³/year]",
    title="(S, coastline orientation)-curve",
    height=400,
    responsive=True,
    show_grid=True,
)

pn.Column(fig, max_width=800, sizing_mode='stretch_width')

Questions about part 3

Now you can try to answer the following questions, using the cell below to check the transport given a particular coastline orientation:

# Get the value as in the figure above for a certain values of the coastline orientation (in degrees)
coastline_orientation = -20

print(
    f"Total yearly transport: {get_S_coastline(coastline_orientation, df):.0f} [m³/year]"  
)
Total yearly transport: 931008 [m³/year]
q = [
    "Q7-yearly_transport2",
    "Q7-angle_zero_transport",
]

question_industry.serve(q)

The end

You have reached the end of this notebook for week 7 (the only notebook of this week).

6: Cross-shore transport
8a: Escoffier curve