planetmodel 1.2.4__tar.gz → 1.3.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.
- {planetmodel-1.2.4 → planetmodel-1.3.0}/PKG-INFO +24 -14
- {planetmodel-1.2.4 → planetmodel-1.3.0}/README.md +19 -10
- {planetmodel-1.2.4 → planetmodel-1.3.0}/pyproject.toml +32 -7
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/__init__.py +47 -8
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/behaviours.py +107 -38
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/catalogue.py +10 -4
- planetmodel-1.3.0/src/planetmodel/conversion/__init__.py +66 -0
- planetmodel-1.3.0/src/planetmodel/conversion/building.py +394 -0
- planetmodel-1.3.0/src/planetmodel/conversion/laws.py +102 -0
- planetmodel-1.3.0/src/planetmodel/conversion/lithosphere.py +218 -0
- planetmodel-1.3.0/src/planetmodel/conversion/parameters.py +285 -0
- planetmodel-1.3.0/src/planetmodel/conversion/profiles.py +120 -0
- planetmodel-1.3.0/src/planetmodel/conversion/prony.py +222 -0
- planetmodel-1.3.0/src/planetmodel/conversion/tables.py +189 -0
- planetmodel-1.3.0/src/planetmodel/datasets/__init__.py +100 -0
- planetmodel-1.3.0/src/planetmodel/datasets/building.py +548 -0
- planetmodel-1.3.0/src/planetmodel/datasets/coefficients.py +126 -0
- planetmodel-1.3.0/src/planetmodel/datasets/decks.py +66 -0
- planetmodel-1.3.0/src/planetmodel/datasets/entries.py +699 -0
- planetmodel-1.3.0/src/planetmodel/datasets/fetch.py +312 -0
- planetmodel-1.3.0/src/planetmodel/datasets/grids.py +910 -0
- planetmodel-1.3.0/src/planetmodel/datasets/models.py +196 -0
- planetmodel-1.3.0/src/planetmodel/datasets/processing.py +502 -0
- planetmodel-1.3.0/src/planetmodel/datasets/projections.py +230 -0
- planetmodel-1.3.0/src/planetmodel/datasets/registry.py +155 -0
- planetmodel-1.3.0/src/planetmodel/datasets/sph.py +230 -0
- planetmodel-1.3.0/src/planetmodel/datasets/strata.py +429 -0
- planetmodel-1.3.0/src/planetmodel/datasets/surfaces.py +238 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/deck.py +225 -27
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/displacement.py +170 -44
- planetmodel-1.3.0/src/planetmodel/expansions.py +284 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/fields.py +57 -21
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/frames.py +54 -5
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/geometry.py +24 -41
- planetmodel-1.3.0/src/planetmodel/gridded.py +555 -0
- planetmodel-1.3.0/src/planetmodel/harmonics.py +395 -0
- planetmodel-1.3.0/src/planetmodel/lateral.py +434 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/layerfunction.py +88 -31
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mapping.py +208 -88
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/materials.py +44 -12
- planetmodel-1.3.0/src/planetmodel/means.py +94 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh1d/__init__.py +4 -3
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh1d/gll.py +4 -3
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh1d/gravity.py +90 -11
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh1d/mesh.py +118 -21
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/_geometry.py +18 -2
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/_sizing.py +25 -8
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/_tagging.py +1 -17
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/export.py +106 -26
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/layered.py +2 -1
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/manifest.py +49 -7
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/spec.py +27 -7
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/model.py +67 -16
- planetmodel-1.3.0/src/planetmodel/modelfile.py +1551 -0
- planetmodel-1.3.0/src/planetmodel/perturbation.py +259 -0
- planetmodel-1.3.0/src/planetmodel/plotting/__init__.py +33 -0
- planetmodel-1.3.0/src/planetmodel/plotting/maps.py +81 -0
- planetmodel-1.3.0/src/planetmodel/plotting/profiles.py +120 -0
- planetmodel-1.3.0/src/planetmodel/plotting/sections.py +106 -0
- planetmodel-1.3.0/src/planetmodel/plotting/spectra.py +81 -0
- planetmodel-1.3.0/src/planetmodel/plotting/style.py +86 -0
- planetmodel-1.3.0/src/planetmodel/plotting/vtk.py +210 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/pushforward.py +16 -5
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/randomfield/__init__.py +4 -4
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/randomfield/basis.py +11 -10
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/randomfield/fields.py +17 -25
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/randomfield/operator.py +37 -12
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/rheology.py +106 -13
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/sampling.py +220 -45
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/skeleton.py +36 -14
- planetmodel-1.3.0/src/planetmodel/slices.py +1076 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/testing.py +493 -28
- planetmodel-1.3.0/src/planetmodel/units.py +395 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/vocabulary.py +69 -6
- planetmodel-1.2.4/src/planetmodel/harmonics.py +0 -205
- planetmodel-1.2.4/src/planetmodel/plotting.py +0 -99
- planetmodel-1.2.4/src/planetmodel/units.py +0 -206
- {planetmodel-1.2.4 → planetmodel-1.3.0}/LICENSE +0 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/character.py +0 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/__init__.py +0 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/_orient.py +0 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/_session.py +0 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/_validate.py +0 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/_writer.py +0 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/mesh3d/offset.py +0 -0
- {planetmodel-1.2.4 → planetmodel-1.3.0}/src/planetmodel/randomfield/mesh.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: planetmodel
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Spherically layered planetary models: reference bodies, fields, mappings and meshes
|
|
5
5
|
License-Expression: BSD-3-Clause
|
|
6
6
|
License-File: LICENSE
|
|
@@ -14,26 +14,32 @@ Classifier: Programming Language :: Python :: 3
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
16
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
-
Provides-Extra:
|
|
17
|
+
Provides-Extra: maps
|
|
18
18
|
Provides-Extra: meshing
|
|
19
19
|
Provides-Extra: mfem
|
|
20
20
|
Provides-Extra: netcdf
|
|
21
21
|
Provides-Extra: notebook
|
|
22
22
|
Provides-Extra: plot
|
|
23
|
-
Requires-Dist:
|
|
23
|
+
Requires-Dist: cartopy (>=0.24,<1) ; extra == "maps"
|
|
24
|
+
Requires-Dist: ducc0 (>=0.41,<1)
|
|
24
25
|
Requires-Dist: gmsh (>=4.13) ; extra == "meshing"
|
|
25
26
|
Requires-Dist: ipykernel (>=6.29) ; extra == "notebook"
|
|
27
|
+
Requires-Dist: matplotlib (>=3.11,<4) ; extra == "maps"
|
|
26
28
|
Requires-Dist: matplotlib (>=3.11,<4) ; extra == "plot"
|
|
27
29
|
Requires-Dist: mfem (>=4.8) ; extra == "mfem"
|
|
28
30
|
Requires-Dist: netCDF4 (>=1.7) ; extra == "netcdf"
|
|
29
31
|
Requires-Dist: numpy (>=2.0,<3)
|
|
30
|
-
Requires-Dist: pyshtools (>=4.14) ; extra == "harmonics"
|
|
31
32
|
Requires-Dist: scipy (>=1.15,<2)
|
|
32
33
|
Project-URL: Homepage, https://github.com/da380/planetmodel
|
|
33
34
|
Project-URL: Repository, https://github.com/da380/planetmodel
|
|
34
35
|
Description-Content-Type: text/markdown
|
|
35
36
|
|
|
36
|
-
# planetmodel
|
|
37
|
+
# planetmodel: A library for spherical and aspherical planetary models
|
|
38
|
+
|
|
39
|
+
[](https://github.com/da380/planetmodel/actions/workflows/ci.yml)
|
|
40
|
+
[](https://pypi.org/project/planetmodel/)
|
|
41
|
+
[](https://opensource.org/licenses/BSD-3-Clause)
|
|
42
|
+
[](https://planetmodel.readthedocs.io/en/latest/?badge=latest)
|
|
37
43
|
|
|
38
44
|
Spherically layered planetary models — a planet described as concentric
|
|
39
45
|
layers with physical properties on each — and the meshes that hand such
|
|
@@ -111,16 +117,19 @@ held to the same call.
|
|
|
111
117
|
## Installing
|
|
112
118
|
|
|
113
119
|
```
|
|
114
|
-
pip install planetmodel # numpy and
|
|
120
|
+
pip install planetmodel # numpy, scipy and ducc0
|
|
115
121
|
pip install 'planetmodel[meshing]' # 2D and 3D meshes via gmsh
|
|
116
122
|
pip install 'planetmodel[mfem]' # export to MFEM (PyMFEM)
|
|
117
|
-
pip install 'planetmodel[
|
|
123
|
+
pip install 'planetmodel[netcdf]' # the model file (netCDF4)
|
|
118
124
|
pip install 'planetmodel[plot]' # matplotlib, for the figures
|
|
119
125
|
pip install 'planetmodel[notebook]' # ipykernel, to run tutorials cell by cell
|
|
120
126
|
```
|
|
121
127
|
|
|
122
|
-
Python 3.12 or later.
|
|
123
|
-
|
|
128
|
+
Python 3.12 or later. ducc0 does the spherical-harmonic transforms;
|
|
129
|
+
it ships wheels for Linux on x86_64, macOS on Apple silicon and
|
|
130
|
+
Windows, and elsewhere pip builds it from source, which needs a C++17
|
|
131
|
+
compiler. `import planetmodel` loads numpy and scipy alone: ducc0 and
|
|
132
|
+
every extra are imported where they are used.
|
|
124
133
|
|
|
125
134
|
## A first look
|
|
126
135
|
|
|
@@ -138,17 +147,18 @@ print(gravity(m, [r_cmb, 6371e3])) # gravity at the CMB and surf
|
|
|
138
147
|
|
|
139
148
|
# Reshape the model: non-dimensionalise it, flatten it by 1/300, and
|
|
140
149
|
# lay a radial spectral-element mesh over it.
|
|
141
|
-
nd = m.nondimensionalised().stretched(flattening(1 / 300
|
|
150
|
+
nd = m.nondimensionalised().stretched(flattening(1 / 300))
|
|
142
151
|
mesh = RadialMesh(nd, ngll=5, drmax=0.05)
|
|
143
152
|
print(mesh.nodal(nd, "rho").shape) # density at every mesh node
|
|
144
153
|
```
|
|
145
154
|
|
|
146
155
|
## Where to go next
|
|
147
156
|
|
|
148
|
-
- `examples/tutorials/`:
|
|
149
|
-
fields
|
|
150
|
-
|
|
151
|
-
|
|
157
|
+
- `examples/tutorials/`: twelve walkthroughs, from a skeleton to random
|
|
158
|
+
fields, deck files and a laterally varying model in a model file, each
|
|
159
|
+
a `# %%` script that runs headless.
|
|
160
|
+
- `docs/source/formats/`: the model file and the manifest beside every
|
|
161
|
+
mesh, described for the codes that read them (also on Read the Docs).
|
|
152
162
|
- `CONTRIBUTING.md`: the development setup, the hooks, the test
|
|
153
163
|
selections and how a release is made.
|
|
154
164
|
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
# planetmodel
|
|
1
|
+
# planetmodel: A library for spherical and aspherical planetary models
|
|
2
|
+
|
|
3
|
+
[](https://github.com/da380/planetmodel/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/planetmodel/)
|
|
5
|
+
[](https://opensource.org/licenses/BSD-3-Clause)
|
|
6
|
+
[](https://planetmodel.readthedocs.io/en/latest/?badge=latest)
|
|
2
7
|
|
|
3
8
|
Spherically layered planetary models — a planet described as concentric
|
|
4
9
|
layers with physical properties on each — and the meshes that hand such
|
|
@@ -76,16 +81,19 @@ held to the same call.
|
|
|
76
81
|
## Installing
|
|
77
82
|
|
|
78
83
|
```
|
|
79
|
-
pip install planetmodel # numpy and
|
|
84
|
+
pip install planetmodel # numpy, scipy and ducc0
|
|
80
85
|
pip install 'planetmodel[meshing]' # 2D and 3D meshes via gmsh
|
|
81
86
|
pip install 'planetmodel[mfem]' # export to MFEM (PyMFEM)
|
|
82
|
-
pip install 'planetmodel[
|
|
87
|
+
pip install 'planetmodel[netcdf]' # the model file (netCDF4)
|
|
83
88
|
pip install 'planetmodel[plot]' # matplotlib, for the figures
|
|
84
89
|
pip install 'planetmodel[notebook]' # ipykernel, to run tutorials cell by cell
|
|
85
90
|
```
|
|
86
91
|
|
|
87
|
-
Python 3.12 or later.
|
|
88
|
-
|
|
92
|
+
Python 3.12 or later. ducc0 does the spherical-harmonic transforms;
|
|
93
|
+
it ships wheels for Linux on x86_64, macOS on Apple silicon and
|
|
94
|
+
Windows, and elsewhere pip builds it from source, which needs a C++17
|
|
95
|
+
compiler. `import planetmodel` loads numpy and scipy alone: ducc0 and
|
|
96
|
+
every extra are imported where they are used.
|
|
89
97
|
|
|
90
98
|
## A first look
|
|
91
99
|
|
|
@@ -103,17 +111,18 @@ print(gravity(m, [r_cmb, 6371e3])) # gravity at the CMB and surf
|
|
|
103
111
|
|
|
104
112
|
# Reshape the model: non-dimensionalise it, flatten it by 1/300, and
|
|
105
113
|
# lay a radial spectral-element mesh over it.
|
|
106
|
-
nd = m.nondimensionalised().stretched(flattening(1 / 300
|
|
114
|
+
nd = m.nondimensionalised().stretched(flattening(1 / 300))
|
|
107
115
|
mesh = RadialMesh(nd, ngll=5, drmax=0.05)
|
|
108
116
|
print(mesh.nodal(nd, "rho").shape) # density at every mesh node
|
|
109
117
|
```
|
|
110
118
|
|
|
111
119
|
## Where to go next
|
|
112
120
|
|
|
113
|
-
- `examples/tutorials/`:
|
|
114
|
-
fields
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
- `examples/tutorials/`: twelve walkthroughs, from a skeleton to random
|
|
122
|
+
fields, deck files and a laterally varying model in a model file, each
|
|
123
|
+
a `# %%` script that runs headless.
|
|
124
|
+
- `docs/source/formats/`: the model file and the manifest beside every
|
|
125
|
+
mesh, described for the codes that read them (also on Read the Docs).
|
|
117
126
|
- `CONTRIBUTING.md`: the development setup, the hooks, the test
|
|
118
127
|
selections and how a release is made.
|
|
119
128
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "planetmodel"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.3.0"
|
|
4
4
|
description = "Spherically layered planetary models: reference bodies, fields, mappings and meshes"
|
|
5
5
|
keywords = ["geophysics", "seismology", "planetary models", "PREM", "spectral elements", "meshing"]
|
|
6
6
|
classifiers = [
|
|
@@ -21,6 +21,7 @@ requires-python = ">=3.12"
|
|
|
21
21
|
dependencies = [
|
|
22
22
|
"numpy>=2.0,<3",
|
|
23
23
|
"scipy>=1.15,<2",
|
|
24
|
+
"ducc0>=0.41,<1",
|
|
24
25
|
]
|
|
25
26
|
|
|
26
27
|
[project.urls]
|
|
@@ -32,20 +33,35 @@ meshing = ["gmsh>=4.13"]
|
|
|
32
33
|
mfem = ["mfem>=4.8"]
|
|
33
34
|
netcdf = ["netCDF4>=1.7"]
|
|
34
35
|
plot = ["matplotlib>=3.11,<4"]
|
|
36
|
+
maps = ["matplotlib>=3.11,<4", "cartopy>=0.24,<1"]
|
|
35
37
|
notebook = ["ipykernel>=6.29"]
|
|
36
|
-
harmonics = ["pyshtools (>=4.14)", "ducc0 (>=0.41)"]
|
|
37
38
|
|
|
38
39
|
[dependency-groups]
|
|
39
40
|
dev = [
|
|
40
41
|
"pytest>=9",
|
|
42
|
+
# opens model files in the tests as CF tools do
|
|
43
|
+
"xarray>=2024.10",
|
|
41
44
|
"ruff>=0.6",
|
|
42
45
|
"pre-commit (>=4)",
|
|
43
46
|
"pytest-cov (>=6)",
|
|
44
47
|
]
|
|
48
|
+
docs = [
|
|
49
|
+
# The lower bound: the apidoc template in docs/apidoc_templates is
|
|
50
|
+
# named package.rst.jinja, the name Sphinx looks for from 7.4
|
|
51
|
+
# onwards. The upper: Sphinx 9's autodoc evaluates PEP 695 `type`
|
|
52
|
+
# aliases, which fails on the names imported under TYPE_CHECKING.
|
|
53
|
+
"sphinx>=7.4,<9",
|
|
54
|
+
"furo>=2024.8",
|
|
55
|
+
]
|
|
45
56
|
|
|
46
57
|
[tool.poetry]
|
|
47
58
|
packages = [{include = "planetmodel", from = "src"}]
|
|
48
59
|
|
|
60
|
+
# The docs group serves Read the Docs and local documentation builds
|
|
61
|
+
# (`poetry install --with docs`); a plain install leaves it out.
|
|
62
|
+
[tool.poetry.group.docs]
|
|
63
|
+
optional = true
|
|
64
|
+
|
|
49
65
|
[build-system]
|
|
50
66
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
51
67
|
build-backend = "poetry.core.masonry.api"
|
|
@@ -54,15 +70,24 @@ build-backend = "poetry.core.masonry.api"
|
|
|
54
70
|
testpaths = ["tests"]
|
|
55
71
|
pythonpath = ["src"]
|
|
56
72
|
norecursedirs = [".*", "build", "dist"]
|
|
57
|
-
# The fast suite is the default: it needs
|
|
58
|
-
# `plot` and `
|
|
59
|
-
# gmsh unit geometries and the MFEM exports, `-m "
|
|
60
|
-
|
|
73
|
+
# The fast suite is the default: it needs the dependencies alone, and the
|
|
74
|
+
# `plot`, `netcdf` and `maps` extras where installed. `-m "not slow and not
|
|
75
|
+
# network"` adds the gmsh unit geometries and the MFEM exports, `-m "not
|
|
76
|
+
# network"` everything off the network, and `-m network` the registry check,
|
|
77
|
+
# which fetches every data set.
|
|
78
|
+
addopts = "-m 'not gmsh and not mfem and not slow and not network'"
|
|
79
|
+
# netCDF4 1.7.4 sets an array's shape when writing a variable of two or
|
|
80
|
+
# more dimensions, which numpy 2.5 deprecates; nothing of ours to change.
|
|
81
|
+
filterwarnings = [
|
|
82
|
+
"ignore:Setting the shape on a NumPy array:DeprecationWarning",
|
|
83
|
+
]
|
|
61
84
|
markers = [
|
|
62
85
|
"slow: long-running tests (minutes); deselect with -m 'not slow'",
|
|
63
86
|
"gmsh: requires the optional gmsh dependency (planetmodel[meshing])",
|
|
64
87
|
"mfem: requires PyMFEM (planetmodel[mfem])",
|
|
65
|
-
"
|
|
88
|
+
"netcdf: requires netCDF4 (planetmodel[netcdf])",
|
|
89
|
+
"maps: requires cartopy (planetmodel[maps])",
|
|
90
|
+
"network: fetches published data sets from their hosts; run with -m network",
|
|
66
91
|
]
|
|
67
92
|
|
|
68
93
|
[tool.ruff]
|
|
@@ -20,11 +20,19 @@ Modules reached by name, one level down:
|
|
|
20
20
|
catalogue PREM, LayeredIsotropicElastic and MineosModel, model types
|
|
21
21
|
deck tabulated models: decks, their formats, fields by interpolation
|
|
22
22
|
sampling a model on a radial mesh times an angular grid
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
lateral values between the nodes of an angular grid: the lateral rules
|
|
24
|
+
modelfile the model file: a model in one netCDF file (netCDF4, optional)
|
|
25
|
+
harmonics real spherical harmonics, through ducc0
|
|
26
|
+
slices a field on a sphere, a section or a profile; spectra and
|
|
27
|
+
correlations by degree
|
|
28
|
+
plotting profiles, sections, maps and spectra drawn (matplotlib and
|
|
29
|
+
cartopy, optional), and VTK files for ParaView
|
|
25
30
|
mesh1d radial spectral-element meshes (GLL), nodal values, gravity
|
|
31
|
+
and hydrostatic pressure
|
|
26
32
|
mesh3d 2D and 3D meshes via gmsh, and MFEM export
|
|
27
33
|
randomfield Matern random fields on balls, annuli and layers
|
|
34
|
+
datasets published models: their entries, fetched on demand into a
|
|
35
|
+
cache, read into the library's objects
|
|
28
36
|
testing the executable contracts, `check_field` and its kin
|
|
29
37
|
"""
|
|
30
38
|
|
|
@@ -40,7 +48,10 @@ from .behaviours import (
|
|
|
40
48
|
from .catalogue import PREM, LayeredIsotropicElastic, MineosModel
|
|
41
49
|
from .character import DENSITY, ELASTIC, SCALAR, STRESS, VECTOR, Character, Symmetry
|
|
42
50
|
from .deck import (
|
|
51
|
+
GIA,
|
|
43
52
|
MINEOS,
|
|
53
|
+
ND,
|
|
54
|
+
TVEL,
|
|
44
55
|
Deck,
|
|
45
56
|
DeckFormat,
|
|
46
57
|
Tabulated,
|
|
@@ -56,6 +67,7 @@ from .displacement import (
|
|
|
56
67
|
flattening,
|
|
57
68
|
layer_linear,
|
|
58
69
|
)
|
|
70
|
+
from .expansions import HarmonicField, HarmonicFunction
|
|
59
71
|
from .fields import (
|
|
60
72
|
AnalyticField,
|
|
61
73
|
ComposedField,
|
|
@@ -64,8 +76,10 @@ from .fields import (
|
|
|
64
76
|
RadialField,
|
|
65
77
|
constant_field,
|
|
66
78
|
)
|
|
79
|
+
from .frames import from_geographic, geographic
|
|
67
80
|
from .geometry import Geometry, InterfaceInfo, LayerInfo
|
|
68
|
-
from .
|
|
81
|
+
from .gridded import GriddedDisplacement, GriddedField, GriddedFunction
|
|
82
|
+
from .harmonics import real_harmonics, synthesise
|
|
69
83
|
from .layerfunction import (
|
|
70
84
|
LayerFunction,
|
|
71
85
|
NumericLayer,
|
|
@@ -87,9 +101,11 @@ from .mapping import (
|
|
|
87
101
|
validity_lattice,
|
|
88
102
|
)
|
|
89
103
|
from .materials import ElasticField, elastic_moduli, is_fluid, kappa_mu, moduli
|
|
104
|
+
from .means import spherical_mean, spherically_averaged
|
|
90
105
|
from .mesh1d import RadialMesh
|
|
91
|
-
from .mesh1d.gravity import gravity, gravity_fields, mass
|
|
106
|
+
from .mesh1d.gravity import gravity, gravity_fields, mass, pressure, pressure_fields
|
|
92
107
|
from .model import CoarseningFit, Layer, Model
|
|
108
|
+
from .perturbation import PerturbedField, perturbed, unperturbed
|
|
93
109
|
from .pushforward import PulledBackField, PushedForwardField, pull_back, push_forward
|
|
94
110
|
from .rheology import (
|
|
95
111
|
dispersive_moduli,
|
|
@@ -98,13 +114,20 @@ from .rheology import (
|
|
|
98
114
|
is_viscoelastic,
|
|
99
115
|
reference_omega,
|
|
100
116
|
)
|
|
101
|
-
from .sampling import
|
|
117
|
+
from .sampling import (
|
|
118
|
+
AngularGrid,
|
|
119
|
+
Sample,
|
|
120
|
+
driscoll_healy,
|
|
121
|
+
equiangular,
|
|
122
|
+
gauss_legendre,
|
|
123
|
+
sample,
|
|
124
|
+
)
|
|
102
125
|
from .skeleton import CoarseningMap, Location, Skeleton
|
|
103
126
|
from .units import EARTH_MEAN_DENSITY, G_SI, Dimensions, Scales
|
|
104
127
|
from .vocabulary import CONSTANTS, VOCABULARY, Constant, FieldSpec
|
|
105
128
|
from . import testing # noqa: F401
|
|
106
129
|
|
|
107
|
-
__version__ = "1.
|
|
130
|
+
__version__ = "1.3.0"
|
|
108
131
|
|
|
109
132
|
__all__ = [
|
|
110
133
|
"Skeleton",
|
|
@@ -148,6 +171,16 @@ __all__ = [
|
|
|
148
171
|
"AnalyticField",
|
|
149
172
|
"ComposedField",
|
|
150
173
|
"constant_field",
|
|
174
|
+
"GriddedField",
|
|
175
|
+
"GriddedFunction",
|
|
176
|
+
"GriddedDisplacement",
|
|
177
|
+
"HarmonicField",
|
|
178
|
+
"HarmonicFunction",
|
|
179
|
+
"PerturbedField",
|
|
180
|
+
"perturbed",
|
|
181
|
+
"unperturbed",
|
|
182
|
+
"spherical_mean",
|
|
183
|
+
"spherically_averaged",
|
|
151
184
|
"ElasticField",
|
|
152
185
|
"is_fluid",
|
|
153
186
|
"moduli",
|
|
@@ -185,6 +218,9 @@ __all__ = [
|
|
|
185
218
|
"Deck",
|
|
186
219
|
"DeckFormat",
|
|
187
220
|
"MINEOS",
|
|
221
|
+
"GIA",
|
|
222
|
+
"ND",
|
|
223
|
+
"TVEL",
|
|
188
224
|
"read_deck",
|
|
189
225
|
"write_deck",
|
|
190
226
|
"deck_layers",
|
|
@@ -195,14 +231,17 @@ __all__ = [
|
|
|
195
231
|
"Sample",
|
|
196
232
|
"sample",
|
|
197
233
|
"gauss_legendre",
|
|
234
|
+
"driscoll_healy",
|
|
198
235
|
"equiangular",
|
|
236
|
+
"geographic",
|
|
237
|
+
"from_geographic",
|
|
199
238
|
"real_harmonics",
|
|
200
239
|
"synthesise",
|
|
201
|
-
"synthesise_grid",
|
|
202
|
-
"analyse_grid",
|
|
203
240
|
"gravity",
|
|
204
241
|
"mass",
|
|
205
242
|
"gravity_fields",
|
|
243
|
+
"pressure",
|
|
244
|
+
"pressure_fields",
|
|
206
245
|
"testing",
|
|
207
246
|
"__version__",
|
|
208
247
|
]
|
|
@@ -39,22 +39,23 @@ import numpy as np
|
|
|
39
39
|
from . import materials, rheology
|
|
40
40
|
from .character import DENSITY, SCALAR
|
|
41
41
|
from .fields import ComposedField, Field
|
|
42
|
-
from .mesh1d.gravity import gravity, gravity_fields, mass
|
|
42
|
+
from .mesh1d.gravity import gravity, gravity_fields, mass, pressure, pressure_fields
|
|
43
43
|
|
|
44
44
|
if TYPE_CHECKING:
|
|
45
|
-
from .model import CoarseningFit, Model
|
|
45
|
+
from .model import CoarseningFit, Layer, Model
|
|
46
46
|
|
|
47
47
|
__all__ = ["layer_method", "with_moduli", "with_velocities", "Elastic", "ConstantQ",
|
|
48
48
|
"SelfGravitating", "Viscoelastic", "ELASTIC_FAMILIES", "elastic_family"]
|
|
49
49
|
|
|
50
50
|
#: The velocity names an elastic description may be given in.
|
|
51
|
-
VELOCITY_NAMES =
|
|
51
|
+
VELOCITY_NAMES = materials.VELOCITY_NAMES
|
|
52
52
|
|
|
53
53
|
#: The ways a layer describes its elastic medium, each a family of names
|
|
54
|
-
#: that is complete on its own: the
|
|
55
|
-
#:
|
|
56
|
-
#: `elastic_moduli`.
|
|
57
|
-
ELASTIC_FAMILIES = (
|
|
54
|
+
#: that is complete on its own: the isotropic and the transversely
|
|
55
|
+
#: isotropic velocities beside rho, the isotropic moduli, the five Love
|
|
56
|
+
#: moduli, and the tensor stored under `elastic_moduli`.
|
|
57
|
+
ELASTIC_FAMILIES = (materials.VELOCITY_SETS[materials.Symmetry.ISOTROPIC],
|
|
58
|
+
materials.VELOCITY_SETS[materials.Symmetry.VTI],
|
|
58
59
|
materials.MODULI_NAMES[materials.Symmetry.ISOTROPIC],
|
|
59
60
|
materials.MODULI_NAMES[materials.Symmetry.VTI],
|
|
60
61
|
("elastic_moduli",))
|
|
@@ -69,6 +70,40 @@ def elastic_family(name: str) -> tuple[str, ...]:
|
|
|
69
70
|
f"{list(materials.ELASTIC_NAMES)}")
|
|
70
71
|
|
|
71
72
|
|
|
73
|
+
#: The fields a frozen layer's complex moduli were derived from.
|
|
74
|
+
_FROZEN_FROM = ("rho",) + materials.ELASTIC_NAMES + rheology.RHEOLOGY_NAMES
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _refuse_frozen(model: Model, why: str) -> None:
|
|
78
|
+
"""Refuse a change that would leave a frozen model's moduli stale."""
|
|
79
|
+
if rheology.is_frozen(model):
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"the model is frozen at omega = {float(model.constant('omega')):g} "
|
|
82
|
+
f"and {why}, so the frozen moduli would go stale; make the change "
|
|
83
|
+
"on the elastic model and freeze it again")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _refuse_other_symmetry(layer: Layer, name: str) -> None:
|
|
87
|
+
"""Refuse a velocity of one symmetry on a layer holding velocities of
|
|
88
|
+
the other, naming what the layer holds and the way out."""
|
|
89
|
+
sets = materials.VELOCITY_SETS
|
|
90
|
+
own = next((s for s, names in sets.items() if name in names), None)
|
|
91
|
+
held = [n for s, names in sets.items() if s is not own for n in names
|
|
92
|
+
if n in layer]
|
|
93
|
+
if own is None or not held:
|
|
94
|
+
return
|
|
95
|
+
if own is materials.Symmetry.ISOTROPIC:
|
|
96
|
+
remedy = ("replace one of those, or describe the layer by vp and vs "
|
|
97
|
+
"with isotropic() first")
|
|
98
|
+
else:
|
|
99
|
+
remedy = (f"no one velocity completes {list(sets[own])}; replace one of "
|
|
100
|
+
"the Love moduli A, C, F, L, N instead, and the velocities are "
|
|
101
|
+
"derived again from the five")
|
|
102
|
+
where = f"layer {layer.index}" + (f" ({layer.name!r})" if layer.name else "")
|
|
103
|
+
raise ValueError(f"{where} holds the velocities {held}, so {name!r} would "
|
|
104
|
+
f"contradict them; {remedy}")
|
|
105
|
+
|
|
106
|
+
|
|
72
107
|
def _description_family(model: Model, group: Sequence[int]) -> tuple[str, ...]:
|
|
73
108
|
"""The elastic names a merged group is refit through: the first
|
|
74
109
|
description every layer of the group holds, velocities preferred
|
|
@@ -160,18 +195,19 @@ class Elastic:
|
|
|
160
195
|
vanishing of shear, and `isotropic()` the model re-described by its
|
|
161
196
|
Voigt average, kappa and mu with the five and the velocities
|
|
162
197
|
recomputed from them. A layer holding a general anisotropic tensor
|
|
163
|
-
is refused by the three that read the five
|
|
164
|
-
average is written.
|
|
198
|
+
is refused by the three that read the five.
|
|
165
199
|
|
|
166
200
|
The derived fields are kept in step by `with_field`: replacing an
|
|
167
201
|
elastic field on a layer makes the family it belongs to (rho with
|
|
168
|
-
the
|
|
169
|
-
the
|
|
170
|
-
description again, so the five follow
|
|
171
|
-
a new `L`; a field added beside the
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
202
|
+
the isotropic or the transversely isotropic velocities, kappa and
|
|
203
|
+
mu, or the five) the description, drops the other elastic names from
|
|
204
|
+
that layer, and completes the description again, so the five follow
|
|
205
|
+
a new `vs` and the velocities a new `L`; a field added beside the
|
|
206
|
+
existing ones is attached as it is, and a velocity of the other
|
|
207
|
+
symmetry than the layer's is refused, as is any change to what a
|
|
208
|
+
frozen model's moduli were derived from. A change that goes through
|
|
209
|
+
`replaced` directly, as the library's own transformations do, is the
|
|
210
|
+
caller's to keep consistent.
|
|
175
211
|
"""
|
|
176
212
|
|
|
177
213
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
@@ -192,16 +228,30 @@ class Elastic:
|
|
|
192
228
|
|
|
193
229
|
Replacing an elastic field makes its family the layer's
|
|
194
230
|
description and derives the other elastic fields from it again;
|
|
195
|
-
|
|
196
|
-
|
|
231
|
+
replacing rho on a layer holding velocities makes the velocities
|
|
232
|
+
the description, so the moduli follow the new density. A field
|
|
233
|
+
added beside the existing ones, or a family too incomplete to
|
|
234
|
+
describe the medium, is attached as it is. A velocity of one
|
|
235
|
+
symmetry on a layer holding velocities of the other is refused,
|
|
236
|
+
added or replaced: no single velocity completes the other set,
|
|
237
|
+
and `isotropic()` re-describes a layer by vp and vs. On a frozen
|
|
238
|
+
model (`rheology.is_frozen`) rho, the elastic names and the
|
|
239
|
+
rheology names are refused, the frozen moduli having been derived
|
|
240
|
+
from them.
|
|
197
241
|
"""
|
|
198
242
|
from .model import Model
|
|
243
|
+
if name in _FROZEN_FROM or rheology.is_rheology_name(name):
|
|
244
|
+
_refuse_frozen(self, f"its moduli were derived from {name!r}")
|
|
245
|
+
_refuse_other_symmetry(self.layer(which), name)
|
|
199
246
|
out = Model.with_field(self, which, name, field, replace=replace)
|
|
200
|
-
if not replace or name not in materials.ELASTIC_NAMES:
|
|
201
|
-
return out
|
|
202
247
|
i = self.layer(which).index
|
|
248
|
+
if name == "rho" and any(n in out.layer(i) for n in VELOCITY_NAMES):
|
|
249
|
+
keep = VELOCITY_NAMES
|
|
250
|
+
elif not replace or name not in materials.ELASTIC_NAMES:
|
|
251
|
+
return out
|
|
252
|
+
else:
|
|
253
|
+
keep = elastic_family(name)
|
|
203
254
|
layers = [dict(layer.fields) for layer in out.layers]
|
|
204
|
-
keep = elastic_family(name)
|
|
205
255
|
fields = {k: f for k, f in layers[i].items()
|
|
206
256
|
if k not in materials.ELASTIC_NAMES or k in keep}
|
|
207
257
|
try:
|
|
@@ -223,9 +273,11 @@ class Elastic:
|
|
|
223
273
|
from the fitted description, so the coarse model satisfies the
|
|
224
274
|
elastic relations exactly. A group whose layers share no such
|
|
225
275
|
description (one speaking only through `elastic_moduli`, say)
|
|
226
|
-
is refused. Layers left whole are
|
|
276
|
+
is refused, as is a frozen model. Layers left whole are
|
|
277
|
+
untouched.
|
|
227
278
|
"""
|
|
228
279
|
from .model import Model
|
|
280
|
+
_refuse_frozen(self, "coarsening refits the elastic description")
|
|
229
281
|
_, cmap = self._coarsening(keep=keep, drop=drop)
|
|
230
282
|
layers = [dict(layer.fields) for layer in self.layers]
|
|
231
283
|
merged = [group for group in cmap.layers if len(group) > 1]
|
|
@@ -293,12 +345,28 @@ class ConstantQ:
|
|
|
293
345
|
name="elastic_moduli")
|
|
294
346
|
|
|
295
347
|
|
|
348
|
+
def _with_radial(model: Model, fields: Sequence[Field], name: str,
|
|
349
|
+
replace: bool) -> Model:
|
|
350
|
+
"""The model with one field per layer attached under `name`, a layer
|
|
351
|
+
already holding it refused unless `replace`."""
|
|
352
|
+
layers = []
|
|
353
|
+
for layer, field in zip(model.layers, fields):
|
|
354
|
+
if name in layer and not replace:
|
|
355
|
+
raise ValueError(
|
|
356
|
+
f"layer {layer.index} ({layer.name!r}) already holds {name!r}; "
|
|
357
|
+
"pass replace=True to replace it")
|
|
358
|
+
layers.append({**layer.fields, name: field})
|
|
359
|
+
return model.replaced(layers=layers)
|
|
360
|
+
|
|
361
|
+
|
|
296
362
|
class SelfGravitating:
|
|
297
|
-
"""The gravity and
|
|
298
|
-
density on every layer: the reference body's, computed on
|
|
299
|
-
and never stored, as numbers by `gravity
|
|
300
|
-
field per layer by `gravity_fields
|
|
301
|
-
|
|
363
|
+
"""The gravity, mass and pressure of a spherically symmetric model with
|
|
364
|
+
a radial density on every layer: the reference body's, computed on
|
|
365
|
+
each call and never stored, as numbers by `gravity`, `mass` and
|
|
366
|
+
`pressure`, as one radial field per layer by `gravity_fields` and
|
|
367
|
+
`pressure_fields`, and as a copy of the model holding that field
|
|
368
|
+
under the vocabulary name `g` by `with_gravity`, `pressure` by
|
|
369
|
+
`with_pressure`;
|
|
302
370
|
`with_stratification` attaches d rho / d Phi_0 = rho'(r) / g(r) to
|
|
303
371
|
the fluid layers. A density that depends on direction is refused,
|
|
304
372
|
and the geometry's mapping does not enter (see `mesh1d.gravity`)."""
|
|
@@ -306,20 +374,21 @@ class SelfGravitating:
|
|
|
306
374
|
gravity = gravity
|
|
307
375
|
mass = mass
|
|
308
376
|
gravity_fields = gravity_fields
|
|
377
|
+
pressure = pressure
|
|
378
|
+
pressure_fields = pressure_fields
|
|
309
379
|
|
|
310
380
|
def with_gravity(self: Model, *, name: str = "g", replace: bool = False) -> Model:
|
|
311
381
|
"""The model with its gravity attached to every layer as a radial
|
|
312
382
|
field under `name`, exact where the density is polynomial; a
|
|
313
383
|
layer already holding `name` is refused unless `replace`."""
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return self.replaced(layers=layers)
|
|
384
|
+
return _with_radial(self, gravity_fields(self), name, replace)
|
|
385
|
+
|
|
386
|
+
def with_pressure(self: Model, *, name: str = "pressure",
|
|
387
|
+
replace: bool = False) -> Model:
|
|
388
|
+
"""The model with its hydrostatic pressure attached to every layer
|
|
389
|
+
as a radial field under `name` (`mesh1d.gravity`); a layer already
|
|
390
|
+
holding `name` is refused unless `replace`."""
|
|
391
|
+
return _with_radial(self, pressure_fields(self), name, replace)
|
|
323
392
|
|
|
324
393
|
def with_stratification(self: Model, *, name: str = "drho_dphi0",
|
|
325
394
|
replace: bool = False) -> Model:
|
|
@@ -373,9 +442,9 @@ class Viscoelastic:
|
|
|
373
442
|
frozen = rheology.frozen
|
|
374
443
|
|
|
375
444
|
def elastic(self: Model) -> Model:
|
|
376
|
-
"""The model without its rheology fields (`
|
|
445
|
+
"""The model without its rheology fields (`rheology.is_rheology_name`), so
|
|
377
446
|
that every layer is elastic; a frozen model keeps its complex
|
|
378
447
|
moduli, which are what its rheology became."""
|
|
379
448
|
layers = [{k: f for k, f in layer.fields.items()
|
|
380
|
-
if
|
|
449
|
+
if not rheology.is_rheology_name(k)} for layer in self.layers]
|
|
381
450
|
return self.replaced(layers=layers, check=False)
|
|
@@ -56,6 +56,10 @@ __all__ = ["PREM", "LayeredIsotropicElastic", "MineosModel", "PREM_RADIUS"]
|
|
|
56
56
|
#: The normalisation radius a of PREM's polynomials, metres.
|
|
57
57
|
PREM_RADIUS = 6371e3
|
|
58
58
|
|
|
59
|
+
_PREM_CITATION = ("Dziewonski, A. M. and D. L. Anderson (1981). Preliminary "
|
|
60
|
+
"reference Earth model. Phys. Earth Planet. Inter. 25, 297-356, "
|
|
61
|
+
"doi:10.1016/0031-9201(81)90046-7")
|
|
62
|
+
|
|
59
63
|
#: Table I of Dziewonski and Anderson (1981), centre outward: the radii
|
|
60
64
|
#: of each region in metres and the polynomial coefficients of each
|
|
61
65
|
#: column in the paper's units, ascending in x = r / a.
|
|
@@ -196,7 +200,8 @@ class PREM(Elastic, ConstantQ, SelfGravitating, Viscoelastic, Model):
|
|
|
196
200
|
name=name)
|
|
197
201
|
layers.append(fields)
|
|
198
202
|
super().__init__(geometry, layers, scales=Scales.SI,
|
|
199
|
-
name="PREM" if ocean else "PREM (no ocean)"
|
|
203
|
+
name="PREM" if ocean else "PREM (no ocean)",
|
|
204
|
+
citation=(_PREM_CITATION,))
|
|
200
205
|
|
|
201
206
|
|
|
202
207
|
class LayeredIsotropicElastic(Elastic, SelfGravitating, Model):
|
|
@@ -263,11 +268,12 @@ class MineosModel(Elastic, ConstantQ, SelfGravitating, Viscoelastic, Tabulated,
|
|
|
263
268
|
becomes the constant `omega_ref` that `moduli_at` and `frozen`
|
|
264
269
|
disperse about, and its title is the model's name. The deck is
|
|
265
270
|
kept as `deck`, its knots per layer as `knots` and its header as
|
|
266
|
-
`header`, so `to_deck` writes the model back out.
|
|
271
|
+
`header`, so `to_deck` writes the model back out. The model is in
|
|
272
|
+
SI, as the deck is; `converted` gives it in other scales.
|
|
267
273
|
"""
|
|
268
274
|
|
|
269
275
|
def __init__(self, source: str | os.PathLike[str] | Deck, *,
|
|
270
|
-
kind: str = "cubic"
|
|
276
|
+
kind: str = "cubic") -> None:
|
|
271
277
|
deck = source if isinstance(source, Deck) else read_deck(source, MINEOS)
|
|
272
278
|
skeleton, layers = deck_layers(deck, kind=kind)
|
|
273
279
|
layer_names, interface_names = mineos_names(deck)
|
|
@@ -282,5 +288,5 @@ class MineosModel(Elastic, ConstantQ, SelfGravitating, Viscoelastic, Tabulated,
|
|
|
282
288
|
self.deck = deck
|
|
283
289
|
self.knots = deck_knots(deck)
|
|
284
290
|
self.header = deck.header
|
|
285
|
-
super().__init__(geometry, layers,
|
|
291
|
+
super().__init__(geometry, layers, constants=constants,
|
|
286
292
|
name=str(deck.header.get("name", "")) or None)
|