Example #1
0
def delta_dg(mesh, expr):
    V = df.FunctionSpace(mesh, "DG", 0)
    m = df.interpolate(expr, V)

    n = df.FacetNormal(mesh)
    h = df.CellSize(mesh)
    h_avg = (h('+') + h('-')) / 2

    alpha = 1.0
    gamma = 0.0

    u = df.TrialFunction(V)
    v = df.TestFunction(V)

    # for DG 0 case, only term contain alpha is nonzero
    a = df.dot(df.grad(v), df.grad(u))*df.dx \
        - df.dot(df.avg(df.grad(v)), df.jump(u, n))*df.dS \
        - df.dot(df.jump(v, n), df.avg(df.grad(u)))*df.dS \
        + alpha/h_avg*df.dot(df.jump(v, n), df.jump(u, n))*df.dS \
        - df.dot(df.grad(v), u*n)*df.ds \
        - df.dot(v*n, df.grad(u))*df.ds \
        + gamma/h*v*u*df.ds

    K = df.assemble(a).array()
    L = df.assemble(v * df.dx).array()

    h = -np.dot(K, m.vector().array()) / (L)

    xs = []
    for cell in df.cells(mesh):
        xs.append(cell.midpoint().x())

    print len(xs), len(h)
    return xs, h
Example #2
0
def assemble_1d(mesh):
    DG = df.FunctionSpace(mesh, "DG", 0)
    n = df.FacetNormal(mesh)
    h = df.CellSize(mesh)
    h_avg = (h('+') + h('-')) / 2

    u = df.TrialFunction(DG)
    v = df.TestFunction(DG)

    a = 1.0 / h_avg * df.dot(df.jump(v, n), df.jump(u, n)) * df.dS

    K = df.assemble(a)
    L = df.assemble(v * df.dx).array()

    return copy_petsc_to_csr(K), L
Example #3
0
    def _setup_divergence(self, vel, method, name='u'):
        """
        Calculate divergence and element to element velocity
        flux differences on the same edges
        """
        V = df.FunctionSpace(self.mesh, 'DG', 0)
        n = df.FacetNormal(self.mesh)
        u, v = df.TrialFunction(V), df.TestFunction(V)

        # The difference between the flux on the same facet between two different cells
        a1 = u * v * dx
        w = dot(vel('+') - vel('-'), n('+'))
        if method == 'div0':
            L1 = w * avg(v) * dS
        else:
            L1 = abs(w) * avg(v) * dS

        # The divergence internally in the cell
        a2 = u * v * dx
        if method in ('div', 'div0'):
            L2 = abs(df.div(vel)) * v * dx
        elif method == 'gradq_avg':
            L2 = dot(avg(vel), n('+')) * jump(v) * dS - dot(vel, grad(v)) * dx
        else:
            raise ValueError('Divergence type %r not supported' % method)

        # Store for usage in projection
        storage = self._div[name] = {}
        storage['dS_solver'] = df.LocalSolver(a1, L1)
        storage['dx_solver'] = df.LocalSolver(a2, L2)
        storage['div_dS'] = df.Function(V)
        storage['div_dx'] = df.Function(V)

        # Pre-factorize matrices
        storage['dS_solver'].factorize()
        storage['dx_solver'].factorize()
        storage['div_dS'].rename('Divergence_%s_dS' % name,
                                 'Divergence_%s_dS' % name)
        storage['div_dx'].rename('Divergence_%s_dx' % name,
                                 'Divergence_%s_dx' % name)
Example #4
0
 def adaptive(self, mesh, eigv, eigf):
     """Refine mesh based on residual errors."""
     fraction = 0.1
     C = FunctionSpace(mesh, "DG", 0)  # constants on triangles
     w = TestFunction(C)
     h = CellSize(mesh)
     n = FacetNormal(mesh)
     marker = CellFunction("bool", mesh)
     print len(marker)
     indicators = np.zeros(len(marker))
     for e, u in zip(eigv, eigf):
         errform = avg(h) * jump(grad(u), n) ** 2 * avg(w) * dS \
             + h * (inner(grad(u), n) - Constant(e) * u) ** 2 * w * ds
         if self.degree > 1:
             errform += h**2 * div(grad(u))**2 * w * dx
         indicators[:] += assemble(errform).array()  # errors for each cell
     print "Residual error: ", sqrt(sum(indicators) / len(eigv))
     cutoff = sorted(indicators,
                     reverse=True)[int(len(indicators) * fraction) - 1]
     marker.array()[:] = indicators > cutoff  # mark worst errors
     mesh = refine(mesh, marker)
     return mesh
Example #5
0
 def adaptive(self, mesh, eigv, eigf):
     """Refine mesh based on residual errors."""
     fraction = 0.1
     C = FunctionSpace(mesh, "DG", 0)  # constants on triangles
     w = TestFunction(C)
     h = CellSize(mesh)
     n = FacetNormal(mesh)
     marker = CellFunction("bool", mesh)
     print len(marker)
     indicators = np.zeros(len(marker))
     for e, u in zip(eigv, eigf):
         errform = avg(h) * jump(grad(u), n) ** 2 * avg(w) * dS \
             + h * (inner(grad(u), n) - Constant(e) * u) ** 2 * w * ds
         if self.degree > 1:
             errform += h ** 2 * div(grad(u)) ** 2 * w * dx
         indicators[:] += assemble(errform).array()  # errors for each cell
     print "Residual error: ", sqrt(sum(indicators) / len(eigv))
     cutoff = sorted(
         indicators, reverse=True)[
         int(len(indicators) * fraction) - 1]
     marker.array()[:] = indicators > cutoff  # mark worst errors
     mesh = refine(mesh, marker)
     return mesh
Example #6
0
    def solve(self):
        """
        Solves the stokes equation with the current multimesh
        """
        (u, p) = TrialFunctions(self.VQ)
        (v, q) = TestFunctions(self.VQ)
        n = FacetNormal(self.multimesh)
        h = 2.0 * Circumradius(self.multimesh)
        alpha = Constant(6.0)

        tensor_jump = lambda u: outer(u("+"), n("+")) + outer(u("-"), n("-"))

        a_s = inner(grad(u), grad(v)) * dX
        a_IP = - inner(avg(grad(u)), tensor_jump(v))*dI\
               - inner(avg(grad(v)), tensor_jump(u))*dI\
               + alpha/avg(h) * inner(jump(u), jump(v))*dI
        a_O = inner(jump(grad(u)), jump(grad(v))) * dO

        b_s = -div(u) * q * dX - div(v) * p * dX
        b_IP = jump(u, n) * avg(q) * dI + jump(v, n) * avg(p) * dI
        l_s = inner(self.f, v) * dX

        s_C = h*h*inner(-div(grad(u)) + grad(p), -div(grad(v)) - grad(q))*dC\
              + h("+")*h("+")*inner(-div(grad(u("+"))) + grad(p("+")),
                                    -div(grad(v("+"))) + grad(q("+")))*dO
        l_C = h*h*inner(self.f, -div(grad(v)) - grad(q))*dC\
              + h("+")*h("+")*inner(self.f("+"),
                                    -div(grad(v("+"))) - grad(q("+")))*dO

        a = a_s + a_IP + a_O + b_s + b_IP + s_C
        l = l_s + l_C

        A = assemble_multimesh(a)
        L = assemble_multimesh(l)
        [bc.apply(A, L) for bc in self.bcs]
        self.VQ.lock_inactive_dofs(A, L)
        solve(A, self.w.vector(), L, "mumps")
        self.splitMMF()
