Beispiel #1
0
    def advective_flux(self, **kwargs):
        keys = ('energy', 'velocity', 'vertical_velocity', 'thickness',
                'energy_inflow', 'energy_surface')
        keys_alt = ('E', 'u', 'w', 'h', 'E_inflow', 'E_surface')
        E, u, w, h, E_inflow, E_surface = get_kwargs_alt(
            kwargs, keys, keys_alt)

        Q = E.function_space()
        ψ = firedrake.TestFunction(Q)

        U = firedrake.as_vector((u[0], u[1], w))
        flux_cells = -E * inner(U, grad(ψ)) * h * dx

        mesh = Q.mesh()
        ν = facet_normal_2(mesh)
        outflow = firedrake.max_value(inner(u, ν), 0)
        inflow = firedrake.min_value(inner(u, ν), 0)

        flux_outflow = (E * outflow * ψ * h * ds_v +
                        E * firedrake.max_value(-w, 0) * ψ * h * ds_b +
                        E * firedrake.max_value(+w, 0) * ψ * h * ds_t)

        flux_inflow = (E_inflow * inflow * ψ * h * ds_v +
                       E_surface * firedrake.min_value(-w, 0) * ψ * h * ds_b +
                       E_surface * firedrake.min_value(+w, 0) * ψ * h * ds_t)

        return flux_cells + flux_outflow + flux_inflow
Beispiel #2
0
def Bueler_profile(mesh, R):
    x, y = firedrake.SpatialCoordinate(mesh)
    r = firedrake.sqrt(x**2 + y**2)
    h_divide = (2 * R * (alpha/A0)**(1/n) * (n-1)/n)**(n/(2*n+2))
    h_part2 = (n+1)*(r/R) - n*(r/R)**((n+1)/n) + n*(max_value(1-(r/R),0))**((n+1)/n) - 1
    h_expr = (h_divide/((n-1)**(n/(2*n+2)))) * (max_value(h_part2,0))**(n/(2*n+2))
    return h_expr
Beispiel #3
0
    def advective_flux(self, **kwargs):
        keys = (
            "energy",
            "velocity",
            "vertical_velocity",
            "thickness",
            "energy_inflow",
            "energy_surface",
        )
        E, u, w, h, E_inflow, E_surface = itemgetter(*keys)(kwargs)

        Q = E.function_space()
        ψ = firedrake.TestFunction(Q)

        # NOTE: Be careful here going to xz! You might have to separate this into
        # the sum of a horizontal and vertical flux if we're shadowing Firedrake's
        # grad operator with out own specialized one.
        U = firedrake.as_vector((u[0], u[1], w))
        flux_cells = -E * inner(U, grad(ψ)) * h * dx

        ν = FacetNormal(Q.mesh())
        outflow = firedrake.max_value(inner(u, ν), 0)
        inflow = firedrake.min_value(inner(u, ν), 0)

        flux_outflow = (E * outflow * ψ * h * ds_v +
                        E * firedrake.max_value(-w, 0) * ψ * h * ds_b +
                        E * firedrake.max_value(+w, 0) * ψ * h * ds_t)

        flux_inflow = (E_inflow * inflow * ψ * h * ds_v +
                       E_surface * firedrake.min_value(-w, 0) * ψ * h * ds_b +
                       E_surface * firedrake.min_value(+w, 0) * ψ * h * ds_t)

        return flux_cells + flux_outflow + flux_inflow
Beispiel #4
0
    def _advect(self, dt, E, u, w, h, s, E_inflow, E_surface):
        Q = E.function_space()
        φ, ψ = firedrake.TrialFunction(Q), firedrake.TestFunction(Q)

        U = firedrake.as_vector((u[0], u[1], w))
        flux_cells = -φ * inner(U, grad(ψ)) * h * dx

        mesh = Q.mesh()
        ν = facet_normal_2(mesh)
        outflow = firedrake.max_value(inner(u, ν), 0)
        inflow = firedrake.min_value(inner(u, ν), 0)

        flux_outflow = φ * ψ * outflow * h * ds_v + \
                       φ * ψ * firedrake.max_value(-w, 0) * h * ds_b + \
                       φ * ψ * firedrake.max_value(+w, 0) * h * ds_t
        F = φ * ψ * h * dx + dt * (flux_cells + flux_outflow)

        flux_inflow = -E_inflow * ψ * inflow * h * ds_v \
                      -E_surface * ψ * firedrake.min_value(-w, 0) * h * ds_b \
                      -E_surface * ψ * firedrake.min_value(+w, 0) * h * ds_t
        A = E * ψ * h * dx + dt * flux_inflow

        solver_parameters = {'ksp_type': 'preonly', 'pc_type': 'lu'}
        degree_E = E.ufl_element().degree()
        degree_u = u.ufl_element().degree()
        degree = (3 * degree_E[0] + degree_u[0], 2 * degree_E[1] + degree_u[1])
        form_compiler_parameters = {'quadrature_degree': degree}
        firedrake.solve(F == A,
                        E,
                        solver_parameters=solver_parameters,
                        form_compiler_parameters=form_compiler_parameters)
