Esempio n. 1
0
    def add_dirichlet(self, g, surface):
        """
        Adds dirichlet boundary conditions to the problem.

        Args:
            g (Function, int, or float):
                The function to apply on the boundary.
            surface (int or list of int):
                The index of the boundary to apply the condition to.
        """
        # Explicitly check against False as None should not be caught here.
        if self._has_boundary is False:
            raise AttributeError('Cannot add boundary after declaring that '
                                 'there are no boundaries')
        norm = FacetNormal(self.mesh)

        if isinstance(g, (float, int)):
            g = Constant(g)

        integrand = -1 * self.v * dot(self.q, norm)

        if surface == 'all':
            dbc = DirichletBC(V=self.V, g=g, sub_domain="on_boundary")
            self.a += integrand * ds
        else:
            dbc = DirichletBC(V=self.V, g=g, sub_domain=surface)
            try:
                self.a += sum(integrand * ds(s) for s in surface)
            except TypeError:
                self.a += integrand * ds(surface)

        self.bcs.append(dbc)
        self._has_boundary = True
Esempio n. 2
0
        def mult(self, mat, x, y):
            # Copy function data into the fivredrake function
            self.x_fntn.dat.data[:] = x[:]
            # Transfer the function to meshmode
            self.meshmode_src_connection.from_firedrake(project(
                self.x_fntn, dgfspace),
                                                        out=self.x_mm_fntn)
            # Restrict to boundary
            x_mm_fntn_on_bdy = src_bdy_connection(self.x_mm_fntn)

            # Apply the operation
            potential_int_mm = self.pyt_op(self.actx,
                                           u=x_mm_fntn_on_bdy,
                                           k=self.k)
            grad_potential_int_mm = self.pyt_grad_op(self.actx,
                                                     u=x_mm_fntn_on_bdy,
                                                     k=self.k)
            # Store in firedrake
            self.potential_int.dat.data[target_indices] = potential_int_mm.get(
            )
            for dim in range(grad_potential_int_mm.shape[0]):
                self.grad_potential_int.dat.data[
                    target_indices, dim] = grad_potential_int_mm[dim].get()

            # Integrate the potential
            r"""
            Compute the inner products using firedrake. Note this
            will be subtracted later, hence appears off by a sign.

            .. math::

                \langle
                    n(x) \cdot \nabla(
                        \int_\Gamma(
                            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                        )d\gamma(y)
                    ), v
                \rangle_\Sigma
                - \langle
                    i \kappa \cdot
                    \int_\Gamma(
                        u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                    )d\gamma(y), v
                \rangle_\Sigma
            """
            self.pyt_result = assemble(
                inner(inner(self.grad_potential_int, self.n), self.v) *
                ds(outer_bdy_id) -
                inner(self.potential_int, self.v) * ds(outer_bdy_id))

            # y <- Ax - evaluated potential
            self.A.mult(x, y)
            with self.pyt_result.dat.vec_ro as ep:
                y.axpy(-1, ep)
Esempio n. 3
0
    def action(self, **kwargs):
        r"""Return the action functional that gives the ice shelf diagnostic
        model as the Euler-Lagrange equations

        The action functional for the ice shelf diagnostic model is

        .. math::
            E(u) = \int_\Omega\left(\frac{n}{n + 1}hM:\dot\varepsilon
            - \frac{1}{2}\varrho gh^2\nabla\cdot u\right)dx

        where :math:`u` is the velocity, :math:`h` is the ice thickness,
        :math:`\dot\varepsilon` is the strain-rate tensor, and :math:`M` is
        the membrane stress tensor.

        Parameters
        ----------
        u : firedrake.Function
            ice velocity
        h : firedrake.Function
            ice thickness

        Returns
        -------
        E : firedrake.Form
            the ice shelf action functional

        Other parameters
        ----------------
        **kwargs
            All other keyword arguments will be passed on to the viscosity
            and gravity functionals. The ice fluidity coefficient, for
            example, is passed as a keyword argument.
        """
        u = kwargs.get('velocity', kwargs.get('u'))
        mesh = u.ufl_domain()
        ice_front_ids = tuple(kwargs.pop('ice_front_ids', ()))
        side_wall_ids = tuple(kwargs.pop('side_wall_ids', ()))

        viscosity = self.viscosity(**kwargs) * dx
        gravity = self.gravity(**kwargs) * dx

        ds_w = ds(domain=mesh, subdomain_id=side_wall_ids)
        side_friction = self.side_friction(**kwargs) * ds_w
        penalty = self.penalty(**kwargs) * ds_w

        ds_t = ds(domain=mesh, subdomain_id=ice_front_ids)
        terminus = self.terminus(**kwargs) * ds_t

        return viscosity + side_friction - gravity - terminus + penalty
Esempio n. 4
0
    def action(self, **kwargs):
        r"""Return the action functional that gives the ice stream
        diagnostic model as the Euler-Lagrange equations"""
        u = kwargs["velocity"]
        mesh = u.ufl_domain()
        ice_front_ids = tuple(kwargs.pop("ice_front_ids", ()))
        side_wall_ids = tuple(kwargs.pop("side_wall_ids", ()))

        metadata = {"quadrature_degree": self.quadrature_degree(**kwargs)}
        dx = firedrake.dx(metadata=metadata)
        ds = firedrake.ds(domain=mesh, metadata=metadata)

        viscosity = self.viscosity(**kwargs) * dx
        friction = self.friction(**kwargs) * dx
        gravity = self.gravity(**kwargs) * dx

        side_friction = self.side_friction(**kwargs) * ds(side_wall_ids)
        if get_mesh_axes(mesh) == "xy":
            penalty = self.penalty(**kwargs) * ds(side_wall_ids)
        else:
            penalty = 0.0

        terminus = self.terminus(**kwargs) * ds(ice_front_ids)

        return viscosity + friction + side_friction - gravity - terminus + penalty
Esempio n. 5
0
def test__heat_driven_cavity_with_water(tmpdir):

    sim = sapphire.simulations.examples.\
        heat_driven_cavity_with_water.Simulation(
            mesh_dimensions = (20, 20),
            element_degrees = (1, 2, 2),
            output_directory_path = tmpdir)

    sim.solution = sim.solve_with_continuation_on_grashof_number()

    p, u, T = sim.solution.split()

    fe.tricontourf(T)

    fe.quiver(u)

    filepath = tmpdir + "/T_and_u.png"

    print("Writing plot to {}".format(filepath))

    matplotlib.pyplot.savefig(filepath)

    dot, grad = fe.dot, fe.grad

    ds = fe.ds(subdomain_id=sim.coldwall_id)

    nhat = fe.FacetNormal(sim.mesh)

    p, u, T = fe.split(sim.solution)

    coldwall_heatflux = fe.assemble(dot(grad(T), nhat) * ds)

    print("Integrated cold wall heat flux = {}".format(coldwall_heatflux))

    assert (round(coldwall_heatflux, 0) == -8.)
