erosionfront 0.1.10__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.
- erosionfront-0.1.10/PKG-INFO +69 -0
- erosionfront-0.1.10/README.md +43 -0
- erosionfront-0.1.10/pyproject.toml +43 -0
- erosionfront-0.1.10/setup.cfg +4 -0
- erosionfront-0.1.10/src/erosionfront/__init__.py +5 -0
- erosionfront-0.1.10/src/erosionfront/geomorphic/gma.py +473 -0
- erosionfront-0.1.10/src/erosionfront/geomorphic/model.py +534 -0
- erosionfront-0.1.10/src/erosionfront/geomorphic/substrate.py +221 -0
- erosionfront-0.1.10/src/erosionfront/geomorphic/surface.py +348 -0
- erosionfront-0.1.10/src/erosionfront/initialize.py +98 -0
- erosionfront-0.1.10/src/erosionfront/levelset/base.py +348 -0
- erosionfront-0.1.10/src/erosionfront/levelset/data.py +138 -0
- erosionfront-0.1.10/src/erosionfront/levelset/domain.py +72 -0
- erosionfront-0.1.10/src/erosionfront/levelset/elementary.py +384 -0
- erosionfront-0.1.10/src/erosionfront/levelset/export.py +108 -0
- erosionfront-0.1.10/src/erosionfront/levelset/gradient.py +67 -0
- erosionfront-0.1.10/src/erosionfront/levelset/grid.py +483 -0
- erosionfront-0.1.10/src/erosionfront/levelset/shock.py +170 -0
- erosionfront-0.1.10/src/erosionfront/levelset/solver.py +323 -0
- erosionfront-0.1.10/src/erosionfront/levelset/speed.py +127 -0
- erosionfront-0.1.10/src/erosionfront/misc/save.py +162 -0
- erosionfront-0.1.10/src/erosionfront/misc/utils.py +302 -0
- erosionfront-0.1.10/src/erosionfront/theory/arrays.py +100 -0
- erosionfront-0.1.10/src/erosionfront/theory/equations.py +210 -0
- erosionfront-0.1.10/src/erosionfront/theory/numerical.py +289 -0
- erosionfront-0.1.10/src/erosionfront/theory/symbols.py +41 -0
- erosionfront-0.1.10/src/erosionfront/topo/analysis.py +333 -0
- erosionfront-0.1.10/src/erosionfront/visualization/base.py +211 -0
- erosionfront-0.1.10/src/erosionfront/visualization/gmaviz.py +1103 -0
- erosionfront-0.1.10/src/erosionfront/visualization/simviz.py +629 -0
- erosionfront-0.1.10/src/erosionfront/visualization/speedviz.py +197 -0
- erosionfront-0.1.10/src/erosionfront/visualization/theoryviz.py +662 -0
- erosionfront-0.1.10/src/erosionfront/visualization/topoviz.py +295 -0
- erosionfront-0.1.10/src/erosionfront/visualization/viz2d.py +158 -0
- erosionfront-0.1.10/src/erosionfront.egg-info/PKG-INFO +69 -0
- erosionfront-0.1.10/src/erosionfront.egg-info/SOURCES.txt +37 -0
- erosionfront-0.1.10/src/erosionfront.egg-info/dependency_links.txt +1 -0
- erosionfront-0.1.10/src/erosionfront.egg-info/requires.txt +7 -0
- erosionfront-0.1.10/src/erosionfront.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: erosionfront
|
|
3
|
+
Version: 0.1.10
|
|
4
|
+
Summary: Tools for simulating rock slope erosion and the emergent geometry of Richter slopes
|
|
5
|
+
Author-email: "Colin P. Stark" <cstarkjp@gmail.com>
|
|
6
|
+
Keywords: geomorphology,erosion,rockslope,mesa,butte,cliff retreat,Richter slope,non-convex,geomorphic Hamiltonian,Hamilton-Jacobi equation,level-set,Lax-Friedrichs,Finsler geometry
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Framework :: Jupyter
|
|
9
|
+
Classifier: Framework :: MkDocs
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
12
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
+
Requires-Python: >=3.14
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: numpy
|
|
20
|
+
Requires-Dist: matplotlib
|
|
21
|
+
Requires-Dist: scipy
|
|
22
|
+
Requires-Dist: shapely
|
|
23
|
+
Requires-Dist: rasterio
|
|
24
|
+
Requires-Dist: ruptures
|
|
25
|
+
Requires-Dist: h5py
|
|
26
|
+
|
|
27
|
+
# ErosionFront
|
|
28
|
+
|
|
29
|
+
<div align="center">
|
|
30
|
+
<h3> Geomorphic Hamiltonian theory of rock slope erosion and the emergent geometry of Richter slopes
|
|
31
|
+
</h3>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div align="center">
|
|
35
|
+
|
|
36
|
+