Beispiel #5
0
def getModelVelocity(baseName, Q, V, minSigma=5, maxSigma=100):
    """Read in a tiff velocity data set and return
    firedrake interpolate functions.

    Parameters
    ----------
    baseName : str
        baseName should be of the form pattern.*.abc or pattern
        The wildcard (*) will be filled with the suffixes (vx, vy.)
        e.g.,pattern.vx.abc.tif, pattern.vy.abc.tif.
    Q : firedrake function space
        function space
    V : firedrake vector space
        vector space
    Returns
    -------
    uObs firedrake interp function on V
        velocity (m/yr)
    speed firedrake interp function on Q
        speed in (m)
    sigmaX firedrake interp function on Q
        vx error (m)
    sigmaY firedrake interp function on Q
        vy error (m)
    """
    # suffixes for products used
    suffixes = ['vx', 'vy', 'ex', 'ey']
    rasters = {}
    # prep baseName - baseName.*.xyz.tif or baseName.*
    if '*' not in baseName:
        baseName += '.*'
    if '.tif' not in baseName:
        baseName += '.tif'
    # read data
    for suffix in suffixes:
        myBand = baseName.replace('*', suffix)
        if not os.path.exists(myBand):
            u.myerror(f'Velocity/error file - {myBand} - does not exist')
        rasters[suffix] = rasterio.open(myBand, 'r')
    # Firedrake interpolators
    uObs = icepack.interpolate((rasters['vx'], rasters['vy']), V)
    # force error to be at least 1 to avoid 0 or negatives.
    sigmaX = icepack.interpolate(rasters['ex'], Q)
    sigmaX = icepack.interpolate(firedrake.max_value(sigmaX, minSigma), Q)
    sigmaX = icepack.interpolate(firedrake.min_value(sigmaX, maxSigma), Q)
    sigmaY = icepack.interpolate(rasters['ey'], Q)
    sigmaY = icepack.interpolate(firedrake.max_value(sigmaY, minSigma), Q)
    sigmaY = icepack.interpolate(firedrake.min_value(sigmaY, maxSigma), Q)
    speed = icepack.interpolate(firedrake.sqrt(inner(uObs, uObs)), Q)
    # return results
    return uObs, speed, sigmaX, sigmaY
Beispiel #6
0
def test_shallow_ice_prognostic_solve():
    R = Constant(500e3)
    num_refinements = 4
    mesh = firedrake.UnitDiskMesh(num_refinements)
    mesh.coordinates.dat.data[:] *= float(R)
    T = Constant(254.15)
    A = icepack.rate_factor(T)

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

    x, y = firedrake.SpatialCoordinate(mesh)
    r = firedrake.sqrt(x**2 + y**2)

    β = Constant(0.5)
    h_divide = Constant(4e3)
    h_expr = h_divide * firedrake.max_value(0, 1 - (r / (β * R))**2)
    h_0 = interpolate(h_expr, Q)
    h = h_0.copy(deepcopy=True)
    u = firedrake.Function(V)

    b = Constant(0.0)
    s = interpolate(b + h, Q)
    a = Constant(0.0)

    model = icepack.models.ShallowIce()
    solver = icepack.solvers.FlowSolver(model)

    final_time = 100.0
    dt = 1.0
    num_steps = int(final_time / dt)

    for step in range(num_steps):
        u = solver.diagnostic_solve(velocity=u,
                                    thickness=h,
                                    surface=s,
                                    fluidity=A)

        h = solver.prognostic_solve(dt,
                                    thickness=h,
                                    velocity=u,
                                    accumulation=a)

        h.interpolate(firedrake.max_value(0, h))
        s.assign(b + h)

    error = abs(assemble(h * dx) / assemble(h_0 * dx) - 1)
    assert error < 1 / 2**(num_refinements + 1)
