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,292 @@
1
+ """Figure of the Earth: rotational flattening, normal gravity, J2 from satellite orbits and the geoid.
2
+
3
+ SI units unless stated otherwise; latitude, inclination and spherical distance in radians.
4
+ """
5
+
6
+ import numpy as np
7
+
8
+ from earthsciencekit._validation import model, quantity, require
9
+
10
+ __all__ = [
11
+ "centrifugal_ratio",
12
+ "homogeneous_flattening",
13
+ "clairaut_gravity",
14
+ "normal_gravity",
15
+ "flattening_from_j2",
16
+ "nodal_precession_rate",
17
+ "stokes_function",
18
+ "degree_geoid_height",
19
+ ]
20
+
21
+ # GRS80 Somigliana constants: equatorial normal gravity, k = (b gamma_p) / (a gamma_e) - 1 and the first eccentricity squared.
22
+ _GAMMA_E = 9.7803267715
23
+ _SOMIGLIANA_K = 0.001931851353
24
+ _E2 = 0.00669438002290
25
+
26
+
27
+ @model
28
+ def centrifugal_ratio(angular_speed, radius, gravity):
29
+ """Ratio of centrifugal to gravitational acceleration at the equator.
30
+
31
+ Rotation flattens a planet in proportion to how strongly it competes with gravity.
32
+
33
+ Parameters
34
+ ----------
35
+ angular_speed : float or array_like
36
+ rad/s; constraint: nonnegative.
37
+ radius : float or array_like
38
+ m; constraint: positive. Equatorial radius.
39
+ gravity : float or array_like
40
+ m/s^2; constraint: positive. Equatorial gravity.
41
+
42
+ Returns
43
+ -------
44
+ numpy scalar or ndarray
45
+ Model value [1]. Scalar input returns a NumPy scalar; arrays broadcast.
46
+
47
+ Notes
48
+ -----
49
+ The geodetic ``m`` uses ``GM / (a b)`` in place of gravity; the two differ at the 0.5% level.
50
+ Equation: ``angular_speed**2 * radius / gravity``.
51
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
52
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
53
+ """
54
+ angular_speed = quantity("angular_speed", angular_speed, lower=0)
55
+ radius = quantity("radius", radius, lower=0, strict_lower=True)
56
+ gravity = quantity("gravity", gravity, lower=0, strict_lower=True)
57
+ return angular_speed**2 * radius / gravity
58
+
59
+
60
+ @model
61
+ def homogeneous_flattening(centrifugal_ratio):
62
+ """Flattening of a slowly rotating homogeneous fluid planet.
63
+
64
+ A uniform self-gravitating fluid in rotation settles into an oblate spheroid.
65
+
66
+ Parameters
67
+ ----------
68
+ centrifugal_ratio : float or array_like
69
+ 1; constraint: 0 <= value <= 0.1.
70
+
71
+ Returns
72
+ -------
73
+ numpy scalar or ndarray
74
+ Model value [1]: ``(a - c) / a``.
75
+
76
+ Notes
77
+ -----
78
+ First order in rotation (Maclaurin spheroid). A centrally condensed Earth is less flattened than this.
79
+ Equation: ``5 * centrifugal_ratio / 4``.
80
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
81
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
82
+ """
83
+ centrifugal_ratio = quantity("centrifugal_ratio", centrifugal_ratio, lower=0, upper=0.1)
84
+ return 5 * centrifugal_ratio / 4
85
+
86
+
87
+ @model
88
+ def clairaut_gravity(latitude, equatorial_gravity, flattening, centrifugal_ratio):
89
+ """Gravity on a rotating spheroid from Clairaut's theorem.
90
+
91
+ The increase of gravity toward the poles measures the flattening without any surveying.
92
+
93
+ Parameters
94
+ ----------
95
+ latitude : float or array_like
96
+ rad; constraint: -pi/2 <= latitude <= pi/2. Geodetic latitude.
97
+ equatorial_gravity : float or array_like
98
+ m/s^2; constraint: positive.
99
+ flattening : float or array_like
100
+ 1; constraint: 0 <= value < 1.
101
+ centrifugal_ratio : float or array_like
102
+ 1; constraint: nonnegative.
103
+
104
+ Returns
105
+ -------
106
+ numpy scalar or ndarray
107
+ Model value [m/s^2]. Scalar input returns a NumPy scalar; arrays broadcast.
108
+
109
+ Notes
110
+ -----
111
+ First order in flattening and rotation for a spheroid in hydrostatic equilibrium; the sin^4 term is omitted.
112
+ Equation: ``equatorial_gravity * (1 + (5 * centrifugal_ratio / 2 - flattening) * np.sin(latitude) ** 2)``.
113
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
114
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
115
+ """
116
+ latitude = quantity("latitude", latitude, lower=-np.pi / 2, upper=np.pi / 2)
117
+ equatorial_gravity = quantity("equatorial_gravity", equatorial_gravity, lower=0, strict_lower=True)
118
+ flattening = quantity("flattening", flattening, lower=0, upper=1, strict_upper=True)
119
+ centrifugal_ratio = quantity("centrifugal_ratio", centrifugal_ratio, lower=0)
120
+ return equatorial_gravity * (1 + (5 * centrifugal_ratio / 2 - flattening) * np.sin(latitude) ** 2)
121
+
122
+
123
+ @model
124
+ def normal_gravity(latitude):
125
+ """Normal gravity on the GRS80 ellipsoid.
126
+
127
+ The reference field that gravity anomalies are measured against.
128
+
129
+ Parameters
130
+ ----------
131
+ latitude : float or array_like
132
+ rad; constraint: -pi/2 <= latitude <= pi/2. Geodetic latitude.
133
+
134
+ Returns
135
+ -------
136
+ numpy scalar or ndarray
137
+ Model value [m/s^2]. Scalar input returns a NumPy scalar; arrays broadcast.
138
+
139
+ Notes
140
+ -----
141
+ Somigliana's closed form on the ellipsoid surface with the GRS80 constants
142
+ ``gamma_e = 9.7803267715``, ``k = 0.001931851353`` and ``e2 = 0.00669438002290``; exact for the level ellipsoid.
143
+ Equation: ``gamma_e * (1 + k * np.sin(latitude) ** 2) / np.sqrt(1 - e2 * np.sin(latitude) ** 2)``.
144
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
145
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
146
+ """
147
+ latitude = quantity("latitude", latitude, lower=-np.pi / 2, upper=np.pi / 2)
148
+ sine_squared = np.sin(latitude) ** 2
149
+ return _GAMMA_E * (1 + _SOMIGLIANA_K * sine_squared) / np.sqrt(1 - _E2 * sine_squared)
150
+
151
+
152
+ @model
153
+ def flattening_from_j2(j2, centrifugal_ratio):
154
+ """Flattening of a hydrostatic Earth from its J2.
155
+
156
+ The oblateness term of the gravity field and the rotation fix the shape of the level surface.
157
+
158
+ Parameters
159
+ ----------
160
+ j2 : float or array_like
161
+ 1; constraint: real. Unnormalized second zonal harmonic ``-C20``.
162
+ centrifugal_ratio : float or array_like
163
+ 1; constraint: nonnegative.
164
+
165
+ Returns
166
+ -------
167
+ numpy scalar or ndarray
168
+ Model value [1]. Scalar input returns a NumPy scalar; arrays broadcast.
169
+
170
+ Notes
171
+ -----
172
+ First order in flattening (Clairaut's relation for the external field); second-order terms change ``1/f`` by
173
+ about 0.1%.
174
+ Equation: ``3 * j2 / 2 + centrifugal_ratio / 2``.
175
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
176
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
177
+ """
178
+ j2 = quantity("j2", j2)
179
+ centrifugal_ratio = quantity("centrifugal_ratio", centrifugal_ratio, lower=0)
180
+ return 3 * j2 / 2 + centrifugal_ratio / 2
181
+
182
+
183
+ @model
184
+ def nodal_precession_rate(semi_major_axis, eccentricity, inclination, j2, radius, gravitational_parameter):
185
+ """Secular regression of a satellite orbit's node.
186
+
187
+ The equatorial bulge torques an inclined orbit, so the node drifts at a rate that measures J2.
188
+
189
+ Parameters
190
+ ----------
191
+ semi_major_axis : float or array_like
192
+ m; constraint: positive.
193
+ eccentricity : float or array_like
194
+ 1; constraint: 0 <= value < 1.
195
+ inclination : float or array_like
196
+ rad; constraint: 0 <= inclination <= pi.
197
+ j2 : float or array_like
198
+ 1; constraint: real.
199
+ radius : float or array_like
200
+ m; constraint: positive. Equatorial reference radius of J2.
201
+ gravitational_parameter : float or array_like
202
+ m^3/s^2; constraint: positive. ``G M`` of the Earth.
203
+
204
+ Returns
205
+ -------
206
+ numpy scalar or ndarray
207
+ Model value [rad/s]: negative (westward) for prograde orbits and positive J2.
208
+
209
+ Notes
210
+ -----
211
+ First-order secular J2 perturbation, without drag or higher harmonics; perigee must clear the reference radius.
212
+ Equation: ``-1.5 * n * j2 * (radius / semi_major_axis) ** 2 * np.cos(inclination) / (1 - eccentricity**2) ** 2``
213
+ with mean motion ``n = np.sqrt(gravitational_parameter / semi_major_axis**3)``.
214
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
215
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
216
+ """
217
+ semi_major_axis = quantity("semi_major_axis", semi_major_axis, lower=0, strict_lower=True)
218
+ eccentricity = quantity("eccentricity", eccentricity, lower=0, upper=1, strict_upper=True)
219
+ inclination = quantity("inclination", inclination, lower=0, upper=np.pi)
220
+ j2 = quantity("j2", j2)
221
+ radius = quantity("radius", radius, lower=0, strict_lower=True)
222
+ gravitational_parameter = quantity("gravitational_parameter", gravitational_parameter, lower=0, strict_lower=True)
223
+ require(semi_major_axis * (1 - eccentricity) > radius, "perigee must lie above the reference radius")
224
+ mean_motion = np.sqrt(gravitational_parameter / semi_major_axis**3)
225
+ return -1.5 * mean_motion * j2 * (radius / semi_major_axis) ** 2 * np.cos(inclination) / (1 - eccentricity**2) ** 2
226
+
227
+
228
+ @model
229
+ def stokes_function(spherical_distance):
230
+ """Stokes's kernel for the geoid.
231
+
232
+ Weighting gravity anomalies over the whole globe by spherical distance gives the geoid height.
233
+
234
+ Parameters
235
+ ----------
236
+ spherical_distance : float or array_like
237
+ rad; constraint: 0 < value <= pi.
238
+
239
+ Returns
240
+ -------
241
+ numpy scalar or ndarray
242
+ Model value [1]. Scalar input returns a NumPy scalar; arrays broadcast.
243
+
244
+ Notes
245
+ -----
246
+ Spherical approximation with no masses outside the geoid; singular as ``2 / psi`` at zero distance. Its Legendre
247
+ expansion is ``sum((2 n + 1) / (n - 1) P_n(cos psi), n >= 2)``, so the geoid is
248
+ ``radius / (4 pi gravity) * integral(anomaly * S dsigma)``.
249
+ Equation: ``1 / s - 6 * s + 1 - 5 * np.cos(psi) - 3 * np.cos(psi) * np.log(s + s**2)`` with ``s = np.sin(psi / 2)``.
250
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
251
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
252
+ """
253
+ psi = quantity("spherical_distance", spherical_distance, lower=0, upper=np.pi, strict_lower=True)
254
+ s = np.sin(psi / 2)
255
+ return 1 / s - 6 * s + 1 - 5 * np.cos(psi) - 3 * np.cos(psi) * np.log(s + s**2)
256
+
257
+
258
+ @model
259
+ def degree_geoid_height(anomaly_coefficient, degree, radius, gravity):
260
+ """Geoid height of one spherical-harmonic degree of the gravity anomaly.
261
+
262
+ Stokes's integral divides each degree of the anomaly by ``n - 1``, so long wavelengths dominate the geoid.
263
+
264
+ Parameters
265
+ ----------
266
+ anomaly_coefficient : float or array_like
267
+ m/s^2; constraint: real. Coefficient of the degree-``n`` surface harmonic of the gravity anomaly.
268
+ degree : float or array_like
269
+ 1; constraint: integer >= 2.
270
+ radius : float or array_like
271
+ m; constraint: positive.
272
+ gravity : float or array_like
273
+ m/s^2; constraint: positive. Mean normal gravity.
274
+
275
+ Returns
276
+ -------
277
+ numpy scalar or ndarray
278
+ Model value [m]: coefficient of the same surface harmonic in the geoid height.
279
+
280
+ Notes
281
+ -----
282
+ Spherical approximation of Stokes's formula; degrees 0 and 1 are excluded by construction.
283
+ Equation: ``radius * anomaly_coefficient / ((degree - 1) * gravity)``.
284
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
285
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
286
+ """
287
+ anomaly_coefficient = quantity("anomaly_coefficient", anomaly_coefficient)
288
+ degree = quantity("degree", degree, lower=2)
289
+ require(degree == np.round(degree), "degree must be an integer")
290
+ radius = quantity("radius", radius, lower=0, strict_lower=True)
291
+ gravity = quantity("gravity", gravity, lower=0, strict_lower=True)
292
+ return radius * anomaly_coefficient / ((degree - 1) * gravity)
@@ -0,0 +1,235 @@
1
+ """Gravity: buried masses, the Bouguer and free-air reductions and the weighing of the Earth.
2
+
3
+ SI units unless stated otherwise.
4
+ """
5
+
6
+ import numpy as np
7
+
8
+ from earthsciencekit._validation import model, quantity, require
9
+ from earthsciencekit.constants import G
10
+
11
+ __all__ = [
12
+ "sphere_gravity",
13
+ "bouguer_correction",
14
+ "free_air_correction",
15
+ "bouguer_anomaly",
16
+ "torsion_balance_gravitational_constant",
17
+ "earth_mean_density",
18
+ ]
19
+
20
+
21
+ @model
22
+ def sphere_gravity(x, depth, radius, density_contrast):
23
+ """Gravitational attraction of a uniform sphere.
24
+
25
+ A compact body with excess density pulls a plumb line sideways and adds to gravity above it.
26
+
27
+ Parameters
28
+ ----------
29
+ x : float or array_like
30
+ m; constraint: real. Horizontal distance of the observer from the point above the centre.
31
+ depth : float or array_like
32
+ m; constraint: real. Depth of the centre below the observer, positive downward.
33
+ radius : float or array_like
34
+ m; constraint: positive.
35
+ density_contrast : float or array_like
36
+ kg/m^3; constraint: real. Density minus that of the surroundings.
37
+
38
+ Returns
39
+ -------
40
+ ndarray, shape (..., 2)
41
+ Model value [m/s^2]: the horizontal component, positive toward increasing ``x``, and the downward component.
42
+
43
+ Notes
44
+ -----
45
+ The observer must lie outside the sphere, where it attracts as a point mass ``M = 4/3 pi radius**3 density_contrast``.
46
+ The horizontal component always points toward the centre for positive contrast.
47
+ Equation: ``G * M * [-x, depth] / (x**2 + depth**2) ** 1.5``.
48
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
49
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
50
+ """
51
+ x = quantity("x", x)
52
+ depth = quantity("depth", depth)
53
+ radius = quantity("radius", radius, lower=0, strict_lower=True)
54
+ density_contrast = quantity("density_contrast", density_contrast)
55
+ distance_squared = x**2 + depth**2
56
+ require(distance_squared >= radius**2, "the observer must lie outside the sphere")
57
+ mass = 4 / 3 * np.pi * radius**3 * density_contrast
58
+ scale = G * mass / distance_squared**1.5
59
+ return np.stack(np.broadcast_arrays(-scale * x, scale * depth), axis=-1)
60
+
61
+
62
+ @model
63
+ def bouguer_correction(density, thickness):
64
+ """Attraction of an infinite horizontal slab.
65
+
66
+ Rock between the station and the reference level adds gravity that does not depend on the slab's extent.
67
+
68
+ Parameters
69
+ ----------
70
+ density : float or array_like
71
+ kg/m^3; constraint: positive.
72
+ thickness : float or array_like
73
+ m; constraint: real. Signed station height above the reference level.
74
+
75
+ Returns
76
+ -------
77
+ numpy scalar or ndarray
78
+ Model value [m/s^2]. Scalar input returns a NumPy scalar; arrays broadcast.
79
+
80
+ Notes
81
+ -----
82
+ Flat, uniform, laterally infinite slab; no terrain or curvature correction. About 0.112 mGal per metre at
83
+ 2670 kg/m^3.
84
+ Equation: ``2 * np.pi * G * density * thickness``.
85
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
86
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
87
+ """
88
+ density = quantity("density", density, lower=0, strict_lower=True)
89
+ thickness = quantity("thickness", thickness)
90
+ return 2 * np.pi * G * density * thickness
91
+
92
+
93
+ @model
94
+ def free_air_correction(height, gravity, radius):
95
+ """Free-air gravity correction.
96
+
97
+ Gravity falls off with distance from the Earth's centre, about 0.3086 mGal per metre at the surface.
98
+
99
+ Parameters
100
+ ----------
101
+ height : float or array_like
102
+ m; constraint: real. Station height above the reference surface.
103
+ gravity : float or array_like
104
+ m/s^2; constraint: positive. Gravity on the reference surface.
105
+ radius : float or array_like
106
+ m; constraint: positive.
107
+
108
+ Returns
109
+ -------
110
+ numpy scalar or ndarray
111
+ Model value [m/s^2]: the amount to add to gravity observed at ``height``.
112
+
113
+ Notes
114
+ -----
115
+ First order in height / radius for a spherical point-mass Earth; no rock between station and reference.
116
+ Equation: ``2 * gravity * height / radius``.
117
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
118
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
119
+ """
120
+ height = quantity("height", height)
121
+ gravity = quantity("gravity", gravity, lower=0, strict_lower=True)
122
+ radius = quantity("radius", radius, lower=0, strict_lower=True)
123
+ return 2 * gravity * height / radius
124
+
125
+
126
+ @model
127
+ def bouguer_anomaly(observed_gravity, normal_gravity, height, density, radius):
128
+ """Simple Bouguer gravity anomaly.
129
+
130
+ Removing latitude, height and the rock slab under a station leaves the attraction of buried density contrasts.
131
+
132
+ Parameters
133
+ ----------
134
+ observed_gravity : float or array_like
135
+ m/s^2; constraint: positive.
136
+ normal_gravity : float or array_like
137
+ m/s^2; constraint: positive. Normal gravity on the reference surface at the station's latitude.
138
+ height : float or array_like
139
+ m; constraint: real. Station height above the reference surface.
140
+ density : float or array_like
141
+ kg/m^3; constraint: positive. Reduction density of the slab.
142
+ radius : float or array_like
143
+ m; constraint: positive.
144
+
145
+ Returns
146
+ -------
147
+ numpy scalar or ndarray
148
+ Model value [m/s^2]. Scalar input returns a NumPy scalar; arrays broadcast.
149
+
150
+ Notes
151
+ -----
152
+ Free-air and infinite-slab reductions only, without terrain correction. The free-air anomaly is the same
153
+ expression without the slab term.
154
+ Equation: ``observed_gravity - normal_gravity + 2 * normal_gravity * height / radius - 2 * np.pi * G * density * height``.
155
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
156
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
157
+ """
158
+ observed_gravity = quantity("observed_gravity", observed_gravity, lower=0, strict_lower=True)
159
+ normal_gravity = quantity("normal_gravity", normal_gravity, lower=0, strict_lower=True)
160
+ height = quantity("height", height)
161
+ density = quantity("density", density, lower=0, strict_lower=True)
162
+ radius = quantity("radius", radius, lower=0, strict_lower=True)
163
+ return observed_gravity - normal_gravity + 2 * normal_gravity * height / radius - 2 * np.pi * G * density * height
164
+
165
+
166
+ @model
167
+ def torsion_balance_gravitational_constant(arm_length, separation, deflection, attracting_mass, period):
168
+ """Gravitational constant from a torsion balance.
169
+
170
+ The twist of a suspended rod between large masses, timed by its free swing, measures G.
171
+
172
+ Parameters
173
+ ----------
174
+ arm_length : float or array_like
175
+ m; constraint: positive. Distance between the two small balls.
176
+ separation : float or array_like
177
+ m; constraint: positive. Centre distance between each small ball and its large ball.
178
+ deflection : float or array_like
179
+ rad; constraint: positive. Equilibrium twist of the rod.
180
+ attracting_mass : float or array_like
181
+ kg; constraint: positive. Mass of each large ball.
182
+ period : float or array_like
183
+ s; constraint: positive. Free oscillation period of the rod.
184
+
185
+ Returns
186
+ -------
187
+ numpy scalar or ndarray
188
+ Model value [m^3/(kg s^2)]. Scalar input returns a NumPy scalar; arrays broadcast.
189
+
190
+ Notes
191
+ -----
192
+ Point masses, a massless rod and attraction by the near large ball only; the small mass cancels.
193
+ Equation: ``2 * np.pi**2 * arm_length * separation**2 * deflection / (attracting_mass * period**2)``.
194
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
195
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
196
+ """
197
+ arm_length = quantity("arm_length", arm_length, lower=0, strict_lower=True)
198
+ separation = quantity("separation", separation, lower=0, strict_lower=True)
199
+ deflection = quantity("deflection", deflection, lower=0, strict_lower=True)
200
+ attracting_mass = quantity("attracting_mass", attracting_mass, lower=0, strict_lower=True)
201
+ period = quantity("period", period, lower=0, strict_lower=True)
202
+ return 2 * np.pi**2 * arm_length * separation**2 * deflection / (attracting_mass * period**2)
203
+
204
+
205
+ @model
206
+ def earth_mean_density(gravity, radius, gravitational_constant=G):
207
+ """Mean density of the Earth.
208
+
209
+ Surface gravity and the gravitational constant together weigh the Earth.
210
+
211
+ Parameters
212
+ ----------
213
+ gravity : float or array_like
214
+ m/s^2; constraint: positive.
215
+ radius : float or array_like
216
+ m; constraint: positive.
217
+ gravitational_constant : float or array_like
218
+ m^3/(kg s^2); constraint: positive. Defaults to :data:`earthsciencekit.constants.G`.
219
+
220
+ Returns
221
+ -------
222
+ numpy scalar or ndarray
223
+ Model value [kg/m^3]. Scalar input returns a NumPy scalar; arrays broadcast.
224
+
225
+ Notes
226
+ -----
227
+ Spherically symmetric, nonrotating Earth, so ``gravity = G M / radius**2``.
228
+ Equation: ``3 * gravity / (4 * np.pi * gravitational_constant * radius)``.
229
+ Invalid, empty, nonreal or nonfinite inputs raise ValueError.
230
+ NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
231
+ """
232
+ gravity = quantity("gravity", gravity, lower=0, strict_lower=True)
233
+ radius = quantity("radius", radius, lower=0, strict_lower=True)
234
+ gravitational_constant = quantity("gravitational_constant", gravitational_constant, lower=0, strict_lower=True)
235
+ return 3 * gravity / (4 * np.pi * gravitational_constant * radius)