"""
The wntr.scenario.earthquake module includes methods to
define an earthquake location, magnitude and depth, and
compute PGA, PGV, and repair rate.
"""
import wntr
import numpy as np
import pandas as pd
from scipy.spatial import distance
[docs]
class Earthquake(object):
"""
Earthquake scenario class.
"""
[docs]
def __init__(self, epicenter, magnitude, depth):
self.epicenter = epicenter
""" Earthquake epicenter, (x,y) tuple in meters"""
self.magnitude = magnitude
"""Earthquake magnitude, Richter scale"""
self.depth = depth
"""Earthquake depth, m"""
[docs]
def distance_to_epicenter(self, wn, element_type=wntr.network.Node):
"""
Distance to the epicenter
Parameters
-----------
wn : WaterNetworkModel
element_type: optional (default = wntr.network.Node)
Returns
---------
A pandas Series with distance to epicenter (m)
"""
R = pd.Series()
if element_type in [wntr.network.Link, wntr.network.Pipe, wntr.network.Pump, wntr.network.Valve]:
# Compute pipe center position
link_pos = {}
for name, link in wn.links(element_type):
start_point = link.start_node.coordinates
end_point = link.end_node.coordinates
link_pos[name] = ((end_point[0] + start_point[0])/2,
(end_point[1] + start_point[1])/2)
for name, link in wn.links(element_type):
R[name] = distance.euclidean(self.epicenter, link_pos[name]) # m
elif element_type in [wntr.network.Node, wntr.network.Junction, wntr.network.Tank, wntr.network.Reservoir]:
for name, node in wn.nodes(element_type):
R[name] = distance.euclidean(self.epicenter, node.coordinates) # m
return R
[docs]
def pga_attenuation_model(self,R,method=None):
"""
Peak ground acceleration attenuation models
Parameters
-----------
R : pd.Series
Distance to epicenter (m)
method : int (optional, default = None, average)
1 = Kawashima et al. (1984)
2 = Baag et al. (1998)
3 = Lee and Cho (2002)
Returns
--------
A pandas Series with peak ground acceleration (g)
"""
R = R/1000 # convert m to km
D = self.depth/1000 # convert m to km
delta = np.sqrt(np.power(R,2) + np.power(D,2))
if method == 1:
# Kawashima et al. (1984)
PGA = 403.8*np.power(10, 0.265*self.magnitude)*np.power(R+30, -1.218)
elif method == 2:
# Baag et al. (1998)
PGA = np.exp(0.4 + 1.2*self.magnitude - 0.76*np.log(delta) - 0.0094*delta)
elif method == 3:
# Lee and Cho (2002)
PGA = np.power(10, -1.83 + 0.386*self.magnitude - np.log10(R) - 0.0015*R)
else:
# Average of the three methods
PGA = ((403.8*np.power(10, 0.265*self.magnitude)*np.power(R+30, -1.218)) + \
np.exp(0.4 + 1.2*self.magnitude - 0.76*np.log(delta) - 0.0094*delta) + \
np.power(10, -1.83 + 0.386*self.magnitude - np.log10(R) - 0.0015*R))/3
PGA = PGA/100 # convert cm/s2 to m/s2
PGA = PGA/9.81 # convert m/s2 to g
return PGA
[docs]
def pgv_attenuation_model(self, R, method=None):
"""
Peak ground velocity attenuation models
Parameters
-----------
R : pd.Series
Distance to epicenter (m)
method : int (optional, default = None, average)
1 = Yu and Jin (2008) - Rock
2 = Yu and Jin (2008) - Soil
Returns
--------
A pandas Series with peak ground velocity (m/s)
"""
R = R/1000 # convert m to km
if method == 1:
# Yu and Jin (2008) - Rock
PGV = np.power(10, -0.848 + 0.775*self.magnitude + -1.834*np.log10(R+17))
elif method == 2:
# Yu and Jin (2008) - Soil
PGV = np.power(10, -0.285 + 0.711*self.magnitude + -1.851*np.log10(R+17))
else:
# Average
PGV = (np.power(10, -0.848 + 0.775*self.magnitude + -1.834*np.log10(R+17)) + \
np.power(10, -0.285 + 0.711*self.magnitude + -1.851*np.log10(R+17)))/2
PGV = PGV/100 # convert cm/s to m/s
return PGV
[docs]
def correction_factor(self, pipe_characteristics, diameter_weight=None, material_weight=None,
topography_weight=None, liquifaction_weight=None):
"""
Correction factor, maps pipe characteristics to weights
Parameters
-----------
pipe_characteristics : pd.DataFrame
Pipe characteristics which includes diameter, material, topography, and liquifaction
diameter_weight, material_weight, topography_weight, liquifaction_weight: dict
Weights, defaults based on Isoyama et al., 2000
Returns
--------
A pandas Series with the correction factor
"""
# Make sure the values are strings
pipe_characteristics = pd.DataFrame(data = pipe_characteristics.values, columns =pipe_characteristics.columns, index = pipe_characteristics.index.astype('str'))
if diameter_weight is None:
diameter_weight = {'Very small': 1.6, 'Small': 1.0, 'Medium': 0.8, 'Large': 0.5}
if material_weight is None:
material_weight = {'ACP': 1.2, 'PV': 1.0, 'PVC': 1.0, 'CIP': 1.0,
'PE': 0.8, 'HI-3P': 0.8, 'SP': 0.3, 'DCIP': 0.3}
if topography_weight is None:
topography_weight = {'Narrow valley': 3.2, 'Terrace': 1.5,
'Disturbed hill': 1.1, 'Alluvial': 1.0, 'Stiff alluvial': 0.4}
if liquifaction_weight is None:
liquifaction_weight = {'Total': 2.4, 'Partial': 2.0, 'None': 1.0}
C0 = pipe_characteristics['Diameter'].map(diameter_weight)
C1 = pipe_characteristics['Material'].map(material_weight)
C2 = pipe_characteristics['Topography'].map(topography_weight)
C3 = pipe_characteristics['Liquifaction'].map(liquifaction_weight)
C = C0*C1*C2*C3
return C
[docs]
def repair_rate_model(self, PGV, C=1, method=1):
"""
Calculate repair rate
Parameters
------------
PGV : pd.Series
Peak ground velocity (m/s)
K : pd.Series
Correction factor
method : int (default = 1)
1 = Linear
2 = Power
Returns
-------
A pandas Series with repair rate (number of repairs per m)
"""
PGV = (100*PGV)/2.54 # in/s
if method == 1:
# linear model
RR = C*0.00187*PGV
elif method == 2:
# Power model
RR = C*0.00108*np.power(PGV, 1.173)
else:
print("invalid method")
RR = RR*(3.28/1000) # convert 1/1000ft to 1/m
return RR
[docs]
def DTGR(self,M,M_min,M_max,b):
"""
Returns the the Doubly Truncated Gutenberg Richter cumulative probability
for the specified magnitude, magnitude range, and coefficient.
"""
B = b*np.log(10)
P = 1 - (np.exp(-B*M) - np.exp(-B*M_max))/(np.exp(-B*M_min) - np.exp(-B*M_max))
return P
[docs]
def DTGR_inv(self, P,M_min,M_max,b):
"""
Returns the inverse of the Doubly Truncated Gutenberg Richter distribution
for the specified probability, magnitude range, and coefficient.
"""
B = b*np.log(10)
M = np.log(np.exp(-B*M_min) - P*(np.exp(-B*M_min)-np.exp(-B*M_max)))/(-B)
return M