def test_density_quotients_decomposition(dim): """ As well as having an orthogonal eigendecomposition, an SPD matrix can also be decomposed as :math:`dVQV^T`, where :math:`d` is the so-called `density' and :math:`Q` are the `anisotropy coefficients'. This test computes such a decomposition and checks that it is valid. """ mesh = uniform_mesh(dim, 20, l=2) P1_vec = VectorFunctionSpace(mesh, "CG", 1) P1_ten = TensorFunctionSpace(mesh, "CG", 1) # Recover Hessian metric for some arbitrary sensor f = prod([sin(pi*xi) for xi in SpatialCoordinate(mesh)]) M = steady_metric(f, mesh=mesh, V=P1_ten, normalise=False, enforce_constraints=False) # Extract the eigendecomposition V = Function(P1_ten, name="Eigenvectors") Λ = Function(P1_vec, name="Eigenvalues") kernel = eigen_kernel(get_eigendecomposition, dim) op2.par_loop(kernel, P1_ten.node_set, V.dat(op2.RW), Λ.dat(op2.RW), M.dat(op2.READ)) # Extract the density and anisotropy quotients d, Q = get_density_and_quotients(M) Λ.interpolate(as_vector([pow(d/Q[i], 2/dim) for i in range(dim)])) # Reassemble the matrix and check the two match dVQVT = Function(P1_ten, name="Reassembled matrix") kernel = eigen_kernel(set_eigendecomposition, dim) op2.par_loop(kernel, P1_ten.node_set, dVQVT.dat(op2.RW), V.dat(op2.READ), Λ.dat(op2.READ)) assert np.allclose(M.dat.data, dVQVT.dat.data)
def test_eigendecomposition(dim): r""" Any symmetric matrix :math:`M` admits an orthogonal eigendecomposition :math:`M = V\Lambda V^T`, where :math:`\Lambda` is diagonal and :math:`V` is unitary. This test computes the eigendecomposition for a symmetric matrix and checks that it is valid. """ mesh = uniform_mesh(dim, 20, l=2) P1_vec = VectorFunctionSpace(mesh, "CG", 1) P1_ten = TensorFunctionSpace(mesh, "CG", 1) # Recover Hessian metric for some arbitrary sensor f = prod([sin(pi*xi) for xi in SpatialCoordinate(mesh)]) M = steady_metric(f, mesh=mesh, V=P1_ten, normalise=False, enforce_constraints=False) # Extract the eigendecomposition V = Function(P1_ten, name="Eigenvectors") Λ = Function(P1_vec, name="Eigenvalues") kernel = eigen_kernel(get_eigendecomposition, dim) op2.par_loop(kernel, P1_ten.node_set, V.dat(op2.RW), Λ.dat(op2.RW), M.dat(op2.READ)) # Check eigenvectors are orthogonal VVT = interpolate(dot(V, transpose(V)), P1_ten) I = interpolate(Identity(dim), P1_ten) assert np.allclose(VVT.dat.data, I.dat.data) # Reassemble it and check the two match VΛVT = Function(P1_ten, name="Reassembled matrix") kernel = eigen_kernel(set_eigendecomposition, dim) op2.par_loop(kernel, P1_ten.node_set, VΛVT.dat(op2.RW), V.dat(op2.READ), Λ.dat(op2.READ)) assert np.allclose(M.dat.data, VΛVT.dat.data)
def density(mesh=None, x=None): """ Adapt to the density of an L1-normalised Hessian metric for the sensor. """ M = steady_metric(sensor(mesh, xy=x), mesh=mesh, op=op, **hessian_kwargs) rho = get_density_and_quotients(M)[0] return 1.0 + alpha * rho / rho.vector().gather().max()
def frobenius(mesh=None, x=None): """ Adapt to the Frobenius norm of an L1-normalised Hessian metric for the sensor. """ P1 = FunctionSpace(mesh, "CG", 1) M = steady_metric(sensor(mesh, xy=x), mesh=mesh, op=op, **hessian_kwargs) M_F = local_frobenius_norm(M, space=P1) return 1.0 + alpha * M_F / interpolate(M_F, P1).vector().gather().max()
0.1 * sin(50 * x) + atan(0.1 / (sin(5 * y) - 2 * x)), atan(0.1 / (sin(5 * y) - 2 * x)) + atan(0.5 / (sin(3 * y) - 7 * x)) ][i] op = DefaultOptions() op.h_min = 1e-6 op.h_max = 0.1 op.num_adapt = 4 modes = ['target', 'p_norm', 'p_norm', 'p_norm'] orders = [None, 1, 2, None] for op.normalisation, op.norm_order in zip(modes, orders): if op.normalisation == 'p_norm': normalisation = 'l-inf' if op.norm_order is None else 'l{:d}'.format( op.norm_order) else: normalisation = 'target' for i in range(4): for k in range(3): print("\nNormalisation {:s} Sensor {:d} Iteration {:d}".format( normalisation, i, k)) op.target = pow(10, k) for j in range(op.num_adapt): if j == 0: mesh = SquareMesh(40, 40, 2, 2) mesh.coordinates.dat.data[:] -= [1, 1] mesh = adapt(mesh, steady_metric(sensor(i, mesh), mesh=mesh, op=op)) File('plots/normalisation_{:s}__sensor_{:d}__mesh_{:d}.pvd'.format( normalisation, i, k)).write(mesh.coordinates)
def recover_boundary_hessian(f, **kwargs): """ Recover the Hessian of `f` on the domain boundary. That is, the Hessian in the direction tangential to the boundary. In two dimensions this gives a scalar field, whereas in three dimensions it gives a 2D field on the surface. The resulting field should only be considered on the boundary and is set arbitrarily to 1/h_max in the interior. :arg f: field which we seek the Hessian of. :kwarg op: `Options` class object providing max cell size value. :kwarg boundary_tag: physical ID for boundary segment :return: reconstructed boundary Hessian associated with `f`. """ from adapt_utils.adapt.metric import steady_metric from adapt_utils.linalg import get_orthonormal_vectors kwargs.setdefault('op', Options()) op = kwargs.get('op') op.print_debug("RECOVERY: Recovering Hessian on domain boundary...") mesh = kwargs.get('mesh', op.default_mesh) dim = mesh.topological_dimension() if dim not in (2, 3): raise ValueError("Dimensions other than 2D and 3D not considered.") # Solver parameters solver_parameters = op.hessian_solver_parameters['parts'] if op.debug: solver_parameters['ksp_monitor'] = None solver_parameters['ksp_converged_reason'] = None # Function spaces P1 = FunctionSpace(mesh, "CG", 1) P1_ten = TensorFunctionSpace(mesh, "CG", 1) # Apply Gram-Schmidt to get tangent vectors n = FacetNormal(mesh) s = get_orthonormal_vectors(n) ns = as_vector([n, *s]) # --- Solve tangent to boundary bcs = kwargs.get('bcs') assert bcs is None # TODO boundary_tag = kwargs.get('boundary_tag', 'on_boundary') Hs, v = TrialFunction(P1), TestFunction(P1) l2_proj = [[Function(P1) for i in range(dim - 1)] for j in range(dim - 1)] h = Constant(1 / op.h_max**2) # Arbitrary value in domain interior a = v * Hs * dx L = v * h * dx # Hessian on boundary a_bc = v * Hs * ds nullspace = VectorSpaceBasis(constant=True) for j, s1 in enumerate(s): for i, s0 in enumerate(s): L_bc = -dot(s0, grad(v)) * dot(s1, grad(f)) * ds bbcs = None # TODO? bcs = EquationBC(a_bc == L_bc, l2_proj[i][j], boundary_tag, bcs=bbcs) solve(a == L, l2_proj[i][j], bcs=bcs, nullspace=nullspace, solver_parameters=solver_parameters) # --- Construct tensor field boundary_hessian = Function(P1_ten) if dim == 2: Hsub = abs(l2_proj[i][j]) H = as_matrix([[h, 0], [0, Hsub]]) else: Hsub = Function(TensorFunctionSpace(mesh, "CG", 1, shape=(2, 2))) Hsub.interpolate( as_matrix([[l2_proj[0][0], l2_proj[0][1]], [l2_proj[1][0], l2_proj[1][1]]])) Hsub = steady_metric(H=Hsub, normalise=False, enforce_constraints=False, op=op) H = as_matrix([[h, 0, 0], [0, Hsub[0, 0], Hsub[0, 1]], [0, Hsub[1, 0], Hsub[1, 1]]]) # Arbitrary value in domain interior sigma, tau = TrialFunction(P1_ten), TestFunction(P1_ten) a = inner(tau, sigma) * dx L = inner(tau, h * Identity(dim)) * dx # Boundary values imposed as in [Loseille et al. 2011] a_bc = inner(tau, sigma) * ds L_bc = inner(tau, dot(transpose(ns), dot(H, ns))) * ds bcs = EquationBC(a_bc == L_bc, boundary_hessian, boundary_tag) solve(a == L, boundary_hessian, bcs=bcs, solver_parameters=solver_parameters) return boundary_hessian