Esempio n. 1
0
    def get_boundary_face_residual(self, bgroup, face_IDs, Uc, resB):
        # unpack
        mesh = self.mesh
        physics = self.physics
        bgroup_num = bgroup.number

        bface_helpers = self.bface_helpers
        elem_helpers = self.elem_helpers
        fluxes = self.params["ConvFluxSwitch"]

        quad_wts = bface_helpers.quad_wts
        normals_bgroups = bface_helpers.normals_bgroups
        x_bgroups = bface_helpers.x_bgroups
        ijac_bgroups = bface_helpers.ijac_bgroups

        basis_val = bface_helpers.faces_to_basis[face_IDs]  # [nbf, nq, nb]
        basis_ref_grad = bface_helpers.faces_to_basis_ref_grad[face_IDs]

        # Interpolate state at quad points
        UqI = helpers.evaluate_state(Uc, basis_val)  # [nbf, nq, ns]

        normals = normals_bgroups[bgroup_num]  # [nbf, nq, ndims]
        x = x_bgroups[bgroup_num]  # [nbf, nq, ndims]
        ijac = ijac_bgroups[bgroup_num]

        BC = physics.BCs[bgroup.name]

        # Interpolate state at quadrature points
        UqI = helpers.evaluate_state(Uc, basis_val)

        # Interpolate gradient of state at quad points
        gUq_ref = self.evaluate_gradient(Uc, basis_ref_grad)

        # Make ref gradient of state the physical gradient
        gUq = self.ref_to_phys_grad(ijac, gUq_ref)

        # Compute any additional helpers for diffusive flux fcn
        if physics.diff_flux_fcn:
            physics.diff_flux_fcn.compute_bface_helpers(self, bgroup_num)

        if fluxes:
            # Compute boundary flux
            Fq, FqB = BC.get_boundary_flux(physics,
                                           UqI,
                                           normals,
                                           x,
                                           self.time,
                                           gUq=gUq)
            FqB_phys = self.ref_to_phys_grad(ijac, FqB)

            # Compute contribution to adjacent element residual
            resB = solver_tools.calculate_boundary_flux_integral(
                basis_val, quad_wts, Fq)

            resB -= self.calculate_boundary_flux_integral_sum(
                basis_ref_grad, quad_wts, FqB_phys)

        return resB
Esempio n. 2
0
def custom_user_function(solver):
    '''
	Provides a custom interface for the model problem into the solver.
	This particular case saves the physical time and solution in time for 
	the "1D" element. The file written is called 'time_hist.txt'.

	Inputs:
	-------
		solver: solver object
	'''
    tstart = 0.
    if solver.time >= tstart:
        # Unpack
        Uc = solver.state_coeffs
        basis_val = solver.elem_helpers.basis_val
        Uq = helpers.evaluate_state(Uc, basis_val)
        time_hist = open('time_hist.txt', 'a')

        # Convert applicable variables to strings
        s = str(solver.time)
        s1 = str(Uq[0, 0, 0])

        # write to the file at each iteration
        time_hist.write(s)
        time_hist.write(' , ')
        time_hist.write(s1)
        time_hist.write('\n')
        time_hist.close()
Esempio n. 3
0
def average_spacetime_guess(solver, W, U_pred, dt=None):
    '''
	This method calculates the average space-time guess for the ADERDG
	predictor step. (This is the default approach for the guess)

	Inputs:
	-------
		solver: solver object
		W: spatial polynomial solution [ne, nb, ns]
		U_pred: space-time polynomial coefficients [ne, nb_st, ns]
		
	Outputs:
	--------
		U_pred: Space-time predicted polynomial coefficients [ne, nb_st, ns]
	'''
    # Unpack
    physics = solver.physics
    basis = solver.basis
    elem_helpers = solver.elem_helpers

    quad_wts = elem_helpers.quad_wts
    basis_val = elem_helpers.basis_val
    djac_elems = elem_helpers.djac_elems
    vol_elems = elem_helpers.vol_elems

    # Calculate the average state for each element in spatial coordinates
    Wq = helpers.evaluate_state(W, basis_val, skip_interp=basis.skip_interp)
    W_bar = helpers.get_element_mean(Wq, quad_wts, djac_elems, vol_elems)

    U_pred[:] = W_bar

    return U_pred, W_bar  # [ne, nb_st, ns]
Esempio n. 4
0
        def get_jacobian_matrix_elems(self, solver, iMM_elems, Uc):
            '''
			Calculates the Jacobian matrix of the source term and its
			inverse for each element. Definition of 'Jacobian' matrix:
			A = I - BETA*dt*iMM^{-1}*dRdU

			Inputs:
			-------
				solver: solver object (e.g., DG, ADERDG, etc...)
				elem_ID: element ID
				iMM: inverse mass matrix [ne, nb, nb]
				Uc: state coefficients [ne, nb, ns]
			Outputs:
			--------
				A: matrix returned for linear solve [ne, nb, nb, ns, ns]
				iA: inverse matrix returned for linear solve
					[ne, nb, nb, ns, ns]
			'''
            mesh = solver.mesh
            nelem = mesh.num_elems
            beta = self.BETA
            dt = solver.stepper.dt
            physics = solver.physics
            source_terms = physics.source_terms

            elem_helpers = solver.elem_helpers
            basis_val = elem_helpers.basis_val
            quad_wts = elem_helpers.quad_wts
            x_elems = elem_helpers.x_elems
            djac_elems = elem_helpers.djac_elems
            nq = quad_wts.shape[0]
            ns = physics.NUM_STATE_VARS
            nb = basis_val.shape[1]

            Uq = helpers.evaluate_state(
                Uc, basis_val,
                skip_interp=solver.basis.skip_interp)  # [ne, nq, ns])

            # Evaluate the source term Jacobian [ne, nq, ns, ns]
            Sjac = np.zeros([nelem, nq, ns, ns])
            Sjac = physics.eval_source_term_jacobians(Uq, x_elems, solver.time,
                                                      Sjac)

            # Call solver helper to get dRdU (see solver/tools.py)
            dRdU = solver_tools.calculate_dRdU(elem_helpers, Sjac)
            # [ne, nb, nb, ns, ns]

            # Define the identity matrix
            I = np.expand_dims(np.expand_dims(np.eye(nb), axis=2), axis=3)

            A = I - beta*dt * \
              np.einsum('eij, ejklm -> eiklm', iMM_elems, dRdU)

            iA = np.zeros_like(A)
            for i in range(ns):
                for s in range(ns):
                    iA[:, :, :, i, s] = np.linalg.inv(A[:, :, :, i, s])

            return A, iA  # [ne, nb, nb, ns, ns]
Esempio n. 5
0
    def get_element_residual(self, Uc, res_elem):
        # Unpack
        physics = self.physics
        ns = physics.NUM_STATE_VARS
        ndims = physics.NDIMS
        elem_helpers = self.elem_helpers
        basis_val = elem_helpers.basis_val
        basis_phys_grad_elems = elem_helpers.basis_phys_grad_elems
        quad_wts = elem_helpers.quad_wts
        djac_elems = elem_helpers.djac_elems
        ijac_elems = elem_helpers.ijac_elems
        x_elems = elem_helpers.x_elems
        nq = quad_wts.shape[0]
        fluxes = self.params["ConvFluxSwitch"]
        sources = self.params["SourceSwitch"]

        # Interpolate state at quad points
        Uq = helpers.evaluate_state(
            Uc, basis_val, skip_interp=self.basis.skip_interp)  # [ne, nq, ns]

        # Interpolate gradient of state at quad points
        gUq = self.evaluate_gradient(Uc, basis_phys_grad_elems)

        if self.verbose:
            # Get min and max of state variables for reporting
            self.get_min_max_state(Uq)

        if fluxes:
            # Evaluate the inviscid flux integral
            Fq = physics.get_conv_flux_interior(Uq)[0]  # [ne, nq, ns, ndims]

            if physics.diff_flux_fcn:
                # Evaluate the diffusion flux
                Fq -= physics.get_diff_flux_interior(Uq, gUq)
                # [ne, nq, ns, ndims]

            res_elem += solver_tools.calculate_volume_flux_integral(
                self, elem_helpers, Fq)  # [ne, nb, ns]

        if sources:
            # Evaluate the source term integral
            # eval_source_terms is an additive function so source needs to be
            # initialized to zero for each time step
            Sq = np.zeros_like(Uq)  # [ne, nq, ns]
            Sq = physics.eval_source_terms(Uq, x_elems, self.time, Sq)
            # [ne, nq, ns]

            res_elem += solver_tools.calculate_source_term_integral(
                elem_helpers, Sq)  # [ne, nb, ns]

        # Add artificial viscosity term
        if self.params["ArtificialViscosity"]:
            av_param = self.params["AVParameter"]
            res_elem -= solver_tools.calculate_artificial_viscosity_integral(
                physics, elem_helpers, Uc, av_param, self.order)

        return res_elem  # [ne, nb, ns]
Esempio n. 6
0
def recalculate_jacobian_on(solver, U_pred, dt, Sjac=None):
    '''
	Method to recalculate the jacobian at each subiteration 
	of a nonlinear solver. Note: This has only been useful in very 
	specific applications.

	Inputs:
	-------
		solver: solver object
		U_pred: Space-time predicted polynomial coefficients [ne, nb_st, ns]
		dt: time step size

	Outputs:
	--------
		Sjac: source term jacobian [nelem, ns, ns]
	'''
    # Unpack
    physics = solver.physics
    elem_helpers = solver.elem_helpers
    elem_helpers_st = solver.elem_helpers_st

    ns = physics.NUM_STATE_VARS
    nelem = U_pred.shape[0]
    quad_wts_st = elem_helpers_st.quad_wts
    basis_val_st = elem_helpers_st.basis_val
    nq_t = elem_helpers_st.nq_tile_constant

    x_elems = elem_helpers.x_elems
    vol_elems = elem_helpers.vol_elems
    djac_elems = elem_helpers.djac_elems

    # Only evaluate Jacobian for stiff sources
    temp_sources = physics.source_terms.copy()
    physics.source_terms = physics.implicit_sources.copy()

    Uq = helpers.evaluate_state(U_pred, basis_val_st)

    U_bar = helpers.get_element_mean(
        Uq, quad_wts_st,
        np.tile(djac_elems, [1, nq_t, 1]) * dt / 2., dt * vol_elems)

    # Calculate the source term Jacobian using average state
    Sjac = Sjac.reshape([U_pred.shape[0], 1, ns, ns])
    Sjac[:] = 0.
    # Sjac = np.zeros([U_pred.shape[0], 1, ns, ns])
    Sjac = physics.eval_source_term_jacobians(U_bar, x_elems, solver.time,
                                              Sjac)
    Sjac = np.reshape(Sjac, [nelem, ns, ns])

    # Set all sources for source_coeffs calculation
    physics.source_terms = temp_sources.copy()
