コード例 #1
0
def test_diagnostic_solver_convergence():
    # Create an ice shelf model
    ice_shelf = icepack.models.IceShelf()
    opts = {'dirichlet_ids': [1], 'side_wall_ids': [3, 4], 'tol': 1e-12}

    # Solve the ice shelf model for successively higher mesh resolution
    for degree in range(1, 4):
        delta_x, error = [], []
        for N in range(16, 97 - 32 * (degree - 1), 4):
            mesh = firedrake.RectangleMesh(N, N, Lx, Ly)
            x, y = firedrake.SpatialCoordinate(mesh)

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

            u_exact = interpolate(as_vector((exact_u(x), 0)), V)
            u_guess = interpolate(u_exact + as_vector((perturb_u(x, y), 0)), V)

            h = interpolate(h0 - dh * x / Lx, Q)
            A = interpolate(firedrake.Constant(icepack.rate_factor(T)), Q)

            u = ice_shelf.diagnostic_solve(h=h, A=A, u0=u_guess, **opts)
            error.append(norm(u_exact - u) / norm(u_exact))
            delta_x.append(Lx / N)

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

        # Fit the error curve and check that the convergence rate is what we
        # expect
        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.8
コード例 #2
0
def test_vector_field():
    Nx, Ny = 16, 16
    mesh2d = firedrake.UnitSquareMesh(Nx, Ny)
    mesh3d = firedrake.ExtrudedMesh(mesh2d, layers=1)
    x, y, z = firedrake.SpatialCoordinate(mesh3d)

    V3D = firedrake.VectorFunctionSpace(mesh3d,
                                        "CG",
                                        2,
                                        vfamily="GL",
                                        vdegree=5,
                                        dim=2)
    u3d = firedrake.interpolate(firedrake.as_vector((1 - z**4, 0)), V3D)
    u_avg = depth_average(u3d)

    V2D = firedrake.VectorFunctionSpace(mesh2d, "CG", 2)
    x, y = firedrake.SpatialCoordinate(mesh2d)
    u2d = firedrake.interpolate(firedrake.as_vector((4 / 5, 0)), V2D)

    assert norm(u_avg - u2d) / norm(u2d) < 1 / (Nx * Ny)**2

    V0 = firedrake.VectorFunctionSpace(mesh3d,
                                       "CG",
                                       2,
                                       vfamily="GL",
                                       vdegree=0,
                                       dim=2)
    u_lift = lift3d(u_avg, V0)
    assert norm(depth_average(u_lift) - u2d) / norm(u2d) < 1 / (Nx * Ny)**2
コード例 #3
0
ファイル: depth_average_test.py プロジェクト: vic1309/icepack
def test_scalar_field():
    Nx, Ny = 16, 16
    mesh2d = firedrake.UnitSquareMesh(Nx, Ny)
    mesh3d = firedrake.ExtrudedMesh(mesh2d, layers=1)
    x, y, z = firedrake.SpatialCoordinate(mesh3d)

    Q3D = firedrake.FunctionSpace(mesh3d,
                                  family='CG',
                                  degree=2,
                                  vfamily='GL',
                                  vdegree=5)
    q3d = firedrake.interpolate((x**2 + y**2) * (1 - z**4), Q3D)
    q_avg = depth_average(q3d)

    p3d = firedrake.interpolate(x**2 + y**2, Q3D)
    p_avg = depth_average(p3d, weight=1 - z**4)

    Q2D = firedrake.FunctionSpace(mesh2d, family='CG', degree=2)
    x, y = firedrake.SpatialCoordinate(mesh2d)
    q2d = firedrake.interpolate(4 * (x**2 + y**2) / 5, Q2D)

    assert q_avg.ufl_domain() is mesh2d
    assert norm(q_avg - q2d) / norm(q2d) < 1 / (Nx * Ny)**2
    assert norm(p_avg - q2d) / norm(q2d) < 1 / (Nx * Ny)**2

    Q0 = firedrake.FunctionSpace(mesh3d,
                                 family='CG',
                                 degree=2,
                                 vfamily='GL',
                                 vdegree=0)
    q_lift = lift3d(q_avg, Q0)
    assert norm(depth_average(q_lift) - q2d) / norm(q2d) < 1 / (Nx * Ny)**2
コード例 #4
0
def get_aspect_ratios2d(mesh, python=False):
    """
    Computes the aspect ratio of each cell in a 2D triangular mesh

    :arg mesh: the input mesh to do computations on
    :kwarg python: compute the measure using Python?

    :rtype: firedrake.function.Function aspect_ratios with
        aspect ratio data
    """
    P0 = firedrake.FunctionSpace(mesh, "DG", 0)
    if python:
        P0_ten = firedrake.TensorFunctionSpace(mesh, "DG", 0)
        J = firedrake.interpolate(ufl.Jacobian(mesh), P0_ten)
        edge1 = ufl.as_vector([J[0, 0], J[1, 0]])
        edge2 = ufl.as_vector([J[0, 1], J[1, 1]])
        edge3 = edge1 - edge2
        a = ufl.sqrt(ufl.dot(edge1, edge1))
        b = ufl.sqrt(ufl.dot(edge2, edge2))
        c = ufl.sqrt(ufl.dot(edge3, edge3))
        aspect_ratios = firedrake.interpolate(
            a * b * c / ((a + b - c) * (b + c - a) * (c + a - b)), P0
        )
    else:
        coords = mesh.coordinates
        aspect_ratios = firedrake.Function(P0)
        op2.par_loop(
            get_pyop2_kernel("get_aspect_ratio", 2),
            mesh.cell_set,
            aspect_ratios.dat(op2.WRITE, aspect_ratios.cell_node_map()),
            coords.dat(op2.READ, coords.cell_node_map()),
        )
    return aspect_ratios