Esempio n. 6
0
def terminus(u, h, s, ice_front_ids=()):
    r"""Return the terminal stress part of the ice stream action functional

    The power exerted due to stress at the ice calving terminus :math:`\Gamma`
    is

    .. math::
       E(u) = \int_\Gamma\left(\frac{1}{2}\rho_Igh^2 - \rho_Wgd^2\right)
       u\cdot \nu\hspace{2pt}ds

    where :math:`d` is the water depth at the terminus. We assume that sea
    level is at :math:`z = 0` for purposes of calculating the water depth.

    Parameters
    ----------
    u : firedrake.Function
        ice velocity
    h : firedrake.Function
        ice thickness
    s : firedrake.Function
        ice surface elevation
    ice_front_ids : list of int
        numeric IDs of the parts of the boundary corresponding to the
        calving front
    """
    from firedrake import conditional, lt
    d = conditional(lt(s - h, 0), s - h, 0)

    τ_I = ρ_I * g * h**2 / 2
    τ_W = ρ_W * g * d**2 / 2

    ν = firedrake.FacetNormal(u.ufl_domain())
    return (τ_I - τ_W) * inner(u, ν) * ds(tuple(ice_front_ids))
Esempio n. 7
0
    def __init__(self, *args, meshsize, **kwargs):

        self.hot_wall_temperature = fe.Constant(1.)

        self.cold_wall_temperature = fe.Constant(-0.01)

        self.topwall_heatflux = fe.Constant(0.)

        super().__init__(
            *args,
            mesh=fe.UnitSquareMesh(meshsize, meshsize),
            initial_values=initial_values,
            dirichlet_boundary_conditions=dirichlet_boundary_conditions,
            **kwargs)

        q = self.topwall_heatflux

        _, _, psi_T = fe.TestFunctions(self.function_space)

        ds = fe.ds(domain=self.mesh, subdomain_id=4)

        self.variational_form_residual += psi_T * q * ds

        Ra = 3.27e5

        Pr = 56.2

        self.grashof_number = self.grashof_number.assign(Ra / Pr)

        self.prandtl_number = self.prandtl_number.assign(Pr)

        self.stefan_number = self.stefan_number.assign(0.045)

        self.liquidus_temperature = self.liquidus_temperature.assign(0.)
    def test_update_a_multiple_surfaces(self):
        """
        Test a is updated correctly for a with a multiple surfaces.
        """
        T = self.problem.T
        v = self.problem.v
        q = self.problem.q
        self.problem.a = T * dx

        g = Function(self.V)
        expected_a = T * dx + -1 * v * dot(q, self.norm) * ds(0)
        expected_a += -1 * v * dot(q, self.norm) * ds(1)

        self.problem.add_dirichlet(g, [0, 1])

        self.assertEqual(expected_a, self.problem.a)
    def test_updates_multiple_surfaces(self):
        """
        Test that a and L are set correctly for two surfaces.
        """
        T = self.problem.T
        v = self.problem.v
        self.problem.a = T * dx
        self.problem.L = T * dx

        alpha = Function(self.V)
        g = Function(self.V)
        expected_a = T * dx + alpha * v * T * ds(0) + alpha * v * T * ds(1)
        expected_L = T * dx + v * g * ds(0) + v * g * ds(1)

        self.problem.add_robin(alpha, g, [0, 1])

        self.assertEqual(expected_a, self.problem.a)
        self.assertEqual(expected_L, self.problem.L)
    def test_updates_single_surface(self):
        """
        Test that a and L are set correctly for a single surface.
        """
        T = self.problem.T
        v = self.problem.v
        self.problem.a = T * dx
        self.problem.L = T * dx

        alpha = Function(self.V)
        g = Function(self.V)
        expected_a = T * dx + alpha * v * T * ds(0)
        expected_L = T * dx + v * g * ds(0)

        self.problem.add_robin(alpha, g, 0)

        self.assertEqual(expected_a, self.problem.a)
        self.assertEqual(expected_L, self.problem.L)
Esempio n. 11
0
        def mult(self, mat, x, y):
            # Perform pytential operation
            self.x_fntn.dat.data[:] = x[:]

            self.pyt_op(self.queue,
                        self.potential_int,
                        u=self.x_fntn,
                        k=self.k)
            self.pyt_grad_op(self.queue,
                             self.grad_potential_int,
                             u=self.x_fntn,
                             k=self.k)

            # Integrate the potential
            r"""
            Compute the inner products using firedrake. Note this
            will be subtracted later, hence appears off by a sign.

            .. math::

                \langle
                    n(x) \cdot \nabla(
                        \int_\Gamma(
                            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                        )d\gamma(y)
                    ), v
                \rangle_\Sigma
                - \langle
                    i \kappa \cdot
                    \int_\Gamma(
                        u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                    )d\gamma(y), v
                \rangle_\Sigma
            """
            self.pyt_result = assemble(
                inner(inner(self.grad_potential_int, self.n), self.v) *
                ds(outer_bdy_id) -
                inner(self.potential_int, self.v) * ds(outer_bdy_id))

            # y <- Ax - evaluated potential
            self.A.mult(x, y)
            with self.pyt_result.dat.vec_ro as ep:
                y.axpy(-1, ep)
Esempio n. 12
0
    def action(self, u, h, s, **kwargs):
        r"""Return the action functional that gives the ice stream
        diagnostic model as the Euler-Lagrange equations"""
        mesh = u.ufl_domain()
        ice_front_ids = tuple(kwargs.pop('ice_front_ids', ()))
        side_wall_ids = tuple(kwargs.pop('side_wall_ids', ()))

        viscosity = self.viscosity(u=u, h=h, s=s, **kwargs) * dx
        friction = self.friction(u=u, h=h, s=s, **kwargs) * dx
        gravity = self.gravity(u=u, h=h, s=s, **kwargs) * dx

        ds_w = ds(domain=mesh, subdomain_id=side_wall_ids)
        side_friction = self.side_friction(u=u, h=h, s=s, **kwargs) * ds_w
        penalty = self.penalty(u=u, h=h, s=s, **kwargs) * ds_w

        ds_t = ds(domain=mesh, subdomain_id=ice_front_ids)
        terminus = self.terminus(u=u, h=h, s=s, **kwargs) * ds_t

        return (viscosity + friction + side_friction - gravity - terminus +
                penalty)
Esempio n. 13
0
    def dgls_form(self, problem, mesh, bcs_p):
        rho = problem.rho
        mu = problem.mu
        k = problem.k
        f = problem.f

        q, p = fire.TrialFunctions(self._W)
        w, v = fire.TestFunctions(self._W)

        n = fire.FacetNormal(mesh)
        h = fire.CellDiameter(mesh)

        # Stabilizing parameters
        has_mesh_characteristic_length = True
        delta_0 = fire.Constant(1)
        delta_1 = fire.Constant(-1 / 2)
        delta_2 = fire.Constant(1 / 2)
        delta_3 = fire.Constant(1 / 2)
        eta_p = fire.Constant(100)
        eta_q = fire.Constant(100)
        h_avg = (h("+") + h("-")) / 2.0
        if has_mesh_characteristic_length:
            delta_2 = delta_2 * h * h
            delta_3 = delta_3 * h * h

        kappa = rho * k / mu
        inv_kappa = 1.0 / kappa

        # Classical mixed terms
        a = (dot(inv_kappa * q, w) - div(w) * p - delta_0 * v * div(q)) * dx
        L = -delta_0 * f * v * dx

        # DG terms
        a += jump(w, n) * avg(p) * dS - avg(v) * jump(q, n) * dS

        # Edge stabilizing terms
        a += (eta_q * h_avg) * avg(inv_kappa) * (
            jump(q, n) * jump(w, n)) * dS + (eta_p / h_avg) * avg(kappa) * dot(
                jump(v, n), jump(p, n)) * dS

        # Add the contributions of the pressure boundary conditions to L
        for pboundary, iboundary in bcs_p:
            L -= pboundary * dot(w, n) * ds(iboundary)

        # Stabilizing terms
        a += (delta_1 * inner(kappa * (inv_kappa * q + grad(p)),
                              delta_0 * inv_kappa * w + grad(v)) * dx)
        a += delta_2 * inv_kappa * div(q) * div(w) * dx
        a += delta_3 * inner(kappa * curl(inv_kappa * q), curl(
            inv_kappa * w)) * dx
        L += delta_2 * inv_kappa * f * div(w) * dx

        return a, L
