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,190 @@
|
|
|
1
|
+
"""Magma: viscosity, water solubility, ascent in dikes and ground deformation above a magma chamber.
|
|
2
|
+
|
|
3
|
+
SI units unless stated otherwise.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from earthsciencekit._validation import model, quantity
|
|
9
|
+
from earthsciencekit.constants import R_GAS
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"arrhenius_viscosity",
|
|
13
|
+
"shaw_viscosity",
|
|
14
|
+
"water_solubility",
|
|
15
|
+
"dike_ascent_velocity",
|
|
16
|
+
"mogi_displacement",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@model
|
|
21
|
+
def arrhenius_viscosity(temperature, reference_viscosity, activation_energy, reference_temperature):
|
|
22
|
+
"""Arrhenius temperature dependence of melt viscosity.
|
|
23
|
+
|
|
24
|
+
Silicate melts stiffen by orders of magnitude as they cool, which controls how lava flows and how magma erupts.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
temperature : float or array_like
|
|
29
|
+
K; constraint: positive.
|
|
30
|
+
reference_viscosity : float or array_like
|
|
31
|
+
Pa s, viscosity at the reference temperature; constraint: positive.
|
|
32
|
+
activation_energy : float or array_like
|
|
33
|
+
J/mol; constraint: nonnegative.
|
|
34
|
+
reference_temperature : float or array_like
|
|
35
|
+
K; constraint: positive.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
numpy scalar or ndarray
|
|
40
|
+
Model value [Pa s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
41
|
+
|
|
42
|
+
Notes
|
|
43
|
+
-----
|
|
44
|
+
Constant activation energy over the range of interest; crystal- and bubble-free Newtonian melt.
|
|
45
|
+
Equation: ``reference_viscosity * np.exp(activation_energy / R_GAS * (1 / temperature - 1 / reference_temperature))``.
|
|
46
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
47
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
48
|
+
"""
|
|
49
|
+
temperature = quantity("temperature", temperature, lower=0, strict_lower=True)
|
|
50
|
+
reference_viscosity = quantity("reference_viscosity", reference_viscosity, lower=0, strict_lower=True)
|
|
51
|
+
activation_energy = quantity("activation_energy", activation_energy, lower=0)
|
|
52
|
+
reference_temperature = quantity("reference_temperature", reference_temperature, lower=0, strict_lower=True)
|
|
53
|
+
return reference_viscosity * np.exp(activation_energy / R_GAS * (1 / temperature - 1 / reference_temperature))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@model
|
|
57
|
+
def shaw_viscosity(temperature, slope):
|
|
58
|
+
"""Shaw's empirical viscosity of an anhydrous or hydrous silicate melt.
|
|
59
|
+
|
|
60
|
+
One composition-dependent slope predicts the viscosity of melts from basalt to rhyolite.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
temperature : float or array_like
|
|
65
|
+
K; constraint: positive.
|
|
66
|
+
slope : float or array_like
|
|
67
|
+
1, Shaw's s from the molar composition (about 2-3 for basalt, 3.5-4 for rhyolite); constraint: positive.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
numpy scalar or ndarray
|
|
72
|
+
Model value [Pa s]. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
73
|
+
|
|
74
|
+
Notes
|
|
75
|
+
-----
|
|
76
|
+
Shaw (1972), Am. J. Sci. 272: 870-893; all lines pass near ln(eta [poise]) = -6.4 at 1e4/T = 1.5. Crystal- and
|
|
77
|
+
bubble-free Newtonian melt; about 0.2 log units of accuracy.
|
|
78
|
+
Equation: ``0.1 * np.exp(slope * (1e4 / temperature) - 1.5 * slope - 6.4)``.
|
|
79
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
80
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
81
|
+
"""
|
|
82
|
+
temperature = quantity("temperature", temperature, lower=0, strict_lower=True)
|
|
83
|
+
slope = quantity("slope", slope, lower=0, strict_lower=True)
|
|
84
|
+
return 0.1 * np.exp(slope * (1e4 / temperature) - 1.5 * slope - 6.4)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@model
|
|
88
|
+
def water_solubility(pressure, solubility_constant):
|
|
89
|
+
"""Dissolved water in a melt saturated at a given pressure.
|
|
90
|
+
|
|
91
|
+
Water dissolves as hydroxyl, so its solubility grows as the square root of pressure and ascending magma exsolves
|
|
92
|
+
bubbles.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
pressure : float or array_like
|
|
97
|
+
Pa; constraint: nonnegative.
|
|
98
|
+
solubility_constant : float or array_like
|
|
99
|
+
1/sqrt(Pa), mass fraction per root pascal (about 4.1e-6 for rhyolite); constraint: positive.
|
|
100
|
+
|
|
101
|
+
Returns
|
|
102
|
+
-------
|
|
103
|
+
numpy scalar or ndarray
|
|
104
|
+
Model value [1], mass fraction. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
105
|
+
|
|
106
|
+
Notes
|
|
107
|
+
-----
|
|
108
|
+
Square-root (Henry-type) law for pure water vapour, valid up to a few hundred MPa; ignores CO2 and temperature.
|
|
109
|
+
Equation: ``solubility_constant * np.sqrt(pressure)``.
|
|
110
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
111
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
112
|
+
"""
|
|
113
|
+
pressure = quantity("pressure", pressure, lower=0)
|
|
114
|
+
solubility_constant = quantity("solubility_constant", solubility_constant, lower=0, strict_lower=True)
|
|
115
|
+
return solubility_constant * np.sqrt(pressure)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@model
|
|
119
|
+
def dike_ascent_velocity(width, density_contrast, viscosity, gravity):
|
|
120
|
+
"""Mean laminar velocity of buoyant magma in a dike.
|
|
121
|
+
|
|
122
|
+
Buoyancy drives magma up a planar crack, and the viscous drag on its walls sets how fast it rises.
|
|
123
|
+
|
|
124
|
+
Parameters
|
|
125
|
+
----------
|
|
126
|
+
width : float or array_like
|
|
127
|
+
m, full dike width; constraint: positive.
|
|
128
|
+
density_contrast : float or array_like
|
|
129
|
+
kg/m^3, host rock minus magma; constraint: real.
|
|
130
|
+
viscosity : float or array_like
|
|
131
|
+
Pa s; constraint: positive.
|
|
132
|
+
gravity : float or array_like
|
|
133
|
+
m/s^2; constraint: positive.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
numpy scalar or ndarray
|
|
138
|
+
Model value [m/s], positive upward. Scalar input returns a NumPy scalar; arrays broadcast.
|
|
139
|
+
|
|
140
|
+
Notes
|
|
141
|
+
-----
|
|
142
|
+
Wilson and Head (1981): plane Poiseuille flow between parallel walls driven by buoyancy alone; check that the Reynolds
|
|
143
|
+
number ``magma_density * velocity * width / viscosity`` stays below about 2000.
|
|
144
|
+
Equation: ``width**2 * density_contrast * gravity / (12 * viscosity)``.
|
|
145
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
146
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
147
|
+
"""
|
|
148
|
+
width = quantity("width", width, lower=0, strict_lower=True)
|
|
149
|
+
density_contrast = quantity("density_contrast", density_contrast)
|
|
150
|
+
viscosity = quantity("viscosity", viscosity, lower=0, strict_lower=True)
|
|
151
|
+
gravity = quantity("gravity", gravity, lower=0, strict_lower=True)
|
|
152
|
+
return width**2 * density_contrast * gravity / (12 * viscosity)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@model
|
|
156
|
+
def mogi_displacement(radial_distance, depth, volume_change, poisson_ratio):
|
|
157
|
+
"""Surface displacement above an inflating point source.
|
|
158
|
+
|
|
159
|
+
Magma entering a chamber lifts the ground in a bell-shaped dome whose width reveals the chamber depth.
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
radial_distance : float or array_like
|
|
164
|
+
m, horizontal distance from the point above the source; constraint: nonnegative.
|
|
165
|
+
depth : float or array_like
|
|
166
|
+
m; constraint: positive.
|
|
167
|
+
volume_change : float or array_like
|
|
168
|
+
m^3, signed change of the source volume; constraint: real.
|
|
169
|
+
poisson_ratio : float or array_like
|
|
170
|
+
1; constraint: between 0 and 0.5.
|
|
171
|
+
|
|
172
|
+
Returns
|
|
173
|
+
-------
|
|
174
|
+
ndarray, shape (..., 2)
|
|
175
|
+
Model value [m]: ``[radial, vertical]`` displacement, outward and upward positive.
|
|
176
|
+
|
|
177
|
+
Notes
|
|
178
|
+
-----
|
|
179
|
+
Mogi (1958) point source in an elastic half-space; the chamber radius must be small compared with its depth. The
|
|
180
|
+
uplift halves at ``radial_distance = 0.766 * depth``.
|
|
181
|
+
Equation: ``(1 - poisson_ratio) * volume_change / np.pi * [radial_distance, depth] / (radial_distance**2 + depth**2)**1.5``.
|
|
182
|
+
Invalid, empty, nonreal or nonfinite inputs raise ValueError.
|
|
183
|
+
NumPy broadcasting applies; singular and overflowing outputs raise ValueError.
|
|
184
|
+
"""
|
|
185
|
+
radial_distance = quantity("radial_distance", radial_distance, lower=0)
|
|
186
|
+
depth = quantity("depth", depth, lower=0, strict_lower=True)
|
|
187
|
+
volume_change = quantity("volume_change", volume_change)
|
|
188
|
+
poisson_ratio = quantity("poisson_ratio", poisson_ratio, lower=0, upper=0.5)
|
|
189
|
+
scale = (1 - poisson_ratio) * volume_change / np.pi / (radial_distance**2 + depth**2) ** 1.5
|
|
190
|
+
return np.stack(np.broadcast_arrays(scale * radial_distance, scale * depth), axis=-1)
|
|
File without changes
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
"""Golden checks and invalid-input contracts for every public model."""
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from earthsciencekit import volcanology as domain
|
|
7
|
+
from earthsciencekit.constants import AVOGADRO, LAMBDA_U238, R_GAS
|
|
8
|
+
|
|
9
|
+
HALF_LIFE_U238 = np.log(2) / LAMBDA_U238
|
|
10
|
+
BAD = [np.nan, np.inf, -np.inf, [], "bad", True, 1j]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_decay_constant_reference():
|
|
14
|
+
result = domain.decay_constant(*[2])
|
|
15
|
+
np.testing.assert_allclose(result, np.log(2) / 2, rtol=1e-11, atol=1e-12)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
19
|
+
def test_decay_constant_rejects_invalid(bad):
|
|
20
|
+
args = [2]
|
|
21
|
+
args[0] = bad
|
|
22
|
+
with pytest.raises(ValueError):
|
|
23
|
+
domain.decay_constant(*args)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.mark.parametrize("index,value", [(0, 0)])
|
|
27
|
+
def test_decay_constant_physical_bound(index, value):
|
|
28
|
+
args = [2]
|
|
29
|
+
args[index] = value
|
|
30
|
+
with pytest.raises(ValueError):
|
|
31
|
+
domain.decay_constant(*args)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_parent_fraction_reference():
|
|
35
|
+
result = domain.parent_fraction(*[np.log(2), 1])
|
|
36
|
+
np.testing.assert_allclose(result, 0.5, rtol=1e-11, atol=1e-12)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
40
|
+
def test_parent_fraction_rejects_invalid(bad):
|
|
41
|
+
args = [np.log(2), 1]
|
|
42
|
+
args[0] = bad
|
|
43
|
+
with pytest.raises(ValueError):
|
|
44
|
+
domain.parent_fraction(*args)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.mark.parametrize("index,value", [(0, 0), (1, -1)])
|
|
48
|
+
def test_parent_fraction_physical_bound(index, value):
|
|
49
|
+
args = [np.log(2), 1]
|
|
50
|
+
args[index] = value
|
|
51
|
+
with pytest.raises(ValueError):
|
|
52
|
+
domain.parent_fraction(*args)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_radiometric_age_reference():
|
|
56
|
+
result = domain.radiometric_age(*[np.e - 1, 2])
|
|
57
|
+
np.testing.assert_allclose(result, 0.5, rtol=1e-11, atol=1e-12)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
61
|
+
def test_radiometric_age_rejects_invalid(bad):
|
|
62
|
+
args = [np.e - 1, 2]
|
|
63
|
+
args[0] = bad
|
|
64
|
+
with pytest.raises(ValueError):
|
|
65
|
+
domain.radiometric_age(*args)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.mark.parametrize("index,value", [(0, -1), (1, 0)])
|
|
69
|
+
def test_radiometric_age_physical_bound(index, value):
|
|
70
|
+
args = [np.e - 1, 2]
|
|
71
|
+
args[index] = value
|
|
72
|
+
with pytest.raises(ValueError):
|
|
73
|
+
domain.radiometric_age(*args)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_radiogenic_daughter_reference():
|
|
77
|
+
result = domain.radiogenic_daughter(*[3, np.log(2), 1])
|
|
78
|
+
np.testing.assert_allclose(result, 3, rtol=1e-11, atol=1e-12)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
82
|
+
def test_radiogenic_daughter_rejects_invalid(bad):
|
|
83
|
+
args = [3, np.log(2), 1]
|
|
84
|
+
args[0] = bad
|
|
85
|
+
with pytest.raises(ValueError):
|
|
86
|
+
domain.radiogenic_daughter(*args)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@pytest.mark.parametrize("index,value", [(0, -1), (1, 0), (2, -1)])
|
|
90
|
+
def test_radiogenic_daughter_physical_bound(index, value):
|
|
91
|
+
args = [3, np.log(2), 1]
|
|
92
|
+
args[index] = value
|
|
93
|
+
with pytest.raises(ValueError):
|
|
94
|
+
domain.radiogenic_daughter(*args)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_radiocarbon_age_reference():
|
|
98
|
+
result = domain.radiocarbon_age(*[np.exp(-1)])
|
|
99
|
+
np.testing.assert_allclose(result, 8033 * 365.25 * 86400, rtol=1e-11, atol=1e-12)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
103
|
+
def test_radiocarbon_age_rejects_invalid(bad):
|
|
104
|
+
args = [np.exp(-1)]
|
|
105
|
+
args[0] = bad
|
|
106
|
+
with pytest.raises(ValueError):
|
|
107
|
+
domain.radiocarbon_age(*args)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@pytest.mark.parametrize("index,value", [(0, 0)])
|
|
111
|
+
def test_radiocarbon_age_physical_bound(index, value):
|
|
112
|
+
args = [np.exp(-1)]
|
|
113
|
+
args[index] = value
|
|
114
|
+
with pytest.raises(ValueError):
|
|
115
|
+
domain.radiocarbon_age(*args)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def test_radiogenic_lead_ratio_reference():
|
|
119
|
+
result = domain.radiogenic_lead_ratio(*[HALF_LIFE_U238])
|
|
120
|
+
np.testing.assert_allclose(result, (2 ** (9.8485 / 1.55125) - 1) / 137.88, rtol=1e-11, atol=1e-12)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
124
|
+
def test_radiogenic_lead_ratio_rejects_invalid(bad):
|
|
125
|
+
args = [HALF_LIFE_U238]
|
|
126
|
+
args[0] = bad
|
|
127
|
+
with pytest.raises(ValueError):
|
|
128
|
+
domain.radiogenic_lead_ratio(*args)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@pytest.mark.parametrize("index,value", [(0, 0)])
|
|
132
|
+
def test_radiogenic_lead_ratio_physical_bound(index, value):
|
|
133
|
+
args = [HALF_LIFE_U238]
|
|
134
|
+
args[index] = value
|
|
135
|
+
with pytest.raises(ValueError):
|
|
136
|
+
domain.radiogenic_lead_ratio(*args)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_lead_lead_age_reference():
|
|
140
|
+
result = domain.lead_lead_age(*[(2 ** (9.8485 / 1.55125) - 1) / 137.88])
|
|
141
|
+
np.testing.assert_allclose(result, HALF_LIFE_U238, rtol=1e-11, atol=1e-12)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
145
|
+
def test_lead_lead_age_rejects_invalid(bad):
|
|
146
|
+
args = [(2 ** (9.8485 / 1.55125) - 1) / 137.88]
|
|
147
|
+
args[0] = bad
|
|
148
|
+
with pytest.raises(ValueError):
|
|
149
|
+
domain.lead_lead_age(*args)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@pytest.mark.parametrize("index,value", [(0, 0.04), (0, 50.0)])
|
|
153
|
+
def test_lead_lead_age_physical_bound(index, value):
|
|
154
|
+
args = [(2 ** (9.8485 / 1.55125) - 1) / 137.88]
|
|
155
|
+
args[index] = value
|
|
156
|
+
with pytest.raises(ValueError):
|
|
157
|
+
domain.lead_lead_age(*args)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_concordia_ratios_reference():
|
|
161
|
+
result = domain.concordia_ratios(*[HALF_LIFE_U238])
|
|
162
|
+
np.testing.assert_allclose(result, [2 ** (9.8485 / 1.55125) - 1, 1], rtol=1e-11, atol=1e-12)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
166
|
+
def test_concordia_ratios_rejects_invalid(bad):
|
|
167
|
+
args = [HALF_LIFE_U238]
|
|
168
|
+
args[0] = bad
|
|
169
|
+
with pytest.raises(ValueError):
|
|
170
|
+
domain.concordia_ratios(*args)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@pytest.mark.parametrize("index,value", [(0, -1)])
|
|
174
|
+
def test_concordia_ratios_physical_bound(index, value):
|
|
175
|
+
args = [HALF_LIFE_U238]
|
|
176
|
+
args[index] = value
|
|
177
|
+
with pytest.raises(ValueError):
|
|
178
|
+
domain.concordia_ratios(*args)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_isochron_fit_reference():
|
|
182
|
+
result = domain.isochron_fit(*[[0, 1, 2], [0.7, 0.7 + np.e - 1, 0.7 + 2 * (np.e - 1)], 1])
|
|
183
|
+
np.testing.assert_allclose(result, [1, 0.7], rtol=1e-11, atol=1e-12)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
187
|
+
def test_isochron_fit_rejects_invalid(bad):
|
|
188
|
+
args = [[0, 1, 2], [0.7, 0.7 + np.e - 1, 0.7 + 2 * (np.e - 1)], 1]
|
|
189
|
+
args[0] = bad
|
|
190
|
+
with pytest.raises(ValueError):
|
|
191
|
+
domain.isochron_fit(*args)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@pytest.mark.parametrize("index,value", [(0, [-1, 1, 2]), (1, [-1, 1, 2]), (2, 0)])
|
|
195
|
+
def test_isochron_fit_physical_bound(index, value):
|
|
196
|
+
args = [[0, 1, 2], [0.7, 0.7 + np.e - 1, 0.7 + 2 * (np.e - 1)], 1]
|
|
197
|
+
args[index] = value
|
|
198
|
+
with pytest.raises(ValueError):
|
|
199
|
+
domain.isochron_fit(*args)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def test_rayleigh_fractionation_reference():
|
|
203
|
+
result = domain.rayleigh_fractionation(*[2, 0.25, 0.5])
|
|
204
|
+
np.testing.assert_allclose(result, 4, rtol=1e-11, atol=1e-12)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
208
|
+
def test_rayleigh_fractionation_rejects_invalid(bad):
|
|
209
|
+
args = [2, 0.25, 0.5]
|
|
210
|
+
args[0] = bad
|
|
211
|
+
with pytest.raises(ValueError):
|
|
212
|
+
domain.rayleigh_fractionation(*args)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@pytest.mark.parametrize("index,value", [(0, 0), (1, 0), (1, 1.5), (2, 0)])
|
|
216
|
+
def test_rayleigh_fractionation_physical_bound(index, value):
|
|
217
|
+
args = [2, 0.25, 0.5]
|
|
218
|
+
args[index] = value
|
|
219
|
+
with pytest.raises(ValueError):
|
|
220
|
+
domain.rayleigh_fractionation(*args)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_delta_value_reference():
|
|
224
|
+
result = domain.delta_value(*[1.01, 1])
|
|
225
|
+
np.testing.assert_allclose(result, 10, rtol=1e-11, atol=1e-12)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
229
|
+
def test_delta_value_rejects_invalid(bad):
|
|
230
|
+
args = [1.01, 1]
|
|
231
|
+
args[0] = bad
|
|
232
|
+
with pytest.raises(ValueError):
|
|
233
|
+
domain.delta_value(*args)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@pytest.mark.parametrize("index,value", [(0, 0), (1, 0)])
|
|
237
|
+
def test_delta_value_physical_bound(index, value):
|
|
238
|
+
args = [1.01, 1]
|
|
239
|
+
args[index] = value
|
|
240
|
+
with pytest.raises(ValueError):
|
|
241
|
+
domain.delta_value(*args)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def test_fractional_crystallization_melt_reference():
|
|
245
|
+
result = domain.fractional_crystallization_melt(*[10, 0.25, 0.5])
|
|
246
|
+
np.testing.assert_allclose(result, 20, rtol=1e-11, atol=1e-12)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
250
|
+
def test_fractional_crystallization_melt_rejects_invalid(bad):
|
|
251
|
+
args = [10, 0.25, 0.5]
|
|
252
|
+
args[0] = bad
|
|
253
|
+
with pytest.raises(ValueError):
|
|
254
|
+
domain.fractional_crystallization_melt(*args)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@pytest.mark.parametrize("index,value", [(0, -1), (1, 0), (1, 1.5), (2, -1)])
|
|
258
|
+
def test_fractional_crystallization_melt_physical_bound(index, value):
|
|
259
|
+
args = [10, 0.25, 0.5]
|
|
260
|
+
args[index] = value
|
|
261
|
+
with pytest.raises(ValueError):
|
|
262
|
+
domain.fractional_crystallization_melt(*args)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def test_equilibrium_crystallization_melt_reference():
|
|
266
|
+
result = domain.equilibrium_crystallization_melt(*[10, 0.5, 0.5])
|
|
267
|
+
np.testing.assert_allclose(result, 40 / 3, rtol=1e-11, atol=1e-12)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
271
|
+
def test_equilibrium_crystallization_melt_rejects_invalid(bad):
|
|
272
|
+
args = [10, 0.5, 0.5]
|
|
273
|
+
args[0] = bad
|
|
274
|
+
with pytest.raises(ValueError):
|
|
275
|
+
domain.equilibrium_crystallization_melt(*args)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@pytest.mark.parametrize("index,value", [(0, -1), (1, -0.5), (1, 1.5), (2, 0)])
|
|
279
|
+
def test_equilibrium_crystallization_melt_physical_bound(index, value):
|
|
280
|
+
args = [10, 0.5, 0.5]
|
|
281
|
+
args[index] = value
|
|
282
|
+
with pytest.raises(ValueError):
|
|
283
|
+
domain.equilibrium_crystallization_melt(*args)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def test_lattice_strain_partition_reference():
|
|
287
|
+
result = domain.lattice_strain_partition(*[2e-10, 1e-10, 3, 1.2 * R_GAS * 1000 / (4 * np.pi * AVOGADRO * 1e-30), 1000])
|
|
288
|
+
np.testing.assert_allclose(result, 3 * np.exp(-1), rtol=1e-11, atol=1e-12)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
292
|
+
def test_lattice_strain_partition_rejects_invalid(bad):
|
|
293
|
+
args = [2e-10, 1e-10, 3, 1.2 * R_GAS * 1000 / (4 * np.pi * AVOGADRO * 1e-30), 1000]
|
|
294
|
+
args[0] = bad
|
|
295
|
+
with pytest.raises(ValueError):
|
|
296
|
+
domain.lattice_strain_partition(*args)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@pytest.mark.parametrize("index,value", [(0, 0), (1, 0), (2, 0), (3, 0), (4, 0)])
|
|
300
|
+
def test_lattice_strain_partition_physical_bound(index, value):
|
|
301
|
+
args = [2e-10, 1e-10, 3, 1.2 * R_GAS * 1000 / (4 * np.pi * AVOGADRO * 1e-30), 1000]
|
|
302
|
+
args[index] = value
|
|
303
|
+
with pytest.raises(ValueError):
|
|
304
|
+
domain.lattice_strain_partition(*args)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_isotope_temperature_reference():
|
|
308
|
+
result = domain.isotope_temperature(*[1, 0])
|
|
309
|
+
np.testing.assert_allclose(result, 273.15 + 16.5 - 4.3 + 0.14, rtol=1e-11, atol=1e-12)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
313
|
+
def test_isotope_temperature_rejects_invalid(bad):
|
|
314
|
+
args = [1, 0]
|
|
315
|
+
args[0] = bad
|
|
316
|
+
with pytest.raises(ValueError):
|
|
317
|
+
domain.isotope_temperature(*args)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def test_arrhenius_viscosity_reference():
|
|
321
|
+
result = domain.arrhenius_viscosity(*[1000, 10, R_GAS * 1000, 500])
|
|
322
|
+
np.testing.assert_allclose(result, 10 * np.exp(-1), rtol=1e-11, atol=1e-12)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
326
|
+
def test_arrhenius_viscosity_rejects_invalid(bad):
|
|
327
|
+
args = [1000, 10, R_GAS * 1000, 500]
|
|
328
|
+
args[0] = bad
|
|
329
|
+
with pytest.raises(ValueError):
|
|
330
|
+
domain.arrhenius_viscosity(*args)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
@pytest.mark.parametrize("index,value", [(0, 0), (1, 0), (2, -1), (3, 0)])
|
|
334
|
+
def test_arrhenius_viscosity_physical_bound(index, value):
|
|
335
|
+
args = [1000, 10, R_GAS * 1000, 500]
|
|
336
|
+
args[index] = value
|
|
337
|
+
with pytest.raises(ValueError):
|
|
338
|
+
domain.arrhenius_viscosity(*args)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def test_shaw_viscosity_reference():
|
|
342
|
+
result = domain.shaw_viscosity(*[1e4 / 1.5, 2])
|
|
343
|
+
np.testing.assert_allclose(result, 0.1 * np.exp(-6.4), rtol=1e-11, atol=1e-12)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
347
|
+
def test_shaw_viscosity_rejects_invalid(bad):
|
|
348
|
+
args = [1e4 / 1.5, 2]
|
|
349
|
+
args[0] = bad
|
|
350
|
+
with pytest.raises(ValueError):
|
|
351
|
+
domain.shaw_viscosity(*args)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
@pytest.mark.parametrize("index,value", [(0, 0), (1, 0)])
|
|
355
|
+
def test_shaw_viscosity_physical_bound(index, value):
|
|
356
|
+
args = [1e4 / 1.5, 2]
|
|
357
|
+
args[index] = value
|
|
358
|
+
with pytest.raises(ValueError):
|
|
359
|
+
domain.shaw_viscosity(*args)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def test_water_solubility_reference():
|
|
363
|
+
result = domain.water_solubility(*[4e8, 4e-6])
|
|
364
|
+
np.testing.assert_allclose(result, 0.08, rtol=1e-11, atol=1e-12)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
368
|
+
def test_water_solubility_rejects_invalid(bad):
|
|
369
|
+
args = [4e8, 4e-6]
|
|
370
|
+
args[0] = bad
|
|
371
|
+
with pytest.raises(ValueError):
|
|
372
|
+
domain.water_solubility(*args)
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
@pytest.mark.parametrize("index,value", [(0, -1), (1, 0)])
|
|
376
|
+
def test_water_solubility_physical_bound(index, value):
|
|
377
|
+
args = [4e8, 4e-6]
|
|
378
|
+
args[index] = value
|
|
379
|
+
with pytest.raises(ValueError):
|
|
380
|
+
domain.water_solubility(*args)
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def test_dike_ascent_velocity_reference():
|
|
384
|
+
result = domain.dike_ascent_velocity(*[2, 300, 100, 10])
|
|
385
|
+
np.testing.assert_allclose(result, 10, rtol=1e-11, atol=1e-12)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
389
|
+
def test_dike_ascent_velocity_rejects_invalid(bad):
|
|
390
|
+
args = [2, 300, 100, 10]
|
|
391
|
+
args[0] = bad
|
|
392
|
+
with pytest.raises(ValueError):
|
|
393
|
+
domain.dike_ascent_velocity(*args)
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
@pytest.mark.parametrize("index,value", [(0, 0), (2, 0), (3, 0)])
|
|
397
|
+
def test_dike_ascent_velocity_physical_bound(index, value):
|
|
398
|
+
args = [2, 300, 100, 10]
|
|
399
|
+
args[index] = value
|
|
400
|
+
with pytest.raises(ValueError):
|
|
401
|
+
domain.dike_ascent_velocity(*args)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def test_mogi_displacement_reference():
|
|
405
|
+
result = domain.mogi_displacement(*[0, 1000, np.pi, 0.5])
|
|
406
|
+
np.testing.assert_allclose(result, [0, 5e-7], rtol=1e-11, atol=1e-12)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
410
|
+
def test_mogi_displacement_rejects_invalid(bad):
|
|
411
|
+
args = [0, 1000, np.pi, 0.5]
|
|
412
|
+
args[0] = bad
|
|
413
|
+
with pytest.raises(ValueError):
|
|
414
|
+
domain.mogi_displacement(*args)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@pytest.mark.parametrize("index,value", [(0, -1), (1, 0), (3, -0.1), (3, 0.6)])
|
|
418
|
+
def test_mogi_displacement_physical_bound(index, value):
|
|
419
|
+
args = [0, 1000, np.pi, 0.5]
|
|
420
|
+
args[index] = value
|
|
421
|
+
with pytest.raises(ValueError):
|
|
422
|
+
domain.mogi_displacement(*args)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def test_plume_rise_height_reference():
|
|
426
|
+
result = domain.plume_rise_height(*[16, 1 / 16])
|
|
427
|
+
np.testing.assert_allclose(result, 80, rtol=1e-11, atol=1e-12)
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
431
|
+
def test_plume_rise_height_rejects_invalid(bad):
|
|
432
|
+
args = [16, 1 / 16]
|
|
433
|
+
args[0] = bad
|
|
434
|
+
with pytest.raises(ValueError):
|
|
435
|
+
domain.plume_rise_height(*args)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
@pytest.mark.parametrize("index,value", [(0, 0), (1, 0)])
|
|
439
|
+
def test_plume_rise_height_physical_bound(index, value):
|
|
440
|
+
args = [16, 1 / 16]
|
|
441
|
+
args[index] = value
|
|
442
|
+
with pytest.raises(ValueError):
|
|
443
|
+
domain.plume_rise_height(*args)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def test_column_height_reference():
|
|
447
|
+
result = domain.column_height(*[1e12])
|
|
448
|
+
np.testing.assert_allclose(result, 8200, rtol=1e-11, atol=1e-12)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
452
|
+
def test_column_height_rejects_invalid(bad):
|
|
453
|
+
args = [1e12]
|
|
454
|
+
args[0] = bad
|
|
455
|
+
with pytest.raises(ValueError):
|
|
456
|
+
domain.column_height(*args)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
@pytest.mark.parametrize("index,value", [(0, 0)])
|
|
460
|
+
def test_column_height_physical_bound(index, value):
|
|
461
|
+
args = [1e12]
|
|
462
|
+
args[index] = value
|
|
463
|
+
with pytest.raises(ValueError):
|
|
464
|
+
domain.column_height(*args)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def test_volcanic_explosivity_index_reference():
|
|
468
|
+
result = domain.volcanic_explosivity_index(*[5e7])
|
|
469
|
+
np.testing.assert_allclose(result, 3, rtol=1e-11, atol=1e-12)
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
@pytest.mark.parametrize("bad", BAD)
|
|
473
|
+
def test_volcanic_explosivity_index_rejects_invalid(bad):
|
|
474
|
+
args = [5e7]
|
|
475
|
+
args[0] = bad
|
|
476
|
+
with pytest.raises(ValueError):
|
|
477
|
+
domain.volcanic_explosivity_index(*args)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
@pytest.mark.parametrize("index,value", [(0, -1)])
|
|
481
|
+
def test_volcanic_explosivity_index_physical_bound(index, value):
|
|
482
|
+
args = [5e7]
|
|
483
|
+
args[index] = value
|
|
484
|
+
with pytest.raises(ValueError):
|
|
485
|
+
domain.volcanic_explosivity_index(*args)
|