Пример #1
0
def test_lhs_rhs_simple():
    """Test taking lhs/rhs of DOLFIN specific forms (constants
    without cell). """

    mesh = RectangleMesh.create(MPI.comm_world,
                                [Point(0, 0), Point(2, 1)], [3, 5],
                                CellType.Type.triangle)
    V = FunctionSpace(mesh, "CG", 1)
    f = 2.0
    g = 3.0
    v = TestFunction(V)
    u = TrialFunction(V)

    F = inner(g * grad(f * v), grad(u)) * dx + f * v * dx
    a, L = system(F)

    Fl = lhs(F)
    Fr = rhs(F)
    assert (Fr)

    a0 = inner(grad(v), grad(u)) * dx

    n = assemble(a).norm("frobenius")  # noqa
    nl = assemble(Fl).norm("frobenius")  # noqa
    n0 = 6.0 * assemble(a0).norm("frobenius")  # noqa

    assert round(n - n0, 7) == 0
    assert round(n - nl, 7) == 0
    def variational_forms(self, dt: df.Constant) -> Tuple[Any, Any]:
        """Create the variational forms corresponding to the given
        discretization of the given system of equations.

        *Arguments*
          kn (:py:class:`ufl.Expr` or float)
            The time step

        *Returns*
          (lhs, rhs) (:py:class:`tuple` of :py:class:`ufl.Form`)

        """
        # Extract theta parameter and conductivities
        theta = self._parameters.theta
        Mi = self._intracellular_conductivity
        Me = self._extracellular_conductivity

        # Define variational formulation
        if self._parameters.linear_solver_type == "direct":
            v, u, multiplier = df.TrialFunctions(self._VUR)
            v_test, u_test, multiplier_test = df.TestFunctions(self._VUR)
        else:
            v, u = df.TrialFunctions(self._VUR)
            v_test, u_test = df.TestFunctions(self._VUR)

        Dt_v = (v - self._v_prev) / dt
        Dt_v *= self._chi_cm  # Chi is surface to volume aration. Cm is capacitance
        v_mid = theta * v + (1.0 - theta) * self._v_prev

        # Set-up measure and rhs from stimulus
        dOmega = df.Measure("dx",
                            domain=self._mesh,
                            subdomain_data=self._cell_function)
        dGamma = df.Measure("ds",
                            domain=self._mesh,
                            subdomain_data=self._interface_function)

        # Loop over all domains
        G = Dt_v * v_test * dOmega()
        for key in self._cell_tags - self._restrict_tags:
            G += df.inner(Mi[key] * df.grad(v_mid),
                          df.grad(v_test)) * dOmega(key)
            G += df.inner(Mi[key] * df.grad(v_mid),
                          df.grad(u_test)) * dOmega(key)

        for key in self._cell_tags:
            G += df.inner(Mi[key] * df.grad(u), df.grad(v_test)) * dOmega(key)
            G += df.inner((Mi[key] + Me[key]) * df.grad(u),
                          df.grad(u_test)) * dOmega(key)
            # If Lagrangian multiplier
            if self._parameters.linear_solver_type == "direct":
                G += (multiplier_test * u + multiplier * u_test) * dOmega(key)

        for key in set(self._interface_tags):
            # Default to 0 if not defined for tag
            G += self._neumann_bc.get(key,
                                      df.Constant(0)) * u_test * dGamma(key)

        a, L = df.system(G)
        return a, L