Esempio n. 14
0
def _boundary_flux(z, h_ext, q_ext, g, boundary_ids):
    Z = z.function_space()
    n = firedrake.FacetNormal(Z.mesh())
    φ, v = firedrake.TestFunctions(Z)

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

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

    return 0.5 * (inner(F_hx, φ * n) + inner(F_qx, outer(v, n)) + inner(
        F_h, φ * n) + inner(F_q, outer(v, n))) * ds(boundary_ids)
Esempio n. 15
0
    def add_robin(self, alpha, g, surface):
        """
        Adds robin boundary conditions to the problem.

        The Robin condition is a mixed codition and is defined by:
            dot(grad(T), n) = g - alpha*T

        Args:
            g (Function, int, or float):
                The function to apply on the boundary as per the above formula.
            alpha (Function, int, or float):
                The function to apply on the boundary as per the above formula.
            surface (int or list of int):
                The index of the boundary to apply the condition to.
        """
        # Explicitly check against False as None should not be caught here.
        if self._has_boundary is False:
            raise AttributeError('Cannot add boundary after declaring that '
                                 'there are no boundaries')

        if isinstance(g, (float, int)):
            g = Constant(g)
        if isinstance(alpha, (float, int)):
            alpha = Constant(alpha)

        a_integrand = alpha * self.v * self.T
        L_integrand = self.v * g

        if surface == 'all':
            self.a += a_integrand * ds
            self.L += L_integrand * ds
        else:
            try:
                self.a += sum(a_integrand * ds(s) for s in surface)
                self.L += sum(L_integrand * ds(s) for s in surface)
            except TypeError:
                self.a += a_integrand * ds(surface)
                self.L += L_integrand * ds(surface)

        self._has_boundary = True
    def test_update_a_single_surface(self):
        """
        Test a is updated correctly for a with a single surface.
        """
        T = self.problem.T
        v = self.problem.v
        q = self.problem.q
        self.problem.a = T * dx

        g = Function(self.V)
        expected_a = T * dx + -1 * v * dot(q, self.norm) * ds(0)

        self.problem.add_dirichlet(g, 0)

        self.assertEqual(expected_a, self.problem.a)
Esempio n. 17
0
def form2indicator(F):
    """
    Multiply throughout in a form and
    assemble as a cellwise error
    indicator.

    :arg F: the form
    """
    mesh = F.ufl_domain()
    P0 = firedrake.FunctionSpace(mesh, "DG", 0)
    p0test = firedrake.TestFunction(P0)
    indicator = firedrake.Function(P0)

    # Contributions from surface integrals
    flux_terms = 0
    integrals = F.integrals_by_type("exterior_facet")
    if len(integrals) > 0:
        for integral in integrals:
            ds = firedrake.ds(integral.subdomain_id())
            flux_terms += p0test * integral.integrand() * ds
    integrals = F.integrals_by_type("interior_facet")
    if len(integrals) > 0:
        for integral in integrals:
            dS = firedrake.dS(integral.subdomain_id())
            flux_terms += p0test("+") * integral.integrand() * dS
            flux_terms += p0test("-") * integral.integrand() * dS
    if flux_terms != 0:
        dx = firedrake.dx
        mass_term = firedrake.TrialFunction(P0) * p0test * dx
        sp = {
            "snes_type": "ksponly",
            "ksp_type": "preonly",
            "pc_type": "jacobi",
        }
        firedrake.solve(mass_term == flux_terms,
                        indicator,
                        solver_parameters=sp)

    # Contributions from volume integrals
    cell_terms = 0
    integrals = F.integrals_by_type("cell")
    if len(integrals) > 0:
        for integral in integrals:
            dx = firedrake.dx(integral.subdomain_id())
            cell_terms += p0test * integral.integrand() * dx
    indicator += firedrake.assemble(cell_terms)

    return indicator
Esempio n. 18
0
def terminus(u, h, ice_front_ids=()):
    r"""Return the terminus stress part of the ice shelf action functional

    The power exerted due to stress at the calving terminus :math:`\Gamma` is

    .. math::
       E(u) = \int_\Gamma\varrho gh^2u\cdot\nu\hspace{2pt}ds

    We assume that sea level is at :math:`z = 0` for calculating the water
    depth.
    """
    mesh = u.ufl_domain()
    ν = firedrake.FacetNormal(mesh)
    IDs = tuple(ice_front_ids)
    ρ = ρ_I * (1 - ρ_I / ρ_W)
    return 0.5 * ρ * g * h**2 * inner(u, ν) * ds(IDs)
Esempio n. 19
0
def normal_flow_penalty(u, scale=1.0, exponent=None, side_wall_ids=()):
    r"""Return the penalty for flow normal to the domain boundary

    For problems where a glacier flows along some boundary, e.g. a fjord
    wall, the velocity has to be parallel to this boundary. Rather than
    enforce this boundary condition directly, we add a penalty for normal
    flow to the action functional.
    """
    mesh = u.ufl_domain()
    ν = firedrake.FacetNormal(mesh)
    L = utilities.diameter(mesh)
    δx = firedrake.CellSize(mesh)
    d = u.ufl_function_space().ufl_element().degree()
    exponent = d + 1 if exponent is None else exponent
    penalty = scale * (L / δx)**exponent
    return 0.5 * penalty * inner(u, ν)**2 * ds(tuple(side_wall_ids))
Esempio n. 20
0
def side_friction(u, h, Cs=firedrake.Constant(0), side_wall_ids=()):
    r"""Return the side wall friction part of the action functional

    The component of the action functional due to friction along the side
    walls of the domain is

    .. math::
       E(u) = -\frac{m}{m + 1}\int_\Gamma h\tau(u, C_s)\cdot u\hspace{2pt}ds

    where :math:`\tau(u, C_s)` is the side wall shear stress, :math:`ds`
    is the element of surface area and :math:`\Gamma` are the side walls.
    Side wall friction is relevant for glaciers that flow through a fjord
    with rock walls on either side.
    """
    mesh = u.ufl_domain()
    ν = firedrake.FacetNormal(mesh)
    u_t = u - inner(u, ν) * ν
    ds_side_walls = ds(domain=mesh, subdomain_id=tuple(side_wall_ids))
    return -m / (m + 1) * h * inner(tau(u_t, Cs), u_t) * ds_side_walls