コード例 #5
0
def test_advection_diffusion():
    E_initial = firedrake.interpolate(E_surface + q_bed / α * h * (1 - ζ), Q)
    E = E_initial.copy(deepcopy=True)

    u0 = 100.0
    du = 100.0
    u_expr = as_vector((u0 + du * x / Lx, 0))
    u = firedrake.interpolate(u_expr, V)
    w = firedrake.interpolate((-du / Lx + dh / Lx / h * u[0]) * ζ, W)

    dt = 10.0
    final_time = Lx / u0
    num_steps = int(final_time / dt) + 1
    model = icepack.models.HeatTransport3D()
    solver = icepack.solvers.HeatTransportSolver(model)
    for step in range(num_steps):
        E = solver.solve(
            dt,
            energy=E,
            velocity=u,
            vertical_velocity=w,
            thickness=h,
            surface=s,
            heat=Constant(0),
            heat_bed=Constant(q_bed),
            energy_inflow=E_initial,
            energy_surface=Constant(E_surface),
        )

    rms = np.sqrt(assemble(E**2 * h * dx) / assemble(h * dx))
    assert (E_surface - 5 < rms) and (rms < E_surface + 5 + q_bed / α * h0)
コード例 #6
0
def test_advection_diffusion():
    E_initial = firedrake.interpolate(E_surface + q_bed / α * h * (1 - ζ), Q)
    E = E_initial.copy(deepcopy=True)

    u0 = 100.0
    du = 100.0
    u_expr = as_vector((u0 + du * x / Lx, 0))
    u = firedrake.interpolate(u_expr, V)
    w = firedrake.interpolate((-du / Lx + dh / Lx / h * u[0]) * ζ, W)

    dt = 10.0
    final_time = Lx / u0
    num_steps = int(final_time / dt) + 1
    model = icepack.models.HeatTransport3D()
    for step in range(num_steps):
        E = model.solve(dt,
                        E0=E,
                        u=u,
                        w=w,
                        h=h,
                        s=s,
                        q=Constant(0),
                        q_bed=q_bed,
                        E_inflow=E_initial,
                        E_surface=Constant(E_surface))

    rms = np.sqrt(assemble(E**2 * h * dx) / assemble(h * dx))
    assert (E_surface - 5 < rms) and (rms < E_surface + 5 + q_bed / α * h0)
コード例 #7
0
def test_diagnostic_solver_convergence():
    shallow_ice = icepack.models.ShallowIce()

    for degree in range(1, 4):
        delta_x, error = [], []
        for N in range(10, 110 - 20 * (degree - 1), 10):

            mesh = make_mesh(R_mesh, R / N)

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

            h_expr = Bueler_profile(mesh, R)
            u_exact = interpolate(exact_u(h_expr, Q), V)

            h = interpolate(h_expr, Q)
            s = interpolate(h_expr, Q)
            u = firedrake.Function(V)

            u_num = shallow_ice.diagnostic_solve(u0=u, h=h, s=s, A=A)
            error.append(norm(u_exact - u_num) / norm(u_exact))
            delta_x.append(R / N)

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

            assert assemble(shallow_ice.scale(u=u_num)) > 0

        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 > 0.9
コード例 #8
0
 def update_phi(self):
     # phi_tmp=firedrake.interpolate(self.phi,self.V_cg)
     self.phi_prev = firedrake.interpolate(self.phi_prev, self.V_cg)
     phi_tmp = firedrake.interpolate(self.phi, self.V_cg)
     self.phi_prev.assign(phi_tmp)
     self.update_N()
     self.update_pfo()
コード例 #9
0
def test_advection():
    E_initial = firedrake.interpolate(E_surface + q_bed / α * h * (1 - ζ), Q)
    E = E_initial.copy(deepcopy=True)

    u0 = 100.0
    du = 100.0
    u_expr = as_vector((u0 + du * x / Lx, 0))
    u = firedrake.interpolate(u_expr, V)
    w = firedrake.interpolate((-du / Lx + dh / Lx / h * u[0]) * ζ, W)

    dt = 10.0
    final_time = Lx / u0
    num_steps = int(final_time / dt) + 1
    model = icepack.models.HeatTransport3D()
    for step in range(num_steps):
        model._advect(dt,
                      E=E,
                      u=u,
                      w=w,
                      h=h,
                      s=s,
                      E_inflow=E_initial,
                      E_surface=Constant(E_surface))

    error_surface = assemble((E - E_surface)**2 * ds_t)
    assert error_surface / assemble(E_surface**2 * ds_t(mesh)) < 1e-2
    error_bed = assemble((E - E_initial)**2 * ds_b)
    assert error_bed / assemble(E_initial**2 * ds_b(mesh)) < 1e-2
