Files
epfl-archive/cs457-gc/assignment_2_3/notebook/hw_2.3.ipynb
T
2022-04-07 18:46:57 +02:00

14 KiB

Description

This notebook intends to gather all the functionalities you'll have to implement for assignment 2.3.

Load libraries

In [1]:
import numpy as np
import igl
import meshplot as mp
import time

import sys as _sys
_sys.path.append("../src")
from elasticsolid import *
from elasticenergy import *
from matplotlib import gridspec
import matplotlib.pyplot as plt

shadingOptions = {
    "flat":True,
    "wireframe":False,   
}

rot = np.array(
    [[1, 0, 0 ],
     [0, 0, 1],
     [0, -1, 0 ]]
)

Load mesh

Several meshes are available for you to play with under data/: ball.obj, dinosaur.obj, and beam.obj. You can also uncomment the few commented lines below to manipulate a simple mesh made out of 2 tetrahedra.

In [2]:
v, _, _, t, _, _ = igl.read_obj("../data/dinosaur.obj")
# v, _, _, t, _, _ = igl.read_obj("../data/beam.obj")

# t = np.array([
#         [0, 1, 2, 3],
#         [1, 2, 3, 4]
#     ])
# v = np.array([
#     [0., 0., 0.],
#     [1., 0., 0.],
#     [0., 1., 0.],
#     [0., 0., 1.],
#     [2/3, 2/3, 2/3]
# ])

be = igl.edges(igl.boundary_facets(t))
e = igl.edges(t)

aabb = np.max(v, axis=0) - np.min(v, axis=0)
length_scale = np.mean(aabb)

p = mp.plot(v @ rot.T, t, shading=shadingOptions)
Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-1.987469…

Linear/Non-Linear Elastic Solid

Instantiation

We first specify the elasticity model to use for the elastic solid, as well as pinned vertices, and volumetric forces.

In [3]:
rho     = 131  # [kg.m-3]
young   = 3e8 # [Pa] 
poisson = 0.2
force_mass = np.zeros(shape = (3,))
force_mass[2] = - rho * 9.81

# minX    = np.min(v[:, 0])
# pin_idx = np.arange(v.shape[0])[v[:, 0] < minX + 0.2*aabb[0]]
minZ    = np.min(v[:, 2])
pin_idx = np.arange(v.shape[0])[v[:, 2] < minZ + 0.1*aabb[2]]

# ee    = LinearElasticEnergy(young, poisson)
ee    = NeoHookeanElasticEnergy(young, poisson)
solid = ElasticSolid(v, t, ee, rho=rho, pin_idx=pin_idx, f_mass=force_mass)

Deform the mesh

This should now involve elastic forces computation.

In [4]:
v_def = v.copy()
v_def[:, 0] *= 2.
solid.update_def_shape(v_def)

p = mp.plot(solid.v_def @ rot.T, solid.t, shading=shadingOptions)
p.add_points(solid.v_def[solid.pin_idx, :] @ rot.T, shading={"point_color":"black", "point_size": 0.1 * length_scale})
forcesScale = 2 * np.max(np.linalg.norm(solid.f_ext, axis=1))
p.add_lines(solid.v_def @ rot.T, (solid.v_def + solid.f_ext / forcesScale) @ rot.T)
p.add_edges(v @ rot.T, be, shading={"line_color": "blue"})
Out [4]:
Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-3.974938…
3

Find equilibrium

We compare different methods: number of steps, computation time.

In [5]:
n_steps = 1000
thresh  = 1.
v_init = v.copy()

Gradient descent

In [6]:
%run ../src/Utils.py

report_GD = equilibrium_convergence_report_GD(solid, v_init, n_steps, 1e-8, thresh=thresh)
energies_el_GD  = report_GD['energies_el']
energies_ext_GD = report_GD['energies_ext']
energy_GD       = energies_el_GD + energies_ext_GD
residuals_GD    = report_GD['residuals']
times_GD        = report_GD['times']
idx_stop_GD     = report_GD['idx_stop']
v_def_GD        = solid.v_def.copy()

# Lastly, plot the resulting shape
p = mp.plot(v_def_GD @ rot.T, solid.t, shading=shadingOptions)
forcesScale = 2 * np.max(np.linalg.norm(solid.f_ext, axis=1))
p.add_lines(v_def_GD @ rot.T, (solid.v_def + solid.f_ext / forcesScale) @ rot.T)
p.add_points(v_def_GD[pin_idx, :] @ rot.T, shading={"point_color":"black", "point_size": 0.1 * length_scale})
p.add_edges(v @ rot.T, be, shading={"line_color": "blue"})
Out [6]:
Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-1.987469…
3

BFGS

In [7]:
report_BFGS = equilibrium_convergence_report_BFGS(solid, v_init, n_steps, 1e-8, thresh=thresh)
energies_el_BFGS  = report_BFGS['energies_el']
energies_ext_BFGS = report_BFGS['energies_ext']
energy_BFGS       = energies_el_BFGS + energies_ext_BFGS
residuals_BFGS    = report_BFGS['residuals']
times_BFGS        = report_BFGS['times']
idx_stop_BFGS     = report_BFGS['idx_stop']
v_def_BFGS        = solid.v_def.copy()

