def assemble_HJBi(self, t=None): """ Assembly matrix discretizing second order terms after diffusion moved to the explicit operator is subtracted. Whenever amount of diffusion used to make some row of an explicit operator monotonic exceeds the amount of natural diffusion at that node we call it artificial diffusion. In such case, this row of the implicit operator is multiplied by zero """ print('Assembling implicit operators') self.implicit_matrices = np.empty([self.csize_beta, self.csize_alpha], dtype=object) remaining_diffusion = Function(self.V) max_art_dif = 0.0 max_art_dif_loc = None diffusion_matrix = self.diag_matrix.copy() for ind_a, alpha in enumerate(self.ctrset_alpha): for ind_b, beta in enumerate(self.ctrset_beta): # TODO: Include assert making sure that diffusion is # non-negative on all of the internal nodes diffusion = self.parameters.diffusion(alpha, beta) if t is not None: diffusion.t = t diff = interpolate(diffusion, self.V).vector() if not np.all(diff >= 0): raise Exception("Choose non-negative diffusion") diff_vec = np.array([ diff[i] if i not in self.boundary_nodes_list else 0.0 for i in range(self.dim) ]) artdif = self.explicit_diffusion[ind_b][ind_a] - diff_vec if np.amax(artdif) > max_art_dif: max_art_dif = np.amax(artdif) max_art_dif_loc = self.coords[np.argmax(artdif)] # discretise second order terms remaining_diffusion.vector()[:] = np.maximum( -artdif, [0] * self.dim) diffusion_matrix.set_diagonal(remaining_diffusion.vector()) Iab = matmult(diffusion_matrix, self.laplacian) self.implicit_matrices[ind_b][ind_a] = self.timestep_size * \ Iab + self.mass_matrix self.scipy_implicit_matrices = [[toscipy(mat) for mat in Ialist] for Ialist in self.implicit_matrices] print('Checking if the implicit operators satisfy' ' monotonicity conditions') for matrix_list in self.scipy_implicit_matrices: for matrix in matrix_list: implicit_check(matrix) with open(self.ad_data_path + '/ad.txt', 'a') as f: time_str = f' at time {t}\n' if t is not None else '\n' f.write(f'For mesh {self.mesh_name} max value of artificial' ' diffusion coefficient was' f' {max_art_dif} at {max_art_dif_loc}' + time_str)
def assemble_lumpedmm(self): """ Assembly lumped mass matrix - plays role of identity matrix """ print("Assembling lumped mass matrix") mass_form = self.w * self.u * dx self.mass_matrix = assemble(mass_form) mass_action_form = action(mass_form, Constant(1)) self.MM_terms = assemble(mass_action_form) self.mass_matrix.zero() self.mass_matrix.set_diagonal(self.MM_terms) self.scipy_mass_matrix = toscipy(self.mass_matrix)
def assemble_lumpedmm(self): """ Assembly lumped mass matrix - equal to 0 on robin boundary since there is no time derivative there. """ print("Assembling lumped mass matrix") mass_form = self.w * self.u * dx mass_action_form = action(mass_form, Constant(1)) self.MM_terms = assemble(mass_action_form) for n in self.robint_nodes_list: self.MM_terms[n] = 1.0 for n in self.robin_nodes_list: self.MM_terms[n] = 0.0 self.mass_matrix = assemble(mass_form) self.mass_matrix.zero() self.mass_matrix.set_diagonal(self.MM_terms) self.scipy_mass_matrix = toscipy(self.mass_matrix)
def assemble_HJBe(self, t=None): """ Assembly explicit operator for every control in the control set, then apply artificial diffusion to all of them. Note that artificial diffusion is calculated differently on the boundary nodes. """ self.explicit_matrices = np.empty([self.csize_beta, self.csize_alpha], dtype=object) self.explicit_diffusion = np.empty([self.csize_beta, self.csize_alpha], dtype=object) diag_vector = Vector(self.mesh.mpi_comm(), self.dim) global_min_timestep = float('inf') # Create explicit operator for each control in control set for ind_a, a in enumerate(self.ctrset_alpha): for ind_b, b in enumerate(self.ctrset_beta): if (ind_a * self.csize_beta + ind_b) % 10 == 0: print(f'Assembling explicit matrix under control ' f'{ind_a*self.csize_beta + ind_b} out of ' f'{self.csize_alpha*self.csize_beta}') # coefficients in the interior advection_x = self.parameters.adv_x(a, b) advection_y = self.parameters.adv_y(a, b) reaction = self.parameters.lin(a, b) if t is not None: advection_x.t = t advection_y.t = t reaction.t = t # Discretize PDE (set values on boundary rows to zero) b = np.array([advection_x, advection_y]) E_int_form = (np.dot(b, grad(self.w)) + reaction * self.w) * self.u * dx explicit_matrix = assemble(E_int_form) # Calculate nodewise minimum diffusion required to # impose monotonicity min_diffusion = [0] * explicit_matrix.size(0) for rows, row_num in getrows(explicit_matrix, self.laplacian, ignore=self.boundary_nodes_list): min_diffusion[row_num] = calc_ad(rows, row_num) self.explicit_diffusion[ind_b][ind_a] = min_diffusion diffusion_matrix = apply_ad(self.laplacian, min_diffusion) explicit_matrix += diffusion_matrix explicit_matrix.get_diagonal(diag_vector) current_min_timestep = get_min_timestep( diag_vector, self.MM_terms, self.boundary_nodes_list) global_min_timestep = min(current_min_timestep, global_min_timestep) self.explicit_matrices[ind_b][ind_a] = toscipy(explicit_matrix) ####################################################################### min_timesteps = int(self.T / global_min_timestep) + 1 if not self.timesteps or self.timesteps < min_timesteps: self.timesteps = min_timesteps try: filename = (f'meshes/{self.parameters.domain}/' f'Mvalues-{self.parameters.experiment}.json') with open(filename, 'r') as f: min_timesteps_dict = json.load(f) except FileNotFoundError: min_timesteps_dict = {} min_timesteps_dict[self.parameters.mesh_name] = self.timesteps with open(filename, 'w') as f: json.dump(min_timesteps_dict, f) self.timestep_size = self.T / self.timesteps # time step size self.parameters.calculate_save_interval() self.explicit_matrices = [[ self.timestep_size * E - self.scipy_mass_matrix for E in Elist ] for Elist in self.explicit_matrices] print('Checking if the explicit operators satisfy' ' monotonicity conditions') for matrix_list in self.explicit_matrices: for matrix in matrix_list: explicit_check(matrix, self.boundary_nodes_list)
def assemble_HJBi(self, t=None): """ Assembly matrix discretizing second order terms after diffusion moved to the explicit operator is subtracted. Whenever amount of diffusion used to make some row of an explicit operator monotonic exceeds the amount of natural diffusion at that node we call it artificial diffusion. In such case, this row of the implicit operator is multiplied by zero """ print('Assembling implicit operators') self.implicit_matrices = [] remaining_diffusion = Function(self.V) max_art_dif = 0.0 max_art_dif_loc = None diffusion_matrix = self.diag_matrix.copy() for explicit_diffusion, alpha in \ zip(self.explicit_diffusion, self.control_set): diffusion = self.parameters.diffusion(alpha) if t is not None: diffusion.t = t diff = interpolate(diffusion, self.V).vector() if not np.all(diff >= 0): raise Exception("Choose non-negative diffusion") diff_vec = np.array([ diff[i] if i not in self.boundary_nodes_list else 0.0 for i in range(self.dim) ]) artificial_diffusion = explicit_diffusion - diff_vec if np.amax(artificial_diffusion) > max_art_dif: max_art_dif = np.amax(artificial_diffusion) max_art_dif_loc = self.coords[np.argmax(artificial_diffusion)] # discretise second order terms remaining_diffusion.vector()[:] = np.maximum( -artificial_diffusion, [0] * self.dim) diffusion_matrix.set_diagonal(remaining_diffusion.vector()) implicit_matrix = matmult(diffusion_matrix, self.laplacian) for j in self.parameters.regions['Robin']: self.set_directional_derivative(implicit_matrix, region=j, nodes=self.robin_nodes_dict[j], control=alpha, time=t) self.implicit_matrices.append(self.timestep_size * implicit_matrix + self.mass_matrix) self.scipy_implicit_matrices = [ toscipy(mat) for mat in self.implicit_matrices ] print('Checking if the implicit operators satisfy' ' monotonicity conditions') for implicit_matrix in self.scipy_implicit_matrices: implicit_check(implicit_matrix) with open(self.ad_data_path + '/ad.txt', 'a') as f: time_str = f' at time {t}\n' if t is not None else '\n' f.write(f'For mesh {self.mesh_name} max value of artificial' ' diffusion coefficient was' f' {max_art_dif} at {max_art_dif_loc}' + time_str)
def assemble_HJBe(self, t=None): """ Assembly explicit operator for every control in the control set, then apply artificial diffusion to all of them. Note that artificial diffusion is calculated differently on the boundary nodes. """ self.explicit_matrices = np.empty(self.control_set_size, dtype=object) self.explicit_diffusion = np.empty([self.control_set_size, self.dim]) diagonal_vector = Vector(self.mesh.mpi_comm(), self.dim) global_min_timestep = float('inf') # Create explicit operator for each control in control set for k, alpha in enumerate(self.control_set): if k % 10 == 0: print(f'Assembling explicit matrix under control {k}' f' out of {self.control_set_size}') # coefficients in the interior advection_x = self.parameters.adv_x(alpha) advection_y = self.parameters.adv_y(alpha) reaction = self.parameters.lin(alpha) if t is not None: advection_x.t = t advection_y.t = t reaction.t = t # Discretize PDE (set values on boundary rows to zero) b = np.array([advection_x, advection_y]) E_interior_form = (np.dot(b, grad(self.w)) + reaction * self.w) * self.u * dx explicit_matrix = assemble(E_interior_form) set_zero_rows(explicit_matrix, self.boundary_nodes_list) # Calculate diffusion necessary to make explicit operator monotone min_diffusion = np.zeros(explicit_matrix.size(0)) for rows, row_num in getrows(explicit_matrix, self.laplacian, ignore=self.boundary_nodes_list): min_diffusion[row_num] = calc_ad(rows, row_num) self.explicit_diffusion[k] = min_diffusion discrete_diffusion = apply_ad(self.laplacian, min_diffusion) explicit_matrix += discrete_diffusion for j in self.parameters.regions['RobinTime']: self.set_directional_derivative( explicit_matrix, region=j, nodes=self.robint_nodes_dict[j], control=alpha, time=t) explicit_matrix.get_diagonal(diagonal_vector) current_min_timestep = get_min_timestep( diagonal_vector, self.MM_terms, self.dirichlet_nodes_list | self.robin_nodes_list) global_min_timestep = min(current_min_timestep, global_min_timestep) self.explicit_matrices[k] = toscipy(explicit_matrix) ####################################################################### min_timesteps = int(self.T / global_min_timestep) + 1 if not self.timesteps or self.timesteps < min_timesteps: self.timesteps = min_timesteps try: filename = (f'meshes/{self.parameters.domain}/' f'Mvalues-{self.parameters.experiment}.json') with open(filename, 'r') as f: min_timesteps_dict = json.load(f) except FileNotFoundError: min_timesteps_dict = {} min_timesteps_dict[self.parameters.mesh_name] = self.timesteps with open(filename, 'w') as f: json.dump(min_timesteps_dict, f) self.timestep_size = self.T / self.timesteps # time step size self.parameters.calculate_save_interval() self.explicit_matrices = self.timestep_size * self.explicit_matrices - \ np.repeat(self.scipy_mass_matrix, self.control_set_size) print('Checking if the explicit operators satisfy' ' monotonicity conditions') for explicit_matrix in self.explicit_matrices: explicit_check(explicit_matrix, self.dirichlet_nodes_list)