Example #1
0
def continuity_form(state, test, q, ibp=IntegrateByParts.ONCE, outflow=False):

    if outflow and ibp == IntegrateByParts.NEVER:
        raise ValueError(
            "outflow is True and ibp is None are incompatible options")
    Vu = state.spaces("HDiv")
    dS_ = (dS_v + dS_h) if Vu.extruded else dS
    ubar = Function(Vu)

    if ibp == IntegrateByParts.ONCE:
        L = -inner(grad(test), outer(q, ubar)) * dx
    else:
        L = inner(test, div(outer(q, ubar))) * dx

    if ibp != IntegrateByParts.NEVER:
        n = FacetNormal(state.mesh)
        un = 0.5 * (dot(ubar, n) + abs(dot(ubar, n)))

        L += dot(jump(test), (un('+') * q('+') - un('-') * q('-'))) * dS_

        if ibp == IntegrateByParts.TWICE:
            L -= (inner(test('+'),
                        dot(ubar('+'), n('+')) * q('+')) +
                  inner(test('-'),
                        dot(ubar('-'), n('-')) * q('-'))) * dS_

    if outflow:
        n = FacetNormal(state.mesh)
        un = 0.5 * (dot(ubar, n) + abs(dot(ubar, n)))
        L += test * un * q * (ds_v + ds_t + ds_b)

    form = transporting_velocity(L, ubar)

    return ibp_label(transport(form, TransportEquationType.conservative), ibp)
Example #2
0
        def get_flux_form(dS, M):

            fluxes = (-inner(2 * avg(outer(phi, n)), avg(grad(gamma) * M)) -
                      inner(avg(grad(phi) * M), 2 * avg(outer(gamma, n))) +
                      mu * inner(2 * avg(outer(phi, n)),
                                 2 * avg(outer(gamma, n) * kappa))) * dS
            return fluxes
Example #3
0
    def get_flux_form(dS, M):

        fluxes = (
            -inner(2*avg(outer(q, n)), avg(grad(test)*M))
            - inner(avg(grad(q)*M), 2*avg(outer(test, n)))
            + mu*inner(2*avg(outer(q, n)), 2*avg(outer(test, n)*kappa))
        )*dS
        return fluxes
Example #4
0
def horizontal_strain(u, s, h):
    r"""Calculate the horizontal strain rate with corrections for terrain-
    following coordinates"""
    ζ = firedrake.SpatialCoordinate(u.ufl_domain())[2]
    b = s - h
    v = -((1 - ζ) * grad_2(b) + ζ * grad_2(s)) / h
    du_dζ = u.dx(2)
    return sym(grad_2(u)) + 0.5 * (outer(du_dζ, v) + outer(v, du_dζ))
Example #5
0
def horizontal_strain_rate(**kwargs):
    r"""Calculate the horizontal strain rate with corrections for terrain-
    following coordinates"""
    u, h, s = itemgetter("velocity", "surface", "thickness")(kwargs)
    mesh = u.ufl_domain()
    dim = mesh.geometric_dimension()
    ζ = firedrake.SpatialCoordinate(mesh)[dim - 1]
    b = s - h
    v = -((1 - ζ) * grad(b) + ζ * grad(s)) / h
    du_dζ = u.dx(dim - 1)
    return sym_grad(u) + 0.5 * (outer(du_dζ, v) + outer(v, du_dζ))
Example #6
0
def _boundary_flux(z, h_ext, q_ext, g, boundary_ids):
    Z = z.function_space()
    n = firedrake.FacetNormal(Z.mesh())
    φ, v = firedrake.TestFunctions(Z)

    F_hx, F_qx = _fluxes(h_ext, q_ext, g)

    h, q = firedrake.split(z)
    F_h, F_q = _fluxes(h, q, g)

    return 0.5 * (inner(F_hx, φ * n) + inner(F_qx, outer(v, n)) + inner(
        F_h, φ * n) + inner(F_q, outer(v, n))) * ds(boundary_ids)