Beispiel #7
0
def terminus(u, h, s):
    r"""Return the terminal stress part of the hybrid model action functional

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

    .. math::
        E(u) = \int_\Gamma\int_0^1\left(\rho_Ig(1 - \zeta) -
        \rho_Wg(\zeta_{\text{sl}} - \zeta)_+\right)u\cdot\nu\; h\, d\zeta\; ds

    where :math:`\zeta_\text{sl}` is the relative depth to sea level and the
    :math:`(\zeta_\text{sl} - \zeta)_+` denotes only the positive part.

    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
    """
    mesh = u.ufl_domain()
    zdegree = u.ufl_element().degree()[1]

    x, y, ζ = firedrake.SpatialCoordinate(mesh)
    b = s - h
    ζ_sl = firedrake.max_value(-b, 0) / h
    p_W = ρ_W * g * h * _pressure_approx(zdegree + 1)(ζ, ζ_sl)
    p_I = ρ_I * g * h * (1 - ζ)

    ν = facet_normal_2(mesh)
    return (p_I - p_W) * inner(u, ν) * h
Beispiel #8
0
    def solve(self, dt, **kwargs):
        if not hasattr(self, '_solvers'):
            self._setup(**kwargs)
        else:
            for name, field in kwargs.items():
                if isinstance(field, firedrake.Function):
                    self.fields[name].assign(field)

        δt = self._timestep
        δt.assign(dt)
        D = self.fields.get('damage', self.fields.get('D'))

        solver1, solver2, solver3 = self._solvers
        D1, D2 = self._stages
        dD = self._damage_change

        solver1.solve()
        D1.assign(D + dD)
        solver2.solve()
        D2.assign(3 / 4 * D + 1 / 4 * (D1 + dD))
        solver3.solve()
        D.assign(1 / 3 * D + 2 / 3 * (D2 + dD))

        S = self.model.sources(**self.fields)
        D.project(min_value(max_value(D + δt * S, 0), 1))
        return D.copy(deepcopy=True)
Beispiel #9
0
    def flux(self, **kwargs):
        keys = ("damage", "velocity", "damage_inflow")
        D, u, D_inflow = itemgetter(*keys)(kwargs)

        Q = D.function_space()
        φ = firedrake.TestFunction(Q)

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

        u_n = max_value(0, inner(u, n))
        f = D * u_n
        flux_faces = (f("+") - f("-")) * (φ("+") - φ("-")) * dS
        flux_cells = -D * div(u * φ) * dx
        flux_out = D * max_value(0, inner(u, n)) * φ * ds
        flux_in = D_inflow * min_value(0, inner(u, n)) * φ * ds

        return flux_faces + flux_cells + flux_out + flux_in
Beispiel #10
0
def reduceNearGLBeta(s, sOrig, zF, grounded, Q, thresh, limit=False):
    """Compute beta reduction in area near grounding line where the height
    above floation is less than thresh.

    Parameters
    ----------
    s : firedrake function
        Current surface.
    sOrig : firedrake function
        Original surface.
    zF : firedrake function
        Flotation height.
    grounded : firedrake function
        grounde mask
    Q : firedrake function space
        scaler function space for model
    thresh : float
        Threshold to determine where to reduce beta (zAbove < thresh)
    """
    # compute original height above flotation for grounded region
    sAboveOrig = (sOrig - zF) * grounded
    # avoid negative/zero values
    sAboveOrig = firedrake.max_value(sAboveOrig, 0.0)
    # Current height above flotation with negative values zeroed out.
    sAbove = firedrake.max_value((s - zF) * grounded, 0)
    # mask so only areas less than thresh but grounded
    sMask = (sAbove < thresh) * grounded
    # print(f'{sAbove.dat.data_ro.min()}, {sAbove.dat.data_ro.max()} {thresh}')
    # Inverse mask
    sMaskInv = sMask < 1
    # scale = fraction of original height above flotation
    # Use 5 to avoid potentially large ratio at small values.
    scaleBeta = sAbove / \
        firedrake.max_value(firedrake.min_value(thresh, sAboveOrig), 3)
    if limit:
        scaleBeta = firedrake.min_value(3, scaleBeta)
    scaleBeta = scaleBeta * sMask + sMaskInv
    # scaleBeta = icepack.interpolate(firedrake.min_value(scaleBeta,1.),Q)
    # sqrt so tau = scale * beta^2
    # scaleBeta = icepack.interpolate(firedrake.sqrt(scaleBeta) * grounded, Q)
    # Removed grounded above because grounded is always applied in friction
    scaleBeta = icepack.interpolate(firedrake.sqrt(scaleBeta), Q)
    # print(f'{scaleBeta.dat.data_ro.min()}, {scaleBeta.dat.data_ro.max()}')
    return scaleBeta