Example #7
0
    def define_pressure_equation(self):
        """
        Setup the pressure Poisson equation

        This implementation assembles the full LHS and RHS each time they are needed
        """
        sim = self.simulation
        Vp = sim.data['Vp']
        p_star = sim.data['p']
        u_star = sim.data['u']

        # Trial and test functions
        p = dolfin.TrialFunction(Vp)
        q = dolfin.TestFunction(Vp)

        c1 = sim.data['time_coeffs'][0]
        dt = sim.data['dt']
        mesh = sim.data['mesh']
        n = dolfin.FacetNormal(mesh)

        # Fluid properties
        mpm = sim.multi_phase_model
        mu = mpm.get_laminar_dynamic_viscosity(0)
        rho = sim.data['rho']

        # Lagrange multiplicator to remove the pressure null space
        # ∫ p dx = 0
        assert not self.use_lagrange_multiplicator, 'NOT IMPLEMENTED YET'

        # Weak form of the Poisson eq. with discontinuous elements
        # -∇⋅∇p = - γ_1/Δt ρ ∇⋅u^*
        K = 1.0 / rho
        a = K * dot(grad(p), grad(q)) * dx
        L = K * dot(grad(p_star), grad(q)) * dx

        # RHS, ∇⋅u^*
        if self.incompressibility_flux_type == 'central':
            u_flux = avg(u_star)
        elif self.incompressibility_flux_type == 'upwind':
            switch = dolfin.conditional(
                dolfin.gt(abs(dot(u_star, n))('+'), 0.0), 1.0, 0.0
            )
            u_flux = switch * u_star('+') + (1 - switch) * u_star('-')
        L += c1 / dt * dot(u_star, grad(q)) * dx
        L -= c1 / dt * dot(u_flux, n('+')) * jump(q) * dS

        # Symmetric Interior Penalty method for -∇⋅∇p
        a -= dot(n('+'), avg(K * grad(p))) * jump(q) * dS
        a -= dot(n('+'), avg(K * grad(q))) * jump(p) * dS

        # Symmetric Interior Penalty method for -∇⋅∇p^*
        L -= dot(n('+'), avg(K * grad(p_star))) * jump(q) * dS
        L -= dot(n('+'), avg(K * grad(q))) * jump(p_star) * dS

        # Weak continuity
        penalty_dS, penalty_ds = self.calculate_penalties()

        # Symmetric Interior Penalty coercivity term
        a += penalty_dS * jump(p) * jump(q) * dS
        # L += penalty_dS*jump(p_star)*jump(q)*dS

        # Collect Dirichlet and outlet boundary values
        dirichlet_vals_and_ds = []
        for dbc in sim.data['dirichlet_bcs'].get('p', []):
            dirichlet_vals_and_ds.append((dbc.func(), dbc.ds()))
        for obc in sim.data['outlet_bcs']:
            p_ = mu * dot(dot(grad(u_star), n), n)
            dirichlet_vals_and_ds.append((p_, obc.ds()))

        # Apply Dirichlet boundary conditions
        for p_bc, dds in dirichlet_vals_and_ds:
            # SIPG for -∇⋅∇p
            a -= dot(n, K * grad(p)) * q * dds
            a -= dot(n, K * grad(q)) * p * dds
            L -= dot(n, K * grad(q)) * p_bc * dds

            # SIPG for -∇⋅∇p^*
            L -= dot(n, K * grad(p_star)) * q * dds
            L -= dot(n, K * grad(q)) * p_star * dds

            # Weak Dirichlet
            a += penalty_ds * p * q * dds
            L += penalty_ds * p_bc * q * dds

            # Weak Dirichlet for p^*
            # L += penalty_ds*p_star*q*dds
            # L -= penalty_ds*p_bc*q*dds

        # Neumann boundary conditions
        neumann_bcs = sim.data['neumann_bcs'].get('p', [])
        for nbc in neumann_bcs:
            # Neumann boundary conditions on p and p_star cancel
            # L += (nbc.func() - dot(n, grad(p_star)))*q*nbc.ds()
            pass

        # Use boundary conditions for the velocity for the
        # term from integration by parts of div(u_star)
        for d in range(sim.ndim):
            dirichlet_bcs = sim.data['dirichlet_bcs'].get('u%d' % d, [])
            neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, [])
            for dbc in dirichlet_bcs:
                u_bc = dbc.func()
                L -= c1 / dt * u_bc * n[d] * q * dbc.ds()
            for nbc in neumann_bcs:
                L -= c1 / dt * u_star[d] * n[d] * q * nbc.ds()

        # ALE mesh velocities
        if sim.mesh_morpher.active:
            cvol_new = dolfin.CellVolume(mesh)
            cvol_old = sim.data['cvolp']

            # Divergence of u should balance expansion/contraction of the cell K
            # ∇⋅u = -∂K/∂t       (See below for definition of the ∇⋅u term)
            L -= (cvol_new - cvol_old) / dt * q * dx

        self.form_lhs = a
        self.form_rhs = L
Example #8
0
    def _setup_dg1_projection_2D(self, w, incompressibility_flux_type, D12,
                                 use_bcs):
        """
        Implement the projection where the result is BDM embeded in a DG1 function
        """
        sim = self.simulation
        k = 1
        gdim = 2
        mesh = w[0].function_space().mesh()
        V = VectorFunctionSpace(mesh, 'DG', k)
        W = FunctionSpace(mesh, 'DGT', k)
        n = FacetNormal(mesh)

        v1 = TestFunction(W)
        u = TrialFunction(V)

        # The same fluxes that are used in the incompressibility equation
        if incompressibility_flux_type == 'central':
            u_hat_dS = dolfin.avg(w)
        elif incompressibility_flux_type == 'upwind':
            w_nU = (dot(w, n) + abs(dot(w, n))) / 2.0
            switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0)
            u_hat_dS = switch * w('+') + (1 - switch) * w('-')

        if D12 is not None:
            u_hat_dS += dolfin.Constant([D12, D12]) * dolfin.jump(w, n)

        # Equation 1 - flux through the sides
        a = L = 0
        for R in '+-':
            a += dot(u(R), n(R)) * v1(R) * dS
            L += dot(u_hat_dS, n(R)) * v1(R) * dS

        # Eq. 1 cont. - flux through external boundaries
        a += dot(u, n) * v1 * ds
        if use_bcs:
            for d in range(gdim):
                dirichlet_bcs = sim.data['dirichlet_bcs']['u%d' % d]
                neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, [])
                robin_bcs = sim.data['robin_bcs'].get('u%d' % d, [])
                outlet_bcs = sim.data['outlet_bcs']

                for dbc in dirichlet_bcs:
                    u_bc = dbc.func()
                    L += u_bc * n[d] * v1 * dbc.ds()

                for nbc in neumann_bcs + robin_bcs + outlet_bcs:
                    if nbc.enforce_zero_flux:
                        pass  # L += 0
                    else:
                        L += w[d] * n[d] * v1 * nbc.ds()

            for sbc in sim.data['slip_bcs'].get('u', []):
                pass  # L += 0
        else:
            L += dot(w, n) * v1 * ds

        # Equation 2 - internal shape   :   empty for DG1
        # Equation 3 - BDM Phi          :   empty for DG1

        return a, L, V
Example #9
0
    def _setup_dg2_projection_2D(self, w, incompressibility_flux_type, D12,
                                 use_bcs):
        """
        Implement the projection where the result is BDM embeded in a DG2 function
        """
        sim = self.simulation
        k = 2
        gdim = 2
        mesh = w[0].function_space().mesh()
        V = VectorFunctionSpace(mesh, 'DG', k)
        n = FacetNormal(mesh)

        # The mixed function space of the projection test functions
        e1 = FiniteElement('DGT', mesh.ufl_cell(), k)
        e2 = VectorElement('DG', mesh.ufl_cell(), k - 2)
        e3 = FiniteElement('Bubble', mesh.ufl_cell(), 3)
        em = MixedElement([e1, e2, e3])
        W = FunctionSpace(mesh, em)
        v1, v2, v3b = TestFunctions(W)
        u = TrialFunction(V)

        # The same fluxes that are used in the incompressibility equation
        if incompressibility_flux_type == 'central':
            u_hat_dS = dolfin.avg(w)
        elif incompressibility_flux_type == 'upwind':
            w_nU = (dot(w, n) + abs(dot(w, n))) / 2.0
            switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0)
            u_hat_dS = switch * w('+') + (1 - switch) * w('-')

        if D12 is not None:
            u_hat_dS += dolfin.Constant([D12, D12]) * dolfin.jump(w, n)

        # Equation 1 - flux through the sides
        a = L = 0
        for R in '+-':
            a += dot(u(R), n(R)) * v1(R) * dS
            L += dot(u_hat_dS, n(R)) * v1(R) * dS

        # Eq. 1 cont. - flux through external boundaries
        a += dot(u, n) * v1 * ds
        if use_bcs:
            for d in range(gdim):
                dirichlet_bcs = sim.data['dirichlet_bcs']['u%d' % d]
                neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, [])
                robin_bcs = sim.data['robin_bcs'].get('u%d' % d, [])
                outlet_bcs = sim.data['outlet_bcs']

                for dbc in dirichlet_bcs:
                    u_bc = dbc.func()
                    L += u_bc * n[d] * v1 * dbc.ds()

                for nbc in neumann_bcs + robin_bcs + outlet_bcs:
                    if nbc.enforce_zero_flux:
                        pass  # L += 0
                    else:
                        L += w[d] * n[d] * v1 * nbc.ds()

            for sbc in sim.data['slip_bcs'].get('u', []):
                pass  # L += 0
        else:
            L += dot(w, n) * v1 * ds

        # Equation 2 - internal shape
        a += dot(u, v2) * dx
        L += dot(w, v2) * dx

        # Equation 3 - BDM Phi
        v3 = as_vector([v3b.dx(1), -v3b.dx(0)])  # Curl of [0, 0, v3b]
        a += dot(u, v3) * dx
        L += dot(w, v3) * dx

        return a, L, V