コード例 #10
0
def test_diffusion(params):
    E_true = firedrake.interpolate(E_surface + q_bed / α * h * (1 - ζ), Q)
    E = firedrake.interpolate(Constant(480), Q)

    # Subclass the heat transport model and turn off advection so that we can
    # test diffusion by itself
    class DiffusionTransportModel(icepack.models.HeatTransport3D):
        def __init__(self):
            super(DiffusionTransportModel, self).__init__()

        def advective_flux(self, **kwargs):
            E = kwargs['energy']
            h = kwargs['thickness']
            Q = E.function_space()
            ψ = firedrake.TestFunction(Q)
            return firedrake.Constant(0) * ψ * h * dx

    model = DiffusionTransportModel()
    solver = icepack.solvers.HeatTransportSolver(model,
                                                 solver_parameters=params)

    dt = 250.0
    final_time = 6000
    num_steps = int(final_time / dt) + 1
    for step in range(num_steps):
        E = solver.solve(dt,
                         energy=E,
                         thickness=h,
                         energy_surface=Constant(E_surface),
                         heat=Constant(0),
                         heat_bed=Constant(q_bed))

    assert assemble((E - E_true)**2 * ds_t) / assemble(E_true**2 * ds_t) < 1e-3
    assert assemble((E - E_true)**2 * ds_b) / assemble(E_true**2 * ds_b) < 1e-3
コード例 #11
0
def test_diagnostic_solver_side_friction():
    model = icepack.models.IceShelf()
    opts = {"dirichlet_ids": [1], "side_wall_ids": [3, 4]}

    mesh = firedrake.RectangleMesh(32, 32, Lx, Ly)
    degree = 2
    V = firedrake.VectorFunctionSpace(mesh, "CG", degree)
    Q = firedrake.FunctionSpace(mesh, "CG", degree)

    x, y = firedrake.SpatialCoordinate(mesh)
    u_initial = interpolate(as_vector((exact_u(x), 0)), V)
    h = interpolate(h0 - dh * x / Lx, Q)
    A = interpolate(firedrake.Constant(icepack.rate_factor(T)), Q)

    # Choose the side wall friction coefficient so that, assuming the ice is
    # sliding at the maximum speed for the solution without friction, the
    # stress is 10 kPa.
    from icepack.constants import weertman_sliding_law as m

    τ = 0.01
    u_max = icepack.norm(u_initial, norm_type="Linfty")
    Cs = firedrake.Constant(τ * u_max**(-1 / m))

    solver = icepack.solvers.FlowSolver(model, **opts)
    fields = {
        "velocity": u_initial,
        "thickness": h,
        "fluidity": A,
        "side_friction": Cs
    }
    u = solver.diagnostic_solve(**fields)

    assert icepack.norm(u) < icepack.norm(u_initial)
コード例 #12
0
 def write_pvds(self):
     self.S.assign(firedrake.interpolate(self.S, self.V_cg))
     self.h.assign(firedrake.interpolate(self.h, self.V_cg))
     self.S_out << self.S
     self.h_out << self.h
     self.phi_out << self.phi
     self.pfo_out << self.pfo
コード例 #13
0
ファイル: misc.py プロジェクト: wathen/Firedrake_project
def Expression(f, V, degree_raise=None):
    if degree_raise != None:
        V = FunctionSpaceDegreeRaise(V, degree_raise)

    mesh_dim = V.mesh().cell_dimension()
    f_len = len(f)

    if mesh_dim == 1:
        x = SpatialCoordinate(V.mesh())
    elif mesh_dim == 2:
        (x, y) = SpatialCoordinate(V.mesh())
    elif mesh_dim == 3:
        (x, y, z) = SpatialCoordinate(V.mesh())

    f_eval = [None] * f_len
    for i in range(f_len):
        if 'x' not in f[i] and 'y' not in f[i] and 'z' not in f[i]:
            f_eval[i] = Constant(eval(f[i]))
        else:
            f_eval[i] = eval(f[i])

    if isinstance(f, list):
        if f_len == 1:
            out = interpolate(f_eval[0], V)
        elif f_len == 2:
            out = interpolate(as_vector([f_eval[0], f_eval[1]]), V)
        elif f_len == 3:
            out = interpolate(as_vector([f_eval[0], f_eval[1], f_eval[2]]), V)
        else:
            raise RuntimeError('Input list for function value is of '
                               'dimension {}, need to be at most a 3D '
                               'vector'.format(len(f)))
        return out
    else:
        raise RuntimeError('Input function f needs to be list')
コード例 #14
0
def test_diagnostic_solver():
    Nx, Ny = 32, 32
    mesh2d = firedrake.RectangleMesh(Nx, Ny, Lx, Ly)
    mesh = firedrake.ExtrudedMesh(mesh2d, layers=1)
    Q = firedrake.FunctionSpace(mesh,
                                family='CG',
                                degree=2,
                                vfamily='DG',
                                vdegree=0)

    x, y, ζ = firedrake.SpatialCoordinate(mesh)
    h = firedrake.interpolate(h0 - dh * x / Lx, Q)
    s = firedrake.interpolate(d + h0 - dh + ds * (1 - x / Lx), Q)
    u_expr = firedrake.as_vector(((0.95 + 0.05 * ζ) * exact_u(x), 0))

    A = firedrake.Constant(icepack.rate_factor(254.15))
    C = firedrake.Constant(0.001)

    model = icepack.models.HybridModel()
    opts = {'dirichlet_ids': [1, 3, 4], 'tol': 1e-12}

    max_degree = 5
    Nz = 32
    xs = np.array([(Lx / 2, Ly / 2, k / Nz) for k in range(Nz + 1)])
    us = np.zeros((max_degree + 1, Nz + 1))
    for vdegree in range(max_degree, 0, -1):
        solver = icepack.solvers.FlowSolver(model, **opts)
        V = firedrake.VectorFunctionSpace(mesh,
                                          dim=2,
                                          family='CG',
                                          degree=2,
                                          vfamily='GL',
                                          vdegree=vdegree)
        u0 = firedrake.interpolate(u_expr, V)
        u = solver.diagnostic_solve(velocity=u0,
                                    thickness=h,
                                    surface=s,
                                    fluidity=A,
                                    friction=C)

        V0 = firedrake.VectorFunctionSpace(mesh,
                                           dim=2,
                                           family='CG',
                                           degree=2,
                                           vfamily='DG',
                                           vdegree=0)

        depth_avg_u = firedrake.project(u, V0)
        shear_u = firedrake.project(u - depth_avg_u, V)
        assert icepack.norm(shear_u, norm_type='Linfty') > 1e-2

        us_center = np.array(u.at(xs, tolerance=1e-6))
        us[vdegree, :] = np.sqrt(np.sum(us_center**2, 1))

        norm = np.linalg.norm(us[max_degree, :])
        error = np.linalg.norm(us[vdegree, :] - us[max_degree, :]) / norm
        print(error, flush=True)
        assert error < 1e-2