Beispiel #11
0
    def flux(self, **kwargs):
        keys = ('damage', 'velocity', 'damage_inflow')
        keys_alt = ('D', 'u', 'D_inflow')
        D, u, D_inflow = get_kwargs_alt(kwargs, keys, keys_alt)

        Q = D.function_space()
        φ = firedrake.TestFunction(Q)

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

        u_n = max_value(0, inner(u, n))
        f = D * u_n
        flux_faces = (f('+') - f('-')) * (φ('+') - φ('-')) * dS
        flux_cells = -D * div(u * φ) * dx
        flux_out = D * max_value(0, inner(u, n)) * φ * ds
        flux_in = D_inflow * min_value(0, inner(u, n)) * φ * ds

        return flux_faces + flux_cells + flux_out + flux_in
Beispiel #12
0
def readSMB(SMBfile, Q):
    ''' Read SMB file an limit values to +/- 6 to avoid no data values

    Returns water equivalent values.
    '''
    if not os.path.exists:
        myerror(f'readSMB: SMB file  ({SMBfile}) does not exist')
    SMB = mf.getModelVarFromTiff(SMBfile, Q)
    # avoid any unreasonably large value
    SMB = icepack.interpolate(
        firedrake.max_value(firedrake.min_value(SMB, 6), -6), Q)
    return SMB
Beispiel #13
0
    def solve(self, dt, h0, a, u, h_inflow=None):
        r"""Propagate the thickness forward by one timestep

        This function uses the implicit Euler timestepping scheme to avoid
        the stability issues associated to using continuous finite elements
        for advection-type equations. The implicit Euler scheme is stable
        for any timestep; you do not need to ensure that the CFL condition
        is satisfied in order to get an answer. Nonetheless, keeping the
        timestep within the CFL bound is a good idea for accuracy.

        Parameters
        ----------
        dt : float
            Timestep
        h0 : firedrake.Function
            Initial ice thickness
        a : firedrake.Function
            Sum of accumulation and melt rates
        u : firedrake.Function
            Ice velocity
        h_inflow : firedrake.Function
            Thickness of the upstream ice that advects into the domain

        Returns
        -------
        h : firedrake.Function
            Ice thickness at `t + dt`
        """
        grad, ds = self.grad, self.ds

        h_inflow = h_inflow if h_inflow is not None else h0

        Q = h0.function_space()
        h, φ = firedrake.TrialFunction(Q), firedrake.TestFunction(Q)

        n = self.facet_normal(Q.mesh())
        outflow = firedrake.max_value(inner(u, n), 0)
        inflow = firedrake.min_value(inner(u, n), 0)

        flux_cells = -h * inner(u, grad(φ)) * dx
        flux_out = h * φ * outflow * ds
        F = h * φ * dx + dt * (flux_cells + flux_out)

        accumulation = a * φ * dx
        flux_in = -h_inflow * φ * inflow * ds
        A = h0 * φ * dx + dt * (accumulation + flux_in)

        h = h0.copy(deepcopy=True)
        solver_parameters = {'ksp_type': 'preonly', 'pc_type': 'lu'}
        firedrake.solve(F == A, h, solver_parameters=solver_parameters)

        return h
Beispiel #14
0
def reduceNearGLBeta(s, sOrig, zF, grounded, Q, thresh, limit=False):
    """Compute beta reduction in area near grounding line where the height
    above floation is less than thresh.

    Parameters
    ----------
    s : firedrake function
        Current surface.
    sOrig : firedrake function
        Original surface.
    zF : firedrake function
        Flotation height.
    grounded : firedrake function
        grounde mask
    Q : firedrake function space
        scaler function space for model
    thresh : float
        Threshold to determine where to reduce beta (zAbove < thresh)
    """
    # compute original height above flotation for grounded region
    sAboveOrig = (sOrig - zF) * grounded
    # avoid negative/zero values
    sAboveOrig = firedrake.max_value(sAboveOrig, 0.001)
    # Current height above flotation with negative values zeroed out.
    sAbove = firedrake.max_value((s - zF) * grounded, 0)
    # mask so only areas less than thresh but grounded
    sMask = (sAbove <= thresh) * grounded
    # Inverse mask
    sMaskInv = sMask < 1
    # scale = fraction of of original height above flotation
    scaleBeta = sAbove / \
        firedrake.max_value(firedrake.min_value(thresh, sAboveOrig), 5)
    if limit:
        scaleBeta = firedrake.min_value(3, scaleBeta)
    scaleBeta = scaleBeta * sMask + sMaskInv
    # scaleBeta = icepack.interpolate(firedrake.min_value(scaleBeta,1.),Q)
    # sqrt so tau = scale * beta^2
    scaleBeta = icepack.interpolate(firedrake.sqrt(scaleBeta) * grounded, Q)
    return scaleBeta