Example #10
0
 def apply_stabilization(self):
     df.dS = self.dS  #Important <----
     h_msh = df.CellDiameter(self.mesh)
     h_avg = (h_msh("+") + h_msh("-")) / 2.0
     #Output
     local_stab = 0.0
     if self.stab_type == 'cip':
         if self.moment_order == 3 or self.moment_order == 'ns':
             local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[0]),self.nv)\
                     * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for temp
         if self.moment_order == 6:
             local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\
                     * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure
             local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\
                     * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x
             local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\
                     * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y
         if self.moment_order == 13:
             local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[3]),self.nv)\
                     * df.jump(df.grad(self.v[3]),self.nv) * df.dS #cip for temp
             local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\
                     * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure
             local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\
                     * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x
             local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\
                     * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y
         if self.moment_order == 'grad13':
             local_stab += self.DELTA_T * h_avg**self.ht * df.jump(df.grad(self.u[3]),self.nv)\
                     * df.jump(df.grad(self.v[6]),self.nv) * df.dS #cip for temp
             local_stab += self.DELTA_P * h_avg**self.hp * df.jump(df.grad(self.u[0]),self.nv)\
                     * df.jump(df.grad(self.v[0]),self.nv) * df.dS #cip for pressure
             local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[1]),self.nv)\
                     * df.jump(df.grad(self.v[1]),self.nv) * df.dS #cip for velocity_x
             local_stab += self.DELTA_U * h_avg**self.hu * df.jump(df.grad(self.u[2]),self.nv)\
                     * df.jump(df.grad(self.v[2]),self.nv) * df.dS #cip for velocity_y
     elif self.stab_type == 'gls':
         local_stab = (0.0001* h_msh * df.inner(self.SA_x * df.Dx(self.u,0)\
                 + self.SA_y * df.Dx(self.u,1) + self.SP_Coeff * self.u, self.SA_x * df.Dx(self.v,0)\
                 + self.SA_y * df.Dx(self.v,1) + self.SP_Coeff * self.v ) * df.dx)
     return local_stab
Example #11
0
        phi = TrialFunction(Vx)
        psi = TestFunction(Vx)

        v = Function(Vx)

        gamma = 1.0

        # Jump penalty term
        stab1 = 2.

        nE = FacetNormal(mx)
        hE = FacetArea(mx)

        test = div(grad(psi))
        S_ = gamma * inner(ax, grad(grad(phi))) * test * dx(mx) \
            + stab1 * avg(hE)**(-1) * inner(jump(grad(phi), nE), jump(grad(psi), nE)) * dS(mx) \
            + gamma * inner(bx, grad(phi)) * test * dx(mx) \
            + gamma * c * phi * test * dx(mx)

        # This matrix also changes since we are testing the whole equation
        # with div(grad(psi)) instead of psi
        M_ = gamma * phi * test * dx(mx)

        bc_Vx = DirichletBC(Vx, g, 'on_boundary')
        S = assemble(S_)
        M = assemble(M_)

        # Prepare special treatment of deterministic part and time-derivative.
        xdofs = Vx.tabulate_dof_coordinates().flatten()
        ix = np.argsort(xdofs)
Example #12
0
def delta_dg(mesh,expr):
    V = df.FunctionSpace(mesh, "DG", 1)
    m = df.interpolate(expr, V)
    
    n = df.FacetNormal(mesh)
    h = df.CellSize(mesh)
    h_avg = (h('+') + h('-'))/2.0
    
    alpha = 1.0
    gamma = 0
    
    u = df.TrialFunction(V)
    v = df.TestFunction(V)
    
    
    a = df.dot(df.grad(v), df.grad(u))*df.dx \
        - df.dot(df.avg(df.grad(v)), df.jump(u, n))*df.dS \
        - df.dot(df.jump(v, n), df.avg(df.grad(u)))*df.dS \
        + alpha/h_avg*df.dot(df.jump(v, n), df.jump(u, n))*df.dS 
        #- df.dot(df.grad(v), u*n)*df.ds \
        #- df.dot(v*n, df.grad(u))*df.ds \
        #+ gamma/h*v*u*df.ds
        
    #a = 1.0/h_avg*df.dot(df.jump(v, n), df.jump(u, n))*df.dS 
        
        #- df.dot(df.grad(v), u*n)*df.ds \
        #- df.dot(v*n, df.grad(u))*df.ds \
        #+ gamma/h*v*u*df.ds

    """
    a1 = df.dot(df.grad(v), df.grad(u))*df.dx 
    a2 = df.dot(df.avg(df.grad(v)), df.jump(u, n))*df.dS
    a3 = df.dot(df.jump(v, n), df.avg(df.grad(u)))*df.dS
    a4 = alpha/h_avg*df.dot(df.jump(v, n), df.jump(u, n))*df.dS 
    a5 = df.dot(df.grad(v), u*n)*df.ds
    a6 = df.dot(v*n, df.grad(u))*df.ds
    a7 = alpha/h*v*u*df.ds
    
    printaa(a1,'a1')
    printaa(a2,'a2')
    printaa(a3,'a3')
    printaa(a4,'a4')
    printaa(a5,'a5')
    printaa(a6,'a6')
    printaa(a7,'a7')
    """
    
    K = df.assemble(a).array()
    L = df.assemble(v * df.dx).array()
    
    
    h = -np.dot(K,m.vector().array())/L
    
    fun = df.Function(V)
    fun.vector().set_local(h)
    
    DG1 = df.FunctionSpace(mesh, "DG", 1)
    CG1 = df.FunctionSpace(mesh, "CG", 1)
    #fun = df.interpolate(fun, DG1)
    fun = df.project(fun, CG1)
    
    dim = mesh.topology().dim()
    
    res = []    
    
    if dim == 1:
        for x in xs:
            res.append(fun(x))
    elif dim == 2:
        df.plot(fun)
        df.interactive()
        for x in xs:
            res.append(fun(x,0.5))
    elif dim == 3:
        for x in xs:
            res.append(fun(x,0.5,0.5))
            
    return res
Example #13
0
# Term stemming from grad(T)
da1_top -= inner(dot(grad(s_top), grad(T)), grad(lmb)) * dX
# Term stemming from grad(lmb)
da1_top -= inner(grad(T), dot(grad(s_top), grad(lmb))) * dX
# Classic shape derivative term bottom mesh
da1_bottom = div(s_bottom) * inner(grad(T), grad(lmb)) * dX
# Term stemming from grad(T)
da1_bottom += inner(grad(dot(s_bottom, grad(T))), grad(lmb)) * dX
# Term stemming from grad(lmb)
da1_bottom += inner(grad(T), grad(dot(s_bottom, grad(lmb)))) * dX
# Material derivative of background T
da1_bottom -= inner(dot(nabla_grad(s_bottom), grad(T)), grad(lmb)) * dX
# Material derivative of background lmb
da1_bottom -= inner(grad(T), dot(nabla_grad(s_bottom), grad(lmb))) * dX
#----------------------------------------------------------------------------
a2 = -dot(avg(grad(T)), jump(lmb, n)) * dI
# Classic shape derivative at interface
da2 = -0.5*tan_div(s_top("-"), n("-"))*inner(n("-"),grad(T("-"))+grad(T("+")))\
      *(lmb("-")-lmb("+"))*dI
# Due to normal variation
da2 -= 0.5*inner(dn_mat(s_top("-"), n("-")), grad(T("-")) + grad(T("+")))*\
      (lmb("-")-lmb("+"))*dI
# Due to grad(T)
da2 += 0.5 * inner(
    n("-"),
    dot(nabla_grad(s_top("-")),
        nabla_grad(T("-")) + nabla_grad(T("+"))) * (lmb("-") - lmb("+"))) * dI
# Material derivative of background grad(T)
da2 -= 0.5 * inner(n("-"), grad(dot(s_top("-"), grad(
    T("+"))))) * (lmb("-") - lmb("+")) * dI
