earthsciencekit 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- earthsciencekit/__init__.py +6 -0
- earthsciencekit/_validation.py +62 -0
- earthsciencekit/atmosphere/__init__.py +57 -0
- earthsciencekit/atmosphere/core/__init__.py +5 -0
- earthsciencekit/atmosphere/core/base.py +10 -0
- earthsciencekit/atmosphere/systems/__init__.py +1 -0
- earthsciencekit/atmosphere/systems/dynamics.py +208 -0
- earthsciencekit/atmosphere/systems/hydrostatics.py +214 -0
- earthsciencekit/atmosphere/systems/moisture.py +162 -0
- earthsciencekit/atmosphere/systems/thermodynamics.py +222 -0
- earthsciencekit/atmosphere/tests/__init__.py +0 -0
- earthsciencekit/atmosphere/tests/test_models.py +741 -0
- earthsciencekit/atmosphere/tests/test_physics.py +81 -0
- earthsciencekit/atmosphere/visualizers/__init__.py +9 -0
- earthsciencekit/climate/__init__.py +63 -0
- earthsciencekit/climate/core/__init__.py +5 -0
- earthsciencekit/climate/core/base.py +10 -0
- earthsciencekit/climate/systems/__init__.py +1 -0
- earthsciencekit/climate/systems/carbon.py +76 -0
- earthsciencekit/climate/systems/forcing.py +124 -0
- earthsciencekit/climate/systems/radiation.py +214 -0
- earthsciencekit/climate/systems/response.py +402 -0
- earthsciencekit/climate/systems/sea_level.py +77 -0
- earthsciencekit/climate/tests/__init__.py +0 -0
- earthsciencekit/climate/tests/test_models.py +1015 -0
- earthsciencekit/climate/tests/test_physics.py +16 -0
- earthsciencekit/climate/visualizers/__init__.py +9 -0
- earthsciencekit/constants/__init__.py +23 -0
- earthsciencekit/geophysics/__init__.py +77 -0
- earthsciencekit/geophysics/core/__init__.py +5 -0
- earthsciencekit/geophysics/core/base.py +10 -0
- earthsciencekit/geophysics/systems/__init__.py +1 -0
- earthsciencekit/geophysics/systems/deformation.py +161 -0
- earthsciencekit/geophysics/systems/dynamo.py +159 -0
- earthsciencekit/geophysics/systems/figure.py +292 -0
- earthsciencekit/geophysics/systems/gravity.py +235 -0
- earthsciencekit/geophysics/systems/magnetism.py +354 -0
- earthsciencekit/geophysics/tests/__init__.py +0 -0
- earthsciencekit/geophysics/tests/test_models.py +898 -0
- earthsciencekit/geophysics/tests/test_physics.py +171 -0
- earthsciencekit/geophysics/visualizers/__init__.py +9 -0
- earthsciencekit/integrators/__init__.py +62 -0
- earthsciencekit/oceanography/__init__.py +61 -0
- earthsciencekit/oceanography/core/__init__.py +5 -0
- earthsciencekit/oceanography/core/base.py +10 -0
- earthsciencekit/oceanography/systems/__init__.py +1 -0
- earthsciencekit/oceanography/systems/circulation.py +388 -0
- earthsciencekit/oceanography/systems/hydrography.py +212 -0
- earthsciencekit/oceanography/systems/inertial.py +86 -0
- earthsciencekit/oceanography/systems/waves.py +281 -0
- earthsciencekit/oceanography/tests/__init__.py +0 -0
- earthsciencekit/oceanography/tests/test_models.py +916 -0
- earthsciencekit/oceanography/tests/test_physics.py +102 -0
- earthsciencekit/oceanography/visualizers/__init__.py +9 -0
- earthsciencekit/plate_tectonics/__init__.py +59 -0
- earthsciencekit/plate_tectonics/core/__init__.py +5 -0
- earthsciencekit/plate_tectonics/core/base.py +10 -0
- earthsciencekit/plate_tectonics/systems/__init__.py +1 -0
- earthsciencekit/plate_tectonics/systems/boundaries.py +201 -0
- earthsciencekit/plate_tectonics/systems/cooling.py +238 -0
- earthsciencekit/plate_tectonics/systems/isostasy.py +157 -0
- earthsciencekit/plate_tectonics/systems/kinematics.py +290 -0
- earthsciencekit/plate_tectonics/tests/__init__.py +0 -0
- earthsciencekit/plate_tectonics/tests/test_models.py +920 -0
- earthsciencekit/plate_tectonics/tests/test_physics.py +94 -0
- earthsciencekit/plate_tectonics/visualizers/__init__.py +9 -0
- earthsciencekit/py.typed +0 -0
- earthsciencekit/results.py +73 -0
- earthsciencekit/seismology/__init__.py +59 -0
- earthsciencekit/seismology/core/__init__.py +5 -0
- earthsciencekit/seismology/core/base.py +10 -0
- earthsciencekit/seismology/systems/__init__.py +1 -0
- earthsciencekit/seismology/systems/interfaces.py +102 -0
- earthsciencekit/seismology/systems/rays.py +221 -0
- earthsciencekit/seismology/systems/sources.py +249 -0
- earthsciencekit/seismology/systems/waves.py +268 -0
- earthsciencekit/seismology/tests/__init__.py +0 -0
- earthsciencekit/seismology/tests/test_models.py +852 -0
- earthsciencekit/seismology/tests/test_physics.py +100 -0
- earthsciencekit/seismology/visualizers/__init__.py +9 -0
- earthsciencekit/tests/__init__.py +0 -0
- earthsciencekit/tests/test_core.py +181 -0
- earthsciencekit/tests/test_relations.py +36 -0
- earthsciencekit/units/__init__.py +66 -0
- earthsciencekit/visualizers/__init__.py +45 -0
- earthsciencekit/volcanology/__init__.py +61 -0
- earthsciencekit/volcanology/core/__init__.py +5 -0
- earthsciencekit/volcanology/core/base.py +10 -0
- earthsciencekit/volcanology/systems/__init__.py +1 -0
- earthsciencekit/volcanology/systems/eruptions.py +161 -0
- earthsciencekit/volcanology/systems/geochemistry.py +226 -0
- earthsciencekit/volcanology/systems/geochronology.py +304 -0
- earthsciencekit/volcanology/systems/magma.py +190 -0
- earthsciencekit/volcanology/tests/__init__.py +0 -0
- earthsciencekit/volcanology/tests/test_models.py +485 -0
- earthsciencekit/volcanology/tests/test_physics.py +176 -0
- earthsciencekit/volcanology/visualizers/__init__.py +9 -0
- earthsciencekit-0.2.0.dist-info/METADATA +127 -0
- earthsciencekit-0.2.0.dist-info/RECORD +102 -0
- earthsciencekit-0.2.0.dist-info/WHEEL +5 -0
- earthsciencekit-0.2.0.dist-info/licenses/LICENSE +15 -0
- earthsciencekit-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Earthquake sources: seismic moment, magnitudes, frequency-magnitude and aftershock laws, and a damped oscillator.
|
|
2
|
+
|
|
3
|
+
SI units unless stated otherwise.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from earthsciencekit._validation import model, quantity, require, safe_arithmetic
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"seismic_moment",
|
|
12
|
+
"moment_magnitude",
|
|
13
|
+
"moment_from_magnitude",
|
|
14
|
+
"gutenberg_richter",
|
|
15
|
+
"omori_rate",
|
|
16
|
+
"local_magnitude",
|
|
17
|
+
"simulate_oscillator",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@model
|
|
22
|
+
def seismic_moment(rigidity, area, slip):
|
|
23
|
+
"""Fault seismic moment.
|
|
24
|
+
|
|
25
|
+
Moment connects an earthquake to fault area and displacement.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
rigidity : float or array_like
|
|
30
|
+
Pa; constraint: positive.
|
|
31
|
+
area : float or array_like
|
|
32
|
+
m^2; constraint: positive.
|
|
33
|
+
slip : float or array_like
|
|
34
|
+
m; constraint: nonnegative.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
numpy scalar or ndarray
|
|
39
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
40
|
+
|
|
41
|
+
Notes
|
|
42
|
+
-----
|
|
43
|
+
Scalar mean-slip estimate; no spatial rupture distribution.
|
|
44
|
+
Equation: ``rigidity * area * slip``.
|
|
45
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
46
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
47
|
+
"""
|
|
48
|
+
rigidity = quantity("rigidity", rigidity, lower=0, strict_lower=True)
|
|
49
|
+
area = quantity("area", area, lower=0, strict_lower=True)
|
|
50
|
+
slip = quantity("slip", slip, lower=0)
|
|
51
|
+
return rigidity * area * slip
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@model
|
|
55
|
+
def moment_magnitude(moment):
|
|
56
|
+
"""Moment magnitude in SI.
|
|
57
|
+
|
|
58
|
+
Moment magnitude relates logarithmic event size to scalar moment.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
moment : float or array_like
|
|
63
|
+
N m; constraint: positive.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
numpy scalar or ndarray
|
|
68
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
69
|
+
|
|
70
|
+
Notes
|
|
71
|
+
-----
|
|
72
|
+
Uses the 9.1 intercept with moment in N m, not dyne cm.
|
|
73
|
+
Equation: ``(2 / 3) * (np.log10(moment) - 9.1)``.
|
|
74
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
75
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
76
|
+
"""
|
|
77
|
+
moment = quantity("moment", moment, lower=0, strict_lower=True)
|
|
78
|
+
return (2 / 3) * (np.log10(moment) - 9.1)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@model
|
|
82
|
+
def moment_from_magnitude(magnitude):
|
|
83
|
+
"""Inverse moment magnitude.
|
|
84
|
+
|
|
85
|
+
Inverting magnitude enables comparisons on a physical moment scale.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
magnitude : float or array_like
|
|
90
|
+
1; constraint: real.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
numpy scalar or ndarray
|
|
95
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
96
|
+
|
|
97
|
+
Notes
|
|
98
|
+
-----
|
|
99
|
+
Same SI convention as moment_magnitude; very large inputs can overflow.
|
|
100
|
+
Equation: ``10 ** (1.5 * magnitude + 9.1)``.
|
|
101
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
102
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
103
|
+
"""
|
|
104
|
+
magnitude = quantity("magnitude", magnitude)
|
|
105
|
+
return 10 ** (1.5 * magnitude + 9.1)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@model
|
|
109
|
+
def local_magnitude(amplitude, distance):
|
|
110
|
+
"""Richter local magnitude.
|
|
111
|
+
|
|
112
|
+
A logarithm of recorded amplitude, corrected for distance, ranks earthquakes by size.
|
|
113
|
+
|
|
114
|
+
Parameters
|
|
115
|
+
----------
|
|
116
|
+
amplitude : float or array_like
|
|
117
|
+
m; constraint: positive. Peak Wood-Anderson displacement amplitude.
|
|
118
|
+
distance : float or array_like
|
|
119
|
+
m; constraint: positive. Hypocentral distance.
|
|
120
|
+
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
numpy scalar or ndarray
|
|
124
|
+
Model value [1]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
125
|
+
|
|
126
|
+
Notes
|
|
127
|
+
-----
|
|
128
|
+
Richter's anchor (1 mm at 100 km is magnitude 3) with the Hutton and Boore (1987)
|
|
129
|
+
southern California distance correction, fitted from about 10 to 700 km.
|
|
130
|
+
Equation: ``np.log10(amplitude / 1e-3) + 1.11 * np.log10(distance / 1e5) + 1.89e-6 * (distance - 1e5) + 3``.
|
|
131
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
132
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
133
|
+
"""
|
|
134
|
+
amplitude = quantity("amplitude", amplitude, lower=0, strict_lower=True)
|
|
135
|
+
distance = quantity("distance", distance, lower=0, strict_lower=True)
|
|
136
|
+
return np.log10(amplitude / 1e-3) + 1.11 * np.log10(distance / 1e5) + 1.89e-6 * (distance - 1e5) + 3
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@model
|
|
140
|
+
def gutenberg_richter(a, b, magnitude):
|
|
141
|
+
"""Earthquake frequency-size relation.
|
|
142
|
+
|
|
143
|
+
A logarithmic frequency-size law summarizes an earthquake catalogue.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
a : float or array_like
|
|
148
|
+
1; constraint: real.
|
|
149
|
+
b : float or array_like
|
|
150
|
+
1; constraint: positive.
|
|
151
|
+
magnitude : float or array_like
|
|
152
|
+
1; constraint: real.
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
numpy scalar or ndarray
|
|
157
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
158
|
+
|
|
159
|
+
Notes
|
|
160
|
+
-----
|
|
161
|
+
Expected cumulative count for the fitted catalogue interval; completeness matters.
|
|
162
|
+
Equation: ``10 ** (a - b * magnitude)``.
|
|
163
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
164
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
165
|
+
"""
|
|
166
|
+
a = quantity("a", a)
|
|
167
|
+
b = quantity("b", b, lower=0, strict_lower=True)
|
|
168
|
+
magnitude = quantity("magnitude", magnitude)
|
|
169
|
+
return 10 ** (a - b * magnitude)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@model
|
|
173
|
+
def omori_rate(productivity, offset, exponent, time):
|
|
174
|
+
"""Modified Omori aftershock rate.
|
|
175
|
+
|
|
176
|
+
Aftershock rates often decay with time after a main shock.
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
productivity : float or array_like
|
|
181
|
+
count s^(p-1); constraint: nonnegative.
|
|
182
|
+
offset : float or array_like
|
|
183
|
+
s; constraint: positive.
|
|
184
|
+
exponent : float or array_like
|
|
185
|
+
1; constraint: positive.
|
|
186
|
+
time : float or array_like
|
|
187
|
+
s; constraint: nonnegative.
|
|
188
|
+
|
|
189
|
+
Returns
|
|
190
|
+
-------
|
|
191
|
+
numpy scalar or ndarray
|
|
192
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
193
|
+
|
|
194
|
+
Notes
|
|
195
|
+
-----
|
|
196
|
+
Empirical fitted rate; no earthquake timing or location prediction.
|
|
197
|
+
Equation: ``productivity / (offset + time)**exponent``.
|
|
198
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
199
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
200
|
+
"""
|
|
201
|
+
productivity = quantity("productivity", productivity, lower=0)
|
|
202
|
+
offset = quantity("offset", offset, lower=0, strict_lower=True)
|
|
203
|
+
exponent = quantity("exponent", exponent, lower=0, strict_lower=True)
|
|
204
|
+
time = quantity("time", time, lower=0)
|
|
205
|
+
return productivity / (offset + time) ** exponent
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@safe_arithmetic
|
|
209
|
+
def simulate_oscillator(frequency, quality_factor, time, *, displacement=1.0, velocity=0.0):
|
|
210
|
+
"""Integrate an unforced damped seismic oscillator.
|
|
211
|
+
|
|
212
|
+
Parameters
|
|
213
|
+
----------
|
|
214
|
+
frequency : float
|
|
215
|
+
Positive scalar undamped natural frequency [Hz].
|
|
216
|
+
quality_factor : float
|
|
217
|
+
Positive scalar oscillator Q; damping ratio is 1/(2Q).
|
|
218
|
+
time : array_like
|
|
219
|
+
Nonnegative strictly increasing sampled times [s].
|
|
220
|
+
displacement, velocity : float
|
|
221
|
+
Finite scalar initial displacement [m] and velocity [m/s].
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
TimeSeries
|
|
226
|
+
Displacement [m] and velocity [m/s].
|
|
227
|
+
|
|
228
|
+
Notes
|
|
229
|
+
-----
|
|
230
|
+
Solves x'' + omega/Q x' + omega^2 x = 0. This single oscillator can
|
|
231
|
+
illustrate a seismometer response; it is not an earthquake rupture model.
|
|
232
|
+
"""
|
|
233
|
+
from earthsciencekit._validation import time_grid
|
|
234
|
+
from earthsciencekit.integrators import integrate_ode
|
|
235
|
+
|
|
236
|
+
frequency = quantity("frequency", frequency, lower=0, strict_lower=True)
|
|
237
|
+
quality_factor = quantity("quality_factor", quality_factor, lower=0, strict_lower=True)
|
|
238
|
+
displacement = quantity("displacement", displacement)
|
|
239
|
+
velocity = quantity("velocity", velocity)
|
|
240
|
+
require(all(v.ndim == 0 for v in (frequency, quality_factor, displacement, velocity)), "parameters must be scalars")
|
|
241
|
+
time = time_grid(time)
|
|
242
|
+
omega = 2 * np.pi * frequency
|
|
243
|
+
return integrate_ode(
|
|
244
|
+
lambda t, y: [y[1], -(omega**2) * y[0] - omega / quality_factor * y[1]],
|
|
245
|
+
[displacement, velocity],
|
|
246
|
+
time,
|
|
247
|
+
names=("displacement", "velocity"),
|
|
248
|
+
units=("m", "m/s"),
|
|
249
|
+
)
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""Elastic waves: body and surface wave speeds, the Ricker wavelet and anelastic attenuation.
|
|
2
|
+
|
|
3
|
+
SI units unless stated otherwise.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from earthsciencekit._validation import model, quantity, require
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"p_wave_speed",
|
|
12
|
+
"s_wave_speed",
|
|
13
|
+
"poisson_ratio",
|
|
14
|
+
"ricker_wavelet",
|
|
15
|
+
"attenuated_amplitude",
|
|
16
|
+
"rayleigh_wave_speed",
|
|
17
|
+
"love_wave_angular_frequency",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@model
|
|
22
|
+
def p_wave_speed(bulk_modulus, shear_modulus, density):
|
|
23
|
+
"""Compressional-wave elasticity.
|
|
24
|
+
|
|
25
|
+
Elastic moduli and density determine compressional wave speed.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
bulk_modulus : float or array_like
|
|
30
|
+
Pa; constraint: positive.
|
|
31
|
+
shear_modulus : float or array_like
|
|
32
|
+
Pa; constraint: nonnegative.
|
|
33
|
+
density : float or array_like
|
|
34
|
+
kg/m^3; constraint: positive.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
numpy scalar or ndarray
|
|
39
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
40
|
+
|
|
41
|
+
Notes
|
|
42
|
+
-----
|
|
43
|
+
Homogeneous isotropic small-strain medium; shear modulus may vanish in a fluid.
|
|
44
|
+
Equation: ``np.sqrt((bulk_modulus + 4 * shear_modulus / 3) / density)``.
|
|
45
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
46
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
47
|
+
"""
|
|
48
|
+
bulk_modulus = quantity("bulk_modulus", bulk_modulus, lower=0, strict_lower=True)
|
|
49
|
+
shear_modulus = quantity("shear_modulus", shear_modulus, lower=0)
|
|
50
|
+
density = quantity("density", density, lower=0, strict_lower=True)
|
|
51
|
+
return np.sqrt((bulk_modulus + 4 * shear_modulus / 3) / density)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@model
|
|
55
|
+
def s_wave_speed(shear_modulus, density):
|
|
56
|
+
"""Shear-wave elasticity.
|
|
57
|
+
|
|
58
|
+
Shear waves reveal rigidity and disappear in an ideal fluid.
|
|
59
|
+
|
|
60
|
+
Parameters
|
|
61
|
+
----------
|
|
62
|
+
shear_modulus : float or array_like
|
|
63
|
+
Pa; constraint: nonnegative.
|
|
64
|
+
density : float or array_like
|
|
65
|
+
kg/m^3; constraint: positive.
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
numpy scalar or ndarray
|
|
70
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
71
|
+
|
|
72
|
+
Notes
|
|
73
|
+
-----
|
|
74
|
+
Homogeneous isotropic medium; zero shear modulus returns zero speed.
|
|
75
|
+
Equation: ``np.sqrt(shear_modulus / density)``.
|
|
76
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
77
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
78
|
+
"""
|
|
79
|
+
shear_modulus = quantity("shear_modulus", shear_modulus, lower=0)
|
|
80
|
+
density = quantity("density", density, lower=0, strict_lower=True)
|
|
81
|
+
return np.sqrt(shear_modulus / density)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@model
|
|
85
|
+
def poisson_ratio(p_speed, s_speed):
|
|
86
|
+
"""Elastic velocity ratio.
|
|
87
|
+
|
|
88
|
+
Wave-speed ratios constrain isotropic elastic response.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
p_speed : float or array_like
|
|
93
|
+
m/s; constraint: positive.
|
|
94
|
+
s_speed : float or array_like
|
|
95
|
+
m/s; constraint: positive.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
numpy scalar or ndarray
|
|
100
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
101
|
+
|
|
102
|
+
Notes
|
|
103
|
+
-----
|
|
104
|
+
Requires positive bulk modulus: vp squared must exceed 4 vs squared over 3.
|
|
105
|
+
Equation: ``(p_speed**2 - 2 * s_speed**2) / (2 * (p_speed**2 - s_speed**2))``.
|
|
106
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
107
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
108
|
+
"""
|
|
109
|
+
p_speed = quantity("p_speed", p_speed, lower=0, strict_lower=True)
|
|
110
|
+
s_speed = quantity("s_speed", s_speed, lower=0, strict_lower=True)
|
|
111
|
+
require(p_speed**2 > 4 * s_speed**2 / 3, "wave speeds imply nonpositive bulk modulus")
|
|
112
|
+
return (p_speed**2 - 2 * s_speed**2) / (2 * (p_speed**2 - s_speed**2))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@model
|
|
116
|
+
def rayleigh_wave_speed(p_speed, s_speed):
|
|
117
|
+
"""Rayleigh surface-wave speed.
|
|
118
|
+
|
|
119
|
+
A free surface guides a slower wave that combines compression and shear.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
p_speed : float or array_like
|
|
124
|
+
m/s; constraint: positive.
|
|
125
|
+
s_speed : float or array_like
|
|
126
|
+
m/s; constraint: positive.
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
numpy scalar or ndarray
|
|
131
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
132
|
+
|
|
133
|
+
Notes
|
|
134
|
+
-----
|
|
135
|
+
Homogeneous isotropic half-space with a traction-free surface; nondispersive.
|
|
136
|
+
Requires positive bulk modulus: vp squared must exceed 4 vs squared over 3.
|
|
137
|
+
Equation: ``s_speed * np.sqrt(xi)``, where ``xi`` is the root in (0, 1) of
|
|
138
|
+
``xi**3 - 8 * xi**2 + (24 - 16 * gamma) * xi - 16 * (1 - gamma) = 0`` and
|
|
139
|
+
``gamma = (s_speed / p_speed)**2``, found by bisection to machine precision.
|
|
140
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
141
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
142
|
+
"""
|
|
143
|
+
p_speed = quantity("p_speed", p_speed, lower=0, strict_lower=True)
|
|
144
|
+
s_speed = quantity("s_speed", s_speed, lower=0, strict_lower=True)
|
|
145
|
+
require(p_speed**2 > 4 * s_speed**2 / 3, "wave speeds imply nonpositive bulk modulus")
|
|
146
|
+
gamma = (s_speed / p_speed) ** 2
|
|
147
|
+
# The cubic is -16(1 - gamma) < 0 at xi = 0 and 1 at xi = 1, with exactly one root between.
|
|
148
|
+
lower, upper = np.zeros_like(gamma), np.ones_like(gamma)
|
|
149
|
+
for _ in range(64):
|
|
150
|
+
middle = (lower + upper) / 2
|
|
151
|
+
below = middle**3 - 8 * middle**2 + (24 - 16 * gamma) * middle - 16 * (1 - gamma) < 0
|
|
152
|
+
lower, upper = np.where(below, middle, lower), np.where(below, upper, middle)
|
|
153
|
+
return s_speed * np.sqrt((lower + upper) / 2)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@model
|
|
157
|
+
def love_wave_angular_frequency(phase_speed, thickness, layer_s_speed, halfspace_s_speed, layer_rigidity, halfspace_rigidity):
|
|
158
|
+
"""Fundamental Love-wave dispersion in a layer over a half-space.
|
|
159
|
+
|
|
160
|
+
Shear waves trapped in a slow surface layer travel at a speed that depends on their period.
|
|
161
|
+
|
|
162
|
+
Parameters
|
|
163
|
+
----------
|
|
164
|
+
phase_speed : float or array_like
|
|
165
|
+
m/s; constraint: strictly between the layer and half-space shear speeds.
|
|
166
|
+
thickness : float or array_like
|
|
167
|
+
m; constraint: positive.
|
|
168
|
+
layer_s_speed : float or array_like
|
|
169
|
+
m/s; constraint: positive.
|
|
170
|
+
halfspace_s_speed : float or array_like
|
|
171
|
+
m/s; constraint: positive.
|
|
172
|
+
layer_rigidity : float or array_like
|
|
173
|
+
Pa; constraint: positive.
|
|
174
|
+
halfspace_rigidity : float or array_like
|
|
175
|
+
Pa; constraint: positive.
|
|
176
|
+
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
numpy scalar or ndarray
|
|
180
|
+
Model value [rad/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
181
|
+
|
|
182
|
+
Notes
|
|
183
|
+
-----
|
|
184
|
+
Fundamental mode of a uniform elastic layer over a faster uniform half-space.
|
|
185
|
+
Solves ``tan(k * thickness * s1) = halfspace_rigidity * s2 / (layer_rigidity * s1)`` for the
|
|
186
|
+
wavenumber k at the given phase speed, with ``s1 = np.sqrt(phase_speed**2 / layer_s_speed**2 - 1)``
|
|
187
|
+
and ``s2 = np.sqrt(1 - phase_speed**2 / halfspace_s_speed**2)``.
|
|
188
|
+
Equation: ``phase_speed * np.arctan(halfspace_rigidity * s2 / (layer_rigidity * s1)) / (thickness * s1)``.
|
|
189
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
190
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
191
|
+
"""
|
|
192
|
+
phase_speed = quantity("phase_speed", phase_speed, lower=0, strict_lower=True)
|
|
193
|
+
thickness = quantity("thickness", thickness, lower=0, strict_lower=True)
|
|
194
|
+
layer_s_speed = quantity("layer_s_speed", layer_s_speed, lower=0, strict_lower=True)
|
|
195
|
+
halfspace_s_speed = quantity("halfspace_s_speed", halfspace_s_speed, lower=0, strict_lower=True)
|
|
196
|
+
layer_rigidity = quantity("layer_rigidity", layer_rigidity, lower=0, strict_lower=True)
|
|
197
|
+
halfspace_rigidity = quantity("halfspace_rigidity", halfspace_rigidity, lower=0, strict_lower=True)
|
|
198
|
+
require(phase_speed > layer_s_speed, "phase speed must exceed the layer shear speed")
|
|
199
|
+
require(phase_speed < halfspace_s_speed, "phase speed must be below the half-space shear speed")
|
|
200
|
+
s1 = np.sqrt(phase_speed**2 / layer_s_speed**2 - 1)
|
|
201
|
+
s2 = np.sqrt(1 - phase_speed**2 / halfspace_s_speed**2)
|
|
202
|
+
return phase_speed * np.arctan(halfspace_rigidity * s2 / (layer_rigidity * s1)) / (thickness * s1)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@model
|
|
206
|
+
def ricker_wavelet(time, frequency):
|
|
207
|
+
"""Ricker seismic source pulse.
|
|
208
|
+
|
|
209
|
+
A compact pulse makes propagation and filtering examples inspectable.
|
|
210
|
+
|
|
211
|
+
Parameters
|
|
212
|
+
----------
|
|
213
|
+
time : float or array_like
|
|
214
|
+
s; constraint: real.
|
|
215
|
+
frequency : float or array_like
|
|
216
|
+
Hz; constraint: positive.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
numpy scalar or ndarray
|
|
221
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
222
|
+
|
|
223
|
+
Notes
|
|
224
|
+
-----
|
|
225
|
+
Unit peak at zero time; frequency is the peak-spectrum convention.
|
|
226
|
+
Equation: ``(1 - 2 * (np.pi * frequency * time)**2) * np.exp(-(np.pi * frequency * time)**2)``.
|
|
227
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
228
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
229
|
+
"""
|
|
230
|
+
time = quantity("time", time)
|
|
231
|
+
frequency = quantity("frequency", frequency, lower=0, strict_lower=True)
|
|
232
|
+
return (1 - 2 * (np.pi * frequency * time) ** 2) * np.exp(-((np.pi * frequency * time) ** 2))
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@model
|
|
236
|
+
def attenuated_amplitude(amplitude, frequency, time, quality_factor):
|
|
237
|
+
"""Constant-Q wave attenuation.
|
|
238
|
+
|
|
239
|
+
Finite quality factor models intrinsic amplitude decay during propagation.
|
|
240
|
+
|
|
241
|
+
Parameters
|
|
242
|
+
----------
|
|
243
|
+
amplitude : float or array_like
|
|
244
|
+
arbitrary amplitude; constraint: real.
|
|
245
|
+
frequency : float or array_like
|
|
246
|
+
Hz; constraint: nonnegative.
|
|
247
|
+
time : float or array_like
|
|
248
|
+
s; constraint: nonnegative.
|
|
249
|
+
quality_factor : float or array_like
|
|
250
|
+
1; constraint: positive.
|
|
251
|
+
|
|
252
|
+
Returns
|
|
253
|
+
-------
|
|
254
|
+
numpy scalar or ndarray
|
|
255
|
+
Model value [m/s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
256
|
+
|
|
257
|
+
Notes
|
|
258
|
+
-----
|
|
259
|
+
Constant-Q narrowband attenuation; geometric spreading is separate.
|
|
260
|
+
Equation: ``amplitude * np.exp(-np.pi * frequency * time / quality_factor)``.
|
|
261
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
262
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
263
|
+
"""
|
|
264
|
+
amplitude = quantity("amplitude", amplitude)
|
|
265
|
+
frequency = quantity("frequency", frequency, lower=0)
|
|
266
|
+
time = quantity("time", time, lower=0)
|
|
267
|
+
quality_factor = quantity("quality_factor", quality_factor, lower=0, strict_lower=True)
|
|
268
|
+
return amplitude * np.exp(-np.pi * frequency * time / quality_factor)
|
|
File without changes
|