Beispiel #15
0
def friction(**kwargs):
    keys = ('velocity', 'thickness', 'surface', 'friction')
    u, h, s, C = map(kwargs.get, keys)

    p_W = ρ_W * g * max_value(0, -(s - h))
    p_I = ρ_I * g * h
    N = p_I - p_W
    τ_c = N / 2

    u_c = (τ_c / C)**m
    u_b = sqrt(inner(u, u))

    return τ_c * ((u_c**(1 / m + 1) + u_b**(1 / m + 1))**(m / (m + 1)) - u_c)
Beispiel #16
0
    def solve(self, dt, h0, a, u, h_inflow=None):
        r"""Propagate the thickness forward by one timestep

        This function uses an implicit second-order Taylor-Galerkin (also
        known as Lax-Wendroff) scheme to solve the conservative advection
        equation for ice thickness.

        Parameters
        ----------
        dt : float
            Timestep
        h0 : firedrake.Function
            Initial ice thickness
        a : firedrake.Function
            Sum of accumulation and melt rates
        u : firedrake.Function
            Ice velocity
        h_inflow : firedrake.Function
            Thickness of the upstream ice that advects into the domain

        Returns
        -------
        h : firedrake.Function
            Ice thickness at `t + dt`
        """
        grad, div, ds = self.grad, self.div, self.ds

        h_inflow = h_inflow if h_inflow is not None else h0

        Q = h0.function_space()
        h, φ = firedrake.TrialFunction(Q), firedrake.TestFunction(Q)

        n = self.facet_normal(Q.mesh())
        outflow = firedrake.max_value(inner(u, n), 0)
        inflow = firedrake.min_value(inner(u, n), 0)

        flux_cells = -h * inner(u, grad(φ)) * dx
        flux_cells_lax = 0.5 * dt * div(h * u) * inner(u, grad(φ)) * dx
        flux_out = (h - 0.5 * dt * div(h * u)) * φ * outflow * ds
        F = h * φ * dx + dt * (flux_cells + flux_cells_lax + flux_out)

        accumulation = a * φ * dx
        flux_in = -(h_inflow - 0.5 * dt * div(h0 * u)) * φ * inflow * ds
        A = h0 * φ * dx + dt * (accumulation + flux_in)

        h = h0.copy(deepcopy=True)
        solver_parameters = {'ksp_type': 'preonly', 'pc_type': 'lu'}
        firedrake.solve(F == A, h, solver_parameters=solver_parameters)

        return h
Beispiel #17
0
def test_mass_transport_solver_convergence(solver_type):
    Lx, Ly = 1.0, 1.0
    u0 = 1.0
    h_in, dh = 1.0, 0.2

    delta_x, error = [], []
    model = icepack.models.IceShelf()
    for N in range(24, 97, 4):
        delta_x.append(Lx / N)

        mesh = firedrake.RectangleMesh(N, N, Lx, Ly)
        x, y = firedrake.SpatialCoordinate(mesh)

        degree = 1
        V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=degree)
        Q = firedrake.FunctionSpace(mesh, family='CG', degree=degree)
        solver = icepack.solvers.FlowSolver(
            model, prognostic_solver_type=solver_type
        )

        h0 = interpolate(h_in - dh * x / Lx, Q)
        a = firedrake.Function(Q)
        u = interpolate(firedrake.as_vector((u0, 0)), V)
        T = 0.5
        δx = 1.0 / N
        δt = δx / u0
        num_timesteps = int(T / δt)

        h = h0.copy(deepcopy=True)
        for step in range(num_timesteps):
            h = solver.prognostic_solve(
                δt,
                thickness=h,
                velocity=u,
                accumulation=a,
                thickness_inflow=h0
            )

        z = x - u0 * num_timesteps * δt
        h_exact = interpolate(h_in - dh/Lx * firedrake.max_value(0, z), Q)
        error.append(norm(h - h_exact) / norm(h_exact))
        print(delta_x[-1], error[-1])

    log_delta_x = np.log2(np.array(delta_x))
    log_error = np.log2(np.array(error))
    slope, intercept = np.polyfit(log_delta_x, log_error, 1)

    print('log(error) ~= {:g} * log(dx) + {:g}'.format(slope, intercept))
    assert slope > degree - 0.1
