Esempio n. 1
0
 def __init__(self, V, fixed_dims=[], direct_solve=False):
     if isinstance(fixed_dims, int):
         fixed_dims = [fixed_dims]
     self.V = V
     self.fixed_dims = fixed_dims
     self.direct_solve = direct_solve
     self.zero = fd.Constant(V.mesh().topological_dimension() * (0, ))
     u = fd.TrialFunction(V)
     v = fd.TestFunction(V)
     self.zero_fun = fd.Function(V)
     self.a = 1e-2 * \
         fd.inner(u, v) * fd.dx + fd.inner(fd.sym(fd.grad(u)),
                                           fd.sym(fd.grad(v))) * fd.dx
     self.bc_fun = fd.Function(V)
     if len(self.fixed_dims) == 0:
         bcs = [fd.DirichletBC(self.V, self.bc_fun, "on_boundary")]
     else:
         bcs = []
         for i in range(self.V.mesh().topological_dimension()):
             if i in self.fixed_dims:
                 bcs.append(fd.DirichletBC(self.V.sub(i), 0, "on_boundary"))
             else:
                 bcs.append(
                     fd.DirichletBC(self.V.sub(i), self.bc_fun.sub(i),
                                    "on_boundary"))
     self.A_ext = fd.assemble(self.a, bcs=bcs, mat_type="aij")
     self.ls_ext = fd.LinearSolver(self.A_ext,
                                   solver_parameters=self.get_params())
     self.A_adj = fd.assemble(self.a,
                              bcs=fd.DirichletBC(self.V, self.zero,
                                                 "on_boundary"),
                              mat_type="aij")
     self.ls_adj = fd.LinearSolver(self.A_adj,
                                   solver_parameters=self.get_params())
Esempio n. 2
0
    def solve(self, phi: fd.Function, iters: int = 5) -> fd.Function:

        marking = fd.Function(self.DG0)
        marking_bc_nodes = fd.Function(self.V)
        # Mark cells cut by phi(x) = 0
        domain = "{[i, j]: 0 <= i < b.dofs}"
        instructions = """
                <float64> min_value = 1e20
                <float64> max_value = -1e20
                for i
                    min_value = fmin(min_value, b[i, 0])
                    max_value = fmax(max_value, b[i, 0])
                end
                a[0, 0] = 1.0 if (min_value < 0 and max_value > 0) else 0.0
                """
        fd.par_loop(
            (domain, instructions),
            dx,
            {
                "a": (marking, RW),
                "b": (phi, READ)
            },
            is_loopy_kernel=True,
        )
        # Mark the nodes in the marked cells
        fd.par_loop(
            ("{[i] : 0 <= i < A.dofs}", "A[i, 0] = fmax(A[i, 0], B[0, 0])"),
            dx,
            {
                "A": (marking_bc_nodes, RW),
                "B": (marking, READ)
            },
            is_loopy_kernel=True,
        )
        # Mark the nodes in the marked cells
        # Project the gradient of phi on the cut cells
        self.phi.assign(phi)
        V = self.V
        rho, sigma = fd.TrialFunction(V), fd.TestFunction(V)
        a = rho * sigma * marking * dx
        L_proj = (self.phi / sqrt(inner(grad(self.phi), grad(self.phi))) *
                  marking * sigma * dx)
        bc_proj = BCOut(V, fd.Constant(0.0), marking_bc_nodes)
        self.A_proj = fd.assemble(a, tensor=self.A_proj, bcs=bc_proj)
        b_proj = fd.assemble(L_proj, bcs=bc_proj)
        solver_proj = fd.LinearSolver(self.A_proj,
                                      solver_parameters=self.solver_parameters)
        solver_proj.solve(self.phi_int, b_proj)

        def nabla_phi_bar(phi):
            return sqrt(inner(grad(phi), grad(phi)))

        def d1(s):
            return fd.Constant(1.0) - fd.Constant(1.0) / s

        def d2(s):
            return conditional(le(s, fd.Constant(1.0)), s - fd.Constant(1.0),
                               d1(s))

        def residual_phi(phi):
            return fd.norm(
                fd.assemble(
                    d2(nabla_phi_bar(phi)) * inner(grad(phi), grad(sigma)) *
                    dx))

        a = inner(grad(rho), grad(sigma)) * dx
        L = (inner(
            (-d2(nabla_phi_bar(self.phi)) + fd.Constant(1.0)) * grad(self.phi),
            grad(sigma),
        ) * dx)
        bc = BCInt(V, self.phi_int, marking_bc_nodes)
        phi_sol = fd.Function(V)
        A = fd.assemble(a, bcs=bc)
        b = fd.assemble(L, bcs=bc)
        solver = fd.LinearSolver(A, solver_parameters=self.solver_parameters)

        # Solve the Signed distance equation with Picard iteration
        bc.apply(phi_sol)
        init_res = residual_phi(phi_sol)
        res = 1e10
        it = 0
        while res > init_res or it < iters:
            solver.solve(phi_sol, b)
            self.phi.assign(phi_sol)
            b = fd.assemble(L, bcs=bc, tensor=b)
            it += 1
            res = residual_phi(phi_sol)
        if res > init_res:
            fd.warning(
                f"Residual in signed distance function increased: {res}, before: {init_res}"
            )
        return self.phi