# Material derivative of background lmb
    def define_advection_equation(self):
        """
        Setup the advection equation for the colour function

        This implementation assembles the full LHS and RHS each time they are needed
        """
        sim = self.simulation
        mesh = sim.data['mesh']
        n = dolfin.FacetNormal(mesh)
        dS, dx = dolfin.dS(mesh), dolfin.dx(mesh)

        # Trial and test functions
        Vc = self.Vc
        c = dolfin.TrialFunction(Vc)
        d = dolfin.TestFunction(Vc)

        c1, c2, c3 = self.time_coeffs
        dt = self.dt
        u_conv = self.u_conv

        if not self.colour_is_discontinuous:
            # Continous Galerkin implementation of the advection equation
            # FIXME: add stabilization
            eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx + div(
                c * u_conv) * d * dx

        elif self.velocity_is_trace:
            # Upstream and downstream normal velocities
            w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2
            w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2

            if self.beta is not None:
                # Define the blended flux
                # The blending factor beta is not DG, so beta('+') == beta('-')
                b = self.beta('+')
                flux = (1 - b) * jump(c * w_nU) + b * jump(c * w_nD)
            else:
                flux = jump(c * w_nU)

            # Discontinuous Galerkin implementation of the advection equation
            eq = (c1 * c + c2 * self.cp +
                  c3 * self.cpp) / dt * d * dx + flux * jump(d) * dS

            # On each facet either w_nD or w_nU will be 0, the other is multiplied
            # with the appropriate flux, either the value c going out of the domain
            # or the Dirichlet value coming into the domain
            for dbc in self.dirichlet_bcs:
                eq += w_nD * dbc.func() * d * dbc.ds()
                eq += w_nU * c * d * dbc.ds()

        elif self.beta is not None:
            # Upstream and downstream normal velocities
            w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2
            w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2

            if self.beta is not None:
                # Define the blended flux
                # The blending factor beta is not DG, so beta('+') == beta('-')
                b = self.beta('+')
                flux = (1 - b) * jump(c * w_nU) + b * jump(c * w_nD)
            else:
                flux = jump(c * w_nU)

            # Discontinuous Galerkin implementation of the advection equation
            eq = ((c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx -
                  dot(c * u_conv, grad(d)) * dx + flux * jump(d) * dS)

            # Enforce Dirichlet BCs weakly
            for dbc in self.dirichlet_bcs:
                eq += w_nD * dbc.func() * d * dbc.ds()
                eq += w_nU * c * d * dbc.ds()

        else:
            # Downstream normal velocities
            w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2

            # Discontinuous Galerkin implementation of the advection equation
            eq = (c1 * c + c2 * self.cp + c3 * self.cpp) / dt * d * dx

            # Convection integrated by parts two times to bring back the original
            # div form (this means we must subtract and add all fluxes)
            eq += div(c * u_conv) * d * dx

            # Replace downwind flux with upwind flux on downwind internal facets
            eq -= jump(w_nD * d) * jump(c) * dS

            # Replace downwind flux with upwind BC flux on downwind external facets
            for dbc in self.dirichlet_bcs:
                # Subtract the "normal" downwind flux
                eq -= w_nD * c * d * dbc.ds()
                # Add the boundary value upwind flux
                eq += w_nD * dbc.func() * d * dbc.ds()

        # Penalty forcing zones
        for fz in self.forcing_zones:
            eq += fz.penalty * fz.beta * (c - fz.target) * d * dx

        a, L = dolfin.system(eq)
        self.form_lhs = dolfin.Form(a)
        self.form_rhs = dolfin.Form(L)
        self.tensor_lhs = None
        self.tensor_rhs = None
Example #15
0
def solverNeilanSalgadoZhang(P, opt):
    '''
    This function implements the method presented in
    Neilan, Salgado, Zhang (2017), Chapter 4
    Main characteristics:
    - test with piecewise second derivative of test_u
    - no discrete Hessian
    - first-order stabilization necessary
    '''

    assert opt["HessianSpace"] == "CG", 'opt["HessianSpace"] has to be "CG"'
    assert opt[
        "stabilityConstant1"], 'opt["stabilityConstant1"] has to be positive'

    gamma = P.normalizeSystem(opt)

    if isinstance(P.g, list):
        bc_V = []
        for fun, dom in P.g:
            bc_V.append(DirichletBC(P.V, fun, dom))
    else:
        if isinstance(P.g, Function):
            bc_V = DirichletBC(P.V, P.g, 'on_boundary')
        else:
            g = project(P.g, P.V)
            bc_V = DirichletBC(P.V, g, 'on_boundary')

    trial_u = TrialFunction(P.V)
    test_u = TestFunction(P.V)

    # Assemble right-hand side in each case
    f_h = gamma * P.f * div(grad(test_u)) * dx

    # Adjust load vector in case of time-dependent problem
    if P.isTimeDependant:
        f_h = gamma * P.u_np1 * div(grad(test_u)) * dx - P.dt * f_h

    rhs = assemble(f_h)
    if isinstance(bc_V, list):
        for bc_v in bc_V:
            bc_v.apply(rhs)
    else:
        bc_V.apply(rhs)

    repeat_setup = False

    if hasattr(P, 'timeDependentCoefs'):
        if P.timeDependentCoefs:
            repeat_setup = True

    if 'HJB' in str(type(P)):
        repeat_setup = True

    if (not P.isTimeDependant) or (P.iter == 0) or repeat_setup:

        print('Setup bilinear form')

        # Define bilinear form
        a_h = gamma * inner(P.a, grad(grad(trial_u))) * div(grad(test_u)) * dx \
            + opt["stabilityConstant1"] * avg(P.hE)**(-1) * inner(
                jump(grad(trial_u), P.nE), jump(grad(test_u), P.nE)) * dS

        if P.hasDrift:
            a_h += gamma * inner(P.b, grad(trial_u)) * div(grad(test_u)) * dx

        if P.hasPotential:
            a_h += gamma * P.c * trial_u * div(grad(test_u)) * dx

        # Adjust system matrix in case of time-dependent problem
        if P.isTimeDependant:
            a_h = gamma * trial_u * div(grad(test_u)) * dx - P.dt * a_h

        S = assemble(a_h)
        if isinstance(bc_V, list):
            for bc_v in bc_V:
                bc_v.apply(S)
        else:
            bc_V.apply(S)

        P.S = S

    if opt['time_check']:
        t1 = time()

    solve(P.S, P.u.vector(), rhs)

    # tmp = assemble(inner(P.a,grad(grad(trial_u))) * div(grad(test_u)) * dx)
    # A = assemble(a_h)
    # print('Row sum: ', sum(A.array(),1).round(4))
    # print('Col sum: ', sum(A.array(),0).round(4))
    # print('Total sum: ', sum(sum(A.array())).round(4))
    # ipdb.set_trace()
    # solve(a_h == f_h, P.u, bc_V, solver_parameters={'linear_solver': 'mumps'})

    if opt['time_check']:
        print("Solve linear equation system ... %.2fs" % (time() - t1))
        sys.stdout.flush()

    N_iter = 1

    return N_iter
def transport_linear(integrator_type, mesh, subdomains, boundaries, t_start, dt, T, solution0, \
                 alpha_0, K_0, mu_l_0, lmbda_l_0, Ks_0, \
                 alpha_1, K_1, mu_l_1, lmbda_l_1, Ks_1, \
                 alpha, K, mu_l, lmbda_l, Ks, \
                 cf_0, phi_0, rho_0, mu_0, k_0,\
                 cf_1, phi_1, rho_1, mu_1, k_1,\
                 cf, phi, rho, mu, k, \
                 d_0, d_1, d_t,
                 vel_c, p_con, A_0, Temp, c_extrapolate):
    # Create mesh and define function space
    parameters["ghost_mode"] = "shared_facet"  # required by dS

    dx = Measure('dx', domain=mesh, subdomain_data=subdomains)
    ds = Measure('ds', domain=mesh, subdomain_data=boundaries)
    dS = Measure('dS', domain=mesh, subdomain_data=boundaries)

    C_cg = FiniteElement("CG", mesh.ufl_cell(), 1)
    C_dg = FiniteElement("DG", mesh.ufl_cell(), 0)
    mini = C_cg + C_dg
    C = FunctionSpace(mesh, mini)
    C = BlockFunctionSpace([C])
    TM = TensorFunctionSpace(mesh, 'DG', 0)
    PM = FunctionSpace(mesh, 'DG', 0)
    n = FacetNormal(mesh)
    vc = CellVolume(mesh)
    fc = FacetArea(mesh)

    h = vc / fc
    h_avg = (vc('+') + vc('-')) / (2 * avg(fc))

    penalty1 = Constant(1.0)

    tau = Function(PM)
    tau = tau_cal(tau, phi, -0.5)

    tuning_para = 0.25

    vel_norm = (dot(vel_c, n) + abs(dot(vel_c, n))) / 2.0

    cell_size = CellDiameter(mesh)
    vnorm = sqrt(dot(vel_c, vel_c))

    I = Identity(mesh.topology().dim())
    d_eff = Function(TM)
    d_eff = diff_coeff_cal_rev(d_eff, d_0, tau,
                               phi) + tuning_para * cell_size * vnorm * I

    monitor_dt = dt

    # Define variational problem
    dc, = BlockTrialFunction(C)
    dc_dot, = BlockTrialFunction(C)
    psic, = BlockTestFunction(C)
    block_c = BlockFunction(C)
    c, = block_split(block_c)
    block_c_dot = BlockFunction(C)
    c_dot, = block_split(block_c_dot)

    theta = -1.0

    a_time = phi * rho * inner(c_dot, psic) * dx

    a_dif = dot(rho*d_eff*grad(c),grad(psic))*dx \
        - dot(avg_w(rho*d_eff*grad(c),weight_e(rho*d_eff,n)), jump(psic, n))*dS \
        + theta*dot(avg_w(rho*d_eff*grad(psic),weight_e(rho*d_eff,n)), jump(c, n))*dS \
        + penalty1/h_avg*k_e(rho*d_eff,n)*dot(jump(c, n), jump(psic, n))*dS

    a_adv = -dot(rho*vel_c*c,grad(psic))*dx \
        + dot(jump(psic), rho('+')*vel_norm('+')*c('+') - rho('-')*vel_norm('-')*c('-') )*dS \
        + dot(psic, rho*vel_norm*c)*ds(3)

    R_c = R_c_cal(c_extrapolate, p_con, Temp)
    c_D1 = Constant(0.5)
    rhs_c = R_c * A_s_cal(phi, phi_0, A_0) * psic * dx - dot(
        rho * phi * vel_c, n) * c_D1 * psic * ds(1)

    r_u = [a_dif + a_adv]
    j_u = block_derivative(r_u, [c], [dc])

    r_u_dot = [a_time]
    j_u_dot = block_derivative(r_u_dot, [c_dot], [dc_dot])
    r = [r_u_dot[0] + r_u[0] - rhs_c]

    # this part is not applied.
    exact_solution_expression1 = Expression("1.0",
                                            t=0,
                                            element=C[0].ufl_element())

    def bc(t):
        p5 = DirichletBC(C.sub(0),
                         exact_solution_expression1,
                         boundaries,
                         1,
                         method="geometric")
        return BlockDirichletBC([p5])

    # Define problem wrapper
    class ProblemWrapper(object):
        def set_time(self, t):
            pass

        # Residual and jacobian functions
        def residual_eval(self, t, solution, solution_dot):
            return r

        def jacobian_eval(self, t, solution, solution_dot,
                          solution_dot_coefficient):
            return [[
                Constant(solution_dot_coefficient) * j_u_dot[0, 0] + j_u[0, 0]
            ]]

        # Define boundary condition
        def bc_eval(self, t):
            pass

        # Define initial condition
        def ic_eval(self):
            return solution0

        # Define custom monitor to plot the solution
        def monitor(self, t, solution, solution_dot):
            pass

    problem_wrapper = ProblemWrapper()
    (solution, solution_dot) = (block_c, block_c_dot)
    solver = TimeStepping(problem_wrapper, solution, solution_dot)
    solver.set_parameters({
        "initial_time": t_start,
        "time_step_size": dt,
        "monitor": {
            "time_step_size": monitor_dt,
        },
        "final_time": T,
        "exact_final_time": "stepover",
        "integrator_type": integrator_type,
        "problem_type": "linear",
        "linear_solver": "mumps",
        "report": True
    })
    export_solution = solver.solve()

    return export_solution, T
Example #17
0
    def on_simulation_start(self):
        """
        This runs when the simulation starts. It does not run in __init__
        since the N-S solver needs the density and viscosity we define, and
        we need the velocity that is defined by the solver
        """
        if self.use_analytical_solution:
            return

        sim = self.simulation
        dirichlet_bcs = sim.data['dirichlet_bcs'].get('rho', [])

        if self.use_rk_method:
            V = self.simulation.data['Vrho']
            if not V.ufl_element().family() == 'Discontinuous Lagrange':
                ocellaris_error(
                    'VariableDensity timestepping error',
                    'Can only use explicit SSP Runge-Kutta method with DG space for rho',
                )

            Vu = sim.data['Vu']
            u_conv, self.funcs_to_extrapolate = [], []
            for d in range(sim.ndim):
                ux = Function(Vu)
                up = sim.data['up_conv%d' % d]
                upp = sim.data['upp_conv%d' % d]
                self.funcs_to_extrapolate.append((ux, up, upp))
                u_conv.append(ux)
            u_conv = dolfin.as_vector(u_conv)

            from dolfin import dot, div, jump, dS

            mesh = self.simulation.data['mesh']

            re = self.rho_explicit = dolfin.Function(V)
            c, d = dolfin.TrialFunction(V), dolfin.TestFunction(V)
            n = dolfin.FacetNormal(mesh)
            w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2
            dx = dolfin.dx(domain=mesh)

            eq = c * d * dx

            # Convection integrated by parts two times to bring back the original
            # div form (this means we must subtract and add all fluxes)
            eq += div(re * u_conv) * d * dx

            # Replace downwind flux with upwind flux on downwind internal facets
            eq -= jump(w_nD * d) * jump(re) * dS

            # Replace downwind flux with upwind BC flux on downwind external facets
            for dbc in dirichlet_bcs:
                # Subtract the "normal" downwind flux
                eq -= w_nD * re * d * dbc.ds()
                # Add the boundary value upwind flux
                eq += w_nD * dbc.func() * d * dbc.ds()

            a, L = dolfin.system(eq)
            self.rk = RungeKuttaDGTimestepping(
                self.simulation,
                a,
                L,
                self.rho,
                self.rho_explicit,
                'rho',
                order=None,
                explicit_funcs=self.funcs_to_extrapolate,
                bcs=dirichlet_bcs,
            )

        else:
            # Use backward Euler (BDF1) for timestep 1
            self.time_coeffs = Constant([1, -1, 0])

            if dolfin.norm(self.rho_pp.vector()) > 0:
                # Use BDF2 from the start
                self.time_coeffs.assign(Constant([3 / 2, -2, 1 / 2]))
                self.simulation.log.info(
                    'Using second order timestepping from the start in VariableDensity'
                )

            # Define equation for advection of the density
            #    ∂ρ/∂t +  ∇⋅(ρ u) = 0
            beta = None
            u_conv = sim.data['u_conv']
            forcing_zones = sim.data['forcing_zones'].get('rho', [])
            self.eq = AdvectionEquation(
                sim,
                sim.data['Vrho'],
                self.rho_p,
                self.rho_pp,
                u_conv,
                beta,
                self.time_coeffs,
                dirichlet_bcs,
                forcing_zones,
            )

            self.solver = linear_solver_from_input(sim, 'solver/rho', SOLVER,
                                                   PRECONDITIONER, None,
                                                   KRYLOV_PARAMETERS)
            self.slope_limiter = SlopeLimiter(sim, 'rho', self.rho)
            self.slope_limiter.set_phi_old(self.rho_p)

        # Make sure the initial value is included in XDMF results from timestep 0
        self.rho.assign(self.rho_p)
Example #18
0
    def setup_scalar_equation(self):
        sim = self.simulation
        V = sim.data['Vphi']
        mesh = V.mesh()
        P = V.ufl_element().degree()

        # Source term
        source_cpp = sim.input.get_value('solver/source', '0', 'string')
        f = dolfin.Expression(source_cpp, degree=P)

        # Create the solution function
        sim.data['phi'] = dolfin.Function(V)

        # DG elliptic penalty
        penalty = define_penalty(mesh, P, k_min=1.0, k_max=1.0)
        penalty_dS = dolfin.Constant(penalty)
        penalty_ds = dolfin.Constant(penalty * 2)
        yh = dolfin.Constant(1 / (penalty * 2))

        # Define weak form
        u, v = dolfin.TrialFunction(V), dolfin.TestFunction(V)
        a = dot(grad(u), grad(v)) * dx
        L = f * v * dx

        # Symmetric Interior Penalty method for -∇⋅∇φ
        n = dolfin.FacetNormal(mesh)
        a -= dot(n('+'), avg(grad(u))) * jump(v) * dS
        a -= dot(n('+'), avg(grad(v))) * jump(u) * dS

        # Symmetric Interior Penalty coercivity term
        a += penalty_dS * jump(u) * jump(v) * dS

        # Dirichlet boundary conditions
        # Nitsche's (1971) method, see e.g. Epshteyn and Rivière (2007)
        dirichlet_bcs = sim.data['dirichlet_bcs'].get('phi', [])
        for dbc in dirichlet_bcs:
            bcval, dds = dbc.func(), dbc.ds()

            # SIPG for -∇⋅∇φ
            a -= dot(n, grad(u)) * v * dds
            a -= dot(n, grad(v)) * u * dds
            L -= dot(n, grad(v)) * bcval * dds

            # Weak Dirichlet
            a += penalty_ds * u * v * dds
            L += penalty_ds * bcval * v * dds

        # Neumann boundary conditions
        neumann_bcs = sim.data['neumann_bcs'].get('phi', [])
        for nbc in neumann_bcs:
            L += nbc.func() * v * nbc.ds()

        # Robin boundary conditions
        # See Juntunen and Stenberg (2009)
        # n⋅∇φ = (φ0 - φ)/b + g
        robin_bcs = sim.data['robin_bcs'].get('phi', [])
        for rbc in robin_bcs:
            b, rds = rbc.blend(), rbc.ds()
            dval, nval = rbc.dfunc(), rbc.nfunc()

            # From IBP of the main equation
            a -= dot(n, grad(u)) * v * rds

            # Test functions for the Robin BC
            z1 = 1 / (b + yh) * v
            z2 = -yh / (b + yh) * dot(n, grad(v))

            # Robin BC added twice with different test functions
            for z in [z1, z2]:
                a += b * dot(n, grad(u)) * z * rds
                a += u * z * rds
                L += dval * z * rds
                L += b * nval * z * rds

        # Does the system have a null-space?
        self.has_null_space = len(dirichlet_bcs) + len(robin_bcs) == 0

        self.form_lhs = a
        self.form_rhs = L
Example #19
0
    def _evaluateLocalEstimator(cls, mu, w, coeff_field, pde, f, quadrature_degree, epsilon=1e-5):
        """Evaluation of patch local equilibrated estimator."""

        # prepare numerical flux and f
        sigma_mu, f_mu = evaluate_numerical_flux(w, mu, coeff_field, f)

        # ###################
        # ## MIXED PROBLEM ##
        # ###################

        # get setup data for mixed problem
        V = w[mu]._fefunc.function_space()
        mesh = V.mesh()
        mesh.init()
        degree = element_degree(w[mu]._fefunc)

        # data for nodal bases
        V_dm = V.dofmap()
        V_dofs = dict([(i, V_dm.cell_dofs(i)) for i in range(mesh.num_cells())])
        V1 = FunctionSpace(mesh, 'CG', 1)   # V1 is to define nodal base functions
        phi_z = Function(V1)
        phi_coeffs = np.ndarray(V1.dim())
        vertex_dof_map = V1.dofmap().vertex_to_dof_map(mesh)
        # vertex_dof_map = vertex_to_dof_map(V1)
        dof_list = vertex_dof_map.tolist()
        # DG0 localisation
        DG0 = FunctionSpace(mesh, 'DG', 0)
        DG0_dofs = dict([(c.index(),DG0.dofmap().cell_dofs(c.index())[0]) for c in cells(mesh)])
        dg0 = TestFunction(DG0)
        # characteristic function of patch
        xi_z = Function(DG0)
        xi_coeffs = np.ndarray(DG0.dim())
        # mesh data
        h = CellSize(mesh)
        n = FacetNormal(mesh)
        cf = CellFunction('size_t', mesh)
        # setup error estimator vector
        eq_est = np.zeros(DG0.dim())

        # setup global equilibrated flux vector
        DG = VectorFunctionSpace(mesh, "DG", degree)
        DG_dofmap = DG.dofmap()

        # define form functions
        tau = TrialFunction(DG)
        v = TestFunction(DG)

        # define global tau
        tau_global = Function(DG)
        tau_global.vector()[:] = 0.0

        # iterate vertices
        for vertex in vertices(mesh):
            # get patch cell indices
            vid = vertex.index()
            patch_cid, FF_inner, FF_boundary = get_vertex_patch(vid, mesh, layers=1)

            # set nodal base function
            phi_coeffs[:] = 0
            phi_coeffs[dof_list.index(vid)] = 1
            phi_z.vector()[:] = phi_coeffs

            # set characteristic function and mark patch
            cf.set_all(0)
            xi_coeffs[:] = 0
            for cid in patch_cid:
                xi_coeffs[DG0_dofs[int(cid)]] = 1
                cf[int(cid)] = 1
            xi_z.vector()[:] = xi_coeffs

            # determine local dofs
            lDG_cell_dofs = dict([(cid, DG_dofmap.cell_dofs(cid)) for cid in patch_cid])
            lDG_dofs = [cd.tolist() for cd in lDG_cell_dofs.values()]
            lDG_dofs = list(iter.chain(*lDG_dofs))

            # print "\nlocal DG subspace has dimension", len(lDG_dofs), "degree", degree, "cells", len(patch_cid), patch_cid
            # print "local DG_cell_dofs", lDG_cell_dofs
            # print "local DG_dofs", lDG_dofs

            # create patch measures
            dx = Measure('dx')[cf]
            dS = Measure('dS')[FF_inner]

            # define forms
            alpha = Constant(1 / epsilon) / h
            a = inner(tau,v) * phi_z * dx(1) + alpha * div(tau) * div(v) * dx(1) + avg(alpha) * jump(tau,n) * jump(v,n) * dS(1)\
                + avg(alpha) * jump(xi_z * tau,n) * jump(v,n) * dS(2)
            L = -alpha * (div(sigma_mu) + f) * div(v) * phi_z * dx(1)\
                - avg(alpha) * jump(sigma_mu,n) * jump(v,n) * avg(phi_z)*dS(1)

    #        print "L2 f + div(sigma)", assemble((f + div(sigma)) * (f + div(sigma)) * dx(0))

            # assemble forms
            lhs = assemble(a, form_compiler_parameters={'quadrature_degree': quadrature_degree})
            rhs = assemble(L, form_compiler_parameters={'quadrature_degree': quadrature_degree})

            # convert DOLFIN representation to scipy sparse arrays
            rows, cols, values = lhs.data()
            lhsA = sps.csr_matrix((values, cols, rows)).tocoo()

            # slice sparse matrix and solve linear problem
            lhsA = coo_submatrix_pull(lhsA, lDG_dofs, lDG_dofs)
            lx = spsolve(lhsA, rhs.array()[lDG_dofs])
            # print ">>> local solution lx", type(lx), lx
            local_tau = Function(DG)
            local_tau.vector()[lDG_dofs] = lx
            # print "div(tau)", assemble(inner(div(local_tau),div(local_tau))*dx(1))

            # add up local fluxes
            tau_global.vector()[lDG_dofs] += lx

        # evaluate estimator
        # maybe TODO: re-define measure dx
        eq_est = assemble( inner(tau_global, tau_global) * dg0 * (dx(0)+dx(1)),\
                           form_compiler_parameters={'quadrature_degree': quadrature_degree})

        # reorder according to cell ids
        eq_est = eq_est[DG0_dofs.values()].array()
        global_est = np.sqrt(np.sum(eq_est))
        # eq_est_global = assemble( inner(tau_global, tau_global) * (dx(0)+dx(1)), form_compiler_parameters={'quadrature_degree': quadrature_degree} )
        # global_est2 = np.sqrt(np.sum(eq_est_global))
        return global_est, FlatVector(np.sqrt(eq_est))#, tau_global
Example #20
0
R_c = (H_c - Hmid) * xsi_c * df.dx

####################################################################
##########################  MASS BALANCE  ##########################
####################################################################

# Solve the transport equation using DG0 finite elements which are
# first order accurate but unconditionally TVD and positivity-preserving,
# and also conserves mass perfectly.

# Grid velocity
v = dLdt * x_spatial[0]

# Inter element flux (upwind)
uH = df.avg((ubar - v)) * df.avg(Hmid * width) + 0.5 * abs(
    df.avg(width * (ubar - v))) * df.jump(Hmid * width)

# Residual of the transport equation with a zero-flux upstream boundary.
R_mass = (Lmid * width * dHdt * xsi + Lmid * Hmid * dWdt * xsi +
          Hmid * width * dLdt * xsi - xsi.dx(0) * (ubar - v) * width * Hmid -
          Lmid * width * adot * xsi) * df.dx + uH * df.jump(xsi) * df.dS + (
              U[0] - v) * Hmid * width * xsi * df.ds(1)

####################################################################
#########################  LENGTH MODEL  ###########################
####################################################################
# Calving thickness (when thickness is less than this, calving begins)
# This is imposed strongly when using the local length evolution equation
H_calving = (1 + df.Constant(q)) * D * rho_w / rho

# Calving velocity proportional to the degree to which the calving thickness
Example #21
0
    def _setup_projection_nedelec(self, w, incompressibility_flux_type, D12,
                                  use_bcs, pdeg, gdim):
        """
        Implement the BDM-like projection using Nedelec elements in the test function
        """
        sim = self.simulation
        k = pdeg
        mesh = w[0].function_space().mesh()
        V = VectorFunctionSpace(mesh, 'DG', k)
        n = FacetNormal(mesh)

        # The mixed function space of the projection test functions
        e1 = FiniteElement('DGT', mesh.ufl_cell(), k)
        e2 = FiniteElement('N1curl', mesh.ufl_cell(), k - 1)
        em = MixedElement([e1, e2])
        W = FunctionSpace(mesh, em)
        v1, v2 = TestFunctions(W)
        u = TrialFunction(V)

        # The same fluxes that are used in the incompressibility equation
        if incompressibility_flux_type == 'central':
            u_hat_dS = dolfin.avg(w)
        elif incompressibility_flux_type == 'upwind':
            w_nU = (dot(w, n) + abs(dot(w, n))) / 2.0
            switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0)
            u_hat_dS = switch * w('+') + (1 - switch) * w('-')

        if D12 is not None:
            u_hat_dS += dolfin.Constant([D12] * gdim) * dolfin.jump(w, n)

        # Equation 1 - flux through the sides
        a = L = 0
        for R in '+-':
            a += dot(u(R), n(R)) * v1(R) * dS
            L += dot(u_hat_dS, n(R)) * v1(R) * dS

        # Eq. 1 cont. - flux through external boundaries
        a += dot(u, n) * v1 * ds
        if use_bcs:
            for d in range(gdim):
                dirichlet_bcs = sim.data['dirichlet_bcs'].get('u%d' % d, [])
                neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, [])
                robin_bcs = sim.data['robin_bcs'].get('u%d' % d, [])
                outlet_bcs = sim.data['outlet_bcs']

                for dbc in dirichlet_bcs:
                    u_bc = dbc.func()
                    L += u_bc * n[d] * v1 * dbc.ds()

                for nbc in neumann_bcs + robin_bcs + outlet_bcs:
                    if nbc.enforce_zero_flux:
                        pass  # L += 0
                    else:
                        L += w[d] * n[d] * v1 * nbc.ds()

            for sbc in sim.data['slip_bcs'].get('u', []):
                pass  # L += 0
        else:
            L += dot(w, n) * v1 * ds

        # Equation 2 - internal shape using 'Nedelec 1st kind H(curl)' elements
        a += dot(u, v2) * dx
        L += dot(w, v2) * dx

        return a, L, V