Esempio n. 7
0
def get_average_solution(physics, solver, x, basis, var_name):
	'''
	This function evaluates the average numerical solution
	at a set of points and plots them on the average x location of
	each element.

	Inputs:
	-------
	    physics: physics object
	    U: state polynomial coefficients
	    	[num_elems, num_basis_coeffs, num_state_vars]
	    x: coordinates at which to evaluate solution
	    	[num_elems, num_pts, ndims], where num_pts is the number of 
	    	sample points per element
	    basis: basis object
	    var_name: name of variable to get

	Outputs:
	-------
		var_numer: values of variable obtained at x
			[num_elems, num_pts, 1]
		x_bar: average coordinates for each element [num_elems, 1, 1]
	'''
	mesh = solver.mesh
	order = solver.order
	U = solver.state_coeffs

	if physics.NDIMS > 1:
		print("Plotting average state not available for 2D")
		raise NotImplementedError

	#Additions for Ubar
	solver.elem_helpers.get_gaussian_quadrature(mesh, physics, basis, 
			3*order)
	solver.elem_helpers.get_basis_and_geom_data(mesh, basis, 3*order)

	elem_helpers = solver.elem_helpers
	quad_wts = elem_helpers.quad_wts
	djacs = elem_helpers.djac_elems
	vols = elem_helpers.vol_elems
	Uq = helpers.evaluate_state(U, basis.basis_val)

	Ubar = helpers.get_element_mean(Uq, quad_wts, djacs, vols)
	var_numer = physics.compute_variable(var_name, Ubar)

	x_bar = np.sum(x, axis=1).reshape([x.shape[0], 1, 1])/x.shape[1]

	return var_numer, x_bar
Esempio n. 8
0
def get_dt_from_cfl(stepper, solver):
    '''
	Calculates dt using a specified CFL number. Updates at everytime step to
	ensure solution remains within the CFL bound.

	Inputs:
	-------
		stepper: stepper object (e.g., FE, RK4, etc...)
		solver: solver object (e.g., DG, ADERDG, etc...)

	Outputs:
	--------
		dt: time step for the solver
	'''
    mesh = solver.mesh
    ndims = mesh.ndims

    physics = solver.physics
    U = solver.state_coeffs

    time = solver.time
    tfinal = solver.params["FinalTime"]
    stepper.tfinal = tfinal
    cfl = solver.params["CFL"]

    elem_helpers = solver.elem_helpers
    vol_elems = elem_helpers.vol_elems
    a = np.zeros([mesh.num_elems, U.shape[1], 1])
    basis_val = solver.elem_helpers.basis_val

    # Interpolate state at quad points
    Uq = helpers.evaluate_state(
        U, basis_val, skip_interp=solver.basis.skip_interp)  # [ne, nq, ns]

    # Calculate max wavespeed
    a = physics.compute_variable("MaxWaveSpeed", Uq, flag_non_physical=True)
    # Calculate the dt for each element
    dt_elems = cfl * vol_elems**(1. / ndims) / a

    # take minimum to set appropriate dt
    dt = np.min(dt_elems)

    # logic to ensure final time step yields FinalTime
    if time + dt < tfinal:
        stepper.num_time_steps += 1
        return dt
    else:
        return tfinal - time
Esempio n. 9
0
        def take_time_step(self, solver):
            mesh = solver.mesh
            elem_helpers = solver.elem_helpers
            basis_val = elem_helpers.basis_val
            iMM_elems = elem_helpers.iMM_elems
            quad_pts = elem_helpers.quad_pts
            quad_wts = elem_helpers.quad_wts
            x_elems = elem_helpers.x_elems

            U = solver.state_coeffs

            Uq = helpers.evaluate_state(
                U, basis_val,
                skip_interp=solver.basis.skip_interp)  # [ne, nq, ns])

            res = self.res

            Uq0, t0 = Uq.reshape(-1), solver.time
            dt = self.dt

            subiterations = []

            # Instantiate ode object
            r = ode(self.rhs_sources, jac=None)
            r.set_integrator('lsoda', nsteps=50000, atol=1e-14, rtol=1e-12)
            r.set_initial_value(Uq0, t0).set_f_params(x_elems, Uq, solver,
                                                      subiterations)

            value = r.integrate(r.t + dt).reshape(
                [res.shape[0], res.shape[1], res.shape[2]])

            subiterations = np.unique(subiterations)

            # Print the number of subiterations for each iteration and
            # store the total number of ODE subiterations for the solver
            print("Subiterations:", len(subiterations))
            solver.count_evaluations += len(subiterations)

            # Project onto the basis state from the quadrature points
            solver_tools.L2_projection(mesh, iMM_elems, solver.basis, quad_pts,
                                       quad_wts, value, U)

            solver.apply_limiter(U)
            solver.state_coeffs = U

            return res  # [ne, nb, ns]
Esempio n. 10
0
    def project_state_to_new_basis(self, U_old, basis_old, order_old):
        '''
		Projects the state to a different basis and/or order

		Inputs:
		-------
			U_old: restart files old solution array
			basis_old: previous basis function
			order_old: previous polynomial order

		Outputs:
		--------
			state is modified
		'''
        mesh = self.mesh
        physics = self.physics
        basis = self.basis
        params = self.params
        iMM_elems = self.elem_helpers.iMM_elems

        U = self.state_coeffs
        ns = physics.NUM_STATE_VARS

        if basis_old.SHAPE_TYPE != basis.SHAPE_TYPE:
            raise errors.IncompatibleError

        if not params["L2InitialCondition"]:
            # Interpolate to solution nodes
            eval_pts = basis.get_nodes(self.order)
        else:
            # Quadrature
            order = 2 * np.amax([self.order, order_old])
            quad_order = basis.get_quadrature_order(mesh, order)

            quad_pts, quad_wts = basis.get_quadrature_data(quad_order)
            eval_pts = quad_pts

        basis_old.get_basis_val_grads(eval_pts, get_val=True)

        Uq_old = helpers.evaluate_state(U_old, basis_old.basis_val)

        if not params["L2InitialCondition"]:
            solver_tools.interpolate_to_nodes(Uq_old, U)
        else:
            solver_tools.L2_projection(mesh, iMM_elems, basis, quad_pts,
                                       quad_wts, Uq_old, U)
Esempio n. 11
0
        def take_time_step(self, solver):
            mesh = solver.mesh
            U = solver.state_coeffs

            mesh = solver.mesh
            elem_helpers = solver.elem_helpers
            basis_val = elem_helpers.basis_val
            iMM_elems = elem_helpers.iMM_elems
            quad_pts = elem_helpers.quad_pts
            quad_wts = elem_helpers.quad_wts
            x_elems = elem_helpers.x_elems

            Uq = helpers.evaluate_state(
                U, basis_val,
                skip_interp=solver.basis.skip_interp)  # [ne, nq, ns])

            # Solve the nonlinear system to get the new solution. This is done
            # for each element and each quadrature point, fully uncoupled. This
            # is actually only valid for orthogonal bases - for nodal bases this
            # could incur some error.
            for i in range(Uq.shape[0]):
                for j in range(Uq.shape[1]):
                    sol = scipy.optimize.root(self.rhs_sources,
                                              Uq[i, j],
                                              args=(solver, x_elems[i,
                                                                    j], Uq[i,
                                                                           j]))
                    Uq[i, j] = sol.x

            res = self.res

            # Project onto the basis state from the quadrature points
            solver_tools.L2_projection(mesh, iMM_elems, solver.basis, quad_pts,
                                       quad_wts, Uq, U)

            solver.apply_limiter(U)
            solver.state_coeffs = U

            return res  # [ne, nb, ns]
Esempio n. 12
0
def get_numerical_solution(physics, U, x, basis, var_name):
	'''
	This function evaluates the numerical solution at a set of points.

	Inputs:
	-------
	    physics: physics object
	    U: state polynomial coefficients
	    	[num_elems, num_basis_coeffs, num_state_vars]
	    x: coordinates at which to evaluate solution
	    	[num_elems, num_pts, ndims], where num_pts is the 
	    	number of sample points per element
	    basis: basis object
	    var_name: name of variable to get

	Outputs:
	-------
		var_numer: values of variable obtained at x
			[num_elems, num_pts, 1]
	'''
	Uq = helpers.evaluate_state(U, basis.basis_val)
	var_numer = physics.compute_variable(var_name, Uq)

	return var_numer
Esempio n. 13
0
def compute_variable(solver, Uc, var_name):
    '''
	This function computes the desired variable at the element and
	face quadrature points, as well as the mean of the variable over
	the element.
	'''
    # Interpolate state at quadrature points over element and on faces
    limiter = solver.limiters[0]
    basis = solver.basis
    physics = solver.physics
    U_elem_faces = helpers.evaluate_state(Uc,
                                          limiter.basis_val_elem_faces,
                                          skip_interp=basis.skip_interp)
    nq_elem = limiter.quad_wts_elem.shape[0]
    U_elem = U_elem_faces[:, :nq_elem, :]
    # Average value of state
    U_bar = helpers.get_element_mean(U_elem, limiter.quad_wts_elem,
                                     limiter.djac_elems, limiter.elem_vols)
    # Compute variable at quadrature points
    var_elem_faces = physics.compute_variable(var_name, U_elem_faces)
    # Compute mean
    var_bar = physics.compute_variable(var_name, U_bar)

    return var_elem_faces, var_bar