Пример #3
0
    def variational_forms(self, k_n: df.Constant):
        """Create the variational forms corresponding to the given
        discretization of the given system of equations.

        *Arguments*
          k_n (:py:class:`ufl.Expr` or float)
            The time step

        *Returns*
          (lhs, rhs, prec) (:py:class:`tuple` of :py:class:`ufl.Form`)
        """
        # Extract theta parameter and conductivities
        theta = self.parameters["theta"]
        M_i = self._M_i

        # Define variational formulation
        v = df.TrialFunction(self.V)
        w = df.TestFunction(self.V)

        chi = self.parameters["Chi"]
        capacitance = self.parameters["Cm"]
        lam = self.parameters["lambda"]
        lam_frac = df.Constant(lam / (1 + lam))

        # Set-up variational problem
        Dt_v_k_n = (v - self.v_) / k_n
        Dt_v_k_n *= chi * capacitance
        v_mid = theta * v + (1.0 - theta) * self.v_

        dz = df.Measure("dx",
                        domain=self._mesh,
                        subdomain_data=self._cell_domains)
        cell_tags = map(int, set(
            self._cell_domains.array()))  # np.int64 does not work

        # Currently not used
        db = df.Measure("ds",
                        domain=self._mesh,
                        subdomain_data=self._facet_domains)
        facet_tags = map(int, set(self._facet_domains.array()))

        prec = 0
        for key in cell_tags:
            G = Dt_v_k_n * w * dz(key)
            G += lam_frac * df.inner(M_i[key] * df.grad(v_mid),
                                     df.grad(w)) * dz(key)

            if self._I_s is None:
                G -= chi * df.Constant(0) * w * dz(key)
            else:
                G -= chi * self._I_s * w * dz(key)

        # Define preconditioner based on educated(?) guess by Marie
        prec += (v * w + k_n / 2.0 *
                 df.inner(M_i[key] * df.grad(v), df.grad(w))) * dz(key)

        a, L = df.system(G)
        return a, L, prec
Пример #4
0
def compare_split_matrices(eq, mat, vec, Wv, Wu, eps=1e-14):
    # Assemble coupled system
    M, v = dolfin.system(eq)
    M = assemble(M).array()
    v = assemble(v).get_local()
    Ni, Nj = mat.shape

    def compute_diff(coupled, split):
        diff = abs(coupled - split).flatten().max()
        return diff

    # Rebuild coupled system from split parts
    M2 = numpy.zeros_like(M)
    v2 = numpy.zeros_like(v)
    for i in range(Ni):
        dm_Wi = Wv.sub(i).dofmap()

        if vec[i] is not None:
            data = assemble(vec[i]).get_local()
            dm_Vi = vec[i].arguments()[0].function_space().dofmap()
            for cell in dolfin.cells(Wv.mesh()):
                dofs_Wi = dm_Wi.cell_dofs(cell.index())
                dofs_Vi = dm_Vi.cell_dofs(cell.index())
                v2[dofs_Wi] = data[dofs_Vi]

        dofs_i = dm_Wi.dofs()
        diff = compute_diff(v[dofs_i], v2[dofs_i])
        print(
            'Vector part %d has error %10.3e' % (i, diff), '<---' if diff > eps else ''
        )

        for j in range(Nj):
            dm_Wj = Wv.sub(j).dofmap()
            if mat[i, j] is not None:
                data = assemble(mat[i, j]).array()
                dm_Vi = mat[i, j].arguments()[0].function_space().dofmap()
                dm_Vj = mat[i, j].arguments()[1].function_space().dofmap()
                for cell in dolfin.cells(Wv.mesh()):
                    dofs_Wi = dm_Wi.cell_dofs(cell.index())
                    dofs_Wj = dm_Wj.cell_dofs(cell.index())
                    dofs_Vi = dm_Vi.cell_dofs(cell.index())
                    dofs_Vj = dm_Vj.cell_dofs(cell.index())
                    W_idx = numpy.ix_(dofs_Wi, dofs_Wj)
                    V_idx = numpy.ix_(dofs_Vi, dofs_Vj)
                    M2[W_idx] = data[V_idx]

            dofs_j = dm_Wj.dofs()
            idx = numpy.ix_(dofs_i, dofs_j)
            diff = compute_diff(M[idx], M2[idx])
            print(
                'Matrix part (%d, %d) has error %10.3e' % (i, j, diff),
                '<---' if diff > eps else '',
            )

    # Check that the original and rebuilt systems are identical
    assert compute_diff(M, M2) < eps
    assert compute_diff(v, v2) < eps
