PyMPDATA_examples.Jarecka_et_al_2015.formulae
1# adapted from: 2# https://github.com/igfuw/shallow-water-elliptic-drop/blob/master/analytical/analytic_equations.py 3# Code used in the paper of Jarecka, Jaruga, Smolarkiewicz - 4# "A Spreading Drop of Shallow Water" (JCP 289, doi:10.1016/j.jcp.2015.02.003). 5 6import numba 7import numpy as np 8from scipy.integrate import odeint 9 10from PyMPDATA.impl.enumerations import ARG_DATA, ARG_FOCUS, MAX_DIM_NUM 11 12 13def amplitude(x, y, lx, ly): 14 A = 1 / lx / ly 15 h = A * (1 - (x / lx) ** 2 - (y / ly) ** 2) 16 return np.where(h > 0, h, 0) 17 18 19@numba.njit() 20def deriv(y, _): 21 """ 22 return derivatives of [lambda_x, dlambda_x/dt, lambda_y, dlambda_y/dt 23 four first-order ODEs based on eq. 7 (Jarecka, Jaruga, Smolarkiewicz) 24 """ 25 return np.array((y[1], 2.0 / y[0] ** 2 / y[2], y[3], 2.0 / y[0] / y[2] ** 2)) 26 27 28def d2_el_lamb_lamb_t_evol(times, lamb_x0, lamb_y0): 29 """ 30 solving coupled nonlinear second-order ODEs - eq. 7 (Jarecka, Jaruga, Smolarkiewicz) 31 returning array with first dim denoting time, second dim: 32 [lambda_x, dot{lambda_x}, lambda_y, dot{lambda_y} 33 """ 34 assert times[0] == 0 35 yinit = np.array([lamb_x0, 0.0, lamb_y0, 0.0]) # initial values (dot_lamb = 0.) 36 result, info = odeint(deriv, yinit, times, full_output=True) 37 assert info["message"] == "Integration successful." 38 return result 39 40 41def make_rhs_indexers(ats, grid_step, time_step, options): 42 @numba.njit(**options.jit_flags) 43 def rhs(m, _0, h, _1, _2, _3): 44 retval = ( 45 m 46 - ((ats(*h, +1) - ats(*h, -1)) / 2) / 2 * ats(*h, 0) * time_step / grid_step 47 ) 48 return retval 49 50 return rhs 51 52 53def make_rhs(grid_step, time_step, axis, options, traversals): 54 indexers = traversals.indexers[traversals.n_dims] 55 apply_scalar = traversals.apply_scalar(loop=False) 56 57 formulae_rhs = tuple( 58 ( 59 make_rhs_indexers( 60 ats=indexers.ats[axis], 61 grid_step=grid_step[axis], 62 time_step=time_step, 63 options=options, 64 ), 65 None, 66 None, 67 ) 68 ) 69 70 @numba.njit(**options.jit_flags) 71 def apply(traversals_data, momentum, h): 72 null_scalarfield, null_scalarfield_bc = traversals_data.null_scalar_field 73 null_vectorfield, null_vectorfield_bc = traversals_data.null_vector_field 74 return apply_scalar( 75 *formulae_rhs, 76 *momentum.field, 77 *null_vectorfield, 78 null_vectorfield_bc, 79 *h.field, 80 h.bc, 81 *null_scalarfield, 82 null_scalarfield_bc, 83 *null_scalarfield, 84 null_scalarfield_bc, 85 *null_scalarfield, 86 null_scalarfield_bc, 87 traversals_data.buffer 88 ) 89 90 return apply 91 92 93def make_interpolate_indexers(ati, options): 94 @numba.njit(**options.jit_flags) 95 def interpolate(momentum_x, _, momentum_y): 96 momenta = (momentum_x[ARG_FOCUS], (momentum_x[ARG_DATA], momentum_y[ARG_DATA])) 97 return ati(*momenta, 0.5) 98 99 return interpolate 100 101 102def make_interpolate(options, traversals): 103 indexers = traversals.indexers[traversals.n_dims] 104 apply_vector = traversals.apply_vector() 105 106 formulae_interpolate = tuple( 107 ( 108 make_interpolate_indexers(ati=indexers.ati[i], options=options) 109 if indexers.ati[i] is not None 110 else None 111 ) 112 for i in range(MAX_DIM_NUM) 113 ) 114 115 @numba.njit(**options.jit_flags) 116 def apply(traversals_data, momentum_x, momentum_y, advector): 117 null_vectorfield, null_vectorfield_bc = traversals_data.null_vector_field 118 return apply_vector( 119 *formulae_interpolate, 120 *advector.field, 121 *momentum_x.field, 122 momentum_x.bc, 123 *null_vectorfield, 124 null_vectorfield_bc, 125 *momentum_y.field, 126 momentum_y.bc, 127 traversals_data.buffer 128 ) 129 130 return apply
def
amplitude(x, y, lx, ly):
@numba.njit()
def
deriv(y, _):
20@numba.njit() 21def deriv(y, _): 22 """ 23 return derivatives of [lambda_x, dlambda_x/dt, lambda_y, dlambda_y/dt 24 four first-order ODEs based on eq. 7 (Jarecka, Jaruga, Smolarkiewicz) 25 """ 26 return np.array((y[1], 2.0 / y[0] ** 2 / y[2], y[3], 2.0 / y[0] / y[2] ** 2))
return derivatives of [lambda_x, dlambda_x/dt, lambda_y, dlambda_y/dt four first-order ODEs based on eq. 7 (Jarecka, Jaruga, Smolarkiewicz)
def
d2_el_lamb_lamb_t_evol(times, lamb_x0, lamb_y0):
29def d2_el_lamb_lamb_t_evol(times, lamb_x0, lamb_y0): 30 """ 31 solving coupled nonlinear second-order ODEs - eq. 7 (Jarecka, Jaruga, Smolarkiewicz) 32 returning array with first dim denoting time, second dim: 33 [lambda_x, dot{lambda_x}, lambda_y, dot{lambda_y} 34 """ 35 assert times[0] == 0 36 yinit = np.array([lamb_x0, 0.0, lamb_y0, 0.0]) # initial values (dot_lamb = 0.) 37 result, info = odeint(deriv, yinit, times, full_output=True) 38 assert info["message"] == "Integration successful." 39 return result
solving coupled nonlinear second-order ODEs - eq. 7 (Jarecka, Jaruga, Smolarkiewicz) returning array with first dim denoting time, second dim: [lambda_x, dot{lambda_x}, lambda_y, dot{lambda_y}
def
make_rhs_indexers(ats, grid_step, time_step, options):
def
make_rhs(grid_step, time_step, axis, options, traversals):
54def make_rhs(grid_step, time_step, axis, options, traversals): 55 indexers = traversals.indexers[traversals.n_dims] 56 apply_scalar = traversals.apply_scalar(loop=False) 57 58 formulae_rhs = tuple( 59 ( 60 make_rhs_indexers( 61 ats=indexers.ats[axis], 62 grid_step=grid_step[axis], 63 time_step=time_step, 64 options=options, 65 ), 66 None, 67 None, 68 ) 69 ) 70 71 @numba.njit(**options.jit_flags) 72 def apply(traversals_data, momentum, h): 73 null_scalarfield, null_scalarfield_bc = traversals_data.null_scalar_field 74 null_vectorfield, null_vectorfield_bc = traversals_data.null_vector_field 75 return apply_scalar( 76 *formulae_rhs, 77 *momentum.field, 78 *null_vectorfield, 79 null_vectorfield_bc, 80 *h.field, 81 h.bc, 82 *null_scalarfield, 83 null_scalarfield_bc, 84 *null_scalarfield, 85 null_scalarfield_bc, 86 *null_scalarfield, 87 null_scalarfield_bc, 88 traversals_data.buffer 89 ) 90 91 return apply
def
make_interpolate_indexers(ati, options):
def
make_interpolate(options, traversals):
103def make_interpolate(options, traversals): 104 indexers = traversals.indexers[traversals.n_dims] 105 apply_vector = traversals.apply_vector() 106 107 formulae_interpolate = tuple( 108 ( 109 make_interpolate_indexers(ati=indexers.ati[i], options=options) 110 if indexers.ati[i] is not None 111 else None 112 ) 113 for i in range(MAX_DIM_NUM) 114 ) 115 116 @numba.njit(**options.jit_flags) 117 def apply(traversals_data, momentum_x, momentum_y, advector): 118 null_vectorfield, null_vectorfield_bc = traversals_data.null_vector_field 119 return apply_vector( 120 *formulae_interpolate, 121 *advector.field, 122 *momentum_x.field, 123 momentum_x.bc, 124 *null_vectorfield, 125 null_vectorfield_bc, 126 *momentum_y.field, 127 momentum_y.bc, 128 traversals_data.buffer 129 ) 130 131 return apply