Esempio n. 14
0
def spacetime_odeguess(solver, W, U_pred, dt=None):
    '''
	This method sets the space-time guess to the predictor step in the 
	ADER-DG scheme by using a built-in ODE solver from scipy. We solve 
	the stationary ODE in time and use the result to construct our guess 
	to the non-linear solver

	NOTE: Current implementation only supports ODEs

	Inputs:
	-------
		solver: solver object
		W: spatial polynomial solution [ne, nb, ns]
		U_pred: space-time polynomial coefficients [ne, nb_st, ns]

	Outputs:
	--------
		U_pred: Space-time predicted polynomial coefficients [ne, nb_st, ns]
		U_bar: Space-time average value [ne, 1, ns]
	'''
    # Unpack
    physics = solver.physics

    ns = physics.NUM_STATE_VARS
    mesh = solver.mesh

    basis = solver.basis
    basis_st = solver.basis_st

    elem_helpers = solver.elem_helpers
    elem_helpers_st = solver.elem_helpers_st
    ader_helpers = solver.ader_helpers
    iMM_elems = ader_helpers.iMM_elems

    quad_wts = elem_helpers.quad_wts
    quad_pts = elem_helpers.quad_pts
    basis_val = elem_helpers.basis_val
    basis_val_st = elem_helpers_st.basis_val
    djac_elems = elem_helpers.djac_elems
    x_elems = elem_helpers.x_elems

    nelem = W.shape[0]
    quad_pts_st = elem_helpers_st.quad_pts
    quad_wts_st = elem_helpers_st.quad_wts
    nq_st = quad_wts_st.shape[0]
    nq_t = elem_helpers_st.nq_tile_constant
    vol_elems = elem_helpers.vol_elems

    # Evaluate spatial coeffs on spatial quadrature points
    Wq = helpers.evaluate_state(W, basis_val, skip_interp=basis.skip_interp)

    # Allocate memory for the guess at the quadrature points
    Uq_guess = np.zeros([nelem, nq_st, ns])
    Uq_guess = np.tile(Wq, [1, Wq.shape[1], 1])

    # Build ref temporal array for space-time element
    t, elem_helpers_st.basis_time = ref_to_phys_time(
        mesh, solver.time, dt, quad_pts[:, -1:], elem_helpers_st.basis_time)

    # Build phys time array for space-time element
    tphys, elem_helpers_st.basis_time = ref_to_phys_time(
        mesh, solver.time, dt, ader_helpers.x_elems[0, 0:2, :],
        elem_helpers_st.basis_time)

    W0, t0 = Wq.reshape(-1), solver.time

    def func(t, y, x, Sq_exp):
        '''
		Function for the ode solver to calculate the RHS

		Inputs:
		-------
			t: time
			y: solution array
			x: quadrature points
			Sq_exp: explicit source term evaluated at quadrature points
		'''
        # Keep track of the number of times func is called
        tvals.append(t)

        # Evaluate the source term at the quadrature points
        Sq = np.zeros([U_pred.shape[0], x.shape[1], ns])
        y = y.reshape(Sq.shape)
        Sq = Sq_exp + physics.eval_source_terms(y, x, t, Sq)

        # NOTE: This function currently does not include the flux evaluation.
        # It will need to be added for the guess to be correct for more
        # complicated systems. Current test cases that require this are
        # only ODE cases.
        return Sq.reshape(-1)

    # Evaluate source terms to be taken explicitly
    temp_sources = physics.source_terms.copy()
    physics.source_terms = physics.explicit_sources.copy()

    Sq_exp = np.zeros([U_pred.shape[0], x_elems.shape[1], ns])
    Sq_exp = physics.eval_source_terms(Wq, x_elems, t, Sq_exp)

    # Set implicit sources only for stiff ODE evaluation
    physics.source_terms = physics.implicit_sources.copy()

    # Initialize the integrator
    r = ode(func, jac=None)
    r.set_integrator('lsoda', nsteps=50000, atol=1e-14, rtol=1e-12)
    r.set_initial_value(W0, t0).set_f_params(x_elems, Sq_exp)

    # Set constants for managing data and begin ODE integration loop
    i = 0
    j = 0
    # Run the ODEsolver guess
    while r.successful() and j < t.shape[0]:
        # Length of tvals represents number of ODE interations per
        # timestep between two quadrature points in time
        tvals = []

        # Runs the integrator
        value = r.integrate(r.t + (t[j] - r.t))

        # Populate the data into the guess
        Uq_guess[:,i:t.shape[0]*j+t.shape[0],:] = \
          value.reshape([nelem, t.shape[0], ns])

        i += t.shape[0]
        j += 1

        tvals = np.unique(tvals)
        solver.count_evaluations += len(tvals)
        # Prints the number of ODE iterations
        print("Steps/quadrature point: ", len(tvals))

    physics.source_terms = temp_sources

    # Get space-time average from initial guess
    U_bar = helpers.get_element_mean(
        Uq_guess, quad_wts_st,
        np.tile(djac_elems, [1, nq_t, 1]) * dt / 2., dt * vol_elems)

    # Project the guess at the space-time quadrature points to the
    # state coefficient's initial guess
    L2_projection(mesh, iMM_elems, solver.basis_st, quad_pts_st, quad_wts_st,
                  np.tile(djac_elems, [1, nq_t, 1]), Uq_guess, U_pred)

    return U_pred, U_bar
Esempio n. 15
0
    def get_interior_face_residual(self, faceL_IDs, faceR_IDs, UcL, UcR):
        # Unpack
        mesh = self.mesh
        physics = self.physics
        fluxes = self.params["ConvFluxSwitch"]

        int_face_helpers = self.int_face_helpers
        elem_helpers = self.elem_helpers
        vol_elems = elem_helpers.vol_elems
        face_lengths = int_face_helpers.face_lengths

        quad_wts = int_face_helpers.quad_wts
        faces_to_basisL = int_face_helpers.faces_to_basisL
        faces_to_basisR = int_face_helpers.faces_to_basisR
        faces_to_basis_ref_gradL = int_face_helpers.faces_to_basis_ref_gradL
        faces_to_basis_ref_gradR = int_face_helpers.faces_to_basis_ref_gradR
        ijacL_elems = int_face_helpers.ijacL_elems
        ijacR_elems = int_face_helpers.ijacR_elems

        normals_int_faces = int_face_helpers.normals_int_faces
        # [nf, nq, ndims]

        ns = physics.NUM_STATE_VARS
        nq = quad_wts.shape[0]

        # Interpolate state at quad points
        UqL = helpers.evaluate_state(UcL, faces_to_basisL[faceL_IDs])
        # [nf, nq, ns]
        UqR = helpers.evaluate_state(UcR, faces_to_basisR[faceR_IDs])
        # [nf, nq, ns]

        # Interpolate gradient of state at quad points
        gUqL_ref = self.evaluate_gradient(UcL,
                                          faces_to_basis_ref_gradL[faceL_IDs])
        gUqR_ref = self.evaluate_gradient(UcR,
                                          faces_to_basis_ref_gradR[faceR_IDs])

        # Make gradient the physical gradient at L/R states
        gUqL = self.ref_to_phys_grad(ijacL_elems, gUqL_ref)
        gUqR = self.ref_to_phys_grad(ijacR_elems, gUqR_ref)

        # Allocate resL and resR (needed for operator splitting)
        nifL = self.int_face_helpers.elemL_IDs.shape[0]
        nifR = self.int_face_helpers.elemR_IDs.shape[0]
        resL = np.zeros([nifL, nq, ns])
        resR = np.zeros([nifR, nq, ns])
        resL_diff = np.zeros([nifL, nq, ns])
        resR_diff = np.zeros([nifR, nq, ns])

        if physics.diff_flux_fcn:
            # Calculate diffusion flux helpers
            physics.diff_flux_fcn.compute_iface_helpers(self)

        if fluxes:
            # Compute numerical flux
            Fq = physics.get_conv_flux_numerical(UqL, UqR, normals_int_faces)
            # [nf, nq, ns]

            # Compute diffusion flux
            Fq_diff, FL, FR = physics.get_diff_flux_numerical(
                UqL, UqR, gUqL, gUqR, normals_int_faces)  # [nf, nq, ns],
            # [nf, nq, ns, ndims], [nf, nq, ns, ndims]
            Fq -= Fq_diff

            FL_phys = self.ref_to_phys_grad(ijacL_elems, FL)
            FR_phys = self.ref_to_phys_grad(ijacR_elems, FR)

            # Compute contribution to left and right element residuals
            resL = solver_tools.calculate_boundary_flux_integral(
                faces_to_basisL[faceL_IDs], quad_wts, Fq)
            resR = solver_tools.calculate_boundary_flux_integral(
                faces_to_basisR[faceR_IDs], quad_wts, Fq)

            # Compute additional boundary flux integrals for diffusion terms
            resL_diff = self.calculate_boundary_flux_integral_sum(
                faces_to_basis_ref_gradL[faceL_IDs], quad_wts, FL_phys)

            resR_diff = self.calculate_boundary_flux_integral_sum(
                faces_to_basis_ref_gradR[faceR_IDs], quad_wts, FR_phys)

        return resL, resR, resL_diff, resR_diff  # [nif, nb, ns]
Esempio n. 16
0
def predictor_elem_explicit(solver, dt, W, U_pred):
    '''
	Calculates the predicted solution state for the ADER-DG method using a
	nonlinear solve of the weak form of the DG discretization in time.

	This function treats the source term explicitly. Appropriate for
	non-stiff systems.

	Inputs:
	-------
		solver: solver object
		dt: time step
		W: previous time step solution in space only [ne, nb, ns]

	Outputs:
	--------
		U_pred: predicted solution in space-time [ne, nb_st, ns]
	'''
    # Unpack
    threshold = solver.params["PredictorThreshold"]
    physics = solver.physics
    ns = physics.NUM_STATE_VARS
    mesh = solver.mesh

    basis = solver.basis
    basis_st = solver.basis_st

    elem_helpers = solver.elem_helpers
    elem_helpers_st = solver.elem_helpers_st
    ader_helpers = solver.ader_helpers
    nq_tile_constant = elem_helpers_st.nq_tile_constant
    basis_ref_grad = elem_helpers.basis_ref_grad
    basis_ref_grad_st = elem_helpers_st.basis_ref_grad
    order = solver.order
    quad_wts = elem_helpers.quad_wts
    basis_val = elem_helpers.basis_val
    djac_elems = elem_helpers.djac_elems

    FTR = ader_helpers.FTR
    MM = ader_helpers.MM
    SMS_elems = ader_helpers.SMS_elems
    iK = ader_helpers.iK

    # Calculate the average state for each element in spatial coordinates
    vol_elems = elem_helpers.vol_elems
    Wq = helpers.evaluate_state(W, basis_val, skip_interp=basis.skip_interp)
    W_bar = helpers.get_element_mean(Wq, quad_wts, djac_elems, vol_elems)

    # Initialize space-time coefficients
    U_pred, U_bar = solver.get_spacetime_guess(solver, W, U_pred, dt=dt)

    # Calculate the source and flux coefficients with initial guess
    source_coeffs = solver.source_coefficients(dt, order, basis_st, U_pred)
    flux_coeffs = solver.flux_coefficients(dt, order, basis_st, U_pred)

    # Iterate using a discrete Picard nonlinear solve for the
    # updated space-time coefficients.
    niter = 100
    for i in range(niter):

        U_pred_new = iK @ ( MM @ source_coeffs - \
         smsflux(SMS_elems, flux_coeffs) + FTR @ W )

        # We check when the coefficients are no longer changing.
        # This can lead to differences between NODAL and MODAL solutions.
        # This could be resolved by evaluating at the quadrature points
        # and comparing the error between those values.
        err = U_pred_new - U_pred

        if np.amax(np.abs(err)) < threshold:
            U_pred = U_pred_new
            print("Predictor iterations: ", i)
            break

        U_pred = np.copy(U_pred_new)

        source_coeffs = solver.source_coefficients(dt, order, basis_st, U_pred)
        flux_coeffs = solver.flux_coefficients(dt, order, basis_st, U_pred)

        if i == niter - 1:
            print('Sub-iterations not converging', np.amax(np.abs(err)))
            raise ValueError('Sub-iterations not converging')

    return U_pred  # [ne, nb_st, ns]
