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,176 @@
1
+ """Physical identities, limits and dynamic checks for earthsciencekit.volcanology."""
2
+
3
+ import numpy as np
4
+ import pytest
5
+ from scipy.integrate import quad
6
+
7
+ from earthsciencekit.constants import LAMBDA_U235, LAMBDA_U238, SECONDS_PER_YEAR, U238_U235
8
+ from earthsciencekit.volcanology.systems.eruptions import (
9
+ column_height,
10
+ plume_rise_height,
11
+ simulate_eruption_column,
12
+ volcanic_explosivity_index,
13
+ )
14
+ from earthsciencekit.volcanology.systems.geochemistry import (
15
+ delta_value,
16
+ equilibrium_crystallization_melt,
17
+ fractional_crystallization_melt,
18
+ isotope_temperature,
19
+ lattice_strain_partition,
20
+ rayleigh_fractionation,
21
+ )
22
+ from earthsciencekit.volcanology.systems.geochronology import (
23
+ concordia_ratios,
24
+ decay_constant,
25
+ isochron_fit,
26
+ lead_lead_age,
27
+ parent_fraction,
28
+ radiocarbon_age,
29
+ radiogenic_daughter,
30
+ radiogenic_lead_ratio,
31
+ radiometric_age,
32
+ )
33
+ from earthsciencekit.volcanology.systems.magma import (
34
+ arrhenius_viscosity,
35
+ dike_ascent_velocity,
36
+ mogi_displacement,
37
+ shaw_viscosity,
38
+ water_solubility,
39
+ )
40
+
41
+ GA = 1e9 * SECONDS_PER_YEAR
42
+
43
+
44
+ def test_decay_conserves_atoms_and_age_inverts_accumulation():
45
+ rate = decay_constant(1.25 * GA)
46
+ time = np.array([0.1, 1, 4.5]) * GA
47
+ remaining = parent_fraction(rate, time)
48
+ np.testing.assert_allclose(parent_fraction(rate, 1.25 * GA), 0.5)
49
+ np.testing.assert_allclose(remaining + radiogenic_daughter(remaining, rate, time), 1)
50
+ np.testing.assert_allclose(radiometric_age(radiogenic_daughter(2.0, rate, time) / 2.0, rate), time)
51
+
52
+
53
+ def test_radiocarbon_half_life_is_libby():
54
+ np.testing.assert_allclose(radiocarbon_age(0.5), 5568 * SECONDS_PER_YEAR, rtol=1e-4)
55
+ assert radiocarbon_age(1.2) < 0
56
+
57
+
58
+ def test_lead_ratio_limits_and_inversion():
59
+ np.testing.assert_allclose(radiogenic_lead_ratio(1e-3 * SECONDS_PER_YEAR), LAMBDA_U235 / (U238_U235 * LAMBDA_U238), rtol=1e-9)
60
+ ages = np.array([0.5, 2.0, 4.55]) * GA
61
+ np.testing.assert_allclose(lead_lead_age(radiogenic_lead_ratio(ages)), ages, rtol=1e-10)
62
+ assert np.all(np.diff(radiogenic_lead_ratio(ages)) > 0)
63
+
64
+
65
+ def test_concordia_clocks_agree_and_discordia_is_a_chord():
66
+ time = np.array([0.3, 1.8]) * GA
67
+ points = concordia_ratios(time)
68
+ np.testing.assert_allclose(radiometric_age(points[:, 0], LAMBDA_U235), time)
69
+ np.testing.assert_allclose(radiometric_age(points[:, 1], LAMBDA_U238), time)
70
+ # A partly reset zircon lies on the straight chord between formation and lead loss.
71
+ mixed = 0.6 * points[1] + 0.4 * points[0]
72
+ chord, offset = points[1] - points[0], mixed - points[0]
73
+ np.testing.assert_allclose(chord[0] * offset[1] - chord[1] * offset[0], 0, atol=1e-14)
74
+
75
+
76
+ def test_isochron_recovers_age_and_initial_ratio():
77
+ rate = 1.42e-11 / SECONDS_PER_YEAR
78
+ parent = np.array([0.1, 0.8, 2.5, 6.0])
79
+ daughter = 0.7030 + parent * np.expm1(rate * 2.0 * GA)
80
+ np.testing.assert_allclose(isochron_fit(parent, daughter, rate), [2.0 * GA, 0.7030], rtol=1e-10)
81
+
82
+
83
+ @pytest.mark.parametrize(
84
+ "args",
85
+ [
86
+ [[1.0], [1.0], 1.0],
87
+ [[1, 2], [1, 2, 3], 1.0],
88
+ [[1, 2], [1, 2], [1.0, 2.0]],
89
+ [[1, 1], [1, 2], 1.0],
90
+ [[1, 2], [2, 1], 1.0],
91
+ ],
92
+ )
93
+ def test_isochron_rejections(args):
94
+ with pytest.raises(ValueError):
95
+ isochron_fit(*args)
96
+
97
+
98
+ def test_rayleigh_limits_and_delta():
99
+ np.testing.assert_allclose(rayleigh_fractionation(0.002, 1, 0.99), 0.002)
100
+ np.testing.assert_allclose(rayleigh_fractionation(0.002, [0.1, 0.5], 1), 0.002)
101
+ shifted = delta_value(rayleigh_fractionation(0.002, 0.5, 1.01), 0.002)
102
+ np.testing.assert_allclose(shifted, 1000 * np.expm1(np.log(0.5) * 0.01))
103
+ assert shifted < 0
104
+
105
+
106
+ def test_crystallization_mass_balance_and_shared_tangent():
107
+ partition, initial = 0.2, 100.0
108
+ melt = 0.3
109
+ removed, _ = quad(lambda f: partition * fractional_crystallization_melt(initial, f, partition), melt, 1)
110
+ np.testing.assert_allclose(removed + melt * fractional_crystallization_melt(initial, melt, partition), initial, rtol=1e-10)
111
+ np.testing.assert_allclose(equilibrium_crystallization_melt(initial, [1, 0], partition), [initial, initial / partition])
112
+ step = 1e-6
113
+ for function in (fractional_crystallization_melt, equilibrium_crystallization_melt):
114
+ slope = (function(initial, 1, partition) - function(initial, 1 - step, partition)) / step
115
+ np.testing.assert_allclose(slope, (partition - 1) * initial, rtol=1e-4)
116
+ assert fractional_crystallization_melt(initial, 0.05, partition) > equilibrium_crystallization_melt(initial, 0.05, partition)
117
+
118
+
119
+ def test_lattice_strain_peaks_at_optimal_radius():
120
+ radius = np.linspace(0.8e-10, 1.3e-10, 2001)
121
+ partition = lattice_strain_partition(radius, 1.05e-10, 4.0, 250e9, 1600)
122
+ np.testing.assert_allclose(radius[np.argmax(partition)], 1.05e-10, atol=1e-13)
123
+ np.testing.assert_allclose(partition.max(), 4.0)
124
+ # For the same misfit, an oversized ion strains the lattice more than an undersized one.
125
+ assert lattice_strain_partition(1.2e-10, 1.05e-10, 4.0, 250e9, 1600) < lattice_strain_partition(0.9e-10, 1.05e-10, 4.0, 250e9, 1600)
126
+
127
+
128
+ def test_isotope_temperature_reference_and_sense():
129
+ np.testing.assert_allclose(isotope_temperature(-1.0, -1.0), 289.65)
130
+ assert isotope_temperature(1.0, 0.0) < isotope_temperature(0.0, 0.0)
131
+
132
+
133
+ def test_viscosity_pivot_and_reference():
134
+ np.testing.assert_allclose(shaw_viscosity(1e4 / 1.5, [1.0, 2.5, 4.0]), 0.1 * np.exp(-6.4))
135
+ assert shaw_viscosity(1100, 4.0) > shaw_viscosity(1100, 2.5)
136
+ np.testing.assert_allclose(arrhenius_viscosity(1300, 50, 3e5, 1300), 50)
137
+
138
+
139
+ def test_solubility_dike_and_mogi_scaling():
140
+ np.testing.assert_allclose(water_solubility(4e8, 4e-6) / water_solubility(1e8, 4e-6), 2)
141
+ np.testing.assert_allclose(dike_ascent_velocity(4, 300, 100, 9.8) / dike_ascent_velocity(2, 300, 100, 9.8), 4)
142
+ depth, volume, nu = 3000.0, 2e6, 0.25
143
+ half = mogi_displacement(depth * np.sqrt(2 ** (2 / 3) - 1), depth, volume, nu)[1]
144
+ np.testing.assert_allclose(half, mogi_displacement(0, depth, volume, nu)[1] / 2)
145
+ uplift, _ = quad(lambda r: 2 * np.pi * r * mogi_displacement(r, depth, volume, nu)[1], 0, np.inf)
146
+ np.testing.assert_allclose(uplift, 2 * (1 - nu) * volume, rtol=1e-8)
147
+
148
+
149
+ def test_plume_scaling_and_vei_bins():
150
+ np.testing.assert_allclose(plume_rise_height(2e9, 0.01) / plume_rise_height(1e9, 0.01), 2**0.25)
151
+ np.testing.assert_allclose(column_height(16e12) / column_height(1e12), 2)
152
+ volume = [0, 9.9e3, 1e4, 9.9e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e15]
153
+ np.testing.assert_array_equal(volcanic_explosivity_index(volume), [0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8])
154
+
155
+
156
+ def test_eruption_column_follows_pure_plume_similarity():
157
+ alpha, buoyancy_flux, origin = 0.12, 1.0, 1.0
158
+ speed = 5 / (6 * alpha) * (0.9 * alpha * buoyancy_flux) ** (1 / 3)
159
+ radius, velocity = 1.2 * alpha * origin, speed * origin ** (-1 / 3)
160
+ time = np.linspace(0, 30, 61)
161
+ result = simulate_eruption_column(radius, velocity, buoyancy_flux / (radius**2 * velocity), 0, time, entrainment=alpha)
162
+ exact = (origin ** (4 / 3) + 4 / 3 * speed * time) ** 0.75
163
+ np.testing.assert_allclose(result.values[:, 0], exact - origin, rtol=1e-6, atol=1e-7)
164
+ np.testing.assert_allclose(result.values[:, 1], speed * exact ** (-1 / 3), rtol=1e-6)
165
+
166
+
167
+ def test_eruption_column_reaches_mtt_height_and_settles():
168
+ frequency = 0.02
169
+ time = np.linspace(5, 3005, 1001)
170
+ result = simulate_eruption_column(1, 1, 1, frequency, time)
171
+ top = result.values[:, 0].max()
172
+ np.testing.assert_allclose(top, plume_rise_height(1, frequency), rtol=0.01)
173
+ assert 0.7 < result.values[-1, 0] / top < 0.9
174
+ assert result.values[0, 0] == 0 and result.time[0] == 5
175
+ with pytest.raises(ValueError):
176
+ simulate_eruption_column(1, [1, 2], 1, frequency, time)
@@ -0,0 +1,9 @@
1
+ """Plots for earthsciencekit.volcanology.
2
+
3
+ The dynamic models return a TimeSeries, whose ``plot`` and ``animate`` methods
4
+ call these package-wide helpers.
5
+ """
6
+
7
+ from earthsciencekit.visualizers import animate_series, plot_series
8
+
9
+ __all__ = ["plot_series", "animate_series"]
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: earthsciencekit
3
+ Version: 0.2.0
4
+ Summary: Inspectable Earth-system models for learning and teaching.
5
+ Author-email: Charles Poli <cpoli374@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/cpoli/earthsciencekit
8
+ Project-URL: Documentation, https://cpoli.github.io/earthsciencekit/
9
+ Project-URL: Issues, https://github.com/cpoli/earthsciencekit/issues
10
+ Project-URL: Changelog, https://github.com/cpoli/earthsciencekit/blob/main/CHANGELOG.md
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: numpy>=1.24
15
+ Requires-Dist: scipy>=1.10
16
+ Requires-Dist: matplotlib>=3.6
17
+ Provides-Extra: test
18
+ Requires-Dist: pytest>=7; extra == "test"
19
+ Requires-Dist: pytest-cov; extra == "test"
20
+ Provides-Extra: docs
21
+ Requires-Dist: sphinx-design>=0.6; extra == "docs"
22
+ Requires-Dist: sphinx-gallery>=0.19; extra == "docs"
23
+ Requires-Dist: sphinx-autodoc-typehints>=2; extra == "docs"
24
+ Requires-Dist: sphinx>=7; extra == "docs"
25
+ Requires-Dist: pydata-sphinx-theme>=0.15; extra == "docs"
26
+ Requires-Dist: sphinx-sitemap>=2.6; extra == "docs"
27
+ Requires-Dist: sphinxext-opengraph>=0.9; extra == "docs"
28
+ Requires-Dist: jupyterlite-sphinx>=0.17; extra == "docs"
29
+ Requires-Dist: jupyterlite-pyodide-kernel>=0.4; extra == "docs"
30
+ Provides-Extra: dev
31
+ Requires-Dist: sphinx-design>=0.6; extra == "dev"
32
+ Requires-Dist: sphinx-sitemap>=2.6; extra == "dev"
33
+ Requires-Dist: sphinxext-opengraph>=0.9; extra == "dev"
34
+ Requires-Dist: sphinx-gallery>=0.19; extra == "dev"
35
+ Requires-Dist: sphinx-autodoc-typehints>=2; extra == "dev"
36
+ Requires-Dist: pytest>=7; extra == "dev"
37
+ Requires-Dist: pytest-cov; extra == "dev"
38
+ Requires-Dist: sphinx>=7; extra == "dev"
39
+ Requires-Dist: pydata-sphinx-theme>=0.15; extra == "dev"
40
+ Requires-Dist: jupyterlite-sphinx>=0.17; extra == "dev"
41
+ Requires-Dist: jupyterlite-pyodide-kernel>=0.4; extra == "dev"
42
+ Requires-Dist: ruff>=0.6; extra == "dev"
43
+ Requires-Dist: mypy>=1.10; extra == "dev"
44
+ Requires-Dist: build>=1.2; extra == "dev"
45
+ Requires-Dist: twine>=5; extra == "dev"
46
+ Dynamic: license-file
47
+
48
+ # EarthScienceKit
49
+
50
+ Inspectable Earth-system computations for learning and teaching.
51
+
52
+ [![PyPI](https://img.shields.io/pypi/v/earthsciencekit)](https://pypi.org/project/earthsciencekit/)
53
+ [![CI](https://github.com/cpoli/earthsciencekit/actions/workflows/ci.yml/badge.svg)](https://github.com/cpoli/earthsciencekit/actions/workflows/ci.yml)
54
+ [![Docs](https://img.shields.io/badge/docs-cpoli.github.io-blue)](https://cpoli.github.io/earthsciencekit/)
55
+ [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/cpoli/earthsciencekit/blob/main/notebooks/quickstart.ipynb)
56
+ [![JupyterLite](https://jupyterlite.rtfd.io/en/latest/_static/badge.svg)](https://cpoli.github.io/earthsciencekit/lite/lab/)
57
+
58
+ EarthScienceKit **0.2.0** follows the MathematicsKit family structure with seven
59
+ subpackages and 171 domain models: 162 algebraic equations and nine sampled
60
+ dynamic models. Every model documents its units, input bounds and applicability.
61
+ NumPy and SciPy supply mature numerical foundations.
62
+
63
+ | Subpackage | Models | Focus |
64
+ | --- | ---: | --- |
65
+ | `plate_tectonics` | 23 | Euler rotation, spreading, subduction, isostasy, flexure, convection, hotspots, cooling |
66
+ | `seismology` | 23 | Elastic, surface and head waves, rays, interfaces, magnitudes, pulses, rebound |
67
+ | `atmosphere` | 22 | Hydrostatics, dry thermodynamics, humidity, stratification, rotation, Rossby waves, barotropic vorticity |
68
+ | `climate` | 24 | Radiation, greenhouse layers, insolation, forcing, feedback, ice-albedo hysteresis, red noise, heat storage, carbon and sea level |
69
+ | `oceanography` | 24 | Tides, trochoidal and dispersive waves, density and TEOS-10 cabbeling, dynamic method, Ekman spiral, gyres, two-box overturning, abyssal mixing |
70
+ | `geophysics` | 31 | Gravity anomalies, normal gravity and flattening, satellite J2, Stokes's geoid, dipole and Gauss coefficients, paleomagnetic poles, reversal anomalies, Love numbers, GRACE water, Rikitake dynamo |
71
+ | `volcanology` | 24 | Decay and radiometric ages, radiocarbon, Pb-Pb, concordia and isochron dating, Rayleigh and crystal fractionation, lattice strain, isotope thermometry, magma viscosity and ascent, Mogi inflation, plume and column heights, VEI |
72
+
73
+ ## Install and explore
74
+
75
+ ```bash
76
+ python -m pip install earthsciencekit
77
+ # Or from a checkout:
78
+ python -m pip install -e '.[dev]'
79
+ ```
80
+
81
+ ```python
82
+ import numpy as np
83
+ from earthsciencekit.climate import simulate_one_box
84
+ from earthsciencekit.units import convert
85
+
86
+ time = convert(np.linspace(0, 100, 201), "year", "s")
87
+ result = simulate_one_box(4, 1.2, 2e8, time)
88
+ print(result.summary())
89
+ figure, axes = result.plot()
90
+ animation = result.animate(interval=40)
91
+ ```
92
+
93
+ SI units apply unless an argument explicitly states otherwise. Latitude and
94
+ angles use radians. Algebraic functions accept scalars and broadcast arrays;
95
+ finite real quantities and physical bounds are validated. Unit conversions are
96
+ explicit. Sampled histories copy their arrays, retain labels and support plots
97
+ and animation export.
98
+
99
+ ## Documentation and validation
100
+
101
+ The Sphinx site has Subpackages, History, Examples and API entry points,
102
+ MathematicsKit-style cards/hubs and cross-links. Seven history pages connect
103
+ at least fifteen dated breakthroughs each to the APIs; seven guides explain equations,
104
+ conventions and independent checks. One executed gallery script per breakthrough
105
+ provides figures and Python/notebook downloads.
106
+
107
+ ```bash
108
+ MPLBACKEND=Agg python -m pytest --cov=earthsciencekit --cov-report=term-missing
109
+ python -m ruff check .
110
+ python -m ruff format --check .
111
+ MPLBACKEND=Agg python -m sphinx -E -W --keep-going -b html docs/source docs/_build/html
112
+ python -m build
113
+ ```
114
+
115
+ Coverage enforces 100% of production statements, including validators and
116
+ visualization; test files alone are omitted. This metric does not establish
117
+ physical applicability. Tests include independent analytic solutions,
118
+ conservation balances, limiting cases and invalid-input contracts. Exact release
119
+ results are in VERIFICATION.md; model limits are in docs/source/limitations.rst.
120
+ The documentation is hosted at https://cpoli.github.io/earthsciencekit/, and
121
+ `notebooks/quickstart.ipynb` runs in Colab from the badge above.
122
+
123
+ ## Contribute
124
+
125
+ See CONTRIBUTING.md. Educational models should have explicit equations, units,
126
+ assumptions, independent benchmarks and an executable example. The MIT license
127
+ is in LICENSE. Please cite this software using CITATION.cff.
@@ -0,0 +1,102 @@
1
+ earthsciencekit/__init__.py,sha256=y2G9auUbEWFqKOBZxNz8sy6F2ZoRbnjxXkkECsqWkak,322
2
+ earthsciencekit/_validation.py,sha256=dLu-IwJ_jX6PI4B7I8RnEI9clSz9oRIP7mUzosdJ1Y0,2374
3
+ earthsciencekit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ earthsciencekit/results.py,sha256=kqIGYdbxfmtKg-n0d0nUsovSA1z4_d5m2aDjjlvfJvk,2743
5
+ earthsciencekit/atmosphere/__init__.py,sha256=BVCwepZFT1q4Ua6dAduki_NsvY-pZolqRwEVIBC6s-I,1499
6
+ earthsciencekit/atmosphere/core/__init__.py,sha256=mK4pzb9WHCqPsmBImhIk9Ms6UTH5AV8t7ShLh1Yfzh0,143
7
+ earthsciencekit/atmosphere/core/base.py,sha256=_HXVjmY_AXZT6EOOWf1RbObenPg840muOU4-8CngIaU,344
8
+ earthsciencekit/atmosphere/systems/__init__.py,sha256=B5-4uqLrp4ZHBVwTYX8KiGEpnc9TLhVyXO1r9mvwvnA,62
9
+ earthsciencekit/atmosphere/systems/dynamics.py,sha256=4W8VtaVE8TrrcO8ubeZDP3dQZlcdz9P77m_-xlzbUmc,7779
10
+ earthsciencekit/atmosphere/systems/hydrostatics.py,sha256=HzaaFOdKgYdeulQK0X_QVNTv2YoKzfsRtPsmeb4Iov8,7702
11
+ earthsciencekit/atmosphere/systems/moisture.py,sha256=bWMF9OF1H5MUT8isMFuO5FQzDSj7cVMJY8C5XgQMEDI,5497
12
+ earthsciencekit/atmosphere/systems/thermodynamics.py,sha256=ML0OE3udGjAEjvNHbvZmICR49jkZ9MuxqtmqtCBEyYs,8291
13
+ earthsciencekit/atmosphere/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ earthsciencekit/atmosphere/tests/test_models.py,sha256=DfAUqZ5YCLWuroL2NT8oyG1PKy3BGWsNMGDi9DicHRc,22334
15
+ earthsciencekit/atmosphere/tests/test_physics.py,sha256=Gr76JA6JUVwIfnpLwxMeUedoLOcGkpBBb2zuGBmW8zk,4188
16
+ earthsciencekit/atmosphere/visualizers/__init__.py,sha256=9VuKM6SRINAc5X3U5fNg1wTSxK-vkPhRII0dis95GbQ,272
17
+ earthsciencekit/climate/__init__.py,sha256=PiMQzgkKcTKhpjQk6Iz_SS5w3g1At8FRf2_aLFScLeM,1610
18
+ earthsciencekit/climate/core/__init__.py,sha256=KcFFGkscE2x7f3RzywgW8U9Wr9dtuSGfjNY3unrfhBs,137
19
+ earthsciencekit/climate/core/base.py,sha256=8ZvEmnfCm7Yqf74-q8pVPjiyg6ZaDXoADkzZ01lpZic,341
20
+ earthsciencekit/climate/systems/__init__.py,sha256=9cTds58wevD1vT-S0eYbWmIpZNNAiQTenVq1w0GYW8o,59
21
+ earthsciencekit/climate/systems/carbon.py,sha256=hUpr3Ym9ZPEBX_f7-lwFBffuzkEybmMrk80eaLQn6yY,2434
22
+ earthsciencekit/climate/systems/forcing.py,sha256=aXXfNrlRpL8tapTxO8y_SSRgmpSxv0U0s0cypUa2dT0,5235
23
+ earthsciencekit/climate/systems/radiation.py,sha256=ADfdhg4UweP0f-Sf5MO5eMn2ZSFkiGFbWzvWfJtC9jQ,8369
24
+ earthsciencekit/climate/systems/response.py,sha256=kPcuDHhTswf1CQCnAUISko8vLAWFp2a1t8tPALCWkPI,15462
25
+ earthsciencekit/climate/systems/sea_level.py,sha256=uSVpUx2s70QvBqhq6WyZtY1g26nyKPMUUniA_TNkFEY,2707
26
+ earthsciencekit/climate/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ earthsciencekit/climate/tests/test_models.py,sha256=H6BTiG1RpdXiByFlSUuEMWHgj_15S0Se1AMd5t8ibH0,32254
28
+ earthsciencekit/climate/tests/test_physics.py,sha256=7PfuMKmoy2euAXtOLe31ewSwXj14Rk-Wmwf7Cj1UGWU,686
29
+ earthsciencekit/climate/visualizers/__init__.py,sha256=jBvDuTeaeZuDgslnqn6E17W7v-sEBZh3A9wdWduUn24,269
30
+ earthsciencekit/constants/__init__.py,sha256=APOuRHb_PUOynz2oHLqhpLfcLa6feEGL2fyQ52Mvceg,1098
31
+ earthsciencekit/geophysics/__init__.py,sha256=J2cj4GMo9f_8Qz4sLpHFAAjjetxgJmUF1AmZ6VUHBcc,2019
32
+ earthsciencekit/geophysics/core/__init__.py,sha256=BTtWNgMANWqopaVh5_eTrjtusv42T97x6fNjY__P01s,143
33
+ earthsciencekit/geophysics/core/base.py,sha256=zxIF98U6plv7yOS0SzcXVlrA3pYHD_cH00zaIfon4RY,344
34
+ earthsciencekit/geophysics/systems/__init__.py,sha256=MdBxyEh8ClRlsbq4BCV5L-h5n5DxfeQqQIbK95E9KaQ,62
35
+ earthsciencekit/geophysics/systems/deformation.py,sha256=NnaJhQi9RTbCu4iNfavM5LtxLqwVgMU8KdzyHnSstjM,6160
36
+ earthsciencekit/geophysics/systems/dynamo.py,sha256=4a6-SZR1K5gzvVVNyIOhGTNz5JLG-WLx1I9fSkviB6I,5971
37
+ earthsciencekit/geophysics/systems/figure.py,sha256=6IdXNhPDjJdivr10Z_y4nZ9-oGT7_YS7s6GdLMgd1_g,11645
38
+ earthsciencekit/geophysics/systems/gravity.py,sha256=8O_Aw1vyOCTs2xJ-qYOt6yv3kU8U0wQc2vy7d9lXLhM,9554
39
+ earthsciencekit/geophysics/systems/magnetism.py,sha256=trf2gXLck4s115y2aQrxkUYcEyQ7pu1JFJbiVM-cXIo,14342
40
+ earthsciencekit/geophysics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
+ earthsciencekit/geophysics/tests/test_models.py,sha256=v_ORQ2hj7F3gwQMFitpQTiVEsNxHFtUcU1vV2yZYU8U,30905
42
+ earthsciencekit/geophysics/tests/test_physics.py,sha256=mlQVx6lI4U9hYMubUBckfp1gBBklffzHjxC-dJN9pB4,8001
43
+ earthsciencekit/geophysics/visualizers/__init__.py,sha256=ZUJH2ISnOIjfouiE63p6G22x-2N_bpnn8h-H7ZI7-_c,272
44
+ earthsciencekit/integrators/__init__.py,sha256=nA-QTqn5lTqYfFsapWCn-cB-A1dvEb_60TdWR3B8XnA,2873
45
+ earthsciencekit/oceanography/__init__.py,sha256=HTBuih1BJAgDkX3E29LFLn2hwHtbLR-xFrhq4_52xuM,1592
46
+ earthsciencekit/oceanography/core/__init__.py,sha256=pelU5srHpHkmMOdS5_ZYu7YzeGEQD2VnfayY7ABuofI,147
47
+ earthsciencekit/oceanography/core/base.py,sha256=4FiQ5im8ZmqA05aI52kpXAXRJTqkvaWl_R9eKyuRHvM,346
48
+ earthsciencekit/oceanography/systems/__init__.py,sha256=r7VwPe9BxWWavWbYpCYTcM6N6n5TkU1aCzuA7ijboVE,64
49
+ earthsciencekit/oceanography/systems/circulation.py,sha256=-q4JYcK1K0FEc94bC7URgR5ALufbcq-XkGigbiQjb8U,16231
50
+ earthsciencekit/oceanography/systems/hydrography.py,sha256=Fj9AuNWM_IoUYhhyEaRWpX9hRwCp7IoySKqZNhvaIjs,8841
51
+ earthsciencekit/oceanography/systems/inertial.py,sha256=c9c9S--H317yFU7S0mY54gg_ckx0zB1j5pqL77ybs9I,2740
52
+ earthsciencekit/oceanography/systems/waves.py,sha256=Y8c114vlSvsc2UXBLGsQ33VHmhmiG8jL7rIPYWF4TJE,9759
53
+ earthsciencekit/oceanography/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
+ earthsciencekit/oceanography/tests/test_models.py,sha256=hZzMi8j4juSME6jaMlw2xazfhdNpDeW8T3kpmPcBtmk,27949
55
+ earthsciencekit/oceanography/tests/test_physics.py,sha256=-KxhtUqS3jmSGjmoiNIO_SNXnw5eUIvrS49LKvxVsuo,5063
56
+ earthsciencekit/oceanography/visualizers/__init__.py,sha256=VN1EpxrXMqE9sliwE--Qn7jbdrnIdXMWI5dao8Sz9tM,274
57
+ earthsciencekit/plate_tectonics/__init__.py,sha256=yz_y2ZOVlHAB2kbChwbcIl8WLNIGAZBLhPIe_HajDBU,1431
58
+ earthsciencekit/plate_tectonics/core/__init__.py,sha256=VS7FHryENkFrsUgSvIKGBj90-5hoSY9KT6KM-pNlMzY,153
59
+ earthsciencekit/plate_tectonics/core/base.py,sha256=EHz27jYu7Hh9kskimhPzVdsSF7n3Ihz12U862PpuWmA,349
60
+ earthsciencekit/plate_tectonics/systems/__init__.py,sha256=LSg9fJWTthOSyJGAjGi0ab2vtLIeaSwdx7eHx4U1Fu0,67
61
+ earthsciencekit/plate_tectonics/systems/boundaries.py,sha256=rffmLFzqeDV0kHBN_Nh-LHxclzcUPbFVJx-O3l1rSuk,6582
62
+ earthsciencekit/plate_tectonics/systems/cooling.py,sha256=j4dB-97feBru6mP-vFT2PYox5bq-_fBndgbjoLAex8I,10820
63
+ earthsciencekit/plate_tectonics/systems/isostasy.py,sha256=V9ScQ2bhUBhJUPGdwq-heQrONzknup0DH7UqL3D36-I,5981
64
+ earthsciencekit/plate_tectonics/systems/kinematics.py,sha256=yyiZabixEz50deQokc4MBh8e3Zu7eeOdxAwoiTNhkc4,9907
65
+ earthsciencekit/plate_tectonics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ earthsciencekit/plate_tectonics/tests/test_models.py,sha256=T1-awDQJQXRnrmlaXJBh8OO19vpmR1qwInlyKvty5gk,27360
67
+ earthsciencekit/plate_tectonics/tests/test_physics.py,sha256=IWl_nfEsVB8e2Fdyh6yWgs3RITxKhWvxypnLU1m-l7E,4590
68
+ earthsciencekit/plate_tectonics/visualizers/__init__.py,sha256=F0wPPWUUKEMYlkKO2vKbrLQjG9rFBquI7mYMO_TSCF4,277
69
+ earthsciencekit/seismology/__init__.py,sha256=4RXyuOtgyxuy1i11OMiu8lAT066IoDWfRxVK89g2UWU,1421
70
+ earthsciencekit/seismology/core/__init__.py,sha256=rG0A0GCwpXfay8TMJdZb_o6Wxdb5jRIGv-SksJfy5Sc,143
71
+ earthsciencekit/seismology/core/base.py,sha256=dMMSmIQ8GDm1qj-9x9vRu6GW1Kq-iGZEao1Ze4Hquy4,344
72
+ earthsciencekit/seismology/systems/__init__.py,sha256=aKYKTPRHjeQr90U8v8PQhQ4my7zki8uMjr_Zqh30qao,62
73
+ earthsciencekit/seismology/systems/interfaces.py,sha256=5mOEmnISb-xxI90oDoLUCiHS_j_lEM0N-BbDW0EjID0,3274
74
+ earthsciencekit/seismology/systems/rays.py,sha256=GmfPkg2PWkVF-gUn5AWvvzhaHV6xn1xFBJaxxLLfF_k,8034
75
+ earthsciencekit/seismology/systems/sources.py,sha256=CvY2RP0rsRTZ0UOVscLmDG4z2vz28b4gtKWzoTforDE,8103
76
+ earthsciencekit/seismology/systems/waves.py,sha256=u7ISl8sLtebHzP2ZhIgYxZ8VNei7-YXrLR6j0zgoVb0,10289
77
+ earthsciencekit/seismology/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
+ earthsciencekit/seismology/tests/test_models.py,sha256=z5NYAgtQoFIqRska_iPeqi1y8wnS5YPy-tyY5G6Ndtw,24992
79
+ earthsciencekit/seismology/tests/test_physics.py,sha256=JJQNUTiIcd8QVgOwWy2symTJvac2iqe83weMnnJAGz0,4499
80
+ earthsciencekit/seismology/visualizers/__init__.py,sha256=S96Dzpe2DU_epc_2xBhOctQk5KUqij-uZ33yH5RHImE,272
81
+ earthsciencekit/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
+ earthsciencekit/tests/test_core.py,sha256=EvJuYyiz4-P5N16BnVMZan9gciQTKFYH0bHV4zC6ud8,7140
83
+ earthsciencekit/tests/test_relations.py,sha256=dSMjfvmxqeA5v-8xiE68eLj9mvEf1si4ZDyz-PnP134,1320
84
+ earthsciencekit/units/__init__.py,sha256=7bLbdfnXrUbuWOYMqizOi9SH3hvioJ6UMw5dLImYOhk,2455
85
+ earthsciencekit/visualizers/__init__.py,sha256=3AyeysWp99kXpkDIQz6VZXY74mYrmFPnICYt0COg3zg,1720
86
+ earthsciencekit/volcanology/__init__.py,sha256=Mq_3G3YwMEqDR0xnsqHy_84QLB_A9JOVlpRpuQBB_dw,1582
87
+ earthsciencekit/volcanology/core/__init__.py,sha256=ktY8Tm787xo6VhJe7Y8zzJjvUm9Hf5jy4moXi_XTfBM,145
88
+ earthsciencekit/volcanology/core/base.py,sha256=taxMqJ215X_CfFzop_OBcoRwD42r-z63kKZAa_lqwf0,345
89
+ earthsciencekit/volcanology/systems/__init__.py,sha256=TuhJ19x37v479kCOvhM0jqKhb0TW9JVAr_looxli0uI,63
90
+ earthsciencekit/volcanology/systems/eruptions.py,sha256=XrG5xHfyrbHEEU366wx3mQ7zZggcF8zUWsnbnAsBFvs,7222
91
+ earthsciencekit/volcanology/systems/geochemistry.py,sha256=UZrh-dBgHfVVrMAwBUv3hbKLhn3m3diIfzXEOypppi8,10204
92
+ earthsciencekit/volcanology/systems/geochronology.py,sha256=F9Zs2_xdYZDQWsO8BKmqUY451s3SY6YOkdcaNxAiLD0,11763
93
+ earthsciencekit/volcanology/systems/magma.py,sha256=QXvASPI_6RuZNjCCFstmNynn4JWmmoRzcfnXNY2c9Qg,7925
94
+ earthsciencekit/volcanology/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
+ earthsciencekit/volcanology/tests/test_models.py,sha256=SsE2-aFg7LF8vCJukdyw6AYyhlIQ_APstXeyZdx46-o,15079
96
+ earthsciencekit/volcanology/tests/test_physics.py,sha256=9deCwe_MFqxturWrghni2M6jghMmD2Mojrkss_3NPHA,7811
97
+ earthsciencekit/volcanology/visualizers/__init__.py,sha256=IUmbJh9L2VH1Da-jd4ZqnS77da3mn5dlFpd-21jKYi8,273
98
+ earthsciencekit-0.2.0.dist-info/licenses/LICENSE,sha256=MUySpnBDFVdpdnG-6L_cwIvZIsKRy3qhfoyhfOlKBIM,1069
99
+ earthsciencekit-0.2.0.dist-info/METADATA,sha256=uZin6rZM30IoE9QiNHqcqD-YHY0kRmDpl51WEUT1zbY,6490
100
+ earthsciencekit-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
101
+ earthsciencekit-0.2.0.dist-info/top_level.txt,sha256=Rd9KaQaJ5nBkcFfy3INGjOpTncSqSSI54R3GH5npSMA,16
102
+ earthsciencekit-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,15 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Charles Poli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
12
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
13
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
15
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ earthsciencekit