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())
Exemple #2
0
def sym_grad(u):
    r"""Compute the symmetric gradient of a vector field"""
    axes = get_mesh_axes(u.ufl_domain())
    if axes == "xy":
        return firedrake.sym(firedrake.grad(u))
    if axes == "xyz":
        return firedrake.sym(firedrake.as_tensor((u.dx(0), u.dx(1))))
    return u.dx(0)
Exemple #3
0
    def get_weak_form(self, V):
        """
        mu is a spatially varying coefficient in the weak form
        of the elasticity equations. The idea is to make the mesh stiff near
        the boundary that is being deformed.
        """
        if self.fixed_bids is not None and len(self.fixed_bids) > 0:
            mu = self.get_mu(V)
        else:
            mu = fd.Constant(1.0)

        u = fd.TrialFunction(V)
        v = fd.TestFunction(V)
        return mu * fd.inner(fd.sym(fd.grad(u)), fd.sym(fd.grad(v))) * fd.dx
Exemple #4
0
def horizontal_strain(u, s, h):
    r"""Calculate the horizontal strain rate with corrections for terrain-
    following coordinates"""
    ζ = firedrake.SpatialCoordinate(u.ufl_domain())[2]
    b = s - h
    v = -((1 - ζ) * grad_2(b) + ζ * grad_2(s)) / h
    du_dζ = u.dx(2)
    return sym(grad_2(u)) + 0.5 * (outer(du_dζ, v) + outer(v, du_dζ))
Exemple #5
0
    def value_form(self):
        f = self.f

        def norm(u):
            return fd.inner(u, u) * fd.dx
        val = self.l2_reg * norm(f)
        val += self.sym_grad_reg * norm(fd.sym(fd.grad(f)))
        val += self.skew_grad_reg * norm(fd.skew(fd.grad(f)))
        return val
Exemple #6
0
def test_damage_transport():
    nx, ny = 32, 32
    Lx, Ly = 20e3, 20e3
    mesh = firedrake.RectangleMesh(nx, ny, Lx, Ly)
    x, y = firedrake.SpatialCoordinate(mesh)

    V = firedrake.VectorFunctionSpace(mesh, "CG", 2)
    Q = firedrake.FunctionSpace(mesh, "CG", 2)

    u0 = 100.0
    h0, dh = 500.0, 100.0
    T = 268.0

    ρ = ρ_I * (1 - ρ_I / ρ_W)
    Z = icepack.rate_factor(T) * (ρ * g * h0 / 4)**n
    q = 1 - (1 - (dh / h0) * (x / Lx))**(n + 1)
    du = Z * q * Lx * (h0 / dh) / (n + 1)

    u = interpolate(as_vector((u0 + du, 0)), V)
    h = interpolate(h0 - dh * x / Lx, Q)
    A = firedrake.Constant(icepack.rate_factor(T))

    S = firedrake.TensorFunctionSpace(mesh, "DG", 1)
    ε = firedrake.project(sym(grad(u)), S)
    M = firedrake.project(membrane_stress(strain_rate=ε, fluidity=A), S)

    degree = 1
    Δ = firedrake.FunctionSpace(mesh, "DG", degree)
    D_inflow = firedrake.Constant(0.0)
    D = firedrake.Function(Δ)

    damage_model = icepack.models.DamageTransport()
    damage_solver = icepack.solvers.DamageSolver(damage_model)

    final_time = Lx / u0
    max_speed = u.at((Lx - 1.0, Ly / 2), tolerance=1e-10)[0]
    δx = Lx / nx
    timestep = δx / max_speed / (2 * degree + 1)
    num_steps = int(final_time / timestep)
    dt = final_time / num_steps

    for step in range(num_steps):
        D = damage_solver.solve(
            dt,
            damage=D,
            velocity=u,
            strain_rate=ε,
            membrane_stress=M,
            damage_inflow=D_inflow,
        )

    Dmax = D.dat.data_ro[:].max()
    assert 0 < Dmax < 1