Example #7
0
def tensor_jump(v, n):
    r"""
    Jump term for vector functions based on the tensor product

    .. math::
        \text{jump}(\mathbf{u}, \mathbf{n}) = (\mathbf{u}^+ \mathbf{n}^+) +
        (\mathbf{u}^- \mathbf{n}^-)

    This is the discrete equivalent of grad(u) as opposed to the
    vectorial UFL jump operator :meth:`ufl.jump` which represents div(u).
    The equivalent of nabla_grad(u) is given by tensor_jump(n, u).
    """
    return outer(v('+'), n('+')) + outer(v('-'), n('-'))
Example #8
0
    def residual(self, test, trial, trial_lagged, fields, bcs):
        u_adv = trial_lagged
        phi = test
        n = self.n
        u = trial

        F = -dot(u, div(outer(phi, u_adv)))*self.dx

        for id, bc in bcs.items():
            if 'u' in bc:
                u_in = bc['u']
            elif 'un' in bc:
                u_in = bc['un'] * n  # this implies u_t=0 on the inflow
            else:
                u_in = zero(self.dim)
            F += conditional(dot(u_adv, n) < 0,
                             dot(phi, u_in)*dot(u_adv, n),
                             dot(phi, u)*dot(u_adv, n)) * self.ds(id)

        if not (is_continuous(self.trial_space) and normal_is_continuous(u_adv)):
            # s=0: u.n(-)<0  =>  flow goes from '+' to '-' => '+' is upwind
            # s=1: u.n(-)>0  =>  flow goes from '-' to '+' => '-' is upwind
            s = 0.5*(sign(dot(avg(u), n('-'))) + 1.0)
            u_up = u('-')*s + u('+')*(1-s)
            F += dot(u_up, (dot(u_adv('+'), n('+'))*phi('+') + dot(u_adv('-'), n('-'))*phi('-'))) * self.dS

        return -F
Example #9
0
def _fluxes(h, q, g):
    r"""Calculate the flux of mass and momentum for the shallow water
    equations"""
    I = firedrake.Identity(2)
    F_h = q
    F_q = outer(q, q) / h + 0.5 * g * h**2 * I
    return F_h, F_q
Example #10
0
    def advection_term(self, q):

        if self.continuity:
            if self.ibp == IntegrateByParts.ONCE:
                L = -inner(grad(self.test), outer(q, self.ubar)) * dx
            else:
                L = inner(self.test, div(outer(q, self.ubar))) * dx
        else:
            if self.ibp == IntegrateByParts.ONCE:
                L = -inner(div(outer(self.test, self.ubar)), q) * dx
            else:
                L = inner(outer(self.test, self.ubar), grad(q)) * dx

        if self.dS is not None and self.ibp != IntegrateByParts.NEVER:
            n = FacetNormal(self.state.mesh)
            un = 0.5 * (dot(self.ubar, n) + abs(dot(self.ubar, n)))

            L += dot(jump(self.test),
                     (un('+') * q('+') - un('-') * q('-'))) * self.dS

            if self.ibp == IntegrateByParts.TWICE:
                L -= (inner(self.test('+'),
                            dot(self.ubar('+'), n('+')) * q('+')) +
                      inner(self.test('-'),
                            dot(self.ubar('-'), n('-')) * q('-'))) * self.dS

        if self.outflow:
            n = FacetNormal(self.state.mesh)
            un = 0.5 * (dot(self.ubar, n) + abs(dot(self.ubar, n)))
            L += self.test * un * q * (ds_v + ds_t + ds_b)

        if self.vector_manifold:
            n = FacetNormal(self.state.mesh)
            w = self.test
            dS = self.dS
            u = q
            L += un('+') * inner(w('-'),
                                 n('+') + n('-')) * inner(u('+'), n('+')) * dS
            L += un('-') * inner(w('+'),
                                 n('+') + n('-')) * inner(u('-'), n('-')) * dS
        return L