コード例 #15
0
def test_diagnostic_solver_parameterization():
    # Define a new viscosity functional, parameterized in terms of the
    # rheology `B` instead of the fluidity `A`
    from firedrake import inner, grad, sym, tr as trace, Identity, sqrt

    def M(ε, B):
        I = Identity(2)
        tr_ε = trace(ε)
        ε_e = sqrt((inner(ε, ε) + tr_ε**2) / 2)
        μ = 0.5 * B * ε_e**(1/n - 1)
        return 2 * μ * (ε + tr_ε * I)

    def ε(u):
        return sym(grad(u))

    def viscosity(**kwargs):
        u = kwargs['velocity']
        h = kwargs['thickness']
        B = kwargs['rheology']
        return n/(n + 1) * h * inner(M(ε(u), B), ε(u))

    # Make a model object with our new viscosity functional
    model = icepack.models.IceShelf(viscosity=viscosity)
    opts = {'dirichlet_ids': [1, 3, 4]}

    # Same as before
    delta_x, error = [], []
    for N in range(16, 65, 4):
        mesh = firedrake.RectangleMesh(N, N, Lx, Ly)
        x, y = firedrake.SpatialCoordinate(mesh)

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

        u_exact = interpolate(as_vector((exact_u(x), 0)), V)
        u_guess = interpolate(as_vector((exact_u(x) + perturb_u(x, y), 0)), V)
        h = interpolate(h0 - dh * x / Lx, Q)
        B = interpolate(firedrake.Constant(icepack.rate_factor(T)**(-1/n)), Q)

        solver = icepack.solvers.FlowSolver(model, **opts)
        u = solver.diagnostic_solve(
            velocity=u_guess,
            thickness=h,
            rheology=B
        )
        error.append(norm(u_exact - u) / norm(u_exact))
        delta_x.append(Lx / N)

        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.05
コード例 #16
0
    def update_pfo(self):
        # Update water pressure
        self.update_pw()

        # Compute overburden pressure
        p_w_tmp = firedrake.interpolate(self.p_w, self.V_cg)
        p_i_tmp = firedrake.interpolate(self.p_i, self.V_cg)
        pfo_tmp = firedrake.assemble(p_w_tmp / p_i_tmp)
        self.pfo.vector().set_local(pfo_tmp.vector().array())
        self.pfo.vector().apply("insert")
コード例 #17
0
def test_poisson_inverse(solver_type):
    Nx, Ny = 32, 32
    mesh = firedrake.UnitSquareMesh(Nx, Ny)
    degree = 2
    Q = firedrake.FunctionSpace(mesh, "CG", degree)

    x, y = firedrake.SpatialCoordinate(mesh)
    q_true = interpolate(-4 * ((x - 0.5)**2 + (y - 0.5)**2), Q)
    f = interpolate(firedrake.Constant(1), Q)

    dirichlet_ids = [1, 2, 3, 4]
    model = PoissonModel()
    poisson_solver = PoissonSolver(model, dirichlet_ids=dirichlet_ids)
    u_bdry = firedrake.Function(Q)
    u_obs = poisson_solver.diagnostic_solve(u=u_bdry, q=q_true, f=f)

    q0 = firedrake.Function(Q)
    u0 = poisson_solver.diagnostic_solve(u=u_bdry, q=q0, f=f)

    def callback(inverse_solver):
        misfit = firedrake.assemble(inverse_solver.objective)
        regularization = firedrake.assemble(inverse_solver.regularization)
        q = inverse_solver.parameter
        error = firedrake.norm(q - q_true)
        print(misfit, regularization, error)

    L = firedrake.Constant(1e-4)
    problem = icepack.inverse.InverseProblem(
        model=model,
        objective=lambda u: 0.5 * (u - u_obs)**2 * dx,
        regularization=lambda q: 0.5 * L**2 * inner(grad(q), grad(q)) * dx,
        state_name="u",
        state=u0,
        parameter_name="q",
        parameter=q0,
        solver_type=PoissonSolver,
        solver_kwargs={"dirichlet_ids": dirichlet_ids},
        diagnostic_solve_kwargs={"f": f},
    )

    solver = solver_type(problem, callback)
    assert solver.state is not None
    assert icepack.norm(solver.state) > 0
    assert icepack.norm(solver.adjoint_state) > 0
    assert icepack.norm(solver.search_direction) > 0

    max_iterations = 1000
    iterations = solver.solve(rtol=2.5e-2,
                              atol=1e-8,
                              max_iterations=max_iterations)
    print(f"Number of iterations: {iterations}")

    assert iterations < max_iterations
    q = solver.parameter
    assert icepack.norm(q - q_true) < 0.25