Exemple #7
0
def viscosity_depth_averaged(u=None, h=None, A=None, **kwargs):
    r"""Return the viscous part of the action for depth-averaged models

    The viscous component of the action for depth-averaged ice flow is

    .. math::
        E(u) = \frac{n}{n+1}\int_\Omega h\cdot
        M(\dot\varepsilon, A):\dot\varepsilon\; dx

    where :math:`M(\dot\varepsilon, A)` is the membrane stress tensor

    .. math::
        M(\dot\varepsilon, A) = A^{-1/n}|\dot\varepsilon|^{1/n - 1}
        (\dot\varepsilon + \text{tr}\dot\varepsilon\cdot I).

    This form assumes that we're using the fluidity parameter instead
    the rheology parameter, the temperature, etc. To use a different
    variable, you can implement your own viscosity functional and pass it
    as an argument when initializing model objects to use your functional
    instead.

    Parameters
    ----------
    velocity : firedrake.Function
    thickness : firedrake.Function
    fluidity : firedrake.Function

    Returns
    -------
    firedrake.Form
    """
    # NOTE: This mess is for backwards-compatibility, so users can still pass
    # in the velocity, thickness, and fluidity as positional arguments if they
    # are still using old code.
    if (u is not None) or (h is not None) or (A is not None):
        warnings.warn("Abbreviated names (u, h, A) have been deprecated, use "
                      "full names (velocity, thickness, fluidity) instead.",
                      FutureWarning)

    if u is None:
        u = kwargs['velocity']
    if h is None:
        h = kwargs['thickness']
    if A is None:
        A = kwargs['fluidity']

    ε = sym(grad(u))
    M = membrane_stress(ε, A)
    return n / (n + 1) * h * inner(M, ε)
Exemple #8
0
def test_eigenvalues():
    nx, ny = 32, 32
    mesh = firedrake.UnitSquareMesh(nx, ny)
    x, y = firedrake.SpatialCoordinate(mesh)

    V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=2)
    u = interpolate(as_vector((x, 0)), V)

    Q = firedrake.FunctionSpace(mesh, family='DG', degree=2)
    ε = sym(grad(u))
    Λ1, Λ2 = eigenvalues(ε)
    λ1 = firedrake.project(Λ1, Q)
    λ2 = firedrake.project(Λ2, Q)

    assert norm(λ1 - Constant(1)) < norm(u) / (nx * ny)
    assert norm(λ2) < norm(u) / (nx * ny)
Exemple #9
0
def test_solver_no_flow_region():
    mesh = fd.Mesh("./2D_mesh.msh")
    no_flow = [2]
    no_flow_markers = [1]
    mesh = mark_no_flow_regions(mesh, no_flow, no_flow_markers)
    P2 = fd.VectorElement("CG", mesh.ufl_cell(), 1)
    P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1)
    TH = P2 * P1
    W = fd.FunctionSpace(mesh, TH)
    (v, q) = fd.TestFunctions(W)

    # Stokes 1
    w_sol1 = fd.Function(W)
    nu = fd.Constant(0.05)
    F = NavierStokesBrinkmannForm(W, w_sol1, nu, beta_gls=2.0)

    x, y = fd.SpatialCoordinate(mesh)
    u_mms = fd.as_vector(
        [sin(2.0 * pi * x) * sin(pi * y),
         sin(pi * x) * sin(2.0 * pi * y)])
    p_mms = -0.5 * (u_mms[0]**2 + u_mms[1]**2)
    f_mms_u = (grad(u_mms) * u_mms + grad(p_mms) -
               2.0 * nu * div(sym(grad(u_mms))))
    f_mms_p = div(u_mms)
    F += -inner(f_mms_u, v) * dx - f_mms_p * q * dx
    bc1 = fd.DirichletBC(W.sub(0), u_mms, "on_boundary")
    bc2 = fd.DirichletBC(W.sub(1), p_mms, "on_boundary")
    bc_no_flow = InteriorBC(W.sub(0), fd.Constant((0.0, 0.0)), no_flow_markers)

    solver_parameters = {"ksp_max_it": 500, "ksp_monitor": None}

    problem1 = fd.NonlinearVariationalProblem(F,
                                              w_sol1,
                                              bcs=[bc1, bc2, bc_no_flow])
    solver1 = NavierStokesBrinkmannSolver(
        problem1,
        options_prefix="navier_stokes",
        solver_parameters=solver_parameters,
    )
    solver1.solve()
    u_sol, _ = w_sol1.split()
    u_mms_func = fd.interpolate(u_mms, W.sub(0))
    error = fd.errornorm(u_sol, u_mms_func)
    assert error < 0.07