Example #11
0
def central_facet_flux(F, v):
    r"""Create the weak form of the central numerical flux through cell facets

    This numerical flux, by itself, is unstable. The full right-hand side of
    the problem requires an additional facet flux term for stability.

    Parameters
    ----------
    F : ufl.Expr
        A symbolic expression for the flux
    v : firedrake.TestFunction
        A test function from the state space

    Returns
    -------
    f : firedrake.Form
        A 1-form that discretizes the residual of the flux
    """
    mesh = v.ufl_domain()
    n = FacetNormal(mesh)
    return inner(avg(F), outer(v('+'), n('+')) + outer(v('-'), n('-'))) * dS
Example #12
0
    def __init__(self, state, V, continuity=False):

        super(DGAdvection, self).__init__(state)

        element = V.fiat_element
        assert element.entity_dofs() == element.entity_closure_dofs(), "Provided space is not discontinuous"
        dt = state.timestepping.dt

        if V.extruded:
            surface_measure = (dS_h + dS_v)
        else:
            surface_measure = dS

        phi = TestFunction(V)
        D = TrialFunction(V)
        self.D1 = Function(V)
        self.dD = Function(V)

        n = FacetNormal(state.mesh)
        # ( dot(v, n) + |dot(v, n)| )/2.0
        un = 0.5*(dot(self.ubar, n) + abs(dot(self.ubar, n)))

        a_mass = inner(phi,D)*dx

        if continuity:
            a_int = -inner(grad(phi), outer(D, self.ubar))*dx
        else:
            a_int = -inner(div(outer(phi,self.ubar)),D)*dx

        a_flux = (dot(jump(phi), un('+')*D('+') - un('-')*D('-')))*surface_measure
        arhs = a_mass - dt*(a_int + a_flux)

        DGproblem = LinearVariationalProblem(a_mass, action(arhs,self.D1),
                                             self.dD)
        self.DGsolver = LinearVariationalSolver(DGproblem,
                                                solver_parameters={
                                                    'ksp_type':'preonly',
                                                    'pc_type':'bjacobi',
                                                    'sub_pc_type': 'ilu'},
                                                options_prefix='DGAdvection')
Example #13
0
    def advection_term(self, q):

        if self.continuity:
            if self.ibp == "once":
                L = -inner(grad(self.test), outer(q, self.ubar)) * dx
            else:
                L = inner(self.test, div(outer(q, self.ubar))) * dx
        else:
            if self.ibp == "once":
                L = -inner(div(outer(self.test, self.ubar)), q) * dx
            else:
                L = inner(outer(self.test, self.ubar), grad(q)) * dx

        if self.dS is not None and self.ibp is not None:
            L += dot(jump(self.test),
                     (self.un('+') * q('+') - self.un('-') * q('-'))) * self.dS
            if self.ibp == "twice":
                L -= (
                    inner(self.test('+'),
                          dot(self.ubar('+'), self.n('+')) * q('+')) +
                    inner(self.test('-'),
                          dot(self.ubar('-'), self.n('-')) * q('-'))) * self.dS

        if self.outflow:
            L += self.test * self.un * q * self.ds

        if self.vector_manifold:
            un = self.un
            w = self.test
            u = q
            n = self.n
            dS = self.dS
            L += un('+') * inner(w('-'),
                                 n('+') + n('-')) * inner(u('+'), n('+')) * dS
            L += un('-') * inner(w('+'),
                                 n('+') + n('-')) * inner(u('-'), n('-')) * dS
        return L
Example #14
0
        def get_flux_form(dS, M):

            fluxes = (-inner(2*avg(outer(phi, n)), avg(grad(gamma)*M))
                      - inner(avg(grad(phi)*M), 2*avg(outer(gamma, n)))
                      + mu*inner(2*avg(outer(phi, n)), 2*avg(outer(gamma, n)*kappa)))*dS
            return fluxes
Example #15
0
 def surf_grad(u):
     return fd.sym(fd.grad(u) - fd.outer(fd.grad(u) * n, n))
