def nonlinear_optimization( X, cells, tol, max_num_steps, verbosity=1, step_filename_format=None ): """Optimal Delaunay Triangulation smoothing. This method minimizes the energy E = int_Omega |u_l(x) - u(x)| rho(x) dx where u(x) = ||x||^2, u_l is its piecewise linear nodal interpolation and rho is the density. Since u(x) is convex, u_l >= u everywhere and u_l(x) = sum_i phi_i(x) u(x_i) where phi_i is the hat function at x_i. With rho(x)=1, this gives E = int_Omega sum_i phi_i(x) u(x_i) - u(x) = 1/(d+1) sum_i ||x_i||^2 |omega_i| - int_Omega ||x||^2 where d is the spatial dimension and omega_i is the star of x_i (the set of all simplices containing x_i). """ import scipy.optimize # TODO remove this assertion and test # flat mesh assert X.shape[1] == 2 mesh = MeshTri(X, cells, flat_cell_correction=None) if step_filename_format: mesh.save( step_filename_format.format(0), show_centroids=False, show_coedges=False, show_axes=False, nondelaunay_edge_color="k", ) if verbosity > 0: print("Before:") extra_cols = ["energy: {:.5e}".format(energy(mesh))] print_stats(mesh, extra_cols=extra_cols) def f(x): mesh.update_interior_node_coordinates(x.reshape(-1, 2)) return energy(mesh, uniform_density=True) # TODO put f and jac together def jac(x): mesh.update_interior_node_coordinates(x.reshape(-1, 2)) grad = numpy.zeros(mesh.node_coords.shape) cc = mesh.cell_circumcenters for mcn in mesh.cells["nodes"].T: fastfunc.add.at( grad, mcn, ((mesh.node_coords[mcn] - cc).T * mesh.cell_volumes).T ) gdim = 2 grad *= 2 / (gdim + 1) return grad[mesh.is_interior_node, :2].flatten() def flip_delaunay(x): flip_delaunay.step += 1 # Flip the edges mesh.update_interior_node_coordinates(x.reshape(-1, 2)) mesh.flip_until_delaunay() if step_filename_format: mesh.save( step_filename_format.format(flip_delaunay.step), show_centroids=False, show_coedges=False, show_axes=False, nondelaunay_edge_color="k", ) if verbosity > 1: print("\nStep {}:".format(flip_delaunay.step)) print_stats(mesh, extra_cols=["energy: {}".format(f(x))]) # mesh.show() # exit(1) return flip_delaunay.step = 0 x0 = X[mesh.is_interior_node, :2].flatten() out = scipy.optimize.minimize( f, x0, jac=jac, method="CG", # method='newton-cg', tol=tol, callback=flip_delaunay, options={"maxiter": max_num_steps}, ) # Don't assert out.success; max_num_steps may be reached, that's fine. # One last edge flip mesh.update_interior_node_coordinates(out.x.reshape(-1, 2)) mesh.flip_until_delaunay() if verbosity > 0: print("\nFinal ({} steps):".format(out.nit)) extra_cols = ["energy: {:.5e}".format(energy(mesh))] print_stats(mesh, extra_cols=extra_cols) print() return mesh.node_coords, mesh.cells["nodes"]
def runner( get_new_interior_points, X, cells, tol, max_num_steps, verbosity=1, step_filename_format=None, uniform_density=False, ): if X.shape[1] == 3: # create flat mesh assert numpy.all(abs(X[:, 2]) < 1.0e-15) X = X[:, :2] mesh = MeshTri(X, cells, flat_cell_correction=None) mesh.flip_until_delaunay() if step_filename_format: mesh.save( step_filename_format.format(0), show_centroids=False, show_coedges=False, show_axes=False, nondelaunay_edge_color="k", ) if verbosity > 0: print("Before:") print_stats(mesh) k = 0 while True: k += 1 new_interior_points = get_new_interior_points(mesh) original_orient = mesh.signed_tri_areas > 0.0 original_coords = mesh.node_coords[mesh.is_interior_node] # Step unless the orientation of any cell changes. alpha = 1.0 while True: xnew = (1 - alpha) * original_coords + alpha * new_interior_points mesh.update_interior_node_coordinates(xnew) new_orient = mesh.signed_tri_areas > 0.0 if numpy.all(original_orient == new_orient): break alpha /= 2 mesh.flip_until_delaunay() if step_filename_format: mesh.save( step_filename_format.format(k), show_centroids=False, show_coedges=False, show_axes=False, nondelaunay_edge_color="k", ) # Abort the loop if the update is small diff = mesh.node_coords[mesh.is_interior_node] - original_coords if numpy.all(numpy.einsum("ij,ij->i", diff, diff) < tol**2): break if k >= max_num_steps: break if verbosity > 1: print("\nstep {}:".format(k)) print_stats(mesh) if verbosity > 0: print("\nFinal ({} steps):".format(k)) print_stats(mesh) print() return mesh.node_coords, mesh.cells["nodes"]