Exemple #10
0
def run_solver(r):
    mesh = fd.UnitSquareMesh(2**r, 2**r)
    P2 = fd.VectorElement("CG", mesh.ufl_cell(), 1)
    P1 = fd.FiniteElement("CG", mesh.ufl_cell(), 1)
    TH = P2 * P1
    W = fd.FunctionSpace(mesh, TH)
    (v, q) = fd.TestFunctions(W)

    # Stokes 1
    w_sol1 = fd.Function(W)
    nu = fd.Constant(0.05)
    F = NavierStokesBrinkmannForm(W, w_sol1, nu, beta_gls=2.0)

    from firedrake import sin, grad, pi, sym, div, inner

    x, y = fd.SpatialCoordinate(mesh)
    u_mms = fd.as_vector(
        [sin(2.0 * pi * x) * sin(pi * y),
         sin(pi * x) * sin(2.0 * pi * y)])
    p_mms = -0.5 * (u_mms[0]**2 + u_mms[1]**2)
    f_mms_u = (grad(u_mms) * u_mms + grad(p_mms) -
               2.0 * nu * div(sym(grad(u_mms))))
    f_mms_p = div(u_mms)
    F += -inner(f_mms_u, v) * dx - f_mms_p * q * dx
    bc1 = fd.DirichletBC(W.sub(0), u_mms, "on_boundary")
    bc2 = fd.DirichletBC(W.sub(1), p_mms, "on_boundary")

    solver_parameters = {"ksp_max_it": 200}

    problem1 = fd.NonlinearVariationalProblem(F, w_sol1, bcs=[bc1, bc2])
    solver1 = NavierStokesBrinkmannSolver(
        problem1,
        options_prefix="navier_stokes",
        solver_parameters=solver_parameters,
    )
    solver1.solve()
    u_sol, _ = w_sol1.split()
    fd.File("test_u_sol.pvd").write(u_sol)
    u_mms_func = fd.interpolate(u_mms, W.sub(0))
    error = fd.errornorm(u_sol, u_mms_func)
    print(f"Error: {error}")
    return error
Exemple #11
0
    def sources(self, **kwargs):
        keys = ('damage', 'velocity', 'fluidity')
        keys_alt = ('D', 'u', 'A')
        D, u, A = get_kwargs_alt(kwargs, keys, keys_alt)

        # Increase/decrease damage depending on stress and strain rates
        ε = sym(grad(u))
        ε_1 = eigenvalues(ε)[0]

        σ = M(ε, A)
        σ_e = sqrt(inner(σ, σ) - det(σ))

        ε_h = firedrake.Constant(self.healing_strain_rate)
        σ_d = firedrake.Constant(self.damage_stress)
        γ_h = firedrake.Constant(self.healing_rate)
        γ_d = firedrake.Constant(self.damage_rate)

        healing = γ_h * min_value(ε_1 - ε_h, 0)
        fracture = γ_d * conditional(σ_e - σ_d > 0, ε_1, 0.) * (1 - D)

        return healing + fracture