Example #16
0
def central_inflow_flux(F_in, v, boundary_ids):
    r"""Create the weak form of the central numerical flux through the domain
    boundary"""
    mesh = v.ufl_domain()
    n = FacetNormal(mesh)
    return inner(F_in, outer(v, n)) * ds(boundary_ids)
Example #17
0
    def residual(self, test, trial, trial_lagged, fields, bcs):

        if 'background_viscosity' in fields:
            assert('grid_resolution' in fields)
            mu_background = fields['background_viscosity']
            grid_dx = fields['grid_resolution'][0]
            grid_dz = fields['grid_resolution'][1]
            mu_h = 0.5*abs(trial[0]) * grid_dx + mu_background
            mu_v = 0.5*abs(trial[1]) * grid_dz + mu_background
            print("use redx viscosity")
            diff_tensor = as_tensor([[mu_h, 0], [0, mu_v]])

        else:
            mu = fields['viscosity']
            if len(mu.ufl_shape) == 2:
                diff_tensor = mu
            else:
                diff_tensor = mu * Identity(self.dim)
        phi = test
        n = self.n
        u = trial
        u_lagged = trial_lagged

        grad_test = nabla_grad(phi)
        stress = dot(diff_tensor, nabla_grad(u))
        if self.symmetric_stress:
            stress += dot(diff_tensor, grad(u))

        F = 0
        F += inner(grad_test, stress)*self.dx

        # Interior Penalty method
        #
        # see https://www.researchgate.net/publication/260085826 for details
        # on the choice of sigma

        degree = self.trial_space.ufl_element().degree()
        if not isinstance(degree, int):
            degree = max(degree[0], degree[1])
        # safety factor: 1.0 is theoretical minimum
        alpha = fields.get('interior_penalty', 2.0)
        if degree == 0:
            # probably only works for orthog. quads and hexes
            sigma = 1.0
        else:
            nf = self.mesh.ufl_cell().num_facets()
            family = self.trial_space.ufl_element().family()
            if family in ['DQ', 'TensorProductElement', 'EnrichedElement']:
                degree_gradient = degree
            else:
                degree_gradient = degree - 1
            sigma = alpha * cell_edge_integral_ratio(self.mesh, degree_gradient) * nf
        # we use (3.23) + (3.20) from https://www.researchgate.net/publication/260085826
        # instead of maximum over two adjacent cells + and -, we just sum (which is 2*avg())
        # and the for internal facets we have an extra 0.5:
        # WEIRDNESS: avg(1/CellVolume(mesh)) crashes TSFC - whereas it works in scalar diffusion! - instead just writing out explicitly
        sigma *= FacetArea(self.mesh)*(1/CellVolume(self.mesh)('-') + 1/CellVolume(self.mesh)('+'))/2

        if not is_continuous(self.trial_space):
            u_tensor_jump = tensor_jump(n, u)
            if self.symmetric_stress:
                u_tensor_jump += transpose(u_tensor_jump)
            F += sigma*inner(tensor_jump(n, phi), dot(avg(diff_tensor), u_tensor_jump))*self.dS
            F += -inner(avg(dot(diff_tensor, nabla_grad(phi))), u_tensor_jump)*self.dS
            F += -inner(tensor_jump(n, phi), avg(stress))*self.dS

        for id, bc in bcs.items():
            if 'u' in bc or 'un' in bc:
                if 'u' in bc:
                    u_tensor_jump = outer(n, u-bc['u'])
                else:
                    u_tensor_jump = outer(n, n)*(dot(n, u)-bc['un'])
                if self.symmetric_stress:
                    u_tensor_jump += transpose(u_tensor_jump)
                # this corresponds to the same 3 terms as the dS integrals for DG above:
                F += 2*sigma*inner(outer(n, phi), dot(diff_tensor, u_tensor_jump))*self.ds(id)
                F += -inner(dot(diff_tensor, nabla_grad(phi)), u_tensor_jump)*self.ds(id)
                if 'u' in bc:
                    F += -inner(outer(n, phi), stress) * self.ds(id)
                elif 'un' in bc:
                    # we only keep, the normal part of stress, the tangential
                    # part is assumed to be zero stress (i.e. free slip), or prescribed via 'stress'
                    F += -dot(n, phi)*dot(n, dot(stress, n)) * self.ds(id)
            if 'stress' in bc:  # a momentum flux, a.k.a. "force"
                # here we need only the third term, because we assume jump_u=0 (u_ext=u)
                # the provided stress = n.(mu.stress_tensor)
                F += dot(-phi, bc['stress']) * self.ds(id)
            if 'drag' in bc:  # (bottom) drag of the form tau = -C_D u |u|
                C_D = bc['drag']
                if 'coriolis_frequency' in fields and self.dim == 2:
                    assert 'u_velocity' in fields
                    u_vel_component = fields['u_velocity']
                    unorm = pow(dot(u_lagged, u_lagged) + pow(u_vel_component, 2) + 1e-6, 0.5)
                else:
                    unorm = pow(dot(u_lagged, u_lagged) + 1e-6, 0.5)

                F += dot(-phi, -C_D*unorm*u) * self.ds(id)

            # NOTE 1: unspecified boundaries are equivalent to free stress (i.e. free in all directions)
            # NOTE 2: 'un' can be combined with 'stress' provided the stress force is tangential (e.g. no-normal flow with wind)

            if 'u' in bc and 'stress' in bc:
                raise ValueError("Cannot apply both 'u' and 'stress' bc on same boundary")
            if 'u' in bc and 'drag' in bc:
                raise ValueError("Cannot apply both 'u' and 'drag' bc on same boundary")
            if 'u' in bc and 'un' in bc:
                raise ValueError("Cannot apply both 'u' and 'un' bc on same boundary")

        return -F