Esempio n. 3
0
    def __init__(self, Q, fixed_bids=[], extra_bcs=[], direct_solve=False):
        if isinstance(extra_bcs, fd.DirichletBC):
            extra_bcs = [extra_bcs]

        self.direct_solve = direct_solve
        self.fixed_bids = fixed_bids  # fixed parts of bdry
        self.params = self.get_params()  # solver parameters
        self.Q = Q
        """
        V: type fd.FunctionSpace
        I: type PETSc.Mat, interpolation matrix between V and  ControlSpace
        """
        (V, I_interp) = Q.get_space_for_inner()
        free_bids = list(V.mesh().topology.exterior_facets.unique_markers)
        self.free_bids = [int(i) for i in free_bids]  # np.int->int
        for bid in self.fixed_bids:
            self.free_bids.remove(bid)

        # Some weak forms have a nullspace. We import the nullspace if no
        # parts of the bdry are fixed (we assume that a DirichletBC is
        # sufficient to empty the nullspace).
        nsp = None
        if len(self.fixed_bids) == 0:
            nsp_functions = self.get_nullspace(V)
            if nsp_functions is not None:
                nsp = fd.VectorSpaceBasis(nsp_functions)
                nsp.orthonormalize()

        bcs = []
        # impose homogeneous Dirichlet bcs on bdry parts that are fixed.
        if len(self.fixed_bids) > 0:
            dim = V.value_size
            if dim == 2:
                zerovector = fd.Constant((0, 0))
            elif dim == 3:
                zerovector = fd.Constant((0, 0, 0))
            else:
                raise NotImplementedError
            bcs.append(fd.DirichletBC(V, zerovector, self.fixed_bids))

        if len(extra_bcs) > 0:
            bcs += extra_bcs

        if len(bcs) == 0:
            bcs = None

        a = self.get_weak_form(V)
        A = fd.assemble(a, mat_type='aij', bcs=bcs)
        ls = fd.LinearSolver(A,
                             solver_parameters=self.params,
                             nullspace=nsp,
                             transpose_nullspace=nsp)
        self.ls = ls
        self.A = A.petscmat
        self.interpolated = False

        # If the matrix I is passed, replace A with transpose(I)*A*I
        # and set up a ksp solver for self.riesz_map
        if I_interp is not None:
            self.interpolated = True
            ITAI = self.A.PtAP(I_interp)
            from firedrake.petsc import PETSc
            import numpy as np
            zero_rows = []

            # if there are zero-rows, replace them with rows that
            # have 1 on the diagonal entry
            for row in range(*ITAI.getOwnershipRange()):
                (cols, vals) = ITAI.getRow(row)
                valnorm = np.linalg.norm(vals)
                if valnorm < 1e-13:
                    zero_rows.append(row)
            for row in zero_rows:
                ITAI.setValue(row, row, 1.0)
            ITAI.assemble()

            # overwrite the self.A created by get_impl
            self.A = ITAI

            # create ksp solver for self.riesz_map
            Aksp = PETSc.KSP().create(comm=V.comm)
            Aksp.setOperators(ITAI)
            Aksp.setType("preonly")
            Aksp.pc.setType("cholesky")
            Aksp.pc.setFactorSolverType("mumps")
            Aksp.setFromOptions()
            Aksp.setUp()
            self.Aksp = Aksp