Esempio n. 17
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
Esempio n. 18
0
def predictor_elem_stiffimplicit(solver, dt, W, U_pred):
    '''
	Calculates the predicted solution state for the ADER-DG method using a
	nonlinear solve of the weak form of the DG discretization in time.

	This function utilizes scipy's root solver (specifically 'hybr' or a 
	modified Powell method) to converge the nonlinear solver. For this 
	method to be effecient, the user should also select the ODEGuess to
	provide the initial condition to the nonlinear solver. 

	This is suitable for very stiff systems such as those observed
	in chemically reacting flows.

	Inputs:
	-------
		solver: solver object
		dt: time step
		W: previous time step solution in space only [ne, nb, ns]

	Outputs:
	--------
		U_pred: predicted solution in space-time [ne, nb_st, ns]
	'''
    # Unpack
    threshold = solver.params["PredictorThreshold"]
    physics = solver.physics
    ns = physics.NUM_STATE_VARS
    mesh = solver.mesh

    basis = solver.basis
    basis_st = solver.basis_st

    elem_helpers = solver.elem_helpers
    ader_helpers = solver.ader_helpers

    order = solver.order
    quad_wts = elem_helpers.quad_wts
    basis_val = elem_helpers.basis_val
    djac_elems = elem_helpers.djac_elems

    FTR = ader_helpers.FTR
    MM = ader_helpers.MM
    SMS_elems = ader_helpers.SMS_elems
    iK = ader_helpers.iK

    # Calculate the average state for each element in spatial coordinates
    vol_elems = elem_helpers.vol_elems
    Wq = helpers.evaluate_state(W, basis_val, skip_interp=basis.skip_interp)
    W_bar = helpers.get_element_mean(Wq, quad_wts, djac_elems, vol_elems)

    # Initialize space-time coefficients
    U_pred, U_bar = solver.get_spacetime_guess(solver, W, U_pred, dt=dt)

    # Calculate the source and flux coefficients with initial guess
    source_coeffs = solver.source_coefficients(dt, order, basis_st, U_pred)
    flux_coeffs = solver.flux_coefficients(dt, order, basis_st, U_pred)

    def rhs_weakform(q):
        '''
		Solves the weak form of the DG discretization while doing
		integration by parts on the temporal term.

		Inputs:
		-------
			q: Space-time polynomial coffecients [ne x nb_st x ns]

		Outputs:
		--------
			zero: The rhs of the nonlinear solver should be zero 
					[ne x nb_st x ns]
		'''
        q = q.reshape([U_pred.shape[0], U_pred.shape[1], U_pred.shape[2]])

        source_coeffs = solver.source_coefficients(dt, order, basis_st, q)
        flux_coeffs = solver.flux_coefficients(dt, order, basis_st, q)
        zero = np.einsum(
            'jk, ikm -> ijm', iK,
            np.einsum('jk, ikl -> ijl', MM, source_coeffs) -
            np.einsum('ijkl, ikml -> ijm', SMS_elems, flux_coeffs) +
            np.einsum('jk, ikm -> ijm', FTR, W)) - q

        q.reshape(-1)  # reshape for the nonlinear solver
        return zero.reshape(-1)  # reshape for the nonlinear solver

    # Iterate using root function
    sol = root(rhs_weakform,
               U_pred.reshape(-1),
               tol=1e-15,
               jac=None,
               method='hybr',
               options={
                   'maxfev': 50000,
                   'xtol': 1e-15
               })
    U_pred = np.copy(sol.x.reshape([U_pred.shape[0], U_pred.shape[1], ns]))

    # Note: Other nonlinear solvers could be more efficient. Further work is
    # needed to determine the most efficient method. Commented code below
    # is another approach.
    # sol = newton_krylov(fun, U_pred.reshape(-1), iter=None,
    # rdiff=None, method='lgmres', maxiter=100)
    # U_pred = np.copy(sol.reshape([U_pred.shape[0], U_pred.shape[1], ns]))

    return U_pred  # [ne, nb_st, ns]
Esempio n. 19
0
    def get_interior_face_residual(self, faceL_IDs, faceR_IDs, UcL, UcR):
        # Unpack
        mesh = self.mesh
        physics = self.physics
        ns = physics.NUM_STATE_VARS

        time_skip = self.elem_helpers_st.time_skip
        nq_tile_constant = self.elem_helpers_st.nq_tile_constant

        int_face_helpers = self.int_face_helpers
        int_face_helpers_st = self.int_face_helpers_st

        faceL_id_st = int_face_helpers_st.faceL_IDs_st
        faceR_id_st = int_face_helpers_st.faceR_IDs_st

        quad_wts_st = int_face_helpers_st.quad_wts

        faces_to_basisL = int_face_helpers.faces_to_basisL
        faces_to_basisR = int_face_helpers.faces_to_basisR

        faces_to_basisL_st = int_face_helpers_st.faces_to_basisL
        faces_to_basisR_st = int_face_helpers_st.faces_to_basisR

        faces_to_basis_ref_gradL = \
         int_face_helpers.faces_to_basis_ref_gradL
        faces_to_basis_ref_gradR = \
         int_face_helpers.faces_to_basis_ref_gradR

        faces_to_basis_ref_gradL_st = \
         int_face_helpers_st.faces_to_basis_ref_gradL
        faces_to_basis_ref_gradR_st = \
         int_face_helpers_st.faces_to_basis_ref_gradR

        basis_valL = faces_to_basisL[faceL_IDs]
        basis_valR = faces_to_basisR[faceR_IDs]

        basis_valL_st = faces_to_basisL_st[faceL_id_st]
        basis_valR_st = faces_to_basisR_st[faceR_id_st]

        ijacL_elems = int_face_helpers.ijacL_elems
        ijacR_elems = int_face_helpers.ijacR_elems

        fluxes = self.params["ConvFluxSwitch"]

        # Interpolate state at quad points
        UqL = helpers.evaluate_state(UcL, basis_valL_st)  # [nf, nq_st, ns]
        UqR = helpers.evaluate_state(UcR, basis_valR_st)  # [nf, nq_st, ns]

        # Interpolate gradient of state at quad points
        gUqL_ref = self.evaluate_gradient(
            UcL, faces_to_basis_ref_gradL_st[faceL_id_st, :, :, :-1])
        gUqR_ref = self.evaluate_gradient(
            UcR, faces_to_basis_ref_gradR_st[faceR_id_st, :, :, :-1])

        ijacL_elems_st = np.tile(ijacL_elems, (1, time_skip, 1, 1))
        ijacR_elems_st = np.tile(ijacR_elems, (1, time_skip, 1, 1))

        # Make gradient the physical gradient at L/R states
        gUqL = self.ref_to_phys_grad(ijacL_elems_st, gUqL_ref)
        gUqR = self.ref_to_phys_grad(ijacR_elems_st, gUqR_ref)

        normals_int_faces = int_face_helpers.normals_int_faces
        normals_int_faces = np.tile(normals_int_faces,
                                    (normals_int_faces.shape[1], 1))

        # Allocate resL/R and resL/R_diff (needed for operator splitting)
        resL = np.zeros_like(self.stepper.res)
        resR = np.zeros_like(self.stepper.res)
        resL_diff = np.zeros_like(resL)
        resR_diff = np.zeros_like(resR)

        if physics.diff_flux_fcn:
            # Calculate diffusion flux helpers
            physics.diff_flux_fcn.compute_iface_helpers(self)

        if fluxes:
            # Compute numerical flux
            Fq = physics.get_conv_flux_numerical(UqL, UqR, normals_int_faces)
            # [nf, nq_st, ns]

            # Compute diffusion flux
            Fq_diff, FL, FR = physics.get_diff_flux_numerical(
                UqL, UqR, gUqL, gUqR, normals_int_faces)  # [nf, nq, ns],
            # [nf, nq, ns, ndims], [nf, nq, ns, ndims]
            Fq -= Fq_diff

            FL_phys = self.ref_to_phys_grad(ijacL_elems_st, FL)
            FR_phys = self.ref_to_phys_grad(ijacR_elems_st, FR)

            # Compute contribution to left and right element residuals
            resL = solver_tools.calculate_boundary_flux_integral(
                time_skip, basis_valL, quad_wts_st, Fq)
            resR = solver_tools.calculate_boundary_flux_integral(
                time_skip, basis_valR, quad_wts_st, Fq)

            # Compute additional boundary flux integrals for diffusion terms
            resL_diff = self.calculate_boundary_flux_integral_sum(
                time_skip, faces_to_basis_ref_gradL[faceL_IDs], quad_wts_st,
                FL_phys)

            resR_diff = self.calculate_boundary_flux_integral_sum(
                time_skip, faces_to_basis_ref_gradR[faceR_IDs], quad_wts_st,
                FR_phys)

        return resL, resR, resL_diff, resR_diff  # [nif, nb, ns]
