示例#1
0
文件: tools.py 项目: IhmeGroup/quail
def L2_projection(mesh, iMM, basis, quad_pts, quad_wts, f, U):
    '''
	Performs an L2 projection

	Inputs:
	-------
		mesh: mesh object
		iMM: space-time inverse mass matrix
		basis: basis object
		quad_pts: quadrature coordinates in reference space
		quad_wts: quadrature weights
		f: array of values to be projected from

	Outputs:
	--------
		U: array of values to be projected to
	'''
    if basis.basis_val.shape[0] != quad_wts.shape[0]:
        basis.get_basis_val_grads(quad_pts, get_val=True)

    for elem_ID in range(U.shape[0]):
        djac, _, _ = basis_tools.element_jacobian(mesh,
                                                  elem_ID,
                                                  quad_pts,
                                                  get_djac=True)
        rhs = np.matmul(basis.basis_val.transpose(),
                        f[elem_ID, :, :] * quad_wts * djac)  # [nb, ns]

        U[elem_ID, :, :] = np.matmul(iMM[elem_ID], rhs)
示例#2
0
    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))
示例#3
0
def element_volumes(mesh, solver=None):
    '''
	This function calculates total and per-element volumes

	Inputs:
	-------
		mesh: mesh object
		solver: solver object (e.g., DG, ADER-DG, etc.)

	Outputs:
	--------
		vol_elems: volume of each element [num_elems]
		domain_vol: total volume of the domain
	'''
    # Check if already calculated
    if solver is not None:
        if hasattr(solver.elem_helpers, "domain_vol") \
          and hasattr(solver.elem_helpers, "vol_elems"):
            return solver.elem_helpers.vol_elems, \
              solver.elem_helpers.domain_vol

    # Allocate, unpack
    vol_elems = np.zeros(mesh.num_elems)
    gorder = mesh.gorder
    gbasis = mesh.gbasis

    # Get quadrature data
    quad_order = gbasis.get_quadrature_order(mesh, gorder)
    quad_pts, quad_wts = gbasis.get_quadrature_data(quad_order)

    # Get element volumes
    for elem_ID in range(mesh.num_elems):
        djac, _, _ = basis_tools.element_jacobian(mesh,
                                                  elem_ID,
                                                  quad_pts,
                                                  get_djac=True)
        vol_elems[elem_ID] = np.sum(quad_wts * djac)

    # Get domain volume
    domain_vol = np.sum(vol_elems)

    return vol_elems, domain_vol  # [num_elems], [1]