Example #18
0
    def __init__(self,
                 mesh: object,
                 kappa: float,
                 comm_space: MPI.Comm,
                 mu: float = 5.,
                 *args,
                 **kwargs):
        """
        Constructor

        :param mesh: spatial domain
        :param kappa: diffusion coefficient
        :param mu: penalty weighting function
        """
        super(Diffusion2D, self).__init__(*args, **kwargs)

        # Spatial domain and function space
        self.mesh = mesh
        V = FunctionSpace(self.mesh, "DG", 1)
        self.function_space = V
        self.comm_space = comm_space

        # Placeholder for time step - will be updated in the update method
        self.dt = Constant(0.)

        # Things we need for the form
        gamma = TestFunction(V)
        phi = TrialFunction(V)
        self.f = Function(V)
        n = FacetNormal(mesh)

        # Set up the rhs and bilinear form of the equation
        a = (inner(gamma, phi) * dx + self.dt *
             (inner(grad(gamma),
                    grad(phi) * kappa) * dx -
              inner(2 * avg(outer(phi, n)), avg(grad(gamma) * kappa)) * dS -
              inner(avg(grad(phi) * kappa), 2 * avg(outer(gamma, n))) * dS +
              mu * inner(2 * avg(outer(phi, n)),
                         2 * avg(outer(gamma, n) * kappa)) * dS))
        rhs = inner(gamma, self.f) * dx

        # Function to hold the solution
        self.soln = Function(V)

        # Setup problem and solver
        prob = LinearVariationalProblem(a, rhs, self.soln)
        self.solver = NonlinearVariationalSolver(prob)

        # Set the data structure for any user-defined time point
        self.vector_template = VectorDiffusion2D(size=len(self.function_space),
                                                 comm_space=self.comm_space)

        # Set initial condition:
        # Setting up a Gaussian blob in the centre of the domain.
        self.vector_t_start = VectorDiffusion2D(size=len(self.function_space),
                                                comm_space=self.comm_space)
        x = SpatialCoordinate(self.mesh)
        initial_tracer = exp(-((x[0] - 5)**2 + (x[1] - 5)**2))
        tmp = Function(self.function_space)
        tmp.interpolate(initial_tracer)
        self.vector_t_start.set_values(np.copy(tmp.dat.data))
    fd.inner(f, v)*fd.dx + idt*c_t*p0*q*fd.dx