Esempio n. 20
0
    def get_element_residual(self, Uc, res_elem):
        physics = self.physics
        mesh = self.mesh
        ns = physics.NUM_STATE_VARS
        ndims = physics.NDIMS

        elem_helpers = self.elem_helpers
        elem_helpers_st = self.elem_helpers_st
        nq_tile_constant = elem_helpers_st.nq_tile_constant

        quad_wts = elem_helpers.quad_wts
        quad_wts_st = elem_helpers_st.quad_wts
        quad_pts_st = elem_helpers_st.quad_pts

        basis_val_st = elem_helpers_st.basis_val
        basis_phys_grad_elems = elem_helpers.basis_phys_grad_elems

        basis_phys_grad_elems_st = elem_helpers_st.basis_phys_grad_elems
        basis_ref_grad_st = elem_helpers_st.basis_ref_grad

        basis_ref_grad = elem_helpers.basis_ref_grad
        ijac_elems = elem_helpers.ijac_elems

        x_elems = elem_helpers.x_elems
        x_elems_st = elem_helpers_st.x_elems

        nq = quad_wts.shape[0]
        nq_st = quad_wts_st.shape[0]

        fluxes = self.params["ConvFluxSwitch"]
        sources = self.params["SourceSwitch"]

        # Interpolate state at quad points
        Uq = helpers.evaluate_state(Uc, basis_val_st)  # [ne, nq_st, ns]

        # Interpolate gradient of state at quad points
        # gUq = solver_tools.evaluate_gradient(nq_tile_constant, Uc,
        # basis_phys_grad_elems)
        gUq_ref = self.evaluate_gradient(Uc, basis_ref_grad_st[:, :, :-1])

        ijac_elems_st = np.tile(ijac_elems, (1, nq_tile_constant, 1, 1))
        gUq = self.ref_to_phys_grad(ijac_elems_st, gUq_ref)

        if self.verbose:
            # Get min and max of state variables for reporting
            self.get_min_max_state(Uq)

        if fluxes:
            # Evaluate the flux volume integral.
            Fq = physics.get_conv_flux_interior(Uq)[0]  # [ne, nq, ns, ndims]

            if physics.diff_flux_fcn:
                # Evaluate the diffusion flux
                Fq -= physics.get_diff_flux_interior(Uq, gUq)
                # [ne, nq, ns, ndims]

            res_elem += solver_tools.calculate_volume_flux_integral(
                self, elem_helpers, elem_helpers_st, Fq)  # [ne, nb, ns]

        if sources:
            # Evaluate the source term integral

            # Get array in physical time from ref time
            t, elem_helpers_st.basis_time = solver_tools.ref_to_phys_time(
                mesh, self.time, self.stepper.dt, quad_pts_st[:, -1:],
                elem_helpers_st.basis_time)

            # Evaluate the source term at the quadrature points
            Sq = elem_helpers_st.Sq

            Sq[:] = 0.  # [ne, nq, sr, ndims]
            Sq = physics.eval_source_terms(Uq, x_elems_st, t, Sq)

            res_elem += solver_tools.calculate_source_term_integral(
                elem_helpers, elem_helpers_st, Sq)  # [ne, nb, ns]

        return res_elem  # [ne, nb, ns]
Esempio n. 21
0
def calculate_artificial_viscosity_integral(physics, elem_helpers, Uc,
                                            av_param, p):
    '''
	Calculates the artificial viscosity volume integral, given in:
		Hartmann, R. and Leicht, T, "Higher order and adaptive DG methods for
		compressible flows", p. 92, 2013.

	Inputs:
	-------
		physics: physics object
		elem_helpers: helpers defined in ElemHelpers
		Uc: state coefficients of each element
		av_param: artificial viscosity parameter
		p: solution basis order

	Outputs:
	--------
		res_elem: artificial viscosity residual array for all elements
		[ne, nb, ns]
	'''
    # Unpack
    quad_wts = elem_helpers.quad_wts  # [nq, 1]
    basis_phys_grad_elems = elem_helpers.basis_phys_grad_elems
    # [ne, nq, nb, dim]
    basis_val = elem_helpers.basis_val  # [nq, nb]
    djac_elems = elem_helpers.djac_elems  # [ne, nq, 1]
    vol_elems = elem_helpers.vol_elems  # [ne]
    ndims = basis_phys_grad_elems.shape[3]

    # Evaluate solution at quadrature points
    Uq = helpers.evaluate_state(Uc, basis_val)
    # Evaluate solution gradient at quadrature points
    grad_Uq = np.einsum('ijnl, ink -> ijkl', basis_phys_grad_elems, Uc)
    # Compute pressure
    pressure = physics.compute_additional_variable(
        "Pressure", Uq, flag_non_physical=False)[:, :, 0]
    # For Euler equations, use pressure as the smoothness variable
    if physics.PHYSICS_TYPE == general.PhysicsType.Euler:
        # Compute pressure gradient
        grad_p = physics.compute_pressure_gradient(Uq, grad_Uq)
        # Compute its magnitude
        norm_grad_p = np.linalg.norm(grad_p, axis=2)
        # Calculate smoothness switch
        f = norm_grad_p / (pressure + 1e-12)
    # For everything else, use the first solution variable
    else:
        U0 = Uq[:, :, 0]
        grad_U0 = grad_Uq[:, :, 0]
        norm_grad_U0 = np.linalg.norm(grad_U0, axis=2)
        # Calculate smoothness switch
        f = norm_grad_U0 / (U0 + 1e-12)

    # Compute s_k
    s = np.zeros((Uc.shape[0], ndims))
    # Loop over dimensions
    for k in range(ndims):
        # Loop over number of faces per element
        for i in range(elem_helpers.normals_elems.shape[1]):
            # Integrate normals
            s[:,
              k] += np.einsum('jx, ij -> i', elem_helpers.face_quad_wts,
                              np.abs(elem_helpers.normals_elems[:, i, :, k]))
        s[:, k] = 2 * vol_elems / s[:, k]
    # Compute h_k (the length scale in the kth direction)
    h = np.empty_like(s)
    # Loop over dimensions
    for k in range(ndims):
        h[:, k] = s[:, k] * (vol_elems / np.prod(s, axis=1))**(1 / 3)
    # Scale with polynomial order
    h_tilde = h / (p + 1)
    # Compute dissipation scaling
    epsilon = av_param * np.einsum('ij, il -> ijl', f, h_tilde**3)
    # Calculate integral, with state coeffs factored out
    integral = np.einsum('ijm, ijpm, ijnm, jx, ijx -> ipn', epsilon,
                         basis_phys_grad_elems, basis_phys_grad_elems,
                         quad_wts, djac_elems)
    # Calculate residual
    res_elem = np.einsum('ipn, ipk -> ink', integral, Uc)

    return res_elem  # [ne, nb, ns]
Esempio n. 22
0
def minmod_shock_indicator(limiter, solver, Uc):
    '''
	TVB modified Minmod calculation used to detect shocks

	Inputs:
	-------
		limiter: limiter object
		solver: solver object
		Uc: state coefficients [ne, nb, ns]

	Outputs:
	--------
		shock_elems: array with IDs of elements flagged for limiting
	'''
    # Unpack
    physics = solver.physics
    mesh = solver.mesh
    tvb_param = limiter.tvb_param
    ns = physics.NUM_STATE_VARS

    elem_helpers = solver.elem_helpers
    int_face_helpers = solver.int_face_helpers

    elemP_IDs = limiter.elemP_IDs
    elemM_IDs = limiter.elemM_IDs
    djacs = limiter.djac_elems
    djacP = limiter.djac_elems[elemP_IDs]
    djacM = limiter.djac_elems[elemM_IDs]

    UcP = solver.state_coeffs[elemP_IDs]
    UcM = solver.state_coeffs[elemM_IDs]

    # Interpolate state at quadrature points over element and on faces
    U_elem_faces = helpers.evaluate_state(Uc,
                                          limiter.basis_val_elem_faces,
                                          skip_interp=solver.basis.skip_interp)
    nq_elem = limiter.quad_wts_elem.shape[0]
    U_elem = U_elem_faces[:, :nq_elem, :]
    U_face = U_elem_faces[:, nq_elem:, :]

    if solver.basis.skip_interp is True:
        U_face = np.zeros([U_elem.shape[0], 2, ns])
        U_face[:, 0, :] = U_elem[:, 0, :]
        U_face[:, 1, :] = U_elem[:, -1, :]

    # Average value of states
    U_bar = helpers.get_element_mean(U_elem, limiter.quad_wts_elem, djacs,
                                     limiter.elem_vols)

    # UcP neighbor evaluated at quadrature points
    Up_elem = helpers.evaluate_state(UcP,
                                     elem_helpers.basis_val,
                                     skip_interp=solver.basis.skip_interp)
    # Average value of state
    Up_bar = helpers.get_element_mean(Up_elem, limiter.quad_wts_elem, djacP,
                                      limiter.elem_vols[elemP_IDs])

    # UcM neighbor evaluated at quadrature points
    Um_elem = helpers.evaluate_state(UcM,
                                     elem_helpers.basis_val,
                                     skip_interp=solver.basis.skip_interp)
    # Average value of state
    Um_bar = helpers.get_element_mean(Um_elem, limiter.quad_wts_elem, djacM,
                                      limiter.elem_vols[elemM_IDs])

    # Unpack the left and right eigenvector matrices
    right_eigen = limiter.right_eigen
    left_eigen = limiter.left_eigen

    # Store the polynomial coeff values for Up, Um, and U.
    limiter.U_elem = Uc
    limiter.Up_elem = UcP
    limiter.Um_elem = UcM

    # Store the average values for Up, Um, and U.
    limiter.U_bar = U_bar
    limiter.Up_bar = Up_bar
    limiter.Um_bar = Um_bar

    U_tilde = (U_face[:, 1, :] - U_bar[:, 0, :]).reshape(U_bar.shape)
    U_dtilde = (U_bar[:, 0, :] - U_face[:, 0, :]).reshape(U_bar.shape)

    deltaP_u_bar = Up_bar - U_bar
    deltaM_u_bar = U_bar - Um_bar

    aj = np.zeros([U_tilde.shape[0], 3, ns])
    aj[:, 0, :] = U_tilde[:, 0, :]
    aj[:, 1, :] = deltaP_u_bar[:, 0, :]
    aj[:, 2, :] = deltaM_u_bar[:, 0, :]
    u_tilde_mod = minmod(aj)

    tvb = np.where(np.abs(aj[:, 0, :]) <= tvb_param * \
      limiter.elem_vols[0]**2)[0]
    u_tilde_mod[tvb, 0] = aj[tvb, 0, :]

    aj = np.zeros([U_dtilde.shape[0], 3, ns])
    aj[:, 0, :] = U_dtilde[:, 0, :]
    aj[:, 1, :] = deltaP_u_bar[:, 0, :]
    aj[:, 2, :] = deltaM_u_bar[:, 0, :]
    u_dtilde_mod = minmod(aj)
    tvd = np.where(np.abs(aj[:, 0, :]) <= tvb_param * \
      limiter.elem_vols[0]**2)[0]
    u_dtilde_mod[tvd, 0] = aj[tvd, 0, :]

    check1 = u_tilde_mod - U_tilde
    check2 = u_dtilde_mod - U_dtilde

    shock_elems = np.where((np.abs(check1[:, :, 0]) > 1.e-12)
                           | (np.abs(check2[:, :, 0]) > 1.e-12))[0]

    # print(shock_elems)
    return shock_elems