示例#4
0
文件: DG.py 项目: IhmeGroup/quail
    def get_basis_and_geom_data(self, mesh, basis, order):
        '''
		Precomputes the basis and geometric data for each boundary face

		Inputs:
		-------
			mesh: mesh object
			basis: basis object
			order: solution order

		Outputs:
		--------
			self.faces_to_basis: basis values evaluated at quadrature points
				of each face [nfaces_per_elem, nq, nb]
			self.faces_to_basis_ref_grad: gradient of basis values 
				evaluated at quadrature points of each face for interior element
				[nfaces_per_elem, nq, nb, ndims]
			self.faces_to_xref: coordinates of quadrature points of each
				face converted to element reference space
				[nfaces_per_elem, nq, ndims]		
			self.normals_bgroups: precomputed normal vectors at each
				boundary face [num_boundary_faces, nq, ndims]
			self.x_bgroups: precomputed physical coordinates of the
				quadrature points [num_boundary_faces, nq, ndims]
			self.ijac_bgroups: stores the evaluated inverse of the geometric 
				Jacobian for each interior element 	
			self.face_lengths_bgroups: stores the precomputed length of each face
				[num_interior_faces, 1]
		'''
        ndims = mesh.ndims
        quad_pts = self.quad_pts
        quad_wts = self.quad_wts
        nq = quad_pts.shape[0]
        nb = basis.nb
        nfaces_per_elem = basis.NFACES

        # Allocate
        self.faces_to_basis = np.zeros([nfaces_per_elem, nq, nb])
        self.faces_to_xref = np.zeros([nfaces_per_elem, nq, basis.NDIMS])
        self.faces_to_basis_ref_grad = np.zeros(
            [nfaces_per_elem, nq, nb, basis.NDIMS])

        # Get values on each face (from interior perspective)
        for face_ID in range(nfaces_per_elem):
            self.faces_to_xref[face_ID] = basis.get_elem_ref_from_face_ref(
                face_ID, quad_pts)
            basis.get_basis_face_val_grads(mesh,
                                           face_ID,
                                           quad_pts,
                                           get_val=True,
                                           get_ref_grad=True)
            self.faces_to_basis[face_ID] = basis.basis_val
            self.faces_to_basis_ref_grad[face_ID] = basis.basis_ref_grad

        # Get boundary information
        i = 0
        for bgroup in mesh.boundary_groups.values():
            self.normals_bgroups.append(
                np.zeros([bgroup.num_boundary_faces, nq, ndims]))
            self.x_bgroups.append(
                np.zeros([bgroup.num_boundary_faces, nq, ndims]))
            self.ijac_bgroups.append(
                np.zeros([bgroup.num_boundary_faces, nq, ndims, ndims]))
            self.face_lengths_bgroups.append(
                np.zeros([bgroup.num_boundary_faces, 1]))

            normal_bgroup = self.normals_bgroups[i]
            x_bgroup = self.x_bgroups[i]
            ijac_bgroup = self.ijac_bgroups[i]
            face_lengths_bgroup = self.face_lengths_bgroups[i]

            j = 0
            for boundary_face in bgroup.boundary_faces:
                # Normals
                normals = mesh.gbasis.calculate_normals(
                    mesh, boundary_face.elem_ID, boundary_face.face_ID,
                    quad_pts)
                elem_pts = basis.get_elem_ref_from_face_ref(
                    boundary_face.face_ID, quad_pts)
                djac, jac, ijac = basis_tools.element_jacobian(
                    mesh,
                    boundary_face.elem_ID,
                    elem_pts,
                    get_djac=True,
                    get_jac=True,
                    get_ijac=True)

                normal_bgroup[j] = normals
                ijac_bgroup[j] = ijac
                djac_faces = np.linalg.norm(normals, axis=1)
                face_lengths_bgroup[j] = mesh_tools.get_face_lengths(
                    djac_faces.reshape([1, nq]), quad_wts)

                # Physical coordinates of quadrature points
                x = mesh_tools.ref_to_phys(
                    mesh, boundary_face.elem_ID,
                    self.faces_to_xref[boundary_face.face_ID])
                # Store
                x_bgroup[j] = x

                # Increment
                j += 1
            i += 1