# Lastly, plot the resulting shape
p = mp.plot(v_def_BFGS @ rot.T, solid.t, shading=shadingOptions)
forcesScale = 2 * np.max(np.linalg.norm(solid.f_ext, axis=1))
p.add_lines(v_def_BFGS @ rot.T, (solid.v_def + solid.f_ext / forcesScale) @ rot.T)
p.add_points(v_def_BFGS[pin_idx, :] @ rot.T, shading={"point_color":"black", "point_size": 0.1 * length_scale})
p.add_edges(v @ rot.T, be, shading={"line_color": "blue"})
Out [7]:
Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-0.297981…
3

Newton-CG

In [ ]:
report_NCG = equilibrium_convergence_report_NCG(solid, v_init, n_steps, thresh=thresh)
energies_el_NCG  = report_NCG['energies_el']
energies_ext_NCG = report_NCG['energies_ext']
energy_NCG       = energies_el_NCG + energies_ext_NCG
residuals_NCG    = report_NCG['residuals']
times_NCG        = report_NCG['times']
idx_stop_NCG     = report_NCG['idx_stop']
v_def_NCG        = solid.v_def.copy()

# Lastly, plot the resulting shape
p = mp.plot(v_def_NCG @ rot.T, solid.t, shading=shadingOptions)
forcesScale = 2 * np.max(np.linalg.norm(solid.f_ext, axis=1))
p.add_lines(v_def_NCG @ rot.T, (solid.v_def + solid.f_ext / forcesScale) @ rot.T)
p.add_points(v_def_NCG[pin_idx, :] @ rot.T, shading={"point_color":"black", "point_size": 0.1 * length_scale})
p.add_edges(v @ rot.T, be, shading={"line_color": "blue"})
Energy: -127985.65419881282 Force norm: 287742.8969935862 Line search Iters: 0
Energy: -178892.14400109363 Force norm: 182952.00531737506 Line search Iters: 0
Energy: -206901.42653635694 Force norm: 151950.01953001413 Line search Iters: 0
Energy: -227792.85782151608 Force norm: 135158.66283929092 Line search Iters: 0
Energy: -244838.85692066932 Force norm: 123433.77675577633 Line search Iters: 0
Energy: -259318.48388755036 Force norm: 114389.35466936302 Line search Iters: 0
Energy: -271937.0843840749 Force norm: 107190.15322070086 Line search Iters: 0
Energy: -283160.0740997393 Force norm: 101411.77988596291 Line search Iters: 0

Compare the different algorithms

In [ ]:
cmap   = plt.get_cmap('viridis')
colors = cmap(np.linspace(0., 1., 4))

gs = gridspec.GridSpec(nrows=1, ncols=1, width_ratios=[1], height_ratios=[1])
fig = plt.figure(figsize=(10, 6))

axTmp = plt.subplot(gs[0, 0])
axTmp.plot(times_GD[:idx_stop_GD+1], residuals_GD[:idx_stop_GD+1], c=colors[0], 
           label="Gradient Descent ({:}its)".format(idx_stop_GD))
axTmp.plot(times_BFGS[:idx_stop_BFGS+1], residuals_BFGS[:idx_stop_BFGS+1], c=colors[1], 
           label="BFGS ({:}its)".format(idx_stop_BFGS))
axTmp.plot(times_NCG[:idx_stop_NCG+1], residuals_NCG[:idx_stop_NCG+1], c=colors[2], 
           label="Newton-CG ({:}its)".format(idx_stop_NCG))
y_lim = axTmp.get_ylim()
axTmp.set_title("Residuals as optimization goes", fontsize=14)
axTmp.set_xlabel("Computation time [s]", fontsize=12)
axTmp.set_ylabel("Force residuals [N]", fontsize=12)
axTmp.set_ylim(y_lim)
plt.legend(fontsize=12)
plt.grid()
plt.show()
In [ ]:
gs = gridspec.GridSpec(nrows=1, ncols=1, width_ratios=[1], height_ratios=[1])
fig = plt.figure(figsize=(10, 6))

axTmp = plt.subplot(gs[0, 0])
axTmp.plot(times_GD[:idx_stop_GD+1], energy_GD[:idx_stop_GD+1], c=colors[0], 
           label="Gradient Descent ({:}its)".format(idx_stop_GD))
axTmp.plot(times_BFGS[:idx_stop_BFGS+1], energy_BFGS[:idx_stop_BFGS+1], c=colors[1], 
           label="BFGS ({:}its)".format(idx_stop_BFGS))
axTmp.plot(times_NCG[:idx_stop_NCG+1], energy_NCG[:idx_stop_NCG+1], c=colors[2], 
           label="Newton-CG ({:}its)".format(idx_stop_NCG))
y_lim = axTmp.get_ylim()
axTmp.set_title("Total energy as optimization goes", fontsize=14)
axTmp.set_xlabel("Computation time [s]", fontsize=12)
axTmp.set_ylabel("Energy [J]", fontsize=12)
axTmp.set_ylim(y_lim)
plt.legend(fontsize=12)
plt.grid()
plt.show()
In [ ]: