pydrex.velocity
PyDRex: Steady-state solutions of velocity (gradients) for various flows.
For the sake of consistency, all callables returned from methods in this module expect a
time scalar and 3D position vector as inputs. They also return 3D tensors in all cases.
This means they can be directly used as arguments to e.g.
pydrex.minerals.Mineral.update_orientations
.
1"""> PyDRex: Steady-state solutions of velocity (gradients) for various flows. 2 3For the sake of consistency, all callables returned from methods in this module expect a 4time scalar and 3D position vector as inputs. They also return 3D tensors in all cases. 5This means they can be directly used as arguments to e.g. 6`pydrex.minerals.Mineral.update_orientations`. 7 8""" 9 10import functools as ft 11 12import numba as nb 13import numpy as np 14 15from pydrex import geometry as _geo 16 17 18@nb.njit(fastmath=True) 19def _simple_shear_2d_grad(t, x, direction, deformation_plane, strain_rate): 20 grad_v = np.zeros((3, 3)) 21 grad_v[direction, deformation_plane] = 2 * strain_rate 22 return grad_v 23 24 25@nb.njit(fastmath=True) 26def _simple_shear_2d(t, x, direction, deformation_plane, strain_rate): 27 v = np.zeros(3) 28 v[direction] = x[deformation_plane] * strain_rate 29 return v 30 31 32@nb.njit(fastmath=True) 33def _cell_2d_grad(t, x, horizontal, vertical, velocity_edge, edge_length): 34 _lim = edge_length / 2 35 if np.abs(x[horizontal]) > _lim or np.abs(x[vertical]) > _lim: 36 # NOTE: At the moment, this prints type info rather than values. Probably a 37 # numba bug, version 0.59 shouldn't require these to be compile-time constants. 38 raise ValueError( 39 f"position ({x[horizontal]}, {x[vertical]}) is outside domain with xᵢ ∈ [-{_lim}, {_lim}]" 40 ) 41 cos_πx_divd = np.cos(np.pi * x[horizontal] / edge_length) 42 cos_πz_divd = np.cos(np.pi * x[vertical] / edge_length) 43 sin_πx_divd = np.sin(np.pi * x[horizontal] / edge_length) 44 sin_πz_divd = np.sin(np.pi * x[vertical] / edge_length) 45 grad_v = np.zeros((3, 3)) 46 grad_v[horizontal, horizontal] = -np.pi / edge_length * sin_πz_divd * sin_πx_divd 47 grad_v[horizontal, vertical] = np.pi / edge_length * cos_πz_divd * cos_πx_divd 48 grad_v[vertical, vertical] = -np.pi / edge_length * cos_πz_divd * cos_πx_divd 49 grad_v[vertical, horizontal] = np.pi / edge_length * sin_πz_divd * sin_πx_divd 50 return velocity_edge * grad_v 51 52 53@nb.njit(fastmath=True) 54def _cell_2d(t, x, horizontal, vertical, velocity_edge, edge_length): 55 _lim = edge_length / 2 56 if np.abs(x[horizontal]) > _lim or np.abs(x[vertical]) > _lim: 57 # NOTE: At the moment, this prints type info rather than values. Probably a 58 # numba bug, version 0.59 shouldn't require these to be compile-time constants. 59 raise ValueError( 60 f"position ({x[horizontal]}, {x[vertical]}) is outside domain with xᵢ ∈ [-{_lim}, {_lim}]" 61 ) 62 cos_πx_divd = np.cos(np.pi * x[horizontal] / edge_length) 63 cos_πz_divd = np.cos(np.pi * x[vertical] / edge_length) 64 sin_πx_divd = np.sin(np.pi * x[horizontal] / edge_length) 65 sin_πz_divd = np.sin(np.pi * x[vertical] / edge_length) 66 v = np.zeros(3) 67 v[horizontal] = velocity_edge * cos_πx_divd * sin_πz_divd 68 v[vertical] = -velocity_edge * sin_πx_divd * cos_πz_divd 69 return v 70 71 72@nb.njit(fastmath=True) 73def _corner_2d(t, x, horizontal, vertical, plate_speed): 74 h = x[horizontal] 75 v = x[vertical] 76 if np.abs(h) < 1e-15 and np.abs(v) < 1e-15: 77 return np.full(3, np.nan) 78 out = np.zeros(3) 79 prefactor = 2 * plate_speed / np.pi 80 out[horizontal] = prefactor * (np.arctan2(h, -v) + h * v / (h**2 + v**2)) 81 out[vertical] = prefactor * v**2 / (h**2 + v**2) 82 return out 83 84 85@nb.njit(fastmath=True) 86def _corner_2d_grad(t, x, horizontal, vertical, plate_speed): 87 h = x[horizontal] 88 v = x[vertical] 89 if np.abs(h) < 1e-15 and np.abs(v) < 1e-15: 90 return np.full((3, 3), np.nan) 91 grad_v = np.zeros((3, 3)) 92 prefactor = 4 * plate_speed / (np.pi * (h**2 + v**2) ** 2) 93 grad_v[horizontal, horizontal] = -(h**2) * v 94 grad_v[horizontal, vertical] = h**3 95 grad_v[vertical, horizontal] = -h * v**2 96 grad_v[vertical, vertical] = h**2 * v 97 return prefactor * grad_v 98 99 100def simple_shear_2d( 101 direction: str, deformation_plane: str, strain_rate: float 102) -> tuple: 103 """Return simple shear velocity and velocity gradient callables. 104 105 The returned callables have signature f(t, x) where x is a 3D position vector. 106 107 - `direction` (one of {"X", "Y", "Z"}) — velocity vector direction 108 - `deformation_plane` (one of {"X", "Y", "Z"}) — direction of velocity gradient 109 - `strain_rate` — 1/2 × magnitude of the largest eigenvalue of the velocity 110 gradient 111 112 .. note:: 113 Input arrays to the returned callables must have homogeneous element types. 114 Arrays with e.g. both floating point and integer values are not supported. 115 116 Examples: 117 118 >>> import numpy as np 119 >>> u, L = simple_shear_2d("X", "Z", 1e-4) 120 >>> u(np.nan, np.array([0, 0, 0])) # Time is not used here. 121 array([0., 0., 0.]) 122 >>> u(np.nan, np.array([0, 0, 1])) 123 array([0.0001, 0. , 0. ]) 124 >>> u(np.nan, np.array([0.0, 0.0, 2.0])) 125 array([0.0002, 0. , 0. ]) 126 >>> L(np.nan, np.array([0, 0, 0])) 127 array([[0. , 0. , 0.0002], 128 [0. , 0. , 0. ], 129 [0. , 0. , 0. ]]) 130 >>> L(np.nan, np.array([0.0, 0.0, 1.0])) 131 array([[0. , 0. , 0.0002], 132 [0. , 0. , 0. ], 133 [0. , 0. , 0. ]]) 134 135 """ 136 try: 137 indices = _geo.to_indices2d(direction, deformation_plane) 138 except ValueError: 139 raise ValueError( 140 "unsupported shear type with" 141 + f" direction = {direction}, deformation_plane = {deformation_plane}" 142 ) 143 144 return ( 145 ft.partial( 146 _simple_shear_2d, 147 direction=indices[0], 148 deformation_plane=indices[1], 149 strain_rate=strain_rate, 150 ), 151 ft.partial( 152 _simple_shear_2d_grad, 153 direction=indices[0], 154 deformation_plane=indices[1], 155 strain_rate=strain_rate, 156 ), 157 ) 158 159 160def cell_2d( 161 horizontal: str, vertical: str, velocity_edge: float, edge_length: float = 2.0 162) -> tuple: 163 r"""Get velocity and velocity gradient callables for a steady-state 2D Stokes cell. 164 165 The cell is centered at (0,0) and the velocity field is defined by: 166 $$ 167 \bm{u} = U\cos(π x/d)\sin(π z/d) \bm{\hat{h}} - U\sin(π x/d)\cos(π z/d) \bm{\hat{v}} 168 $$ 169 where $\bm{\hat{h}}$ and $\bm{\hat{v}}$ are unit vectors in the chosen horizontal 170 and vertical directions, respectively. The velocity at the cell edge has a magnitude 171 of $U$ and $d$ is the length of a cell edge. 172 173 The returned callables have signature f(t, x) where x is a 3D position vector. 174 175 - `horizontal` (one of {"X", "Y", "Z"}) — horizontal direction 176 - `vertical` (one of {"X", "Y", "Z"}) — vertical direction 177 - `velocity_edge` — velocity magnitude at the center of the cell edge 178 - `edge_length` (optional) — the edge length of the cell (= 2 by default) 179 180 Examples: 181 182 >>> import numpy as np 183 >>> u, L = cell_2d("X", "Z", 1) 184 >>> u(np.nan, np.array([0, 0, 0])) # Time value is not used for steady flows. 185 array([ 0., 0., -0.]) 186 >>> u(np.nan, np.array([0, 0, 1])) 187 array([ 1., 0., -0.]) 188 >>> u(np.nan, np.array([0, 1, 0])) # Y-value is not used. 189 array([ 0., 0., -0.]) 190 >>> u(np.nan, np.array([0, 0, -1])) 191 array([-1., 0., -0.]) 192 >>> u(np.nan, np.array([1, 0, 0])) 193 array([ 0., 0., -1.]) 194 >>> u(np.nan, np.array([-0.5, 0.0, 0.0])) 195 array([0. , 0. , 0.70710678]) 196 >>> L(np.nan, np.array([0, 0, 0])) 197 array([[-0. , 0. , 1.57079633], 198 [ 0. , 0. , 0. ], 199 [ 0. , 0. , -1.57079633]]) 200 >>> L(np.nan, np.array([0.5, 0.0, 0.0])) 201 array([[-0. , 0. , 1.11072073], 202 [ 0. , 0. , 0. ], 203 [ 0. , 0. , -1.11072073]]) 204 >>> L(np.nan, np.array([0, 0, 0])) == L(np.nan, np.array([0, 1, 0])) # Y-value is not used. 205 array([[ True, True, True], 206 [ True, True, True], 207 [ True, True, True]]) 208 >>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([0, 0, 1])) 209 array([[ True, True, True], 210 [ True, True, True], 211 [ True, True, True]]) 212 >>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([-1, 0, 0])) 213 array([[ True, True, True], 214 [ True, True, True], 215 [ True, True, True]]) 216 >>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([0, 0, -1])) 217 array([[ True, True, True], 218 [ True, True, True], 219 [ True, True, True]]) 220 >>> L(np.nan, np.array([0.5, 0.0, 0.5])) 221 array([[-0.78539816, 0. , 0.78539816], 222 [ 0. , 0. , 0. ], 223 [ 0.78539816, 0. , -0.78539816]]) 224 225 >>> u, L = cell_2d("X", "Z", 6.3e-10, 1e5) 226 >>> u(np.nan, np.array([0, 0, 0])) 227 array([ 0., 0., -0.]) 228 >>> u(np.nan, np.array([0.0, 0.0, -5e4])) 229 array([-6.3e-10, 0.0e+00, -0.0e+00]) 230 >>> u(np.nan, np.array([2e2, 0e0, 0e0])) 231 array([ 0.0000000e+00, 0.0000000e+00, -3.9583807e-12]) 232 233 """ 234 if edge_length < 0: 235 raise ValueError(f"edge length of 2D cell must be positive, not {edge_length}") 236 try: 237 indices = _geo.to_indices2d(horizontal, vertical) 238 except ValueError: 239 raise ValueError( 240 "unsupported convection cell geometry with" 241 + f" horizontal = {horizontal}, vertical = {vertical}" 242 ) 243 244 return ( 245 ft.partial( 246 _cell_2d, 247 horizontal=indices[0], 248 vertical=indices[1], 249 velocity_edge=velocity_edge, 250 edge_length=edge_length, 251 ), 252 ft.partial( 253 _cell_2d_grad, 254 horizontal=indices[0], 255 vertical=indices[1], 256 velocity_edge=velocity_edge, 257 edge_length=edge_length, 258 ), 259 ) 260 261 262def corner_2d(horizontal: str, vertical: str, plate_speed: float) -> tuple: 263 r"""Get velocity and velocity gradient callables for a steady-state 2D corner flow. 264 265 The velocity field is defined by: 266 $$ 267 \bm{u} = \frac{dr}{dt} \bm{\hat{r}} + r \frac{dθ}{dt} \bm{\hat{θ}} 268 = \frac{2 U}{π}(θ\sinθ - \cosθ) ⋅ \bm{\hat{r}} + \frac{2 U}{π}θ\cosθ ⋅ \bm{\hat{θ}} 269 $$ 270 where $θ = 0$ points vertically downwards along the ridge axis 271 and $θ = π/2$ points along the surface. $U$ is the half spreading velocity. 272 Streamlines for the flow obey: 273 $$ 274 ψ = \frac{2 U r}{π}θ\cosθ 275 $$ 276 and are related to the velocity through: 277 $$ 278 \bm{u} = -\frac{1}{r} ⋅ \frac{dψ}{dθ} ⋅ \bm{\hat{r}} + \frac{dψ}{dr}\bm{\hat{θ}} 279 $$ 280 Conversion to Cartesian ($x,y,z$) coordinates yields: 281 $$ 282 \bm{u} = \frac{2U}{π} \left[ 283 \tan^{-1}\left(\frac{x}{-z}\right) + \frac{xz}{x^{2} + z^{2}} \right] \bm{\hat{x}} + 284 \frac{2U}{π} \frac{z^{2}}{x^{2} + z^{2}} \bm{\hat{z}} 285 $$ 286 where 287 \begin{align\*} 288 x &= r \sinθ \cr 289 z &= -r \cosθ 290 \end{align\*} 291 and the velocity gradient is: 292 $$ 293 L = \frac{4 U}{π{(x^{2}+z^{2})}^{2}} ⋅ 294 \begin{bmatrix} 295 -x^{2}z & 0 & x^{3} \cr 296 0 & 0 & 0 \cr 297 -xz^{2} & 0 & x^{2}z 298 \end{bmatrix} 299 $$ 300 See also Fig. 5 in [Kaminski & Ribe, 2002](https://doi.org/10.1029/2001GC000222). 301 302 The returned callables have signature f(t, x) where x is a 3D position vector. 303 304 - `horizontal` (one of {"X", "Y", "Z"}) — horizontal direction 305 - `vertical` (one of {"X", "Y", "Z"}) — vertical direction 306 - `plate_speed` (float) — speed of the “plate” i.e. upper boundary 307 308 """ 309 try: 310 indices = _geo.to_indices2d(horizontal, vertical) 311 except ValueError: 312 raise ValueError( 313 "unsupported convection cell geometry with" 314 + f" horizontal = {horizontal}, vertical = {vertical}" 315 ) 316 317 return ( 318 ft.partial( 319 _corner_2d, 320 horizontal=indices[0], 321 vertical=indices[1], 322 plate_speed=plate_speed, 323 ), 324 ft.partial( 325 _corner_2d_grad, 326 horizontal=indices[0], 327 vertical=indices[1], 328 plate_speed=plate_speed, 329 ), 330 )
101def simple_shear_2d( 102 direction: str, deformation_plane: str, strain_rate: float 103) -> tuple: 104 """Return simple shear velocity and velocity gradient callables. 105 106 The returned callables have signature f(t, x) where x is a 3D position vector. 107 108 - `direction` (one of {"X", "Y", "Z"}) — velocity vector direction 109 - `deformation_plane` (one of {"X", "Y", "Z"}) — direction of velocity gradient 110 - `strain_rate` — 1/2 × magnitude of the largest eigenvalue of the velocity 111 gradient 112 113 .. note:: 114 Input arrays to the returned callables must have homogeneous element types. 115 Arrays with e.g. both floating point and integer values are not supported. 116 117 Examples: 118 119 >>> import numpy as np 120 >>> u, L = simple_shear_2d("X", "Z", 1e-4) 121 >>> u(np.nan, np.array([0, 0, 0])) # Time is not used here. 122 array([0., 0., 0.]) 123 >>> u(np.nan, np.array([0, 0, 1])) 124 array([0.0001, 0. , 0. ]) 125 >>> u(np.nan, np.array([0.0, 0.0, 2.0])) 126 array([0.0002, 0. , 0. ]) 127 >>> L(np.nan, np.array([0, 0, 0])) 128 array([[0. , 0. , 0.0002], 129 [0. , 0. , 0. ], 130 [0. , 0. , 0. ]]) 131 >>> L(np.nan, np.array([0.0, 0.0, 1.0])) 132 array([[0. , 0. , 0.0002], 133 [0. , 0. , 0. ], 134 [0. , 0. , 0. ]]) 135 136 """ 137 try: 138 indices = _geo.to_indices2d(direction, deformation_plane) 139 except ValueError: 140 raise ValueError( 141 "unsupported shear type with" 142 + f" direction = {direction}, deformation_plane = {deformation_plane}" 143 ) 144 145 return ( 146 ft.partial( 147 _simple_shear_2d, 148 direction=indices[0], 149 deformation_plane=indices[1], 150 strain_rate=strain_rate, 151 ), 152 ft.partial( 153 _simple_shear_2d_grad, 154 direction=indices[0], 155 deformation_plane=indices[1], 156 strain_rate=strain_rate, 157 ), 158 )
Return simple shear velocity and velocity gradient callables.
The returned callables have signature f(t, x) where x is a 3D position vector.
direction
(one of {"X", "Y", "Z"}) — velocity vector directiondeformation_plane
(one of {"X", "Y", "Z"}) — direction of velocity gradientstrain_rate
— 1/2 × magnitude of the largest eigenvalue of the velocity gradient
Input arrays to the returned callables must have homogeneous element types. Arrays with e.g. both floating point and integer values are not supported.
Examples:
>>> import numpy as np
>>> u, L = simple_shear_2d("X", "Z", 1e-4)
>>> u(np.nan, np.array([0, 0, 0])) # Time is not used here.
array([0., 0., 0.])
>>> u(np.nan, np.array([0, 0, 1]))
array([0.0001, 0. , 0. ])
>>> u(np.nan, np.array([0.0, 0.0, 2.0]))
array([0.0002, 0. , 0. ])
>>> L(np.nan, np.array([0, 0, 0]))
array([[0. , 0. , 0.0002],
[0. , 0. , 0. ],
[0. , 0. , 0. ]])
>>> L(np.nan, np.array([0.0, 0.0, 1.0]))
array([[0. , 0. , 0.0002],
[0. , 0. , 0. ],
[0. , 0. , 0. ]])
161def cell_2d( 162 horizontal: str, vertical: str, velocity_edge: float, edge_length: float = 2.0 163) -> tuple: 164 r"""Get velocity and velocity gradient callables for a steady-state 2D Stokes cell. 165 166 The cell is centered at (0,0) and the velocity field is defined by: 167 $$ 168 \bm{u} = U\cos(π x/d)\sin(π z/d) \bm{\hat{h}} - U\sin(π x/d)\cos(π z/d) \bm{\hat{v}} 169 $$ 170 where $\bm{\hat{h}}$ and $\bm{\hat{v}}$ are unit vectors in the chosen horizontal 171 and vertical directions, respectively. The velocity at the cell edge has a magnitude 172 of $U$ and $d$ is the length of a cell edge. 173 174 The returned callables have signature f(t, x) where x is a 3D position vector. 175 176 - `horizontal` (one of {"X", "Y", "Z"}) — horizontal direction 177 - `vertical` (one of {"X", "Y", "Z"}) — vertical direction 178 - `velocity_edge` — velocity magnitude at the center of the cell edge 179 - `edge_length` (optional) — the edge length of the cell (= 2 by default) 180 181 Examples: 182 183 >>> import numpy as np 184 >>> u, L = cell_2d("X", "Z", 1) 185 >>> u(np.nan, np.array([0, 0, 0])) # Time value is not used for steady flows. 186 array([ 0., 0., -0.]) 187 >>> u(np.nan, np.array([0, 0, 1])) 188 array([ 1., 0., -0.]) 189 >>> u(np.nan, np.array([0, 1, 0])) # Y-value is not used. 190 array([ 0., 0., -0.]) 191 >>> u(np.nan, np.array([0, 0, -1])) 192 array([-1., 0., -0.]) 193 >>> u(np.nan, np.array([1, 0, 0])) 194 array([ 0., 0., -1.]) 195 >>> u(np.nan, np.array([-0.5, 0.0, 0.0])) 196 array([0. , 0. , 0.70710678]) 197 >>> L(np.nan, np.array([0, 0, 0])) 198 array([[-0. , 0. , 1.57079633], 199 [ 0. , 0. , 0. ], 200 [ 0. , 0. , -1.57079633]]) 201 >>> L(np.nan, np.array([0.5, 0.0, 0.0])) 202 array([[-0. , 0. , 1.11072073], 203 [ 0. , 0. , 0. ], 204 [ 0. , 0. , -1.11072073]]) 205 >>> L(np.nan, np.array([0, 0, 0])) == L(np.nan, np.array([0, 1, 0])) # Y-value is not used. 206 array([[ True, True, True], 207 [ True, True, True], 208 [ True, True, True]]) 209 >>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([0, 0, 1])) 210 array([[ True, True, True], 211 [ True, True, True], 212 [ True, True, True]]) 213 >>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([-1, 0, 0])) 214 array([[ True, True, True], 215 [ True, True, True], 216 [ True, True, True]]) 217 >>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([0, 0, -1])) 218 array([[ True, True, True], 219 [ True, True, True], 220 [ True, True, True]]) 221 >>> L(np.nan, np.array([0.5, 0.0, 0.5])) 222 array([[-0.78539816, 0. , 0.78539816], 223 [ 0. , 0. , 0. ], 224 [ 0.78539816, 0. , -0.78539816]]) 225 226 >>> u, L = cell_2d("X", "Z", 6.3e-10, 1e5) 227 >>> u(np.nan, np.array([0, 0, 0])) 228 array([ 0., 0., -0.]) 229 >>> u(np.nan, np.array([0.0, 0.0, -5e4])) 230 array([-6.3e-10, 0.0e+00, -0.0e+00]) 231 >>> u(np.nan, np.array([2e2, 0e0, 0e0])) 232 array([ 0.0000000e+00, 0.0000000e+00, -3.9583807e-12]) 233 234 """ 235 if edge_length < 0: 236 raise ValueError(f"edge length of 2D cell must be positive, not {edge_length}") 237 try: 238 indices = _geo.to_indices2d(horizontal, vertical) 239 except ValueError: 240 raise ValueError( 241 "unsupported convection cell geometry with" 242 + f" horizontal = {horizontal}, vertical = {vertical}" 243 ) 244 245 return ( 246 ft.partial( 247 _cell_2d, 248 horizontal=indices[0], 249 vertical=indices[1], 250 velocity_edge=velocity_edge, 251 edge_length=edge_length, 252 ), 253 ft.partial( 254 _cell_2d_grad, 255 horizontal=indices[0], 256 vertical=indices[1], 257 velocity_edge=velocity_edge, 258 edge_length=edge_length, 259 ), 260 )
Get velocity and velocity gradient callables for a steady-state 2D Stokes cell.
The cell is centered at (0,0) and the velocity field is defined by: $$ \bm{u} = U\cos(π x/d)\sin(π z/d) \bm{\hat{h}} - U\sin(π x/d)\cos(π z/d) \bm{\hat{v}} $$ where $\bm{\hat{h}}$ and $\bm{\hat{v}}$ are unit vectors in the chosen horizontal and vertical directions, respectively. The velocity at the cell edge has a magnitude of $U$ and $d$ is the length of a cell edge.
The returned callables have signature f(t, x) where x is a 3D position vector.
horizontal
(one of {"X", "Y", "Z"}) — horizontal directionvertical
(one of {"X", "Y", "Z"}) — vertical directionvelocity_edge
— velocity magnitude at the center of the cell edgeedge_length
(optional) — the edge length of the cell (= 2 by default)
Examples:
>>> import numpy as np
>>> u, L = cell_2d("X", "Z", 1)
>>> u(np.nan, np.array([0, 0, 0])) # Time value is not used for steady flows.
array([ 0., 0., -0.])
>>> u(np.nan, np.array([0, 0, 1]))
array([ 1., 0., -0.])
>>> u(np.nan, np.array([0, 1, 0])) # Y-value is not used.
array([ 0., 0., -0.])
>>> u(np.nan, np.array([0, 0, -1]))
array([-1., 0., -0.])
>>> u(np.nan, np.array([1, 0, 0]))
array([ 0., 0., -1.])
>>> u(np.nan, np.array([-0.5, 0.0, 0.0]))
array([0. , 0. , 0.70710678])
>>> L(np.nan, np.array([0, 0, 0]))
array([[-0. , 0. , 1.57079633],
[ 0. , 0. , 0. ],
[ 0. , 0. , -1.57079633]])
>>> L(np.nan, np.array([0.5, 0.0, 0.0]))
array([[-0. , 0. , 1.11072073],
[ 0. , 0. , 0. ],
[ 0. , 0. , -1.11072073]])
>>> L(np.nan, np.array([0, 0, 0])) == L(np.nan, np.array([0, 1, 0])) # Y-value is not used.
array([[ True, True, True],
[ True, True, True],
[ True, True, True]])
>>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([0, 0, 1]))
array([[ True, True, True],
[ True, True, True],
[ True, True, True]])
>>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([-1, 0, 0]))
array([[ True, True, True],
[ True, True, True],
[ True, True, True]])
>>> L(np.nan, np.array([1, 0, 0])) == L(np.nan, np.array([0, 0, -1]))
array([[ True, True, True],
[ True, True, True],
[ True, True, True]])
>>> L(np.nan, np.array([0.5, 0.0, 0.5]))
array([[-0.78539816, 0. , 0.78539816],
[ 0. , 0. , 0. ],
[ 0.78539816, 0. , -0.78539816]])
>>> u, L = cell_2d("X", "Z", 6.3e-10, 1e5)
>>> u(np.nan, np.array([0, 0, 0]))
array([ 0., 0., -0.])
>>> u(np.nan, np.array([0.0, 0.0, -5e4]))
array([-6.3e-10, 0.0e+00, -0.0e+00])
>>> u(np.nan, np.array([2e2, 0e0, 0e0]))
array([ 0.0000000e+00, 0.0000000e+00, -3.9583807e-12])
263def corner_2d(horizontal: str, vertical: str, plate_speed: float) -> tuple: 264 r"""Get velocity and velocity gradient callables for a steady-state 2D corner flow. 265 266 The velocity field is defined by: 267 $$ 268 \bm{u} = \frac{dr}{dt} \bm{\hat{r}} + r \frac{dθ}{dt} \bm{\hat{θ}} 269 = \frac{2 U}{π}(θ\sinθ - \cosθ) ⋅ \bm{\hat{r}} + \frac{2 U}{π}θ\cosθ ⋅ \bm{\hat{θ}} 270 $$ 271 where $θ = 0$ points vertically downwards along the ridge axis 272 and $θ = π/2$ points along the surface. $U$ is the half spreading velocity. 273 Streamlines for the flow obey: 274 $$ 275 ψ = \frac{2 U r}{π}θ\cosθ 276 $$ 277 and are related to the velocity through: 278 $$ 279 \bm{u} = -\frac{1}{r} ⋅ \frac{dψ}{dθ} ⋅ \bm{\hat{r}} + \frac{dψ}{dr}\bm{\hat{θ}} 280 $$ 281 Conversion to Cartesian ($x,y,z$) coordinates yields: 282 $$ 283 \bm{u} = \frac{2U}{π} \left[ 284 \tan^{-1}\left(\frac{x}{-z}\right) + \frac{xz}{x^{2} + z^{2}} \right] \bm{\hat{x}} + 285 \frac{2U}{π} \frac{z^{2}}{x^{2} + z^{2}} \bm{\hat{z}} 286 $$ 287 where 288 \begin{align\*} 289 x &= r \sinθ \cr 290 z &= -r \cosθ 291 \end{align\*} 292 and the velocity gradient is: 293 $$ 294 L = \frac{4 U}{π{(x^{2}+z^{2})}^{2}} ⋅ 295 \begin{bmatrix} 296 -x^{2}z & 0 & x^{3} \cr 297 0 & 0 & 0 \cr 298 -xz^{2} & 0 & x^{2}z 299 \end{bmatrix} 300 $$ 301 See also Fig. 5 in [Kaminski & Ribe, 2002](https://doi.org/10.1029/2001GC000222). 302 303 The returned callables have signature f(t, x) where x is a 3D position vector. 304 305 - `horizontal` (one of {"X", "Y", "Z"}) — horizontal direction 306 - `vertical` (one of {"X", "Y", "Z"}) — vertical direction 307 - `plate_speed` (float) — speed of the “plate” i.e. upper boundary 308 309 """ 310 try: 311 indices = _geo.to_indices2d(horizontal, vertical) 312 except ValueError: 313 raise ValueError( 314 "unsupported convection cell geometry with" 315 + f" horizontal = {horizontal}, vertical = {vertical}" 316 ) 317 318 return ( 319 ft.partial( 320 _corner_2d, 321 horizontal=indices[0], 322 vertical=indices[1], 323 plate_speed=plate_speed, 324 ), 325 ft.partial( 326 _corner_2d_grad, 327 horizontal=indices[0], 328 vertical=indices[1], 329 plate_speed=plate_speed, 330 ), 331 )
Get velocity and velocity gradient callables for a steady-state 2D corner flow.
The velocity field is defined by: $$ \bm{u} = \frac{dr}{dt} \bm{\hat{r}} + r \frac{dθ}{dt} \bm{\hat{θ}} = \frac{2 U}{π}(θ\sinθ - \cosθ) ⋅ \bm{\hat{r}} + \frac{2 U}{π}θ\cosθ ⋅ \bm{\hat{θ}} $$ where $θ = 0$ points vertically downwards along the ridge axis and $θ = π/2$ points along the surface. $U$ is the half spreading velocity. Streamlines for the flow obey: $$ ψ = \frac{2 U r}{π}θ\cosθ $$ and are related to the velocity through: $$ \bm{u} = -\frac{1}{r} ⋅ \frac{dψ}{dθ} ⋅ \bm{\hat{r}} + \frac{dψ}{dr}\bm{\hat{θ}} $$ Conversion to Cartesian ($x,y,z$) coordinates yields: $$ \bm{u} = \frac{2U}{π} \left[ \tan^{-1}\left(\frac{x}{-z}\right) + \frac{xz}{x^{2} + z^{2}} \right] \bm{\hat{x}} + \frac{2U}{π} \frac{z^{2}}{x^{2} + z^{2}} \bm{\hat{z}} $$ where \begin{align*} x &= r \sinθ \cr z &= -r \cosθ \end{align*} and the velocity gradient is: $$ L = \frac{4 U}{π{(x^{2}+z^{2})}^{2}} ⋅ \begin{bmatrix} -x^{2}z & 0 & x^{3} \cr 0 & 0 & 0 \cr -xz^{2} & 0 & x^{2}z \end{bmatrix} $$ See also Fig. 5 in Kaminski & Ribe, 2002.
The returned callables have signature f(t, x) where x is a 3D position vector.
horizontal
(one of {"X", "Y", "Z"}) — horizontal directionvertical
(one of {"X", "Y", "Z"}) — vertical directionplate_speed
(float) — speed of the “plate” i.e. upper boundary