# -------------------------------------------------------------------------
# transport
# coefficients
K = fd.Constant(K)
Dt = fd.Constant(dt)
c_mid = 0.5 * (c + c0)  # Crank-Nicolson timestepping

# advective velocity
vel = fd.Function(V, name='velocity')
vnorm = fd.sqrt(fd.dot(vel, vel))

# Diffusion tensor
Diff = fd.Identity(mesh.geometric_dimension())*(Dm + alphaT*vnorm) + \
    (alphaL-alphaT)*fd.outer(vel, vel)/vnorm

# source term
fc = fd.Constant(s)

# weak form (transport)
F1 = w * (c - c0) * fd.dx + Dt * (w * fd.dot(vel, fd.grad(c_mid)) + fd.dot(
    fd.grad(w), Diff * fd.grad(c_mid)) + w * K * c_mid -
                                  fc * w) * fd.dx  # - Dt*h_n*w*fd.ds(outlet)

# strong form
R = (c - c0) + Dt * (fd.dot(vel, fd.grad(c_mid)) -
                     fd.div(Diff * fd.grad(c_mid)) + K * c_mid - fc)

# *** Adding SUPG stabilizing and shock cap. terms ***
# SUPG stabilisation parameters
Example #20
0
dt = np.sqrt(tol)
dtc = fd.Constant(dt)
n = fd.FacetNormal(mesh)
h = fd.sqrt(2) * fd.CellVolume(mesh) / fd.CellDiameter(mesh)

# advective velocity
velocity = fd.Function(vDG1)
velocity.interpolate(fd.Constant(v_inlet))
vnorm = fd.sqrt(fd.dot(velocity, velocity))

# Diffusion tensor
if d_t < 0:
    Diff = fd.Identity(mesh.geometric_dimension()) * Dm
else:
    Diff = fd.Identity(mesh.geometric_dimension())*(Dm + d_t*vnorm) + \
        fd.Constant(d_l-d_t)*fd.outer(velocity, velocity)/vnorm

# upwind ter
# vn = 0.5*(fd.dot(velocity, n) + abs(fd.dot(velocity, n)))
# cupw = fd.conditional(vn < 0, ch, lmbd_h)

# stability
tau_d = fd.Constant(max([Dm, tau_e])) / h
tau_a = abs(fd.dot(velocity, n))
# tau_a = vn

# numerical flux
chat = lmbd_h
###########################################
qhat = qh + tau_d * (ch - chat) * n + velocity * chat + tau_a * (ch - chat) * n
# qhat = qh + tau*(ch - chat)*n + velocity * chat + tau_a*(ch - chat)*n
# 3.3) set boundary conditions
t_bc = fd.DirichletBC(X, cIn, inlet)


# ======================
# 3.4) Variational Form
# coefficients
# advective velocity
vel = fd.Function(V, name='velocity')
vel.interpolate(v_inlet)
vnorm = fd.sqrt(fd.dot(vel, vel))


# Diffusion tensor
Diff = fd.Identity(mesh.geometric_dimension())*(Dm + d_t*vnorm) + \
    fd.Constant(d_l-d_t)*fd.outer(vel, vel)/vnorm
Diff = fd.Identity(mesh.geometric_dimension())*Dm

Dt = fd.Constant(dt)
K = fd.Constant(K)
c_mid = 0.5 * (c + c0)  # Crank-Nicolson timestepping
fc = fd.Constant(s)

# weak form (transport)
F = w*(c - c0)*fd.dx + Dt*(w*fd.dot(vel, fd.grad(c_mid))
                           + fd.dot(fd.grad(w),
                                    Diff*fd.grad(c_mid)) + w*K*c_mid
                           - fc*w)*fd.dx  # - Dt*h_n*w*fd.ds(outlet)

# strong form
R = (c - c0) + Dt*(fd.dot(vel, fd.grad(c_mid)) -