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_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_order_0(): def h_expr(x): return h0 - dh * x / Lx def s_expr(x): return d + h0 - dh + ds * (1 - x / Lx) A = firedrake.Constant(icepack.rate_factor(254.15)) C = firedrake.Constant(0.001) opts = {'dirichlet_ids': [1], 'side_wall_ids': [3, 4], 'tolerance': 1e-14} Nx, Ny = 64, 64 mesh2d = firedrake.RectangleMesh(Nx, Ny, Lx, Ly) x, y = firedrake.SpatialCoordinate(mesh2d) Q2d = firedrake.FunctionSpace(mesh2d, family='CG', degree=2) V2d = firedrake.VectorFunctionSpace(mesh2d, family='CG', degree=2) h = firedrake.interpolate(h_expr(x), Q2d) s = firedrake.interpolate(s_expr(x), Q2d) u_expr = firedrake.as_vector((exact_u(x), 0)) u0 = firedrake.interpolate(u_expr, V2d) model2d = icepack.models.IceStream() solver2d = icepack.solvers.FlowSolver(model2d, **opts) u2d = solver2d.diagnostic_solve(velocity=u0, thickness=h, surface=s, fluidity=A, friction=C) mesh = firedrake.ExtrudedMesh(mesh2d, layers=1) x, y, ζ = firedrake.SpatialCoordinate(mesh) Q3d = firedrake.FunctionSpace(mesh, family='CG', degree=2, vfamily='DG', vdegree=0) V3d = firedrake.VectorFunctionSpace(mesh, dim=2, family='CG', degree=2, vfamily='GL', vdegree=0) h = firedrake.interpolate(h_expr(x), Q3d) s = firedrake.interpolate(s_expr(x), Q3d) u_expr = firedrake.as_vector((exact_u(x), 0)) u0 = firedrake.interpolate(u_expr, V3d) model3d = icepack.models.HybridModel() solver3d = icepack.solvers.FlowSolver(model3d, **opts) u3d = solver3d.diagnostic_solve(velocity=u0, thickness=h, surface=s, fluidity=A, friction=C) U2D, U3D = u2d.dat.data_ro, u3d.dat.data_ro assert np.linalg.norm(U3D - U2D) / np.linalg.norm(U2D) < 1e-2
def get_functionspace(self): """Construct trial/test space for state and adjoint equations.""" if self.mini: # MINI elements mini = fd.FiniteElement("CG", fd.triangle, 1) \ + fd.FiniteElement("B", fd.triangle, 3) Vvel = fd.VectorFunctionSpace(self.mesh_m, mini) else: # P2/P1 Taylor-Hood elements Vvel = fd.VectorFunctionSpace(self.mesh_m, "Lagrange", 2) Vpres = fd.FunctionSpace(self.mesh_m, "CG", 1) return Vvel * Vpres
def __init__(self, mesh_r, refinements=1, order=1): mh = fd.MeshHierarchy(mesh_r, refinements) self.mesh_hierarchy = mh # Control space on coarsest mesh self.mesh_r_coarse = self.mesh_hierarchy[0] self.V_r_coarse = fd.VectorFunctionSpace(self.mesh_r_coarse, "CG", order) # Create self.id and self.T on refined mesh. element = self.V_r_coarse.ufl_element() self.intermediate_Ts = [] for i in range(refinements - 1): mesh = self.mesh_hierarchy[i + 1] V = fd.FunctionSpace(mesh, element) self.intermediate_Ts.append(fd.Function(V)) self.mesh_r = self.mesh_hierarchy[-1] element = self.V_r_coarse.ufl_element() self.V_r = fd.FunctionSpace(self.mesh_r, element) X = fd.SpatialCoordinate(self.mesh_r) self.id = fd.Function(self.V_r).interpolate(X) self.T = fd.Function(self.V_r, name="T") self.T.assign(self.id) self.mesh_m = fda.Mesh(self.T) self.V_m = fd.FunctionSpace(self.mesh_m, element)
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 interpolate(self, mesh, k0L): V = fd.VectorFunctionSpace(mesh, "CG", 1) x = fd.SpatialCoordinate(mesh) pw = fd.interpolate(self.p * fd.exp(1j * k0L * fd.dot(self.s, x)), V) return pw
def __init__(self, mesh_r): # Create mesh_r and V_r self.mesh_r = mesh_r element = self.mesh_r.coordinates.function_space().ufl_element() self.V_r = fd.FunctionSpace(self.mesh_r, element) # Create self.id and self.T, self.mesh_m, and self.V_m. X = fd.SpatialCoordinate(self.mesh_r) self.id = fd.interpolate(X, self.V_r) self.T = fd.Function(self.V_r, name="T") self.T.assign(self.id) self.mesh_m = fd.Mesh(self.T) self.V_m = fd.FunctionSpace(self.mesh_m, element) self.is_DG = False """ ControlSpace for discontinuous coordinate fields (e.g. periodic domains) In Firedrake, periodic meshes are implemented using a discontinuous field. This implies that self.V_r contains discontinuous functions. To ensure domain updates do not create holes in the domain, use a continuous subspace self.V_c of self.V_r as control space. """ if element.family() == 'Discontinuous Lagrange': self.is_DG = True self.V_c = fd.VectorFunctionSpace(self.mesh_r, "CG", element._degree) self.Ip = fd.Interpolator(fd.TestFunction(self.V_c), self.V_r).callable().handle
def setupMesh(meshFile, degree=2, meshOversample=None, savegmsh=False): """ Read argus mesh file and return mesh alongw ith function spaces Parameters ---------- meshFile : str argus meshfile name degree : int, optional degree of function spaces, by default 2 Returns ------- mesh firedrake mesh Q, V firedrake scalar and vectory functions """ # Input the mesh maxOversample = 4 # Arbitrary could be increased mesh, opts = argusToFiredrakeMesh(meshFile, savegmsh=savegmsh) if meshOversample is not None: numLevels = meshOversample - 1 if numLevels < 0 or numLevels > (maxOversample - 1): myerror(f'meshOverample={meshOversample} but 0 < ' 'meshOversample < 4') mesh = firedrake.MeshHierarchy(mesh, numLevels)[numLevels] # Create scalar and vector function spaces Q = firedrake.FunctionSpace(mesh, family='CG', degree=degree) V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=degree) return mesh, Q, V, opts
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 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 test_hybrid_prognostic_solve(): Lx, Ly = 20e3, 20e3 h0, dh = 500.0, 100.0 T = 254.15 u_in = 100.0 model = icepack.models.HybridModel() opts = {'dirichlet_ids': [1], 'side_wall_ids': [3, 4], 'tol': 1e-12} Nx, Ny = 32, 32 mesh2d = firedrake.RectangleMesh(Nx, Ny, Lx, Ly) mesh = firedrake.ExtrudedMesh(mesh2d, layers=1) V = firedrake.VectorFunctionSpace(mesh, dim=2, family='CG', degree=2, vfamily='GL', vdegree=1) Q = firedrake.FunctionSpace(mesh, family='CG', degree=2, vfamily='DG', vdegree=0) 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 = u_in + 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 = interpolate((model.prognostic_solve(dt, h0=h, a=a0, u=u) - h) / dt, Q) 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_interpolating_vector_field(): n = 32 array_vx = np.array([[(i + j) / n for j in range(n + 1)] for i in range(n + 1)]) missing = -9999.0 array_vx[0, 0] = missing array_vx = np.flipud(array_vx) array_vy = np.array([[(j - i) / n for j in range(n + 1)] for i in range(n + 1)]) array_vy[-1, -1] = -9999.0 array_vy = np.flipud(array_vy) vx = make_rio_dataset(array_vx, missing) vy = make_rio_dataset(array_vy, missing) mesh = make_domain(48, 48, xmin=1 / 4, ymin=1 / 4, width=1 / 2, height=1 / 2) x, y = firedrake.SpatialCoordinate(mesh) V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=1) u = firedrake.interpolate(firedrake.as_vector((x + y, x - y)), V) v = icepack.interpolate((vx, vy), V) assert firedrake.norm(u - v) / firedrake.norm(u) < 1e-10
def vectorspaces(mesh, vertical_higher_order=0): '''On an extruded mesh, build finite element spaces for velocity u, pressure p, and vertical displacement c. Construct component spaces by explicitly applying TensorProductElement(). Elements are Q2 prisms [P2(triangle)xP2(interval)] for velocity, Q1 prisms [P1(triangle)xP1(interval)] for pressure, and Q1 prisms [P1(triangle)xP1(interval)] for displacement. Optionally the base mesh can be built from quadrilaterals and/or the vertical factors can be higher order for velocity and pressure.''' if mesh._base_mesh.cell_dimension() not in {1, 2}: raise ValueError('only 2D and 3D extruded meshes are allowed') ThreeD = (mesh._base_mesh.cell_dimension() == 2) quad = (mesh._base_mesh.ufl_cell() == fd.quadrilateral) if quad and not ThreeD: raise ValueError('base mesh from quadilaterals only possible in 3D') zudeg, zpdeg = _degreexz[vertical_higher_order] # velocity u (vector) if ThreeD: if quad: xuE = fd.FiniteElement('Q', fd.quadrilateral, 2) else: xuE = fd.FiniteElement('P', fd.triangle, 2) else: xuE = fd.FiniteElement('P', fd.interval, 2) zuE = fd.FiniteElement('P', fd.interval, zudeg) uE = fd.TensorProductElement(xuE, zuE) Vu = fd.VectorFunctionSpace(mesh, uE) # pressure p (scalar) # Note Isaac et al (2015) recommend discontinuous pressure space # to get mass conservation. Switching base mesh elements to dP0 # (or dQ0 for base quads) is inconsistent w.r.t. velocity result; # (unstable?) and definitely more expensive. if ThreeD: if quad: xpE = fd.FiniteElement('Q', fd.quadrilateral, 1) else: xpE = fd.FiniteElement('P', fd.triangle, 1) else: xpE = fd.FiniteElement('P', fd.interval, 1) zpE = fd.FiniteElement('P', fd.interval, zpdeg) pE = fd.TensorProductElement(xpE, zpE) Vp = fd.FunctionSpace(mesh, pE) # vertical displacement c (scalar) if ThreeD: if quad: xcE = fd.FiniteElement('Q', fd.quadrilateral, 1) else: xcE = fd.FiniteElement('P', fd.triangle, 1) else: xcE = fd.FiniteElement('P', fd.interval, 1) zcE = fd.FiniteElement('P', fd.interval, 1) cE = fd.TensorProductElement(xcE, zcE) Vc = fd.FunctionSpace(mesh, cE) return Vu, Vp, Vc
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 VectorFunctionSpaceHierarchy(mesh_hierarchy, *args, **kwargs): from firedrake.logging import warning, RED warning( RED % "VectorFunctionSpaceHierarchy is obsolete. Just build a FunctionSpace on the relevant mesh" ) return tuple( firedrake.VectorFunctionSpace(mesh, *args, **kwargs) for mesh in mesh_hierarchy)
def test_firedrake_to_numpy_mixed_function(): # Functions in DG0 have nodes at centers of finite element cells mesh = firedrake.UnitIntervalMesh(10) vec_dim = 4 V = firedrake.VectorFunctionSpace(mesh, "DG", 0, dim=vec_dim) x = firedrake.SpatialCoordinate(mesh) test_input = firedrake.interpolate(firedrake.as_vector(vec_dim * (x[0], )), V) expected = numpy.linspace(0.05, 0.95, num=10) expected = numpy.reshape(numpy.tile(expected, (4, 1)).T, V.dim()) assert numpy.allclose(to_numpy(test_input), expected)
def siahorizontalvelocity(mesh): hbase = surfaceelevation(mesh) if mesh._base_mesh.cell_dimension() == 2: if mesh._base_mesh.ufl_cell() == fd.quadrilateral: Vvectorbase = fd.VectorFunctionSpace(mesh._base_mesh, 'DQ', 0) VvectorR = fd.VectorFunctionSpace(mesh, 'DQ', 0, vfamily='R', vdegree=0, dim=2) else: Vvectorbase = fd.VectorFunctionSpace(mesh._base_mesh, 'DP', 0) VvectorR = fd.VectorFunctionSpace(mesh, 'DP', 0, vfamily='R', vdegree=0, dim=2) gradhbase = fd.project(fd.grad(hbase), Vvectorbase) Vvector = fd.VectorFunctionSpace(mesh, 'DQ', 0, dim=2) elif mesh._base_mesh.cell_dimension() == 1: Vvectorbase = fd.FunctionSpace(mesh._base_mesh, 'DP', 0) gradhbase = fd.project(hbase.dx(0), Vvectorbase) VvectorR = fd.FunctionSpace(mesh, 'DP', 0, vfamily='R', vdegree=0) Vvector = fd.FunctionSpace(mesh, 'DQ', 0) else: raise ValueError('base mesh of extruded input mesh must be 1D or 2D') gradh = fd.Function(VvectorR) gradh.dat.data[:] = gradhbase.dat.data_ro[:] h = extend(mesh, hbase) DQ0 = fd.FunctionSpace(mesh, 'DQ', 0) h0 = fd.project(h, DQ0) x = fd.SpatialCoordinate(mesh) z0 = fd.project(x[mesh._base_mesh.cell_dimension()], DQ0) # FIXME following only valid in flat bed case uvsia = -Gamma * (h0**(n + 1) - (h0 - z0)**(n + 1)) * abs(gradh)**(n - 1) * gradh uv = fd.Function(Vvector).interpolate(uvsia) uv.rename('velocitySIA') return uv
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_plot_vector_field(): nx, ny = 32, 32 mesh = firedrake.UnitSquareMesh(nx, ny) V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=1) x, y = firedrake.SpatialCoordinate(mesh) u = interpolate(as_vector((x + 0.01, x * y * (1 - y) * (y - 0.5))), V) arrows = icepack.plot.quiver(u) assert arrows is not None streamlines = icepack.plot.streamplot(u, density=1 / nx, precision=1 / nx) assert streamlines is not None
def add_vector_field_to(self, geo_map, vector_field, mode=None, levels=None, scale=None, cmap=None, name=None): """ Add a vector field to a folium map. Parameters ---------- geo_map : folium.Map Map to which the mesh plot should be added. vector_field: firedrake.Function A firedrake Function representing the vector field. mode: str, optional Plot to be generated, either contourf, contour or quiver. If not provided, contourf is used. levels: int or list of numbers, optional In contourf or contour mode: values of the contour lines. In quiver mode: number of ticks to be added to the color bar. If integer, it will determine the number of equispaced values to be used between the minimum and maximum entry of the scalar field. If list, it will determine the values to be used. If not provided, 10 levels are used by default. scale: number, optional This is only applicable for quiver mode: scaling to be applied before drawing arrows. If not provided, no scaling will be applied. cmap: str, optional matplotlib color map to be used. If not provided, the jet colormap is used. name: str, optional Name of the field, to be used in the creation of the color bar. If not provided, the name "vector field" will be used. """ mesh = vector_field.function_space().mesh() vector_function_space = firedrake.VectorFunctionSpace(mesh, "CG", 1) vertices = mesh.coordinates.dat.data_ro cells = mesh.coordinates.cell_node_map().values vector_field_values = firedrake.interpolate( vector_field, vector_function_space).vector().array().reshape(-1, 2) return BaseSolutionPlotter.add_vector_field_to(self, geo_map, vertices, cells, vector_field_values, mode, levels, scale, cmap, name)
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 __init__(self, mesh_m, viscosity): super().__init__() self.mesh_m = mesh_m self.failed_to_solve = False # when self.solver.solve() fail # Setup problem, Taylor-Hood finite elements self.V = fd.VectorFunctionSpace(self.mesh_m, "CG", 2) \ * fd.FunctionSpace(self.mesh_m, "CG", 1) # Preallocate solution variables for state equation self.solution = fd.Function(self.V, name="State") self.testfunction = fd.TestFunction(self.V) # Define viscosity parameter self.viscosity = viscosity # Weak form of incompressible Navier-Stokes equations z = self.solution u, p = fd.split(z) test = self.testfunction v, q = fd.split(test) nu = self.viscosity # shorten notation self.F = nu*fd.inner(fd.grad(u), fd.grad(v))*fd.dx - p*fd.div(v)*fd.dx\ + fd.inner(fd.dot(fd.grad(u), u), v)*fd.dx + fd.div(u)*q*fd.dx # Dirichlet Boundary conditions X = fd.SpatialCoordinate(self.mesh_m) dim = self.mesh_m.topological_dimension() if dim == 2: uin = 4 * fd.as_vector([(1 - X[1]) * X[1], 0]) elif dim == 3: rsq = X[0]**2 + X[1]**2 # squared radius = 0.5**2 = 1/4 uin = fd.as_vector([0, 0, 1 - 4 * rsq]) else: raise NotImplementedError self.bcs = [ fd.DirichletBC(self.V.sub(0), 0., [12, 13]), fd.DirichletBC(self.V.sub(0), uin, [10]) ] # PDE-solver parameters self.nsp = None self.params = { "snes_max_it": 10, "mat_type": "aij", "pc_type": "lu", "pc_factor_mat_solver_type": "superlu_dist", # "snes_monitor": None, "ksp_monitor": None, }
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 test_eigenvalues(): nx, ny = 32, 32 mesh = firedrake.UnitSquareMesh(nx, ny) x, y = firedrake.SpatialCoordinate(mesh) V = firedrake.VectorFunctionSpace(mesh, family='CG', degree=2) u = interpolate(as_vector((x, 0)), V) Q = firedrake.FunctionSpace(mesh, family='DG', degree=2) ε = sym(grad(u)) Λ1, Λ2 = eigenvalues(ε) λ1 = firedrake.project(Λ1, Q) λ2 = firedrake.project(Λ2, Q) assert norm(λ1 - Constant(1)) < norm(u) / (nx * ny) assert norm(λ2) < norm(u) / (nx * ny)
def interpolate(f, Q): r"""Interpolate an expression or a gridded data set to a function space Parameters ---------- f : rasterio dataset or tuple of rasterio datasets The gridded data set for scalar fields or the tuple of gridded data sets for each component Q : firedrake.FunctionSpace The function space where the result will live Returns ------- firedrake.Function A finite element function defined on `Q` with the same nodal values as the data `f` """ if isinstance(f, (ufl.core.expr.Expr, firedrake.Function)): return firedrake.interpolate(f, Q) mesh = Q.mesh() element = Q.ufl_element() if len(element.sub_elements()) > 0: element = element.sub_elements()[0] V = firedrake.VectorFunctionSpace(mesh, element) X = firedrake.interpolate(mesh.coordinates, V).dat.data_ro num_points = X.shape[0] q = firedrake.Function(Q) if isinstance(f, rasterio.DatasetReader): q.dat.data[:] = np.fromiter(f.sample(X, indexes=1), dtype=np.float64, count=num_points) elif (isinstance(f, tuple) and all(isinstance(fi, rasterio.DatasetReader) for fi in f)): for i, fi in enumerate(f): q.dat.data[:, i] = np.fromiter(fi.sample(X, indexes=1), dtype=np.float64, count=num_points) else: raise ValueError('Argument must be a rasterio data set or a tuple of ' 'data sets!') return q
def random_field_generator(function_space, minval=0, maxval=1, var=1e-2, len_scale=1, len_low=0, seed=20170519): mesh = function_space.mesh() dim = mesh.geometric_dimension() model = TPLStable(dim=dim, var=var, len_scale=len_scale, len_low=len_low) # model = Exponential(dim=dim, var=var, len_scale=len_scale) # FIXME: Diego, the exponential model, instead of TPLStable, resulted in some chemical equilibrium errors (returning to previous at the moment) srf = SRF(model, seed=seed) ndofs = function_space.dim() size = int(ndofs**(1 / dim)) xyz = [range(size) for _ in range(dim)] data = srf(xyz, mesh_type="structured") # The min and max values of x,y,z in the mesh xyz_min = [mesh.coordinates.dat.data[:, i].min() for i in range(dim)] xyz_max = [mesh.coordinates.dat.data[:, i].max() for i in range(dim)] xyz = [np.linspace(xyz_min[i], xyz_max[i], size) for i in range(dim)] datainterp = RegularGridInterpolator(xyz, data) # Now make the VectorFunctionSpace corresponding to V. W = fire.VectorFunctionSpace(mesh, function_space.ufl_element()) # Next, interpolate the coordinates onto the nodes of W. X = fire.interpolate(mesh.coordinates, W) # Make an output function. k = fire.Function(function_space, name="Permeability") # Use the external data function to interpolate the values of f. kdata = np.array([datainterp(xyz) for xyz in X.dat.data_ro]) # k.dat.data[:] = mydata(X.dat.data_ro) k.dat.data[:] = rescale_field(kdata, minval, maxval)[:, 0] return k
def test_diagnostic_solver_convergence(solver_type): # Create an ice shelf model model = icepack.models.IceShelf() opts = { "dirichlet_ids": [1], "side_wall_ids": [3, 4], "diagnostic_solver_type": solver_type, } # 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) solver = icepack.solvers.FlowSolver(model, **opts) u = solver.diagnostic_solve( velocity=u_guess, thickness=h, fluidity=A, strain_rate_min=firedrake.Constant(0.0), ) 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(f"log(error) ~= {slope:g} * log(dx) + {intercept:g}") assert slope > degree + 0.8