#!/usr/bin/env python # coding: utf-8 # In[5]: from scipy.optimize import fsolve import matplotlib.pyplot as plt import numpy as np from matplotlib.lines import Line2D import matplotlib.image as mpimg from matplotlib.offsetbox import AnchoredText import plotly.figure_factory as ff import pandas as pd from io import BytesIO import base64 # In[17]: def dispersion(T, h): """ Dispersion relationship uses fsolve to find the wave length Parameters ---------- T : float wave period [s] h : float water depth [m] Returns L : float the wave length [m] """ g=32 # deep water wave length L0 = g*T**2/(2*np.pi) # define dispersion relation with a lambda function to solve L dispersion = lambda L : L0 * np.tanh(2*np.pi*h/L) - L # solve for L L = fsolve(dispersion, L0)[0] print(L) return L # In[24]: def goda(Case, Hs, Hmax, h, d, h_acc, hc, T, beta, rho, slope_foreshore, B, lambda_, Still_Water_Level, Top_of_Wall_Height, Bottom_of_Wall, Pmin, s, p2_bottom): '''Parameters ---------- Hs : float mean of the highest 1/3 of the wave heights [m]. Hmax : float design wave height, equal to the mean of the highest 1/250 of the wave heights [m]. h : float water depth [m] d : float water depth in front of the caisson, on top of the foundation [m] h_acc : float submerged depth of the caisson [m] hc : float height of the caisson above the water line [m] Bm : float width of the berm [m] T : float wave period, Goda (2000) advises to use :math:`T_{1/3}` [s] beta : float angle between direction of wave approach and a line normal to the breakwater [rad] rho : float density of water [kg/m³] slope_foreshore : float slope of the foreshore [rad] lambda_ : list, optional, default: [1, 1, 1] modification factors of Takahasi (2002) for alternative monolithic breakwater. Input must be \\lambda_= [:math:`\\lambda_1, \\lambda_2, \\lambda_3`].''' # set dimensions as private variables h = h d = d h_acc = h_acc hc = hc rho = rho g=32 # water depth at a location of 5x H1/3 hb = Hs #h + 5 * np.tan(slope_foreshore) * Hs L = dispersion(T=T, h=h) # compute wave pressure coefficients (Goda, 2000) alpha_1 = (0.6 + 0.5*((4*np.pi*h/L)/ np.sinh(4*np.pi*h/L))**2) alpha_2_1 = ((hb-d)/(3*hb)*(Hmax/d)**2) alpha_2_2 = (2*d/Hmax) alpha_2 = min((alpha_2_1), (alpha_2_2)) alpha_3 = 1 - (h_acc/h) * (1-1/np.cosh(2*np.pi*h/L)) alpha_star = alpha_2 # Compute the elevation to which the wave pressure is exerted eta_star = 0.75*(1 + np.cos(beta))*lambda_[0]*Hmax # Compute the wave pressures p1 = (0.5*(1 + np.cos(beta))* (lambda_[0]*alpha_1 + lambda_[1]*alpha_star*np.cos(beta)**2)*rho*g*Hmax) p3 = alpha_3*p1 if eta_star > hc: p4 = p1*(1-hc/eta_star) else: p4 = 0 #Computes the Uplift wave forces pu = (0.5*(1 + np.cos(beta))* lambda_[2]*alpha_1*alpha_3*rho*g*Hmax) # Determine h_c_star h_c_star = min([eta_star, hc]) #Computes the Horizontal force due to the pressure P = (0.5*(p1+p3)*h_acc + 0.5*(p1+p4)*h_c_star)/1000 goda.p1 = p1 goda.p3 = p3 goda.hc = hc goda.eta_star = eta_star goda.h_acc = h_acc goda.pu = pu def distributed_load(loads, positions): # loads is a list of load values at different positions # positions is a list of positions corresponding to the load values x_centroid = 0 y_centroid = 0 total_load = 0 for i in range(len(loads)): x_centroid += loads[i] * positions[i] y_centroid += loads[i] total_load += loads[i] x_centroid /= y_centroid return (total_load, x_centroid) # Example usage loads = [goda.p1, p4, goda.p3] positions = [Still_Water_Level, Top_of_Wall_Height, Bottom_of_Wall] resultant, centroid = distributed_load(loads, positions) #Suction Loads (wave load under a wave trough) Steepness = Hs/L depth_to_wavelength = h/L print('Steepness(H/L) = ' + str(Steepness)) print('h/L = ' + str(depth_to_wavelength)) img = mpimg.imread('Goda_Suction.png') imgplot = plt.imshow(img) plt.axis('off') plt.show() Pmin = Pmin s = s p2_bottom = p2_bottom Suction_Load = Pmin*rho*g*h*Hs/1000 Load_Distance_from_Bottom = s*h Bottom_Pressure = p2_bottom*rho*g*Hs/1000 #Horizontal Loading Table Data data_matrix = [['Parameters', 'Units', 'Value'], [ 'Acceleration due to gravity', 'g[ft/s2]', g], [ 'Water Density', '[slug/ft3]', rho], ['Wave Period', 'T[s]', T], ['Significant Wave Height', 'Hs[ft]', Hs], ['Max. Wave Height', 'Hmax[ft]', round(Hmax,2)], ['Depth in front of scour protection', 'h[ft]', h], ['Water Depth 5Hs away', 'hb[ft]', h], ['Height of Seawall from bottom to SWL', 'h\'[ft]', h_acc], ['Height of Seawall above SWL', 'hc[ft]', hc], ['Depth in front of Seawall', 'd[ft]', d], ['Wavelength', 'L[ft]', round(L,2)], ['Angle of Incoming Waves', 'deg', beta], [ ], ['a1', '', round(alpha_1,2)], ['a2', '', round(alpha_star, 2)], ['a3', '', round(alpha_3,2)], ['Eta*', '[ft]', round(eta_star,2)], ['hc*', '[ft]', h_c_star], [ ], ['p1', '[ksf]', round(p1/1000,2)], ['p2', '[ksf]', round(p4/1000,2)], ['p3', '[ksf]', round(p3/1000,2)], [ ], ['Force on Breakwater', '[kip/ft]', round(P,2)]] #fig = ff.create_table(data_matrix) #fig.update_layout(width=1000) df = pd.DataFrame(data_matrix) # Generate a unique filename for each CSV file filename = f'goda_horizontal_load_{Case}.csv' # Create a buffer to hold the CSV data csv_buffer = BytesIO() df.to_csv(csv_buffer, header=None, index=None) # Reset the buffer position to the start csv_buffer.seek(0) # Create a download link for the generated CSV file b64 = base64.b64encode(csv_buffer.read()).decode() href = f'Download Horizontal Loading CSV' st.markdown(href, unsafe_allow_html=True) #fig.write_image() # Suction Loads Data Table data_matrix = [['Parameters', 'Units', 'Value'], [ 'Acceleration due to gravity', 'g[ft/s2]', g], [ 'Water Density', '[slug/ft3]', rho], ['Wave Period', 'T[s]', T], ['Significant Wave Height', 'Hs[ft]', Hs], ['Max. Wave Height', 'Hmax[ft]', round(Hmax,2)], ['Depth in front of Seawall', 'd[ft]', d], ['Wavelength', 'L[ft]', round(L,2)], ['Seabed Bottom Elevation', '[ft]', Bottom_of_Wall], [ ], ['Wave Steepness', 'H/L', round(Steepness,2)], ['Depth to Wavelength Ratio', 'h/L', round(depth_to_wavelength, 2)], ['Nondimensional Value of Wave Thrust Load', 'Pmin/pgHh', round(Pmin,2)], ['Nondimensional Value of Wave Thrust Arm', 's/d', round(s,2)], ['Suction Load', '[kip/ft]', round(Suction_Load,2)], ['Load Distance from Seabed Bottom', 's[ft]', round(Load_Distance_from_Bottom,2)], ['Load Elevation', '[ft] MSL', round(Bottom_of_Wall + Load_Distance_from_Bottom,2)]] df = pd.DataFrame(data_matrix) # Generate a unique filename for each CSV file filename = f'goda_suction_load_{Case}.csv' # Create a buffer to hold the CSV data csv_buffer = BytesIO() df.to_csv(csv_buffer, header=None, index=None) # Reset the buffer position to the start csv_buffer.seek(0) # Create a download link for the generated CSV file b64 = base64.b64encode(csv_buffer.read()).decode() href = f'Download Suction Loading CSV' st.markdown(href, unsafe_allow_html=True) # get the width B = B p = np.array([goda.p3, goda.p1, goda.p1-goda.p1*goda.hc/goda.eta_star])/1000 y = np.array([Bottom_of_Wall, Still_Water_Level, Top_of_Wall_Height]) x = np.array([0, B]) pu = np.array([goda.pu, 0])/1000 # scale the size of the caisson with the pressure scale = np.max(p)/np.max(y) scale_1 = np.max(y)/np.max(p) y = y x = x pu_1 = pu*scale_1*0.25 B = B*scale p_s = np.array([Bottom_Pressure, 1.5*Bottom_Pressure, 0]) y_s = np.array([Bottom_of_Wall, Bottom_of_Wall+Load_Distance_from_Bottom, Still_Water_Level]) x_s = np.array([0, B]) # scale the size of the caisson with the pressure scale_s = np.max(y_s)/np.max(p) x_s = x #y_s = y_s[0:2] custom_lines = [Line2D([0], [0], color='k', lw=4), Line2D([0], [0], color='b', lw=4), Line2D([0], [0], color='g', lw=4), Line2D([0], [0], color='r', lw=4) ] fig, ax = plt.subplots() # plot the caisson and wlev plt.vlines(x=0, ymin=((Bottom_of_Wall)), ymax=Top_of_Wall_Height, linewidth=2, color='k') plt.vlines(x=-B, ymin=((Bottom_of_Wall)), ymax=Top_of_Wall_Height, linewidth=2, color = 'k') plt.hlines(y=-(abs(Bottom_of_Wall)), xmin=0, xmax=-B, linewidth=2, color = 'k') plt.hlines(y=Top_of_Wall_Height, xmin=0, xmax=-B, linewidth=2, color='k') plt.hlines(y=(Still_Water_Level), xmin=0, xmax=np.max(p)*1.3, color='b') # plot pressure distributions plt.hlines(y=Bottom_of_Wall, xmin=0, xmax=p[0], color='g') plt.hlines(y=Top_of_Wall_Height, xmin=0, xmax=p[2], color='g') plt.plot(p, y, color='g') #plt.vlines(x=0, ymin=Bottom_of_Wall, ymax=Bottom_of_Wall - pu_1[0], color='purple') #plt.plot([0,-B], Bottom_of_Wall-pu_1, color='purple') # plot suction load distributions plt.hlines(y=Bottom_of_Wall, xmin=-p_s[0], xmax=0, color='r') #plt.hlines(y=-y_s[1], xmin=-p_s[1], xmax=0, color='r') plt.plot(-p_s, y_s, color='r') # red arrow plt.arrow(-p_s[1], Bottom_of_Wall+Load_Distance_from_Bottom , p_s[1], 0, head_width=0.15*Load_Distance_from_Bottom, head_length=0.03, linewidth=4, color='r', length_includes_head=True) # green arrow plt.arrow(resultant, centroid , -resultant, 0, head_width=0.15*Load_Distance_from_Bottom, head_length=0.03, linewidth=4, color='g', length_includes_head=True) # Dimensions Annotation Suction plt.arrow(-0.025, Bottom_of_Wall, 0, (Load_Distance_from_Bottom), color='k', head_length = 0.15*Load_Distance_from_Bottom, head_width = 0.01, length_includes_head = True) plt.arrow(-0.025, Bottom_of_Wall+Load_Distance_from_Bottom, 0, -Load_Distance_from_Bottom, color='k', head_length = 0.15*Load_Distance_from_Bottom, head_width = 0.01, length_includes_head = True) plt.annotate(str(round(Load_Distance_from_Bottom, 2)) + ' ft', xy=(0, 0), xytext=(-0.03, (Bottom_of_Wall+Load_Distance_from_Bottom)/.45), rotation=90 ) # Dimensions Annotation Horizontal plt.arrow(0.025, Bottom_of_Wall, 0, -(Bottom_of_Wall-centroid), color='k', head_length = 0.15*Load_Distance_from_Bottom, head_width = 0.01, length_includes_head = True) plt.arrow(0.025, Bottom_of_Wall+Load_Distance_from_Bottom, 0, -Load_Distance_from_Bottom, color='k', head_length = 0.15*Load_Distance_from_Bottom, head_width = 0.01, length_includes_head = True) plt.annotate(str(round(-Bottom_of_Wall+centroid, 1)) + ' ft', xy=(0, 0), xytext=(0.05, -(-Bottom_of_Wall+centroid)/1.5), rotation=90 ) #invert axes plt.xlim(np.max(p)*1.3, -np.max(p_s)) #plt.ylim(Bottom_of_Wall*1.6, np.max(y)*2) #plt.ylim(np.max(pu_1)+10, (-np.max(y)-5)) HL = 'Horizontal Load (kip/ft) = ' + str(round(P,2)) #UL = 'Uplift Load (ksf/ft) = ' + str(round(pu[0],2)) SL = 'Suction Load (kip/ft) = ' + str(round(Suction_Load, 2)) VW = 'Seawall' SW = 'Still Water Level' # add title and label plt.title('Pressure distributions computed with Goda (2000)') plt.xlabel('Pressure [ksf/ft]') plt.ylabel('Surface Elevation [ft], NGVD 29') plt.legend(custom_lines, [VW, SW, HL, SL], loc ='lower left') # add grid and show plot plt.grid() plt.savefig('Case_Number_' + str(Case) + '.png', dpi = 300) st.pyplot(fig) return goda.p1, p4, goda.p3, P, pu[0], Suction_Load, Bottom_Pressure, Load_Distance_from_Bottom # In[29]: import streamlit as st #def main(): # st.title("Goda Horizontal Loading Calculator") # # Display the image # img = mpimg.imread('Goda_Suction.png') # st.image(img, use_column_width=True) # User Inputs # Case_number = st.number_input("Case Number", value=3, min_value=1, step=1) # B = st.number_input("Width of Wall", value=2.0, min_value=0.0) # Bottom_of_Wall = st.number_input("Bottom of Wall", value=-11.92) # Still_Water_Level = st.number_input("Stillwater Level", value=0.0) # Top_of_Wall_Height = st.number_input("Top of Wall Height", value=7.27) # Hs = st.number_input("Hs", value=4.5) # T = st.number_input("T", value=7.0) # beta = st.number_input("Beta", value=0.0) # rho = st.number_input("Rho", value=2.0) # Pmin = st.number_input("Non-Dimensional Pmin (For Suction Loads - from Graphs Above)", value=0.4) # s = st.number_input("Non-Dimensional s (For Suction Loads - from Graphs Above)", value=0.5) # p2_bottom = st.number_input("Non-Dimensional p2 (For Suction Loads - from Graphs Above)", value=0.55) # Hmax = Hs*1.8 #design wave height, equal to the mean of the highest 1/250 of the wave heights [m]. # h = Still_Water_Level - Bottom_of_Wall #water depth [m] # d = h #water depth in front of the caisson, on top of the foundation [m] # h_acc = h #submerged depth of the caisson [m] # hc = Top_of_Wall_Height - Still_Water_Level #height of the caisson above the water line [m] # slope_foreshore = [1,100] #slope of the foreshore [rad] # lambda_=[1,1,1] # if st.button("Calculate"): # result = goda(Case = Case_number, Hs=Hs, Hmax=Hmax, h=h, d=d, h_acc=h_acc, hc=hc, T=T, # beta=beta, rho=rho, slope_foreshore=slope_foreshore, B = B, lambda_=lambda_, # Still_Water_Level = Still_Water_Level, Top_of_Wall_Height= Top_of_Wall_Height, # Bottom_of_Wall=Bottom_of_Wall, Pmin=Pmin, s=s, p2_bottom=p2_bottom ) def main(): st.title("Goda Horizontal Loading Calculator") # Display the image img = mpimg.imread('Goda_Suction.png') st.image(img, use_column_width=True) # User Inputs Case_number = st.number_input("Case Number", value=3, min_value=1, step=1) Hs = st.number_input("Wave Height, Hs [ft]", value=4.5) T = st.number_input("Period, T [s]", value=7.0) h = st.number_input("Depth, h [ft]", value=11.0) if st.button("Run Dispersion"): dispersion_result = dispersion(T=T, h=h) st.write("Suction Load Parameters:") st.write("Wavelength:", dispersion_result) st.write("Wave Steepness:", Hs/dispersion_result) st.write("h/L:", h/dispersion_result) B = 2.0 #st.number_input("Width of Wall [ft]", value=2.0, min_value=0.0) Bottom_of_Wall = st.number_input("Bottom of Wall [ft]", value=-11.92) Still_Water_Level = st.number_input("Stillwater Level [ft]", value=0.0) Top_of_Wall_Height = st.number_input("Top of Wall Height [ft]", value=7.27) beta = st.number_input("Beta [deg]", value=0.0) rho = 2 #st.number_input("Rho", value=2.0) Pmin = st.number_input("Non-Dimensional Pmin (For Suction Loads - from Graphs Above)", value=0.4) s = st.number_input("Non-Dimensional s (For Suction Loads - from Graphs Above)", value=0.5) p2_bottom = st.number_input("Non-Dimensional p2 (For Suction Loads - from Graphs Above)", value=0.55) Hmax = Hs*1.8 #design wave height, equal to the mean of the highest 1/250 of the wave heights [m]. h = Still_Water_Level - Bottom_of_Wall #water depth [m] d = h #water depth in front of the caisson, on top of the foundation [m] h_acc = h #submerged depth of the caisson [m] hc = Top_of_Wall_Height - Still_Water_Level #height of the caisson above the water line [m] slope_foreshore = [1,100] #slope of the foreshore [rad] lambda_=[1,1,1] if st.button("Run Goda"): goda_result = goda(Case=Case_number, Hs=Hs, Hmax=Hmax, h=h, d=d, h_acc=h_acc, hc=hc, T=T, beta=beta, rho=rho, slope_foreshore=slope_foreshore, B=B, lambda_=lambda_, Still_Water_Level=Still_Water_Level, Top_of_Wall_Height=Top_of_Wall_Height, Bottom_of_Wall=Bottom_of_Wall, Pmin=Pmin, s=s, p2_bottom=p2_bottom) #st.write("Goda Result:") #st.write("Output:", goda_result) if __name__ == "__main__": main()