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
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
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
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
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)
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)
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
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()
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
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
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)
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
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')
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
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
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")
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
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
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
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
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
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
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
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
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)
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)
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)
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)
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)
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
# 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)
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
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