Esempio n. 23
0
    def limit_solution(self, solver, Uc):
        # Unpack
        physics = solver.physics
        elem_helpers = solver.elem_helpers
        int_face_helpers = solver.int_face_helpers
        basis = solver.basis

        djac = self.djac_elems

        # Interpolate state at quadrature points over element and on faces
        U_elem_faces = helpers.evaluate_state(Uc,
                                              self.basis_val_elem_faces,
                                              skip_interp=basis.skip_interp)
        nq_elem = self.quad_wts_elem.shape[0]
        U_elem = U_elem_faces[:, :nq_elem, :]

        # Average value of state
        U_bar = helpers.get_element_mean(U_elem, self.quad_wts_elem, djac,
                                         self.elem_vols)
        ne = self.elem_vols.shape[0]
        # Density and pressure from averaged state
        rho_bar = physics.compute_variable(self.var_name1, U_bar)
        p_bar = physics.compute_variable(self.var_name2, U_bar)
        rhoY_bar = physics.compute_variable(self.var_name3, U_bar)

        if np.any(rho_bar < 0.) or np.any(p_bar < 0.) or np.any(rhoY_bar < 0.):
            raise errors.NotPhysicalError

        # Ignore divide-by-zero
        np.seterr(divide='ignore')
        ''' Limit density '''
        # Compute density
        rho_elem_faces = physics.compute_variable(self.var_name1, U_elem_faces)
        # Check if limiting is needed
        theta = np.abs((rho_bar - POS_TOL) / (rho_bar - rho_elem_faces))
        # Truncate theta1; otherwise, can get noticeably different
        # results across machines, possibly due to poor conditioning in its
        # calculation
        theta1 = trunc(np.minimum(1., np.min(theta, axis=1)))

        irho = physics.get_state_index(self.var_name1)
        # Get IDs of elements that need limiting
        elem_IDs = np.where(theta1 < 1.)[0]
        # Modify density coefficients
        if basis.MODAL_OR_NODAL == general.ModalOrNodal.Nodal:
            Uc[elem_IDs, :, irho] = theta1[elem_IDs]*Uc[elem_IDs, :, irho] \
              + (1. - theta1[elem_IDs])*rho_bar[elem_IDs, 0]
        elif basis.MODAL_OR_NODAL == general.ModalOrNodal.Modal:
            Uc[elem_IDs, :, irho] *= theta1[elem_IDs]
            Uc[elem_IDs, 0,
               irho] += (1. - theta1[elem_IDs, 0]) * rho_bar[elem_IDs, 0, 0]
        else:
            raise NotImplementedError

        if np.any(theta1 < 1.):
            # Intermediate limited solution
            U_elem_faces = helpers.evaluate_state(
                Uc, self.basis_val_elem_faces, skip_interp=basis.skip_interp)
        ''' Limit mass fraction '''
        rhoY_elem_faces = physics.compute_variable(self.var_name3,
                                                   U_elem_faces)
        theta = np.abs(rhoY_bar / (rhoY_bar - rhoY_elem_faces + POS_TOL))
        # Truncate theta2; otherwise, can get noticeably different
        # results across machines, possibly due to poor conditioning in its
        # calculation
        theta2 = trunc(np.minimum(1., np.amin(theta, axis=1)))

        irhoY = physics.get_state_index(self.var_name3)
        # Get IDs of elements that need limiting
        elem_IDs = np.where(theta2 < 1.)[0]
        # Modify density coefficients
        if basis.MODAL_OR_NODAL == general.ModalOrNodal.Nodal:
            Uc[elem_IDs, :,
               irhoY] = theta2[elem_IDs] * Uc[elem_IDs, :, irhoY] + (
                   1. - theta2[elem_IDs]) * rho_bar[elem_IDs, 0]
        elif basis.MODAL_OR_NODAL == general.ModalOrNodal.Modal:
            Uc[elem_IDs, :, irhoY] *= theta2[elem_IDs]
            Uc[elem_IDs, 0,
               irhoY] += (1. - theta2[elem_IDs, 0]) * rho_bar[elem_IDs, 0, 0]
        else:
            raise NotImplementedError

        if np.any(theta2 < 1.):
            U_elem_faces = helpers.evaluate_state(
                Uc, self.basis_val_elem_faces, skip_interp=basis.skip_interp)
        ''' Limit pressure '''
        # Compute pressure at quadrature points
        p_elem_faces = physics.compute_variable(self.var_name2, U_elem_faces)
        theta[:] = 1.
        # Indices where pressure is negative
        negative_p_indices = np.where(p_elem_faces < 0.)
        elem_IDs = negative_p_indices[0]
        i_neg_p = negative_p_indices[1]

        theta[elem_IDs, i_neg_p] = p_bar[elem_IDs, :, 0] / (
            p_bar[elem_IDs, :, 0] - p_elem_faces[elem_IDs, i_neg_p])

        # Truncate theta3; otherwise, can get noticeably different
        # results across machines, possibly due to poor conditioning in its
        # calculation
        theta3 = trunc(np.min(theta, axis=1))
        # Get IDs of elements that need limiting
        elem_IDs = np.where(theta3 < 1.)[0]
        # Modify coefficients
        if basis.MODAL_OR_NODAL == general.ModalOrNodal.Nodal:
            Uc[elem_IDs] = np.einsum(
                'im, ijk -> ijk', theta3[elem_IDs], Uc[elem_IDs]) + np.einsum(
                    'im, ijk -> ijk', 1 - theta3[elem_IDs], U_bar[elem_IDs])
        elif basis.MODAL_OR_NODAL == general.ModalOrNodal.Modal:
            Uc[elem_IDs] *= np.expand_dims(theta3[elem_IDs], axis=2)
            Uc[elem_IDs, 0] += np.einsum('im, ijk -> ik', 1 - theta3[elem_IDs],
                                         U_bar[elem_IDs])
        else:
            raise NotImplementedError

        np.seterr(divide='warn')

        return Uc  # [ne, nq, ns]
Esempio n. 24
0
def predictor_elem_implicit(solver, dt, W, U_pred):
    '''
	Calculates the predicted solution state for the ADER-DG method using a
	nonlinear solve of the weak form of the DG discretization in time.

	This function applies the source term implicitly. Appropriate for
	stiff systems of equations. The implicit solve utilizes the Sylvester
	equation of the form:

		AX + XB = C

	This is a built-in function via the scipy.linalg library.

	Inputs:
	-------
		solver: solver object
		dt: time step
		W: previous time step solution in space only [ne, nb, ns]

	Outputs:
	--------
		U_pred: predicted solution in space-time [ne, nb_st, ns]
	'''
    # Unpack
    threshold = solver.params["PredictorThreshold"]
    physics = solver.physics
    source_terms = physics.source_terms

    ns = physics.NUM_STATE_VARS
    mesh = solver.mesh

    basis = solver.basis
    basis_st = solver.basis_st

    order = solver.order
    elem_helpers = solver.elem_helpers
    ader_helpers = solver.ader_helpers

    quad_wts = elem_helpers.quad_wts
    basis_val = elem_helpers.basis_val
    djac_elems = elem_helpers.djac_elems
    x_elems = elem_helpers.x_elems

    FTR = ader_helpers.FTR
    iMM = ader_helpers.iMM
    SMS_elems = ader_helpers.SMS_elems
    K = ader_helpers.K

    # Initialize space-time coefficients
    U_pred, U_bar = solver.get_spacetime_guess(solver, W, U_pred, dt=dt)

    # Get physical average for testing purposes
    vol_elems = elem_helpers.vol_elems
    Wq = helpers.evaluate_state(W, basis_val, skip_interp=basis.skip_interp)
    W_bar = helpers.get_element_mean(Wq, quad_wts, djac_elems, vol_elems)

    # Only evaluate Jacobian for stiff sources
    temp_sources = physics.source_terms.copy()
    physics.source_terms = physics.implicit_sources.copy()

    # Calculate the source term Jacobian using average state
    Sjac = np.zeros([U_pred.shape[0], 1, ns, ns])
    Sjac = physics.eval_source_term_jacobians(W_bar, x_elems, solver.time,
                                              Sjac)
    Sjac = Sjac[:, 0, :, :]

    # Set all sources for source_coeffs calculation
    physics.source_terms = temp_sources.copy()

    # Calculate the source and flux coefficients with initial guess
    source_coeffs = solver.source_coefficients(dt, order, basis_st, U_pred)
    flux_coeffs = solver.flux_coefficients(dt, order, basis_st, U_pred)

    # Iterate using a nonlinear Sylvester solver for the
    # updated space-time coefficients. Solves for X in the form:
    # 	AX + XB = C
    # Update: We now transform AX+XB=C into KX=C using kronecker
    # products.
    niter = 10000

    A = np.matmul(iMM, K)

    U_pred_new = np.zeros_like(U_pred)

    for i in range(niter):

        B = -1.0 * dt * Sjac.transpose(0, 2, 1)

        Q = np.einsum('jk, ikm -> ijm', FTR, W) - np.einsum(
            'ijkl, ikml -> ijm', SMS_elems, flux_coeffs)

        C = source_coeffs - dt*np.matmul(U_pred[:],
          Sjac[:].transpose(0, 2, 1)) + \
          np.einsum('jk, ikl -> ijl', iMM, Q)

        # Build identity matrices for kronecker procucts
        I2 = np.eye(A.shape[1])
        I1 = np.eye(B.shape[1])

        for ie in range(U_pred.shape[0]):

            # Conduct kronecker products to transfrom Ax+xB=C system to Ax=b
            kronecker = np.kron(I1, A) + np.kron(B[ie, :, :].transpose(), I2)
            U_pred_hold = np.linalg.solve(kronecker,
                                          C[ie, :, :].transpose().reshape(-1))
            U_pred_new[ie, :, :] = U_pred_hold.reshape(
                U_pred.shape[2], U_pred.shape[1]).transpose()

            # Note: Previous implementaion used sylvester solve directly.
            # This still requires further testing to determine which is
            # more efficient.
            # U_pred_new[ie, :, :] = solve_sylvester(A, B[ie, :, :],
            # 		C[ie, :, :])

        # We check when the coefficients are no longer changing.
        # This can lead to differences between NODAL and MODAL solutions.
        # This could be resolved by evaluating at the quadrature points
        # and comparing the error between those values.
        err = U_pred_new - U_pred

        if (np.amax(np.abs(err)) < threshold):
            print("Predictor iterations: ", i)
            U_pred = np.copy(U_pred_new)
            break

        U_pred = np.copy(U_pred_new)

        source_coeffs = solver.source_coefficients(dt, order, basis_st, U_pred)
        flux_coeffs = solver.flux_coefficients(dt, order, basis_st, U_pred)

        # Recalculate jacobian for subiterations (Default is OFF)
        solver.recalculate_jacobian(solver, U_pred, dt, Sjac)

        if i == niter - 1:
            print('Sub-iterations not converging', np.amax(np.abs(err)))

    return U_pred  #_update # [ne, nb_st, ns]