示例#5
0
文件: DG.py 项目: IhmeGroup/quail
    def get_basis_and_geom_data(self, mesh, basis, order):
        '''
		Precomputes the basis and geometric data for each interior face

		Inputs:
		-------
			mesh: mesh object
			basis: basis object
			order: solution order

		Outputs:
		--------
			self.faces_to_basisL: basis values evaluated at quadrature
				points of each face for left element
				[nfaces_per_elem, nq, nb]
			self.faces_to_basisR: basis values evaluated at quadrature
				points of each face for right element
				[nfaces_per_elem, nq, nb]
			self.faces_to_basis_ref_gradL: gradient of basis values 
				evaluated at quadrature points of each face for left element
				[nfaces_per_elem, nq, nb, ndims]
			self.faces_to_basis_ref_gradR: gradient of basis values 
				evaluated at quadrature points of each face for right element
				[nfaces_per_elem, nq, nb, ndims]
			self.normals_int_faces: precomputed normal vectors at each
				interior face [num_interior_faces, nq, ndims]
			self.ijacL_elems: stores the evaluated inverse of the geometric 
				Jacobian for each left element 
				[num_interior_faces, nq, ndims, ndims]
			self.ijacR_elems: stores the evaluated inverse of the geometric 
				Jacobian for each right element 
				[num_interior_faces, nq, ndims, ndims]
			self.face_lengths: stores the precomputed length of each face
				[num_interior_faces, 1]
		
		Note(s):
		--------
			We separate ndims_basis and ndims to allow for basis
			and mesh to have different number of dimensions
			(ex: when using a space-time basis function and 
			only a spatial mesh)
		'''
        ndims_basis = basis.NDIMS
        ndims = mesh.ndims

        # unpack
        quad_pts = self.quad_pts
        quad_wts = self.quad_wts
        nq = quad_pts.shape[0]
        nb = basis.nb
        nfaces_per_elem = basis.NFACES
        nfaces = mesh.num_interior_faces

        # Allocate
        self.faces_to_basisL = np.zeros([nfaces_per_elem, nq, nb])
        self.faces_to_basisR = np.zeros([nfaces_per_elem, nq, nb])
        self.faces_to_basis_ref_gradL = np.zeros(
            [nfaces_per_elem, nq, nb, ndims_basis])
        self.faces_to_basis_ref_gradR = np.zeros(
            [nfaces_per_elem, nq, nb, ndims_basis])
        self.ijacL_elems = np.zeros([nfaces, nq, ndims, ndims])
        self.ijacR_elems = np.zeros([nfaces, nq, ndims, ndims])
        self.normals_int_faces = np.zeros([mesh.num_interior_faces, nq, ndims])
        djac_faces = np.zeros([mesh.num_interior_faces, nq])

        # Get values on each face (from both left and right perspectives)
        # for both the basis and the reference gradient of the basis
        for face_ID in range(nfaces_per_elem):
            # Left
            basis.get_basis_face_val_grads(mesh,
                                           face_ID,
                                           quad_pts,
                                           get_val=True,
                                           get_ref_grad=True)
            self.faces_to_basisL[face_ID] = basis.basis_val
            self.faces_to_basis_ref_gradL[face_ID] = basis.basis_ref_grad

            # Right
            basis.get_basis_face_val_grads(mesh,
                                           face_ID,
                                           quad_pts[::-1],
                                           get_val=True,
                                           get_ref_grad=True)
            self.faces_to_basisR[face_ID] = basis.basis_val
            self.faces_to_basis_ref_gradR[face_ID] = basis.basis_ref_grad

        # Normals
        i = 0
        for interior_face in mesh.interior_faces:
            normals = mesh.gbasis.calculate_normals(mesh,
                                                    interior_face.elemL_ID,
                                                    interior_face.faceL_ID,
                                                    quad_pts)
            self.normals_int_faces[i] = normals

            # Left state
            # Convert from face ref space to element ref space
            elem_pts = basis.get_elem_ref_from_face_ref(
                interior_face.faceL_ID, quad_pts)

            _, _, ijacL = basis_tools.element_jacobian(mesh,
                                                       interior_face.elemL_ID,
                                                       elem_pts,
                                                       get_djac=False,
                                                       get_jac=False,
                                                       get_ijac=True)

            # Right state
            # Convert from face ref space to element ref space
            elem_pts = basis.get_elem_ref_from_face_ref(
                interior_face.faceR_ID, quad_pts[::-1])
            _, _, ijacR = basis_tools.element_jacobian(mesh,
                                                       interior_face.elemR_ID,
                                                       elem_pts,
                                                       get_djac=False,
                                                       get_jac=False,
                                                       get_ijac=True)

            # Store
            self.ijacL_elems[i] = ijacL
            self.ijacR_elems[i] = ijacR

            # Used for face_length calculations
            djac_faces[i] = np.linalg.norm(normals, axis=1)
            i += 1

        self.face_lengths = mesh_tools.get_face_lengths(djac_faces, quad_wts)
示例#6
0
文件: DG.py 项目: IhmeGroup/quail
    def get_basis_and_geom_data(self, mesh, basis, order):
        '''
		Precomputes the basis and geometric data for each element

		Inputs:
		-------
			mesh: mesh object
			basis: basis object
			order: solution order

		Outputs:
		--------
			self.basis_val: precomputed basis value [nq, nb]
			self.basis_ref_grad: precomputed basis gradient for the
				reference element [nq, nb, ndims]
			self.basis_phys_grad_elems: precomputed basis gradient for each
				physical element [num_elems, nq, nb, ndims]
			self.jac_elems: precomputed Jacobian for each element
				[num_elems, nq, ndims, ndims]
			self.ijac_elems: precomputed inverse Jacobian for each element
				[num_elems, nq, ndims, ndims]
			self.djac_elems: precomputed determinant of the Jacobian for each
				element [num_elems, nq, 1]
			self.x_elems: precomputed coordinates of the quadrature points
				in physical space [num_elems, nq, ndims]
		'''
        ndims = mesh.ndims
        num_elems = mesh.num_elems
        quad_pts = self.quad_pts
        nq = quad_pts.shape[0]
        nb = basis.nb

        # Allocate
        self.jac_elems = np.zeros([num_elems, nq, ndims, ndims])
        self.ijac_elems = np.zeros([num_elems, nq, ndims, ndims])
        self.djac_elems = np.zeros([num_elems, nq, 1])
        self.x_elems = np.zeros([num_elems, nq, ndims])
        self.basis_phys_grad_elems = np.zeros([num_elems, nq, nb, basis.NDIMS])
        self.normals_elems = np.empty([
            num_elems, mesh.gbasis.NFACES, self.face_quad_pts.shape[0], ndims
        ])

        # Basis data
        basis.get_basis_val_grads(self.quad_pts,
                                  get_val=True,
                                  get_ref_grad=True)

        self.basis_val = basis.basis_val
        self.basis_ref_grad = basis.basis_ref_grad

        for elem_ID in range(mesh.num_elems):
            # Jacobian
            djac, jac, ijac = basis_tools.element_jacobian(mesh,
                                                           elem_ID,
                                                           quad_pts,
                                                           get_djac=True,
                                                           get_jac=True,
                                                           get_ijac=True)
            # Store
            self.jac_elems[elem_ID] = jac
            self.ijac_elems[elem_ID] = ijac
            self.djac_elems[elem_ID] = djac

            # Physical coordinates of quadrature points
            x = mesh_tools.ref_to_phys(mesh, elem_ID, quad_pts)
            # Store
            self.x_elems[elem_ID] = x

            if self.need_phys_grad:
                # Physical gradient
                basis.get_basis_val_grads(quad_pts,
                                          get_phys_grad=True,
                                          ijac=ijac)
                self.basis_phys_grad_elems[elem_ID] = basis.basis_phys_grad
                # [nq, nb, ndims]

            # Face normals
            for i in range(mesh.gbasis.NFACES):
                self.normals_elems[elem_ID, i] = mesh.gbasis.calculate_normals(
                    mesh, elem_ID, i, self.face_quad_pts)

        # Volumes
        self.vol_elems, self.domain_vol = mesh_tools.element_volumes(mesh)