Esempio n. 21
0
    def static_solver(self):
        def epsilon(u):
            return 0.5 * (fd.nabla_grad(u) + fd.nabla_grad(u).T)
            #return sym(nabla_grad(u))

        def sigma(u):
            d = u.geometric_dimension()  # space dimension
            return self.lam * fd.nabla_div(u) * fd.Identity(
                d) + 2 * self.mu * epsilon(u)

        # Define variational problem
        u = fd.TrialFunction(self.V)
        v = fd.TestFunction(self.V)
        #        f = fd.Constant((0, 0, -self.g)) # body force / rho
        f = fd.Constant((0, 0, 0))  # body force / rho
        T = self.surface_force()

        a = fd.inner(sigma(u), epsilon(v)) * fd.dx
        L = fd.dot(f, v) * fd.dx + fd.dot(T, v) * fd.ds(1)
        # Compute solution
        self.DBC = fd.DirichletBC(self.V, fd.Expression([0., 0., 0.]),
                                  self.bottom_id)
        fd.solve(a == L, self.X, bcs=self.DBC)
Esempio n. 22
0
    def __init__(self, test_space, trial_space, quad_degree=None):
        """
        :arg test_space: the test functionspace
        :arg trial_space: The trial functionspace
        test and trial space are only used to determine the the discretisation that's used (ufl_element)
        not what test and trial functions are actually used (these are provided seperately in residual())
        :arg quad_degree: quadrature degree, default is 2*p+1 where p is the polynomial degree of trial_space
        """
        self.test_space = test_space
        self.trial_space = trial_space
        self.mesh = trial_space.mesh()

        p = trial_space.ufl_element().degree()
        if isinstance(p, int):
            # isotropic mesh
            if quad_degree is None:
                quad_degree = 2 * p + 1
            self.ds = firedrake.ds(domain=self.mesh, degree=quad_degree)
            self.dS = firedrake.dS(domain=self.mesh, degree=quad_degree)
        else:
            # extruded mesh
            p_h, p_v = p
            if quad_degree is None:
                quad_degree = 2 * max(p_h, p_v) + 1

            self.ds = CombinedSurfaceMeasure(self.mesh, quad_degree)
            self.dS = firedrake.dS_v(domain=self.mesh,
                                     degree=quad_degree) + firedrake.dS_h(
                                         domain=self.mesh, degree=quad_degree)
        self.dx = firedrake.dx(domain=self.mesh, degree=quad_degree)

        # self._terms stores the actual instances of the BaseTerm-classes in self.terms
        self._terms = []
        for TermClass in self.terms:
            self._terms.append(
                TermClass(test_space, trial_space, self.dx, self.ds, self.dS))