Esempio n. 25
0
    def get_boundary_face_residual(self, bgroup, face_ID, Uc, resB):
        # Unpack
        mesh = self.mesh
        ndims = mesh.ndims
        physics = self.physics
        ns = physics.NUM_STATE_VARS
        fluxes = self.params["ConvFluxSwitch"]

        bgroup_num = bgroup.number
        nq_t = self.elem_helpers_st.nq_tile_constant
        time_skip = self.elem_helpers_st.time_skip
        time_tile = self.elem_helpers_st.time_tile

        bface_helpers = self.bface_helpers
        bface_helpers_st = self.bface_helpers_st
        quad_wts_st = bface_helpers_st.quad_wts
        faces_to_xref_st = bface_helpers_st.faces_to_xref

        faces_to_basis = bface_helpers.faces_to_basis
        faces_to_basis_st = bface_helpers_st.faces_to_basis
        faces_to_basis_ref_grad_st = bface_helpers_st.faces_to_basis_ref_grad
        faces_to_basis_ref_grad = bface_helpers.faces_to_basis_ref_grad

        normals_bgroups = bface_helpers.normals_bgroups
        x_bgroups = bface_helpers.x_bgroups
        ijac_bgroups = bface_helpers.ijac_bgroups
        face_ID = bface_helpers.face_IDs[bgroup_num]
        face_ID_st = bface_helpers_st.face_IDs_st[bgroup_num]

        basis_val = faces_to_basis[face_ID]
        basis_val_st = faces_to_basis_st[face_ID_st]
        basis_ref_grad_st = faces_to_basis_ref_grad_st[face_ID_st]
        basis_ref_grad = faces_to_basis_ref_grad[face_ID]
        xref_st = faces_to_xref_st[face_ID_st]
        ijac = ijac_bgroups[bgroup_num]

        nq_st = quad_wts_st.shape[0]

        # Get array in physical time from ref time
        t, self.elem_helpers_st.basis_time = solver_tools.ref_to_phys_time(
            mesh, self.time, self.stepper.dt, xref_st[:, :, -1:],
            self.elem_helpers_st.basis_time)
        ''' 
		Define time slice to make time arrays one dimensional cases even
		in the 2D case.
		'''

        # Build tiled time array. Removes unnecessary extra data.
        time_slice = slice(0, t.shape[1], time_skip)
        time = t[:, time_slice]
        time_t = np.tile(time, (1, time_tile, 1))

        # Interpolate state at quadrature points
        UqI = helpers.evaluate_state(Uc, basis_val_st)  # [nbf, nq, ns]

        # Interpolate gradient of state at quad points
        gUq_ref = self.evaluate_gradient(Uc, basis_ref_grad_st[:, :, :, :-1])
        # import code; code.interact(local=locals())
        ijac_st = np.tile(ijac, (1, time_skip, 1, 1))

        # Make ref gradient of state the physical gradient
        gUq = self.ref_to_phys_grad(ijac_st, gUq_ref)

        # Unpack normals and x on boundary faces
        normals = normals_bgroups[bgroup_num]
        x = x_bgroups[bgroup_num]

        # Tile normals and x to prepare for looping over elements in time
        normals = np.tile(normals, (time_tile, 1))
        x = np.tile(x, (time_tile, 1))

        # Get boundary state
        BC = physics.BCs[bgroup.name]
        nbf = UqI.shape[0]
        Fq = np.zeros([nbf, nq_st, ns])
        FqB = np.zeros([nbf, nq_st, ns, ndims])

        # Need to allocate data for gradient when not using diffusion
        if not physics.diff_flux_fcn:
            gUq = np.zeros([Uc.shape[0], nq_st, ns, ndims])

        # Compute any additional helpers for diffusive flux fcn
        if physics.diff_flux_fcn:
            physics.diff_flux_fcn.compute_bface_helpers(self, bgroup_num)

        if fluxes:
            # Loop over time to apply BC at each temporal quadrature point
            for i in range(t.shape[1]):
                # Need time to be constant not an array to work with
                # get_boundary_flux appropriately
                t_ = time_t[:, i][0, 0]
                x_ = x[:, i].reshape([nbf, 1, ndims])
                normals_ = normals[:, i].reshape([nbf, 1, ndims])

                Fq_hold, FqB_hold = BC.get_boundary_flux(
                    physics,
                    UqI[:, i, :].reshape([nbf, 1, ns]),
                    normals_,
                    x_,
                    t_,
                    gUq=gUq[:, i, :, :].reshape([nbf, 1, ns, ndims]))

                if not physics.diff_flux_fcn:
                    FqB_hold = np.zeros([nbf, ns, ndims])

                Fq[:, i, :] = Fq_hold.reshape([nbf, ns])
                FqB[:, i, :, :] = FqB_hold.reshape([nbf, ns, ndims])

            FqB_phys = self.ref_to_phys_grad(ijac_st, FqB)

            resB = solver_tools.calculate_boundary_flux_integral(
                time_skip, basis_val, quad_wts_st, Fq)  # [nbf, nb, ns]

            resB -= self.calculate_boundary_flux_integral_sum(
                time_skip, basis_ref_grad, quad_wts_st, FqB_phys)

        return resB  # [nbf, nb, ns]
Esempio n. 26
0
def get_boundary_info(solver,
                      mesh,
                      physics,
                      bname,
                      var_name,
                      dot_normal_with_vec=False,
                      vec=0.,
                      integrate=True,
                      plot_vs_x=False,
                      plot_vs_y=False,
                      ylabel=None,
                      fmt='k-',
                      legend_label=None,
                      **kwargs):
    '''
	This function integrates and/or plots a given quantity over a specific
	boundary.

	Inputs:
	-------
	    mesh: mesh object
	    physics: physics object
	    solver: solver object
	    bname: name of boundary
	    var_name: name of variable to compute
	    dot_normal_with_vec: if dot_normal_with_vec is True, will multiply
	    	var by the dot product between the outward-pointing unit normal
	    	vector and vec
	    vec: vector to dot with normal (see above) [ndims]
	    integrate: if True, will integrate variable over boundary
	    plot_vs_x: if True, will plot variable vs. x
	    plot_vs_y: if True, will plot variable vs. y (lower priority than
	    	plot_vs_x)
		ylabel: y-axis label for figure
		fmt: format string for plotting, e.g. "bo" for blue circles
		legend_label: legend label
		kwargs: keyword arguments (see plot_defs.finalize_plot)
	'''
    if mesh.ndims != 2:
        raise errors.IncompatibleError

    # Extract boundary group
    boundary_group = mesh.boundary_groups[bname]
    boundary_num = boundary_group.number
    # Extract helpers
    bface_helpers = solver.bface_helpers
    quad_pts = bface_helpers.quad_pts
    quad_wts = bface_helpers.quad_wts
    faces_to_basis = bface_helpers.faces_to_basis
    normals_bgroups = bface_helpers.normals_bgroups
    x_bgroups = bface_helpers.x_bgroups
    nq = quad_wts.shape[0]

    # For plotting
    plot = True
    if plot_vs_x:
        xlabel = "x"
        d = 0
    elif plot_vs_y:
        xlabel = "y"
        d = 1
    else:
        plot = False
    if plot:
        bvalues = np.zeros([boundary_group.num_boundary_faces, nq])
        # [num_boundary_faces, nq]
        bpoints = x_bgroups[boundary_num][:, :, d].flatten()
        # [num_boundary_faces, nq, ndims]

    integ_val = 0.

    if dot_normal_with_vec:
        # Convert to numpy array
        vec = np.array(vec)
        vec.shape = 1, 2

    # Extract
    elem_ID = bface_helpers.elem_IDs[boundary_num]
    face_ID = bface_helpers.face_IDs[boundary_num]
    basis_val = faces_to_basis[face_ID]

    # Interpolate state at quad points
    Uq = helpers.evaluate_state(solver.state_coeffs[elem_ID], basis_val)

    # Get requested variable
    varq = physics.compute_variable(var_name, Uq)  # [nf, nq, 1]

    # Normals
    normals = normals_bgroups[boundary_num]  # [nf, nq, ndims]
    jac = np.linalg.norm(normals, axis=2, keepdims=True)  # [nf, nq, 1]

    # If requested, account for normal and dot with input dir
    if dot_normal_with_vec:
        varq *= np.sum(normals / jac * vec, axis=2, keepdims=True)

    # Integrate and sum over faces
    if integrate:
        integ_val = np.sum(np.sum(varq * jac * quad_wts, axis=1), axis=0)

    if plot:
        bvalues = varq[:, :, 0]

    if integrate:
        print("Boundary integral = %g" % (integ_val))

    # Plot if requested
    if plot:
        plt.figure()
        bvalues = bvalues.flatten()
        ylabel = plot_defs.get_ylabel(physics, var_name, ylabel)
        plot_defs.plot_1D(physics, bpoints, bvalues, ylabel, fmt, legend_label)
        plot_defs.finalize_plot(xlabel=xlabel, **kwargs)
Esempio n. 27
0
    solution = []
    importlib.reload(model_psr)

    # Run the simulation
    os.system("quail " + filename)

    # Access and process the final time step
    final_file = model_psr.prefix + '_final.pkl'

    # Read data file
    solver = readwritedatafiles.read_data_file(final_file)

    Uc = solver.state_coeffs
    basis_val = solver.elem_helpers.basis_val
    Uq = helpers.evaluate_state(Uc, basis_val)

    solution.append(Uq[0, 0, 0])  # Assumes 0D

    order = model_psr.order

    file_out = f'convergence_testing/{scheme_name}/{j}.pkl'
    write_file(file_out, solution)

    # text from previous case
    if j + 1 < dt.shape[0]:
        text_to_search = f'timestep = {dt[j]}'
        replacement_text = f'timestep = {dt[j+1]}'
        search_and_replace(filename, text_to_search, replacement_text)

    time.sleep(1)
