planetmodel 1.0.0__tar.gz

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 (50) hide show
  1. planetmodel-1.0.0/LICENSE +28 -0
  2. planetmodel-1.0.0/PKG-INFO +161 -0
  3. planetmodel-1.0.0/README.md +125 -0
  4. planetmodel-1.0.0/pyproject.toml +82 -0
  5. planetmodel-1.0.0/src/planetmodel/__init__.py +208 -0
  6. planetmodel-1.0.0/src/planetmodel/behaviours.py +286 -0
  7. planetmodel-1.0.0/src/planetmodel/catalogue.py +274 -0
  8. planetmodel-1.0.0/src/planetmodel/character.py +146 -0
  9. planetmodel-1.0.0/src/planetmodel/deck.py +447 -0
  10. planetmodel-1.0.0/src/planetmodel/displacement.py +312 -0
  11. planetmodel-1.0.0/src/planetmodel/fields.py +599 -0
  12. planetmodel-1.0.0/src/planetmodel/frames.py +232 -0
  13. planetmodel-1.0.0/src/planetmodel/geometry.py +420 -0
  14. planetmodel-1.0.0/src/planetmodel/harmonics.py +184 -0
  15. planetmodel-1.0.0/src/planetmodel/layerfunction.py +503 -0
  16. planetmodel-1.0.0/src/planetmodel/loading/__init__.py +41 -0
  17. planetmodel-1.0.0/src/planetmodel/loading/assembly.py +391 -0
  18. planetmodel-1.0.0/src/planetmodel/loading/love.py +342 -0
  19. planetmodel-1.0.0/src/planetmodel/loading/material.py +184 -0
  20. planetmodel-1.0.0/src/planetmodel/mapping.py +512 -0
  21. planetmodel-1.0.0/src/planetmodel/materials.py +465 -0
  22. planetmodel-1.0.0/src/planetmodel/mesh1d/__init__.py +8 -0
  23. planetmodel-1.0.0/src/planetmodel/mesh1d/gll.py +81 -0
  24. planetmodel-1.0.0/src/planetmodel/mesh1d/gravity.py +152 -0
  25. planetmodel-1.0.0/src/planetmodel/mesh1d/mesh.py +266 -0
  26. planetmodel-1.0.0/src/planetmodel/mesh3d/__init__.py +41 -0
  27. planetmodel-1.0.0/src/planetmodel/mesh3d/_geometry.py +138 -0
  28. planetmodel-1.0.0/src/planetmodel/mesh3d/_orient.py +228 -0
  29. planetmodel-1.0.0/src/planetmodel/mesh3d/_session.py +76 -0
  30. planetmodel-1.0.0/src/planetmodel/mesh3d/_sizing.py +144 -0
  31. planetmodel-1.0.0/src/planetmodel/mesh3d/_tagging.py +201 -0
  32. planetmodel-1.0.0/src/planetmodel/mesh3d/_validate.py +134 -0
  33. planetmodel-1.0.0/src/planetmodel/mesh3d/_writer.py +90 -0
  34. planetmodel-1.0.0/src/planetmodel/mesh3d/export.py +514 -0
  35. planetmodel-1.0.0/src/planetmodel/mesh3d/layered.py +202 -0
  36. planetmodel-1.0.0/src/planetmodel/mesh3d/manifest.py +547 -0
  37. planetmodel-1.0.0/src/planetmodel/mesh3d/offset.py +246 -0
  38. planetmodel-1.0.0/src/planetmodel/mesh3d/spec.py +400 -0
  39. planetmodel-1.0.0/src/planetmodel/model.py +471 -0
  40. planetmodel-1.0.0/src/planetmodel/plotting.py +99 -0
  41. planetmodel-1.0.0/src/planetmodel/pushforward.py +322 -0
  42. planetmodel-1.0.0/src/planetmodel/randomfield/__init__.py +28 -0
  43. planetmodel-1.0.0/src/planetmodel/randomfield/fields.py +514 -0
  44. planetmodel-1.0.0/src/planetmodel/randomfield/operator.py +385 -0
  45. planetmodel-1.0.0/src/planetmodel/rheology.py +173 -0
  46. planetmodel-1.0.0/src/planetmodel/sampling.py +434 -0
  47. planetmodel-1.0.0/src/planetmodel/skeleton.py +285 -0
  48. planetmodel-1.0.0/src/planetmodel/testing.py +634 -0
  49. planetmodel-1.0.0/src/planetmodel/units.py +204 -0
  50. planetmodel-1.0.0/src/planetmodel/vocabulary.py +117 -0
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, David Al-Attar
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.4
2
+ Name: planetmodel
3
+ Version: 1.0.0
4
+ Summary: Spherically layered planetary models: reference bodies, fields, mappings and meshes
5
+ License-Expression: BSD-3-Clause
6
+ License-File: LICENSE
7
+ Keywords: geophysics,seismology,planetary models,PREM,spectral elements,meshing
8
+ Author: David Al-Attar
9
+ Author-email: da380@cam.ac.uk
10
+ Requires-Python: >=3.12
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Scientific/Engineering :: Physics
17
+ Provides-Extra: harmonics
18
+ Provides-Extra: meshing
19
+ Provides-Extra: mfem
20
+ Provides-Extra: netcdf
21
+ Provides-Extra: notebook
22
+ Provides-Extra: plot
23
+ Requires-Dist: ducc0 (>=0.41) ; extra == "harmonics"
24
+ Requires-Dist: gmsh (>=4.13) ; extra == "meshing"
25
+ Requires-Dist: ipykernel (>=6.29) ; extra == "notebook"
26
+ Requires-Dist: matplotlib (>=3.11,<4) ; extra == "plot"
27
+ Requires-Dist: mfem (>=4.8) ; extra == "mfem"
28
+ Requires-Dist: netCDF4 (>=1.7) ; extra == "netcdf"
29
+ Requires-Dist: numpy (>=2.5,<3)
30
+ Requires-Dist: pyshtools (>=4.14) ; extra == "harmonics"
31
+ Requires-Dist: scipy (>=1.18,<2)
32
+ Project-URL: Homepage, https://github.com/da380/planetmodel
33
+ Project-URL: Repository, https://github.com/da380/planetmodel
34
+ Description-Content-Type: text/markdown
35
+
36
+ # planetmodel
37
+
38
+ Spherically layered planetary models: a skeleton of boundary radii, a
39
+ geometry that places it in the physical world through one continuous
40
+ mapping, fields on each layer, and the meshes that hand a model to a
41
+ solver.
42
+
43
+ The library covers the skeleton, the geometry and its mappings; fields
44
+ on one interval with an exact polynomial algebra; the model with its
45
+ units, named model types (PREM from its polynomials, simple layered
46
+ models, any mineos deck) and the mixins that complete them with moduli,
47
+ velocities, gravity and linear rheologies; a radial spectral-element
48
+ mesh; 2D and 3D meshes via gmsh with a manifest and export to MFEM; and
49
+ two sub-packages that consume the radial mesh: `planetmodel.loading`
50
+ solves the loading and tidal problem and gives Love numbers, and
51
+ `planetmodel.randomfield` draws Matern random fields on balls, annuli
52
+ and layers. A netCDF file for 3D models is next.
53
+
54
+ ## The ideas
55
+
56
+ **A skeleton.** A strictly increasing list of boundary radii, possibly
57
+ starting above zero for a shell. It answers geometric questions
58
+ (intervals, where a radius lies, with the two sides of a boundary left to
59
+ the caller) and supports surgery: refine, truncate, hollow, extend,
60
+ coarsen.
61
+
62
+ **A geometry.** A skeleton, one mapping from the reference ball to the
63
+ physical body, and the names of layers and interfaces. The mapping must
64
+ be orientation-preserving, continuous, and kinked only on skeleton
65
+ boundaries; a geometry checks those invariants when it is built. The
66
+ shipped mapping is the radial stretch `m(X) = (r + h) e_r` driven by any
67
+ callable `h(r, theta, phi)`, with closed forms for its deformation
68
+ gradient, Jacobian, validity, inverse and linearisation; any object with
69
+ `__call__`, `deformation_gradient` and `jacobian` is a mapping.
70
+
71
+ **A field on one interval.** Data on one layer: an interval, a character
72
+ (the tensor rank and weight that say how it transforms), a name, and
73
+ `evaluate(r, theta, phi, *, frame)` giving components in the local
74
+ spherical frame or in Cartesian ones. A discontinuity is two layers
75
+ asked separately. Radial fields sit on layer functions whose algebra is
76
+ exact on polynomials, so PREM's moduli `rho v^2` are exact polynomials;
77
+ analytic formulas, pointwise compositions, and fields pushed forward
78
+ through a mapping are fields too. A field may be complex-valued, which
79
+ is what a model frozen at a frequency holds.
80
+
81
+ **One model class.** A geometry with a bag of fields on every layer.
82
+ What a name means comes from the shipped vocabulary or the specs a model
83
+ is given. A model type (`PREM`, `LayeredIsotropicElastic`, `MineosModel`,
84
+ or your own) is a class derived from `Model` alone; the mixins of
85
+ `planetmodel.behaviours` add the shared derivations as methods, written
86
+ once as free functions: the Love moduli beside the velocities, the
87
+ elastic tensor and its Voigt average, gravity, and the constant-Q and
88
+ Maxwell rheologies frozen at a frequency.
89
+
90
+ **Units in one place.** The model's `Scales` say what one stored unit
91
+ is in SI, and `converted` re-expresses the whole model by name, exactly
92
+ for polynomials; `G` is read in the model's units. Nothing else names a
93
+ unit: radii are numbers, every tolerance is relative, and the meshers
94
+ hand the geometry's numbers to gmsh unchanged.
95
+
96
+ **Two meshers.** `planetmodel.mesh1d` lays Gauss-Lobatto-Legendre
97
+ elements along the radius with every skeleton boundary an element
98
+ boundary, evaluates a model's fields and gravity on its nodes, and
99
+ samples a model on an angular grid. `planetmodel.mesh3d` meshes a
100
+ geometry, full or hollow, in 2D or 3D, with shells outside it, writes a
101
+ JSON manifest saying what every attribute means, and exports the mesh,
102
+ the mapping's displacement and the model's fields to MFEM.
103
+
104
+ **Executable contracts.** `planetmodel.testing` holds one `check_*`
105
+ function per protocol; the shipped implementations and yours are held to
106
+ the same call.
107
+
108
+ ## Installing
109
+
110
+ ```
111
+ pip install planetmodel # numpy and scipy only
112
+ pip install 'planetmodel[meshing]' # 2D and 3D meshes via gmsh
113
+ pip install 'planetmodel[mfem]' # export to MFEM (PyMFEM)
114
+ pip install 'planetmodel[harmonics]' # grid transforms via pyshtools and ducc0
115
+ pip install 'planetmodel[plot]' # matplotlib, for the figures
116
+ pip install 'planetmodel[notebook]' # ipykernel, to run tutorials cell by cell
117
+ ```
118
+
119
+ Python 3.12 or later. Nothing optional is imported by `import
120
+ planetmodel`.
121
+
122
+ ## Twelve lines
123
+
124
+ ```python
125
+ import numpy as np
126
+ from planetmodel import PREM, RadialMesh, flattening, gravity
127
+
128
+ m = PREM() # exact polynomials, SI
129
+ oc, mantle = m.layer("outer_core"), m.layer("lowermost_mantle")
130
+ cmb = m.geometry.interface("cmb").radius
131
+ print(oc["rho"](cmb), mantle["rho"](cmb), m.is_fluid("outer_core")) # both sides
132
+ print(m.elastic_moduli("lowermost_mantle")(cmb, 0.3, 0.0)[:3, :3] / 1e9) # Voigt, GPa
133
+ print(gravity(m, [cmb, 6371e3]), m.moduli_at("lower_mantle", 2 * np.pi / 3600)["L"](5e6))
134
+
135
+ nd = m.nondimensionalised().stretched(flattening(1 / 300, rmax=1.0))
136
+ mesh = RadialMesh(nd, ngll=5, drmax=0.05)
137
+ print(nd.G, mesh.nodal(nd, "rho").shape, nd.geometry.validity())
138
+ ```
139
+
140
+ ## Where to go next
141
+
142
+ - `examples/tutorials/`: twelve walkthroughs, from a skeleton to Love
143
+ numbers, random fields and deck files, each a `# %%` script that runs
144
+ headless.
145
+ - `src/planetmodel/mesh3d/manifest.py`: the manifest beside every mesh,
146
+ its schema described from the consumer's side.
147
+ - `CONTRIBUTING.md`: the development setup, the hooks, the test
148
+ selections and how a release is made.
149
+
150
+ ## Tests
151
+
152
+ ```
153
+ poetry run pytest # the fast suite
154
+ poetry run pytest -m "not slow" # with gmsh and MFEM
155
+ poetry run ruff check .
156
+ ```
157
+
158
+ ## Licence
159
+
160
+ BSD-3
161
+
@@ -0,0 +1,125 @@
1
+ # planetmodel
2
+
3
+ Spherically layered planetary models: a skeleton of boundary radii, a
4
+ geometry that places it in the physical world through one continuous
5
+ mapping, fields on each layer, and the meshes that hand a model to a
6
+ solver.
7
+
8
+ The library covers the skeleton, the geometry and its mappings; fields
9
+ on one interval with an exact polynomial algebra; the model with its
10
+ units, named model types (PREM from its polynomials, simple layered
11
+ models, any mineos deck) and the mixins that complete them with moduli,
12
+ velocities, gravity and linear rheologies; a radial spectral-element
13
+ mesh; 2D and 3D meshes via gmsh with a manifest and export to MFEM; and
14
+ two sub-packages that consume the radial mesh: `planetmodel.loading`
15
+ solves the loading and tidal problem and gives Love numbers, and
16
+ `planetmodel.randomfield` draws Matern random fields on balls, annuli
17
+ and layers. A netCDF file for 3D models is next.
18
+
19
+ ## The ideas
20
+
21
+ **A skeleton.** A strictly increasing list of boundary radii, possibly
22
+ starting above zero for a shell. It answers geometric questions
23
+ (intervals, where a radius lies, with the two sides of a boundary left to
24
+ the caller) and supports surgery: refine, truncate, hollow, extend,
25
+ coarsen.
26
+
27
+ **A geometry.** A skeleton, one mapping from the reference ball to the
28
+ physical body, and the names of layers and interfaces. The mapping must
29
+ be orientation-preserving, continuous, and kinked only on skeleton
30
+ boundaries; a geometry checks those invariants when it is built. The
31
+ shipped mapping is the radial stretch `m(X) = (r + h) e_r` driven by any
32
+ callable `h(r, theta, phi)`, with closed forms for its deformation
33
+ gradient, Jacobian, validity, inverse and linearisation; any object with
34
+ `__call__`, `deformation_gradient` and `jacobian` is a mapping.
35
+
36
+ **A field on one interval.** Data on one layer: an interval, a character
37
+ (the tensor rank and weight that say how it transforms), a name, and
38
+ `evaluate(r, theta, phi, *, frame)` giving components in the local
39
+ spherical frame or in Cartesian ones. A discontinuity is two layers
40
+ asked separately. Radial fields sit on layer functions whose algebra is
41
+ exact on polynomials, so PREM's moduli `rho v^2` are exact polynomials;
42
+ analytic formulas, pointwise compositions, and fields pushed forward
43
+ through a mapping are fields too. A field may be complex-valued, which
44
+ is what a model frozen at a frequency holds.
45
+
46
+ **One model class.** A geometry with a bag of fields on every layer.
47
+ What a name means comes from the shipped vocabulary or the specs a model
48
+ is given. A model type (`PREM`, `LayeredIsotropicElastic`, `MineosModel`,
49
+ or your own) is a class derived from `Model` alone; the mixins of
50
+ `planetmodel.behaviours` add the shared derivations as methods, written
51
+ once as free functions: the Love moduli beside the velocities, the
52
+ elastic tensor and its Voigt average, gravity, and the constant-Q and
53
+ Maxwell rheologies frozen at a frequency.
54
+
55
+ **Units in one place.** The model's `Scales` say what one stored unit
56
+ is in SI, and `converted` re-expresses the whole model by name, exactly
57
+ for polynomials; `G` is read in the model's units. Nothing else names a
58
+ unit: radii are numbers, every tolerance is relative, and the meshers
59
+ hand the geometry's numbers to gmsh unchanged.
60
+
61
+ **Two meshers.** `planetmodel.mesh1d` lays Gauss-Lobatto-Legendre
62
+ elements along the radius with every skeleton boundary an element
63
+ boundary, evaluates a model's fields and gravity on its nodes, and
64
+ samples a model on an angular grid. `planetmodel.mesh3d` meshes a
65
+ geometry, full or hollow, in 2D or 3D, with shells outside it, writes a
66
+ JSON manifest saying what every attribute means, and exports the mesh,
67
+ the mapping's displacement and the model's fields to MFEM.
68
+
69
+ **Executable contracts.** `planetmodel.testing` holds one `check_*`
70
+ function per protocol; the shipped implementations and yours are held to
71
+ the same call.
72
+
73
+ ## Installing
74
+
75
+ ```
76
+ pip install planetmodel # numpy and scipy only
77
+ pip install 'planetmodel[meshing]' # 2D and 3D meshes via gmsh
78
+ pip install 'planetmodel[mfem]' # export to MFEM (PyMFEM)
79
+ pip install 'planetmodel[harmonics]' # grid transforms via pyshtools and ducc0
80
+ pip install 'planetmodel[plot]' # matplotlib, for the figures
81
+ pip install 'planetmodel[notebook]' # ipykernel, to run tutorials cell by cell
82
+ ```
83
+
84
+ Python 3.12 or later. Nothing optional is imported by `import
85
+ planetmodel`.
86
+
87
+ ## Twelve lines
88
+
89
+ ```python
90
+ import numpy as np
91
+ from planetmodel import PREM, RadialMesh, flattening, gravity
92
+
93
+ m = PREM() # exact polynomials, SI
94
+ oc, mantle = m.layer("outer_core"), m.layer("lowermost_mantle")
95
+ cmb = m.geometry.interface("cmb").radius
96
+ print(oc["rho"](cmb), mantle["rho"](cmb), m.is_fluid("outer_core")) # both sides
97
+ print(m.elastic_moduli("lowermost_mantle")(cmb, 0.3, 0.0)[:3, :3] / 1e9) # Voigt, GPa
98
+ print(gravity(m, [cmb, 6371e3]), m.moduli_at("lower_mantle", 2 * np.pi / 3600)["L"](5e6))
99
+
100
+ nd = m.nondimensionalised().stretched(flattening(1 / 300, rmax=1.0))
101
+ mesh = RadialMesh(nd, ngll=5, drmax=0.05)
102
+ print(nd.G, mesh.nodal(nd, "rho").shape, nd.geometry.validity())
103
+ ```
104
+
105
+ ## Where to go next
106
+
107
+ - `examples/tutorials/`: twelve walkthroughs, from a skeleton to Love
108
+ numbers, random fields and deck files, each a `# %%` script that runs
109
+ headless.
110
+ - `src/planetmodel/mesh3d/manifest.py`: the manifest beside every mesh,
111
+ its schema described from the consumer's side.
112
+ - `CONTRIBUTING.md`: the development setup, the hooks, the test
113
+ selections and how a release is made.
114
+
115
+ ## Tests
116
+
117
+ ```
118
+ poetry run pytest # the fast suite
119
+ poetry run pytest -m "not slow" # with gmsh and MFEM
120
+ poetry run ruff check .
121
+ ```
122
+
123
+ ## Licence
124
+
125
+ BSD-3
@@ -0,0 +1,82 @@
1
+ [project]
2
+ name = "planetmodel"
3
+ version = "1.0.0"
4
+ description = "Spherically layered planetary models: reference bodies, fields, mappings and meshes"
5
+ keywords = ["geophysics", "seismology", "planetary models", "PREM", "spectral elements", "meshing"]
6
+ classifiers = [
7
+ "Development Status :: 3 - Alpha",
8
+ "Intended Audience :: Science/Research",
9
+ "Programming Language :: Python :: 3",
10
+ "Programming Language :: Python :: 3.12",
11
+ "Programming Language :: Python :: 3.13",
12
+ "Topic :: Scientific/Engineering :: Physics",
13
+ ]
14
+ authors = [
15
+ {name = "David Al-Attar",email = "da380@cam.ac.uk"}
16
+ ]
17
+ readme = "README.md"
18
+ license = "BSD-3-Clause"
19
+ license-files = ["LICENSE"]
20
+ requires-python = ">=3.12"
21
+ dependencies = [
22
+ "numpy>=2.5,<3",
23
+ "scipy>=1.18,<2",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/da380/planetmodel"
28
+ Repository = "https://github.com/da380/planetmodel"
29
+
30
+ [project.optional-dependencies]
31
+ meshing = ["gmsh>=4.13"]
32
+ mfem = ["mfem>=4.8"]
33
+ netcdf = ["netCDF4>=1.7"]
34
+ plot = ["matplotlib>=3.11,<4"]
35
+ notebook = ["ipykernel>=6.29"]
36
+ harmonics = ["pyshtools (>=4.14)", "ducc0 (>=0.41)"]
37
+
38
+ [dependency-groups]
39
+ dev = [
40
+ "pytest>=9",
41
+ "ruff>=0.6",
42
+ "pre-commit (>=4)",
43
+ "pytest-cov (>=6)",
44
+ ]
45
+
46
+ [tool.poetry]
47
+ packages = [{include = "planetmodel", from = "src"}]
48
+
49
+ [build-system]
50
+ requires = ["poetry-core>=2.0.0,<3.0.0"]
51
+ build-backend = "poetry.core.masonry.api"
52
+
53
+ [tool.pytest.ini_options]
54
+ testpaths = ["tests"]
55
+ pythonpath = ["src"]
56
+ norecursedirs = ["archive", ".*", "build", "dist"]
57
+ # The fast suite is the default: it needs numpy and scipy alone, and the
58
+ # `plot` and `harmonics` extras where installed. `-m "not slow"` adds the
59
+ # gmsh unit geometries and the MFEM exports, `-m ""` everything.
60
+ addopts = "-m 'not gmsh and not mfem and not slow'"
61
+ markers = [
62
+ "slow: long-running tests (minutes); deselect with -m 'not slow'",
63
+ "gmsh: requires the optional gmsh dependency (planetmodel[meshing])",
64
+ "mfem: requires PyMFEM (planetmodel[mfem])",
65
+ "harmonics: requires pyshtools (planetmodel[harmonics])",
66
+ ]
67
+
68
+ [tool.ruff]
69
+ line-length = 88
70
+ src = ["src", "tests"]
71
+ extend-exclude = ["archive"]
72
+
73
+ [tool.ruff.lint]
74
+ select = ["E", "F", "W"]
75
+ # Deliberate house style, not defects:
76
+ # E741 `l` is the spherical-harmonic degree and `I` an integral -- the
77
+ # standard symbols in this field, used consistently throughout.
78
+ # E702 paired assignments to symmetric matrix entries are written on one
79
+ # line so the symmetry is visible.
80
+ # E731 short lambdas bound to names read as the mathematical definitions
81
+ # they are (fitters, coefficient functions).
82
+ ignore = ["E741", "E702", "E731"]
@@ -0,0 +1,208 @@
1
+ """planetmodel: spherically layered planetary models, and their meshes.
2
+
3
+ A model is a skeleton of boundary radii, a geometry that places the
4
+ skeleton in the physical world through one continuous mapping, and a
5
+ bag of fields on each layer. This package holds the skeleton and the
6
+ geometry, the mappings that place them, the fields and the model, the
7
+ catalogue of named models, and the meshers that hand a model to a
8
+ solver. Every internal is numbers: the model alone carries units.
9
+
10
+ Modules reached by name, one level down:
11
+
12
+ frames the local spherical frame, tensors moved between frames, Voigt
13
+ materials velocity and moduli conversions, the elastic field, what a
14
+ layer's fields imply (fluidity, moduli)
15
+ rheology what a layer's rheology fields imply, and a model frozen at
16
+ a frequency with complex moduli
17
+ units Dimensions, Scales and the dimension constants
18
+ vocabulary the shipped field names, their characters and dimensions
19
+ behaviours the free functions as methods: `layer_method` and the mixins
20
+ catalogue PREM, LayeredIsotropicElastic and MineosModel, model types
21
+ deck tabulated models: decks, their formats, fields by interpolation
22
+ sampling a model on a radial mesh times an angular grid
23
+ harmonics real spherical harmonics; grid transforms via pyshtools (optional)
24
+ plotting radial profiles drawn one way, radius upward (matplotlib, optional)
25
+ mesh1d radial spectral-element meshes (GLL), nodal values, gravity
26
+ mesh3d 2D and 3D meshes via gmsh, and MFEM export
27
+ loading the loading and tidal problem on a radial mesh: Love numbers
28
+ randomfield Matern random fields on balls, annuli and layers
29
+ testing the executable contracts, `check_field` and its kin
30
+ """
31
+
32
+ from .behaviours import (
33
+ ConstantQ,
34
+ Elastic,
35
+ SelfGravitating,
36
+ Viscoelastic,
37
+ layer_method,
38
+ with_moduli,
39
+ with_velocities,
40
+ )
41
+ from .catalogue import PREM, LayeredIsotropicElastic, MineosModel
42
+ from .character import DENSITY, ELASTIC, SCALAR, STRESS, VECTOR, Character, Symmetry
43
+ from .deck import (
44
+ MINEOS,
45
+ Deck,
46
+ DeckFormat,
47
+ Tabulated,
48
+ deck_layers,
49
+ read_deck,
50
+ write_deck,
51
+ )
52
+ from .displacement import (
53
+ CallableDisplacement,
54
+ RadialDisplacement,
55
+ ZeroDisplacement,
56
+ as_displacement,
57
+ flattening,
58
+ layer_linear,
59
+ )
60
+ from .fields import (
61
+ AnalyticField,
62
+ ComposedField,
63
+ Field,
64
+ FieldBase,
65
+ RadialField,
66
+ constant_field,
67
+ )
68
+ from .geometry import Geometry, InterfaceInfo, LayerInfo
69
+ from .harmonics import analyse_grid, real_harmonics, synthesise, synthesise_grid
70
+ from .layerfunction import (
71
+ LayerFunction,
72
+ NumericLayer,
73
+ PolynomialLayer,
74
+ as_layer_function,
75
+ constant_layer,
76
+ polynomial_fit,
77
+ polynomial_layer,
78
+ )
79
+ from .mapping import (
80
+ IdentityMapping,
81
+ Mapping,
82
+ MappingBase,
83
+ MappingPerturbation,
84
+ RadialStretch,
85
+ ScaledMapping,
86
+ ValidityReport,
87
+ outer_radius_of,
88
+ validity_lattice,
89
+ )
90
+ from .materials import ElasticField, elastic_moduli, is_fluid, kappa_mu, moduli
91
+ from .mesh1d import RadialMesh
92
+ from .mesh1d.gravity import gravity, gravity_fields, mass
93
+ from .model import Layer, Model
94
+ from .pushforward import PulledBackField, PushedForwardField, pull_back, push_forward
95
+ from .rheology import (
96
+ dispersive_moduli,
97
+ frozen,
98
+ frozen_moduli,
99
+ is_viscoelastic,
100
+ reference_omega,
101
+ )
102
+ from .sampling import AngularGrid, Sample, equiangular, gauss_legendre, sample
103
+ from .skeleton import CoarseningMap, Location, Skeleton
104
+ from .units import EARTH_MEAN_DENSITY, G_SI, Dimensions, Scales
105
+ from .vocabulary import CONSTANTS, VOCABULARY, Constant, FieldSpec
106
+ from . import testing # noqa: F401
107
+
108
+ __version__ = "1.0.0"
109
+
110
+ __all__ = [
111
+ "Skeleton",
112
+ "Location",
113
+ "CoarseningMap",
114
+ "Geometry",
115
+ "LayerInfo",
116
+ "InterfaceInfo",
117
+ "Mapping",
118
+ "MappingBase",
119
+ "IdentityMapping",
120
+ "RadialStretch",
121
+ "ScaledMapping",
122
+ "ValidityReport",
123
+ "MappingPerturbation",
124
+ "validity_lattice",
125
+ "outer_radius_of",
126
+ "RadialDisplacement",
127
+ "ZeroDisplacement",
128
+ "CallableDisplacement",
129
+ "as_displacement",
130
+ "RadialMesh",
131
+ "Character",
132
+ "SCALAR",
133
+ "DENSITY",
134
+ "VECTOR",
135
+ "STRESS",
136
+ "ELASTIC",
137
+ "Symmetry",
138
+ "LayerFunction",
139
+ "PolynomialLayer",
140
+ "NumericLayer",
141
+ "as_layer_function",
142
+ "polynomial_layer",
143
+ "constant_layer",
144
+ "polynomial_fit",
145
+ "Field",
146
+ "FieldBase",
147
+ "RadialField",
148
+ "AnalyticField",
149
+ "ComposedField",
150
+ "constant_field",
151
+ "ElasticField",
152
+ "is_fluid",
153
+ "moduli",
154
+ "elastic_moduli",
155
+ "kappa_mu",
156
+ "is_viscoelastic",
157
+ "frozen_moduli",
158
+ "dispersive_moduli",
159
+ "frozen",
160
+ "reference_omega",
161
+ "push_forward",
162
+ "pull_back",
163
+ "PushedForwardField",
164
+ "PulledBackField",
165
+ "Dimensions",
166
+ "Scales",
167
+ "G_SI",
168
+ "EARTH_MEAN_DENSITY",
169
+ "FieldSpec",
170
+ "Constant",
171
+ "VOCABULARY",
172
+ "CONSTANTS",
173
+ "Layer",
174
+ "Model",
175
+ "layer_method",
176
+ "with_moduli",
177
+ "with_velocities",
178
+ "Elastic",
179
+ "ConstantQ",
180
+ "SelfGravitating",
181
+ "Viscoelastic",
182
+ "PREM",
183
+ "LayeredIsotropicElastic",
184
+ "MineosModel",
185
+ "Deck",
186
+ "DeckFormat",
187
+ "MINEOS",
188
+ "read_deck",
189
+ "write_deck",
190
+ "deck_layers",
191
+ "Tabulated",
192
+ "flattening",
193
+ "layer_linear",
194
+ "AngularGrid",
195
+ "Sample",
196
+ "sample",
197
+ "gauss_legendre",
198
+ "equiangular",
199
+ "real_harmonics",
200
+ "synthesise",
201
+ "synthesise_grid",
202
+ "analyse_grid",
203
+ "gravity",
204
+ "mass",
205
+ "gravity_fields",
206
+ "testing",
207
+ "__version__",
208
+ ]