Example #22
0
    def define_coupled_equation(self):
        """
        Setup the coupled Navier-Stokes equation

        This implementation assembles the full LHS and RHS each time they are needed
        """
        sim = self.simulation
        mpm = sim.multi_phase_model
        mesh = sim.data['mesh']
        Vcoupled = sim.data['Vcoupled']
        u_conv = sim.data['u_conv']

        # Unpack the coupled trial and test functions
        uc = dolfin.TrialFunction(Vcoupled)
        vc = dolfin.TestFunction(Vcoupled)
        ulist = []
        vlist = []
        ndim = self.simulation.ndim
        sigma, tau = [], []
        for d in range(ndim):
            ulist.append(uc[d])
            vlist.append(vc[d])
            indices = list(range(1 + ndim * (d + 1), 1 + ndim * (d + 2)))
            sigma.append(dolfin.as_vector([uc[i] for i in indices]))
            tau.append(dolfin.as_vector([vc[i] for i in indices]))

        u = dolfin.as_vector(ulist)
        v = dolfin.as_vector(vlist)
        p = uc[ndim]
        q = vc[ndim]

        c1, c2, c3 = sim.data['time_coeffs']
        dt = sim.data['dt']
        g = sim.data['g']
        n = dolfin.FacetNormal(mesh)

        # Fluid properties
        rho = mpm.get_density(0)
        mu = mpm.get_laminar_dynamic_viscosity(0)

        # Upwind and downwind velocities
        w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2.0
        w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2.0

        # LDG penalties
        C11 = Constant(1.0 + sim.ndim)
        switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), n('+'), n('-'))
        C12 = 0.5 * switch

        # Start building the coupled equations
        eq = 0

        # Momentum equations
        for d in range(sim.ndim):
            up = sim.data['up%d' % d]
            upp = sim.data['upp%d' % d]

            # Divergence free criterion
            # ∇⋅u = 0
            u_hat_p = avg(u[d])
            if self.use_grad_q_form:
                eq -= u[d] * q.dx(d) * dx
                eq += u_hat_p * jump(q) * n[d]('+') * dS
            else:
                eq += q * u[d].dx(d) * dx
                eq -= avg(q) * jump(u[d]) * n[d]('+') * dS

            # Time derivative
            # ∂(ρu)/∂t
            eq += rho * (c1 * u[d] + c2 * up + c3 * upp) / dt * v[d] * dx

            # Convection:
            # -w⋅∇(ρu)
            flux_nU = u[d] * w_nU
            flux = jump(flux_nU)
            eq -= u[d] * dot(grad(rho * v[d]), u_conv) * dx
            eq += flux * jump(rho * v[d]) * dS

            # Diffusion:
            # -∇⋅∇u
            u_hat_dS = avg(u[d]) - dot(C12, jump(u[d], n))
            sigma_hat_dS = avg(
                sigma[d]) - C11 * jump(u[d], n) + C12 * jump(sigma[d], n)
            eq += dot(sigma[d], tau[d]) * dx
            eq += u[d] * div(mu * tau[d]) * dx
            eq -= u_hat_dS * jump(mu * tau[d], n) * dS
            eq += dot(sigma[d], grad(v[d])) * dx
            eq -= dot(sigma_hat_dS, jump(v[d], n)) * dS

            # Pressure
            # ∇p
            if self.use_grad_p_form:
                eq += v[d] * p.dx(d) * dx
                eq -= avg(v[d]) * jump(p) * n[d]('+') * dS
            else:
                eq -= p * v[d].dx(d) * dx
                eq += avg(p) * jump(v[d]) * n[d]('+') * dS

            # Body force (gravity)
            # ρ g
            eq -= rho * g[d] * v[d] * dx

            # Other sources
            for f in sim.data['momentum_sources']:
                eq -= f[d] * v[d] * dx

            # Dirichlet boundary
            dirichlet_bcs = sim.data['dirichlet_bcs'].get('u%d' % d, [])
            for dbc in dirichlet_bcs:
                u_bc = dbc.func()

                # Divergence free criterion
                if self.use_grad_q_form:
                    eq += q * u_bc * n[d] * dbc.ds()
                else:
                    eq -= q * u[d] * n[d] * dbc.ds()
                    eq += q * u_bc * n[d] * dbc.ds()

                # Convection
                eq += rho * u[d] * w_nU * v[d] * dbc.ds()
                eq += rho * u_bc * w_nD * v[d] * dbc.ds()

                # Diffusion
                u_hat_ds = u_bc
                sigma_hat_ds = sigma[d] - C11 * (u[d] - u_bc) * n
                eq -= u_hat_ds * mu * dot(tau[d], n) * dbc.ds()
                eq -= dot(sigma_hat_ds, n) * v[d] * dbc.ds()

                # Pressure
                if not self.use_grad_p_form:
                    eq += p * v[d] * n[d] * dbc.ds()

            # Neumann boundary
            neumann_bcs = sim.data['neumann_bcs'].get('u%d' % d, [])
            for nbc in neumann_bcs:
                # Divergence free criterion
                if self.use_grad_q_form:
                    eq += q * u[d] * n[d] * nbc.ds()
                else:
                    eq -= q * u[d] * n[d] * nbc.ds()

                # Convection
                eq += rho * u[d] * w_nU * v[d] * nbc.ds()

                # Diffusion
                u_hat_ds = u[d]
                sigma_hat_ds = nbc.func() / mu * n
                eq -= u_hat_ds * mu * dot(tau[d], n) * nbc.ds()
                eq -= dot(sigma_hat_ds, n) * v[d] * nbc.ds()

                # Pressure
                if not self.use_grad_p_form:
                    eq += p * v[d] * n[d] * nbc.ds()

        a, L = dolfin.system(eq)
        self.form_lhs = a
        self.form_rhs = L
        self.tensor_lhs = None
        self.tensor_rhs = None