Esempio n. 28
0
    def flux_coefficients(self, dt, order, basis, Up):
        '''
		Calculates the polynomial coefficients for the flux functions in
		ADER-DG

		Inputs:
		-------
			dt: time step size
			order: solution order
			basis: basis object
			Up: coefficients of predicted solution [ne, nb_st, ns]

		Outputs:
		--------
			F: polynomial coefficients of the flux function
				[ne, nb_st, ns, ndims]
		'''
        # Unpack
        physics = self.physics
        mesh = self.mesh
        ns = physics.NUM_STATE_VARS
        ndims = physics.NDIMS
        params = self.params

        InterpolateFluxADER = params["InterpolateFluxADER"]

        elem_helpers = self.elem_helpers
        elem_helpers_st = self.elem_helpers_st
        djac_elems = elem_helpers.djac_elems
        basis_ref_grad_st = elem_helpers_st.basis_ref_grad
        ijac_elems = elem_helpers.ijac_elems
        ader_helpers = self.ader_helpers
        ijac_nodes = ader_helpers.ijac_elems
        nq_t = self.elem_helpers_st.nq_tile_constant

        # Allocate flux coefficients
        F = np.zeros(
            [Up.shape[0],
             basis.get_num_basis_coeff(order), ns, ndims],
            dtype=Up.dtype)

        # Flux coefficient calc from interpolation or L2-projection
        if InterpolateFluxADER:
            # Calculate flux
            Fq = physics.get_conv_flux_interior(Up)[0]

            if physics.diff_flux_fcn:
                # Calculate the gradient of the state
                gUp_ref = solver_tools.get_spacetime_gradient(self, Up)
                gUp = self.ref_to_phys_grad(ijac_nodes, gUp_ref)
                Fq -= physics.get_diff_flux_interior(Up, gUp)

            # Interpolate flux coefficient to nodes
            dg_tools.interpolate_to_nodes(Fq, F)

        else:
            # Unpack for L2-projection
            basis_val_st = elem_helpers_st.basis_val
            quad_wts_st = elem_helpers_st.quad_wts
            quad_wts = elem_helpers.quad_wts
            quad_pts_st = elem_helpers_st.quad_pts
            quad_pts = elem_helpers.quad_pts
            nq_st = quad_wts_st.shape[0]
            nq = quad_wts.shape[0]
            iMM_elems = ader_helpers.iMM_elems

            # Interpolate state at quadrature points
            Uq = helpers.evaluate_state(Up, basis_val_st)

            # Interpolate gradient of the state
            gUq_ref = self.evaluate_gradient(Up, basis_ref_grad_st[:, :, :-1])

            ijac_elems_st = np.tile(ijac_elems, (1, nq_t, 1, 1))
            gUq = self.ref_to_phys_grad(ijac_elems_st, gUq_ref)

            # Evaluate the inviscid flux
            Fq = physics.get_conv_flux_interior(Uq)[0]

            # Evaluate the diffusive flux
            if physics.diff_flux_fcn:
                Fq -= physics.get_diff_flux_interior(Uq, gUq)

            # Project Fq to the space-time basis coefficients
            for d in range(ndims):
                solver_tools.L2_projection(mesh, iMM_elems, basis, quad_pts_st,
                                           quad_wts_st,
                                           np.tile(djac_elems, (nq_t, 1)),
                                           Fq[:, :, :, d], F[:, :, :, d])

        return F * dt / 2.0  # [ne, nb_st, ns, ndims]
Esempio n. 29
0
    def limit_solution(self, solver, Uc):
        # Unpack
        ns = solver.physics.NUM_STATE_VARS
        physics = solver.physics
        mesh = solver.mesh
        elem_helpers = solver.elem_helpers
        int_face_helpers = solver.int_face_helpers

        vols = self.elem_vols
        basis_phys_grads = elem_helpers.basis_phys_grad_elems
        quad_wts = elem_helpers.quad_wts
        elemP_IDs = self.elemP_IDs
        elemM_IDs = self.elemM_IDs
        djacs = self.djac_elems
        djacP = self.djac_elems[elemP_IDs]
        djacM = self.djac_elems[elemM_IDs]

        # Interpolate state at quadrature points over element and on faces
        U_elem_faces = helpers.evaluate_state(
            Uc,
            self.basis_val_elem_faces,
            skip_interp=solver.basis.skip_interp)

        nq_elem = self.quad_wts_elem.shape[0]
        U_elem = U_elem_faces[:, :nq_elem, :]
        U_face = U_elem_faces[:, nq_elem:, :]

        # Average value of states
        U_bar = helpers.get_element_mean(U_elem, self.quad_wts_elem, djacs,
                                         vols)

        # Calculate the eigenvectors if available (they are pre-allocated in
        # precompute_helpers)
        get_eigenvector_function = getattr(physics, "get_conv_eigenvectors",
                                           None)
        if callable(get_eigenvector_function):
            self.right_eigen, self.left_eigen = \
              physics.get_conv_eigenvectors(U_bar)

        Vc = np.einsum('elij, elj -> eli', self.left_eigen, Uc)

        # Determine if the elements requires limiting
        shock_indicated = self.shock_indicator(self, solver, Uc)

        # Unpack limiter info from shock indicator
        p0 = np.einsum('ebij, elj -> eli', self.left_eigen, self.Um_elem)
        p1 = np.einsum('ebij, elj -> eli', self.left_eigen, self.U_elem)
        p2 = np.einsum('ebij, elj -> eli', self.left_eigen, self.Up_elem)

        p0_bar = np.einsum('ebij, elj -> eli', self.left_eigen, self.Um_bar)
        p1_bar = np.einsum('ebij, elj -> eli', self.left_eigen, self.U_bar)
        p2_bar = np.einsum('ebij, elj -> eli', self.left_eigen, self.Up_bar)

        # Check basis type and adjust coefficients to  maintain element
        # p1's average value.
        if solver.basis.MODAL_OR_NODAL == general.ModalOrNodal.Modal:
            p0_tilde = p0
            p2_tilde = p2
            p0_tilde[:, 0, :] = p1_bar[:, 0, :]
            p2_tilde[:, 0, :] = p1_bar[:, 0, :]
        else:
            p0_tilde = p0 - p0_bar + p1_bar
            p2_tilde = p2 - p2_bar + p1_bar

        # Currently only implemented up to  P2
        if solver.order > 2:
            raise NotImplementedError

        # Allocate weno_wts
        weno_wts = np.zeros([Uc.shape[0], 3, ns])

        # Calculate non-linear weights
        weno_wts[:, 0, :] = self.get_nonlinearwts(solver.order, p0_tilde,
                                                  0.001, basis_phys_grads,
                                                  quad_wts, vols, djacM)
        weno_wts[:, 1, :] = self.get_nonlinearwts(solver.order, p1, 0.998,
                                                  basis_phys_grads, quad_wts,
                                                  vols, djacs)
        weno_wts[:, 2, :] = self.get_nonlinearwts(solver.order, p2_tilde,
                                                  0.001, basis_phys_grads,
                                                  quad_wts, vols, djacP)
        # Normalize the weights
        normal_wts = weno_wts / np.sum(weno_wts, axis=1).reshape(
            [Uc.shape[0], 1, ns])

        # Update state_coeffs for indicated elements
        Vc[shock_indicated] = \
          np.einsum('ik, ijk -> ijk', normal_wts[shock_indicated, 0],
          p0_tilde[shock_indicated]) + \
          np.einsum('ik, ijk -> ijk', normal_wts[shock_indicated, 1],
          p1[shock_indicated]) + \
          np.einsum('ik, ijk -> ijk', normal_wts[shock_indicated, 2],
          p2_tilde[shock_indicated])

        # Transform characteristic variables back to physical.
        Uc[shock_indicated] = np.einsum('ebij, elj -> eli',
                                        self.right_eigen[shock_indicated],
                                        Vc[shock_indicated])

        return Uc  # [ne, nq, ns]
Esempio n. 30
0
    def source_coefficients(self, dt, order, basis, Up):
        '''
		Calculates the polynomial coefficients for the source functions in
		ADER-DG

		Inputs:
		-------
			elem_ID: element index
			dt: time step size
			order: solution order
			basis: basis object
			Up: coefficients of predicted solution [ne, nb_st, ns]

		Outputs:
		--------
			S: polynomical coefficients of the flux function [ne, nb_st, ns]
		'''
        # Unpack
        mesh = self.mesh
        ndims = mesh.ndims
        physics = self.physics
        ns = physics.NUM_STATE_VARS
        params = self.params

        elem_helpers = self.elem_helpers
        elem_helpers_st = self.elem_helpers_st
        djac_elems = elem_helpers.djac_elems
        x_elems = elem_helpers.x_elems

        nq_t = self.elem_helpers_st.nq_tile_constant

        ader_helpers = self.ader_helpers
        x_elems_ader = ader_helpers.x_elems
        InterpolateFluxADER = params["InterpolateFluxADER"]
        if InterpolateFluxADER:

            xnodes = basis.get_nodes(order)
            nb = xnodes.shape[0]

            # Get array in physical time from ref time
            t, elem_helpers_st.basis_time = solver_tools.ref_to_phys_time(
                mesh, self.time, self.stepper.dt, xnodes[:, -1:],
                elem_helpers_st.basis_time)

            # Evaluate the source term at the quadrature points
            Sq = np.zeros([Up.shape[0], t.shape[0], ns])
            S = np.zeros_like(Sq)
            Sq = physics.eval_source_terms(Up, x_elems_ader, t, Sq)

            # Interpolate source coefficient to nodes
            dg_tools.interpolate_to_nodes(Sq, S)
        else:
            # Unpack for L2-projection
            ader_helpers = self.ader_helpers
            basis_val_st = elem_helpers_st.basis_val
            nb_st = basis_val_st.shape[1]
            quad_wts_st = elem_helpers_st.quad_wts
            quad_wts = elem_helpers.quad_wts
            quad_pts_st = elem_helpers_st.quad_pts
            nq_st = quad_wts_st.shape[0]
            nq = quad_wts.shape[0]
            iMM_elems = ader_helpers.iMM_elems

            # Interpolate state at quadrature points
            Uq = helpers.evaluate_state(Up, basis_val_st)
            x_elems_st = np.tile(x_elems, [1, nq_t, 1])
            # Get array in physical time from ref time
            t = np.zeros([nq_st, ndims])
            t, elem_helpers_st.basis_time = solver_tools.ref_to_phys_time(
                mesh, self.time, self.stepper.dt, quad_pts_st[:, -1:],
                elem_helpers_st.basis_time)

            # Evaluate the source term at the quadrature points
            Sq = np.zeros_like(Uq)
            S = np.zeros([Uq.shape[0], nb_st, ns])
            Sq = physics.eval_source_terms(Uq, x_elems_st, t, Sq)
            # [ne, nq, ns, ndims]

            # Project Sq to the space-time basis coefficients
            solver_tools.L2_projection(mesh, iMM_elems, basis, quad_pts_st,
                                       quad_wts_st,
                                       np.tile(djac_elems, (nq_t, 1)), Sq, S)

        return S * dt / 2.0  # [ne, nb_st, ns]