コード例 #18
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
コード例 #19
0
def test_poisson_inverse(solver_type):
    Nx, Ny = 32, 32
    mesh = firedrake.UnitSquareMesh(Nx, Ny)
    degree = 2
    Q = firedrake.FunctionSpace(mesh, 'CG', degree)

    x, y = firedrake.SpatialCoordinate(mesh)
    q_true = interpolate(-4 * ((x - 0.5)**2 + (y - 0.5)**2), Q)
    f = interpolate(firedrake.Constant(1), Q)

    dirichlet_ids = [1, 2, 3, 4]
    model = PoissonModel()
    u_obs = model.solve(q=q_true, f=f, dirichlet_ids=dirichlet_ids)

    q0 = interpolate(firedrake.Constant(0), Q)
    u0 = model.solve(q=q0, f=f, dirichlet_ids=dirichlet_ids)

    def callback(inverse_solver):
        misfit = firedrake.assemble(inverse_solver.objective)
        regularization = firedrake.assemble(inverse_solver.regularization)
        q = inverse_solver.parameter
        error = firedrake.norm(q - q_true)
        print(misfit, regularization, error)

    L = firedrake.Constant(1e-4)
    problem = icepack.inverse.InverseProblem(
        model=model,
        method=PoissonModel.solve,
        objective=lambda u: 0.5 * (u - u_obs)**2 * dx,
        regularization=lambda q: L**2 / 2 * inner(grad(q), grad(q)) * dx,
        state_name='u',
        state=u0,
        parameter_name='q',
        parameter=q0,
        model_args={'f': f},
        dirichlet_ids=dirichlet_ids)

    solver = solver_type(problem, callback)
    assert solver.state is not None
    assert icepack.norm(solver.state) > 0
    assert icepack.norm(solver.adjoint_state) > 0
    assert icepack.norm(solver.search_direction) > 0

    max_iterations = 1000
    iterations = solver.solve(rtol=2.5e-2,
                              atol=1e-8,
                              max_iterations=max_iterations)
    print('Number of iterations: {}'.format(iterations))

    assert iterations < max_iterations
    q = solver.parameter
    assert icepack.norm(q - q_true) < 0.25
コード例 #20
0
def test_computing_surface():
    N = 16
    mesh = firedrake.RectangleMesh(N, N, Lx, Ly)
    degree = 2
    Q = firedrake.FunctionSpace(mesh, "CG", degree)

    x, y = firedrake.SpatialCoordinate(mesh)
    h = interpolate(h0 - dh * x / Lx, Q)
    b0 = ρ_I / ρ_W * (dh / 2 - h0)
    b = interpolate(firedrake.Constant(b0), Q)

    s = icepack.compute_surface(thickness=h, bed=b)
    x0, y0 = Lx / 2, Ly / 2
    assert abs(s((x0, y0)) - (1 - ρ_I / ρ_W) * h((x0, y0))) < 1e-8
コード例 #21
0
def test_ice_stream_prognostic_solve():
    Lx, Ly = 20e3, 20e3
    h0, dh = 500.0, 100.0
    T = 254.15
    u0 = 100.0

    model = icepack.models.IceStream()
    opts = {'dirichlet_ids': [1], 'side_wall_ids': [3, 4], 'tol': 1e-12}

    N = 32
    mesh = firedrake.RectangleMesh(N, N, Lx, Ly)

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

    x, y = firedrake.SpatialCoordinate(mesh)
    height_above_flotation = 10.0
    d = -ρ_I / ρ_W * (h0 - dh) + height_above_flotation
    ρ = ρ_I - ρ_W * d**2 / (h0 - dh)**2

    Z = icepack.rate_factor(T) * (ρ * g * h0 / 4)**n
    q = 1 - (1 - (dh / h0) * (x / Lx))**(n + 1)
    ux = u0 + Z * q * Lx * (h0 / dh) / (n + 1)
    u0 = interpolate(firedrake.as_vector((ux, 0)), V)

    thickness = h0 - dh * x / Lx
    β = 1 / 2
    α = β * ρ / ρ_I * dh / Lx
    h = interpolate(h0 - dh * x / Lx, Q)
    h_inflow = h.copy(deepcopy=True)
    ds = (1 + β) * ρ / ρ_I * dh
    s = interpolate(d + h0 - dh + ds * (1 - x / Lx), Q)
    b = interpolate(s - h, Q)

    C = interpolate(α * (ρ_I * g * thickness) * ux**(-1 / m), Q)
    A = firedrake.Constant(icepack.rate_factor(T))

    final_time, dt = 1.0, 1.0 / 12
    num_timesteps = int(final_time / dt)

    u = model.diagnostic_solve(u0=u0, h=h, s=s, C=C, A=A, **opts)
    a0 = firedrake.Constant(0)
    a = (model.prognostic_solve(dt, h0=h, a=a0, u=u) - h) / dt

    for k in range(num_timesteps):
        h = model.prognostic_solve(dt, h0=h, a=a, u=u, h_inflow=h_inflow)
        s = icepack.compute_surface(h=h, b=b)
        u = model.diagnostic_solve(u0=u, h=h, s=s, C=C, A=A, **opts)

    assert icepack.norm(h, norm_type='Linfty') < np.inf
コード例 #22
0
def test_ice_shelf_prognostic_solver(solver_type):
    ρ = ρ_I * (1 - ρ_I / ρ_W)

    Lx, Ly = 20.0e3, 20.0e3
    h0 = 500.0
    u0 = 100.0
    T = 254.15

    model = icepack.models.IceShelf(mass_transport=solver_type())
    opts = {'dirichlet_ids': [1], 'side_wall_ids': [3, 4], 'tol': 1e-12}

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

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

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

        q = (n + 1) * (ρ * g * h0 * u0 / 4)**n * icepack.rate_factor(T)
        ux = (u0**(n + 1) + q * x)**(1 / (n + 1))

        h = interpolate(h0 * u0 / ux, Q)
        h_initial = h.copy(deepcopy=True)

        A = firedrake.Constant(icepack.rate_factor(T))
        a = firedrake.Constant(0)

        u_guess = interpolate(firedrake.as_vector((ux, 0)), V)
        u = model.diagnostic_solve(u0=u_guess, h=h, A=A, **opts)

        final_time, dt = 1.0, 1.0 / 12
        num_timesteps = int(final_time / dt)

        for k in range(num_timesteps):
            h = model.prognostic_solve(dt, h0=h, a=a, u=u, h_inflow=h_initial)
            u = model.diagnostic_solve(u0=u, h=h, A=A, **opts)

        error.append(norm(h - h_initial) / norm(h_initial))
        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.05
コード例 #23
0
def test_vertical_velocity():

    Lx, Ly = 20e3, 20e3
    nx, ny = 48, 48

    mesh2d = firedrake.RectangleMesh(nx, ny, Lx, Ly)
    mesh = firedrake.ExtrudedMesh(mesh2d, layers=1)
    Q = firedrake.FunctionSpace(mesh,
                                family="CG",
                                degree=2,
                                vfamily="DG",
                                vdegree=0)
    Q3D = firedrake.FunctionSpace(mesh,
                                  family="DG",
                                  degree=2,
                                  vfamily="GL",
                                  vdegree=6)
    V = firedrake.VectorFunctionSpace(mesh,
                                      dim=2,
                                      family="CG",
                                      degree=2,
                                      vfamily="GL",
                                      vdegree=5)
    # note we should call the families in the vertical velocity solver.

    x, y, ζ = firedrake.SpatialCoordinate(mesh)

    u_inflow = 1.0
    v_inflow = 2.0
    mu = 0.003
    mv = 0.001

    b = firedrake.interpolate(firedrake.Constant(0.0), Q)
    s = firedrake.interpolate(firedrake.Constant(1000.0), Q)
    h = firedrake.interpolate(s - b, Q)
    u = firedrake.interpolate(
        firedrake.as_vector((mu * x + u_inflow, mv * y + v_inflow)), V)

    m = -0.01

    def analytic_vertical_velocity(h, ζ, mu, mv, m, Q3D):
        return firedrake.interpolate(
            firedrake.Constant(m) - (firedrake.Constant(mu + mv) * h * ζ), Q3D)

    w = firedrake.interpolate(
        icepack.utilities.vertical_velocity(u, h, m=m) * h, Q3D)
    w_analytic = analytic_vertical_velocity(h, ζ, mu, mv, m, Q3D)

    assert np.mean(np.abs(w.dat.data - w_analytic.dat.data)) < 10e-9
コード例 #24
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
コード例 #25
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)
コード例 #26
0
ファイル: plot.py プロジェクト: zxdhpc/firedrake
def _plot_2d_field(method_name,
                   function,
                   *args,
                   complex_component="real",
                   **kwargs):
    axes = kwargs.pop("axes", None)
    if axes is None:
        figure = plt.figure()
        axes = figure.add_subplot(111)

    if len(function.ufl_shape) == 1:
        mesh = function.ufl_domain()
        element = function.ufl_element().sub_elements()[0]
        Q = FunctionSpace(mesh, element)
        function = interpolate(sqrt(inner(function, function)), Q)

    num_sample_points = kwargs.pop("num_sample_points", 10)
    coords, vals, triangles = _two_dimension_triangle_func_val(
        function, num_sample_points)

    coords = toreal(coords, "real")
    x, y = coords[:, 0], coords[:, 1]
    triangulation = matplotlib.tri.Triangulation(x, y, triangles=triangles)

    method = getattr(axes, method_name)
    return method(triangulation, toreal(vals, complex_component), *args,
                  **kwargs)