Esempio n. 23
0
def nonlocal_integral_eq(
    mesh,
    scatterer_bdy_id,
    outer_bdy_id,
    wave_number,
    options_prefix=None,
    solver_parameters=None,
    fspace=None,
    vfspace=None,
    true_sol_grad_expr=None,
    actx=None,
    dgfspace=None,
    dgvfspace=None,
    meshmode_src_connection=None,
    qbx_kwargs=None,
):
    r"""
        see run_method for descriptions of unlisted args

        args:

        gamma and beta are used to precondition
        with the following equation:

        \Delta u - \kappa^2 \gamma u = 0
        (\partial_n - i\kappa\beta) u |_\Sigma = 0
    """
    # make sure we get outer bdy id as tuple in case it consists of multiple ids
    if isinstance(outer_bdy_id, int):
        outer_bdy_id = [outer_bdy_id]
    outer_bdy_id = tuple(outer_bdy_id)
    # away from the excluded region, but firedrake and meshmode point
    # into
    pyt_inner_normal_sign = -1

    ambient_dim = mesh.geometric_dimension()

    # {{{ Build src and tgt

    # build connection meshmode near src boundary -> src boundary inside meshmode
    from meshmode.discretization.poly_element import \
        InterpolatoryQuadratureSimplexGroupFactory
    from meshmode.discretization.connection import make_face_restriction
    factory = InterpolatoryQuadratureSimplexGroupFactory(
        dgfspace.finat_element.degree)
    src_bdy_connection = make_face_restriction(actx,
                                               meshmode_src_connection.discr,
                                               factory, scatterer_bdy_id)
    # source is a qbx layer potential
    from pytential.qbx import QBXLayerPotentialSource
    disable_refinement = (fspace.mesh().geometric_dimension() == 3)
    qbx = QBXLayerPotentialSource(src_bdy_connection.to_discr,
                                  **qbx_kwargs,
                                  _disable_refinement=disable_refinement)

    # get target indices and point-set
    target_indices, target = get_target_points_and_indices(
        fspace, outer_bdy_id)

    # }}}

    # build the operations
    from pytential import bind, sym
    r"""
    ..math:

    x \in \Sigma

    grad_op(x) =
        \nabla(
            \int_\Gamma(
                u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y)
        )
    """
    grad_op = pyt_inner_normal_sign * sym.grad(
        ambient_dim,
        sym.D(HelmholtzKernel(ambient_dim),
              sym.var("u"),
              k=sym.var("k"),
              qbx_forced_limit=None))
    r"""
    ..math:

    x \in \Sigma

    op(x) =
        i \kappa \cdot
        \int_\Gamma(
            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
        )d\gamma(y)
    """
    op = pyt_inner_normal_sign * 1j * sym.var("k") * (sym.D(
        HelmholtzKernel(ambient_dim),
        sym.var("u"),
        k=sym.var("k"),
        qbx_forced_limit=None))

    # bind the operations
    pyt_grad_op = bind((qbx, target), grad_op)
    pyt_op = bind((qbx, target), op)

    # }}}

    class MatrixFreeB(object):
        def __init__(self, A, pyt_grad_op, pyt_op, actx, kappa):
            """
            :arg kappa: The wave number
            """

            self.actx = actx
            self.k = kappa
            self.pyt_op = pyt_op
            self.pyt_grad_op = pyt_grad_op
            self.A = A
            self.meshmode_src_connection = meshmode_src_connection

            # {{{ Create some functions needed for multing
            self.x_fntn = Function(fspace)

            # CG
            self.potential_int = Function(fspace)
            self.potential_int.dat.data[:] = 0.0
            self.grad_potential_int = Function(vfspace)
            self.grad_potential_int.dat.data[:] = 0.0
            self.pyt_result = Function(fspace)

            self.n = FacetNormal(mesh)
            self.v = TestFunction(fspace)

            # some meshmode ones
            self.x_mm_fntn = self.meshmode_src_connection.discr.empty(
                self.actx, dtype='c')

            # }}}

        def mult(self, mat, x, y):
            # Copy function data into the fivredrake function
            self.x_fntn.dat.data[:] = x[:]
            # Transfer the function to meshmode
            self.meshmode_src_connection.from_firedrake(project(
                self.x_fntn, dgfspace),
                                                        out=self.x_mm_fntn)
            # Restrict to boundary
            x_mm_fntn_on_bdy = src_bdy_connection(self.x_mm_fntn)

            # Apply the operation
            potential_int_mm = self.pyt_op(self.actx,
                                           u=x_mm_fntn_on_bdy,
                                           k=self.k)
            grad_potential_int_mm = self.pyt_grad_op(self.actx,
                                                     u=x_mm_fntn_on_bdy,
                                                     k=self.k)
            # Store in firedrake
            self.potential_int.dat.data[target_indices] = potential_int_mm.get(
            )
            for dim in range(grad_potential_int_mm.shape[0]):
                self.grad_potential_int.dat.data[
                    target_indices, dim] = grad_potential_int_mm[dim].get()

            # Integrate the potential
            r"""
            Compute the inner products using firedrake. Note this
            will be subtracted later, hence appears off by a sign.

            .. math::

                \langle
                    n(x) \cdot \nabla(
                        \int_\Gamma(
                            u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                        )d\gamma(y)
                    ), v
                \rangle_\Sigma
                - \langle
                    i \kappa \cdot
                    \int_\Gamma(
                        u(y) \partial_n H_0^{(1)}(\kappa |x - y|)
                    )d\gamma(y), v
                \rangle_\Sigma
            """
            self.pyt_result = assemble(
                inner(inner(self.grad_potential_int, self.n), self.v) *
                ds(outer_bdy_id) -
                inner(self.potential_int, self.v) * ds(outer_bdy_id))

            # y <- Ax - evaluated potential
            self.A.mult(x, y)
            with self.pyt_result.dat.vec_ro as ep:
                y.axpy(-1, ep)

    # {{{ Compute normal helmholtz operator
    u = TrialFunction(fspace)
    v = TestFunction(fspace)
    r"""
    .. math::

        \langle
            \nabla u, \nabla v
        \rangle
        - \kappa^2 \cdot \langle
            u, v
        \rangle
        - i \kappa \langle
            u, v
        \rangle_\Sigma
    """
    a = inner(grad(u), grad(v)) * dx \
        - Constant(wave_number**2) * inner(u, v) * dx \
        - Constant(1j * wave_number) * inner(u, v) * ds(outer_bdy_id)

    # get the concrete matrix from a general bilinear form
    A = assemble(a).M.handle
    # }}}

    # {{{ Setup Python matrix
    B = PETSc.Mat().create()

    # build matrix context
    Bctx = MatrixFreeB(A, pyt_grad_op, pyt_op, actx, wave_number)

    # set up B as same size as A
    B.setSizes(*A.getSizes())

    B.setType(B.Type.PYTHON)
    B.setPythonContext(Bctx)
    B.setUp()
    # }}}

    # {{{ Create rhs

    # Remember f is \partial_n(true_sol)|_\Gamma
    # so we just need to compute \int_\Gamma\partial_n(true_sol) H(x-y)

    sigma = sym.make_sym_vector("sigma", ambient_dim)
    r"""
    ..math:

    x \in \Sigma

    grad_op(x) =
        \nabla(
            \int_\Gamma(
                f(y) H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y)
        )
    """
    grad_op = pyt_inner_normal_sign * \
        sym.grad(ambient_dim, sym.S(HelmholtzKernel(ambient_dim),
                                    sym.n_dot(sigma),
                                    k=sym.var("k"), qbx_forced_limit=None))
    r"""
    ..math:

    x \in \Sigma

    op(x) =
        i \kappa \cdot
        \int_\Gamma(
            f(y) H_0^{(1)}(\kappa |x - y|)
        )d\gamma(y)
        )
    """
    op = 1j * sym.var("k") * pyt_inner_normal_sign * \
        sym.S(HelmholtzKernel(ambient_dim),
              sym.n_dot(sigma),
              k=sym.var("k"),
              qbx_forced_limit=None)

    rhs_grad_op = bind((qbx, target), grad_op)
    rhs_op = bind((qbx, target), op)

    # Transfer to meshmode
    metadata = {'quadrature_degree': 2 * fspace.ufl_element().degree()}
    dg_true_sol_grad = project(true_sol_grad_expr,
                               dgvfspace,
                               form_compiler_parameters=metadata)
    true_sol_grad_mm = meshmode_src_connection.from_firedrake(dg_true_sol_grad,
                                                              actx=actx)
    true_sol_grad_mm = src_bdy_connection(true_sol_grad_mm)
    # Apply the operations
    f_grad_convoluted_mm = rhs_grad_op(actx,
                                       sigma=true_sol_grad_mm,
                                       k=wave_number)
    f_convoluted_mm = rhs_op(actx, sigma=true_sol_grad_mm, k=wave_number)
    # Transfer function back to firedrake
    f_grad_convoluted = Function(vfspace)
    f_convoluted = Function(fspace)
    f_grad_convoluted.dat.data[:] = 0.0
    f_convoluted.dat.data[:] = 0.0

    for dim in range(f_grad_convoluted_mm.shape[0]):
        f_grad_convoluted.dat.data[target_indices,
                                   dim] = f_grad_convoluted_mm[dim].get()
    f_convoluted.dat.data[target_indices] = f_convoluted_mm.get()
    r"""
        \langle
            f, v
        \rangle_\Gamma
        + \langle
            i \kappa \cdot \int_\Gamma(
                f(y) H_0^{(1)}(\kappa |x - y|)
            )d\gamma(y), v
        \rangle_\Sigma
        - \langle
            n(x) \cdot \nabla(
                \int_\Gamma(
                    f(y) H_0^{(1)}(\kappa |x - y|)
                )d\gamma(y)
            ), v
        \rangle_\Sigma
    """
    rhs_form = inner(inner(true_sol_grad_expr, FacetNormal(mesh)),
                     v) * ds(scatterer_bdy_id, metadata=metadata) \
        + inner(f_convoluted, v) * ds(outer_bdy_id) \
        - inner(inner(f_grad_convoluted, FacetNormal(mesh)),
                v) * ds(outer_bdy_id)

    rhs = assemble(rhs_form)

    # {{{ set up a solver:
    solution = Function(fspace, name="Computed Solution")

    #       {{{ Used for preconditioning
    if 'gamma' in solver_parameters or 'beta' in solver_parameters:
        gamma = complex(solver_parameters.pop('gamma', 1.0))

        import cmath
        beta = complex(solver_parameters.pop('beta', cmath.sqrt(gamma)))

        p = inner(grad(u), grad(v)) * dx \
            - Constant(wave_number**2 * gamma) * inner(u, v) * dx \
            - Constant(1j * wave_number * beta) * inner(u, v) * ds(outer_bdy_id)
        P = assemble(p).M.handle

    else:
        P = A
    #       }}}

    # Set up options to contain solver parameters:
    ksp = PETSc.KSP().create()
    if solver_parameters['pc_type'] == 'pyamg':
        del solver_parameters['pc_type']  # We are using the AMG preconditioner

        pyamg_tol = solver_parameters.get('pyamg_tol', None)
        if pyamg_tol is not None:
            pyamg_tol = float(pyamg_tol)
        pyamg_maxiter = solver_parameters.get('pyamg_maxiter', None)
        if pyamg_maxiter is not None:
            pyamg_maxiter = int(pyamg_maxiter)
        ksp.setOperators(B)
        ksp.setUp()
        pc = ksp.pc
        pc.setType(pc.Type.PYTHON)
        pc.setPythonContext(
            AMGTransmissionPreconditioner(wave_number,
                                          fspace,
                                          A,
                                          tol=pyamg_tol,
                                          maxiter=pyamg_maxiter,
                                          use_plane_waves=True))
    # Otherwise use regular preconditioner
    else:
        ksp.setOperators(B, P)

    options_manager = OptionsManager(solver_parameters, options_prefix)
    options_manager.set_from_options(ksp)

    import petsc4py.PETSc
    petsc4py.PETSc.Sys.popErrorHandler()
    with rhs.dat.vec_ro as b:
        with solution.dat.vec as x:
            ksp.solve(b, x)
    # }}}

    return ksp, solution
