def calculate_2D_normals(mesh, elem_ID, face_ID, quad_pts): ''' Calculate the normals for 2D shapes (triangles and quadrilaterals). Inputs: ------- mesh: mesh object elem_ID: element index face_ID: face index quad_pts: points in reference space at which to calculate normals Outputs: -------- normals: normal vector [nq, ndims] ''' gbasis = mesh.gbasis gorder = mesh.gorder elem_coords = mesh.elements[elem_ID].node_coords ''' Get face coordinates ''' # Get local IDs of face nodes fnodes = gbasis.get_local_face_node_nums(gorder, face_ID) # Instantiate segment basis basis_seg = basis_defs.LagrangeSeg(gorder) # Compute basis values basis_ref_grad = basis_seg.get_grads(quad_pts) # Extract coordinates of face nodes face_coords = elem_coords[fnodes] ''' Calculate 2D normals ''' xphys_grad = np.matmul( face_coords.transpose(), basis_ref_grad)[:, :, 0] # gradient of physical space w.r.t ref space normals = xphys_grad[:, ::-1] normals[:, 1] *= -1. return normals # [nq, ndims]
def set_basis(order, basis_name): ''' Sets the basis class given the basis_name string argument Inputs: ------- order: solution order basis_name: name of the basis function we wish to instantiate as a class Outputs: -------- basis: instantiated basis class Raise: ------ If the basis class is not defined, returns a NotImplementedError ''' if BasisType[basis_name] == BasisType.LagrangeSeg: basis = basis_defs.LagrangeSeg(order) elif BasisType[basis_name] == BasisType.LegendreSeg: basis = basis_defs.LegendreSeg(order) elif BasisType[basis_name] == BasisType.LagrangeQuad: basis = basis_defs.LagrangeQuad(order) elif BasisType[basis_name] == BasisType.LegendreQuad: basis = basis_defs.LegendreQuad(order) elif BasisType[basis_name] == BasisType.LagrangeTri: basis = basis_defs.LagrangeTri(order) elif BasisType[basis_name] == BasisType.HierarchicH1Tri: basis = basis_defs.HierarchicH1Tri(order) else: raise NotImplementedError return basis
def ref_to_phys_time(mesh, time, dt, tref, basis=None): ''' This function converts reference time coordinates to physical time coordinates Intputs: -------- mesh: mesh object elem_ID: element ID time: current solution time dt: solution time step tref: time in reference space [nq, 1] basis: basis object Outputs: -------- tphys: coordinates in temporal space [nq, 1] ''' gorder = 1 if basis is None: basis = basis_defs.LagrangeSeg(gorder) basis.get_basis_val_grads(tref, get_val=True) tphys = (time / 2.) * (1. - tref) + (time + dt) / 2. * (1. + tref) return tphys, basis
def get_geom_data(self, mesh, basis, order): ''' Precomputes the geometric data for the ADER-DG scheme Inputs: ------- mesh: mesh object basis: basis object order: solution order Outputs: -------- self.jac_elems: precomputed Jacobian for each element [num_elems, nb, ndims, ndims] self.ijac_elems: precomputed inverse Jacobian for each element [num_elems, nb, ndims, ndims] self.djac_elems: precomputed determinant of the Jacobian for each element [num_elems, nb, 1] self.x_elems: precomputed coordinates of the nodal points in physical space [num_elems, nb, ndims] ''' ndims = mesh.ndims num_elems = mesh.num_elems nb = basis.nb gbasis = mesh.gbasis tile_basis = basis_defs.LagrangeSeg(order) # Define geometric basis for tiling jac, ijac, and djac xnodes = gbasis.get_nodes(order) nnodes = xnodes.shape[0] tile_xnodes = tile_basis.get_nodes(order) tile_nnodes = tile_xnodes.shape[0] # Allocate self.jac_elems = np.zeros([num_elems, nb, ndims, ndims]) self.ijac_elems = np.zeros([num_elems, nb, ndims, ndims]) self.djac_elems = np.zeros([num_elems, nb, 1]) self.x_elems = np.zeros([num_elems, nb, ndims]) for elem_ID in range(mesh.num_elems): # Jacobian djac, jac, ijac = basis_tools.element_jacobian(mesh, elem_ID, xnodes, get_djac=True, get_jac=True, get_ijac=True) self.jac_elems[elem_ID] = np.tile(jac, (tile_nnodes, 1, 1)) self.ijac_elems[elem_ID] = np.tile(ijac, (tile_nnodes, 1, 1)) self.djac_elems[elem_ID] = np.tile(djac, (tile_nnodes, 1)) # Physical coordinates of nodal points x = mesh_tools.ref_to_phys(mesh, elem_ID, xnodes) # Store self.x_elems[elem_ID] = np.tile(x, (tile_nnodes, 1))
def get_lagrange_basis_3D(xq, xnodes, basis_val=None, basis_ref_grad=None): ''' Calculates the 3D Lagrange basis functions Inputs: ------- xq: coordinates of quadrature points [nq, ndims] xnodes: coordinates of nodes in 1D ref space [nb, ndims] Outputs: -------- basis_val: evaluated basis [nq, nb] basis_ref_grad: evaluated gradient of basis [nq, nb, ndims] ''' if basis_ref_grad is not None: gradx = np.zeros((xq.shape[0], xnodes.shape[0], 1)) grady = np.zeros_like(gradx) gradz = np.zeros_like(gradx) else: gradx = None grady = None gradz = None # Always need basis_val valx = np.zeros((xq.shape[0], xnodes.shape[0])) valy = np.zeros_like(valx) valz = np.zeros_like(valx) # Get 1D basis values first nnodes_1D = xnodes.shape[0] lagrange_eq_seg = basis_defs.LagrangeSeg(nnodes_1D - 1) get_lagrange_basis_1D(xq[:, 0].reshape(-1, 1), xnodes, valx, gradx) get_lagrange_basis_1D(xq[:, 1].reshape(-1, 1), xnodes, valy, grady) get_lagrange_basis_1D(xq[:, 2].reshape(-1, 1), xnodes, valz, gradz) # Tensor products to get 3D basis values if basis_val is not None: for i in range(xq.shape[0]): basis_val[i, :] = np.reshape( np.outer(np.outer(valx[i, :], valy[i, :]), valz[i, :]), (-1, ), 'F') if basis_ref_grad is not None: for i in range(xq.shape[0]): basis_ref_grad[i, :, 0] = np.reshape( np.outer(np.outer(gradx[i, :, 0], valy[i, :]), valz[i, :]), (-1, ), 'F') basis_ref_grad[i, :, 1] = np.reshape( np.outer(np.outer(valx[i, :], grady[i, :, 0]), valz[i, :]), (-1, ), 'F') basis_ref_grad[i, :, 2] = np.reshape( np.outer(np.outer(valx[i, :], valy[i, :]), gradz[i, :, 0]), (-1, ), 'F')
def __init__(self, ndims=1, num_nodes=1, num_elems=1, gbasis=None, gorder=1): if gbasis is None: gbasis = basis_defs.LagrangeSeg(1) self.ndims = ndims self.num_nodes = num_nodes self.node_coords = None self.num_interior_faces = 0 self.interior_faces = [] self.num_boundary_groups = 0 self.boundary_groups = {} self.gbasis = gbasis self.gorder = gorder self.num_elems = num_elems self.num_nodes_per_elem = gbasis.get_num_basis_coeff(gorder) self.elem_to_node_IDs = np.zeros(0, dtype=int) self.elements = []
def create_gmsh_element_database(): ''' This function creates a database that holds information about all supported Gmsh elements. Outputs: -------- gmsh_element_database: Gmsh element database ''' gmsh_element_database = {} ''' Assume most element types are not supported Only fill in supported elements ''' # Point etype_num = 15 elem_data = GmshElementData() gmsh_element_database.update({etype_num: elem_data}) elem_data.gorder = 0 elem_data.gbasis = basis_defs.PointShape() # shape here instead of gbasis elem_data.num_nodes = 1 elem_data.node_order = np.array([0]) # Line segments (q = 1 to q = 11) etype_nums = np.array([1, 8, 26, 27, 28, 62, 63, 64, 65, 66]) for i in range(etype_nums.shape[0]): etype_num = etype_nums[i] elem_data = GmshElementData() gmsh_element_database.update({etype_num: elem_data}) gorder = i + 1 elem_data.gorder = gorder elem_data.gbasis = basis_defs.LagrangeSeg(gorder) elem_data.num_nodes = gorder + 1 elem_data.node_order = gmsh_node_order_seg(gorder) # Triangles (q = 1 to q = 10) etype_nums = np.array([2, 9, 21, 23, 25, 42, 43, 44, 45, 46]) for i in range(etype_nums.shape[0]): etype_num = etype_nums[i] elem_data = GmshElementData() gmsh_element_database.update({etype_num: elem_data}) gorder = i + 1 elem_data.gorder = gorder elem_data.gbasis = basis_defs.LagrangeTri(gorder) elem_data.num_nodes = (gorder + 1) * (gorder + 2) // 2 elem_data.node_order = gmsh_node_order_tri(gorder) # Quadrilaterals (q = 1 to q = 11) etype_nums = np.array([3, 10, 36, 37, 38, 47, 48, 49, 50, 51]) for i in range(etype_nums.shape[0]): etype_num = etype_nums[i] elem_data = GmshElementData() gmsh_element_database.update({etype_num: elem_data}) gorder = i + 1 elem_data.gorder = gorder elem_data.gbasis = basis_defs.LagrangeQuad(gorder) elem_data.num_nodes = (gorder + 1)**2 elem_data.node_order = gmsh_node_order_quadril(gorder) return gmsh_element_database
def mesh_1D(num_elems=10, xmin=-1., xmax=1.): ''' This function creates a 1D uniform mesh. Inputs: ------- num_elems: number of mesh elements xmin: minimum x-coordinate xmax: maximum x-coordinate Outputs: -------- mesh: mesh object Notes: ------ Two boundary groups are created: x1: located at x = xmin x2: located at x = xmax ''' ''' Create mesh and set node coordinates ''' num_nodes = num_elems + 1 mesh = mesh_defs.Mesh(ndims=1, num_nodes=num_nodes) mesh.node_coords = np.zeros([mesh.num_nodes, mesh.ndims]) mesh.node_coords[:, 0] = np.linspace(xmin, xmax, mesh.num_nodes) # Set parameters mesh.set_params(gbasis=basis_defs.LagrangeSeg(1), gorder=1, num_elems=num_elems) ''' Interior faces ''' mesh.num_interior_faces = num_elems - 1 mesh.allocate_interior_faces() for i in range(mesh.num_interior_faces): interior_face = mesh.interior_faces[i] interior_face.elemL_ID = i interior_face.faceL_ID = 1 interior_face.elemR_ID = i + 1 interior_face.faceR_ID = 0 ''' Boundary groups and faces ''' # Left boundary_group = mesh.add_boundary_group("x1") boundary_group.num_boundary_faces = 1 boundary_group.allocate_boundary_faces() boundary_face = boundary_group.boundary_faces[0] boundary_face.elem_ID = 0 boundary_face.face_ID = 0 # Right boundary_group = mesh.add_boundary_group("x2") boundary_group.num_boundary_faces = 1 boundary_group.allocate_boundary_faces() boundary_face = boundary_group.boundary_faces[0] boundary_face.elem_ID = num_elems - 1 boundary_face.face_ID = 1 ''' Create element-to-node-ID map ''' mesh.allocate_elem_to_node_IDs_map() for elem_ID in range(mesh.num_elems): for n in range(mesh.num_nodes_per_elem): mesh.elem_to_node_IDs[elem_ID][n] = elem_ID + n ''' Create element objects ''' mesh.create_elements() return mesh
import numerics.basis.tools as basis_tools import processing.plot as plot ''' Parameters ''' p = 4 # polynomial order plot_all = True # if True, will plot all basis functions b = 0 # the (b+1)th basis function will be plotted if plot_all is False # Node type (matters for nodal basis only) node_type = "Equidistant" # node_type = "GaussLobatto" # Basis type basis = basis_defs.LagrangeSeg(p) # Lagranage basis # basis = basis_defs.LegendreSeg(p) # Legendre basis ''' Pre-processing ''' # Solution nodes basis.get_1d_nodes = basis_tools.set_1D_node_calc(node_type) # Sample points p_plot = 100 # (p_plot + 1) points xp = basis.equidistant_nodes(p_plot) ''' Evaluate ''' basis.get_basis_val_grads(xp, get_val=True) ''' Plot