|
|
39
|
+
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
An iconic image of the American West is the mesa: a steep cliff, rising above a ramp-like rock slope, capped by a flat bench. This famous landform has long been assumed to develop where strong rock overlies weak, and where rockfall debris suppresses ramp erosion. Such an explanation cannot be true in general, however, because the archetypal geometry can arise even in uniform bedrock with no talus armouring. Here we argue instead that it is an emergent property. Theoretical evidence comes a simple model of scarp retreat whose combined rates of weathering and surface-normal erosion are written as a slowly varying function of gradient. Model analysis and simulation, using geometric mechanics and level sets, reveal the ramp-cliff transition to form automatically as a shock solution of a non-convex Hamilton-Jacobi equation (HJE). Erodibility contrasts are not needed to explain this behaviour, but when present they help lock the landform into its classic shape and allow it to persist long-term. These conclusions are vindicated by 3D topographic analysis of differential cliff recession in geologically homogeneous material.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Level-set solution of a geomorphic HJE
|
|
47
|
+
|
|
48
|
+
The purpose of the Python code presented here is to derive, analyze, and numerically solve a geomorphic Hamiltonian[^1] model of rock slope erosion and retreat[^2]. The code is provided as a
|
|
49
|
+
[Python library package](src/erosionfront)
|
|
50
|
+
and associated Jupyter notebooks (e.g., [here](notebooks/simulation/ErosionFront.ipynb) and [here](notebooks/analysis/3DProfiling.ipynb)).
|
|
51
|
+
|
|
52
|
+
<div align="center">
|
|
53
|
+
|
|
54
|
+

