wmbe 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wmbe/__init__.py +23 -0
- wmbe/data.py +220 -0
- wmbe/initialize.py +65 -0
- wmbe/plot.py +930 -0
- wmbe/save.py +79 -0
- wmbe/solve1d.py +232 -0
- wmbe/solve1p1d.py +159 -0
- wmbe/symbols.py +27 -0
- wmbe/theory.py +109 -0
- wmbe-0.1.1.dist-info/METADATA +57 -0
- wmbe-0.1.1.dist-info/RECORD +14 -0
- wmbe-0.1.1.dist-info/WHEEL +5 -0
- wmbe-0.1.1.dist-info/licenses/LICENSE.md +636 -0
- wmbe-0.1.1.dist-info/top_level.txt +1 -0
wmbe/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Initialize WMBE package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from wmbe import data
|
|
6
|
+
from wmbe import initialize
|
|
7
|
+
from wmbe import plot
|
|
8
|
+
from wmbe import save
|
|
9
|
+
from wmbe import solve1p1d
|
|
10
|
+
from wmbe import theory
|
|
11
|
+
|
|
12
|
+
__version__ = "2026.6.07"
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"data",
|
|
16
|
+
"initialize",
|
|
17
|
+
"plot",
|
|
18
|
+
"save",
|
|
19
|
+
"solve",
|
|
20
|
+
"solve1p1d",
|
|
21
|
+
"symbols",
|
|
22
|
+
"theory",
|
|
23
|
+
]
|
wmbe/data.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
---------------------------------------------------------------------
|
|
3
|
+
|
|
4
|
+
Module to import and model Inoue & Li experimental data.
|
|
5
|
+
|
|
6
|
+
---------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
Requires Python packages/modules:
|
|
9
|
+
- :mod:`numpy`
|
|
10
|
+
- :mod:`os`
|
|
11
|
+
- :mod:`pandas`
|
|
12
|
+
- :mod:`scipy.optimize`
|
|
13
|
+
|
|
14
|
+
---------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
.. _`Inoue et al (2017)`: https://doi.org/10.1016/j.geomorph.2017.02.018
|
|
17
|
+
.. _`Li et al (2016)`: https://doi.org/10.25103/jestr.093.10
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os, pandas as pd, numpy as np
|
|
22
|
+
from scipy.optimize import curve_fit
|
|
23
|
+
|
|
24
|
+
def linear_model(x,m,c):
|
|
25
|
+
"""
|
|
26
|
+
Simple linear model of form: :math:`y = m x + c`
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
x (float or numpy.ndarray) : coordinate
|
|
30
|
+
m (float) : gradient
|
|
31
|
+
c (float) : intercept
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
float or numpy.ndarray: y
|
|
35
|
+
"""
|
|
36
|
+
return m*x+c
|
|
37
|
+
|
|
38
|
+
def exponential_decay_model(x,m,c):
|
|
39
|
+
r"""
|
|
40
|
+
Shifted exponential decay model of form: :math:`y = 1 + c \exp(-x/m)`
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
x (float or numpy.ndarray) : coordinate
|
|
44
|
+
m (float) : e-folding scale
|
|
45
|
+
c (float) : magnitude
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
float or numpy.ndarray: y
|
|
49
|
+
"""
|
|
50
|
+
return 1 + c*np.exp(-x/m)
|
|
51
|
+
|
|
52
|
+
def weathering_model(wetdryN_P,k,w0,tau0):
|
|
53
|
+
r"""
|
|
54
|
+
Shifted exponential decay weathering model:
|
|
55
|
+
:math:`w = 1 + w_0(\\tau+\\tau_0)\exp(-k\chi)`
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
wetdryN_P (numpy.ndarray) : pair :math:`(\\tau,\chi)`
|
|
59
|
+
k (float) : reciprocal e-folding scale :math:`k`
|
|
60
|
+
w0 (float) : magnitude :math:`w_0`
|
|
61
|
+
tau0 (float) : time offset :math:`\\tau_0`
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
float: y
|
|
65
|
+
"""
|
|
66
|
+
tau = wetdryN_P[0]
|
|
67
|
+
chi = wetdryN_P[1]
|
|
68
|
+
return 1 + w0*(tau+tau0)*np.exp(-k*chi)
|
|
69
|
+
|
|
70
|
+
class ExptData:
|
|
71
|
+
"""
|
|
72
|
+
Rock weathering experimental data import and modeling
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
def __init__(self):
|
|
76
|
+
"""
|
|
77
|
+
Initialize class instance.
|
|
78
|
+
|
|
79
|
+
Attributes:
|
|
80
|
+
ddict (:obj:`dict`) : data dictionary, to contain experimental results of
|
|
81
|
+
`Inoue et al (2017)`_ and `Li et al (2016)`_ variously on
|
|
82
|
+
rock tensile strength, rock compressive strength,
|
|
83
|
+
erodibility, number of wetting/drying cycles, and confining pressure
|
|
84
|
+
fdict (:obj:`dict`) : 1d model dictionary, to contain regression fits of
|
|
85
|
+
weathering model(s) to experimental data
|
|
86
|
+
sdict (:obj:`dict`) : 2d model dictionary, to contain regression fits of
|
|
87
|
+
2d weathering model to experimental data
|
|
88
|
+
"""
|
|
89
|
+
self.ddict = dict()
|
|
90
|
+
self.fdict = dict()
|
|
91
|
+
self.sdict = dict()
|
|
92
|
+
|
|
93
|
+
def read_excel(self, data_set,
|
|
94
|
+
dir_name=("..","data"), file_name="Inoue_wetdryN_sigmaT",
|
|
95
|
+
header=0, skiprows=[1]):
|
|
96
|
+
"""
|
|
97
|
+
Read data from an Excel file into a :mod:`pandas` dataframe
|
|
98
|
+
|
|
99
|
+
Attributes:
|
|
100
|
+
data_set (:obj:`str`) : name of dataset in data dictionary
|
|
101
|
+
dir_name (:obj:`list`) : data source directory as platform-indepedent list
|
|
102
|
+
file_name (:obj:`str`) : data source filename
|
|
103
|
+
header (:obj:`int`) : number of header (column title etc) rows
|
|
104
|
+
skiprows (:obj:`int`) : number of rows to skip when creating dataframe
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
dir_name = os.path.join(*dir_name)
|
|
108
|
+
if not os.path.exists(dir_name):
|
|
109
|
+
print("Cannot find data directory")
|
|
110
|
+
raise
|
|
111
|
+
try:
|
|
112
|
+
df = pd.read_excel(os.path.join(dir_name,file_name+".xlsx"),
|
|
113
|
+
header=header, skiprows=skiprows)
|
|
114
|
+
except OSError:
|
|
115
|
+
print("Cannot find data directory")
|
|
116
|
+
raise
|
|
117
|
+
except:
|
|
118
|
+
raise
|
|
119
|
+
self.ddict.update({data_set:df})
|
|
120
|
+
|
|
121
|
+
def fit_linear_model(self, data_set, x_name, y_name, select=None):
|
|
122
|
+
"""
|
|
123
|
+
Regress a 1d model against experimental data.
|
|
124
|
+
|
|
125
|
+
Perform a linear regression fit of a linear model to the given
|
|
126
|
+
experimental data, such as modeling the degree of rock weakness
|
|
127
|
+
as a linear function of the number of wetting and drying cycles.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
data_set (str) : which experimental dataset,
|
|
131
|
+
as key to ddict element :class:`pandas.DataFrame`
|
|
132
|
+
x_name (numpy.ndarray) : abscissa :math:`x`
|
|
133
|
+
y_name (numpy.ndarray) : ordinate :math:`y`
|
|
134
|
+
select (str) : which computation of weakness from rock strength
|
|
135
|
+
|
|
136
|
+
Attributes:
|
|
137
|
+
fdict[data_set] or fdict[selection_name] (:obj:`dict` element) :
|
|
138
|
+
model fit
|
|
139
|
+
w_s2_means (:class:`numpy.ndarray`) :
|
|
140
|
+
mean values of weakness :math:`w`
|
|
141
|
+
w_s2_stds (:class:`numpy.ndarray`) :
|
|
142
|
+
standard deviations of weakness :math:`w`
|
|
143
|
+
"""
|
|
144
|
+
df = self.ddict[data_set]
|
|
145
|
+
if select is not None:
|
|
146
|
+
for selection in np.unique(self.ddict[data_set][select]):
|
|
147
|
+
selection_name = "{0}_{1}_{2}".format(data_set,select,selection)
|
|
148
|
+
x = df.loc[df[select]==selection][x_name]
|
|
149
|
+
y = df.loc[df[select]==selection][y_name]
|
|
150
|
+
self.fdict[selection_name] = curve_fit(linear_model, x, y)
|
|
151
|
+
else:
|
|
152
|
+
x = df[x_name]
|
|
153
|
+
y = df[y_name]
|
|
154
|
+
self.fdict[data_set] = curve_fit(linear_model, x, y)
|
|
155
|
+
|
|
156
|
+
self.w_s2_means = self.ddict[data_set].groupby("wetdryN").mean()["w_sigma2"]
|
|
157
|
+
self.w_s2_stds = self.ddict[data_set].groupby("wetdryN").std()[ "w_sigma2"]
|
|
158
|
+
|
|
159
|
+
def fit_weathering_model(self, data_set, select):
|
|
160
|
+
"""
|
|
161
|
+
Regress a 2d model against experimental data.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
data_set (:obj:`str`) : which experimental dataset,
|
|
165
|
+
as key to ddict element :class:`pandas.DataFrame`
|
|
166
|
+
select (:obj:`str`) : which computation of weakness from rock strength
|
|
167
|
+
|
|
168
|
+
Attributes:
|
|
169
|
+
fdict[data_set] (:obj:`list`) : model surface fit as
|
|
170
|
+
meshgrid X=wet/dry :math:`N`, Y=confining pressure :math:`P`
|
|
171
|
+
and corresponding surface Z[X,Y] as list (X,Y,Z)
|
|
172
|
+
sdict[data_set] (:obj:`list`) : model surface estimates at experimental values as
|
|
173
|
+
meshgrid X=wet/dry :math:`N`, Y=confining pressure :math:`P`
|
|
174
|
+
and corresponding surface Z[X,Y] as list (X,Y,Z)
|
|
175
|
+
w_s2normed_means (:class:`numpy.ndarray`) :
|
|
176
|
+
mean values of model-normed weakness :math:`w`
|
|
177
|
+
w_s2normed_stds (:class:`numpy.ndarray`) :
|
|
178
|
+
standard deviations of :math:`w`
|
|
179
|
+
|
|
180
|
+
"""
|
|
181
|
+
df = self.ddict[data_set]
|
|
182
|
+
wdN_vec = df.wetdryN
|
|
183
|
+
P_vec = df.P
|
|
184
|
+
sig_vec = df.sigmaC/180
|
|
185
|
+
w_vec = sig_vec**(-2)
|
|
186
|
+
wdN_P_array = np.vstack((wdN_vec,P_vec))
|
|
187
|
+
model_fit = curve_fit(weathering_model, wdN_P_array, w_vec)
|
|
188
|
+
self.fdict[data_set] = model_fit
|
|
189
|
+
|
|
190
|
+
n_pts = 30
|
|
191
|
+
X = np.linspace(0, wdN_vec.max()*1.1, n_pts)
|
|
192
|
+
Y = np.linspace(0, P_vec.max()*1.1, n_pts)
|
|
193
|
+
X,Y = np.meshgrid(X, Y)
|
|
194
|
+
X_Y_array = np.vstack((X.reshape(n_pts**2), Y.reshape(n_pts**2)))
|
|
195
|
+
Z = weathering_model(X_Y_array,*model_fit[0]).reshape(n_pts,n_pts)
|
|
196
|
+
self.fdict[data_set+"_"+select+"_surface"] = (X,Y,Z)
|
|
197
|
+
|
|
198
|
+
P_vec = np.linspace(0,P_vec.max()*1.3)
|
|
199
|
+
X,Y = np.meshgrid(np.unique(wdN_vec), P_vec)
|
|
200
|
+
X_Y_array = np.vstack((X.reshape(X.shape[0]*Y.shape[1]),
|
|
201
|
+
Y.reshape(X.shape[0]*Y.shape[1])))
|
|
202
|
+
self.sdict[data_set] \
|
|
203
|
+
= X[0], Y[:,0], \
|
|
204
|
+
weathering_model(X_Y_array,*model_fit[0]).reshape(X.shape[0],Y.shape[1])
|
|
205
|
+
|
|
206
|
+
pd.options.mode.chained_assignment = None
|
|
207
|
+
w_ref_vec = (self.sdict[data_set][2].T.copy())[:,0]-1
|
|
208
|
+
df["w_s2normed"] = 0.0
|
|
209
|
+
for idx,wetdryN in enumerate(np.unique(df.wetdryN)):
|
|
210
|
+
w__ = df.w_sigma2[df.wetdryN==wetdryN].copy()
|
|
211
|
+
w_normed = (w__-1)/w_ref_vec[idx]+1
|
|
212
|
+
# df["w_s2normed"][(idx*3):(idx*3+3)] = w_normed
|
|
213
|
+
# Updated 2024/05/29
|
|
214
|
+
df.loc[(idx*3):(idx*3+3),"w_s2normed"] = w_normed #.astype("float")
|
|
215
|
+
|
|
216
|
+
self.w_s2normed_means = self.ddict["li"].groupby("P").mean()["w_s2normed"]
|
|
217
|
+
self.w_s2normed_stds = self.ddict["li"].groupby("P").std()["w_s2normed"]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
wmbe/initialize.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configure to run in `IPython`_.
|
|
3
|
+
|
|
4
|
+
---------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
Sets up `IPython`_ environment if e.g. we're running
|
|
7
|
+
in a `Jupyter notebook`_ or `Jupyter QtConsole`_.
|
|
8
|
+
|
|
9
|
+
- prepares Matplotlib to display inline and (for Macs)
|
|
10
|
+
at a 'retina' resolution -- if this
|
|
11
|
+
is not available, a benign error report (currently disabled)
|
|
12
|
+
is made and progress continues
|
|
13
|
+
- enables automatic reloading (in case the code has been modded) when
|
|
14
|
+
a notebook is re-run in-situ
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"initialize",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def initialize() -> None:
|
|
22
|
+
try:
|
|
23
|
+
from IPython import get_ipython #type: ignore
|
|
24
|
+
|
|
25
|
+
def check_is_ipython() -> bool:
|
|
26
|
+
"""Check if we are running an IPython kernel from Jupyter etc."""
|
|
27
|
+
try:
|
|
28
|
+
if "IPKernelApp" not in get_ipython().config: # pragma: no cover
|
|
29
|
+
return False
|
|
30
|
+
except ImportError:
|
|
31
|
+
return False
|
|
32
|
+
except AttributeError:
|
|
33
|
+
return False
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
is_python: bool = check_is_ipython()
|
|
38
|
+
|
|
39
|
+
if is_python:
|
|
40
|
+
try:
|
|
41
|
+
get_ipython().run_line_magic(
|
|
42
|
+
"config",
|
|
43
|
+
"InlineBackend.figure_format = 'retina'",
|
|
44
|
+
)
|
|
45
|
+
except NameError:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
get_ipython().run_line_magic("matplotlib", "inline",)
|
|
50
|
+
except NameError:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
get_ipython().run_line_magic("load_ext", "autoreload",)
|
|
55
|
+
get_ipython().run_line_magic("autoreload", "2",)
|
|
56
|
+
except NameError as error:
|
|
57
|
+
print(
|
|
58
|
+
"Error trying to invoke get_ipython(), "
|
|
59
|
+
+ "possibly because not running IPython:",
|
|
60
|
+
error,
|
|
61
|
+
)
|
|
62
|
+
except:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
initialize()
|