Beispiel #18
0
def compute_surface(h, b):
    r"""Return the ice surface elevation consistent with a given
    thickness and bathymetry

    If the bathymetry beneath a tidewater glacier is too low, the ice
    will go afloat. The surface elevation of a floating ice shelf is

    .. math::
       s = (1 - \rho_I / \rho_W)h,

    provided everything is in hydrostatic balance.
    """
    Q = h.ufl_function_space()
    s_expr = firedrake.max_value(h + b, (1 - ρ_I / ρ_W) * h)
    return firedrake.interpolate(s_expr, Q)
Beispiel #19
0
    def __call__(self, dt, **kwargs):
        keys = ('thickness', 'velocity', 'accumulation')
        keys_alt = ('h', 'u', 'a')
        h, u, a = utilities.get_kwargs_alt(kwargs, keys, keys_alt)
        h_inflow = kwargs.get('thickness_inflow', kwargs.get('h_inflow', h))

        Q = h.function_space()
        q = firedrake.TestFunction(Q)

        grad, ds, n = self.grad, self.ds, self.facet_normal(Q.mesh())
        u_n = inner(u, n)
        flux_cells = -inner(h * u, grad(q)) * dx
        flux_out = h * firedrake.max_value(u_n, 0) * q * ds
        flux_in = h_inflow * firedrake.min_value(u_n, 0) * q * ds
        accumulation = a * q * dx
        return accumulation - (flux_in + flux_out + flux_cells)
Beispiel #20
0
def test_converting_fields():
    δT = 5.0
    T_surface = Tm - δT
    T_expr = firedrake.min_value(Tm, T_surface + 2 * δT * (1 - ζ))
    f_expr = firedrake.max_value(0, 0.0033 * (1 - 2 * ζ))

    model = icepack.models.HeatTransport3D()
    E = firedrake.project(model.energy_density(T_expr, f_expr), Q)
    f = firedrake.project(model.meltwater_fraction(E), Q)
    T = firedrake.project(model.temperature(E), Q)

    avg_meltwater = firedrake.assemble(f * ds_b) / (Lx * Ly)
    assert avg_meltwater > 0

    avg_temp = firedrake.assemble(T * h * dx) / firedrake.assemble(h * dx)
    assert (avg_temp > T_surface) and (avg_temp < Tm)
Beispiel #21
0
    def equation(q):
        Q = q.function_space()
        φ = firedrake.TestFunction(Q)

        F = q * u

        mesh = Q.mesh()
        n = firedrake.FacetNormal(mesh)
        c = abs(inner(u, n))

        sources = s * φ * dx
        fluxes = (forms.cell_flux(F, φ) + forms.central_facet_flux(F, φ) +
                  forms.lax_friedrichs_facet_flux(q, c, φ) +
                  q * max_value(0, inner(u, n)) * φ * ds +
                  q_in * min_value(0, inner(u, n)) * φ * ds)

        return sources - fluxes
Beispiel #22
0
    def setup(self, **kwargs):
        r"""Create the internal data structures that help reuse information
        from past prognostic solves"""
        for name, field in kwargs.items():
            if name in self._fields.keys():
                self._fields[name].assign(field)
            else:
                if isinstance(field, firedrake.Constant):
                    self._fields[name] = firedrake.Constant(field)
                elif isinstance(field, firedrake.Function):
                    self._fields[name] = field.copy(deepcopy=True)
                else:
                    raise TypeError(
                        "Input %s field has type %s, must be Constant or Function!"
                        % (name, type(field))
                    )

        dt = firedrake.Constant(1.0)
        h = self._fields["thickness"]
        u = self._fields["velocity"]
        h_0 = h.copy(deepcopy=True)

        Q = h.function_space()
        mesh = Q.mesh()
        n = FacetNormal(mesh)
        outflow = firedrake.max_value(0, inner(u, n))
        inflow = firedrake.min_value(0, inner(u, n))

        # Additional streamlining terms that give 2nd-order accuracy
        q = firedrake.TestFunction(Q)
        ds = firedrake.ds if mesh.layers is None else firedrake.ds_v
        flux_cells = -div(h * u) * inner(u, grad(q)) * dx
        flux_out = div(h * u) * q * outflow * ds
        flux_in = div(h_0 * u) * q * inflow * ds
        d2h_dt2 = flux_cells + flux_out + flux_in

        dh_dt = self._continuity(dt, **self._fields)
        F = (h - h_0) * q * dx - dt * (dh_dt + 0.5 * dt * d2h_dt2)

        problem = firedrake.NonlinearVariationalProblem(F, h)
        self._solver = firedrake.NonlinearVariationalSolver(
            problem, solver_parameters=self._solver_parameters
        )

        self._thickness_old = h_0
        self._timestep = dt