Esempio n. 24
0
def heat_exchanger_optimization(mu=0.03, n_iters=1000):

    output_dir = "2D/"

    path = os.path.abspath(__file__)
    dir_path = os.path.dirname(path)
    mesh = fd.Mesh(f"{dir_path}/2D_mesh.msh")
    # Perturb the mesh coordinates. Necessary to calculate shape derivatives
    S = fd.VectorFunctionSpace(mesh, "CG", 1)
    s = fd.Function(S, name="deform")
    mesh.coordinates.assign(mesh.coordinates + s)

    # Initial level set function
    x, y = fd.SpatialCoordinate(mesh)
    PHI = fd.FunctionSpace(mesh, "CG", 1)
    phi_expr = sin(y * pi / 0.2) * cos(x * pi / 0.2) - fd.Constant(0.8)
    # Avoid recording the operation interpolate into the tape.
    # Otherwise, the shape derivatives will not be correct
    with fda.stop_annotating():
        phi = fd.interpolate(phi_expr, PHI)
        phi.rename("LevelSet")
        fd.File(output_dir + "phi_initial.pvd").write(phi)

    # Physics
    mu = fd.Constant(mu)  # viscosity
    alphamin = 1e-12
    alphamax = 2.5 / (2e-4)
    parameters = {
        "mat_type": "aij",
        "ksp_type": "preonly",
        "ksp_converged_reason": None,
        "pc_type": "lu",
        "pc_factor_mat_solver_type": "mumps",
    }
    stokes_parameters = parameters
    temperature_parameters = parameters
    u_inflow = 2e-3
    tin1 = fd.Constant(10.0)
    tin2 = fd.Constant(100.0)

    P2 = fd.VectorElement("CG", mesh.ufl_cell(), 2)
    P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1)
    TH = P2 * P1
    W = fd.FunctionSpace(mesh, TH)

    U = fd.TrialFunction(W)
    u, p = fd.split(U)
    V = fd.TestFunction(W)
    v, q = fd.split(V)

    epsilon = fd.Constant(10000.0)

    def hs(phi, epsilon):
        return fd.Constant(alphamax) * fd.Constant(1.0) / (
            fd.Constant(1.0) + exp(-epsilon * phi)) + fd.Constant(alphamin)

    def stokes(phi, BLOCK_INLET_MOUTH, BLOCK_OUTLET_MOUTH):
        a_fluid = mu * inner(grad(u), grad(v)) - div(v) * p - q * div(u)
        darcy_term = inner(u, v)
        return (a_fluid * dx + hs(phi, epsilon) * darcy_term * dx(0) +
                alphamax * darcy_term *
                (dx(BLOCK_INLET_MOUTH) + dx(BLOCK_OUTLET_MOUTH)))

    # Dirichlet boundary conditions
    inflow1 = fd.as_vector([
        u_inflow * sin(
            ((y - (line_sep -
                   (dist_center + inlet_width))) * pi) / inlet_width),
        0.0,
    ])
    inflow2 = fd.as_vector([
        u_inflow * sin(((y - (line_sep + dist_center)) * pi) / inlet_width),
        0.0,
    ])

    noslip = fd.Constant((0.0, 0.0))

    # Stokes 1
    bcs1_1 = fd.DirichletBC(W.sub(0), noslip, WALLS)
    bcs1_2 = fd.DirichletBC(W.sub(0), inflow1, INLET1)
    bcs1_3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), OUTLET1)
    bcs1_4 = fd.DirichletBC(W.sub(0), noslip, INLET2)
    bcs1_5 = fd.DirichletBC(W.sub(0), noslip, OUTLET2)
    bcs1 = [bcs1_1, bcs1_2, bcs1_3, bcs1_4, bcs1_5]

    # Stokes 2
    bcs2_1 = fd.DirichletBC(W.sub(0), noslip, WALLS)
    bcs2_2 = fd.DirichletBC(W.sub(0), inflow2, INLET2)
    bcs2_3 = fd.DirichletBC(W.sub(1), fd.Constant(0.0), OUTLET2)
    bcs2_4 = fd.DirichletBC(W.sub(0), noslip, INLET1)
    bcs2_5 = fd.DirichletBC(W.sub(0), noslip, OUTLET1)
    bcs2 = [bcs2_1, bcs2_2, bcs2_3, bcs2_4, bcs2_5]

    # Forward problems
    U1, U2 = fd.Function(W), fd.Function(W)
    L = inner(fd.Constant((0.0, 0.0, 0.0)), V) * dx
    problem = fd.LinearVariationalProblem(stokes(-phi, INMOUTH2, OUTMOUTH2),
                                          L,
                                          U1,
                                          bcs=bcs1)
    solver_stokes1 = fd.LinearVariationalSolver(
        problem,
        solver_parameters=stokes_parameters,
        options_prefix="stokes_1")
    solver_stokes1.solve()
    problem = fd.LinearVariationalProblem(stokes(phi, INMOUTH1, OUTMOUTH1),
                                          L,
                                          U2,
                                          bcs=bcs2)
    solver_stokes2 = fd.LinearVariationalSolver(
        problem,
        solver_parameters=stokes_parameters,
        options_prefix="stokes_2")
    solver_stokes2.solve()

    # Convection difussion equation
    ks = fd.Constant(1e0)
    cp_value = 5.0e5
    cp = fd.Constant(cp_value)
    T = fd.FunctionSpace(mesh, "DG", 1)
    t = fd.Function(T, name="Temperature")
    w = fd.TestFunction(T)

    # Mesh-related functions
    n = fd.FacetNormal(mesh)
    h = fd.CellDiameter(mesh)
    u1, p1 = fd.split(U1)
    u2, p2 = fd.split(U2)

    def upwind(u):
        return (dot(u, n) + abs(dot(u, n))) / 2.0

    u1n = upwind(u1)
    u2n = upwind(u2)

    # Penalty term
    alpha = fd.Constant(500.0)
    # Bilinear form
    a_int = dot(grad(w), ks * grad(t) - cp * (u1 + u2) * t) * dx

    a_fac = (fd.Constant(-1.0) * ks * dot(avg(grad(w)), jump(t, n)) * dS +
             fd.Constant(-1.0) * ks * dot(jump(w, n), avg(grad(t))) * dS +
             ks("+") *
             (alpha("+") / avg(h)) * dot(jump(w, n), jump(t, n)) * dS)

    a_vel = (dot(
        jump(w),
        cp * (u1n("+") + u2n("+")) * t("+") - cp *
        (u1n("-") + u2n("-")) * t("-"),
    ) * dS + dot(w,
                 cp * (u1n + u2n) * t) * ds)

    a_bnd = (dot(w,
                 cp * dot(u1 + u2, n) * t) * (ds(INLET1) + ds(INLET2)) +
             w * t * (ds(INLET1) + ds(INLET2)) - w * tin1 * ds(INLET1) -
             w * tin2 * ds(INLET2) + alpha / h * ks * w * t *
             (ds(INLET1) + ds(INLET2)) - ks * dot(grad(w), t * n) *
             (ds(INLET1) + ds(INLET2)) - ks * dot(grad(t), w * n) *
             (ds(INLET1) + ds(INLET2)))

    aT = a_int + a_fac + a_vel + a_bnd

    LT_bnd = (alpha / h * ks * tin1 * w * ds(INLET1) +
              alpha / h * ks * tin2 * w * ds(INLET2) -
              tin1 * ks * dot(grad(w), n) * ds(INLET1) -
              tin2 * ks * dot(grad(w), n) * ds(INLET2))

    problem = fd.LinearVariationalProblem(derivative(aT, t), LT_bnd, t)
    solver_temp = fd.LinearVariationalSolver(
        problem,
        solver_parameters=temperature_parameters,
        options_prefix="temperature",
    )
    solver_temp.solve()
    # fd.solve(eT == 0, t, solver_parameters=temperature_parameters)

    # Cost function: Flux at the cold outlet
    scale_factor = 4e-4
    Jform = fd.assemble(
        fd.Constant(-scale_factor * cp_value) * inner(t * u1, n) * ds(OUTLET1))
    # Constraints: Pressure drop on each fluid
    power_drop = 1e-2
    Power1 = fd.assemble(p1 / power_drop * ds(INLET1))
    Power2 = fd.assemble(p2 / power_drop * ds(INLET2))

    phi_pvd = fd.File("phi_evolution.pvd")

    def deriv_cb(phi):
        with stop_annotating():
            phi_pvd.write(phi[0])

    c = fda.Control(s)

    # Reduced Functionals
    Jhat = LevelSetFunctional(Jform, c, phi, derivative_cb_pre=deriv_cb)
    P1hat = LevelSetFunctional(Power1, c, phi)
    P1control = fda.Control(Power1)

    P2hat = LevelSetFunctional(Power2, c, phi)
    P2control = fda.Control(Power2)

    Jhat_v = Jhat(phi)
    print("Initial cost function value {:.5f}".format(Jhat_v), flush=True)
    print("Power drop 1 {:.5f}".format(Power1), flush=True)
    print("Power drop 2 {:.5f}".format(Power2), flush=True)

    beta_param = 0.08
    # Regularize the shape derivatives only in the domain marked with 0
    reg_solver = RegularizationSolver(S,
                                      mesh,
                                      beta=beta_param,
                                      gamma=1e5,
                                      dx=dx,
                                      design_domain=0)

    tol = 1e-5
    dt = 0.05
    params = {
        "alphaC": 1.0,
        "debug": 5,
        "alphaJ": 1.0,
        "dt": dt,
        "K": 1e-3,
        "maxit": n_iters,
        "maxtrials": 5,
        "itnormalisation": 10,
        "tol_merit":
        5e-3,  # new merit can be within 0.5% of the previous merit
        # "normalize_tol" : -1,
        "tol": tol,
    }

    solver_parameters = {
        "reinit_solver": {
            "h_factor": 2.0,
        }
    }
    # Optimization problem
    problem = InfDimProblem(
        Jhat,
        reg_solver,
        ineqconstraints=[
            Constraint(P1hat, 1.0, P1control),
            Constraint(P2hat, 1.0, P2control),
        ],
        solver_parameters=solver_parameters,
    )
    results = nlspace_solve(problem, params)

    return results