示例#7
0
def get_error(mesh,
              physics,
              solver,
              var_name,
              ord=2,
              print_error=True,
              normalize_by_volume=True):
    '''
	This function computes the Lp-error, where p is the "ord" input argument.

	Inputs:
	-------
	    mesh: mesh object
	    physics: physics object
	    solver: solver object
	    var_name: name of variable to compute error of
	    ord: order of the error
	    print_error: if True, will print total error
	    normalize_by_volume: if True, will normalize the error by the
	    	volume of the domain

	Outputs:
	--------
	    tot_err: total error
	    err_elems: error over each element [num_elems]
	'''
    # Extract info
    time = solver.time
    U = solver.state_coeffs
    basis = solver.basis
    order = solver.order
    if physics.exact_soln is None:
        raise ValueError("No exact solution provided")

    # Get element volumes
    if normalize_by_volume:
        _, tot_vol = mesh_tools.element_volumes(mesh, solver)
    else:
        tot_vol = 1.

    # Allocate, initialize
    err_elems = np.zeros([mesh.num_elems])
    tot_err = 0.

    # Get quadrature data
    quad_order = basis.get_quadrature_order(mesh,
                                            2 * np.amax([order, 1]),
                                            physics=physics)

    gbasis = mesh.gbasis
    quad_pts, quad_wts = gbasis.get_quadrature_data(quad_order)

    # Get x for each element
    xphys = np.empty((mesh.num_elems, ) + quad_pts.shape)
    for elem_ID in range(mesh.num_elems):
        xphys[elem_ID] = mesh_tools.ref_to_phys(mesh, elem_ID, quad_pts)

    # Evaluate exact solution at quadrature points
    u_exact = physics.exact_soln.get_state(physics, x=xphys, t=time)

    # Interpolate state to quadrature points
    basis.get_basis_val_grads(quad_pts, True)
    u = helpers.evaluate_state(U, basis.basis_val)

    # Computed requested quantity
    s = physics.compute_variable(var_name, u)
    s_exact = physics.compute_variable(var_name, u_exact)

    # Loop through elements
    for elem_ID in range(mesh.num_elems):
        # Calculate element-local error
        djac, _, _ = basis_tools.element_jacobian(mesh,
                                                  elem_ID,
                                                  quad_pts,
                                                  get_djac=True)
        err = np.sum((s[elem_ID] - s_exact[elem_ID])**ord * quad_wts * djac)
        err_elems[elem_ID] = err
        tot_err += err_elems[elem_ID]

    tot_err = (tot_err / tot_vol)**(1. / ord)

    # Print if requested
    if print_error:
        print("Total error = %.15f" % (tot_err))

    return tot_err, err_elems