Example #23
0
def define_dg_equations(
    u,
    v,
    p,
    q,
    lm_trial,
    lm_test,
    simulation,
    include_hydrostatic_pressure,
    incompressibility_flux_type,
    use_grad_q_form,
    use_grad_p_form,
    use_stress_divergence_form,
    velocity_continuity_factor_D12=0,
    pressure_continuity_factor=0,
):
    """
    Define the coupled equations. Also used by the SIMPLE and IPCS-A solvers

    Weak form of the Navier-Stokes eq. with discontinuous elements

    :type simulation: ocellaris.Simulation
    """
    sim = simulation
    show = sim.log.info
    show('    Creating DG weak form with BCs')
    show('        include_hydrostatic_pressure = %r' %
         include_hydrostatic_pressure)
    show('        incompressibility_flux_type = %s' %
         incompressibility_flux_type)
    show('        use_grad_q_form = %r' % use_grad_q_form)
    show('        use_grad_p_form = %r' % use_grad_p_form)
    show('        use_stress_divergence_form = %r' %
         use_stress_divergence_form)
    show('        velocity_continuity_factor_D12 = %r' %
         velocity_continuity_factor_D12)
    show('        pressure_continuity_factor = %r' %
         pressure_continuity_factor)

    mpm = sim.multi_phase_model
    mesh = sim.data['mesh']
    u_conv = sim.data['u_conv']
    dx = dolfin.dx(domain=mesh)
    dS = dolfin.dS(domain=mesh)

    c1, c2, c3 = sim.data['time_coeffs']
    dt = sim.data['dt']
    g = sim.data['g']
    n = dolfin.FacetNormal(mesh)
    x = dolfin.SpatialCoordinate(mesh)

    # Fluid properties
    rho = mpm.get_density(0)
    nu = mpm.get_laminar_kinematic_viscosity(0)
    mu = mpm.get_laminar_dynamic_viscosity(0)

    # Hydrostatic pressure correction
    if include_hydrostatic_pressure:
        p += sim.data['p_hydrostatic']

    # Start building the coupled equations
    eq = 0

    # ALE mesh velocities
    if sim.mesh_morpher.active:
        u_mesh = sim.data['u_mesh']

        # Either modify the convective velocity or just include the mesh
        # velocity on cell integral form. Only activate one of the lines below
        # PS: currently the UFL form splitter (used in e.g. IPCS-A) has a
        #     problem with line number 2, but the Coupled solver handles
        #     both options with approximately the same resulting convergence
        #     errors on the Taylor-Green test case (TODO: fix form splitter)
        u_conv -= u_mesh
        # eq -= dot(div(rho * dolfin.outer(u, u_mesh)), v) * dx

        # Divergence of u should balance expansion/contraction of the cell K
        # ∇⋅u = -∂x/∂t       (See below for definition of the ∇⋅u term)
        # THIS IS SOMEWHAT EXPERIMENTAL
        cvol_new = dolfin.CellVolume(mesh)
        cvol_old = sim.data['cvolp']
        eq += (cvol_new - cvol_old) / dt * q * dx

    # Elliptic penalties
    penalty_dS, penalty_ds, D11, D12 = navier_stokes_stabilization_penalties(
        sim, nu, velocity_continuity_factor_D12, pressure_continuity_factor)
    yh = 1 / penalty_ds

    # Upwind and downwind velocities
    w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2.0
    w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2.0

    # Lagrange multiplicator to remove the pressure null space
    # ∫ p dx = 0
    if lm_trial is not None:
        eq = (p * lm_test + q * lm_trial) * dx

    # Momentum equations
    for d in range(sim.ndim):
        up = sim.data['up%d' % d]
        upp = sim.data['upp%d' % d]

        # Divergence free criterion
        # ∇⋅u = 0
        if incompressibility_flux_type == 'central':
            u_hat_p = avg(u[d])
        elif incompressibility_flux_type == 'upwind':
            assert use_grad_q_form, 'Upwind only implemented for grad_q_form'
            switch = dolfin.conditional(dolfin.gt(w_nU('+'), 0.0), 1.0, 0.0)
            u_hat_p = switch * u[d]('+') + (1 - switch) * u[d]('-')

        if use_grad_q_form:
            eq -= u[d] * q.dx(d) * dx
            eq += (u_hat_p + D12[d] * jump(u, n)) * jump(q) * n[d]('+') * dS
        else:
            eq += q * u[d].dx(d) * dx
            eq -= (avg(q) - dot(D12, jump(q, n))) * jump(u[d]) * n[d]('+') * dS

        # Time derivative
        # ∂(ρu)/∂t
        eq += rho * (c1 * u[d] + c2 * up + c3 * upp) / dt * v[d] * dx

        # Convection:
        # -w⋅∇(ρu)
        flux_nU = u[d] * w_nU
        flux = jump(flux_nU)
        eq -= u[d] * dot(grad(rho * v[d]), u_conv) * dx
        eq += flux * jump(rho * v[d]) * dS

        # Stabilizing term when w is not divergence free
        eq += 1 / 2 * div(u_conv) * u[d] * v[d] * dx

        # Diffusion:
        # -∇⋅μ∇u
        eq += mu * dot(grad(u[d]), grad(v[d])) * dx

        # Symmetric Interior Penalty method for -∇⋅μ∇u
        eq -= avg(mu) * dot(n('+'), avg(grad(u[d]))) * jump(v[d]) * dS
        eq -= avg(mu) * dot(n('+'), avg(grad(v[d]))) * jump(u[d]) * dS

        # Symmetric Interior Penalty coercivity term
        eq += penalty_dS * jump(u[d]) * jump(v[d]) * dS

        # -∇⋅μ(∇u)^T
        if use_stress_divergence_form:
            eq += mu * dot(u.dx(d), grad(v[d])) * dx
            eq -= avg(mu) * dot(n('+'), avg(u.dx(d))) * jump(v[d]) * dS
            eq -= avg(mu) * dot(n('+'), avg(v.dx(d))) * jump(u[d]) * dS

        # Pressure
        # ∇p
        if use_grad_p_form:
            eq += v[d] * p.dx(d) * dx
            eq -= (avg(v[d]) + D12[d] * jump(v, n)) * jump(p) * n[d]('+') * dS
        else:
            eq -= p * v[d].dx(d) * dx
            eq += (avg(p) - dot(D12, jump(p, n))) * jump(v[d]) * n[d]('+') * dS

        # Pressure continuity stabilization. Needed for equal order discretization
        if D11 is not None:
            eq += D11 * dot(jump(p, n), jump(q, n)) * dS

        # Body force (gravity)
        # ρ g
        eq -= rho * g[d] * v[d] * dx

        # Other sources
        for f in sim.data['momentum_sources']:
            eq -= f[d] * v[d] * dx

        # Penalty forcing zones
        for fz in sim.data['forcing_zones'].get('u', []):
            eq += fz.penalty * fz.beta * (u[d] - fz.target[d]) * v[d] * dx

        # Boundary conditions that do not couple the velocity components
        # The BCs are imposed for each velocity component u[d] separately

        eq += add_dirichlet_bcs(sim, d, u, p, v, q, rho, mu, n, w_nU, w_nD,
                                penalty_ds, use_grad_q_form, use_grad_p_form)

        eq += add_neumann_bcs(sim, d, u, p, v, q, rho, mu, n, w_nU, w_nD,
                              penalty_ds, use_grad_q_form, use_grad_p_form)

        eq += add_robin_bcs(sim, d, u, p, v, q, rho, mu, n, w_nU, w_nD, yh,
                            use_grad_q_form, use_grad_p_form)

        eq += add_outlet_bcs(sim, d, u, p, v, q, rho, mu, n, w_nU, w_nD, g, x,
                             use_grad_q_form, use_grad_p_form)

    # Boundary conditions that couple the velocity components
    # Decomposing the velocity into wall normal and parallel parts

    eq += add_slip_bcs(sim, u, p, v, q, rho, mu, n, w_nU, w_nD, penalty_ds,
                       use_grad_q_form, use_grad_p_form)

    return eq