Esempio n. 4
0
 def solver(self):
     return firedrake.LinearSolver(self.A, solver_parameters=self.solver_parameters)
Esempio n. 5
0
def solver_CG(mesh, el, space, deg, T, dt=0.001, warm_up=False):
    """Solve the scalar wave equation on a unit square/cube using a
    CG FEM formulation with several different element types.

    Parameters
    ----------
    mesh: Firedrake.mesh
        A utility mesh from the Firedrake package
    el: string
        The type of element either "tria" or "quad".
        `tria` in 3d implies tetrahedra and
        `quad` in 3d implies hexahedral elements.
    space: string
        The space of the FEM. Available options are:
            "CG": Continuous Galerkin Finite Elements,
            "KMV": Kong-Mulder-Veldhuzien higher-order mass lumped elements
            "S" (for Serendipity) (NB: quad/hexs only)
            "spectral": spectral elements using GLL quad
                        points (NB: quads/hexs only)
    deg: int
        The spatial polynomial degree.
    T: float
        The simulation duration in simulation seconds.
    dt: float, optional
        Simulation timestep
    warm_up: boolean, optional
        Warm up symbolics by running one timestep and shutting down.

    Returns
    -------
    u_n: Firedrake.Function
        The solution at time `T`


    """

    sd = mesh.geometric_dimension()

    V = _build_space(mesh, el, space, deg)

    quad_rule1, quad_rule2 = _build_quad_rule(el, V, space)

    params = _select_params(space)

    # DEBUG
    # outfile = fd.File(os.getcwd() + "/results/simple_shots.pvd")
    # END DEBUG

    tot_dof = COMM_WORLD.allreduce(V.dof_dset.total_size, op=MPI.SUM)
    # if COMM_WORLD.rank == 0:
    #    print("------------------------------------------")
    #    print("The problem has " + str(tot_dof) + " degrees of freedom.")
    #    print("------------------------------------------")

    nt = int(T / dt)  # number of timesteps

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

    u_np1 = fd.Function(V)  # n+1
    u_n = fd.Function(V)  # n
    u_nm1 = fd.Function(V)  # n-1

    # constant speed
    c = Constant(1.5)

    m = (
        (1.0 / (c * c))
        * (u - 2.0 * u_n + u_nm1)
        / Constant(dt * dt)
        * v
        * dx(rule=quad_rule1)
    )  # mass-like matrix

    a = dot(grad(u_n), grad(v)) * dx(rule=quad_rule2)  # stiffness matrix

    # injection of source into mesh
    ricker = Constant(0.0)
    source = Constant([0.5] * sd)
    coords = fd.SpatialCoordinate(mesh)
    F = m + a - delta_expr(source, *coords) * ricker * v * dx(rule=quad_rule2)

    a, r = fd.lhs(F), fd.rhs(F)
    A, R = fd.assemble(a), fd.assemble(r)
    solver = fd.LinearSolver(A, solver_parameters=params, options_prefix="")

    # timestepping loop
    results = []

    t = 0.0
    for step in range(nt):

        with PETSc.Log.Stage("{el}{deg}".format(el=el, deg=deg)):
            ricker.assign(RickerWavelet(t, freq=6))

            R = fd.assemble(r, tensor=R)

            solver.solve(u_np1, R)

            snes = _get_time("SNESSolve")
            ksp = _get_time("KSPSolve")
            pcsetup = _get_time("PCSetUp")
            pcapply = _get_time("PCApply")
            jac = _get_time("SNESJacobianEval")
            residual = _get_time("SNESFunctionEval")
            sparsity = _get_time("CreateSparsity")

            results.append(
                [tot_dof, snes, ksp, pcsetup, pcapply, jac, residual, sparsity]
            )

        if warm_up:
            # Warm up symbolics/disk cache
            solver.solve(u_np1, R)
            sys.exit("Warming up...")

        u_nm1.assign(u_n)
        u_n.assign(u_np1)

        t = step * float(dt)

        # if step % 10 == 0:
        #    outfile.write(u_n)
        #    print("Time is " + str(t), flush=True)

    results = np.asarray(results)
    if mesh.comm.rank == 0:
        with open(
            "data/scalar_wave.{el}.{deg}.{space}.csv".format(
                el=el, deg=deg, space=space
            ),
            "w",
        ) as f:
            np.savetxt(
                f,
                results,
                fmt=["%d"] + ["%e"] * 7,
                delimiter=",",
                header="tot_dof,SNESSolve,KSPSolve,PCSetUp,PCApply,SNESJacobianEval,SNESFunctionEval,CreateSparsity",
                comments="",
            )

    return u_n