コード例 #27
0
ファイル: plot.py プロジェクト: xywei/firedrake
def trisurf(function, *args, **kwargs):
    r"""Create a 3D surface plot of a 2D Firedrake :class:`~.Function`

    If the input function is a vector field, the magnitude will be plotted.

    :arg function: the Firedrake :class:`~.Function` to plot
    :arg args: same as for matplotlib :meth:`plot_trisurf <mpl_toolkits.mplot3d.axes3d.Axes3D.plot_trisurf>`
    :arg kwargs: same as for matplotlib
    :return: matplotlib :class:`Poly3DCollection <mpl_toolkits.mplot3d.art3d.Poly3DCollection>` object
    """
    axes = kwargs.pop("axes", None)
    if axes is None:
        figure = plt.figure()
        axes = figure.add_subplot(111, projection='3d')

    _kwargs = {"antialiased": False, "edgecolor": "none",
               "cmap": plt.rcParams["image.cmap"]}
    _kwargs.update(kwargs)

    mesh = function.ufl_domain()
    if mesh.geometric_dimension() == 3:
        return _trisurf_3d(axes, function, *args, **_kwargs)

    if len(function.ufl_shape) == 1:
        element = function.ufl_element().sub_elements()[0]
        Q = FunctionSpace(mesh, element)
        function = interpolate(sqrt(inner(function, function)), Q)

    num_sample_points = kwargs.pop("num_sample_points", 10)
    coords, vals, triangles = _two_dimension_triangle_func_val(function,
                                                               num_sample_points)
    x, y = coords[:, 0], coords[:, 1]
    triangulation = matplotlib.tri.Triangulation(x, y, triangles=triangles)
    _kwargs.update({"shade": False})
    return axes.plot_trisurf(triangulation, vals, *args, **_kwargs)
コード例 #28
0
ファイル: test_assemble.py プロジェクト: IvanYashchuk/fecr
def test_firedrake_forward():
    numpy_output, _, _, _, = evaluate_primal(assemble_firedrake, templates,
                                             *inputs)
    u1 = firedrake.interpolate(firedrake.Constant(1.0), V)
    J = assemble_firedrake(u1, firedrake.Constant(0.5),
                           firedrake.Constant(0.6))
    assert np.isclose(numpy_output, J)
コード例 #29
0
def _plot_2d_field(method_name,
                   function,
                   *args,
                   complex_component="real",
                   **kwargs):
    axes = kwargs.pop("axes", None)
    if axes is None:
        figure = plt.figure()
        axes = figure.add_subplot(111)

    Q = function.function_space()
    mesh = Q.mesh()
    if len(function.ufl_shape) == 1:
        element = function.ufl_element().sub_elements()[0]
        Q = FunctionSpace(mesh, element)
        function = interpolate(sqrt(inner(function, function)), Q)

    num_sample_points = kwargs.pop("num_sample_points", 10)
    function_plotter = FunctionPlotter(mesh, num_sample_points)
    triangulation = function_plotter.triangulation
    values = function_plotter(function)

    method = getattr(axes, method_name)
    return method(triangulation, toreal(values, complex_component), *args,
                  **kwargs)
コード例 #30
0
def test_plot_field():
    mesh = firedrake.UnitSquareMesh(32, 32)
    Q = firedrake.FunctionSpace(mesh, "CG", 1)
    x, y = firedrake.SpatialCoordinate(mesh)
    u = interpolate(x * y, Q)

    fig, axes = icepack.plot.subplots(nrows=2,
                                      ncols=2,
                                      sharex=True,
                                      sharey=True)

    filled_contours = icepack.plot.tricontourf(u, axes=axes[0, 0])
    assert filled_contours is not None
    colorbar = plt.colorbar(filled_contours, ax=axes[0, 0])
    assert colorbar is not None

    contours = icepack.plot.tricontour(u, axes=axes[0, 1])
    assert contours is not None

    colors_flat = icepack.plot.tripcolor(u, shading="flat", axes=axes[1, 0])
    assert colors_flat is not None

    colors_gouraud = icepack.plot.tripcolor(u,
                                            shading="gouraud",
                                            axes=axes[1, 1])
    assert colors_flat.get_array().shape != colors_gouraud.get_array().shape
コード例 #31
0
ファイル: eigendrake.py プロジェクト: rbpiccinini/codes
# We'll also need the test and trial functions corresponding to this
# function space::

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

# We declare a function over our function space and give it the
# value of our right hand side function::


# Permeability tensor
# Homogeneous
one = fd.Constant(1.0)
zero = fd.Constant(0.0)
Khom = fd.interpolate(one, V)
Khom.rename('K', 'Permeability')

# Heterogeneous
x, y, z = fd.SpatialCoordinate(W.mesh()) 
fx = fd.pi/Lx/2
fy = fd.pi/Ly/2
fz = fd.pi/Lz

Khet = fd.as_tensor(((1.0 + x, 0, 0), 
                      (0, 1.0+y, 0),
                       (0, 0, 1.0+z)))