|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
Numerical solution of the model Hamilton-Jacobi equation is achieved with a level-set scheme[^3] that employs Lax-Friedrichs finite differencing to obtain stable viscosity solutions for a non-convex Hamiltonian. The level-set code is custom implemented in Python.
|
|
60
|
+
|
|
61
|
+
Model analysis is performed using some tools from geometric mechanics[^4]: having converted the rock-slope erosion model into geomorphic Hamiltonian $\mathcal{H}(\mathbf{r}, \mathbf{p})$ form, this Hamiltonian is then used to derive Hamilton's ray tracing equations $(\partial_{\mathbf{p}}\mathcal{H}, -\partial_{\mathbf{r}}\mathcal{H})$ and the co-metric tensor $g^{ij} = \partial_{ij}\mathcal{H}$; these properties are then probed to understand model stability, notably to place bounds on the non-convexity of $\mathcal{H}$ and to identify critical angles.
|
|
62
|
+
|
|
63
|
+
[^1]: [Stark, C.P., & Stark, G.J., 2022. The direction of landscape erosion. Earth Surface Dynamics, 10: 383-419.](https://doi.org/10.5194/esurf-10-383-2022)
|
|
64
|
+
|
|
65
|
+
[^2]: [Howard, A.D., & Selby, M.J., 2009. Rock Slopes. In: Parsons, A.J., Abrahams, A.D. (eds). Geomorphology of Desert Environments. Springer, Dordrecht. ](https://doi.org/10.1007/978-1-4020-5719-9_8)
|
|
66
|
+
|
|
67
|
+
[^3]: [Osher, S., & Fedkiw, R., 2003. Level Set Methods and Dynamic Implicit Surfaces. Springer-Verlag New York, Inc.](https://link.springer.com/book/10.1007/b98879) See page 50.
|
|
68
|
+
|
|
69
|
+
[^4]: [Holm, D.D., 2011. Geometric Mechanics. Part I: Dynamics and Symmetry (2nd Edition)](https://www.ma.imperial.ac.uk/~dholm/classnotes/HolmPart1-GM.pdf)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ErosionFront
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<h3> Geomorphic Hamiltonian theory of rock slope erosion and the emergent geometry of Richter slopes
|
|
5
|
+
</h3>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div align="center">
|
|
9
|
+
|
|
10
|
+

|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
An iconic image of the American West is the mesa: a steep cliff, rising above a ramp-like rock slope, capped by a flat bench. This famous landform has long been assumed to develop where strong rock overlies weak, and where rockfall debris suppresses ramp erosion. Such an explanation cannot be true in general, however, because the archetypal geometry can arise even in uniform bedrock with no talus armouring. Here we argue instead that it is an emergent property. Theoretical evidence comes a simple model of scarp retreat whose combined rates of weathering and surface-normal erosion are written as a slowly varying function of gradient. Model analysis and simulation, using geometric mechanics and level sets, reveal the ramp-cliff transition to form automatically as a shock solution of a non-convex Hamilton-Jacobi equation (HJE). Erodibility contrasts are not needed to explain this behaviour, but when present they help lock the landform into its classic shape and allow it to persist long-term. These conclusions are vindicated by 3D topographic analysis of differential cliff recession in geologically homogeneous material.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Level-set solution of a geomorphic HJE
|
|
21
|
+
|
|
22
|
+
The purpose of the Python code presented here is to derive, analyze, and numerically solve a geomorphic Hamiltonian[^1] model of rock slope erosion and retreat[^2]. The code is provided as a
|
|
23
|
+
[Python library package](src/erosionfront)
|
|
24
|
+
and associated Jupyter notebooks (e.g., [here](notebooks/simulation/ErosionFront.ipynb) and [here](notebooks/analysis/3DProfiling.ipynb)).
|
|
25
|
+
|
|
26
|
+
<div align="center">
|
|
27
|
+
|
|
28
|
+

|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
Numerical solution of the model Hamilton-Jacobi equation is achieved with a level-set scheme[^3] that employs Lax-Friedrichs finite differencing to obtain stable viscosity solutions for a non-convex Hamiltonian. The level-set code is custom implemented in Python.
|
|
34
|
+
|
|
35
|
+
Model analysis is performed using some tools from geometric mechanics[^4]: having converted the rock-slope erosion model into geomorphic Hamiltonian $\mathcal{H}(\mathbf{r}, \mathbf{p})$ form, this Hamiltonian is then used to derive Hamilton's ray tracing equations $(\partial_{\mathbf{p}}\mathcal{H}, -\partial_{\mathbf{r}}\mathcal{H})$ and the co-metric tensor $g^{ij} = \partial_{ij}\mathcal{H}$; these properties are then probed to understand model stability, notably to place bounds on the non-convexity of $\mathcal{H}$ and to identify critical angles.
|
|
36
|
+
|
|
37
|
+
[^1]: [Stark, C.P., & Stark, G.J., 2022. The direction of landscape erosion. Earth Surface Dynamics, 10: 383-419.](https://doi.org/10.5194/esurf-10-383-2022)
|
|
38
|
+
|
|
39
|
+
[^2]: [Howard, A.D., & Selby, M.J., 2009. Rock Slopes. In: Parsons, A.J., Abrahams, A.D. (eds). Geomorphology of Desert Environments. Springer, Dordrecht. ](https://doi.org/10.1007/978-1-4020-5719-9_8)
|
|
40
|
+
|
|
41
|
+
[^3]: [Osher, S., & Fedkiw, R., 2003. Level Set Methods and Dynamic Implicit Surfaces. Springer-Verlag New York, Inc.](https://link.springer.com/book/10.1007/b98879) See page 50.
|
|
42
|
+
|
|
43
|
+
[^4]: [Holm, D.D., 2011. Geometric Mechanics. Part I: Dynamics and Symmetry (2nd Edition)](https://www.ma.imperial.ac.uk/~dholm/classnotes/HolmPart1-GM.pdf)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "erosionfront"
|
|
3
|
+
version = "0.1.10"
|
|
4
|
+
description = "Tools for simulating rock slope erosion and the emergent geometry of Richter slopes"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
classifiers = [
|
|
7
|
+
"Development Status :: 3 - Alpha",
|
|
8
|
+
"Framework :: Jupyter",
|
|
9
|
+
"Framework :: MkDocs",
|
|
10
|
+
"Intended Audience :: Science/Research",
|
|
11
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
12
|
+
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
13
|
+
"Operating System :: MacOS",
|
|
14
|
+
"Operating System :: POSIX :: Linux",
|
|
15
|
+
"Operating System :: Microsoft :: Windows",
|
|
16
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
17
|
+
]
|
|
18
|
+
requires-python = ">=3.14"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"numpy", "matplotlib", "scipy", "shapely", "rasterio", "ruptures", "h5py"
|
|
21
|
+
]
|
|
22
|
+
authors = [{ name = 'Colin P. Stark', email = 'cstarkjp@gmail.com' }]
|
|
23
|
+
keywords = [
|
|
24
|
+
"geomorphology",
|
|
25
|
+
"erosion",
|
|
26
|
+
"rockslope",
|
|
27
|
+
"mesa",
|
|
28
|
+
"butte",
|
|
29
|
+
"cliff retreat",
|
|
30
|
+
"Richter slope",
|
|
31
|
+
"non-convex",
|
|
32
|
+
"geomorphic Hamiltonian",
|
|
33
|
+
"Hamilton-Jacobi equation",
|
|
34
|
+
"level-set",
|
|
35
|
+
"Lax-Friedrichs",
|
|
36
|
+
"Finsler geometry"
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[[tool.uv.index]]
|
|
40
|
+
name = "testpypi"
|
|
41
|
+
url = "https://test.pypi.org/simple/"
|
|
42
|
+
publish-url = "https://test.pypi.org/legacy/"
|
|
43
|
+
explicit = true
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Geometric mechanics analysis of geomorphic Hamiltonian.
|
|
3
|
+
"""
|
|
4
|
+
import warnings
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from functools import partial
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from numpy.typing import NDArray
|
|
12
|
+
import scipy.interpolate
|
|
13
|
+
import scipy.optimize
|
|
14
|
+
|
|
15
|
+
from erosionfront.geomorphic.model import (
|
|
16
|
+
Isotropic,
|
|
17
|
+
Step,
|
|
18
|
+
ExponentialActivation,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
warnings.filterwarnings("ignore")
|
|
22
|
+
|
|
23
|
+
__all__ = ["GeometricMechanicsAnalysis"]
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class GeometricMechanicsAnalysis:
|
|
27
|
+
"""
|
|
28
|
+
Analysis of geomorphic Hamiltonian using geometric mechanics.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
model: Isotropic | Step| ExponentialActivation
|
|
33
|
+
Surface-normal erosion function model.
|
|
34
|
+
|
|
35
|
+
Attributes
|
|
36
|
+
----------
|
|
37
|
+
β_mpx: float | None = None
|
|
38
|
+
Surface slope angle at maximum px.
|
|
39
|
+
|
|
40
|
+
α_mpx: float | None = None
|
|
41
|
+
Ray angle at maximum px.
|
|
42
|
+
|
|
43
|
+
β_c0: float | None = None
|
|
44
|
+
Surface slope at first critical ray angle.
|
|
45
|
+
|
|
46
|
+
β_c1: float | None = None
|
|
47
|
+
Surface slope at second critical ray angle.
|
|
48
|
+
|
|
49
|
+
α_c0: float | None = None
|
|
50
|
+
First critical ray angle.
|
|
51
|
+
|
|
52
|
+
α_c1: float | None = None
|
|
53
|
+
Second critical ray angle.
|
|
54
|
+
|
|
55
|
+
β_rs0: float | None = None
|
|
56
|
+
Lower ramp-slope angle.
|
|
57
|
+
|
|
58
|
+
β_rs1: float | None = None
|
|
59
|
+
Upper ramp-slope angle.
|
|
60
|
+
|
|
61
|
+
α_rs0: float | None = None
|
|
62
|
+
Angle of ray for lower ramp.
|
|
63
|
+
|
|
64
|
+
α_rs1: float | None = None
|
|
65
|
+
Angle of ray for upper ramp.
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
β_mpx: float | None = None
|
|
69
|
+
α_mpx: float | None = None
|
|
70
|
+
β_c0: float | None = None
|
|
71
|
+
β_c1: float | None = None
|
|
72
|
+
α_c0: float | None = None
|
|
73
|
+
α_c1: float | None = None
|
|
74
|
+
β_rs0: float | None = None
|
|
75
|
+
β_rs1: float | None = None
|
|
76
|
+
α_rs0: float | None = None
|
|
77
|
+
α_rs1: float | None = None
|
|
78
|
+
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
model: Isotropic | Step| ExponentialActivation,
|
|
82
|
+
) -> None:
|
|
83
|
+
"""
|
|
84
|
+
Instantiate `GeometricMechanicsAnalysis` class.
|
|
85
|
+
|
|
86
|
+
Performs a suite of analyses, generating a lot of new
|
|
87
|
+
attributes containing the output.
|
|
88
|
+
"""
|
|
89
|
+
self.model = model
|
|
90
|
+
self.build_H_interpolation()
|
|
91
|
+
self.compute_dHdp()
|
|
92
|
+
self.compute_H_det_Hessian()
|
|
93
|
+
self.build_gstar_interpolation()
|
|
94
|
+
self.construct_detgstar_curve()
|
|
95
|
+
self.build_dHdp_interpolation()
|
|
96
|
+
self.construct_α_curve()
|
|
97
|
+
self.find_detgstar_zeros()
|
|
98
|
+
|
|
99
|
+
def px_onshell(self, β: float | NDArray,) -> float | NDArray:
|
|
100
|
+
"""
|
|
101
|
+
Use speed function to compute slowness component px from β.
|
|
102
|
+
|
|
103
|
+
The suffix 'on-shell' emphasizes that this computation only works
|
|
104
|
+
for px/pz/β values where H(px,pz)=1/2.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
β: float | NDArray
|
|
109
|
+
Surface slope angle(s).
|
|
110
|
+
|
|
111
|
+
Attributes
|
|
112
|
+
----------
|
|
113
|
+
Uses ξ_fn_β function attribute.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
float | NDArray:
|
|
118
|
+
Value(s) of slowness component px.
|
|
119
|
+
"""
|
|
120
|
+
return np.sin(β)/self.model.ξ_fn_β(β)
|
|
121
|
+
|
|
122
|
+
def pz_onshell(self, β: float | NDArray,) -> float | NDArray:
|
|
123
|
+
"""
|
|
124
|
+
Use speed function to compute slowness component pz from β.
|
|
125
|
+
|
|
126
|
+
The suffix 'on-shell' emphasizes that this computation only works
|
|
127
|
+
for px/pz/β values where H(px,pz)=1/2.
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
β: float | NDArray
|
|
132
|
+
Surface slope angle(s).
|
|
133
|
+
|
|
134
|
+
Attributes
|
|
135
|
+
----------
|
|
136
|
+
Uses ξ_fn_β function attribute.
|
|
137
|
+
|
|
138
|
+
Returns
|
|
139
|
+
-------
|
|
140
|
+
float | NDArray:
|
|
141
|
+
Value(s) of slowness component pz.
|
|
142
|
+
"""
|
|
143
|
+
return -np.cos(β)/self.model.ξ_fn_β(β)
|
|
144
|
+
|
|
145
|
+
def p_dot_v(self) -> NDArray:
|
|
146
|
+
"""
|
|
147
|
+
Compute inner product p(v).
|
|
148
|
+
|
|
149
|
+
Attributes
|
|
150
|
+
----------
|
|
151
|
+
dHdpx_onshell: NDArray
|
|
152
|
+
px component of H gradient = vx
|
|
153
|
+
|
|
154
|
+
dHdpz_onshell: NDArray
|
|
155
|
+
pz component of H gradient = vz
|
|
156
|
+
|
|
157
|
+
px: NDArray
|
|
158
|
+
x components of p covector array
|
|
159
|
+
|
|
160
|
+
pz: NDArray
|
|
161
|
+
z components of p covector array
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
NDArray:
|
|
166
|
+
Inner product p(v), which should all be ones.
|
|
167
|
+
"""
|
|
168
|
+
return self.dHdpx_onshell*self.px + self.dHdpz_onshell*self.pz
|
|
169
|
+
|
|
170
|
+
def Hamiltonian(
|
|
171
|
+
self,
|
|
172
|
+
px: float | NDArray,
|
|
173
|
+
pz: float | NDArray
|
|
174
|
+
) -> float | NDArray:
|
|
175
|
+
"""
|
|
176
|
+
Compute Hamiltonian values H(px,pz) anywhere (not just for H=1/2, obvs).
|
|
177
|
+
|
|
178
|
+
Parameters
|
|
179
|
+
----------
|
|
180
|
+
px: float | NDArray
|
|
181
|
+
x component(s) of p covector
|
|
182
|
+
|
|
183
|
+
pz: float | NDArray
|
|
184
|
+
z component(s) of p covector
|
|
185
|
+
|
|
186
|
+
Attributes
|
|
187
|
+
----------
|
|
188
|
+
Uses ξ_fn_β function attribute.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
float | NDArray:
|
|
193
|
+
Value(s) of H 'off-shell' or on.
|
|
194
|
+
"""
|
|
195
|
+
return 0.5*(self.model.ξ_fn_β(np.arctan2(px,-pz))**2)*(px**2+pz**2)
|
|
196
|
+
|
|
197
|
+
def build_H_interpolation(self) -> None:
|
|
198
|
+
"""
|
|
199
|
+
Generate a 2D spline interpolant for off-shell Hamiltonian grid.
|
|
200
|
+
|
|
201
|
+
Attributes
|
|
202
|
+
----------
|
|
203
|
+
Uses px_onshell, pz_onshell, Hamiltonian.
|
|
204
|
+
Modifies many.
|
|
205
|
+
"""
|
|
206
|
+
n_H_grid_points: int = 3001
|
|
207
|
+
self.β: NDArray = np.linspace(0, np.pi, n_H_grid_points)
|
|
208
|
+
self.pz: NDArray = np.array(self.pz_onshell(self.β))
|
|
209
|
+
self.px: NDArray = np.array(self.px_onshell(self.β))
|
|
210
|
+
self.px_span: tuple[float,float] = (
|
|
211
|
+
np.min(self.px),
|
|
212
|
+
np.max(self.px)*1.05,
|
|
213
|
+
)
|
|
214
|
+
self.pz_span: tuple[float,float] = (
|
|
215
|
+
np.min(self.pz)*1.03,
|
|
216
|
+
np.max(self.pz)*1.05,
|
|
217
|
+
)
|
|
218
|
+
self.n_grid_px_pts: int = n_H_grid_points
|
|
219
|
+
self.n_grid_pz_pts: int = n_H_grid_points
|
|
220
|
+
self.pxz_points: tuple[NDArray, NDArray] = (
|
|
221
|
+
np.linspace(*self.px_span, self.n_grid_px_pts),
|
|
222
|
+
np.linspace(*self.pz_span, self.n_grid_pz_pts),
|
|
223
|
+
)
|
|
224
|
+
self.H_grid: NDArray = np.array(
|
|
225
|
+
self.Hamiltonian(*np.meshgrid(*self.pxz_points, indexing="ij"))
|
|
226
|
+
)
|
|
227
|
+
self.H_interpfn: Callable \
|
|
228
|
+
= scipy.interpolate.RegularGridInterpolator(
|
|
229
|
+
self.pxz_points, self.H_grid
|
|
230
|
+
)
|
|
231
|
+
self.n_interp_grid_px_pts: int = self.n_grid_px_pts
|
|
232
|
+
self.n_interp_grid_pz_pts: int = self.n_grid_pz_pts
|
|
233
|
+
px_pts: NDArray = np.linspace(*self.px_span, self.n_interp_grid_px_pts)
|
|
234
|
+
pz_pts: NDArray = np.linspace(*self.pz_span, self.n_interp_grid_pz_pts)
|
|
235
|
+
self.Δpx: float = px_pts[1]-px_pts[0]
|
|
236
|
+
self.Δpz: float = pz_pts[1]-pz_pts[0]
|
|
237
|
+
px_mg: NDArray
|
|
238
|
+
pz_mg: NDArray
|
|
239
|
+
(px_mg, pz_mg,) = np.meshgrid(px_pts, pz_pts, indexing="ij",)
|
|
240
|
+
self.px_pz_interp_grid_pts: NDArray \
|
|
241
|
+
= np.array([px_mg.ravel(), pz_mg.ravel()]).T
|
|
242
|
+
self.H_interp_grid: NDArray = self.H_interpfn(
|
|
243
|
+
self.px_pz_interp_grid_pts,
|
|
244
|
+
method="linear").reshape(
|
|
245
|
+
self.n_interp_grid_px_pts,
|
|
246
|
+
self.n_interp_grid_pz_pts,
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def compute_dHdp(self) -> None:
|
|
250
|
+
"""
|
|
251
|
+
Compute gradient of H.
|
|
252
|
+
|
|
253
|
+
Attributes
|
|
254
|
+
----------
|
|
255
|
+
Uses H_grid, Δpx, Δpz.
|
|
256
|
+
Modifies dHdpx, dHdpz.
|
|
257
|
+
"""
|
|
258
|
+
Δxz: tuple[float,float] = (self.Δpx, self.Δpz,)
|
|
259
|
+
self.dHdpx: NDArray
|
|
260
|
+
self.dHdpz: NDArray
|
|
261
|
+
(self.dHdpx, self.dHdpz,) = np.gradient(self.H_grid, *Δxz,)
|
|
262
|
+
|
|
263
|
+
def compute_H_det_Hessian(self) -> None:
|
|
264
|
+
"""
|
|
265
|
+
Compute determinant of Hessian of H.
|
|
266
|
+
|
|
267
|
+
Attributes
|
|
268
|
+
----------
|
|
269
|
+
Uses dHdpx, dHdpx.
|
|
270
|
+
Modifies det_gstar_grid.
|
|
271
|
+
"""
|
|
272
|
+
Δxz: tuple[float,float] = (self.Δpx, self.Δpz,)
|
|
273
|
+
d2Hdpx2: NDArray
|
|
274
|
+
d2Hdpzdpx: NDArray
|
|
275
|
+
d2Hdpxdpz: NDArray
|
|
276
|
+
d2Hdpz2: NDArray
|
|
277
|
+
(d2Hdpx2, d2Hdpzdpx) = np.gradient(self.dHdpx, *Δxz,)
|
|
278
|
+
(d2Hdpxdpz, d2Hdpz2) = np.gradient(self.dHdpz, *Δxz,)
|
|
279
|
+
self.det_gstar_grid: NDArray = d2Hdpx2*d2Hdpz2 - d2Hdpxdpz*d2Hdpzdpx
|
|
280
|
+
|
|
281
|
+
def build_gstar_interpolation(self) -> None:
|
|
282
|
+
"""
|
|
283
|
+
Generate an interpolating function for the det Hessian of H.
|
|
284
|
+
|
|
285
|
+
Attributes
|
|
286
|
+
----------
|
|
287
|
+
Uses pxz_points, px_pz_interp_grid_pts, n_grid_px_pts, n_grid_pz_pts.
|
|
288
|
+
Modifies det_gstar_interpfn, det_gstar_interp_grid.
|
|
289
|
+
"""
|
|
290
|
+
self.det_gstar_interpfn: Callable \
|
|
291
|
+
= scipy.interpolate.RegularGridInterpolator(
|
|
292
|
+
self.pxz_points,
|
|
293
|
+
self.det_gstar_grid
|
|
294
|
+
)
|
|
295
|
+
self.det_gstar_interp_grid: NDArray \
|
|
296
|
+
= self.det_gstar_interpfn(
|
|
297
|
+
self.px_pz_interp_grid_pts,
|
|
298
|
+
method="linear"
|
|
299
|
+
).reshape(self.n_grid_px_pts, self.n_grid_pz_pts,)
|
|
300
|
+
|
|
301
|
+
def construct_detgstar_curve(self) -> None:
|
|
302
|
+
"""
|
|
303
|
+
Generate a spline-interpolation fn for |g*(β)| for on-shell px, pz.
|
|
304
|
+
|
|
305
|
+
Attributes
|
|
306
|
+
----------
|
|
307
|
+
Uses px, pz, β, det_gstar_interpfn, modifies det_gstar_onshell,
|
|
308
|
+
det_gstar_onshell_interpfn.
|
|
309
|
+
"""
|
|
310
|
+
px_pz_onshell_pts: NDArray = np.array([self.px, self.pz]).T
|
|
311
|
+
self.det_gstar_onshell: NDArray \
|
|
312
|
+
= self.det_gstar_interpfn(px_pz_onshell_pts)
|
|
313
|
+
self.det_gstar_onshell_interpfn: Callable \
|
|
314
|
+
= scipy.interpolate.CubicSpline(
|
|
315
|
+
self.β,
|
|
316
|
+
self.det_gstar_onshell,
|
|
317
|
+
bc_type="clamped",
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
def build_dHdp_interpolation(self) -> None:
|
|
321
|
+
"""
|
|
322
|
+
Build interpolating functions and grids for dHdpx, dHdpz.
|
|
323
|
+
|
|
324
|
+
Attributes
|
|
325
|
+
----------
|
|
326
|
+
Uses pxz_points, pxz_points, dHdpx, dHdpz, px_pz_interp_grid_pts,
|
|
327
|
+
n_grid_px_pts, n_grid_pz_pts.
|
|
328
|
+
|
|
329
|
+
Modifies dHdpx_interpfn, dHdpz_interpfn, dHdpx_interp_grid,
|
|
330
|
+
dHdpx_interp_grid.
|
|
331
|
+
"""
|
|
332
|
+
self.dHdpx_interpfn: Callable \
|
|
333
|
+
= scipy.interpolate.RegularGridInterpolator(
|
|
334
|
+
self.pxz_points,
|
|
335
|
+
self.dHdpx,
|
|
336
|
+
)
|
|
337
|
+
self.dHdpz_interpfn: Callable \
|
|
338
|
+
= scipy.interpolate.RegularGridInterpolator(
|
|
339
|
+
self.pxz_points,
|
|
340
|
+
self.dHdpz,
|
|
341
|
+
)
|
|
342
|
+
self.dHdpx_interp_grid: NDArray \
|
|
343
|
+
= self.dHdpx_interpfn(
|
|
344
|
+
self.px_pz_interp_grid_pts,
|
|
345
|
+
method="linear",
|
|
346
|
+
).reshape(self.n_grid_px_pts, self.n_grid_pz_pts,)
|
|
347
|
+
self.dHdpz_interp_grid: NDArray \
|
|
348
|
+
= self.dHdpz_interpfn(
|
|
349
|
+
self.px_pz_interp_grid_pts,
|
|
350
|
+
method="linear",
|
|
351
|
+
).reshape(self.n_grid_px_pts, self.n_grid_pz_pts,)
|
|
352
|
+
|
|
353
|
+
def construct_α_curve(self) -> None:
|
|
354
|
+
"""
|
|
355
|
+
Generate several grids and spline interpolation fns.
|
|
356
|
+
|
|
357
|
+
Make grids for onshell dHdpx, dHdpz.
|
|
358
|
+
Build spline interpolation fns for on-shell dHdpx, dHdpz. ray angle α.
|
|
359
|
+
|
|
360
|
+
Attributes
|
|
361
|
+
----------
|
|
362
|
+
Uses px, pz, β, dHdpx_interpfn, dHdpz_interpfn.
|
|
363
|
+
|
|
364
|
+
Modifies dHdpx_onshell, dHdpz_onshell, dHdpx_onshell_interpfn,
|
|
365
|
+
dHdpz_onshell_interpfn, α_onshell_interpfn.
|
|
366
|
+
"""
|
|
367
|
+
px_pz_onshell_pts: NDArray = np.array([self.px, self.pz]).T
|
|
368
|
+
self.dHdpx_onshell: NDArray = self.dHdpx_interpfn(px_pz_onshell_pts)
|
|
369
|
+
self.dHdpz_onshell: NDArray = self.dHdpz_interpfn(px_pz_onshell_pts)
|
|
370
|
+
spline: Callable = partial(
|
|
371
|
+
scipy.interpolate.CubicSpline, x=self.β, bc_type="clamped",
|
|
372
|
+
)
|
|
373
|
+
self.dHdpx_onshell_interpfn: Callable = spline(y=self.dHdpx_onshell)
|
|
374
|
+
self.dHdpz_onshell_interpfn: Callable = spline(y=self.dHdpz_onshell)
|
|
375
|
+
self.α_onshell_interpfn: Callable = spline(
|
|
376
|
+
y=np.arctan2(self.dHdpz_onshell, self.dHdpx_onshell)
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
def find_detgstar_zeros(self) -> None:
|
|
380
|
+
"""
|
|
381
|
+
Find critical angles.
|
|
382
|
+
|
|
383
|
+
Attributes
|
|
384
|
+
----------
|
|
385
|
+
Uses several, modifies several.
|
|
386
|
+
"""
|
|
387
|
+
# Find approx β where px is a maximum using brute force sampling
|
|
388
|
+
beta_samples: NDArray = np.linspace(1e-6, np.pi, 1000)
|
|
389
|
+
px_samples: NDArray = np.array(self.px_onshell(beta_samples))
|
|
390
|
+
# i_sample_max_px: int = np.where(px_samples>=np.max(px_samples))[0]
|
|
391
|
+
beta_sample_max: float = beta_samples[
|
|
392
|
+
px_samples>=np.max(px_samples)
|
|
393
|
+
][0]
|
|
394
|
+
approx_zero: float = 1e-4
|
|
395
|
+
dgstar_max: float = np.max(self.det_gstar_onshell)
|
|
396
|
+
neg: NDArray = np.s_[
|
|
397
|
+
self.det_gstar_onshell/dgstar_max < (-approx_zero)
|
|
398
|
+
]
|
|
399
|
+
critical_β_guesses: tuple[float,float] \
|
|
400
|
+
= (
|
|
401
|
+
(float(self.β[neg][0]), float(self.β[neg][-1]),)
|
|
402
|
+
if self.β[neg].shape[0]>0
|
|
403
|
+
else (np.pi, np.pi,)
|
|
404
|
+
)
|
|
405
|
+
print(f"critical_β_guesses: {critical_β_guesses}")
|
|
406
|
+
self.β_mpx = float(beta_sample_max)
|
|
407
|
+
self.α_mpx = float(self.α_onshell_interpfn(self.β_mpx))
|
|
408
|
+
print(f"β_mpx = {np.rad2deg(self.β_mpx):3.2f}")
|
|
409
|
+
print(f"α_mpx = {np.rad2deg(self.α_mpx):3.2f}")
|
|
410
|
+
|
|
411
|
+
# Make a wrapper for root finder
|
|
412
|
+
find_detgstar_root: Callable = partial(
|
|
413
|
+
scipy.optimize.root_scalar,
|
|
414
|
+
self.det_gstar_onshell_interpfn,
|
|
415
|
+
method="newton",
|
|
416
|
+
bracket=(np.pi/3000,np.pi/2,),
|
|
417
|
+
)
|
|
418
|
+
detgstar_roots: tuple[Any,Any] = (
|
|
419
|
+
find_detgstar_root(
|
|
420
|
+
x0=(
|
|
421
|
+
critical_β_guesses[0] if np.abs(critical_β_guesses[0])>1e-3
|
|
422
|
+
else beta_sample_max*1.4
|
|
423
|
+
# else np.pi/5
|
|
424
|
+
),
|
|
425
|
+
x1=beta_sample_max*2,
|
|
426
|
+
),
|
|
427
|
+
find_detgstar_root(
|
|
428
|
+
x0=critical_β_guesses[1]
|
|
429
|
+
),
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
# Find β_c0, β_c1
|
|
433
|
+
if not detgstar_roots[0].converged:
|
|
434
|
+
print("Failed to find β_c0")
|
|
435
|
+
self.β_c0 = 0
|
|
436
|
+
if not detgstar_roots[1].converged:
|
|
437
|
+
print("Failed to find β_c1")
|
|
438
|
+
self.β_c1 = 0
|
|
439
|
+
if detgstar_roots[0].converged or detgstar_roots[1].converged:
|
|
440
|
+
det_gstar_zeros: list[float] = [
|
|
441
|
+
soln_.root if soln_.root>0 else np.pi+soln_.root
|
|
442
|
+
for soln_ in detgstar_roots
|
|
443
|
+
]
|
|
444
|
+
self.β_c0 = float(det_gstar_zeros[0])
|
|
445
|
+
self.β_c1 = float(det_gstar_zeros[1])
|
|
446
|
+
|
|
447
|
+
# self.β_crit_guesses = critical_β_guesses
|
|
448
|
+
self.α_c0 = float(self.α_onshell_interpfn(self.β_c0))
|
|
449
|
+
self.α_c1 = float(self.α_onshell_interpfn(self.β_c1))
|
|
450
|
+
|
|
451
|
+
α_offset_fn_ = lambda β: self.α_onshell_interpfn(β) - self.α_c1
|
|
452
|
+
soln_ = scipy.optimize.root_scalar(
|
|
453
|
+
α_offset_fn_,
|
|
454
|
+
# x0 = self.β_c0*0.1,
|
|
455
|
+
method="bisect",
|
|
456
|
+
bracket=(0, np.pi/2.01,) #self.β_c0),
|
|
457
|
+
)
|
|
458
|
+
if not soln_.converged:
|
|
459
|
+
raise ValueError(r"Failed to find β_rs1")
|
|
460
|
+
self.β_rs1 = float(soln_.root)
|
|
461
|
+
self.α_rs1 = float(self.α_onshell_interpfn(self.β_rs1))
|
|
462
|
+
|
|
463
|
+
α_offset_fn_ = lambda β: self.α_onshell_interpfn(β) - self.α_c0
|
|
464
|
+
soln_ = scipy.optimize.root_scalar(
|
|
465
|
+
α_offset_fn_,
|
|
466
|
+
x0 = np.pi/4 if self.β_c1 is None else self.β_c1*1.1,
|
|
467
|
+
# method="bisect",
|
|
468
|
+
# bracket=(self.β_c1, np.pi/2),
|
|
469
|
+
)
|
|
470
|
+
if not soln_.converged:
|
|
471
|
+
raise ValueError(r"Failed to find β_rs0")
|
|
472
|
+
self.β_rs0 = float(soln_.root)
|
|
473
|
+
self.α_rs0 = float(self.α_onshell_interpfn(self.β_rs0))
|