def rhs_to_scipy_ode(rhs, t, x_vect, u_vect, constants, *args, **lambdify_kwargs): """Convert rhs to lambda function and jacobian comaptible with scipy. Given a state space description of a dynamic system, create a lambda function with a signature compatible with scipy.ode.integrate: f(t, x, u, *args). Parameters ---------- rhs : sympy.Matrix Column vector of expressions for the derivative of x (continuous), or for the change in x (discrete). t : sympy.Symbol The independent time-like variable. x_vect : sympy.Matrix Column vector of symbols in the x (state) vector. u_vect : sympy.Matrix Column vector of symbols in the u (input) vector. Most controllers pass a vector of inputs and this supports this structure. constants: dict Dictionary of constants to substitute into rhs. *args: dict Additioinal symbols in f_vect that are not states, inputs or constants. **lambdify_kwargs: Additional arugments to pass to lambdify. @see sympy.utilities.lambdify.lambdify Returns ------- f : function A lambda function that compute the right hand side of the ode: f(t, x, u, *f_args). jac : function A lambda function that computes the jacobian of the right hand side with respect to the state x: jac(t, x, u, *jac_args). """ if constants is not None: rhs = rhs.subs(constants) x = sympy.DeferredVector('x') u = sympy.DeferredVector('u') ss_subs = {x_vect[i]: x[i] for i in range(len(x_vect))} ss_subs.update({u_vect[i]: u[i] for i in range(len(u_vect))}) if 'default_array' not in lambdify_kwargs.keys(): lambdify_kwargs['default_array'] = True f = sympy.lambdify((t, x, u) + args, rhs.subs(ss_subs), **lambdify_kwargs) jac_vect = rhs.jacobian(x_vect) jac = sympy.lambdify((t, x, u) + args, jac_vect.subs(ss_subs), **lambdify_kwargs) return (f, jac)
def _integrate_exact(f, tetrahedron): # # Note that # # \int_T f(x) dx = \int_T0 |J(xi)| f(P(xi)) dxi # # with # # P(xi) = x0 * (1-xi[0]-xi[1]) + x1 * xi[0] + x2 * xi[1]. # # and T0 being the reference tetrahedron [(0.0, 0.0), (1.0, 0.0), (0.0, # 1.0)]. # The determinant of the transformation matrix J equals twice the volume of # the tetrahedron. (See, e.g., # <http://math2.uncc.edu/~shaodeng/TEACHING/math5172/Lectures/Lect_15.PDF>). # xi = sympy.DeferredVector('xi') x_xi = \ + tetrahedron[0] * (1 - xi[0] - xi[1] - xi[2]) \ + tetrahedron[1] * xi[0] \ + tetrahedron[2] * xi[1] \ + tetrahedron[3] * xi[2] abs_det_J = 6 * quadpy.tetrahedron.volume(tetrahedron) exact = sympy.integrate( sympy.integrate( sympy.integrate(abs_det_J * f(x_xi), (xi[2], 0, 1 - xi[0] - xi[1])), (xi[1], 0, 1 - xi[0])), (xi[0], 0, 1)) return float(exact)
def test_serendipity_derivatives(): cell = UFCQuadrilateral() S = Serendipity(cell, 2) x = sympy.DeferredVector("X") X, Y = x[0], x[1] basis_functions = [ (1 - X) * (1 - Y), Y * (1 - X), X * (1 - Y), X * Y, Y * (1 - X) * (Y - 1), X * Y * (Y - 1), X * (1 - Y) * (X - 1), X * Y * (X - 1), ] points = [[0.5, 0.5], [0.25, 0.75]] for alpha, actual in S.tabulate(2, points).items(): expect = list( sympy.diff(basis, *zip([X, Y], alpha)) for basis in basis_functions) expect = list( [basis.subs(dict(zip([X, Y], point))) for point in points] for basis in expect) assert actual.shape == (8, 2) assert np.allclose(np.asarray(expect, dtype=float), actual.reshape(8, 2))
def __init__(self): from sympy import pi, sin, cos x = sympy.DeferredVector('x') u = ( +pi * 2 * sin(pi * x[1]) * cos(pi * x[1]) * sin(pi * x[0])**2, -pi * 2 * sin(pi * x[0]) * cos(pi * x[0]) * sin(pi * x[1])**2, ) p = cos(pi * x[0]) * sin(pi * x[1]) self.solution = { 'u': { 'value': u, 'degree': MAX_DEGREE }, 'p': { 'value': p, 'degree': MAX_DEGREE }, } self.mu = 1.0 self.f = { 'value': _get_stokes_rhs(u, p, self.mu), 'degree': MAX_DEGREE, } return
def to_riesz(self, poly_set): es = poly_set.get_expansion_set() ed = poly_set.get_embedded_degree() result = numpy.zeros(es.get_num_members(ed)) sd = self.ref_el.get_spatial_dimension() X = sympy.DeferredVector('x') dX = numpy.asarray([X[i] for i in range(sd)]) # evaluate bfs symbolically bfs = es.tabulate(ed, [dX])[:, 0] n = self.n qwts = self.Q.get_weights() for i in range(len(result)): thing = sympy.lambdify( X, sum([sympy.diff(bfs[i], dxi) * ni for dxi, ni in zip(dX, n)])) for j, pt in enumerate(self.deriv_dict.keys()): result[i] += qwts[j] * self.f_at_qpts[j] * thing(pt) return result
def _setup(self): self._F0_N_ssro, self._F1_N_ssro, self._F0_e_ssro, self._F1_e_ssro, self._F0_RO_pulse, self._F1_RO_pulse, self._P_min1, self._P_0 = \ sympy.symbols('F0_N_ssro F1_N_ssro F0_e_ssro F1_e_ssro F0_RO_pulse F1_RO_pulse P_min1 P_0') self._p_correlations = sympy.DeferredVector('p_correlations') # The total error matrix is the tensor product of the nitrogen and electron error matrices. # The nitrogen error matrix is built up of three matrices RO_err, CNOT_err and Init_err. # RO_err reflects fidelities of the electron RO, # CNOT_err reflects the fidelities in the pi (F0) and 2pi (F1) parts of the pi-2pi pulse (as CNOT gate). # Init_err includes populations in the three nitrogen lines after initialization in -1. #The inverse matrices are calculated before the tensorproduct, since this is much faster. self.error_matrix_N = (sympy.Matrix([[self._F0_N_ssro, 1.-self._F1_N_ssro],[1.-self._F0_N_ssro, self._F1_N_ssro]]) * \ sympy.Matrix([[self._F0_RO_pulse, 1.-self._F1_RO_pulse, 0.],[1.-self._F0_RO_pulse, self._F1_RO_pulse, 1.]]) * \ sympy.Matrix([[self._P_min1,self._P_0],[self._P_0,self._P_min1],[1.-self._P_0-self._P_min1,1.-self._P_0-self._P_min1]])) self.error_matrix_e = (sympy.Matrix( [[self._F0_e_ssro, 1. - self._F1_e_ssro], [1. - self._F0_e_ssro, self._F1_e_ssro]])) self.correction_matrix_N = self.error_matrix_N.inv() self.correction_matrix_e = self.error_matrix_e.inv() self.correction_matrix = TensorProduct(self.correction_matrix_N, self.correction_matrix_e) corr_vec = self.correction_matrix * \ sympy.Matrix([self._p_correlations[0], self._p_correlations[1], self._p_correlations[2], self._p_correlations[3]]) corr_p_correlations = corr_vec self.p0_formula = error.Formula() self.p0_formula.formula = corr_p_correlations
def problem_taylor(): '''Taylor--Green vortex, cf. <http://en.wikipedia.org/wiki/Taylor%E2%80%93Green_vortex>. ''' def mesh_generator(n): return RectangleMesh(Point(0.0, 0.0), Point(2 * pi, 2 * pi), n, n, 'crossed') mu = 1.0 rho = 1.0 cell_type = triangle x = sympy.DeferredVector('x') t = sympy.symbols('t') x0 = x[0] x1 = x[1] # F = sympy.exp(-2*mu*t) F = 1 - 2 * mu * t u = (sympy.sin(x0) * sympy.cos(x1) * F, -sympy.cos(x0) * sympy.sin(x1) * F, 0) p = rho / 4 * (sympy.cos(2 * x0) + sympy.cos(2 * x1)) * F**2 solution = { 'u': { 'value': u, 'degree': MAX_DEGREE }, 'p': { 'value': p, 'degree': MAX_DEGREE }, } f = { 'value': _get_navier_stokes_rhs(u, p), 'degree': MAX_DEGREE, } return mesh_generator, solution, f, mu, rho, cell_type
def problem_guermond2(): '''Problem 2 from section 3.7.2 in An overview of projection methods for incompressible flows; Guermond, Minev, Shen; Comp. Meth. in Appl. Mech. and Eng., vol. 195, 44-47, pp. 6011-6045; <http://www.sciencedirect.com/science/article/pii/S0045782505004640>. ''' def mesh_generator(n): return UnitSquareMesh(n, n, 'crossed') cell_type = triangle x = sympy.DeferredVector('x') t = sympy.symbols('t') u = (sympy.sin(x[0] + t) * sympy.sin(x[1] + t), sympy.cos(x[0] + t) * sympy.cos(x[1] + t)) p = sympy.sin(x[0] - x[1] + t) solution = { 'u': { 'value': u, 'degree': MAX_DEGREE }, 'p': { 'value': p, 'degree': MAX_DEGREE }, } f = { 'value': _get_navier_stokes_rhs(u, p), 'degree': MAX_DEGREE, } mu = 1.0 rho = 1.0 return mesh_generator, solution, f, mu, rho, cell_type
def problem_guermond1(): '''Problem 1 from section 3.7.1 in An overview of projection methods for incompressible flows; Guermond, Minev, Shen; Comp. Meth. in Appl. Mech. and Eng., vol. 195, 44-47, pp. 6011-6045; <http://www.sciencedirect.com/science/article/pii/S0045782505004640>. ''' def mesh_generator(n): return RectangleMesh(Point(-1, -1), Point(1, 1), n, n, 'crossed') cell_type = triangle x = sympy.DeferredVector('x') t = sympy.symbols('t') # m = sympy.exp(t) - 0.0 m = sympy.sin(t) u = (+pi * m * 2 * sympy.sin(pi * x[1]) * sympy.cos(pi * x[1]) * sympy.sin(pi * x[0])**2, -pi * m * 2 * sympy.sin(pi * x[0]) * sympy.cos(pi * x[0]) * sympy.sin(pi * x[1])**2) p = m * sympy.cos(pi * x[0]) * sympy.sin(pi * x[1]) solution = { 'u': { 'value': u, 'degree': MAX_DEGREE }, 'p': { 'value': p, 'degree': MAX_DEGREE } } f = {'value': _get_navier_stokes_rhs(u, p), 'degree': MAX_DEGREE} mu = 1.0 rho = 1.0 return mesh_generator, solution, f, mu, rho, cell_type
def __init__(self, f, past, helpers=(), control_pars=(), n_basic=None): self.past = past self.t, self.y, self.diff = self.past[-1] self.n = len(self.y) self.n_basic = n_basic or self.n self.last_garbage = -1 self.old_new_y = None self.parameters = [] from jitcdde._jitcdde import t, y, current_y, past_y, anchors Y = sympy.DeferredVector("Y") substitutions = list(helpers[::-1]) + [(y(i), Y[i]) for i in range(self.n)] past_calls = 0 f_wc = [] for entry in f(): new_entry = entry.subs(substitutions).simplify(ratio=1.0) past_calls += new_entry.count(anchors) f_wc.append(new_entry) F = sympy.lambdify([t, Y] + list(control_pars), f_wc, [{ anchors.__name__: self.get_past_anchors, past_y.__name__: self.get_past_value }, "math"]) self.f = lambda *args: np.array(F(*args)).flatten() self.anchor_mem = (len(past) - 1) * np.ones(past_calls, dtype=int)
def problem_whirl(): '''Example from Teodora I. Mitkova's text "Finite-Elemente-Methoden fur die Stokes-Gleichungen". ''' def mesh_generator(n): return UnitSquareMesh(n, n, 'left/right') cell_type = triangle x = sympy.DeferredVector('x') # t = sympy.symbols('t') # Note that the exact solution is indeed div-free. u = (x[0]**2 * (1 - x[0])**2 * 2 * x[1] * (1 - x[1]) * (2 * x[1] - 1), x[1]**2 * (1 - x[1])**2 * 2 * x[0] * (1 - x[0]) * (1 - 2 * x[0])) p = x[0] * (1 - x[0]) * x[1] * (1 - x[1]) solution = { 'u': { 'value': u, 'degree': 7 }, 'p': { 'value': p, 'degree': 4 }, } f = { 'value': _get_navier_stokes_rhs(u, p), 'degree': MAX_DEGREE, } mu = 1.0 rho = 1.0 return mesh_generator, solution, f, mu, rho, cell_type
def _lambdify(self, outputs): # TODO : We could forgo this substitution for generation speed # purposes and have lots of args for lambdify (like it used to be # done) but there may be some limitations on number of args. subs = {} vec_inputs = [] if self.specifieds is None: def_vecs = ['q', 'u', 'p'] else: def_vecs = ['q', 'u', 'r', 'p'] for syms, vec_name in zip(self.inputs, def_vecs): v = sm.DeferredVector(vec_name) for i, sym in enumerate(syms): subs[sym] = v[i] vec_inputs.append(v) try: outputs = [me.msubs(output, subs) for output in outputs] except AttributeError: # msubs doesn't exist in SymPy < 0.7.6. outputs = [output.subs(subs) for output in outputs] modules = [{'ImmutableMatrix': np.array}, 'numpy'] return sm.lambdify(vec_inputs, outputs, modules=modules)
def _get_data(dim, n, point_fun, symbolic): # points idxs = numpy.array(get_all_exponents(dim + 1, n)[-1]) points = point_fun(idxs) # weights weights = numpy.empty(len(points)) kk = 0 for idx in idxs: # Define the polynomial which to integrate over the simplex. t = sympy.DeferredVector("t") g = prod( sympy.poly((t[i] - point_fun(k)) / (point_fun(m) - point_fun(k))) for i, m in enumerate(idx) for k in range(m)) if isinstance(g, int): exp = [0] * (dim + 1) coeffs = g else: # make sure the exponents are all of the correct length exp = [list(m) + [0] * (dim - len(m) + 1) for m in g.monoms()] coeffs = g.coeffs() weights[kk] = sum(c * integrate_bary(m, symbolic) for c, m in zip(coeffs, exp)) kk += 1 return weights, points
def problem_taylor_cylindrical(): """Taylor--Green vortex, cf. <http://en.wikipedia.org/wiki/Taylor%E2%80%93Green_vortex>. """ alpha = 1.0 def mesh_generator(n): return RectangleMesh( Point(0.0 + alpha, 0.0), Point(2 * pi + alpha, 2 * pi), n, n, "crossed" ) mu = 1.0 rho = 1.0 cell_type = triangle x = sympy.DeferredVector("x") t = sympy.symbols("t") x0 = x[0] - alpha x1 = x[1] F = 1 - 2 * mu * t u = ( +sympy.sin(x0) * sympy.cos(x1) * F / x[0], -sympy.cos(x0) * sympy.sin(x1) * F / x[0], 0, ) p = rho / 4 * (sympy.cos(2 * x0) + sympy.cos(2 * x1)) * F ** 2 solution = { "u": {"value": u, "degree": numpy.infty}, "p": {"value": p, "degree": numpy.infty}, } f = {"value": _get_navier_stokes_rhs_cylindrical(u, p), "degree": numpy.infty} return mesh_generator, solution, f, mu, rho, cell_type
def problem_flat(): '''Nothing interesting happening in the domain. ''' def mesh_generator(n): return UnitSquareMesh(n, n, 'left/right') cell_type = triangle x = sympy.DeferredVector('x') u = (0.0 * x[0], 0.0 * x[1]) # Only grad(p) is of physical significance, and the numerical procedure # chooses p such that int(p) = 0. Also, numerical solutions have n.grad(p) # = 0 for non-penetration boundaries. Make sure that the exact solution # respects those things to make error analysis easier. # p = sympy.cos(pi * x[1]) p = -x[1] solution = { 'u': { 'value': u, 'degree': 1 }, 'p': { 'value': p, 'degree': 1 }, } f = { 'value': _get_navier_stokes_rhs(u, p), 'degree': MAX_DEGREE, } mu = 1.0 rho = 1.0 return mesh_generator, solution, f, mu, rho, cell_type
def __call__(self, fn): """Evaluate the functional on the function fn. Note that this depends on sympy being able to differentiate fn.""" x = list(self.deriv_dict.keys())[0] X = sympy.DeferredVector('x') dX = numpy.asarray([X[i] for i in range(len(x))]) dvars = tuple(d for d, a in zip(dX, self.alpha) for count in range(a)) return sympy.diff(fn(X), *dvars).evalf(subs=dict(zip(dX, x)))
def _newton_cotes(n, point_fun): ''' Construction after P. Silvester, Symmetric quadrature formulae for simplexes Math. Comp., 24, 95-100 (1970), <https://doi.org/10.1090/S0025-5718-1970-0258283-6>. ''' degree = n # points idx = numpy.array([[i, j, k, n - i - j - k] for i in range(n + 1) for j in range(n + 1 - i) for k in range(n + 1 - i - j)]) bary = point_fun(idx, n) points = bary[:, [1, 2, 3]] # weights if n == 0: weights = numpy.ones(1) return points, weights, degree def get_poly(t, m, n): return sympy.prod([ sympy.poly( (t - point_fun(k, n)) / (point_fun(m, n) - point_fun(k, n))) for k in range(m) ]) weights = numpy.empty(len(points)) idx = 0 for i in range(n + 1): for j in range(n + 1 - i): for k in range(n + 1 - i - j): L = n - i - j - k # Compute weight. # Define the polynomial which to integrate over the # tetrahedron. t = sympy.DeferredVector('t') g = get_poly(t[0], i, n) \ * get_poly(t[1], j, n) \ * get_poly(t[2], k, n) \ * get_poly(t[3], L, n) # The integral of monomials over a tetrahedron are well-known, # see Silvester. weights[idx] = numpy.sum([ c * numpy.prod([math.factorial(k) for k in m]) * 6.0 / math.factorial(numpy.sum(m) + 3) for m, c in zip(g.monoms(), g.coeffs()) ]) idx += 1 return points, weights, degree
def test_lambdify_matrix_vec_input(): X = sympy.DeferredVector('X') M = Matrix([[X[0]**2, X[0] * X[1], X[0] * X[2]], [X[1] * X[0], X[1]**2, X[1] * X[2]], [X[2] * X[0], X[2] * X[1], X[2]**2]]) f = lambdify(X, M, [{'ImmutableMatrix': numpy.array}, "numpy"]) Xh = array([1.0, 2.0, 3.0]) expected = array([[Xh[0]**2, Xh[0] * Xh[1], Xh[0] * Xh[2]], [Xh[1] * Xh[0], Xh[1]**2, Xh[1] * Xh[2]], [Xh[2] * Xh[0], Xh[2] * Xh[1], Xh[2]**2]]) actual = f(Xh) assert numpy.allclose(actual, expected)
def _integrate_exact(k, pyra): def f(x): return x[0] ** int(k[0]) * x[1] ** int(k[1]) * x[2] ** int(k[2]) # map the reference hexahedron [-1,1]^3 to the pyramid xi = sympy.DeferredVector("xi") pxi = ( +pyra[0] * (1 - xi[0]) * (1 - xi[1]) * (1 - xi[2]) / 8 + pyra[1] * (1 + xi[0]) * (1 - xi[1]) * (1 - xi[2]) / 8 + pyra[2] * (1 + xi[0]) * (1 + xi[1]) * (1 - xi[2]) / 8 + pyra[3] * (1 - xi[0]) * (1 + xi[1]) * (1 - xi[2]) / 8 + pyra[4] * (1 + xi[2]) / 2 ) pxi = [sympy.expand(pxi[0]), sympy.expand(pxi[1]), sympy.expand(pxi[2])] # determinant of the transformation matrix J = sympy.Matrix( [ [ sympy.diff(pxi[0], xi[0]), sympy.diff(pxi[0], xi[1]), sympy.diff(pxi[0], xi[2]), ], [ sympy.diff(pxi[1], xi[0]), sympy.diff(pxi[1], xi[1]), sympy.diff(pxi[1], xi[2]), ], [ sympy.diff(pxi[2], xi[0]), sympy.diff(pxi[2], xi[1]), sympy.diff(pxi[2], xi[2]), ], ] ) det_J = sympy.det(J) # we cannot use abs(), see <https://github.com/sympy/sympy/issues/4212>. # abs_det_J = sympy.Piecewise((det_J, det_J >= 0), (-det_J, det_J < 0)) # This is quite the leap of faith, but sympy will cowardly bail out # otherwise. abs_det_J = det_J exact = sympy.integrate( sympy.integrate( sympy.integrate(abs_det_J * f(pxi), (xi[2], -1, 1)), (xi[1], -1, +1) ), (xi[0], -1, +1), ) return float(exact)
def _integrate_exact(f, hexa): xi = sympy.DeferredVector('xi') pxi = \ + hexa[0] * 0.125*(1.0 - xi[0])*(1.0 - xi[1])*(1.0 - xi[2]) \ + hexa[1] * 0.125*(1.0 + xi[0])*(1.0 - xi[1])*(1.0 - xi[2]) \ + hexa[2] * 0.125*(1.0 + xi[0])*(1.0 + xi[1])*(1.0 - xi[2]) \ + hexa[3] * 0.125*(1.0 - xi[0])*(1.0 + xi[1])*(1.0 - xi[2]) \ + hexa[4] * 0.125*(1.0 - xi[0])*(1.0 - xi[1])*(1.0 + xi[2]) \ + hexa[5] * 0.125*(1.0 + xi[0])*(1.0 - xi[1])*(1.0 + xi[2]) \ + hexa[6] * 0.125*(1.0 + xi[0])*(1.0 + xi[1])*(1.0 + xi[2]) \ + hexa[7] * 0.125*(1.0 - xi[0])*(1.0 + xi[1])*(1.0 + xi[2]) pxi = [ sympy.expand(pxi[0]), sympy.expand(pxi[1]), sympy.expand(pxi[2]), ] # determinant of the transformation matrix J = sympy.Matrix([ [ sympy.diff(pxi[0], xi[0]), sympy.diff(pxi[0], xi[1]), sympy.diff(pxi[0], xi[2]) ], [ sympy.diff(pxi[1], xi[0]), sympy.diff(pxi[1], xi[1]), sympy.diff(pxi[1], xi[2]) ], [ sympy.diff(pxi[2], xi[0]), sympy.diff(pxi[2], xi[1]), sympy.diff(pxi[2], xi[2]) ], ]) det_J = sympy.det(J) # we cannot use abs(), see <https://github.com/sympy/sympy/issues/4212>. # pylint: disable=invalid-unary-operand-type abs_det_J = sympy.Piecewise((det_J, det_J >= 0), (-det_J, det_J < 0)) g_xi = f(pxi) exact = \ sympy.integrate( sympy.integrate( sympy.integrate(abs_det_J * g_xi, (xi[2], -1, 1)), (xi[1], -1, 1) ), (xi[0], -1, 1) ) return float(exact)
def to_riesz(self, poly_set): x = list(self.deriv_dict.keys())[0] X = sympy.DeferredVector('x') dx = numpy.asarray([X[i] for i in range(len(x))]) es = poly_set.get_expansion_set() ed = poly_set.get_embedded_degree() bfs = es.tabulate(ed, [dx])[:, 0] # Expand the multi-index as a series of variables to # differentiate with respect to. dvars = tuple(d for d, a in zip(dx, self.alpha) for count in range(a)) return numpy.asarray( [sympy.lambdify(X, sympy.diff(b, *dvars))(x) for b in bfs])
def problem_flat_cylindrical(): """Nothing interesting happening in the domain. """ def mesh_generator(n): return UnitSquareMesh(n, n, "left/right") cell_type = triangle # Coordinates ordered as (r, z, phi). x = sympy.DeferredVector("x") u = (0.0 * x[0], 0.0 * x[1], 0.0 * x[2]) p = -9.81 * x[1] solution = {"u": {"value": u, "degree": 1}, "p": {"value": p, "degree": 1}} f = {"value": _get_navier_stokes_rhs_cylindrical(u, p), "degree": numpy.infty} mu = 1.0 rho = 1.0 return mesh_generator, solution, f, mu, rho, cell_type
def _get_stokes_rhs(u, p, mu): '''Given a solution u of the Cartesian Navier-Stokes equations, return a matching right-hand side f. ''' x = sympy.DeferredVector('x') # Make sure that the exact solution is indeed analytically div-free. d = sympy.diff(u[0], x[0]) + sympy.diff(u[1], x[1]) d = sympy.simplify(d) assert d == 0 f0 = (-mu * (sympy.diff(u[0], x[0], 2) + sympy.diff(u[0], x[1], 2)) + sympy.diff(p, x[0])) f1 = (-mu * (sympy.diff(u[1], x[0], 2) + sympy.diff(u[1], x[1], 2)) + sympy.diff(p, x[1])) f = (sympy.simplify(f0), sympy.simplify(f1)) return f
def to_riesz(self, poly_set): x = list(self.deriv_dict.keys())[0] X = sympy.DeferredVector('x') dx = numpy.asarray([X[i] for i in range(len(x))]) es = poly_set.get_expansion_set() ed = poly_set.get_embedded_degree() bfs = es.tabulate(ed, [dx])[:, 0] # We need the gradient dotted with the normal. return numpy.asarray([ sympy.lambdify( X, sum([sympy.diff(b, dxi) * ni for dxi, ni in zip(dx, self.n)]))(x) for b in bfs ])
def _integrate_exact(f, quadrilateral): xi = sympy.DeferredVector("xi") pxi = (quadrilateral[0] * 0.25 * (1.0 + xi[0]) * (1.0 + xi[1]) + quadrilateral[1] * 0.25 * (1.0 - xi[0]) * (1.0 + xi[1]) + quadrilateral[2] * 0.25 * (1.0 - xi[0]) * (1.0 - xi[1]) + quadrilateral[3] * 0.25 * (1.0 + xi[0]) * (1.0 - xi[1])) pxi = [sympy.expand(pxi[0]), sympy.expand(pxi[1])] # determinant of the transformation matrix det_J = +sympy.diff(pxi[0], xi[0]) * sympy.diff( pxi[1], xi[1]) - sympy.diff(pxi[1], xi[0]) * sympy.diff(pxi[0], xi[1]) # we cannot use abs(), see <https://github.com/sympy/sympy/issues/4212>. abs_det_J = sympy.Piecewise((det_J, det_J >= 0), (-det_J, det_J < 0)) g_xi = f(pxi) exact = sympy.integrate(sympy.integrate(abs_det_J * g_xi, (xi[1], -1, 1)), (xi[0], -1, 1)) return float(exact)
def problem_guermond1_cylindrical(): """Cylindrical variation of Guermond's test problem. """ alpha = 1.5 def mesh_generator(n): return RectangleMesh( Point(-1 + alpha, -1), Point(1 + alpha, 1), n, n, "crossed" ) cell_type = triangle x = sympy.DeferredVector("x") t = sympy.symbols("t") x0 = x[0] - alpha x1 = x[1] # m = sympy.exp(t) - 0.0 m = sympy.sin(t) + 1.0 u = ( +pi * m * 2 * sympy.sin(pi * x1) * sympy.cos(pi * x1) * sympy.sin(pi * x0) ** 2 / x[0], -pi * m * 2 * sympy.sin(pi * x0) * sympy.cos(pi * x0) * sympy.sin(pi * x1) ** 2 / x[0], 0, ) p = m * sympy.cos(pi * x0) * sympy.sin(pi * x1) solution = { "u": {"value": u, "degree": numpy.infty}, "p": {"value": p, "degree": numpy.infty}, } f = {"value": _get_navier_stokes_rhs_cylindrical(u, p), "degree": numpy.infty} mu = 1.0 rho = 1.0 return mesh_generator, solution, f, mu, rho, cell_type
def _newton_cotes(n, point_fun): degree = n # points idx = numpy.array([[i, j, k, n - i - j - k] for i in range(n + 1) for j in range(n + 1 - i) for k in range(n + 1 - i - j)]) points = point_fun(idx, n) # weights if n == 0: weights = numpy.ones(1) return weights, points, weights, degree def get_poly(t, m, n): return sympy.prod([ sympy.poly( (t - point_fun(k, n)) / (point_fun(m, n) - point_fun(k, n))) for k in range(m) ]) weights = numpy.empty(len(points)) idx = 0 for i in range(n + 1): for j in range(n + 1 - i): for k in range(n + 1 - i - j): L = n - i - j - k # Compute weight. # Define the polynomial which to integrate over the # tetrahedron. t = sympy.DeferredVector("t") g = (get_poly(t[0], i, n) * get_poly(t[1], j, n) * get_poly(t[2], k, n) * get_poly(t[3], L, n)) # The integral of monomials over a tetrahedron are well-known, # see Silvester. weights[idx] = numpy.sum([ c * numpy.prod([math.factorial(k) for k in m]) * 6.0 / math.factorial(numpy.sum(m) + 3) for m, c in zip(g.monoms(), g.coeffs()) ]) idx += 1 return weights, points, degree, citation
def __init__(self): x = sympy.DeferredVector('x') u = (0 * x[0], 0 * x[1]) p = -9.81 * x[1] self.solution = { 'u': { 'value': u, 'degree': 1 }, 'p': { 'value': p, 'degree': 1 }, } self.mu = 1.0 self.f = { 'value': _get_stokes_rhs(u, p, self.mu), 'degree': MAX_DEGREE, } return
def problem_whirl_cylindrical(): """Example from Teodora I. Mitkova's text "Finite-Elemente-Methoden fur die Stokes-Gleichungen", adapted for cylindrical Navier-Stokes. """ alpha = 1.0 def mesh_generator(n): # return UnitSquareMesh(n, n, 'left/right') return RectangleMesh( Point(alpha, 0.0), Point(1.0 + alpha, 1.0), n, n, "left/right" ) cell_type = triangle x = sympy.DeferredVector("x") # Note that the exact solution is indeed div-free. x0 = x[0] - alpha x1 = x[1] u = ( x0 ** 2 * (1 - x0) ** 2 * 2 * x1 * (1 - x1) * (2 * x1 - 1) / x[0], x1 ** 2 * (1 - x1) ** 2 * 2 * x0 * (1 - x0) * (1 - 2 * x0) / x[0], 0, ) p = x0 * (1 - x0) * x1 * (1 - x1) solution = { "u": {"value": u, "degree": numpy.infty}, "p": {"value": p, "degree": 4}, } plot_solution = False if plot_solution: sol_u = Expression( (helpers.ccode(u[0]), helpers.ccode(u[1])), degree=numpy.infty, t=0.0, cell=cell_type, ) plot(sol_u, mesh=mesh_generator(20)) f = {"value": _get_navier_stokes_rhs_cylindrical(u, p), "degree": numpy.infty} mu = 1.0 rho = 1.0 return mesh_generator, solution, f, mu, rho, cell_type
def _get_navier_stokes_rhs(u, p): '''Given a solution u of the Cartesian Navier-Stokes equations, return a matching right-hand side f. ''' x = sympy.DeferredVector('x') t, mu, rho = sympy.symbols('t, mu, rho') # Make sure that the exact solution is indeed analytically div-free. d = sympy.diff(u[0], x[0]) + sympy.diff(u[1], x[1]) d = sympy.simplify(d) assert d == 0 # Get right-hand side associated with this solution, i.e., according # the Navier-Stokes # # rho (du_x/dt + u_x du_x/dx + u_y du_x/dy) # = - dp/dx + mu [d^2u_x/dx^2 + d^2u_x/dy^2] + f_x, # rho (du_y/dt + u_x du_y/dx + u_y du_y/dy) # = - dp/dx + mu [d^2u_y/dx^2 + d^2u_y/dy^2] + f_y, # du_x/dx + du_y/dy = 0. # # rho (du/dt + (u.\nabla)u) = -\nabla p + mu [\div(\nabla u)] + f, # div(u) = 0. # f0 = rho * (sympy.diff(u[0], t) + u[0] * sympy.diff(u[0], x[0]) + u[1] * sympy.diff(u[0], x[1]) ) \ + sympy.diff(p, x[0]) \ - mu * (sympy.diff(u[0], x[0], 2) + sympy.diff(u[0], x[1], 2)) f1 = rho * (sympy.diff(u[1], t) + u[0] * sympy.diff(u[1], x[0]) + u[1] * sympy.diff(u[1], x[1]) ) \ + sympy.diff(p, x[1]) \ - mu * (sympy.diff(u[1], x[0], 2) + sympy.diff(u[1], x[1], 2)) f = (sympy.simplify(f0), sympy.simplify(f1)) return f