def _coeff_initialise(self,mesh,coeff_pre): """Initialises self.coeff equal to coeff_pre, but sets up Firedrake Constant structure to allow for sampling. Parameters: mesh - a Firedrake mesh. coeff_pre - see init. """ if self._coeff_dims == [2,2]\ and coeff_pre != fd.as_matrix([[1.0,0.0],[0.0,1.0]]): warnings.warn("coeff_pre is not the identity. There is no\ guarantee that the randomly-generated matrices are\ positive-definite, or have the correct amount of noise.") d = mesh.geometric_dimension() # Bit of a hack, set up num_pieces num_pieces_list = [] [num_pieces_list.append(self._num_pieces) for ii in range(d)] # Set up all the Firedrake Constants: self._constant_array = np.empty(num_pieces_list,dtype=object) with np.nditer(self._constant_array,flags=['refs_ok'],op_flags=['writeonly']) as array_it: for const in array_it: if self._coeff_dims == [2,2]: const[...] = fd.Constant(np.array([[0.0,0.0],[0.0,0.0]]),domain=mesh) elif self._coeff_dims == [1]: const[...] = fd.Constant(0.0,domain=mesh) # Form coeff by looping over all the subdomains x = fd.SpatialCoordinate(mesh) self.coeff = coeff_pre array_it = np.nditer(self._constant_array,flags=['refs_ok','multi_index']) while not array_it.finished: const = array_it[0] loc_array = np.array((array_it.multi_index,1+np.array(array_it.multi_index)) ,dtype='float').T/float(self._num_pieces) self.coeff += utils.nd_indicator(x,const,loc_array) array_it.iternext()
def sharp_cutoff(self, centre, width, apply_to_preconditioner=False): """Applies a sharp cutoff function to A&n. Applying this function means A=I and n=1 on the boundary The cutoff function is 1 on a square/cube, and zero outside the square/cube. Inputs: centre - numpy array containing the coordinates of the centre of the cutoff zone. width - the width of the zone on which the original values of A & n hold. apply_to_preconditioner - boolean - if true, does this only to the preconditioner, rather than the problem itself. Used mainly when setting an already-existing problem as the preconditioner. """ x = fd.SpatialCoordinate(self.V.mesh()) dim = self.V.mesh().geometric_dimension() indicator_region = np.array(centre)\ + np.repeat(0.5*np.array([-width,width],ndmin=2), dim,axis=0) ind = nd_indicator(x, 1.0, indicator_region) identity = fd.as_matrix([[1.0, 0.0], [0.0, 1.0]]) if apply_to_preconditioner: self.set_n_pre(1.0 + ind * (self._n_pre - 1.0)) self.set_A_pre(identity + ind * (self._A_pre - identity)) else: self.set_n(1.0 + ind * (self._n - 1.0)) self.set_A(identity + ind * (self._A - identity))
k = 30.0 # Define a mesh that keeps pollution error bounded num_cells = h_to_num_cells(k**-1.5, 2) L = 1.0 mesh = SquareMesh(num_cells, num_cells, L) # Use piecewise-linear finite elements V = FunctionSpace(mesh, "CG", 1) # n = 1 inside a square of size 1/3 x 1/3, and n=0.5 outside x = SpatialCoordinate(mesh) square_limits = np.array([[1.0 / 3.0, 2.0 / 3.0], [1.0 / 3.0, 2.0 / 3.0]]) n = 0.5 + nd_indicator(x, 0.5, square_limits) # Define the problem prob = hh.HelmholtzProblem(k, V, n=n) # Use f and g corresponding to a plane wave (in homogeneous media) prob.f_g_plane_wave() # Use MUMPS as a direct solver prob.use_mumps() # Solve the problem prob.solve() # Plot the solution prob.plot()
def nearby_preconditioning_piecewise_experiment_set( A_pre_type,n_pre_type,dim,num_pieces,seed,num_repeats, k_list,h_list,p_list,noise_master_level_list,noise_modifier_list, save_location): """Test nearby preconditioning for a range of parameter values. Performs nearby preconditioning tests for a range of values of k, the mesh size h, and the size of the random noise (which can be specified in terms of k and h). The random noise is piecewise constant on a grid unrelated to the finite-element mesh. Parameters: A_pre_type - string - options are 'constant', giving A_pre = [[1.0,0.0],[0.0,1.0]]. n_pre_type - string - options are 'constant', giving n_pre = 1.0; 'jump_down' giving n_pre = 2/3 on a central square of side length 1/3, and 1 otherwise; and 'jump_up' giving n_pre = 1.5 on a central square of side length 1/3, and 1 otherwise. dim - 2 or 3, the dimension of the problem. num_pieces - see helmholtz.coefficients.PieceWiseConstantCoeffGenerator. seed - see StochasticHelmholtzProblem. num_repeats - see nearby_preconditioning_test. k_list - list of positive floats - the values of k for which we will run experiments. h_list - list of 2-tuples; in each tuple (call it t) t[0] should be a positive float and t[1] should be a float. These specify the values of the mesh size h for which we will run experiments. h = t[0] * k**t[1]. p_list - list of positive ints, the polynomial degrees to run experiments for. Degree >= 5 will be very slow because of the implementation in Firedrake. noise_master_level_list - list of 2-tuples, where each entry of the tuple is a positive float. This defines the values of base_noise_A and base_noise_n to be used in the experiments. Call a given tuple t. Then base_noise_A = t[0] and base_noise_n = t[1]. noise_modifier_list - list of 4-tuples; the entries of each tuple should be floats. Call a given tuple t. This modifies the base noise so that the L^\infty norms of A and n are less than or equal to (respectively) base_noise_A * h**t[0] * k**t[1] and base_noise_n * h**t[2] * k**t[3]. save_location - see utils.write_repeats_to_csv. """ if not(isinstance(A_pre_type,str)): raise TypeError("Input A_pre_type should be a string") elif A_pre_type is not "constant": raise HelmholtzNotImplementedError( "Currently only implemented A_pre_type = 'constant'.") if not(isinstance(n_pre_type,str)): raise TypeError("Input n_pre_type should be a string") if not(isinstance(k_list,list)): raise TypeError("Input k_list should be a list.") elif any(not(isinstance(k,float)) for k in k_list): raise TypeError("Input k_list should be a list of floats.") elif any(k <= 0 for k in k_list): raise TypeError( "Input k_list should be a list of positive floats.") if not(isinstance(h_list,list)): raise TypeError("Input h_list should be a list.") elif any(not(isinstance(h_tuple,tuple)) for h_tuple in h_list): raise TypeError("Input h_list should be a list of tuples.") elif any(len(h_tuple) is not 2 for h_tuple in h_list): raise TypeError("Input h_list should be a list of 2-tuples.") elif any(not(isinstance(h_tuple[0],float)) for h_tuple in h_list)\ or any(h_tuple[0] <= 0 for h_tuple in h_list): raise TypeError( "The first item of every tuple in h_list\ should be a positive float.") elif any(not(isinstance(h_tuple[1],float)) for h_tuple in h_list): raise TypeError( "The second item of every tuple in h_list should be a float.") if not(isinstance(noise_master_level_list,list)): raise TypeError( "Input noise_master_level_list should be a list.") elif any(not(isinstance(noise_tuple,tuple)) for noise_tuple in noise_master_level_list): raise TypeError( "Input noise_master_level_list should be a list of tuples.") elif any(len(noise_tuple) is not 2 for noise_tuple in noise_master_level_list): raise TypeError( "Input noise_master_level_list should be a list of 2-tuples.") elif any(any(not(isinstance(noise_tuple[i],float)) for i in range(len(noise_tuple))) for noise_tuple in noise_master_level_list): raise TypeError( "Input noise_master_level_list\ should be a list of 2-tuples of floats.") if not(isinstance(noise_modifier_list,list)): raise TypeError("Input noise_modifier_list should be a list.") elif any(not(isinstance(mod_tuple,tuple)) for mod_tuple in noise_modifier_list): raise TypeError( "Input noise_modifier_list should be a list of tuples.") elif any(len(mod_tuple) is not 4 for mod_tuple in noise_modifier_list): raise TypeError( "Input noise_modifier_list should be a list of 4-tuples.") elif any(any(not(isinstance(mod_tuple[i],float)) for i in range(len(mod_tuple))) for mod_tuple in noise_modifier_list): raise TypeError( "Input noise_modifier_list\ should be a list of 4-tuples of floats.") for k in k_list: for h_tuple in h_list: for p in p_list: h = h_tuple[0] * k**h_tuple[1] mesh_points = hh_utils.h_to_num_cells(h,dim) mesh = fd.UnitSquareMesh(mesh_points,mesh_points) V = fd.FunctionSpace(mesh, "CG", p) f = 0.0 d = fd.as_vector([1.0/fd.sqrt(2.0),1.0/fd.sqrt(2.0)]) x = fd.SpatialCoordinate(mesh) nu = fd.FacetNormal(mesh) g=1j*k*fd.exp(1j*k*fd.dot(x,d))*(fd.dot(d,nu)-1) if A_pre_type is "constant": A_pre = fd.as_matrix([[1.0,0.0],[0.0,1.0]]) if n_pre_type is "constant": n_pre = 1.0 elif n_pre_type is "jump_down": n_pre = (2.0/3.0)\ + hh_utils.nd_indicator( x,1.0/3.0, np.array([[1.0/3.0,2.0/3.0], [1.0/3.0,2.0/3.0], [1.0/3.0,2.0/3.0]] ) ) elif n_pre_type is "jump_up": n_pre = 1.5\ + hh_utils.nd_indicator( x,-1.0/2.0, np.array([[1.0/3.0,2.0/3.0], [1.0/3.0,2.0/3.0], [1.0/3.0,2.0/3.0]] ) ) for noise_master in noise_master_level_list: A_noise_master = noise_master[0] n_noise_master = noise_master[1] for modifier in noise_modifier_list: if fd.COMM_WORLD.rank == 0: print(k,h_tuple,noise_master,modifier) A_modifier = h ** modifier[0] * k**modifier[1] n_modifier = h ** modifier[2] * k**modifier[3] A_noise_level = A_noise_master * A_modifier n_noise_level = n_noise_master * n_modifier A_stoch = coeff.PiecewiseConstantCoeffGenerator( mesh,num_pieces,A_noise_level,A_pre,[2,2]) n_stoch = coeff.PiecewiseConstantCoeffGenerator( mesh,num_pieces,n_noise_level,n_pre,[1]) np.random.seed(seed) GMRES_its = nearby_preconditioning_experiment( V,k,A_pre,A_stoch,n_pre,n_stoch,f,g,num_repeats) if fd.COMM_WORLD.rank == 0: hh_utils.write_GMRES_its( GMRES_its,save_location, {'k' : k, 'h_tuple' : h_tuple, 'p' : p, 'num_pieces' : num_pieces, 'A_pre_type' : A_pre_type, 'n_pre_type' : n_pre_type, 'noise_master' : noise_master, 'modifier' : modifier, 'num_repeats' : num_repeats } )
print(k, flush=True) eps = eps_const / k**eps_power shift = np.array([[eps, 0.0], [0.0, 0.0]]) num_cells = h_to_num_cells(k**(-1.5), 2) mesh = fd.UnitSquareMesh(num_cells, num_cells) V = fd.FunctionSpace(mesh, "CG", 1) x = fd.SpatialCoordinate(mesh) n = 0.5 + nd_indicator(x, 1.0, discon + eps) n_pre = 0.5 + nd_indicator(x, 1.0, discon) A = fd.as_matrix([[1.0, 0.0], [0.0, 1.0]]) prob = HelmholtzProblem(k, V, A=A, n=n, A_pre=A, n_pre=n_pre) prob.f_g_plane_wave([np.cos(angle), np.sin(angle)]) prob.solve() storage = np.append(storage, np.array((k, prob.GMRES_its), ndmin=2), axis=0)
varying_coeff = fd.as_matrix([[1.0, 0.0], [0.0, 1.0]]) else: constant_to_multiply = 1.0 varying_coeff = 1.0 for ii in range(num_pieces): for jj in range(num_pieces): if np.mod(ii + jj, 2) == 1: value = +alpha * k_multipler else: value = -alpha * k_multipler fl_ii = float(ii) fl_jj = float(jj) print(ii, jj, value) varying_coeff += hh_utils.nd_indicator( x, value * constant_to_multiply, np.array([[fl_ii, fl_ii + 1.0], [fl_jj, fl_jj + 1.0]]) / float(num_pieces)) if A_vs_n: A = varying_coeff n = 1.0 else: A = fd.as_matrix([[1.0, 0.0], [0.0, 1.0]]) n = varying_coeff A_pre = fd.as_matrix([[1.0, 0.0], [0.0, 1.0]]) n_pre = 1.0 prob = hh.HelmholtzProblem(k=k, V=V, A=A, n=n, A_pre=A_pre, n_pre=n_pre)