Esempio n. 25
0
    return kl[i, j]


Kinv.dat.data[:, 0, 0] = 1 / \
    fix_perm_map(ccenter.dat.data[:, 0], ccenter.dat.data[:, 1])
Kinv.dat.data[:, 1, 1] = 1 / \
    fix_perm_map(ccenter.dat.data[:, 0], ccenter.dat.data[:, 1])

# -------
# 3.4) Variational Form
# the bilinear and linear forms of the variational problem are defined as: ::
a = fd.dot(v, Kinv * u) * fd.dx - fd.div(v) * p * fd.dx + q * fd.div(u) * fd.dx

f = fd.Constant(0.0)
n = fd.FacetNormal(mesh)
L = q * f * fd.dx - fd.Constant(pbar) * fd.inner(v, n) * fd.ds(outlet)

# ----
# 3.5) set boundary conditions
# The strongly enforced boundary conditions on the BDM space on the top and
# bottom of the domain are declared as: ::
bc0 = fd.DirichletBC(W.sub(0), fd.Constant(qbar), inlet)
bc1 = fd.DirichletBC(W.sub(0), fd.Constant(q0bar), noflow)

# ----
# 4) Define and solve the problem
#
# Now we're ready to solve the variational problem. We define `w` to be a
# function to hold the solution on the mixed space.
w = fd.Function(W)
Esempio n. 26
0
def pml(mesh,
        scatterer_bdy_id,
        outer_bdy_id,
        wave_number,
        options_prefix=None,
        solver_parameters=None,
        inner_region=None,
        fspace=None,
        tfspace=None,
        true_sol_grad=None,
        pml_type=None,
        delta=None,
        quad_const=None,
        speed=None,
        pml_min=None,
        pml_max=None):
    """
        For unlisted arg descriptions, see run_method

        :arg inner_region: boundary id of non-pml region
        :arg pml_type: Type of pml function, either 'quadratic' or 'bdy_integral'
        :arg delta: For :arg:`pml_type` of 'bdy_integral', added to denominator
                    to prevent 1 / 0 at edge of boundary
        :arg quad_const: For :arg:`pml_type` of 'quadratic', a scaling constant
        :arg speed: Speed of sound
        :arg pml_min: A list, *pml_min[i]* is where to begin pml layer in direction
                      *i*
        :arg pml_max: A list, *pml_max[i]* is where to end pml layer in direction *i*
    """
    # Handle defauls
    if pml_type is None:
        pml_type = 'bdy_integral'
    if delta is None:
        delta = 1e-3
    if quad_const is None:
        quad_const = 1.0
    if speed is None:
        speed = 340.0

    pml_types = ['bdy_integral', 'quadratic']
    if pml_type not in pml_types:
        raise ValueError("PML type of %s is not one of %s" %
                         (pml_type, pml_types))

    xx = SpatialCoordinate(mesh)
    # {{{ create sigma functions for PML
    sigma = None
    if pml_type == 'bdy_integral':
        sigma = [
            Constant(speed) / (Constant(delta + extent) - abs(coord))
            for extent, coord in zip(pml_max, xx)
        ]
    elif pml_type == 'quadratic':
        sigma = [
            Constant(quad_const) * (abs(coord) - Constant(min_))**2
            for min_, coord in zip(pml_min, xx)
        ]
    r"""
        Here \kappa is the wave number and c is the speed

        ..math::

        \kappa = \frac{ \omega } { c }
    """
    omega = wave_number * speed

    # {{{ Set up PML functions
    gamma = [
        Constant(1.0) + conditional(
            abs(real(coord)) >= real(min_),
            Constant(1j / omega) * sigma_i, Constant(0.0))
        for min_, coord, sigma_i in zip(pml_min, xx, sigma)
    ]

    kappa = [None] * len(gamma)
    gamma_prod = 1.0
    for i in range(len(gamma)):
        gamma_prod *= gamma[i]
        tensor_i = [Constant(0.0) for _ in range(len(gamma))]
        tensor_i[i] = 1.0
        r"""
            *i*th entry is

            .. math::

            \frac{\prod_{j\neq i} \gamma_j}{ \gamma_i }
        """
        for j in range(len(gamma)):
            if j != i:
                tensor_i[i] *= gamma[j]
            else:
                tensor_i[i] /= gamma[j]
        kappa[i] = tensor_i

    kappa = as_tensor(kappa)

    # }}}

    p = TrialFunction(fspace)
    q = TestFunction(fspace)

    k = wave_number  # Just easier to look at
    a = (inner(dot(grad(p), kappa), grad(q)) -
         Constant(k**2) * gamma_prod * inner(p, q)) * dx

    n = FacetNormal(mesh)
    L = inner(dot(true_sol_grad, n), q) * ds(scatterer_bdy_id)

    bc = DirichletBC(fspace, Constant(0), outer_bdy_id)

    solution = Function(fspace)

    #solve(a == L, solution, bcs=[bc], options_prefix=options_prefix)
    # Create a solver and return the KSP object with the solution so that can get
    # PETSc information
    # Create problem
    problem = vs.LinearVariationalProblem(a, L, solution, [bc], None)
    # Create solver and call solve
    solver = vs.LinearVariationalSolver(problem,
                                        solver_parameters=solver_parameters,
                                        options_prefix=options_prefix)
    solver.solve()

    return solver.snes, solution
