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.
Files changed (102) hide show
  1. earthsciencekit/__init__.py +6 -0
  2. earthsciencekit/_validation.py +62 -0
  3. earthsciencekit/atmosphere/__init__.py +57 -0
  4. earthsciencekit/atmosphere/core/__init__.py +5 -0
  5. earthsciencekit/atmosphere/core/base.py +10 -0
  6. earthsciencekit/atmosphere/systems/__init__.py +1 -0
  7. earthsciencekit/atmosphere/systems/dynamics.py +208 -0
  8. earthsciencekit/atmosphere/systems/hydrostatics.py +214 -0
  9. earthsciencekit/atmosphere/systems/moisture.py +162 -0
  10. earthsciencekit/atmosphere/systems/thermodynamics.py +222 -0
  11. earthsciencekit/atmosphere/tests/__init__.py +0 -0
  12. earthsciencekit/atmosphere/tests/test_models.py +741 -0
  13. earthsciencekit/atmosphere/tests/test_physics.py +81 -0
  14. earthsciencekit/atmosphere/visualizers/__init__.py +9 -0
  15. earthsciencekit/climate/__init__.py +63 -0
  16. earthsciencekit/climate/core/__init__.py +5 -0
  17. earthsciencekit/climate/core/base.py +10 -0
  18. earthsciencekit/climate/systems/__init__.py +1 -0
  19. earthsciencekit/climate/systems/carbon.py +76 -0
  20. earthsciencekit/climate/systems/forcing.py +124 -0
  21. earthsciencekit/climate/systems/radiation.py +214 -0
  22. earthsciencekit/climate/systems/response.py +402 -0
  23. earthsciencekit/climate/systems/sea_level.py +77 -0
  24. earthsciencekit/climate/tests/__init__.py +0 -0
  25. earthsciencekit/climate/tests/test_models.py +1015 -0
  26. earthsciencekit/climate/tests/test_physics.py +16 -0
  27. earthsciencekit/climate/visualizers/__init__.py +9 -0
  28. earthsciencekit/constants/__init__.py +23 -0
  29. earthsciencekit/geophysics/__init__.py +77 -0
  30. earthsciencekit/geophysics/core/__init__.py +5 -0
  31. earthsciencekit/geophysics/core/base.py +10 -0
  32. earthsciencekit/geophysics/systems/__init__.py +1 -0
  33. earthsciencekit/geophysics/systems/deformation.py +161 -0
  34. earthsciencekit/geophysics/systems/dynamo.py +159 -0
  35. earthsciencekit/geophysics/systems/figure.py +292 -0
  36. earthsciencekit/geophysics/systems/gravity.py +235 -0
  37. earthsciencekit/geophysics/systems/magnetism.py +354 -0
  38. earthsciencekit/geophysics/tests/__init__.py +0 -0
  39. earthsciencekit/geophysics/tests/test_models.py +898 -0
  40. earthsciencekit/geophysics/tests/test_physics.py +171 -0
  41. earthsciencekit/geophysics/visualizers/__init__.py +9 -0
  42. earthsciencekit/integrators/__init__.py +62 -0
  43. earthsciencekit/oceanography/__init__.py +61 -0
  44. earthsciencekit/oceanography/core/__init__.py +5 -0
  45. earthsciencekit/oceanography/core/base.py +10 -0
  46. earthsciencekit/oceanography/systems/__init__.py +1 -0
  47. earthsciencekit/oceanography/systems/circulation.py +388 -0
  48. earthsciencekit/oceanography/systems/hydrography.py +212 -0
  49. earthsciencekit/oceanography/systems/inertial.py +86 -0
  50. earthsciencekit/oceanography/systems/waves.py +281 -0
  51. earthsciencekit/oceanography/tests/__init__.py +0 -0
  52. earthsciencekit/oceanography/tests/test_models.py +916 -0
  53. earthsciencekit/oceanography/tests/test_physics.py +102 -0
  54. earthsciencekit/oceanography/visualizers/__init__.py +9 -0
  55. earthsciencekit/plate_tectonics/__init__.py +59 -0
  56. earthsciencekit/plate_tectonics/core/__init__.py +5 -0
  57. earthsciencekit/plate_tectonics/core/base.py +10 -0
  58. earthsciencekit/plate_tectonics/systems/__init__.py +1 -0
  59. earthsciencekit/plate_tectonics/systems/boundaries.py +201 -0
  60. earthsciencekit/plate_tectonics/systems/cooling.py +238 -0
  61. earthsciencekit/plate_tectonics/systems/isostasy.py +157 -0
  62. earthsciencekit/plate_tectonics/systems/kinematics.py +290 -0
  63. earthsciencekit/plate_tectonics/tests/__init__.py +0 -0
  64. earthsciencekit/plate_tectonics/tests/test_models.py +920 -0
  65. earthsciencekit/plate_tectonics/tests/test_physics.py +94 -0
  66. earthsciencekit/plate_tectonics/visualizers/__init__.py +9 -0
  67. earthsciencekit/py.typed +0 -0
  68. earthsciencekit/results.py +73 -0
  69. earthsciencekit/seismology/__init__.py +59 -0
  70. earthsciencekit/seismology/core/__init__.py +5 -0
  71. earthsciencekit/seismology/core/base.py +10 -0
  72. earthsciencekit/seismology/systems/__init__.py +1 -0
  73. earthsciencekit/seismology/systems/interfaces.py +102 -0
  74. earthsciencekit/seismology/systems/rays.py +221 -0
  75. earthsciencekit/seismology/systems/sources.py +249 -0
  76. earthsciencekit/seismology/systems/waves.py +268 -0
  77. earthsciencekit/seismology/tests/__init__.py +0 -0
  78. earthsciencekit/seismology/tests/test_models.py +852 -0
  79. earthsciencekit/seismology/tests/test_physics.py +100 -0
  80. earthsciencekit/seismology/visualizers/__init__.py +9 -0
  81. earthsciencekit/tests/__init__.py +0 -0
  82. earthsciencekit/tests/test_core.py +181 -0
  83. earthsciencekit/tests/test_relations.py +36 -0
  84. earthsciencekit/units/__init__.py +66 -0
  85. earthsciencekit/visualizers/__init__.py +45 -0
  86. earthsciencekit/volcanology/__init__.py +61 -0
  87. earthsciencekit/volcanology/core/__init__.py +5 -0
  88. earthsciencekit/volcanology/core/base.py +10 -0
  89. earthsciencekit/volcanology/systems/__init__.py +1 -0
  90. earthsciencekit/volcanology/systems/eruptions.py +161 -0
  91. earthsciencekit/volcanology/systems/geochemistry.py +226 -0
  92. earthsciencekit/volcanology/systems/geochronology.py +304 -0
  93. earthsciencekit/volcanology/systems/magma.py +190 -0
  94. earthsciencekit/volcanology/tests/__init__.py +0 -0
  95. earthsciencekit/volcanology/tests/test_models.py +485 -0
  96. earthsciencekit/volcanology/tests/test_physics.py +176 -0
  97. earthsciencekit/volcanology/visualizers/__init__.py +9 -0
  98. earthsciencekit-0.2.0.dist-info/METADATA +127 -0
  99. earthsciencekit-0.2.0.dist-info/RECORD +102 -0
  100. earthsciencekit-0.2.0.dist-info/WHEEL +5 -0
  101. earthsciencekit-0.2.0.dist-info/licenses/LICENSE +15 -0
  102. 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