diff --git a/Update_Scheme.py b/Update_Scheme.py new file mode 100644 index 0000000000000000000000000000000000000000..dfb518400b808255c0805bbfcab3a55e75ef7f04 --- /dev/null +++ b/Update_Scheme.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- +""" +@author: Laura C. Kühle + +d = detail coefficient (rename?) +other A (from M) = ? (Is it the same???) +A = basis_projection_left +M1 = wavelet_projection_left +phi = DG basis vector +psi = wavelet vector + +TODO: Find better names for A, B, M1, and M2 +TODO: Contemplate how to handle closing brackets \ + (end of line?, indented in new line?) -> Done (in line seems to be fine) +TODO: Contemplate giving option to change number of iterations for history \ + -> Done (history_threshold) +TODO: Rename counter -> Done (iteration) +TODO: Investigate whether list comprehension improves runtime \ + (compared to for-loops) -> Done (not really but more compact) +""" +import numpy as np +from sympy import Symbol, integrate + +from Vectors_of_Polynomials import OrthonormalLegendre, AlpertsWavelet + +x = Symbol('x') +xi = Symbol('z') + +class UpdateScheme(object): + + def __init__(self, detector, limiter, init_cond, mesh, wave_speed, + polynom_degree, num_grid_cells, final_time, history_threshold, + left_bound, right_bound): + # Unpack positional arguments + self.detector = detector + self.limiter = limiter + self.init_cond = init_cond + self.mesh = mesh + self.wave_speed = wave_speed + self.polynom_degree = polynom_degree + self.num_grid_cells = num_grid_cells + self.final_time = final_time + self.history_threshold = history_threshold + self.left_bound = left_bound + self.right_bound = right_bound + + self._reset() + pass + + def get_name(self): + return self.name + + def get_troubled_cell_history(self): + return self.troubled_cell_history + + def get_time_history(self): + return self.time_history + + def get_wavelet_coeffs(self, projection=None): + if projection is None: + return self.multiwavelet_coeffs + else: + return self._calculate_wavelet_coeffs(projection) + + def step(self, projection, cfl_number, time): + self.original_projection = projection + self.current_projection = projection + self.cfl_number = cfl_number + self.time = time + + self._apply_stability_method() + + self.iteration += 1 + + if (self.iteration % self.history_threshold) == 0: + self.troubled_cell_history.append(self.troubled_cells) + self.time_history.append(self.time) + + return self.current_projection, self.troubled_cells + + def _reset(self): + # Set fixed basis and wavelet vectors + self.basis = OrthonormalLegendre( + self.polynom_degree).get_vector(x) + self.wavelet = AlpertsWavelet(self.polynom_degree).get_vector(x) + + # Set additional necessary fixed instance variables + self.interval_len = self.right_bound-self.left_bound + self.cell_len = self.interval_len / self.num_grid_cells + self.M1 = self._set_multiwavelet_matrix(xi, -0.5*(xi-1), True) + self.M2 = self._set_multiwavelet_matrix(xi, 0.5*(xi+1), False) + + # Set matrix A + matrix = [] + for i in range(self.polynom_degree+1): + new_row = [] + for j in range(self.polynom_degree+1): + new_entry = -1.0 + if ((j < i) & ((i+j) % 2 == 1)): + new_entry = 1.0 + new_row.append(new_entry*np.sqrt((i+0.5) * (j+0.5))) + matrix.append(new_row) + self.A = np.array(matrix) # former: inv_mass @ np.array(matrix) + + # Set matrix B + matrix = [] + for i in range(self.polynom_degree+1): + new_row = [] + for j in range(self.polynom_degree+1): + new_entry = np.sqrt((i+0.5) * (j+0.5)) * (-1.0)**i + new_row.append(new_entry) + matrix.append(new_row) + self.B = np.array(matrix) # former: inv_mass @ np.array(matrix) + + # Initialize temporary instance variables + self.original_projection = [] + self.current_projection = [] + self.right_hand_side = [] + self.multiwavelet_coeffs = [] + self.troubled_cells = [] + self.troubled_cell_history = [] + self.time_history = [] + self.cfl_number = 0 + self.time = 0 + self.iteration = 0 + + def _set_multiwavelet_matrix(self, first_param, second_param, is_M1): + matrix = [] + for i in range(self.polynom_degree+1): + row = [] + for j in range(self.polynom_degree+1): + entry = integrate(self.basis[i].subs(x, first_param) + * self.wavelet[j].subs(x, second_param), + (xi, -1, 1)) + if (is_M1): + entry = entry * (-1)**(j + self.polynom_degree + 1) + row.append(np.float64(entry)) + matrix.append(row) + return matrix + + def _update_wavelet_coeffs(self): + projection = self.current_projection[:, 1: -1] + self.multiwavelet_coeffs = self._calculate_wavelet_coeffs(projection) + + def _calculate_wavelet_coeffs(self, projection): + transposed_vector = np.transpose(projection) + output_matrix = [] + for i in range(int(len(projection[0])/2)): + new_entry = 0.5*(transposed_vector[2*i] @ self.M1 + + transposed_vector[2*i+1] @ self.M2) + output_matrix.append(new_entry) + return np.transpose(np.array(output_matrix)) + + def _apply_limiter(self): + self._update_wavelet_coeffs() + self.troubled_cells = self.detector.get_cells(self.multiwavelet_coeffs, + self.current_projection) + + new_projection = self.current_projection.copy() + for cell in self.troubled_cells: + np.transpose(new_projection)[cell] = self.limiter.apply( + self.current_projection, cell) + + self.current_projection = new_projection + + def _enforce_boundary_condition(self): + transposed_projection = np.transpose(self.current_projection) + + transposed_projection[0] = transposed_projection[self.num_grid_cells] + transposed_projection[self.num_grid_cells+1] = transposed_projection[1] + + self.current_projection = np.transpose(transposed_projection) + + +class SSPRK3(UpdateScheme): + + def __init__(self, detector, limiter, init_cond, mesh, wave_speed, + polynom_degree, num_grid_cells, final_time, history_threshold, + left_bound, right_bound): + # Set name of update scheme + self.name = 'SSPRK3' + + super().__init__(detector, limiter, init_cond, mesh, wave_speed, + polynom_degree, num_grid_cells, final_time, + history_threshold, left_bound, right_bound) + + # Override method of superclass + def _apply_stability_method(self): + self._apply_first_step() + self._apply_limiter() + self._enforce_boundary_condition() + + self._apply_second_step() + self._apply_limiter() + self._enforce_boundary_condition() + + self._apply_third_step() + self._apply_limiter() + self._enforce_boundary_condition() + + def _update_right_hand_side(self): + # Transpose projection for easier calculation + transposed_projection = np.transpose(self.current_projection) + + # Initialze vector and set first entry to accommodate for ghost cell + right_hand_side = [0] + + for j in range(self.num_grid_cells): + right_hand_side.append( + 2*(self.A @ transposed_projection[j+1] + + self.B @ transposed_projection[j])) + + # Set ghost cells to respective value + right_hand_side[0] = right_hand_side[self.num_grid_cells] + right_hand_side.append(right_hand_side[1]) + + self.right_hand_side = np.transpose(right_hand_side) + + def _apply_first_step(self): + self._update_right_hand_side() + self.current_projection = self.original_projection \ + + (self.cfl_number*self.right_hand_side) + + def _apply_second_step(self): + self._update_right_hand_side() + self.current_projection = 1/4 * ( + 3*self.original_projection + + (self.current_projection + self.cfl_number*self.right_hand_side)) + + def _apply_third_step(self): + self._update_right_hand_side() + self.current_projection = 1/3 * ( + self.original_projection + + 2 * (self.current_projection + + self.cfl_number*self.right_hand_side))