Beispiel #23
0
    def __call__(self, dt, **kwargs):
        keys = ("thickness", "velocity", "accumulation")
        h, u, a = itemgetter(*keys)(kwargs)
        h_inflow = kwargs.get("thickness_inflow", h)

        Q = h.function_space()
        q = firedrake.TestFunction(Q)

        mesh = Q.mesh()
        n = FacetNormal(mesh)
        ds = firedrake.ds if mesh.layers is None else firedrake.ds_v

        u_n = inner(u, n)
        flux_cells = -inner(h * u, grad(q)) * dx
        flux_out = h * firedrake.max_value(u_n, 0) * q * ds
        flux_in = h_inflow * firedrake.min_value(u_n, 0) * q * ds
        accumulation = a * q * dx
        return accumulation - (flux_in + flux_out + flux_cells)
Beispiel #24
0
def compute_surface(**kwargs):
    r"""Return the ice surface elevation consistent with a given
    thickness and bathymetry

    If the bathymetry beneath a tidewater glacier is too low, the ice
    will go afloat. The surface elevation of a floating ice shelf is

    .. math::
       s = (1 - \rho_I / \rho_W)h,

    provided everything is in hydrostatic balance.
    """
    # TODO: Remove the 'h' and 'b' arguments once these are deprecated.
    h = kwargs.get('thickness', kwargs.get('h'))
    b = kwargs.get('bed', kwargs.get('b'))

    Q = h.ufl_function_space()
    s_expr = firedrake.max_value(h + b, (1 - ρ_I / ρ_W) * h)
    return firedrake.interpolate(s_expr, Q)
Beispiel #25
0
    def setup(self, **kwargs):
        r"""Create the internal data structures that help reuse information
        from past prognostic solves"""
        for name, field in kwargs.items():
            if name in self._fields.keys():
                self._fields[name].assign(field)
            else:
                if isinstance(field, firedrake.Constant):
                    self._fields[name] = firedrake.Constant(field)
                elif isinstance(field, firedrake.Function):
                    self._fields[name] = field.copy(deepcopy=True)
                else:
                    raise TypeError(
                        'Input fields must be Constant or Function!')

        dt = firedrake.Constant(1.)
        h = self._fields.get('thickness', self._fields.get('h'))
        u = self._fields.get('velocity', self._fields.get('u'))
        h_0 = h.copy(deepcopy=True)

        Q = h.function_space()
        model = self._continuity
        n = model.facet_normal(Q.mesh())
        outflow = firedrake.max_value(0, inner(u, n))
        inflow = firedrake.min_value(0, inner(u, n))

        # Additional streamlining terms that give 2nd-order accuracy
        q = firedrake.TestFunction(Q)
        div, grad, ds = model.div, model.grad, model.ds
        flux_cells = -div(h * u) * inner(u, grad(q)) * dx
        flux_out = div(h * u) * q * outflow * ds
        flux_in = div(h_0 * u) * q * inflow * ds
        d2h_dt2 = flux_cells + flux_out + flux_in

        dh_dt = model(dt, **self._fields)
        F = (h - h_0) * q * dx - dt * (dh_dt + 0.5 * dt * d2h_dt2)

        problem = firedrake.NonlinearVariationalProblem(F, h)
        self._solver = firedrake.NonlinearVariationalSolver(
            problem, solver_parameters=self._solver_parameters)

        self._thickness_old = h_0
        self._timestep = dt
Beispiel #26
0
def penalty(**kwargs):
    r"""Return the penalty of the shallow ice action functional

    The penalty for the shallow ice action functional is

    .. math::
        E(u) = \frac{1}{2}\int_\Omega l^2\nabla u\cdot \nabla u\; dx

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

    Returns
    -------
    firedrake.Form
    """
    u, h = get_kwargs_alt(kwargs, ('velocity', 'thickness'), ('u', 'h'))
    l = 2 * firedrake.max_value(firedrake.CellDiameter(u.ufl_domain()), 5 * h)
    return .5 * l**2 * inner(grad(u), grad(u))