Example #24
0
    def define_coupled_equation(self):
        """
        Setup the coupled Navier-Stokes equation

        This implementation assembles the full LHS and RHS each time they are needed
        """
        sim = self.simulation
        mpm = sim.multi_phase_model
        mesh = sim.data['mesh']
        Vcoupled = sim.data['Vcoupled']
        u_conv = sim.data['u_conv']

        # Unpack the coupled trial and test functions
        uc = dolfin.TrialFunction(Vcoupled)
        vc = dolfin.TestFunction(Vcoupled)
        ulist = []
        vlist = []
        sigmas, taus = [], []
        for d in range(sim.ndim):
            ulist.append(uc[d])
            vlist.append(vc[d])
            indices = list(
                range(1 + sim.ndim * (d + 1), 1 + sim.ndim * (d + 2)))
            sigmas.append([uc[i] for i in indices])
            taus.append([vc[i] for i in indices])

        u = dolfin.as_vector(ulist)
        v = dolfin.as_vector(vlist)
        p = uc[sim.ndim]
        q = vc[sim.ndim]
        sigma = dolfin.as_tensor(sigmas)
        tau = dolfin.as_tensor(taus)

        c1, c2, c3 = sim.data['time_coeffs']
        dt = sim.data['dt']
        g = sim.data['g']
        n = dolfin.FacetNormal(mesh)
        h = dolfin.FacetArea(mesh)

        # Fluid properties
        rho = mpm.get_density(0)
        mu = mpm.get_laminar_dynamic_viscosity(0)

        # Upwind and downwind velocities
        w_nU = (dot(u_conv, n) + abs(dot(u_conv, n))) / 2.0
        w_nD = (dot(u_conv, n) - abs(dot(u_conv, n))) / 2.0
        u_uw_s = dolfin.conditional(dolfin.gt(dot(u_conv, n), 0.0), 1.0,
                                    0.0)('+')
        u_uw = u_uw_s * u('+') + (1 - u_uw_s) * u('-')

        # LDG penalties
        # kappa_0 = Constant(4.0)
        # kappa = mu*kappa_0/h
        C11 = avg(mu / h)
        D11 = avg(h / mu)
        C12 = 0.2 * n('+')
        D12 = 0.2 * n('+')

        def ojump(v, n):
            return outer(v, n)('+') + outer(v, n)('-')

        # Interior facet fluxes
        # u_hat_dS = avg(u)
        # sigma_hat_dS = avg(sigma) - avg(kappa)*ojump(u, n)
        # p_hat_dS = avg(p)
        u_hat_s_dS = avg(u) + dot(ojump(u, n), C12)
        u_hat_p_dS = avg(u) + D11 * jump(p, n) + D12 * jump(u, n)
        sigma_hat_dS = avg(sigma) - C11 * ojump(u, n) - outer(
            jump(sigma, n), C12)
        p_hat_dS = avg(p) - dot(D12, jump(p, n))

        # Time derivative
        up = sim.data['up']
        upp = sim.data['upp']
        eq = rho * dot(c1 * u + c2 * up + c3 * upp, v) / dt * dx

        # LDG equation 1
        eq += inner(sigma, tau) * dx
        eq += dot(u, div(mu * tau)) * dx
        eq -= dot(u_hat_s_dS, jump(mu * tau, n)) * dS

        # LDG equation 2
        eq += (inner(sigma, grad(v)) - p * div(v)) * dx
        eq -= (inner(sigma_hat_dS, ojump(v, n)) - p_hat_dS * jump(v, n)) * dS
        eq -= dot(u, div(outer(v, rho * u_conv))) * dx
        eq += rho('+') * dot(u_conv('+'), n('+')) * dot(u_uw, v('+')) * dS
        eq += rho('-') * dot(u_conv('-'), n('-')) * dot(u_uw, v('-')) * dS
        momentum_sources = sim.data['momentum_sources'] + [rho * g]
        eq -= dot(sum(momentum_sources), v) * dx

        # LDG equation 3
        eq -= dot(u, grad(q)) * dx
        eq += dot(u_hat_p_dS, jump(q, n)) * dS

        # Dirichlet boundary
        dirichlet_bcs = get_collected_velocity_bcs(sim, 'dirichlet_bcs')
        for ds, u_bc in dirichlet_bcs.items():
            # sigma_hat_ds = sigma - kappa*outer(u, n)
            sigma_hat_ds = sigma - C11 * outer(u - u_bc, n)
            u_hat_ds = u_bc
            p_hat_ds = p

            # LDG equation 1
            eq -= dot(u_hat_ds, dot(mu * tau, n)) * ds

            # LDG equation 2
            eq -= (inner(sigma_hat_ds, outer(v, n)) -
                   p_hat_ds * dot(v, n)) * ds
            eq += rho * w_nU * dot(u, v) * ds
            eq += rho * w_nD * dot(u_bc, v) * ds

            # LDG equation 3
            eq += dot(u_hat_ds, q * n) * ds

        # Neumann boundary
        neumann_bcs = get_collected_velocity_bcs(sim, 'neumann_bcs')
        assert not neumann_bcs
        for ds, du_bc in neumann_bcs.items():
            # Divergence free criterion
            if self.use_grad_q_form:
                eq += q * dot(u, n) * ds
            else:
                eq -= q * dot(u, n) * ds

            # Convection
            eq += rho * w_nU * dot(u, v) * ds

            # Diffusion
            u_hat_ds = u
            sigma_hat_ds = outer(du_bc, n) / mu
            eq -= dot(u_hat_ds, dot(mu * tau, n)) * ds
            eq -= inner(sigma_hat_ds, outer(v, n)) * ds

            # Pressure
            if not self.use_grad_p_form:
                eq += p * dot(v, n) * ds

        a, L = dolfin.system(eq)
        self.form_lhs = a
        self.form_rhs = L
        self.tensor_lhs = None
        self.tensor_rhs = None