Пример #5
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
        """
        Vcoupled = self.simulation.data['Vcoupled']

        # Unpack the coupled trial and test functions
        uc = dolfin.TrialFunction(Vcoupled)
        vc = dolfin.TestFunction(Vcoupled)
        ulist = []
        vlist = []
        ndim = self.simulation.ndim
        for d in range(ndim):
            ulist.append(uc[d])
            vlist.append(vc[d])

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

        lm_trial = lm_test = None
        if self.use_lagrange_multiplicator:
            lm_trial = uc[ndim + 1]
            lm_test = vc[ndim + 1]

        assert self.flux_type == UPWIND
        eq = define_dg_equations(
            u,
            v,
            p,
            q,
            lm_trial,
            lm_test,
            self.simulation,
            include_hydrostatic_pressure=self.include_hydrostatic_pressure,
            incompressibility_flux_type=self.incompressibility_flux_type,
            use_grad_q_form=self.use_grad_q_form,
            use_grad_p_form=self.use_grad_p_form,
            use_stress_divergence_form=self.use_stress_divergence_form,
            velocity_continuity_factor_D12=self.velocity_continuity_factor_D12,
            pressure_continuity_factor=self.pressure_continuity_factor,
        )

        a, L = dolfin.system(eq)
        self.form_lhs = a
        self.form_rhs = L
        self.tensor_lhs = None
        self.tensor_rhs = None
    def _variational_forms(self) -> Tuple[Any, Any]:
        # Localise variables for convenicence
        dt = self._timestep
        theta = self._parameters.theta
        Mi = self._conductivity
        lbda = self._lambda

        dOmega = self._dOmega
        dGamma = self._dGamma

        v = self._v_trial
        v_test = self._v_test

        # Set-up variational problem
        dvdt = (v - self._v_prev) / dt
        dvdt *= self._chi_cm
        v_mid = theta * v + (1.0 - theta) * self._v_prev

        # Cell contributions
        Form = dvdt * v_test * dOmega()
        for cell_tag in filter(lambda x: x is not None, self._cell_tags):
            factor = lbda[cell_tag] / (1 + lbda[cell_tag])
            Form += factor * df.inner(Mi[cell_tag] * df.grad(v_mid),
                                      df.grad(v_test)) * dOmega(cell_tag)

        # Boundary contributions
        for interface_tag in filter(lambda x: x is not None,
                                    self._interface_tags):
            neumann_bc = self._neumann_boundary_condition.get(
                interface_tag, df.Constant(0))
            neumann_bc = neumann_bc * v_test * dGamma(interface_tag)
            Form += neumann_bc

        # Interface conditions
        # csf_tag = self._cell_tags.CSF
        # gm_tag = self._cell_tags.GM
        # csf_gm_interface_tag = self._interface_tags.CSF_GM
        # interface_contribution = df.inner(Mi[csf_tag]*df.grad(v), Mi[gm_tag]/(1 + lbda[gm_tag])*df.grad(v))
        # interface_contribution *= dGamma(csf_gm_interface_tag)
        # Form += interface_contribution

        # rhs   # TODO: This is not necessary
        # Form += df.Constant(0)*v_test*dOmega

        a, L = df.system(Form)
        return a, L
    def step(self, solution_fields):
        """
        Solve on the given time interval (t0, t1).

        *Arguments*
          interval (:py:class:`tuple`)
            The time interval (t0, t1) for the step

        *Invariants*
          Assuming that v\_ is in the correct state for t0, gives
          self.vur in correct state at t1.
        """

        # Define variational problem
        a, L = system(self._form)
        problem = LinearVariationalProblem(a, L, solution_fields)

        # Set-up solver
        solver = LinearVariationalSolver(problem)
        solver.parameters.update(self.parameters)
        solver.solve()
Пример #8
0
    def define_momentum_equation(self):
        """
        Setup the momentum equation weak form
        """
        sim = self.simulation
        Vuvw = sim.data['uvw_star'].function_space()
        tests = dolfin.TestFunctions(Vuvw)
        trials = dolfin.TrialFunctions(Vuvw)

        # Split into components
        v = dolfin.as_vector(tests[:])
        u = dolfin.as_vector(trials[:])

        # The pressure is explicit p* and q is zero (on a domain, to avoid warnings)
        p = sim.data['p']

        class MyZero(Zero):
            def ufl_domains(self):
                return p.ufl_domains()

        q = MyZero()

        lm_trial = lm_test = None

        # Define the momentum equation weak form
        eq = define_dg_equations(
            u,
            v,
            p,
            q,
            lm_trial,
            lm_test,
            self.simulation,
            include_hydrostatic_pressure=self.include_hydrostatic_pressure,
            incompressibility_flux_type='central',  # Only used with q
            use_grad_q_form=False,  # Only used with q
            use_grad_p_form=self.use_grad_p_form,
            use_stress_divergence_form=self.use_stress_divergence_form,
        )
        self.form_lhs, self.form_rhs = dolfin.system(eq)
Пример #9
0
def setup_S(w_S, u, p, v, q, p0, q0, dx, ds, normal, dirichlet_bcs,
            neumann_bcs, boundary_to_mark, u_1, phi_, rho_, rho_1, g_, M_, mu_,
            rho_e_, c_, V_, c_1, V_1, dbeta, solutes, per_tau, drho, sigma_bar,
            eps, dveps, grav, fric, u_comoving, enable_PF, enable_EC,
            use_iterative_solvers, use_pressure_stabilization, p_lagrange,
            q_rhs):
    """ Set up Stokes subproblem """
    F = (per_tau * rho_1 * df.dot(u - u_1, v) * dx +
         mu_ * df.inner(df.grad(u), df.grad(v)) * dx - p * df.div(v) * dx +
         q * df.div(u) * dx - rho_ * df.dot(grav, v) * dx)

    print("Linear system size", w_S.function_space().dim())

    a, L = df.system(F)
    problem = df.LinearVariationalProblem(a, L, w_S, dirichlet_bcs)
    solver = df.LinearVariationalSolver(problem)

    solver.parameters["linear_solver"] = "mumps"

    if use_iterative_solvers:
        solver.parameters["linear_solver"] = "gmres"
        solver.parameters["preconditioner"] = "amg"

    return solver
Пример #10
0
    def variational_forms(self, kn: df.Constant) -> tp.Tuple[tp.Any, tp.Any]:
        """Create the variational forms corresponding to the given
        discretization of the given system of equations.

        *Arguments*
          kn (:py:class:`ufl.Expr` or float)
            The time step

        *Returns*
          (lhs, rhs) (:py:class:`tuple` of :py:class:`ufl.Form`)

        """
        # Extract theta parameter and conductivities
        theta = self._parameters["theta"]
        Mi = self._M_i
        Me = self._M_e

        # Define variational formulation
        if self._parameters["linear_solver_type"] == "direct":
            v, u, l = df.TrialFunctions(self.VUR)
            w, q, lamda = df.TestFunctions(self.VUR)
        else:
            v, u = df.TrialFunctions(self.VUR)
            w, q = df.TestFunctions(self.VUR)

        # Get physical parameters
        chi = self._parameters["Chi"]
        capacitance = self._parameters["Cm"]

        Dt_v = (v - self.v_) / kn
        Dt_v *= chi * capacitance
        v_mid = theta * v + (1.0 - theta) * self.v_

        # Set-up measure and rhs from stimulus
        dz = df.Measure("dx",
                        domain=self._mesh,
                        subdomain_data=self._cell_domains)
        db = df.Measure("ds",
                        domain=self._mesh,
                        subdomain_data=self._facet_domains)

        # Get domain tags
        cell_tags = map(int, set(
            self._cell_domains.array()))  # np.int64 does not work
        facet_tags = map(int, set(self._facet_domains.array()))

        # Loop over all domains
        G = Dt_v * w * dz()
        for key in cell_tags:
            G += df.inner(Mi[key] * df.grad(v_mid), df.grad(w)) * dz(key)
            G += df.inner(Mi[key] * df.grad(u), df.grad(w)) * dz(key)
            G += df.inner(Mi[key] * df.grad(v_mid), df.grad(q)) * dz(key)
            G += df.inner(
                (Mi[key] + Me[key]) * df.grad(u), df.grad(q)) * dz(key)

            if self._I_s is None:
                G -= chi * df.Constant(0) * w * dz(key)
            else:
                _is = self._I_s.get(key, df.Constant(0))
                G -= chi * _is * w * dz(key)

            # If Lagrangian multiplier
            if self._parameters["linear_solver_type"] == "direct":
                G += (lamda * u + l * q) * dz(key)

            if self._I_a:
                G -= chi * self._I_a[key] * q * dz(key)

        for key in facet_tags:
            if self._ect_current is not None:
                # Default to 0 if not defined for tag I do not I should apply `chi` here.
                G += self._ect_current.get(key, df.Constant(0)) * q * db(key)

        a, L = df.system(G)
        return a, L
Пример #11
0
    def step(self, t0: float, t1: float) -> None:
        """Solve on the given time interval (t0, t1).

        Arguments:
            interval (:py:class:`tuple`)
                The time interval (t0, t1) for the step

        *Invariants*
            Assuming that v\_ is in the correct state for t0, gives
            self.vur in correct state at t1.
        """
        timer = df.Timer("PDE step")

        # Extract theta and conductivities
        theta = self._parameters["theta"]
        Mi = self._M_i
        Me = self._M_e

        # Extract interval and thus time-step
        kn = df.Constant(t1 - t0)

        # Define variational formulation
        if self._parameters["linear_solver_type"] == "direct":
            v, u, l = df.TrialFunctions(self.VUR)
            w, q, lamda = df.TestFunctions(self.VUR)
        else:
            v, u = df.TrialFunctions(self.VUR)
            w, q = df.TestFunctions(self.VUR)

        # Get physical parameters
        chi = self._parameters["Chi"]
        capacitance = self._parameters["Cm"]

        Dt_v = (v - self.v_) / kn
        Dt_v *= chi * capacitance
        v_mid = theta * v + (1.0 - theta) * self.v_

        # Set time
        t = t0 + theta * (t1 - t0)
        self.time.assign(t)

        # Define spatial integration domains:
        dz = df.Measure("dx",
                        domain=self._mesh,
                        subdomain_data=self._cell_domains)
        db = df.Measure("ds",
                        domain=self._mesh,
                        subdomain_data=self._facet_domains)

        # Get domain labels
        cell_tags = map(int, set(
            self._cell_domains.array()))  # np.int64 does not workv
        facet_tags = map(int, set(self._facet_domains.array()))

        # Loop overe all domain labels
        G = Dt_v * w * dz()
        for key in cell_tags:
            G += df.inner(Mi[key] * df.grad(v_mid), df.grad(w)) * dz(key)
            G += df.inner(Mi[key] * df.grad(u), df.grad(w)) * dz(key)
            G += df.inner(Mi[key] * df.grad(v_mid), df.grad(q)) * dz(key)
            G += df.inner(
                (Mi[key] + Me[key]) * df.grad(u), df.grad(q)) * dz(key)

            if self._I_s is None:
                G -= chi * df.Constant(0) * w * dz(key)
            else:
                # _is = self._I_s.get(key, df.Constant(0))
                # G -= chi*_is*w*dz(key)
                G -= chi * self._I_s[key] * w * dz(key)

            # If Lagrangian multiplier
            if self._parameters["linear_solver_type"] == "direct":
                G += (lamda * u + l * q) * dz(key)

            # Add applied current as source in elliptic equation if applicable
            if self._I_a:
                G -= chi * self._I_a[key] * q * dz(key)

        if self._ect_current is not None:
            for key in facet_tags:
                # Detfalt to 0 if not defined for that facet tag
                # TODO: Should I include `chi` here? I do not think so
                G += self._ect_current.get(key, df.Constant(0)) * q * db(key)

        # Define variational problem
        a, L = df.system(G)
        pde = df.LinearVariationalProblem(a, L, self.vur, bcs=self._bcs)

        # Set-up solver
        solver = df.LinearVariationalSolver(pde)
        solver.solve()
Пример #12
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)
Пример #13
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
        for d in range(ndim):
            ulist.append(uc[d])
            vlist.append(vc[d])

        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)

        # Hydrostatic pressure correction
        if self.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 these lines
            # 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)
            cvol_new = dolfin.CellVolume(mesh)
            cvol_old = sim.data['cvolp']
            eq += (cvol_new - cvol_old) / dt * q * dx

        # Lagrange multiplicator to remove the pressure null space
        # ∫ p dx = 0
        if self.use_lagrange_multiplicator:
            lm_trial = uc[ndim + 1]
            lm_test = vc[ndim + 1]
            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
            eq += u[d].dx(d) * q * dx

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

            # Convection
            # ∇⋅(ρ u ⊗ u_conv)
            eq += rho * dot(u_conv, grad(u[d])) * v[d] * dx

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

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

            # Pressure
            # ∇p
            eq -= v[d].dx(d) * p * dx

            # 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

            # Neumann boundary conditions
            neumann_bcs_pressure = sim.data['neumann_bcs'].get('p', [])
            for nbc in neumann_bcs_pressure:
                eq += p * v[d] * n[d] * nbc.ds()

            # Outlet boundary
            for obc in sim.data['outlet_bcs']:
                # Diffusion
                mu_dudn = p * n[d]
                eq -= mu_dudn * v[d] * obc.ds()

                # Pressure
                p_ = mu * dot(dot(grad(u), n), n)
                eq += p_ * v[d] * n[d] * obc.ds()

        a, L = dolfin.system(eq)
        self.form_lhs = a
        self.form_rhs = L
        self.tensor_lhs = None
        self.tensor_rhs = None
Пример #14
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
Пример #15
0
    def step(self, t0: float, t1: float) -> None:
        r"""Solve on the given time interval (t0, t1).

        *Arguments*
          interval (:py:class:`tuple`)
            The time interval (t0, t1) for the step

        *Invariants*
          Assuming that v\_ is in the correct state for t0, gives
          self.v in correct state at t1.
        """
        # Extract interval and thus time-step
        k_n = df.Constant(t1 - t0)

        # Extract theta parameter and conductivities
        theta = self.parameters["theta"]
        M_i = self._M_i

        # Set time
        t = t0 + theta * (t1 - t0)
        self.time.assign(t)

        # Get physical parameters
        chi = self.parameters["Chi"]
        capacitance = self.parameters["Cm"]
        lam = self.parameters["lambda"]
        lam_frac = df.Constant(lam / (1 + lam))

        # Define variational formulation
        v = df.TrialFunction(self.V)
        w = df.TestFunction(self.V)
        Dt_v_k_n = (v - self.v_) / k_n
        Dt_v_k_n *= chi * capacitance
        v_mid = theta * v + (1.0 - theta) * self.v_

        dz = df.Measure("dx",
                        domain=self._mesh,
                        subdomain_data=self._cell_domains)
        db = df.Measure("ds",
                        domain=self._mesh,
                        subdomain_data=self._facet_domains)
        # dz, rhs = rhs_with_markerwise_field(self._I_s, self._mesh, w)
        cell_tags = map(int, set(
            self._cell_domains.array()))  # np.int64 does not work
        facet_tags = map(int, set(self._facet_domains.array()))

        for key in cell_tags:
            G = Dt_v_k_n * w * dz(key)
            G += lam_frac * df.inner(M_i[key] * df.grad(v_mid),
                                     df.grad(w)) * dz(key)

            if self._I_s is None:
                G -= chi * df.Constant(0) * w * dz(key)
            else:
                G -= chi * self._I_s * w * dz(key)

        # Define variational problem
        a, L = df.system(G)
        pde = df.LinearVariationalProblem(a, L, self.v)

        # Set-up solver
        solver_type = self.parameters["linear_solver_type"]
        solver = df.LinearVariationalSolver(pde)
        solver.solve()
Пример #16
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
Пример #17
0
    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