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,354 @@
|
|
|
1
|
+
"""Geomagnetism and paleomagnetism: the dipole, Gauss coefficients, field directions, poles and crustal anomalies.
|
|
2
|
+
|
|
3
|
+
SI units unless stated otherwise; latitude, longitude, declination and inclination in radians.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from scipy.special import lpmv
|
|
8
|
+
|
|
9
|
+
from earthsciencekit._validation import model, quantity, require
|
|
10
|
+
from earthsciencekit.constants import MU0
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"dipole_field",
|
|
14
|
+
"dipole_inclination",
|
|
15
|
+
"paleolatitude",
|
|
16
|
+
"zonal_field",
|
|
17
|
+
"gauss_dipole_moment",
|
|
18
|
+
"field_elements",
|
|
19
|
+
"direction_angle",
|
|
20
|
+
"virtual_geomagnetic_pole",
|
|
21
|
+
"block_magnetic_anomaly",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _latitude(name, value):
|
|
26
|
+
return quantity(name, value, lower=-np.pi / 2, upper=np.pi / 2)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@model
|
|
30
|
+
def dipole_field(latitude, distance, moment):
|
|
31
|
+
"""Field of a geocentric axial dipole.
|
|
32
|
+
|
|
33
|
+
A uniformly magnetized Earth gives a field that dips more steeply toward the poles.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
latitude : float or array_like
|
|
38
|
+
rad; constraint: -pi/2 <= latitude <= pi/2. Geocentric latitude.
|
|
39
|
+
distance : float or array_like
|
|
40
|
+
m; constraint: positive. Distance from the Earth's centre.
|
|
41
|
+
moment : float or array_like
|
|
42
|
+
A m^2; constraint: real. Positive for today's polarity, whose field points down in the northern hemisphere.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
ndarray, shape (..., 2)
|
|
47
|
+
Model value [T]: the northward and the downward component.
|
|
48
|
+
|
|
49
|
+
Notes
|
|
50
|
+
-----
|
|
51
|
+
Pure axial dipole, outside its sources. The field at the poles is twice that at the equator.
|
|
52
|
+
Equation: ``MU0 * moment / (4 * np.pi * distance**3) * [np.cos(latitude), 2 * np.sin(latitude)]``.
|
|
53
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
54
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
55
|
+
"""
|
|
56
|
+
latitude = _latitude("latitude", latitude)
|
|
57
|
+
distance = quantity("distance", distance, lower=0, strict_lower=True)
|
|
58
|
+
moment = quantity("moment", moment)
|
|
59
|
+
scale = MU0 * moment / (4 * np.pi * distance**3)
|
|
60
|
+
return np.stack(np.broadcast_arrays(scale * np.cos(latitude), 2 * scale * np.sin(latitude)), axis=-1)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@model
|
|
64
|
+
def dipole_inclination(latitude):
|
|
65
|
+
"""Inclination of an axial dipole field.
|
|
66
|
+
|
|
67
|
+
The dip of a compass needle tells a navigator, or a rock, its magnetic latitude.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
latitude : float or array_like
|
|
72
|
+
rad; constraint: -pi/2 <= latitude <= pi/2.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
numpy scalar or ndarray
|
|
77
|
+
Model value [rad]: positive downward, for today's polarity.
|
|
78
|
+
|
|
79
|
+
Notes
|
|
80
|
+
-----
|
|
81
|
+
Geocentric axial dipole; ``tan(I) = 2 tan(latitude)``.
|
|
82
|
+
Equation: ``np.arctan2(2 * np.sin(latitude), np.cos(latitude))``.
|
|
83
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
84
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
85
|
+
"""
|
|
86
|
+
latitude = _latitude("latitude", latitude)
|
|
87
|
+
return np.arctan2(2 * np.sin(latitude), np.cos(latitude))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@model
|
|
91
|
+
def paleolatitude(inclination):
|
|
92
|
+
"""Paleolatitude from a remanent inclination.
|
|
93
|
+
|
|
94
|
+
A rock's frozen-in dip records how far from the pole it formed.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
inclination : float or array_like
|
|
99
|
+
rad; constraint: -pi/2 <= inclination <= pi/2. Positive downward.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
numpy scalar or ndarray
|
|
104
|
+
Model value [rad]: the magnetic latitude of the site when the rock was magnetized.
|
|
105
|
+
|
|
106
|
+
Notes
|
|
107
|
+
-----
|
|
108
|
+
Assumes a geocentric axial dipole averaged over secular variation; a reversed direction must be inverted first.
|
|
109
|
+
Equation: ``np.arctan2(np.sin(inclination), 2 * np.cos(inclination))``.
|
|
110
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
111
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
112
|
+
"""
|
|
113
|
+
inclination = _latitude("inclination", inclination)
|
|
114
|
+
return np.arctan2(np.sin(inclination), 2 * np.cos(inclination))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@model
|
|
118
|
+
def zonal_field(latitude, distance, degree, coefficient, reference_radius):
|
|
119
|
+
"""Field of one zonal Gauss coefficient.
|
|
120
|
+
|
|
121
|
+
Expanding the potential in spherical harmonics separates the dipole from smaller, faster-decaying parts.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
latitude : float or array_like
|
|
126
|
+
rad; constraint: -pi/2 <= latitude <= pi/2. Geocentric latitude.
|
|
127
|
+
distance : float or array_like
|
|
128
|
+
m; constraint: positive. Distance from the Earth's centre.
|
|
129
|
+
degree : float or array_like
|
|
130
|
+
1; constraint: integer >= 1.
|
|
131
|
+
coefficient : float or array_like
|
|
132
|
+
T; constraint: real. Gauss coefficient ``g_n^0``; for zonal terms Schmidt and unnormalized Legendre functions agree.
|
|
133
|
+
reference_radius : float or array_like
|
|
134
|
+
m; constraint: positive. Usually 6371.2 km.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
ndarray, shape (..., 2)
|
|
139
|
+
Model value [T]: the northward and the downward component.
|
|
140
|
+
|
|
141
|
+
Notes
|
|
142
|
+
-----
|
|
143
|
+
Internal sources only, potential ``V = reference_radius * sum((a / r) ** (n + 1) g_n^0 P_n(sin latitude))``.
|
|
144
|
+
Sum several calls for a truncated zonal model; non-zonal terms (``m > 0``) are not included.
|
|
145
|
+
Equation: ``(a / r) ** (n + 2) * coefficient * [lpmv(1, n, x), -(n + 1) * lpmv(0, n, x)]`` with
|
|
146
|
+
``x = np.sin(latitude)``, ``a = reference_radius``, ``r = distance`` and ``n = degree``.
|
|
147
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
148
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
149
|
+
"""
|
|
150
|
+
latitude = _latitude("latitude", latitude)
|
|
151
|
+
distance = quantity("distance", distance, lower=0, strict_lower=True)
|
|
152
|
+
degree = quantity("degree", degree, lower=1)
|
|
153
|
+
require(degree == np.round(degree), "degree must be an integer")
|
|
154
|
+
coefficient = quantity("coefficient", coefficient)
|
|
155
|
+
reference_radius = quantity("reference_radius", reference_radius, lower=0, strict_lower=True)
|
|
156
|
+
x = np.sin(latitude)
|
|
157
|
+
scale = (reference_radius / distance) ** (degree + 2) * coefficient
|
|
158
|
+
north = scale * lpmv(1, degree, x)
|
|
159
|
+
down = -(degree + 1) * scale * lpmv(0, degree, x)
|
|
160
|
+
return np.stack(np.broadcast_arrays(north, down), axis=-1)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@model
|
|
164
|
+
def gauss_dipole_moment(g10, g11, h11, reference_radius):
|
|
165
|
+
"""Dipole moment from the degree-1 Gauss coefficients.
|
|
166
|
+
|
|
167
|
+
Gauss's analysis weighed the Earth's magnet from surface measurements alone.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
g10, g11, h11 : float or array_like
|
|
172
|
+
T; constraint: real. Schmidt semi-normalized degree-1 coefficients.
|
|
173
|
+
reference_radius : float or array_like
|
|
174
|
+
m; constraint: positive.
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
numpy scalar or ndarray
|
|
179
|
+
Model value [A m^2]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
180
|
+
|
|
181
|
+
Notes
|
|
182
|
+
-----
|
|
183
|
+
Magnitude of the best-fitting geocentric (tilted) dipole; the tilt is ``arccos(abs(g10) / B0)``.
|
|
184
|
+
Equation: ``4 * np.pi * reference_radius**3 / MU0 * np.sqrt(g10**2 + g11**2 + h11**2)``.
|
|
185
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
186
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
187
|
+
"""
|
|
188
|
+
g10 = quantity("g10", g10)
|
|
189
|
+
g11 = quantity("g11", g11)
|
|
190
|
+
h11 = quantity("h11", h11)
|
|
191
|
+
reference_radius = quantity("reference_radius", reference_radius, lower=0, strict_lower=True)
|
|
192
|
+
return 4 * np.pi * reference_radius**3 / MU0 * np.sqrt(g10**2 + g11**2 + h11**2)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@model
|
|
196
|
+
def field_elements(north, east, down):
|
|
197
|
+
"""Declination, inclination and intensity of a field vector.
|
|
198
|
+
|
|
199
|
+
Magnetic directions in rocks and observatories are reported as two angles and a strength.
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
north, east, down : float or array_like
|
|
204
|
+
T; constraint: real, not all zero.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
ndarray, shape (..., 3)
|
|
209
|
+
Model value: declination [rad] east of north in (-pi, pi], inclination [rad] positive down, intensity [T].
|
|
210
|
+
|
|
211
|
+
Notes
|
|
212
|
+
-----
|
|
213
|
+
Local north-east-down frame. A reversed field has declination near pi and inclination of the opposite sign.
|
|
214
|
+
Equation: ``[np.arctan2(east, north), np.arctan2(down, np.hypot(north, east)), np.sqrt(north**2 + east**2 + down**2)]``.
|
|
215
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
216
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
217
|
+
"""
|
|
218
|
+
north = quantity("north", north)
|
|
219
|
+
east = quantity("east", east)
|
|
220
|
+
down = quantity("down", down)
|
|
221
|
+
intensity = np.sqrt(north**2 + east**2 + down**2)
|
|
222
|
+
require(intensity > 0, "the field vector must be nonzero")
|
|
223
|
+
declination = np.arctan2(east, north)
|
|
224
|
+
inclination = np.arctan2(down, np.hypot(north, east))
|
|
225
|
+
return np.stack(np.broadcast_arrays(declination, inclination, intensity), axis=-1)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@model
|
|
229
|
+
def direction_angle(declination_a, inclination_a, declination_b, inclination_b):
|
|
230
|
+
"""Angle between two magnetic directions.
|
|
231
|
+
|
|
232
|
+
A remanence more than 90 degrees from the present field records a reversed polarity.
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
declination_a, declination_b : float or array_like
|
|
237
|
+
rad; constraint: real.
|
|
238
|
+
inclination_a, inclination_b : float or array_like
|
|
239
|
+
rad; constraint: -pi/2 <= inclination <= pi/2.
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
numpy scalar or ndarray
|
|
244
|
+
Model value [rad] in [0, pi]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
245
|
+
|
|
246
|
+
Notes
|
|
247
|
+
-----
|
|
248
|
+
Great-circle distance between unit vectors; intensities are ignored.
|
|
249
|
+
Equation: ``np.arccos(np.sin(Ia) * np.sin(Ib) + np.cos(Ia) * np.cos(Ib) * np.cos(Da - Db))``.
|
|
250
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
251
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
252
|
+
"""
|
|
253
|
+
declination_a = quantity("declination_a", declination_a)
|
|
254
|
+
inclination_a = _latitude("inclination_a", inclination_a)
|
|
255
|
+
declination_b = quantity("declination_b", declination_b)
|
|
256
|
+
inclination_b = _latitude("inclination_b", inclination_b)
|
|
257
|
+
cosine = np.sin(inclination_a) * np.sin(inclination_b) + np.cos(inclination_a) * np.cos(inclination_b) * np.cos(
|
|
258
|
+
declination_a - declination_b
|
|
259
|
+
)
|
|
260
|
+
return np.arccos(np.clip(cosine, -1, 1))
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@model
|
|
264
|
+
def virtual_geomagnetic_pole(site_latitude, site_longitude, declination, inclination):
|
|
265
|
+
"""Virtual geomagnetic pole of a site direction.
|
|
266
|
+
|
|
267
|
+
Poles from rocks of one age on one continent should agree; when they wander, the continent moved.
|
|
268
|
+
|
|
269
|
+
Parameters
|
|
270
|
+
----------
|
|
271
|
+
site_latitude : float or array_like
|
|
272
|
+
rad; constraint: -pi/2 <= latitude <= pi/2.
|
|
273
|
+
site_longitude : float or array_like
|
|
274
|
+
rad; constraint: real.
|
|
275
|
+
declination : float or array_like
|
|
276
|
+
rad; constraint: real.
|
|
277
|
+
inclination : float or array_like
|
|
278
|
+
rad; constraint: -pi/2 <= inclination <= pi/2.
|
|
279
|
+
|
|
280
|
+
Returns
|
|
281
|
+
-------
|
|
282
|
+
ndarray, shape (..., 2)
|
|
283
|
+
Model value [rad]: pole latitude and pole longitude in [0, 2 pi).
|
|
284
|
+
|
|
285
|
+
Notes
|
|
286
|
+
-----
|
|
287
|
+
The pole of the geocentric dipole that gives the observed direction at the site; a reversed direction gives a
|
|
288
|
+
southern pole. Its angular distance from the site is ``p = np.arctan2(2 * np.cos(I), np.sin(I))``, along the
|
|
289
|
+
great circle leaving the site at azimuth ``D``.
|
|
290
|
+
Equation: ``pole = site * np.cos(p) + (north * np.cos(D) + east * np.sin(D)) * np.sin(p)`` with the site's unit
|
|
291
|
+
position vector and local north and east unit vectors; latitude ``np.arctan2(z, np.hypot(x, y))``.
|
|
292
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
293
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
294
|
+
"""
|
|
295
|
+
site_latitude = _latitude("site_latitude", site_latitude)
|
|
296
|
+
site_longitude = quantity("site_longitude", site_longitude)
|
|
297
|
+
declination = quantity("declination", declination)
|
|
298
|
+
inclination = _latitude("inclination", inclination)
|
|
299
|
+
p = np.arctan2(2 * np.cos(inclination), np.sin(inclination))
|
|
300
|
+
sin_lat, cos_lat, sin_lon, cos_lon = np.sin(site_latitude), np.cos(site_latitude), np.sin(site_longitude), np.cos(site_longitude)
|
|
301
|
+
along = np.cos(p)
|
|
302
|
+
toward_north, toward_east = np.sin(p) * np.cos(declination), np.sin(p) * np.sin(declination)
|
|
303
|
+
x = cos_lat * cos_lon * along - sin_lat * cos_lon * toward_north - sin_lon * toward_east
|
|
304
|
+
y = cos_lat * sin_lon * along - sin_lat * sin_lon * toward_north + cos_lon * toward_east
|
|
305
|
+
z = sin_lat * along + cos_lat * toward_north
|
|
306
|
+
pole_latitude = np.arctan2(z, np.hypot(x, y))
|
|
307
|
+
pole_longitude = np.mod(np.arctan2(y, x), 2 * np.pi)
|
|
308
|
+
return np.stack(np.broadcast_arrays(pole_latitude, pole_longitude), axis=-1)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
@model
|
|
312
|
+
def block_magnetic_anomaly(x, left, right, top, bottom, magnetization):
|
|
313
|
+
"""Magnetic anomaly of a vertically magnetized two-dimensional block.
|
|
314
|
+
|
|
315
|
+
Strips of normally and reversely magnetized crust give the alternating anomalies over mid-ocean ridges.
|
|
316
|
+
|
|
317
|
+
Parameters
|
|
318
|
+
----------
|
|
319
|
+
x : float or array_like
|
|
320
|
+
m; constraint: real. Horizontal position of the observer, perpendicular to the strike.
|
|
321
|
+
left, right : float or array_like
|
|
322
|
+
m; constraint: left < right. Edges of the block.
|
|
323
|
+
top, bottom : float or array_like
|
|
324
|
+
m; constraint: 0 < top < bottom. Depths below the observation level.
|
|
325
|
+
magnetization : float or array_like
|
|
326
|
+
A/m; constraint: real. Positive downward (normal polarity at the north magnetic pole).
|
|
327
|
+
|
|
328
|
+
Returns
|
|
329
|
+
-------
|
|
330
|
+
numpy scalar or ndarray
|
|
331
|
+
Model value [T]: vertical field anomaly, equal to the total-field anomaly reduced to the pole.
|
|
332
|
+
|
|
333
|
+
Notes
|
|
334
|
+
-----
|
|
335
|
+
Infinite strike length, uniform vertical magnetization and a vertical ambient field. A laterally infinite layer has
|
|
336
|
+
no external field.
|
|
337
|
+
Equation: ``MU0 * magnetization / (2 * np.pi) * (phi(top) - phi(bottom))`` with
|
|
338
|
+
``phi(z) = np.arctan((right - x) / z) - np.arctan((left - x) / z)``.
|
|
339
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
340
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
341
|
+
"""
|
|
342
|
+
x = quantity("x", x)
|
|
343
|
+
left = quantity("left", left)
|
|
344
|
+
right = quantity("right", right)
|
|
345
|
+
top = quantity("top", top, lower=0, strict_lower=True)
|
|
346
|
+
bottom = quantity("bottom", bottom, lower=0, strict_lower=True)
|
|
347
|
+
magnetization = quantity("magnetization", magnetization)
|
|
348
|
+
require(left < right, "left must lie west of right")
|
|
349
|
+
require(top < bottom, "top must be shallower than bottom")
|
|
350
|
+
|
|
351
|
+
def subtended(depth):
|
|
352
|
+
return np.arctan((right - x) / depth) - np.arctan((left - x) / depth)
|
|
353
|
+
|
|
354
|
+
return MU0 * magnetization / (2 * np.pi) * (subtended(top) - subtended(bottom))
|
|
File without changes
|