Exemple #12
0
 def surf_grad(u):
     return fd.sym(fd.grad(u) - fd.outer(fd.grad(u) * n, n))
    def solve(self, dt, D0, u, A, D_inflow=None, **kwargs):
        r"""Propogate the damage forward by one timestep

        This function uses a Runge-Kutta scheme to upwind damage
        (limiting damage diffusion) while sourcing and sinking
        damage assocaited with crevasse opening/crevasse healing

        Parameters
        ----------
        dt : float
            Timestep
        D0 : firedrake.Function
            initial damage feild should be discontinuous
        u : firedrake.Function
            Ice velocity
        A : firedrake.Function
            fluidity parameter
        D_inflow : firedrake.Function
            Damage of the upstream ice that advects into the domain

        Returns
        -------
        D : firedrake.Function
            Ice damage at `t + dt`
        """

        D_inflow = D_inflow if D_inflow is not None else D0
        Q = D0.function_space()
        dD, φ = firedrake.TrialFunction(Q), firedrake.TestFunction(Q)
        d = φ * dD * dx
        D = D0.copy(deepcopy=True)

        n = firedrake.FacetNormal(Q.mesh())

        un = 0.5 * (inner(u, n) + abs(inner(u, n)))
        L1 = dt * (D * div(φ * u) * dx - φ * max_value(inner(u, n), 0) * D * ds
                   - φ * min_value(inner(u, n), 0) * D_inflow * ds -
                   (φ('+') - φ('-')) *
                   (un('+') * D('+') - un('-') * D('-')) * dS)
        D1 = firedrake.Function(Q)
        D2 = firedrake.Function(Q)
        L2 = firedrake.replace(L1, {D: D1})
        L3 = firedrake.replace(L1, {D: D2})

        dq = firedrake.Function(Q)

        # Three-stage strong structure-preserving Runge Kutta (SSPRK3) method
        params = {
            'ksp_type': 'preonly',
            'pc_type': 'bjacobi',
            'sub_pc_type': 'ilu'
        }
        prob1 = firedrake.LinearVariationalProblem(d, L1, dq)
        solv1 = firedrake.LinearVariationalSolver(prob1,
                                                  solver_parameters=params)
        prob2 = firedrake.LinearVariationalProblem(d, L2, dq)
        solv2 = firedrake.LinearVariationalSolver(prob2,
                                                  solver_parameters=params)
        prob3 = firedrake.LinearVariationalProblem(d, L3, dq)
        solv3 = firedrake.LinearVariationalSolver(prob3,
                                                  solver_parameters=params)

        solv1.solve()
        D1.assign(D + dq)
        solv2.solve()
        D2.assign(0.75 * D + 0.25 * (D1 + dq))
        solv3.solve()
        D.assign((1.0 / 3.0) * D + (2.0 / 3.0) * (D2 + dq))

        # Increase/decrease damage depending on stress and strain rates
        ε = sym(grad(u))
        ε_1 = eigenvalues(ε)[0]

        σ = M(ε, A)
        σ_e = sqrt(inner(σ, σ) - det(σ))

        ε_h = firedrake.Constant(self.healing_strain_rate)
        σ_d = firedrake.Constant(self.damage_stress)
        γ_h = firedrake.Constant(self.healing_rate)
        γ_d = firedrake.Constant(self.damage_rate)

        healing = γ_h * min_value(ε_1 - ε_h, 0)
        fracture = γ_d * conditional(σ_e - σ_d > 0, ε_1, 0.) * (1 - D)

        # Clamp damage field to [0, 1]
        D.project(min_value(max_value(D + dt * (healing + fracture), 0), 1))
        return D
Exemple #14
0
def ε(u):
    r"""Calculate the strain rate for a given flow velocity"""
    return sym(grad(u))
Exemple #15
0
def D(x):
    return 2*fd.sym(fd.grad(x))
Exemple #16
0
 def ε(u):
     return sym(grad(u))
Exemple #17
0
 def epsilon(v):
     return sym(nabla_grad(v))
Exemple #18
0
h = h0.copy(deepcopy=True)
u = flow_solver.diagnostic_solve(
    velocity=u0, thickness=h, fluidity=A, damage=D
)

final_time = args.final_time
num_timesteps = args.num_steps
dt = final_time / num_timesteps
a = firedrake.Constant(0.0)

for step in tqdm.trange(num_timesteps):
    h = flow_solver.prognostic_solve(
        dt, thickness=h, accumulation=a, velocity=u, thickness_inflow=h0
    )
    if args.damage:
        ε = firedrake.project(sym(grad(u)), S)
        M = firedrake.project((1 - D) * membrane_stress(ε, A), S)
        D = damage_solver.solve(
            dt,
            damage=D,
            velocity=u,
            strain_rate=ε,
            membrane_stress=M,
            damage_inflow=D_inflow
        )

    u = flow_solver.diagnostic_solve(
        velocity=u, thickness=h, fluidity=A, damage=D
    )

output_name = os.path.splitext(args.output)[0]
Exemple #19
0
 def epsilon(u):
     return sym(nabla_grad(u))
Exemple #20
0
                                        Q,
                                        fixed_bids=fixed_bids,
                                        direct_solve=True)

# import IPython; IPython.embed()

res = [0, 1, 10, 50, 100, 150, 200, 250, 300, 400, 499, 625, 750, 875, 999]
res = [r for r in res if r <= optre - 1]
if res[-1] != optre - 1:
    res.append(optre - 1)
results = run_solver(solver, res, args)
# import sys; sys.exit()

u, _ = fd.split(solver.z)
nu = solver.nu
objective_form = nu * fd.inner(fd.sym(fd.grad(u)), fd.sym(fd.grad(u))) * fd.dx
solver.setup_adjoint(objective_form)
solver.solver_adjoint.solve()

# import sys; sys.exit()


class Constraint(fs.PdeConstraint):
    def solve(self):
        super().solve()
        solver.solve(optre)


class Objective(fs.ShapeObjective):
    def __init__(self, *args_, **kwargs_):
        super().__init__(*args_, **kwargs_)