# stability
# tau = fd.Constant(1.0) / h + abs(fd.dot(velocity, n))
tau = fd.Constant(5) / h + vn

# numerical flux
chat = lmbd_h
qhat = qh + tau * (ch - chat) * n + velocity * chat
# qhat_n = fd.dot(qh, n) + tau*(ch - chat) + chat*vn

a_u = (
    fd.inner(fd.inv(Diff) * qh, vh) * fd.dx - ch * fd.div(vh) * fd.dx +
    # internal faces
    fd.jump(lmbd_h * vh, n) * fd.dS +
    # other faces
    lmbd_h * fd.inner(vh, n) * fd.ds(outflow) +
    lmbd_h * fd.inner(vh, n) * fd.ds(TOP) +
    lmbd_h * fd.inner(vh, n) * fd.ds(BOTTOM))

# Dirichlet faces
L_u = -fd.Constant(cIn) * fd.inner(vh, n) * fd.ds(inlet)

a_c = (wh * (ch - c0) / dtc * fd.dx -
       fd.inner(fd.grad(wh), qh + ch * velocity) * fd.dx +
       wh("+") * fd.jump(qhat, n) * fd.dS + wh * fd.inner(qhat, n) * fd.ds)

L_c = 0

# transmission boundary condition
F_q = mu_h("+")*fd.jump(qhat, n)*fd.dS + \
    mu_h*fd.inner(qhat, n)*fd.ds(outflow) + \
Esempio n. 28
0
def lax_friedrichs_boundary_flux(s, s_ex, c, v, boundary_ids):
    r"""Create the Lax-Friedrichs numerical flux through the domain boundary"""
    return c * inner(s - s_ex, v) * ds(boundary_ids)
Esempio n. 29
0
def central_inflow_flux(F_in, v, boundary_ids):
    r"""Create the weak form of the central numerical flux through the domain
    boundary"""
    mesh = v.ufl_domain()
    n = FacetNormal(mesh)
    return inner(F_in, outer(v, n)) * ds(boundary_ids)
Esempio n. 30
0
    def sdhm_form(self, problem, mesh, bcs_p, bcs_u):
        rho = problem.rho
        mu = problem.mu
        k = problem.k
        f = problem.f

        q, p, lambda_h = fire.split(self.solution)
        w, v, mu_h = fire.TestFunctions(self._W)

        n = fire.FacetNormal(mesh)
        h = fire.CellDiameter(mesh)

        # Stabilizing parameters
        has_mesh_characteristic_length = True
        beta_0 = fire.Constant(1e-15)
        delta_0 = fire.Constant(1)
        delta_1 = fire.Constant(-1 / 2)
        delta_2 = fire.Constant(1 / 2)
        delta_3 = fire.Constant(1 / 2)

        # h_avg = (h('+') + h('-')) / 2.
        beta = beta_0 / h
        beta_avg = beta_0 / h("+")
        if has_mesh_characteristic_length:
            delta_2 = delta_2 * h * h
            delta_3 = delta_3 * h * h

        kappa = rho * k / mu
        inv_kappa = 1.0 / kappa

        # Classical mixed terms
        a = (dot(inv_kappa * q, w) - div(w) * p - delta_0 * v * div(q)) * dx
        L = -delta_0 * f * v * dx

        # Hybridization terms
        a += lambda_h("+") * dot(w, n)("+") * dS + mu_h("+") * dot(q,
                                                                   n)("+") * dS
        a += beta_avg * kappa("+") * (lambda_h("+") - p("+")) * (mu_h("+") -
                                                                 v("+")) * dS

        # Add the contributions of the pressure boundary conditions to L
        primal_bc_markers = list(mesh.exterior_facets.unique_markers)
        for pboundary, iboundary in bcs_p:
            primal_bc_markers.remove(iboundary)
            a += (pboundary * dot(w, n) + mu_h * dot(q, n)) * ds(iboundary)
            a += beta * kappa * (lambda_h - pboundary) * mu_h * ds(iboundary)

        unprescribed_primal_bc = primal_bc_markers
        for bc_marker in unprescribed_primal_bc:
            a += (lambda_h * dot(w, n) + mu_h * dot(q, n)) * ds(bc_marker)
            a += beta * kappa * lambda_h * mu_h * ds(bc_marker)

        # Add the (weak) contributions of the velocity boundary conditions to L
        for uboundary, iboundary, component in bcs_u:
            if component is not None:
                dim = mesh.geometric_dimension()
                bc_array = []
                for _ in range(dim):
                    bc_array.append(0.0)
                bc_array[component] = uboundary
                bc_as_vector = fire.Constant(bc_array)
                L += mu_h * dot(bc_as_vector, n) * ds(iboundary)
            else:
                L += mu_h * dot(uboundary, n) * ds(iboundary)

        # Stabilizing terms
        a += (delta_1 * inner(kappa * (inv_kappa * q + grad(p)),
                              delta_0 * inv_kappa * w + grad(v)) * dx)
        a += delta_2 * inv_kappa * div(q) * div(w) * dx
        a += delta_3 * inner(kappa * curl(inv_kappa * q), curl(
            inv_kappa * w)) * dx
        L += delta_2 * inv_kappa * f * div(w) * dx

        return a, L