Beispiel #27
0
def betaInit(s, h, speed, V, Q, Q1, grounded, inversionParams):
    """Compute intitial beta using 0.5 taud.
    Parameters
    ----------
    s : firedrake function
        model surface elevation
    h : firedrake function
        model thickness
    speed : firedrake function
        modelled speed
    V : firedrake vector function space
        vector function space
    Q : firedrake function space
        scalar function space
    grounded : firedrake function
        Mask with 1s for grounded 0 for floating.
    """
    # Use a result from prior inversion
    checkFile = inversionParams['initFile']
    Quse = Q
    if inversionParams['initWithDeg1']:
        checkFile = f'{inversionParams["inversionResult"]}.deg1'
        Quse = Q1
    if checkFile is not None:
        betaTemp = mf.getCheckPointVars(checkFile, 'betaInv', Quse)['betaInv']
        beta1 = icepack.interpolate(betaTemp, Q)
        return beta1
    # No prior result, so use fraction of taud
    tauD = firedrake.project(-rhoI * g * h * grad(s), V)
    #
    stress = firedrake.sqrt(firedrake.inner(tauD, tauD))
    Print('stress', firedrake.assemble(stress * firedrake.dx))
    fraction = firedrake.Constant(0.95)
    U = max_value(speed, 1)
    C = fraction * stress / U**(1/m)
    if inversionParams['friction'] == 'schoof':
        mExp = 1/m + 1
        U0 = firedrake.Constant(inversionParams['uThresh'])
        C = C * (m/(m+1)) * (U0**mExp + U**mExp)**(1/(m+1))
    beta = firedrake.interpolate(firedrake.sqrt(C) * grounded, Q)
    return beta
Beispiel #28
0
def test_mass_transport_solver_convergence():
    Lx, Ly = 1.0, 1.0
    u0 = 1.0
    h_in, dh = 1.0, 0.2

    delta_x, error = [], []
    mass_transport = MassTransport()
    for N in range(16, 97, 4):
        delta_x.append(Lx / N)

        mesh = firedrake.RectangleMesh(N, N, Lx, Ly)
        x, y = firedrake.SpatialCoordinate(mesh)

        degree = 1
        V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=degree)
        Q = firedrake.FunctionSpace(mesh, family='CG', degree=degree)

        h = interpolate(h_in - dh * x / Lx, Q)
        a = firedrake.Function(Q)
        u = interpolate(firedrake.as_vector((u0, 0)), V)
        T = 0.5
        num_timesteps = int(0.5 * N * u0 * T / Lx)
        dt = T / num_timesteps

        for k in range(num_timesteps):
            h = mass_transport.solve(dt, h0=h, a=a, u=u)

        z = x - u0 * T
        h_exact = interpolate(h_in - dh / Lx * firedrake.max_value(0, z), Q)
        error.append(norm(h - h_exact) / norm(h_exact))

        print(delta_x[-1], error[-1])

    log_delta_x = np.log2(np.array(delta_x))
    log_error = np.log2(np.array(error))
    slope, intercept = np.polyfit(log_delta_x, log_error, 1)

    assert slope > degree - 0.05
    print(slope, intercept)
Beispiel #29
0
def flotationHeight(zb, Q, rhoI=rhoI, rhoW=rhoW):
    """Given bed elevation, determine height of flotation for function space Q.

    Parameters
    ----------
    zb  : firedrake interp function
        bed elevation (m)
    Q : firedrake function space
        function space
    rhoI : [type], optional
        [description], by default rhoI
    rhoW : [type], optional
        [description], by default rhoW
    Returns
    -------
    zF firedrake interp function
        Flotation height (m)
    """
    # computation for height above flotation
    zF = firedrake.interpolate(firedrake.max_value(-zb * (rhoW / rhoI - 1), 0),
                               Q)
    return zF
Beispiel #30
0
def terminus(u, h, s, ice_front_ids=()):
    r"""Return the terminal stress part of the hybrid model action functional

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

    .. math::
        E(u) = \int_\Gamma\int_0^1\left(\rho_Ig(1 - \zeta) -
        \rho_Wg(\zeta_{\text{sl}} - \zeta)_+\right)hd\zeta ds

    where :math:`\zeta_\text{sl}` is the relative depth to sea level and the
    :math:`(\zeta - \zeta_\text{sl})` denotes only the positive part.

    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
    """
    xdegree_u, zdegree_u = u.ufl_element().degree()
    degree_h = h.ufl_element().degree()[0]
    degree = (xdegree_u + degree_h, 2 * zdegree_u + 1)
    metadata = {'quadrature_degree': degree}

    x, y, ζ = firedrake.SpatialCoordinate(u.ufl_domain())
    b = s - h
    ζ_sl = firedrake.max_value(-b, 0) / h
    p_W = ρ_W * g * h * _pressure_approx(zdegree_u + 1)(ζ, ζ_sl)
    p_I = ρ_I * g * h * (1 - ζ)

    ν = facet_normal_2(u.ufl_domain())
    dγ = ds_v(tuple(ice_front_ids), metadata=metadata)
    return (p_I - p_W) * inner(u, ν) * h * dγ