#Khet = fd.as_tensor(((1,0,0),(0,1,0),(0,0,1)))
#Khet = fd.as_tensor(((1,0),(0,1)))
Khet = fd.Function(W).interpolate(Khet)
コード例 #32
0
ファイル: plot.py プロジェクト: firedrakeproject/firedrake
def plot_mesh(mesh, axes=None, surface=False, colors=None, **kwargs):
    """Plot a mesh.

    :arg mesh: The mesh to plot.
    :arg axes: Optional matplotlib axes to draw on.
    :arg surface: Plot surface of mesh only?
    :arg colors: Colour for the edges (passed to constructor of LineCollection)
    :arg **kwargs: Extra keyword arguments to pass to matplotlib.

    Note that high-order coordinate fields are downsampled to
    piecewise linear first.

    """
    from matplotlib import pyplot as plt
    gdim = mesh.geometric_dimension()
    tdim = mesh.topological_dimension()
    if surface:
        tdim -= 1
    if tdim not in [1, 2]:
        raise NotImplementedError("Not implemented except for %d-dimensional meshes", tdim)
    if gdim == 3:
        from mpl_toolkits.mplot3d import Axes3D  # noqa: F401
        from mpl_toolkits.mplot3d.art3d import Line3DCollection as Lines
        projection = "3d"
    else:
        from matplotlib.collections import LineCollection as Lines
        from matplotlib.collections import CircleCollection as Circles
        projection = None
    coordinates = mesh.coordinates
    ele = coordinates.function_space().ufl_element()
    if ele.degree() != 1:
        # Interpolate to piecewise linear.
        from firedrake import VectorFunctionSpace, interpolate
        V = VectorFunctionSpace(mesh, ele.family(), 1)
        coordinates = interpolate(coordinates, V)
    idx = tuple(range(tdim + 1))
    if surface:
        values = coordinates.exterior_facet_node_map().values
        dofs = np.asarray(list(coordinates.function_space().finat_element.entity_closure_dofs()[tdim].values()))
        local_facet = mesh.exterior_facets.local_facet_dat.data_ro
        indices = dofs[local_facet]
        values = np.choose(indices, values[np.newaxis, ...].T)
    else:
        quad = mesh.ufl_cell().cellname() == "quadrilateral"
        values = coordinates.cell_node_map().values
        if tdim == 2 and quad:
            # permute for clockwise ordering
            idx = (0, 1, 3, 2)
    # Plus first vertex again to close loop
    idx = idx + (0, )
    coords = coordinates.dat.data_ro
    if tdim == gdim and tdim == 1:
        # Pad 1D array with zeros
        coords = np.dstack((coords, np.zeros_like(coords))).reshape(-1, 2)
    vertices = coords[values[:, idx]]

    if axes is None:
        figure = plt.figure()
        axes = figure.add_subplot(111, projection=projection, **kwargs)

    lines = Lines(vertices, colors=colors)

    if gdim == 3:
        axes.add_collection3d(lines)
    else:
        if not surface:
            points = np.unique(vertices.reshape(-1, gdim), axis=0)
            points = Circles([10] * points.shape[0],
                             offsets=points,
                             transOffset=axes.transData,
                             edgecolors="black", facecolors="black")
            axes.add_collection(points)
        axes.add_collection(lines)
    for setter, idx in zip(["set_xlim",
                            "set_ylim",
                            "set_zlim"],
                           range(coords.shape[1])):
        try:
            setter = getattr(axes, setter)
        except AttributeError:
            continue
        amin = coords[:, idx].min()
        amax = coords[:, idx].max()
        extra = (amax - amin) / 20
        if extra == 0.0:
            # 1D interval
            extra = 0.5
        amin -= extra
        amax += extra
        setter(amin, amax)
    axes.set_aspect("equal")
    return axes
コード例 #33
0
ファイル: plot.py プロジェクト: kalogirou/firedrake
def plot_mesh(mesh, axes=None, **kwargs):
    """Plot a mesh.

    :arg mesh: The mesh to plot.
    :arg axes: Optional matplotlib axes to draw on.
    :arg **kwargs: Extra keyword arguments to pass to matplotlib.

    Note that high-order coordinate fields are downsampled to
    piecewise linear first.

    """
    from matplotlib import pyplot as plt
    gdim = mesh.geometric_dimension()
    tdim = mesh.topological_dimension()
    if tdim not in [1, 2]:
        raise NotImplementedError("Not implemented except for %d-dimensional meshes", tdim)
    if gdim == 3:
        from mpl_toolkits.mplot3d import Axes3D  # noqa: F401
        from mpl_toolkits.mplot3d.art3d import Line3DCollection as Lines
        projection = "3d"
    else:
        from matplotlib.collections import LineCollection as Lines
        from matplotlib.collections import CircleCollection as Circles
        projection = None
    coordinates = mesh.coordinates
    ele = coordinates.function_space().ufl_element()
    if ele.degree() != 1:
        # Interpolate to piecewise linear.
        from firedrake import VectorFunctionSpace, interpolate
        V = VectorFunctionSpace(mesh, ele.family(), 1)
        coordinates = interpolate(coordinates, V)
    quad = mesh.ufl_cell().cellname() == "quadrilateral"
    cell = coordinates.cell_node_map().values
    if tdim == 2:
        if quad:
            # permute for clockwise ordering
            # Plus first vertex again to close loop
            idx = (0, 1, 3, 2, 0)
        else:
            idx = (0, 1, 2, 0)
    else:
        idx = (0, 1, 0)
    coords = coordinates.dat.data_ro
    if tdim == gdim and tdim == 1:
        # Pad 1D array with zeros
        coords = np.dstack((coords, np.zeros_like(coords))).reshape(-1, 2)
    vertices = coords[cell[:, idx]]
    figure = plt.figure()
    if axes is None:
        axes = figure.add_subplot(111, projection=projection, **kwargs)

    lines = Lines(vertices)

    if gdim == 3:
        axes.add_collection3d(lines)
    else:
        points = Circles([10] * coords.shape[0],
                         offsets=coords,
                         transOffset=axes.transData,
                         edgecolors="black", facecolors="black")
        axes.add_collection(lines)
        axes.add_collection(points)
    for setter, idx in zip(["set_xlim",
                            "set_ylim",
                            "set_zlim"],
                           range(coords.shape[1])):
        try:
            setter = getattr(axes, setter)
        except AttributeError:
            continue
        amin = coords[:, idx].min()
        amax = coords[:, idx].max()
        extra = (amax - amin) / 20
        if extra == 0.0:
            # 1D interval
            extra = 0.5
        amin -= extra
        amax += extra
        setter(amin, amax)
    axes.set_aspect("equal")
    return axes