示例#8
0
def get_stiffness_matrix_ader(mesh,
                              basis,
                              basis_st,
                              order,
                              dt,
                              elem_ID,
                              grad_dir,
                              physical_space=False):
    '''
	Calculate the stiffness matrix for ADER-DG prediction step

	Inputs:
	-------
		mesh: mesh object
		basis: basis object
		basis_st: space-time basis object
		order: solution order
		dt: time step
		elem_ID: element index
		grad_dir: direction of gradient calculation

	Outputs:
	--------
		SM: stiffness matrix for ADER-DG [nb_st, nb_st]
	'''
    ndims = mesh.ndims

    quad_order_st = basis_st.get_quadrature_order(mesh, order * 2)
    quad_order = quad_order_st

    quad_pts_st, quad_wts_st = basis_st.get_quadrature_data(quad_order_st)
    quad_pts, quad_wts = basis.get_quadrature_data(quad_order)

    nq_st = quad_pts_st.shape[0]
    nq = quad_pts.shape[0]

    if physical_space:
        djac, jac, ijac = basis_tools.element_jacobian(mesh,
                                                       elem_ID,
                                                       quad_pts_st,
                                                       get_djac=True,
                                                       get_ijac=True)

        ijac_st = np.zeros([nq_st, ndims + 1, ndims + 1])
        ijac_st[:, :ndims, :ndims] = ijac

        # Add the temporal Jacobian in the ndims+1 dimension
        ijac_st[:, ndims, ndims] = 2. / dt

    basis_st.get_basis_val_grads(quad_pts_st, get_val=True, get_ref_grad=True)

    nb_st = basis_st.basis_val.shape[1]
    basis_st_val = basis_st.basis_val

    if physical_space:
        basis_ref_grad = basis_st.basis_ref_grad
        basis_st_grad = np.transpose(
            np.matmul(ijac_st.transpose(0, 2, 1),
                      basis_ref_grad.transpose(0, 2, 1)), (0, 2, 1))
    else:
        basis_st_grad = basis_st.basis_ref_grad

    # ------------------------------------------------------------------- #
    # Example of ADER Stiffness Matrix calculation using for-loops
    # ------------------------------------------------------------------- #
    #
    # nb_st = basis_st.basis_val.shape[1]
    #
    # for i in range(nb_st):
    #	  for j in range(nb_st):
    #		  a = 0.
    #		  for iq in range(nq_st):
    #			  a += basis_st_grad[iq, i, grad_dir]*basis_st_val[iq, j]
    #					 * quad_wts_st[iq]
    #		  SM[i,j] = a
    #
    # ------------------------------------------------------------------- #
    SM = np.matmul(basis_st_grad[:, :, grad_dir].transpose(),
                   basis_st_val * quad_wts_st)

    return SM  # [nb_st, nb_st]
示例#9
0
def get_elem_mass_matrix_ader(mesh,
                              basis,
                              order,
                              elem_ID=-1,
                              physical_space=False):
    '''
	Calculate the mass matrix for ADER-DG prediction step

	Inputs:
	-------
		mesh: mesh object
		basis: basis object
		order: solution order
		elem_ID: [OPTIONAL] element index
		physical_space: [OPTIONAL] Flag to calc matrix in physical or
			reference space (default: False {reference space})

	Outputs:
	--------
		MM: mass matrix for ADER-DG [nb_st, nb_st]
	'''
    if physical_space:
        gbasis = mesh.gbasis
        quad_order = gbasis.get_quadrature_order(mesh, order * 2)
    else:
        quad_order = order * 2

    quad_pts, quad_wts = basis.get_quadrature_data(quad_order)

    nq = quad_pts.shape[0]

    basis.get_basis_val_grads(quad_pts, get_val=True)

    if physical_space:
        djac, _, _ = basis_tools.element_jacobian(mesh,
                                                  elem_ID,
                                                  quad_pts,
                                                  get_djac=True)

        if len(djac) == 1:
            djac = np.full(nq, djac[0])
    else:
        djac = np.full(nq, 1.).reshape(nq, 1)

    # ------------------------------------------------------------------- #
    # Example of ADER Flux Matrix calculation using for-loops
    # ------------------------------------------------------------------- #
    #
    # nb_st = basis.basis_val.shape[1]
    # basis_val = basis.basis_val
    #
    # for i in range(nb_st):
    #	  for j in range(nb_st):
    #		  a = 0.
    #		  for iq in range(nq):
    #			  a += basis_val[iq,i]*basis_val[iq,j]*quad_wts[iq]*djac[iq]
    #		  MM[i,j] = a
    #
    # ------------------------------------------------------------------- #
    MM = np.matmul(basis.basis_val.transpose(), basis.basis_val * \
      quad_wts * djac) # [nb_st, nb_st]

    return MM  # [nb_st, nb_st]