|
| 1 | +"""Generic velocity profiles.""" |
| 2 | + |
| 3 | +import warnings |
| 4 | + |
| 5 | +import numpy as np |
| 6 | +import pandas as pd |
| 7 | + |
| 8 | + |
| 9 | +def kea16_profile(depth: float, vs30: float, region: str) -> pd.DataFrame: |
| 10 | + """ |
| 11 | + Calculate the median shear-wave velocity profile and its standard deviation |
| 12 | + at a given depth, based on the VS30 value and region, using the model from |
| 13 | + Kamai et al. (2016). |
| 14 | +
|
| 15 | + Parameters |
| 16 | + ---------- |
| 17 | + depth : float or np.ndarray |
| 18 | + The depth(s) in meters at which to calculate the profile. |
| 19 | + vs30 : float |
| 20 | + The VS30 value in m/sec. |
| 21 | + region : str |
| 22 | + The region for the model. Must be either 'California' or 'Japan'. |
| 23 | +
|
| 24 | + Returns |
| 25 | + ------- |
| 26 | + pd.DataFrame |
| 27 | + DataFrame with index as depth and columns: |
| 28 | + - 'vs_median': float or np.ndarray |
| 29 | + The median shear-wave velocity at the given depth(s) in m/sec. |
| 30 | + - 'std_vs_ln_units': float |
| 31 | + The standard deviation of the shear-wave velocity in ln units. |
| 32 | +
|
| 33 | + Raises |
| 34 | + ------ |
| 35 | + ValueError |
| 36 | + If the region is not 'California' or 'Japan'. |
| 37 | +
|
| 38 | + Warns |
| 39 | + ----- |
| 40 | + UserWarning |
| 41 | + If vs30 is outside the recommended range [250, 850] m/sec. |
| 42 | +
|
| 43 | + References |
| 44 | + ---------- |
| 45 | + Kamai, R., et al. (2016). "Nonlinear site response from the KiK-net |
| 46 | + database." Bulletin of the Seismological Society of America, 106(4), |
| 47 | + 1710-1723. |
| 48 | + """ |
| 49 | + # Coefficients from Table 2 [cite: 184, 185] |
| 50 | + if region == "california": |
| 51 | + b1 = 0.25 |
| 52 | + b2 = 104 |
| 53 | + b3 = 0.22 |
| 54 | + b4 = 166 |
| 55 | + b5 = 53 |
| 56 | + b6 = -0.00388 |
| 57 | + b7 = 0.0002 |
| 58 | + b8 = 0.195 |
| 59 | + elif region == "japan": |
| 60 | + b1 = 0.2 |
| 61 | + b2 = 40 |
| 62 | + b3 = 0.25 |
| 63 | + b4 = 260 |
| 64 | + b5 = 28 |
| 65 | + b6 = -0.00296 |
| 66 | + b7 = 0 |
| 67 | + b8 = 0.4 |
| 68 | + else: |
| 69 | + raise ValueError("Region must be 'California' or 'Japan'") |
| 70 | + |
| 71 | + if not (250 <= vs30 <= 850): |
| 72 | + # The model is recommended for VS30 between 250 and 850 m/sec. |
| 73 | + warnings.warn( |
| 74 | + "The model is recommended for VS30 between 250 and 850 m/sec. " |
| 75 | + "Values outside this range may lead to inaccurate results.", |
| 76 | + UserWarning, |
| 77 | + ) |
| 78 | + |
| 79 | + a0 = b1 * vs30 + b2 # Equation 3a |
| 80 | + a1 = b3 * vs30 + b4 # Equation 3b |
| 81 | + a2 = b5 * np.exp(b6 * vs30) # Equation 3c |
| 82 | + |
| 83 | + # Ensure the argument to log is positive and handle depth=0 or negative depths |
| 84 | + log_arg = (np.array(depth) + a2) / a2 |
| 85 | + vs_median = a0 + a1 * np.log( |
| 86 | + np.maximum(log_arg, 1e-9) |
| 87 | + ) # Equation 3, using maximum to handle non-positive log arguments |
| 88 | + |
| 89 | + a3 = b7 * vs30 + b8 # Equation 4a |
| 90 | + std_vs_ln_units = a3 # Equation 4 |
| 91 | + |
| 92 | + df = pd.DataFrame( |
| 93 | + {"vs_median": vs_median, "std_vs_ln_units": std_vs_ln_units}, |
| 94 | + index=pd.Index(depth, name="depth"), |
| 95 | + ) |
| 96 | + |
| 97 | + return df |
0 commit comments