468 lines
14 KiB
Plaintext
468 lines
14 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Description\n",
|
|
"\n",
|
|
"This notebook intends to gather all the functionalities you'll have to implement for assignment 2.3.\n",
|
|
"\n",
|
|
"# Load libraries"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import numpy as np\n",
|
|
"import igl\n",
|
|
"import meshplot as mp\n",
|
|
"import time\n",
|
|
"\n",
|
|
"import sys as _sys\n",
|
|
"_sys.path.append(\"../src\")\n",
|
|
"from elasticsolid import *\n",
|
|
"from elasticenergy import *\n",
|
|
"from matplotlib import gridspec\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"\n",
|
|
"shadingOptions = {\n",
|
|
" \"flat\":True,\n",
|
|
" \"wireframe\":False, \n",
|
|
"}\n",
|
|
"\n",
|
|
"rot = np.array(\n",
|
|
" [[1, 0, 0 ],\n",
|
|
" [0, 0, 1],\n",
|
|
" [0, -1, 0 ]]\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Load mesh\n",
|
|
"\n",
|
|
"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."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "bc63cdd8cfe14603b4e54d871b4206d7",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-1.987469…"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"v, _, _, t, _, _ = igl.read_obj(\"../data/dinosaur.obj\")\n",
|
|
"# v, _, _, t, _, _ = igl.read_obj(\"../data/beam.obj\")\n",
|
|
"\n",
|
|
"# t = np.array([\n",
|
|
"# [0, 1, 2, 3],\n",
|
|
"# [1, 2, 3, 4]\n",
|
|
"# ])\n",
|
|
"# v = np.array([\n",
|
|
"# [0., 0., 0.],\n",
|
|
"# [1., 0., 0.],\n",
|
|
"# [0., 1., 0.],\n",
|
|
"# [0., 0., 1.],\n",
|
|
"# [2/3, 2/3, 2/3]\n",
|
|
"# ])\n",
|
|
"\n",
|
|
"be = igl.edges(igl.boundary_facets(t))\n",
|
|
"e = igl.edges(t)\n",
|
|
"\n",
|
|
"aabb = np.max(v, axis=0) - np.min(v, axis=0)\n",
|
|
"length_scale = np.mean(aabb)\n",
|
|
"\n",
|
|
"p = mp.plot(v @ rot.T, t, shading=shadingOptions)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Linear/Non-Linear Elastic Solid\n",
|
|
"\n",
|
|
"## Instantiation\n",
|
|
"\n",
|
|
"We first specify the elasticity model to use for the elastic solid, as well as pinned vertices, and volumetric forces."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"rho = 131 # [kg.m-3]\n",
|
|
"young = 3e8 # [Pa] \n",
|
|
"poisson = 0.2\n",
|
|
"force_mass = np.zeros(shape = (3,))\n",
|
|
"force_mass[2] = - rho * 9.81\n",
|
|
"\n",
|
|
"# minX = np.min(v[:, 0])\n",
|
|
"# pin_idx = np.arange(v.shape[0])[v[:, 0] < minX + 0.2*aabb[0]]\n",
|
|
"minZ = np.min(v[:, 2])\n",
|
|
"pin_idx = np.arange(v.shape[0])[v[:, 2] < minZ + 0.1*aabb[2]]\n",
|
|
"\n",
|
|
"# ee = LinearElasticEnergy(young, poisson)\n",
|
|
"ee = NeoHookeanElasticEnergy(young, poisson)\n",
|
|
"solid = ElasticSolid(v, t, ee, rho=rho, pin_idx=pin_idx, f_mass=force_mass)\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Deform the mesh\n",
|
|
"\n",
|
|
"This should now involve elastic forces computation."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {
|
|
"scrolled": false
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "b6c6920e14be4332b4a96e814c9236da",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-3.974938…"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"3"
|
|
]
|
|
},
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"v_def = v.copy()\n",
|
|
"v_def[:, 0] *= 2.\n",
|
|
"solid.update_def_shape(v_def)\n",
|
|
"\n",
|
|
"p = mp.plot(solid.v_def @ rot.T, solid.t, shading=shadingOptions)\n",
|
|
"p.add_points(solid.v_def[solid.pin_idx, :] @ rot.T, shading={\"point_color\":\"black\", \"point_size\": 0.1 * length_scale})\n",
|
|
"forcesScale = 2 * np.max(np.linalg.norm(solid.f_ext, axis=1))\n",
|
|
"p.add_lines(solid.v_def @ rot.T, (solid.v_def + solid.f_ext / forcesScale) @ rot.T)\n",
|
|
"p.add_edges(v @ rot.T, be, shading={\"line_color\": \"blue\"})\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Find equilibrium\n",
|
|
"\n",
|
|
"We compare different methods: number of steps, computation time."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"n_steps = 1000\n",
|
|
"thresh = 1.\n",
|
|
"v_init = v.copy()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Gradient descent"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "fe18307f9c24439db1ed8fc58928c284",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-1.987469…"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"3"
|
|
]
|
|
},
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"%run ../src/Utils.py\n",
|
|
"\n",
|
|
"report_GD = equilibrium_convergence_report_GD(solid, v_init, n_steps, 1e-8, thresh=thresh)\n",
|
|
"energies_el_GD = report_GD['energies_el']\n",
|
|
"energies_ext_GD = report_GD['energies_ext']\n",
|
|
"energy_GD = energies_el_GD + energies_ext_GD\n",
|
|
"residuals_GD = report_GD['residuals']\n",
|
|
"times_GD = report_GD['times']\n",
|
|
"idx_stop_GD = report_GD['idx_stop']\n",
|
|
"v_def_GD = solid.v_def.copy()\n",
|
|
"\n",
|
|
"# Lastly, plot the resulting shape\n",
|
|
"p = mp.plot(v_def_GD @ rot.T, solid.t, shading=shadingOptions)\n",
|
|
"forcesScale = 2 * np.max(np.linalg.norm(solid.f_ext, axis=1))\n",
|
|
"p.add_lines(v_def_GD @ rot.T, (solid.v_def + solid.f_ext / forcesScale) @ rot.T)\n",
|
|
"p.add_points(v_def_GD[pin_idx, :] @ rot.T, shading={\"point_color\":\"black\", \"point_size\": 0.1 * length_scale})\n",
|
|
"p.add_edges(v @ rot.T, be, shading={\"line_color\": \"blue\"})"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## BFGS"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"application/vnd.jupyter.widget-view+json": {
|
|
"model_id": "0621481088744a34af8662b30613ceac",
|
|
"version_major": 2,
|
|
"version_minor": 0
|
|
},
|
|
"text/plain": [
|
|
"Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-0.297981…"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"3"
|
|
]
|
|
},
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"report_BFGS = equilibrium_convergence_report_BFGS(solid, v_init, n_steps, 1e-8, thresh=thresh)\n",
|
|
"energies_el_BFGS = report_BFGS['energies_el']\n",
|
|
"energies_ext_BFGS = report_BFGS['energies_ext']\n",
|
|
"energy_BFGS = energies_el_BFGS + energies_ext_BFGS\n",
|
|
"residuals_BFGS = report_BFGS['residuals']\n",
|
|
"times_BFGS = report_BFGS['times']\n",
|
|
"idx_stop_BFGS = report_BFGS['idx_stop']\n",
|
|
"v_def_BFGS = solid.v_def.copy()\n",
|
|
"\n",
|
|
"# Lastly, plot the resulting shape\n",
|
|
"p = mp.plot(v_def_BFGS @ rot.T, solid.t, shading=shadingOptions)\n",
|
|
"forcesScale = 2 * np.max(np.linalg.norm(solid.f_ext, axis=1))\n",
|
|
"p.add_lines(v_def_BFGS @ rot.T, (solid.v_def + solid.f_ext / forcesScale) @ rot.T)\n",
|
|
"p.add_points(v_def_BFGS[pin_idx, :] @ rot.T, shading={\"point_color\":\"black\", \"point_size\": 0.1 * length_scale})\n",
|
|
"p.add_edges(v @ rot.T, be, shading={\"line_color\": \"blue\"})"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Newton-CG"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Energy: -127985.65419881282 Force norm: 287742.8969935862 Line search Iters: 0\n",
|
|
"Energy: -178892.14400109363 Force norm: 182952.00531737506 Line search Iters: 0\n",
|
|
"Energy: -206901.42653635694 Force norm: 151950.01953001413 Line search Iters: 0\n",
|
|
"Energy: -227792.85782151608 Force norm: 135158.66283929092 Line search Iters: 0\n",
|
|
"Energy: -244838.85692066932 Force norm: 123433.77675577633 Line search Iters: 0\n",
|
|
"Energy: -259318.48388755036 Force norm: 114389.35466936302 Line search Iters: 0\n",
|
|
"Energy: -271937.0843840749 Force norm: 107190.15322070086 Line search Iters: 0\n",
|
|
"Energy: -283160.0740997393 Force norm: 101411.77988596291 Line search Iters: 0\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"report_NCG = equilibrium_convergence_report_NCG(solid, v_init, n_steps, thresh=thresh)\n",
|
|
"energies_el_NCG = report_NCG['energies_el']\n",
|
|
"energies_ext_NCG = report_NCG['energies_ext']\n",
|
|
"energy_NCG = energies_el_NCG + energies_ext_NCG\n",
|
|
"residuals_NCG = report_NCG['residuals']\n",
|
|
"times_NCG = report_NCG['times']\n",
|
|
"idx_stop_NCG = report_NCG['idx_stop']\n",
|
|
"v_def_NCG = solid.v_def.copy()\n",
|
|
"\n",
|
|
"# Lastly, plot the resulting shape\n",
|
|
"p = mp.plot(v_def_NCG @ rot.T, solid.t, shading=shadingOptions)\n",
|
|
"forcesScale = 2 * np.max(np.linalg.norm(solid.f_ext, axis=1))\n",
|
|
"p.add_lines(v_def_NCG @ rot.T, (solid.v_def + solid.f_ext / forcesScale) @ rot.T)\n",
|
|
"p.add_points(v_def_NCG[pin_idx, :] @ rot.T, shading={\"point_color\":\"black\", \"point_size\": 0.1 * length_scale})\n",
|
|
"p.add_edges(v @ rot.T, be, shading={\"line_color\": \"blue\"})"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Compare the different algorithms"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"cmap = plt.get_cmap('viridis')\n",
|
|
"colors = cmap(np.linspace(0., 1., 4))\n",
|
|
"\n",
|
|
"gs = gridspec.GridSpec(nrows=1, ncols=1, width_ratios=[1], height_ratios=[1])\n",
|
|
"fig = plt.figure(figsize=(10, 6))\n",
|
|
"\n",
|
|
"axTmp = plt.subplot(gs[0, 0])\n",
|
|
"axTmp.plot(times_GD[:idx_stop_GD+1], residuals_GD[:idx_stop_GD+1], c=colors[0], \n",
|
|
" label=\"Gradient Descent ({:}its)\".format(idx_stop_GD))\n",
|
|
"axTmp.plot(times_BFGS[:idx_stop_BFGS+1], residuals_BFGS[:idx_stop_BFGS+1], c=colors[1], \n",
|
|
" label=\"BFGS ({:}its)\".format(idx_stop_BFGS))\n",
|
|
"axTmp.plot(times_NCG[:idx_stop_NCG+1], residuals_NCG[:idx_stop_NCG+1], c=colors[2], \n",
|
|
" label=\"Newton-CG ({:}its)\".format(idx_stop_NCG))\n",
|
|
"y_lim = axTmp.get_ylim()\n",
|
|
"axTmp.set_title(\"Residuals as optimization goes\", fontsize=14)\n",
|
|
"axTmp.set_xlabel(\"Computation time [s]\", fontsize=12)\n",
|
|
"axTmp.set_ylabel(\"Force residuals [N]\", fontsize=12)\n",
|
|
"axTmp.set_ylim(y_lim)\n",
|
|
"plt.legend(fontsize=12)\n",
|
|
"plt.grid()\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"gs = gridspec.GridSpec(nrows=1, ncols=1, width_ratios=[1], height_ratios=[1])\n",
|
|
"fig = plt.figure(figsize=(10, 6))\n",
|
|
"\n",
|
|
"axTmp = plt.subplot(gs[0, 0])\n",
|
|
"axTmp.plot(times_GD[:idx_stop_GD+1], energy_GD[:idx_stop_GD+1], c=colors[0], \n",
|
|
" label=\"Gradient Descent ({:}its)\".format(idx_stop_GD))\n",
|
|
"axTmp.plot(times_BFGS[:idx_stop_BFGS+1], energy_BFGS[:idx_stop_BFGS+1], c=colors[1], \n",
|
|
" label=\"BFGS ({:}its)\".format(idx_stop_BFGS))\n",
|
|
"axTmp.plot(times_NCG[:idx_stop_NCG+1], energy_NCG[:idx_stop_NCG+1], c=colors[2], \n",
|
|
" label=\"Newton-CG ({:}its)\".format(idx_stop_NCG))\n",
|
|
"y_lim = axTmp.get_ylim()\n",
|
|
"axTmp.set_title(\"Total energy as optimization goes\", fontsize=14)\n",
|
|
"axTmp.set_xlabel(\"Computation time [s]\", fontsize=12)\n",
|
|
"axTmp.set_ylabel(\"Energy [J]\", fontsize=12)\n",
|
|
"axTmp.set_ylim(y_lim)\n",
|
|
"plt.legend(fontsize=12)\n",
|
|
"plt.grid()\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3 (ipykernel)",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.9.7"
|
|
},
|
|
"toc": {
|
|
"base_numbering": 1,
|
|
"nav_menu": {},
|
|
"number_sections": true,
|
|
"sideBar": true,
|
|
"skip_h1_title": false,
|
|
"title_cell": "Table of Contents",
|
|
"title_sidebar": "Contents",
|
|
"toc_cell": false,
|
|
"toc_position": {},
|
|
"toc_section_display": true,
|
|
"toc_window_display": false
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
}
|