Exemplo n.º 1
0
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"]
Exemplo n.º 2
0
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"]
Exemplo n.º 3
0
def nonlinear_optimization_uniform(
    X,
    cells,
    tol,
    max_num_steps,
    verbose=False,
    step_filename_format=None,
    callback=None,
):
    """Optimal Delaunay Tesselation 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

    mesh = MeshTri(X, cells)

    if step_filename_format:
        mesh.save(
            step_filename_format.format(0),
            show_coedges=False,
            show_axes=False,
            cell_quality_coloring=("viridis", 0.0, 1.0, False),
        )

    if verbose:
        print("Before:")
        extra_cols = ["energy: {:.5e}".format(energy(mesh))]
        print_stats(mesh, extra_cols=extra_cols)

    def f(x):
        mesh.set_points(x.reshape(-1, X.shape[1]), mesh.is_interior_point)
        return energy(mesh, uniform_density=True)

    # TODO put f and jac together
    def jac(x):
        mesh.set_points(x.reshape(-1, X.shape[1]), mesh.is_interior_point)

        grad = numpy.zeros(mesh.points.shape)
        n = grad.shape[0]
        cc = mesh.cell_circumcenters
        for mcn in mesh.cells["points"].T:
            vals = (mesh.points[mcn] - cc).T * mesh.cell_volumes
            # numpy.add.at(grad, mcn, vals)
            grad += numpy.array(
                [numpy.bincount(mcn, val, minlength=n) for val in vals]).T
        gdim = 2
        grad *= 2 / (gdim + 1)
        return grad[mesh.is_interior_point].flatten()

    def flip_delaunay(x):
        flip_delaunay.step += 1
        # Flip the edges
        mesh.set_points(x.reshape(-1, X.shape[1]), mesh.is_interior_point)
        mesh.flip_until_delaunay()

        if step_filename_format:
            mesh.save(
                step_filename_format.format(flip_delaunay.step),
                show_coedges=False,
                show_axes=False,
                cell_quality_coloring=("viridis", 0.0, 1.0, False),
            )

        if callback:
            callback(flip_delaunay.step, mesh)

        # mesh.show()
        # exit(1)
        return

    flip_delaunay.step = 0

    x0 = X[mesh.is_interior_point].flatten()

    if callback:
        callback(0, mesh)

    out = scipy.optimize.minimize(
        f,
        x0,
        jac=jac,
        # method="Nelder-Mead",
        # method="Powell",
        # method="CG",
        # method="Newton-CG",
        method="BFGS",
        # method="L-BFGS-B",
        # method="TNC",
        # method="COBYLA",
        # method="SLSQP",
        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.set_points(out.x.reshape(-1, X.shape[1]), mesh.is_interior_point)

    mesh.flip_until_delaunay()

    info = (
        f"{out.nit} steps," +
        "Optimal Delaunay Tesselation (ODT), uniform density, BFGS variant")
    if verbose:
        print(f"\nFinal ({info})")
        extra_cols = ["energy: {:.5e}".format(energy(mesh))]
        print_stats(mesh, extra_cols=extra_cols)
        print()

    return mesh.points, mesh.cells["points"]
Exemplo n.º 4
0
def lloyd(
    X,
    cells,
    tol,
    max_num_steps,
    fcc_type="boundary",
    verbosity=1,
    step_filename_format=None,
):
    # flat mesh
    if X.shape[1] == 3:
        assert numpy.all(numpy.abs(X[:, 2]) < 1.0e-15)
        X = X[:, :2]

    original_X = X.copy()

    # create mesh data structure
    mesh = MeshTri(X, cells, flat_cell_correction=fcc_type)
    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("\nBefore:")
        print_stats(mesh)

    k = 0
    while True:
        k += 1

        # move interior points into centroids
        new_points = mesh.control_volume_centroids[mesh.is_interior_node]

        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_points
            # Preserve boundary nodes
            original_X[mesh.is_interior_node] = xnew
            # A new mesh is created in every step. Ugh. We do that since meshplex
            # doesn't have update_node_coordinates with flat_cell_correction.
            mesh = MeshTri(original_X,
                           mesh.cells["nodes"],
                           flat_cell_correction=fcc_type)
            # mesh.update_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"]