Esempio n. 6
0
    def initialize(self, pc):
        if pc.getType() != "python":
            raise ValueError("Expecting PC type python")
        prefix = pc.getOptionsPrefix() + "diagfft_"

        # we assume P has things stuffed inside of it
        _, P = pc.getOperators()
        context = P.getPythonContext()
        appctx = context.appctx
        self.appctx = appctx

        # all at once solution passed through the appctx
        self.w_all = appctx.get("w_all", None)

        # FunctionSpace checks
        test, trial = context.a.arguments()
        if test.function_space() != trial.function_space():
            raise ValueError("Pressure space test and trial space differ")
        W = test.function_space()

        # basic model function space
        get_blockV = appctx.get("get_blockV", None)
        self.blockV = get_blockV()
        M = int(W.dim() / self.blockV.dim())
        assert (self.blockV.dim() * M == W.dim())
        self.M = M
        self.NM = W.dim()

        # Input/Output wrapper Functions
        self.xf = fd.Function(W)  # input
        self.yf = fd.Function(W)  # output

        # Gamma coefficients
        Nt = M
        exponents = np.arange(Nt) / Nt
        alphav = appctx.get("alpha", None)
        self.Gam = alphav**exponents

        # Di coefficients
        thetav = appctx.get("theta", None)
        Dt = appctx.get("dt", None)
        C1col = np.zeros(Nt)
        C2col = np.zeros(Nt)
        C1col[:2] = np.array([1, -1]) / Dt
        C2col[:2] = np.array([thetav, 1 - thetav])
        self.D1 = np.sqrt(Nt) * fft(self.Gam * C1col)
        self.D2 = np.sqrt(Nt) * fft(self.Gam * C2col)

        # Block system setup
        # First need to build the vector function space version of
        # blockV
        mesh = self.blockV.mesh()
        Ve = self.blockV.ufl_element()
        if isinstance(Ve, fd.MixedElement):
            MixedCpts = []
            self.ncpts = Ve.num_sub_elements()
            for cpt in range(Ve.num_sub_elements()):
                SubV = Ve.sub_elements()[cpt]
                if isinstance(SubV, fd.FiniteElement):
                    MixedCpts.append(fd.VectorElement(SubV, dim=2))
                elif isinstance(SubV, fd.VectorElement):
                    shape = (2, SubV.num_sub_elements())
                    MixedCpts.append(fd.TensorElement(SubV, shape))
                elif isinstance(SubV, fd.TensorElement):
                    shape = (2, ) + SubV._shape
                    MixedCpts.append(fd.TensorElement(SubV, shape))
                else:
                    raise NotImplementedError

            dim = len(MixedCpts)
            self.CblockV = np.prod(
                [fd.FunctionSpace(mesh, MixedCpts[i]) for i in range(dim)])
        else:
            self.ncpts = 1
            if isinstance(Ve, fd.FiniteElement):
                self.CblockV = fd.FunctionSpace(mesh,
                                                fd.VectorElement(Ve, dim=2))
            elif isinstance(Ve, fd.VectorElement):
                shape = (2, Ve.num_sub_elements())
                self.CblockV = fd.FunctionSpace(mesh,
                                                fd.TensorElement(Ve, shape))
            elif isinstance(Ve, fd.TensorElement):
                shape = (2, ) + Ve._shape
                self.CblockV = fd.FunctionSpace(mesh,
                                                fd.TensorElement(Ve, shape))
            else:
                raise NotImplementedError

        # Now need to build the block solver
        vs = fd.TestFunctions(self.CblockV)
        self.u0 = fd.Function(self.CblockV)  # we will create a linearisation
        us = fd.split(self.u0)

        # extract the real and imaginary parts
        vsr = []
        vsi = []
        usr = []
        usi = []

        if isinstance(Ve, fd.MixedElement):
            N = Ve.num_sub_elements()
            for i in range(N):
                SubV = Ve.sub_elements()[i]
                if len(SubV.value_shape()) == 0:
                    vsr.append(vs[i][0])
                    vsi.append(vs[i][1])
                    usr.append(us[i][0])
                    usi.append(us[i][1])
                elif len(SubV.value_shape()) == 1:
                    vsr.append(vs[i][0, :])
                    vsi.append(vs[i][1, :])
                    usr.append(us[i][0, :])
                    usi.append(us[i][1, :])
                elif len(SubV.value_shape()) == 2:
                    vsr.append(vs[i][0, :, :])
                    vsi.append(vs[i][1, :, :])
                    usr.append(us[i][0, :, :])
                    usi.append(us[i][1, :, :])
                else:
                    raise NotImplementedError
        else:
            if isinstance(Ve, fd.FiniteElement):
                vsr.append(vs[0])
                vsi.append(vs[1])
                usr.append(us[0])
                usi.append(us[1])
            elif isinstance(Ve, fd.VectorElement):
                vsr.append(vs[0, :])
                vsi.append(vs[1, :])
                usr.append(self.u0[0, :])
                usi.append(self.u0[1, :])
            elif isinstance(Ve, fd.TensorElement):
                vsr.append(vs[0, :])
                vsi.append(vs[1, :])
                usr.append(self.u0[0, :])
                usi.append(self.u0[1, :])
            else:
                raise NotImplementedError

        # input and output functions
        self.Jprob_in = fd.Function(self.CblockV)
        self.Jprob_out = fd.Function(self.CblockV)

        # A place to store all the inputs to the block problems
        self.xfi = fd.Function(W)
        self.xfr = fd.Function(W)

        #  Building the nonlinear operator
        self.Jsolvers = []
        self.Js = []
        form_mass = appctx.get("form_mass", None)
        form_function = appctx.get("form_function", None)

        # setting up the Riesz map
        # input for the Riesz map
        self.xtemp = fd.Function(self.CblockV)
        v = fd.TestFunction(self.CblockV)
        u = fd.TrialFunction(self.CblockV)
        a = fd.assemble(fd.inner(u, v) * fd.dx)
        self.Proj = fd.LinearSolver(a, options_prefix=prefix + "mass_")
        # building the block problem solvers
        for i in range(M):
            D1i = fd.Constant(np.imag(self.D1[i]))
            D1r = fd.Constant(np.real(self.D1[i]))
            D2i = fd.Constant(np.imag(self.D2[i]))
            D2r = fd.Constant(np.real(self.D2[i]))

            # pass sigma into PC:
            sigma = self.D1[i]**2 / self.D2[i]
            sigma_inv = self.D2[i]**2 / self.D1[i]
            appctx_h = appctx.copy()
            appctx_h["sr"] = fd.Constant(np.real(sigma))
            appctx_h["si"] = fd.Constant(np.imag(sigma))
            appctx_h["sinvr"] = fd.Constant(np.real(sigma_inv))
            appctx_h["sinvi"] = fd.Constant(np.imag(sigma_inv))
            appctx_h["D2r"] = D2r
            appctx_h["D2i"] = D2i
            appctx_h["D1r"] = D1r
            appctx_h["D1i"] = D1i

            A = (D1r * form_mass(*usr, *vsr) - D1i * form_mass(*usi, *vsr) +
                 D2r * form_function(*usr, *vsr) -
                 D2i * form_function(*usi, *vsr) +
                 D1r * form_mass(*usi, *vsi) + D1i * form_mass(*usr, *vsi) +
                 D2r * form_function(*usi, *vsi) +
                 D2i * form_function(*usr, *vsi))

            # The linear operator
            J = fd.derivative(A, self.u0)

            # The rhs
            v = fd.TestFunction(self.CblockV)
            L = fd.inner(v, self.Jprob_in) * fd.dx

            block_prefix = prefix + str(i) + '_'
            jprob = fd.LinearVariationalProblem(J, L, self.Jprob_out)
            Jsolver = fd.LinearVariationalSolver(jprob,
                                                 appctx=appctx_h,
                                                 options_prefix=block_prefix)
            self.Jsolvers.append(Jsolver)