def solve_states(self, left_state, right_state, x, t): f_min = lambda s: np.min( [self.flux_function.q_derivative(s, x, t), 0.0]) f_max = lambda s: np.max( [self.flux_function.q_derivative(s, x, t), 0.0]) f_min_integral = math_utils.quadrature(f_min, 0.0, right_state) f_max_integral = math_utils.quadrature(f_max, 0.0, left_state) numerical_flux = f_min_integral + f_max_integral + self.flux_function( 0.0, x, t) return numerical_flux
def test_quadrature(): f = lambda x: np.cos(x) int_f = lambda x: np.sin(x) x_left = 0.0 x_right = 1.0 approx_int = math_utils.quadrature(f, x_left, x_right) exact_int = int_f(x_right) - int_f(x_left) assert np.abs(approx_int - exact_int) <= tolerance for p in range(10): f = lambda x: np.power(x, p) int_f = lambda x: 1 / (p + 1) * np.power(x, p + 1) approx_int = math_utils.quadrature(f, x_left, x_right) exact_int = int_f(x_right) - int_f(x_left) assert np.abs(approx_int - exact_int) <= tolerance
def solve_states(self, left_state, right_state, x, t, n): # Nonconservative Flux # min_speed = s_l, max_speed = s_r # Let G = 1/2 \dintt{0}{1}{g(\psi(\tau, Q_l, Q_r))\psi_{\tau}}{\tau} # Let Q^* = (s_r Q_r - s_l Q_l + f(Q_l) - f(Q_r))/(s_r - s_l) - 1/(s_r - s_l) G # if min_speed = s_l > 0, # F = f(Q_l) - G # if min_speed = s_l < 0 and max_speed = s_r > 0 # F = 1/2 (f(Q_l) + f(Q_r)) + 1/2(s_r Q^* + s_l Q^* - s_l Q_l - s_r Q_r) # if max_speed = s_r < 0 # F = f(Q_r) + G tuple_ = self.problem.app_.wavespeeds_hlle(left_state, right_state, x, t, n) min_speed = tuple_[0] max_speed = tuple_[1] quad_func = self._get_quad_func(left_state, right_state) G = math_utils.quadrature(quad_func, 0.0, 1.0) if min_speed > 0: return self.flux_function(left_state) - G if max_speed < 0: return self.flux_function(right_state) + G else: f_r = self.flux_function(right_state) f_l = self.flux_function(left_state) q_star = (max_speed * right_state - min_speed * left_state + f_l - f_r - G) / (max_speed - min_speed) return 0.5 * (f_l + f_r) + 0.5 * ( (max_speed + min_speed) * q_star - min_speed * left_state - max_speed * right_state)
def compute_quadrature_strong(dg_solution, t, flux_function, i): # quadrature_function = M^{-1} \dintt{D_i}{f(Q_i, x, t)_x \Phi}{x} # M^{-1} \dintt{D_i}{f(Q_i(x), x, t)_x \Phi(xi(x))}{x} # M^{-1} \dintt{D_i}{(f_q(Q_i(x), x, t) (Q_i(x))_x + f_x(Q_i(x), x, t)) # \phi(xi(x))}{x} # M^{-1} \dintt{-1}{1}{(f_q(Q_i(xi), x(xi), t) (Q_i(xi))_x + f_x(Q_i(xi), x(xi), t)) # \phi(xi) dx/dxi}{xi} basis_ = dg_solution.basis mesh_ = dg_solution.mesh result = np.zeros(basis_.num_basis_cpts) # if first order then will be zero # if basis_.num_basis_cpts == 1: # return 0.0 for l in range(basis_.num_basis_cpts): def quadrature_function(xi): x = dg_solution.mesh.transform_to_mesh(xi, i) q = dg_solution.evaluate_canonical(xi, i) f_q = flux_function.q_derivative(q, x, t) q_x = dg_solution.x_derivative_canonical(xi, i) f_x = flux_function.x_derivative(q, x, t) phi = basis_(xi, l) # elem_metrics[i] = dx/dxi return (f_q * q_x + f_x) * phi * mesh_.elem_metrics[i] result[l] = math_utils.quadrature(quadrature_function, -1.0, 1.0) result = np.matmul(basis_.mass_matrix_inverse, result) return result
def flux_basis_derivative_quadrature(dg_solution, flux_function, t, i): # \dintt{-1}{1}{\v{f}(\m{Q}_i \v{\Phi}, x_i(xi), t) \v{\phi}_{\xi}^T}{xi} mesh_ = dg_solution.mesh_ basis_ = dg_solution.basis_ def quad_func(xi): return np.outer( flux_function(dg_solution(xi, i), mesh_.transform_to_mesh(xi, i), t), basis_.derivative(xi), ) return math_utils.quadrature(quad_func, -1.0, 1.0, basis_.num_basis_cpts)
def test_compute_quadrature_matrix(): squared = flux_functions.Polynomial(degree=2) cubed = flux_functions.Polynomial(degree=3) initial_condition = functions.Sine() t = 0.0 x_left = 0.0 x_right = 1.0 for f in [squared, cubed]: for basis_class in basis.BASIS_LIST: for num_basis_cpts in range(1, 6): error_list = [] basis_ = basis_class(num_basis_cpts) for num_elems in [10, 20]: mesh_ = mesh.Mesh1DUniform(x_left, x_right, num_elems) dg_solution = basis_.project(initial_condition, mesh_) quadrature_matrix = ldg_utils.compute_quadrature_matrix( dg_solution, t, f) result = solution.DGSolution(None, basis_, mesh_) direct_quadrature = solution.DGSolution( None, basis_, mesh_) for i in range(mesh_.num_elems): # compute value as B_i Q_i result[i] = np.matmul(quadrature_matrix[i], dg_solution[i]) # also compute quadrature directly for l in range(basis_.num_basis_cpts): quadrature_function = (lambda xi: f( initial_condition( mesh_.transform_to_mesh(xi, i)), xi, ) * initial_condition( mesh_.transform_to_mesh(xi, i)) * basis_. derivative(xi, l)) direct_quadrature[i, l] = math_utils.quadrature( quadrature_function, -1.0, 1.0) # need to multiply by mass inverse direct_quadrature[i] = np.matmul( basis_.mass_matrix_inverse, direct_quadrature[i]) error = (result - direct_quadrature).norm() error_list.append(error) if error_list[-1] != 0.0: order = utils.convergence_order(error_list) assert order >= (num_basis_cpts - 1)
def compute_quadrature_matrix_weak(basis_, mesh_, t, flux_function, i): # function to compute quadrature_matrix B_i, useful for using dg_weak_form_matrix # B_i = M^{-1}\dintt{D_i}{a(x, t) \Phi(xi(x))_x \Phi^T(xi(x))}{x} # B_i = M^{-1}\dintt{-1}{1}{a(x(xi), t) \Phi(xi)_x \Phi^T(xi) dx/dxi}{xi} # B_i = M^{-1}\dintt{-1}{1}{a(x(xi), t) \Phi_xi(xi) dxi/dx \Phi^T(xi) dx/dxi}{xi} # B_i = M^{-1}\dintt{-1}{1}{a(x(xi), t) \Phi_xi(xi) \Phi^T(xi)}{xi} # f(Q, x, t) = a(x, t) Q num_basis_cpts = basis_.num_basis_cpts B = np.zeros((num_basis_cpts, num_basis_cpts)) for j in range(num_basis_cpts): for k in range(num_basis_cpts): def quadrature_function(xi): x = mesh_.transform_to_mesh(xi, i) phi_xi_j = basis_.derivative(xi, j) phi_k = basis_(xi, k) a = flux_function.q_derivative(0.0, x, t) return a * phi_xi_j * phi_k B[j, k] = math_utils.quadrature(quadrature_function, -1.0, 1.0) B = np.matmul(basis_.mass_matrix_inverse, B) return B
def compute_quadrature_weak(dg_solution, t, flux_function, i): # quadrature_function(i) = # \dintt{-1}{1}{\v{f}(\m{Q}_i \v{\Phi}, x, t) \v{\phi}_{\xi}^T}{x} M^{-1} basis_ = dg_solution.basis_ mesh_ = dg_solution.mesh_ # if first order then will be zero # phi_xi(xi) = 0 for 1st basis_cpt # assume basis has more than one component def quadrature_function(xi): x = mesh_.transform_to_mesh(xi, i) q = dg_solution.evaluate_canonical(xi, i) phi_xi = basis_.derivative(xi) return flux_function(q, x, t) * phi_xi result = math_utils.quadrature(quadrature_function, -1.0, 1.0, quad_order=basis_.num_basis_cpts) result = np.matmul(result, basis_.mass_matrix_inverse) return result
def limit_solution(self, problem, dg_solution): # update dg_solution to limited solution # also return limited solution num_elems = dg_solution.mesh_.num_elems mesh_ = dg_solution.mesh_ basis_ = dg_solution.basis_ # Step 1 # elem_min[i_elem, i_eqn] = min value of i_eqn on i_elem # elem_min[i, l] = w^l_{m_i} # elem_max[i_elem, i_eqn] = max value of i_eqn on i_elem # elem_max[i, l] = w^l_{M_i} if self.variable_transformation is None: elem_min = np.array( [ np.min(dg_solution.evaluate_canonical(self.xi_points, i), axis=1) for i in range(num_elems) ] ) elem_max = np.array( [ np.max(dg_solution.evaluate_canonical(self.xi_points, i), axis=1) for i in range(num_elems) ] ) elem_ave = np.array([dg_solution.elem_average(i) for i in range(num_elems)]) else: elem_min = np.array( [ np.min( self.variable_transformation( dg_solution.evaluate_canonical(self.xi_points, i) ), axis=1, ) for i in range(num_elems) ] ) elem_max = np.array( [ np.max( self.variable_transformation( dg_solution.evaluate_canonical(self.xi_points, i) ), axis=1, ) for i in range(num_elems) ] ) elem_ave = np.zeros(elem_max.shape) for i in range(num_elems): def quadrature_function(xi): return self.variable_transformation( dg_solution.evaluate_canonical(xi, i) ) elem_ave[i] = 0.5 * math_utils.quadrature( quadrature_function, -1, 1, basis_.num_basis_cpts ) num_eqns = elem_min.shape[1] # Step 2 # upper_bounds[i, l] = M_i^l = max{\bar{w}_i^l + \alpha(h), max_{N}{w_{M_j}^l}} upper_bounds = np.zeros((num_elems, num_eqns)) # lower_bounds[i, l] = m_i^l = min{\bar{w}_i^l - \alpha(h), min_{N}{w_{m_j}^l}} lower_bounds = np.zeros((num_elems, num_eqns)) for i_elem in mesh_.boundary_elems: h = mesh_.elem_volumes[i_elem] alpha_h = self._alpha_function(h) neighbors = problem.boundary_condition.get_neighbors_indices(mesh_, i_elem) neighbor_max = np.max([elem_max[j] for j in neighbors], axis=0) neighbor_min = np.min([elem_min[j] for j in neighbors], axis=0) upper_bounds[i_elem] = np.maximum(elem_ave[i_elem] + alpha_h, neighbor_max) lower_bounds[i_elem] = np.minimum(elem_ave[i_elem] - alpha_h, neighbor_min) for i_elem in mesh_.interior_elems: h = mesh_.elem_volumes[i_elem] alpha_h = self._alpha_function(h) neighbors = mesh_.get_neighbors_indices(i_elem) neighbor_max = np.max([elem_max[j] for j in neighbors], axis=0) neighbor_min = np.min([elem_min[j] for j in neighbors], axis=0) upper_bounds[i_elem] = np.maximum(elem_ave[i_elem] + alpha_h, neighbor_max) lower_bounds[i_elem] = np.minimum(elem_ave[i_elem] - alpha_h, neighbor_min) # Step 3 # \theta_{M_i} = \min_l{\phi((M_i^l - \bar{w}_i^l)/(w_{M_i}^l - \bar{w}_i^l))} # max_limiting[i] = min[l]{_phi((upper_bounds[i] - elem_ave[i]) # /(elem_max[i] - elem_ave[i]))} # \theta_{m_i} = \min_l{\phi((m_i^l - \bar{w}_i^l)/(w_{m_i}^l - \bar{w}_i^l))} # min_limiting[i] = min[l]{_phi((lower_bounds[i] - elem_ave[i]) # /(elem_min[i] - elem_ave[i]))} # Step 4 # \theta_i = min{1, \theta_{m_i}, \theta_{M_i}} # limiting[i] = min{1, min_limiting[i], max_limiting[i]} max_limiting = np.zeros(num_elems) min_limiting = np.zeros(num_elems) limiting = np.zeros(num_elems) for i_elem in range(num_elems): max_limiting[i_elem] = np.min( self._phi( np.abs((upper_bounds[i_elem] - elem_ave[i_elem])) / (np.abs((elem_max[i_elem] - elem_ave[i_elem])) + 1e-10) ) ) min_limiting[i_elem] = np.min( self._phi( np.abs((lower_bounds[i_elem] - elem_ave[i_elem])) / (np.abs((elem_min[i_elem] - elem_ave[i_elem])) + 1e-10) ) ) limiting[i_elem] = min(1, min_limiting[i_elem], max_limiting[i_elem]) # Step 5 # Limit Solution # \tilde{q}^h(x) = \bar{q}_i + \theta_i (q^h(x) - \bar{q}_i) basis_.limit_higher_moments(dg_solution, limiting) return dg_solution