tilupy 0.1.4__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tilupy might be problematic. Click here for more details.
- tilupy/calibration.py +29 -32
- tilupy/cmd.py +114 -60
- tilupy/initdata.py +36 -0
- tilupy/make_mass.py +108 -0
- tilupy/make_topo.py +415 -0
- tilupy/models/shaltop/initsimus.py +168 -55
- tilupy/models/shaltop/read.py +319 -164
- tilupy/notations.py +332 -47
- tilupy/plot.py +472 -175
- tilupy/read.py +817 -235
- tilupy/utils.py +99 -71
- {tilupy-0.1.4.dist-info → tilupy-1.0.0.dist-info}/METADATA +3 -2
- tilupy-1.0.0.dist-info/RECORD +26 -0
- {tilupy-0.1.4.dist-info → tilupy-1.0.0.dist-info}/WHEEL +1 -1
- tilupy-0.1.4.dist-info/RECORD +0 -24
- {tilupy-0.1.4.dist-info → tilupy-1.0.0.dist-info}/LICENSE +0 -0
- {tilupy-0.1.4.dist-info → tilupy-1.0.0.dist-info}/entry_points.txt +0 -0
- {tilupy-0.1.4.dist-info → tilupy-1.0.0.dist-info}/top_level.txt +0 -0
tilupy/make_topo.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on Fri Aug 4 12:09:41 2023
|
|
4
|
+
|
|
5
|
+
@author: peruzzetto
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import scipy
|
|
10
|
+
|
|
11
|
+
import tilupy.plot
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def gray99(nx=None, ny=None, xmin=-0.4, x1=1.75, x2=2.15,
|
|
15
|
+
xmax=3.2, ymax=0.5, R=1.1,
|
|
16
|
+
theta1=40, theta2=0, maxz=None, dx=0.01, dy=0.01, plot=False):
|
|
17
|
+
"""
|
|
18
|
+
Construct channel as in Gray et all 99.
|
|
19
|
+
|
|
20
|
+
Input coordinates are in curvilinear coordinates along the reference
|
|
21
|
+
topography following the channel bottom. Output coordinates are in the
|
|
22
|
+
fixed cartesian frame.
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
nx : int
|
|
26
|
+
Size of the grid in x direction
|
|
27
|
+
ny : int
|
|
28
|
+
Size of the y direction
|
|
29
|
+
dx : float, optional
|
|
30
|
+
Cell size of the x axis. if specified, nx is recomputed. Default: 0.01
|
|
31
|
+
dy : float, optional
|
|
32
|
+
Cell size of the y axis. if specified, ny is recomputed. Default: 0.01
|
|
33
|
+
xmin : float, optional
|
|
34
|
+
Minimum x coordinate. The default is -0.4.
|
|
35
|
+
x1 : float, optional
|
|
36
|
+
Min coordinate of the channel outlet (transition zone).
|
|
37
|
+
The default is 1.75.
|
|
38
|
+
x2 : float, optional
|
|
39
|
+
Max coordinate of the channel outlet (transition zone).
|
|
40
|
+
The default is 2.15.
|
|
41
|
+
xmax : float, optional
|
|
42
|
+
Maximum x coordinate. The default is 3.2.
|
|
43
|
+
ymax : float, optional
|
|
44
|
+
Maximum y coordinate the final yxais spans from -ymax to xmax.
|
|
45
|
+
The default is 0.5.
|
|
46
|
+
R : float, optional
|
|
47
|
+
Radius of curvature of the channel. The default is 1.1.
|
|
48
|
+
theta1 : float, optional
|
|
49
|
+
Slope of the channel in degree. The default is 40.
|
|
50
|
+
theta2 : float, optional
|
|
51
|
+
Slope after the channel. The default is 0.
|
|
52
|
+
maxz : float, optional
|
|
53
|
+
Maximum z coordinate. The default is None.
|
|
54
|
+
plot : boolean, optional
|
|
55
|
+
Plot result. The default is False.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
Xout : float nx*ny array
|
|
61
|
+
Mesh of X coordinates in the cartesian frame
|
|
62
|
+
Yout : float nx*ny array
|
|
63
|
+
Mesh of Y coordinates in the cartesian frame.
|
|
64
|
+
Zout : float nx*ny array
|
|
65
|
+
Mesh of Z coordinates in the cartesian frame.
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
theta1 = np.deg2rad(theta1)
|
|
69
|
+
theta2 = np.deg2rad(theta2)
|
|
70
|
+
|
|
71
|
+
if nx is None:
|
|
72
|
+
x = np.arange(xmin, xmax+dx/2, dx)
|
|
73
|
+
nx = len(x)
|
|
74
|
+
else:
|
|
75
|
+
x = np.linspace(xmin, xmax, nx)
|
|
76
|
+
|
|
77
|
+
if ny is None:
|
|
78
|
+
y = np.arange(-ymax, ymax+dy/2, dy)
|
|
79
|
+
ny = len(y)
|
|
80
|
+
else:
|
|
81
|
+
y = np.linspace(-ymax, ymax, ny)
|
|
82
|
+
|
|
83
|
+
ycurv = np.tile(y.reshape((1, ny)), (nx, 1))
|
|
84
|
+
|
|
85
|
+
# Superficial topography : channel
|
|
86
|
+
# alpha=1/(2*R)*np.sin(0.5*np.pi*(x-x2)/(x1-x2))**2
|
|
87
|
+
# alpha=1/(2*R)*np.abs(x-x2)**1/np.abs(x1-x2)**1
|
|
88
|
+
alpha = 1/(2*R)*(3*((x-x2)/(x1-x2))**2-2*((x-x2)/(x1-x2))**3)
|
|
89
|
+
alpha[x > x2] = 0
|
|
90
|
+
alpha[x < x1] = 1/(2*R)
|
|
91
|
+
alpha = np.tile(alpha.reshape((nx, 1)), (1, ny))
|
|
92
|
+
|
|
93
|
+
zchannel = alpha*np.abs(ycurv)**2
|
|
94
|
+
# plt.figure()
|
|
95
|
+
# plt.imshow(zchannel)
|
|
96
|
+
|
|
97
|
+
# del alpha
|
|
98
|
+
|
|
99
|
+
if not maxz:
|
|
100
|
+
maxz = R/2
|
|
101
|
+
|
|
102
|
+
zchannel[zchannel > maxz] = maxz
|
|
103
|
+
|
|
104
|
+
# Base topography in curvilinear system.
|
|
105
|
+
# The transition zone between x1 and x2 is a cylindre
|
|
106
|
+
zbase = -np.sin(theta2)*(x-xmax)
|
|
107
|
+
|
|
108
|
+
ind = (x <= x2) & (x >= x1)
|
|
109
|
+
angle = (x[ind]-x1)/(x2-x1)*(theta2-theta1)+theta1
|
|
110
|
+
R2 = (x2-x1)/(theta1-theta2)
|
|
111
|
+
z2 = -np.sin(theta2)*(x2-xmax)
|
|
112
|
+
zbase[ind] = R2*(1-np.cos(angle))-R2*(1-np.cos(theta2))+z2
|
|
113
|
+
|
|
114
|
+
ind = x <= x1
|
|
115
|
+
z1 = R2*(1-np.cos(theta1))-R2*(1-np.cos(theta2))+z2
|
|
116
|
+
zbase[ind] = -np.sin(theta1)*(x[ind]-x1)+z1
|
|
117
|
+
zbase = np.tile(zbase.reshape((nx, 1)), (1, ny))
|
|
118
|
+
|
|
119
|
+
# Conversion in fixed cartesian frame
|
|
120
|
+
zd = np.gradient(zbase, x[1]-x[0], edge_order=2, axis=0)
|
|
121
|
+
Xd = np.sqrt(1-zd**2)
|
|
122
|
+
X = scipy.integrate.cumtrapz(Xd, x, axis=0, initial=0)
|
|
123
|
+
X = X+xmin*np.cos(theta1)
|
|
124
|
+
|
|
125
|
+
# plt.figure()
|
|
126
|
+
# plt.plot(X[:,0])
|
|
127
|
+
|
|
128
|
+
del Xd
|
|
129
|
+
|
|
130
|
+
# Topography conversion in fixed cartesian frame
|
|
131
|
+
[Fx, Fy] = np.gradient(zbase, X[:, 0], ycurv[0, :], edge_order=2)
|
|
132
|
+
Fz = np.ones(zbase.shape)
|
|
133
|
+
costh = 1/np.sqrt(Fx**2+Fy**2+1) # Slope angle
|
|
134
|
+
Fx = -Fx*costh
|
|
135
|
+
Fy = -Fy*costh
|
|
136
|
+
Fz = Fz*costh
|
|
137
|
+
Z = zbase+zchannel*Fz
|
|
138
|
+
Xmesh = X+zchannel*Fx
|
|
139
|
+
Ymesh = ycurv+zchannel*Fy
|
|
140
|
+
|
|
141
|
+
# Reconstruction of regular cartesian mesh
|
|
142
|
+
Xout = np.linspace(Xmesh.min(), Xmesh.max(), nx)
|
|
143
|
+
Xout = np.tile(Xout.reshape((nx, 1)), (1, ny))
|
|
144
|
+
Yout = np.linspace(Ymesh.min(), Ymesh.max(), ny)
|
|
145
|
+
Yout = np.tile(Yout.reshape((1, ny)), (nx, 1))
|
|
146
|
+
Zout = scipy.interpolate.griddata((Xmesh.reshape(nx*ny),
|
|
147
|
+
Ymesh.reshape(nx*ny)),
|
|
148
|
+
Z.reshape(nx*ny),
|
|
149
|
+
(Xout, Yout), method='cubic')
|
|
150
|
+
Ztmp = scipy.interpolate.griddata((Xmesh.reshape(nx*ny),
|
|
151
|
+
Ymesh.reshape(nx*ny)),
|
|
152
|
+
Z.reshape(nx*ny),
|
|
153
|
+
(Xout, Yout), method='nearest')
|
|
154
|
+
ind = np.isnan(Zout)
|
|
155
|
+
Zout[ind] = Ztmp[ind]
|
|
156
|
+
|
|
157
|
+
del Ztmp
|
|
158
|
+
# fz=scipy.interpolate.Rbf(Xmesh,Ymesh,Z)
|
|
159
|
+
# Zout=fz(Xout,Yout)
|
|
160
|
+
|
|
161
|
+
if plot:
|
|
162
|
+
if theta2 == 0:
|
|
163
|
+
blod, thin = tilupy.plot.get_contour_intervals(np.nanmin(Zout),
|
|
164
|
+
np.nanmax(Zout))
|
|
165
|
+
level_min = thin
|
|
166
|
+
else:
|
|
167
|
+
level_min = None
|
|
168
|
+
tilupy.plot.plot_topo(Zout.T, Xout[:, 1], Yout[1, :],
|
|
169
|
+
level_min=level_min)
|
|
170
|
+
|
|
171
|
+
return Xout[:, 1], Yout[1, :], Zout.T
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def channel(nx=None, ny=None, dx=None, dy=None,
|
|
175
|
+
xmin=-0.4, xmax=3.6, ymax=0.5, xstart_channel=0.65, xend_channel=2.3,
|
|
176
|
+
xstart_trans=0.4, xend_trans=2.75,
|
|
177
|
+
R=1.1, bend=0.2, nbends=1,
|
|
178
|
+
theta_start=40, theta_channel=40, theta_end=0,
|
|
179
|
+
plot=False, maxh=None, interp_method='linear'):
|
|
180
|
+
"""
|
|
181
|
+
Generate channel with potential multiple bends. Input coordinates are
|
|
182
|
+
curvilinear along the flattened topography.
|
|
183
|
+
|
|
184
|
+
Parameters
|
|
185
|
+
----------
|
|
186
|
+
nx : int
|
|
187
|
+
Size of the grid in x direction
|
|
188
|
+
ny : int
|
|
189
|
+
Size of the y direction
|
|
190
|
+
dx : float, optional
|
|
191
|
+
Cell size of the x axis. if specified, nx is recomputed. Default: 0.01
|
|
192
|
+
dy : float, optional
|
|
193
|
+
Cell size of the y axis. if specified, ny is recomputed. Default: 0.01
|
|
194
|
+
xmin : float, optional
|
|
195
|
+
Minimum x coordinate. The default is -0.4.
|
|
196
|
+
xmax : float, optional
|
|
197
|
+
Maximum x coordinate. The default is 3.2.
|
|
198
|
+
ymax : float, optional
|
|
199
|
+
Maximum y coordinate the final yxais spans from -ymax to xmax.
|
|
200
|
+
The default is 0.5.
|
|
201
|
+
xstart_channel : float, optional
|
|
202
|
+
Start of the channel. The default is 0.65.
|
|
203
|
+
xend_channel : float, optional
|
|
204
|
+
end of the channel. The default is 2.3.
|
|
205
|
+
xstart_trans : TYPE, optional
|
|
206
|
+
start of the transition zone before the channel start.
|
|
207
|
+
The default is 0.4.
|
|
208
|
+
xend_trans : TYPE, optional
|
|
209
|
+
End of the transition zone after the channel end. The default is 2.75.
|
|
210
|
+
R : float, optional
|
|
211
|
+
Radius of curvature of the channel. The default is 1.1.
|
|
212
|
+
bend : float, optional
|
|
213
|
+
Width of the channel bend. The default is 0.2.
|
|
214
|
+
nbends : ind, optional
|
|
215
|
+
Number of bends. The default is 1.
|
|
216
|
+
theta_start : float, optional
|
|
217
|
+
Slope before the channel. The default is 40.
|
|
218
|
+
theta_channel : float, optional
|
|
219
|
+
Slope of the channel. The default is 40.
|
|
220
|
+
theta_end : float, optional
|
|
221
|
+
Slope after the channel. The default is 0.
|
|
222
|
+
plot : bool, optional
|
|
223
|
+
Plot generated topography. The default is False.
|
|
224
|
+
maxh : float, optional
|
|
225
|
+
Depth of the channel. The default is None.
|
|
226
|
+
interp_method : string, optional
|
|
227
|
+
Interpolation method for converting the topography from curvilinear
|
|
228
|
+
coordinates to cartesian coordinates. The default is 'linear'.
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
TYPE
|
|
233
|
+
DESCRIPTION.
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
theta_start = np.deg2rad(theta_start)
|
|
238
|
+
theta_channel = np.deg2rad(theta_channel)
|
|
239
|
+
theta_end = np.deg2rad(theta_end)
|
|
240
|
+
|
|
241
|
+
if ny is None and dy is None:
|
|
242
|
+
dy = ymax/100
|
|
243
|
+
|
|
244
|
+
if nx is None and dx is None:
|
|
245
|
+
if dy is not None:
|
|
246
|
+
dx = dy
|
|
247
|
+
else:
|
|
248
|
+
raise ValueError('nx or dx must be specified as input')
|
|
249
|
+
|
|
250
|
+
# x and y coordinates in the flattened topography
|
|
251
|
+
if nx is None:
|
|
252
|
+
xtopo = np.arange(xmin, xmax+dx/2, dx)
|
|
253
|
+
nx = len(xtopo)
|
|
254
|
+
else:
|
|
255
|
+
xtopo = np.linspace(xmin, xmax, nx)
|
|
256
|
+
|
|
257
|
+
if ny is None:
|
|
258
|
+
ytopo = np.arange(-ymax, ymax+dy/2, dy)
|
|
259
|
+
ny = len(ytopo)
|
|
260
|
+
else:
|
|
261
|
+
ytopo = np.linspace(-ymax, ymax, ny)
|
|
262
|
+
|
|
263
|
+
xtopo = np.tile(xtopo[:, np.newaxis], (1, ny))
|
|
264
|
+
ytopo = np.tile(ytopo[np.newaxis, :], (nx, 1))
|
|
265
|
+
|
|
266
|
+
# Height above flattened topography is a channel
|
|
267
|
+
# in alpha(x)*(y-thalweg(x))**2,
|
|
268
|
+
|
|
269
|
+
# alpha(x) is 1/2R in the channel, and depends on a transition
|
|
270
|
+
# function in the transition zones
|
|
271
|
+
def trans_function(x, x1, x2):
|
|
272
|
+
xx = 3*((x-x2)/(x1-x2))**2-2*((x-x2)/(x1-x2))**3
|
|
273
|
+
return xx
|
|
274
|
+
alpha = np.zeros((nx, ny))
|
|
275
|
+
ind = (xtopo > xstart_channel) & (xtopo < xend_channel)
|
|
276
|
+
alpha[ind] = 1/(2*R)
|
|
277
|
+
ind = (xtopo > xstart_trans) & (xtopo <= xstart_channel)
|
|
278
|
+
alpha[ind] = 1/(2*R)*trans_function(xtopo[ind],
|
|
279
|
+
xstart_channel, xstart_trans)
|
|
280
|
+
ind = (xtopo > xend_channel) & (xtopo <= xend_trans)
|
|
281
|
+
alpha[ind] = 1/(2*R)*trans_function(xtopo[ind], xend_channel, xend_trans)
|
|
282
|
+
|
|
283
|
+
# the thalweg is centered on y=0 outside [xstart_channel,xend_channel]. Inbetween,
|
|
284
|
+
# it is given by a cos**2
|
|
285
|
+
def end_bend(x, x1, x2):
|
|
286
|
+
yy = (bend/2)*(1+np.cos(np.pi*(x-x2)/(x1-x2)))
|
|
287
|
+
return yy
|
|
288
|
+
|
|
289
|
+
def mid_bend(x, x1, x2):
|
|
290
|
+
yy = bend*np.cos(np.pi*(x-x1)/(x2-x1))
|
|
291
|
+
return yy
|
|
292
|
+
thalweg = np.zeros((nx, ny))
|
|
293
|
+
|
|
294
|
+
if nbends > 0:
|
|
295
|
+
step = (xend_channel-xstart_channel)/nbends
|
|
296
|
+
|
|
297
|
+
ind = (xtopo > xstart_channel) & (xtopo < xstart_channel+step/2)
|
|
298
|
+
thalweg[ind] = end_bend(
|
|
299
|
+
xtopo[ind], xstart_channel, xstart_channel+step/2)
|
|
300
|
+
ind = (xtopo >= xend_channel-step/2) & (xtopo < xend_channel)
|
|
301
|
+
thalweg[ind] = (-1)**(nbends+1)*end_bend(xtopo[ind],
|
|
302
|
+
xend_channel, xend_channel-step/2)
|
|
303
|
+
if nbends > 1:
|
|
304
|
+
ind = (xtopo >= xstart_channel+step /
|
|
305
|
+
2) & (xtopo < xend_channel-step/2)
|
|
306
|
+
thalweg[ind] = mid_bend(
|
|
307
|
+
xtopo[ind], xstart_channel+step/2, xstart_channel+(3/2)*step)
|
|
308
|
+
|
|
309
|
+
htopo = alpha*(ytopo-thalweg)**2
|
|
310
|
+
|
|
311
|
+
if not maxh:
|
|
312
|
+
maxh = R/2
|
|
313
|
+
|
|
314
|
+
htopo[htopo > maxh] = maxh
|
|
315
|
+
|
|
316
|
+
# Reconstruction of bz the basal topography. The real topo is given by
|
|
317
|
+
# bz+\vec{n}*htopo. Slopes of bz are given by theta_* outside the transition
|
|
318
|
+
# zones. We use a cylinder shape inbetween. This is done by computing the slope
|
|
319
|
+
# angle of bz, and using then -sin(slope_angle)=d(bz)/d(xtopo)
|
|
320
|
+
|
|
321
|
+
slope_angle = np.zeros((nx, ny))
|
|
322
|
+
ind = xtopo < xstart_trans
|
|
323
|
+
slope_angle[ind] = theta_start
|
|
324
|
+
ind = xtopo >= xend_trans
|
|
325
|
+
slope_angle[ind] = theta_end
|
|
326
|
+
ind = (xtopo >= xstart_channel) & (xtopo < xend_channel)
|
|
327
|
+
slope_angle[ind] = theta_channel
|
|
328
|
+
|
|
329
|
+
ind = (xtopo >= xstart_trans) & (xtopo < xstart_channel)
|
|
330
|
+
slope_angle[ind] = (xtopo[ind]-xstart_trans)/(xstart_channel -
|
|
331
|
+
xstart_trans)
|
|
332
|
+
slope_angle[ind] = slope_angle[ind]*(theta_channel-theta_start)+theta_start
|
|
333
|
+
|
|
334
|
+
ind = (xtopo >= xend_channel) & (xtopo < xend_trans)
|
|
335
|
+
slope_angle[ind] = (xtopo[ind]-xend_trans)/(xend_channel -
|
|
336
|
+
xend_trans)
|
|
337
|
+
slope_angle[ind] = slope_angle[ind]*(theta_channel-theta_end)+theta_end
|
|
338
|
+
|
|
339
|
+
bz = scipy.integrate.cumtrapz(-np.sin(slope_angle),
|
|
340
|
+
xtopo, axis=0, initial=0)
|
|
341
|
+
bz = bz-np.min(bz)
|
|
342
|
+
|
|
343
|
+
# Get the coordinates of (xtopo,ytopo) in the cartesian reference frame
|
|
344
|
+
# by=ytopo
|
|
345
|
+
bx = scipy.integrate.cumtrapz(
|
|
346
|
+
np.cos(slope_angle), xtopo, axis=0, initial=0)
|
|
347
|
+
bx = bx+xmin*np.cos(theta_start)
|
|
348
|
+
|
|
349
|
+
# Vector normal to topography in cartesian coordinates
|
|
350
|
+
# (nx,ny,nz)=(-sin(theta),0,cos(theta))
|
|
351
|
+
# as the topography does vary in the y direction
|
|
352
|
+
# The real topography is thus given in cartesian coordinates by
|
|
353
|
+
# (xcart,ycart,zcart)=(bx,by,bz)+htopo(nx,ny,nz)
|
|
354
|
+
xcart = bx+htopo*np.sin(slope_angle)
|
|
355
|
+
zcart = bz+htopo*np.cos(slope_angle)
|
|
356
|
+
|
|
357
|
+
# Reconstruct regular mesh for interpolation
|
|
358
|
+
Xout = np.linspace(xcart[0, 0], xcart[-1, 0], nx)
|
|
359
|
+
Yout = ytopo
|
|
360
|
+
Xout = np.tile(Xout[:, np.newaxis], (1, ny))
|
|
361
|
+
Zout = scipy.interpolate.griddata((xcart.reshape(nx*ny),
|
|
362
|
+
ytopo.reshape(nx*ny)),
|
|
363
|
+
zcart.reshape(nx*ny),
|
|
364
|
+
(Xout, Yout), method=interp_method)
|
|
365
|
+
Ztmp = scipy.interpolate.griddata((xcart.reshape(nx*ny),
|
|
366
|
+
ytopo.reshape(nx*ny)),
|
|
367
|
+
zcart.reshape(nx*ny),
|
|
368
|
+
(Xout, Yout), method='nearest')
|
|
369
|
+
ind = np.isnan(Zout)
|
|
370
|
+
Zout[ind] = Ztmp[ind]
|
|
371
|
+
|
|
372
|
+
if plot:
|
|
373
|
+
if theta_end == 0:
|
|
374
|
+
blod, thin = tilupy.plot.get_contour_intervals(np.nanmin(Zout),
|
|
375
|
+
np.nanmax(Zout))
|
|
376
|
+
level_min = thin
|
|
377
|
+
else:
|
|
378
|
+
level_min = None
|
|
379
|
+
tilupy.plot.plot_topo(Zout.T, Xout[:, 1], Yout[1, :],
|
|
380
|
+
level_min=level_min)
|
|
381
|
+
|
|
382
|
+
return Xout[:, 1], Yout[1, :], Zout.T, thalweg
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
if __name__ == '__main__':
|
|
386
|
+
|
|
387
|
+
# %% Test gray99
|
|
388
|
+
X, Y, Z = gray99(plot=True)
|
|
389
|
+
|
|
390
|
+
# %% Test synthetic channel
|
|
391
|
+
bend = 0.25
|
|
392
|
+
R = 0.2
|
|
393
|
+
|
|
394
|
+
nx = 600
|
|
395
|
+
ny = 300
|
|
396
|
+
|
|
397
|
+
xmin = 0.1
|
|
398
|
+
xmax = 4.5
|
|
399
|
+
xstart_trans = -0.3
|
|
400
|
+
xstart_channel = 0.2
|
|
401
|
+
xend_channel = 2.3
|
|
402
|
+
xend_trans = 2.75
|
|
403
|
+
ymax = 1
|
|
404
|
+
|
|
405
|
+
theta_start = 10
|
|
406
|
+
theta_channel = 10
|
|
407
|
+
theta_end = 0
|
|
408
|
+
x, y, z, t = channel(nx, ny, xmin=xmin, xmax=xmax, ymax=ymax,
|
|
409
|
+
xstart_channel=xstart_channel,
|
|
410
|
+
xend_channel=xend_channel,
|
|
411
|
+
xstart_trans=xstart_trans,
|
|
412
|
+
theta_start=theta_start,
|
|
413
|
+
theta_end=theta_end,
|
|
414
|
+
theta_channel=theta_channel,
|
|
415
|
+
R=R, bend=bend, maxh=R, plot=True)
|
|
@@ -7,24 +7,23 @@ Created on Tue May 25 15:18:31 2021
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import os
|
|
10
|
+
import posixpath
|
|
10
11
|
import numpy as np
|
|
11
12
|
|
|
13
|
+
from tilupy.utils import format_path_linux
|
|
14
|
+
|
|
12
15
|
import tilupy.notations
|
|
13
16
|
import tilupy.raster
|
|
14
17
|
|
|
15
|
-
README_PARAM_MATCH = dict(tmax='tmax',
|
|
16
|
-
CFL='cflhyp',
|
|
17
|
-
h_min='eps0',
|
|
18
|
-
dt_im_output='dt_im')
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
README_PARAM_MATCH = dict(
|
|
20
|
+
tmax="tmax", CFL="cflhyp", h_min="eps0", dt_im_output="dt_im"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
SHALTOP_LAW_ID = dict(coulomb=1, voellmy=8, bingham=6, muI=7)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def write_params_file(params, directory=None,
|
|
27
|
-
file_name='params.txt'):
|
|
26
|
+
def write_params_file(params, directory=None, file_name="params.txt"):
|
|
28
27
|
"""
|
|
29
28
|
Write params file for shaltop simulations
|
|
30
29
|
|
|
@@ -47,32 +46,145 @@ def write_params_file(params, directory=None,
|
|
|
47
46
|
|
|
48
47
|
if directory is None:
|
|
49
48
|
directory = os.getcwd()
|
|
50
|
-
with open(os.path.join(directory, file_name),
|
|
49
|
+
with open(os.path.join(directory, file_name), "w") as file_params:
|
|
51
50
|
for name in params:
|
|
52
51
|
val = params[name]
|
|
53
|
-
if
|
|
54
|
-
file_params.write(
|
|
55
|
-
if
|
|
56
|
-
file_params.write(
|
|
57
|
-
if
|
|
58
|
-
file_params.write(
|
|
52
|
+
if isinstance(val, int) or isinstance(val, np.int64):
|
|
53
|
+
file_params.write("{:s} {:d}\n".format(name, val))
|
|
54
|
+
if isinstance(val, float) or isinstance(val, np.float64):
|
|
55
|
+
file_params.write("{:s} {:.8G}\n".format(name, val))
|
|
56
|
+
if isinstance(val, str):
|
|
57
|
+
file_params.write("{:s} {:s}\n".format(name, val))
|
|
58
|
+
|
|
59
59
|
|
|
60
60
|
def raster_to_shaltop_txtfile(file_in, file_out, folder_out=None):
|
|
61
|
-
|
|
62
61
|
if folder_out is not None:
|
|
63
62
|
file_out = os.path.join(folder_out, file_out)
|
|
64
|
-
|
|
63
|
+
|
|
65
64
|
x, y, rast = tilupy.raster.read_raster(file_in)
|
|
66
|
-
np.savetxt(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
np.savetxt(
|
|
66
|
+
file_out,
|
|
67
|
+
np.reshape(np.flip(rast, axis=0), (rast.size, 1)),
|
|
68
|
+
fmt="%.12G",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
res = dict(
|
|
72
|
+
x0=x[0], y0=y[0], dx=x[1] - x[0], dy=y[1] - y[0], nx=len(x), ny=len(y)
|
|
73
|
+
)
|
|
74
|
+
|
|
73
75
|
return res
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
def write_job_files(
|
|
79
|
+
dirs,
|
|
80
|
+
param_files,
|
|
81
|
+
file_job,
|
|
82
|
+
job_name,
|
|
83
|
+
max_time_hours=24,
|
|
84
|
+
ncores_per_node=6,
|
|
85
|
+
partitions="cpuall,data,datanew",
|
|
86
|
+
shaltop_file="shaltop",
|
|
87
|
+
folder_conf_in_job=None,
|
|
88
|
+
replace_path=None,
|
|
89
|
+
number_conf_file=True,
|
|
90
|
+
):
|
|
91
|
+
"""
|
|
92
|
+
Write job/conf files for slurm jobs. The conf contains all the commands
|
|
93
|
+
needed to run each simulation (one command per simulation).
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
dirs : list of string
|
|
98
|
+
list of paths where simus will be run.
|
|
99
|
+
param_files : list string
|
|
100
|
+
list of shaltop parameter files.
|
|
101
|
+
file_job : string
|
|
102
|
+
name of job file called by sbatch.
|
|
103
|
+
job_name : string
|
|
104
|
+
name of conf file used by file_job.
|
|
105
|
+
max_time_hours : int, optional
|
|
106
|
+
Maximum job duration in hours before stop. The default is 24.
|
|
107
|
+
ncores_per_node : int, optional
|
|
108
|
+
Number of cores per nodes. Used to know the number of nodes required
|
|
109
|
+
for the job. The default is 6.
|
|
110
|
+
partitions : string, optional
|
|
111
|
+
Names of partitions on which jobs can be launched.
|
|
112
|
+
The default is "cpuall,data,datanew".
|
|
113
|
+
shaltop_file : string, optional
|
|
114
|
+
Bash command used to call shaltop. Can be a path.
|
|
115
|
+
The default is "shaltop".
|
|
116
|
+
folder_conf_in_job : string, optional
|
|
117
|
+
Folder where the conf file is located. The default is the folder
|
|
118
|
+
path of file_job.
|
|
119
|
+
replace_path : list, optional
|
|
120
|
+
replace replace_path[0] by replace_path[1] for every path in dir. This
|
|
121
|
+
is used if simulations are prepared and run on two different machines
|
|
122
|
+
(e.g. laptop and cluster).
|
|
123
|
+
The default is None.
|
|
124
|
+
number_conf_file : bool, optional
|
|
125
|
+
If True, add a number in front of each line of the conf file. Required
|
|
126
|
+
to identify slurm jobs.
|
|
127
|
+
The default is True.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
None.
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
ntasks = len(dirs)
|
|
135
|
+
nnodes = int(np.ceil(ntasks / ncores_per_node))
|
|
136
|
+
|
|
137
|
+
if folder_conf_in_job is None:
|
|
138
|
+
folder_conf_in_job = os.path.dirname(file_job)
|
|
139
|
+
if folder_conf_in_job == "":
|
|
140
|
+
folder_conf_in_job = "."
|
|
141
|
+
|
|
142
|
+
with open(file_job + ".conf", "w", newline="\n") as conf_file:
|
|
143
|
+
if number_conf_file:
|
|
144
|
+
line = "{:d} {:s} {:s} {:s}\n"
|
|
145
|
+
else:
|
|
146
|
+
line = "{:s} {:s} {:s}\n"
|
|
147
|
+
for i in range(ntasks):
|
|
148
|
+
if replace_path is not None:
|
|
149
|
+
folder = dirs[i].replace(replace_path[0], replace_path[1])
|
|
150
|
+
param_file = param_files[i].replace(
|
|
151
|
+
replace_path[0], replace_path[1]
|
|
152
|
+
)
|
|
153
|
+
else:
|
|
154
|
+
folder = dirs[i]
|
|
155
|
+
param_file = param_files[i]
|
|
156
|
+
folder = format_path_linux(folder)
|
|
157
|
+
param_file = format_path_linux(param_file)
|
|
158
|
+
if number_conf_file:
|
|
159
|
+
line2 = line.format(i, shaltop_file, folder, param_file)
|
|
160
|
+
else:
|
|
161
|
+
line2 = line.format(shaltop_file, folder, param_file)
|
|
162
|
+
conf_file.write(line2)
|
|
163
|
+
|
|
164
|
+
n_hours = np.floor(max_time_hours)
|
|
165
|
+
n_min = (max_time_hours - n_hours) * 60
|
|
166
|
+
str_time = "{:02.0f}:{:02.0f}:00\n".format(n_hours, n_min)
|
|
167
|
+
|
|
168
|
+
basename = os.path.basename(file_job)
|
|
169
|
+
path_conf_in_job = posixpath.join(folder_conf_in_job, basename + ".conf")
|
|
170
|
+
|
|
171
|
+
with open(file_job + ".job", "w", newline="\n") as job_file:
|
|
172
|
+
job_file.write("#!/bin/sh\n")
|
|
173
|
+
job_file.write("#SBATCH -J multijob\n")
|
|
174
|
+
job_file.write("#SBATCH --job-name={:s}\n".format(job_name))
|
|
175
|
+
job_file.write("#SBATCH --output={:s}%j.out\n".format(job_name))
|
|
176
|
+
job_file.write("#SBATCH --partition " + partitions + "\n")
|
|
177
|
+
job_file.write("#SBATCH --nodes={:d}".format(nnodes) + "\n")
|
|
178
|
+
job_file.write("#SBATCH --ntasks={:d}".format(ntasks) + "\n")
|
|
179
|
+
job_file.write("#SBATCH --time={:s}\n".format(str_time))
|
|
180
|
+
job_file.write("\n")
|
|
181
|
+
job_file.write("module purge\n")
|
|
182
|
+
job_file.write("module load slurm\n")
|
|
183
|
+
job_file.write("\n")
|
|
184
|
+
line = "srun -n {:d} -l --multi-prog {:s}"
|
|
185
|
+
job_file.write(line.format(ntasks, path_conf_in_job))
|
|
186
|
+
|
|
187
|
+
|
|
76
188
|
def make_simus(law, rheol_params, folder_data, folder_out, readme_file):
|
|
77
189
|
"""
|
|
78
190
|
Write shaltop initial file for simple slope test case
|
|
@@ -92,57 +204,58 @@ def make_simus(law, rheol_params, folder_data, folder_out, readme_file):
|
|
|
92
204
|
|
|
93
205
|
"""
|
|
94
206
|
# Get topography and initial mass, and write them in Shaltop format
|
|
95
|
-
zfile = os.path.join(folder_data,
|
|
96
|
-
mfile = os.path.join(folder_data,
|
|
207
|
+
zfile = os.path.join(folder_data, "topo.asc")
|
|
208
|
+
mfile = os.path.join(folder_data, "mass.asc")
|
|
97
209
|
x, y, z, dx = tilupy.raster.read_ascii(zfile)
|
|
98
210
|
_, _, m, _ = tilupy.raster.read_ascii(mfile)
|
|
99
|
-
np.savetxt(os.path.join(folder_out,
|
|
100
|
-
np.savetxt(os.path.join(folder_out,
|
|
211
|
+
np.savetxt(os.path.join(folder_out, "z.d"), z.T.flatten())
|
|
212
|
+
np.savetxt(os.path.join(folder_out, "m.d"), m.T.flatten())
|
|
101
213
|
|
|
102
214
|
# Get simulation parameters from README.txt and raster .asc files
|
|
103
215
|
params = tilupy.notations.readme_to_params(readme_file, README_PARAM_MATCH)
|
|
104
|
-
params[
|
|
105
|
-
params[
|
|
106
|
-
params[
|
|
107
|
-
params[
|
|
108
|
-
params[
|
|
109
|
-
params[
|
|
216
|
+
params["nx"] = len(x)
|
|
217
|
+
params["ny"] = len(y)
|
|
218
|
+
params["per"] = dx * len(x)
|
|
219
|
+
params["pery"] = dx * len(y)
|
|
220
|
+
params["file_m_init"] = "../m.d"
|
|
221
|
+
params["file_z_init"] = "../z.d"
|
|
110
222
|
|
|
111
223
|
# Folder for rheological law, and set params accordingly
|
|
112
224
|
folder_law = os.path.join(folder_out, law)
|
|
113
|
-
params[
|
|
225
|
+
params["icomp"] = SHALTOP_LAW_ID[law]
|
|
114
226
|
|
|
115
227
|
param_names = [param for param in rheol_params]
|
|
116
228
|
|
|
117
229
|
texts = tilupy.notations.make_rheol_string(rheol_params, law)
|
|
118
230
|
|
|
119
231
|
# Run shaltop file
|
|
120
|
-
run_shaltop_file = os.path.join(folder_law,
|
|
232
|
+
run_shaltop_file = os.path.join(folder_law, "run_shaltop.sh")
|
|
121
233
|
file_txt = ""
|
|
122
234
|
|
|
123
235
|
for i in range(len(rheol_params[param_names[0]])):
|
|
124
|
-
|
|
125
236
|
simu_text = texts[i]
|
|
126
237
|
for param_name in param_names:
|
|
127
238
|
params[param_name] = rheol_params[param_name][i]
|
|
128
|
-
params[
|
|
239
|
+
params["folder_output"] = simu_text
|
|
129
240
|
folder_results = os.path.join(folder_law, simu_text)
|
|
130
241
|
os.makedirs(folder_results, exist_ok=True)
|
|
131
|
-
with open(os.path.join(folder_results,
|
|
132
|
-
fid.write(
|
|
133
|
-
fid.write(
|
|
134
|
-
fid.write(
|
|
135
|
-
fid.write(
|
|
136
|
-
|
|
137
|
-
write_params_file(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
file_txt +=
|
|
141
|
-
file_txt += '
|
|
142
|
-
file_txt +=
|
|
143
|
-
file_txt += (
|
|
144
|
-
|
|
145
|
-
|
|
242
|
+
with open(os.path.join(folder_results, ".gitignore"), "w") as fid:
|
|
243
|
+
fid.write("# Ignore everything in this directory")
|
|
244
|
+
fid.write("*")
|
|
245
|
+
fid.write("# Except this file")
|
|
246
|
+
fid.write("!.gitignore")
|
|
247
|
+
|
|
248
|
+
write_params_file(
|
|
249
|
+
params, directory=folder_law, file_name=simu_text + ".txt"
|
|
250
|
+
)
|
|
251
|
+
file_txt += "start_time=`date +%s`\n"
|
|
252
|
+
file_txt += 'shaltop "" ' + simu_text + ".txt\n"
|
|
253
|
+
file_txt += "end_time=`date +%s`\n"
|
|
254
|
+
file_txt += "elapsed_time=$(($end_time - $start_time))\n"
|
|
255
|
+
file_txt += (
|
|
256
|
+
'string_time="${start_time} ' + simu_text + ' ${elapsed_time}"\n'
|
|
257
|
+
)
|
|
258
|
+
file_txt += "echo ${string_time} >> simulation_duration.txt\n\n"
|
|
146
259
|
|
|
147
260
|
with open(run_shaltop_file, "w") as fid:
|
|
148
261
|
fid.write(file_txt)
|