epfl-archive/cs457-gc/assignment_2_4/notebook/inverse_design_dino.ipynb
2022-04-07 18:46:57 +02:00

475 lines
34 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import torch\n",
"import igl\n",
"\n",
"import meshplot as mp\n",
"import sys as _sys\n",
"_sys.path.append(\"../src\")\n",
"from elasticenergy import *\n",
"from elasticsolid import *\n",
"from adjoint_sensitivity import *\n",
"from vis_utils import *\n",
"from objectives import *\n",
"from harmonic_interpolator import *\n",
"from shape_optimizer import *\n",
"\n",
"from utils import *\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",
")\n",
"\n",
"torch.set_default_dtype(torch.float64)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Create the deformed object\n",
"\n",
"## Load the mesh"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "35cf42b4eded4cb99f3811a16dcb4d67",
"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": [
"<meshplot.Viewer.Viewer at 0x7fa5b879cc10>"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"vNP, _, _, tNP, _, _ = igl.read_obj(\"../data/dinosaur.obj\")\n",
"# vNP, _, _, tNP, _, _ = igl.read_obj(\"../data/beam.obj\")\n",
"\n",
"aabb = np.max(vNP, axis=0) - np.min(vNP, axis=0)\n",
"length_scale = np.mean(aabb)\n",
"\n",
"\n",
"v, t = torch.tensor(vNP), torch.tensor(tNP)\n",
"eNP = igl.edges(tNP)\n",
"beNP = igl.edges(igl.boundary_facets(tNP))\n",
"\n",
"bvNP, ivNP = get_boundary_and_interior(v.shape[0], tNP)\n",
"\n",
"mp.plot(vNP @ rot.T, np.array(tNP), shading=shadingOptions)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Add some physical characteristics"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pinned vertices: [ 46 47 50 59 60 62 64 65 88 89 91 98 99 102 103 104]\n"
]
}
],
"source": [
"rho = 131 # [kg.m-3], if aabb[0] ~ 14m, and m_tot = 6000kg\n",
"young = 3e8 # [Pa] \n",
"poisson = 0.2\n",
"\n",
"# Find some of the lowest vertices and pin them\n",
"minZ = torch.min(v[:, 2])\n",
"pin_idx = torch.arange(v.shape[0])[v[:, 2] < minZ + 0.01*aabb[2]]\n",
"vIdx = np.arange(v.shape[0])\n",
"pin_idx = vIdx[np.in1d(vIdx, bvNP) & np.in1d(vIdx, pin_idx)]\n",
"print(\"Pinned vertices: {}\".format(pin_idx))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Initial guess\n",
"\n",
"The idea is that we start deforming the mesh by inverting gravity."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"scrolled": false
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "5d62f192ccc940388718972e4a447f2b",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-2.468080…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Inverted gravity\n",
"force_mass = torch.zeros(size=(3,))\n",
"force_mass[2] = + rho * 9.81\n",
"\n",
"# Gravity going in the wrong direction\n",
"\n",
"ee = NeoHookeanElasticEnergy(young, poisson)\n",
"\n",
"v = HarmonicInterpolator(v, t, ivNP).interpolate(v[bvNP])\n",
"solid_init = ElasticSolid(v, t, ee, rho=rho, pin_idx=pin_idx, f_mass=force_mass)\n",
"\n",
"solid_init.find_equilibrium()\n",
"plot_torch_solid(solid_init, beNP, rot, length_scale)\n",
"\n",
"# Use these as initial guesses\n",
"v_init_rest = solid_init.v_def.clone().detach()\n",
"v_init_def = solid_init.v_rest.clone().detach()\n",
"\n",
"# v_init_rest = solid_init.v_rest.clone().detach()\n",
"# v_init_def = solid_init.v_def.clone().detach()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Inverse design\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Initial objective: 6.6351e+02\n",
"\n"
]
}
],
"source": [
"force_mass = torch.zeros(size=(3,))\n",
"force_mass[2] = - rho * 9.81\n",
"use_linear = False\n",
"\n",
"# The target is the initial raw mesh\n",
"vt_surf = torch.tensor(vNP[bvNP, :])\n",
"\n",
"# Create solid\n",
"if use_linear:\n",
" ee = LinearElasticEnergy(young, poisson)\n",
"else:\n",
" ee = NeoHookeanElasticEnergy(young, poisson)\n",
"solid_ = ElasticSolid(v_init_rest, t, ee, rho=rho, pin_idx=pin_idx, f_mass=force_mass)\n",
"solid_.update_def_shape(v_init_def)\n",
"\n",
"optimizer = ShapeOptimizer(solid_, vt_surf, weight_reg=0.)\n",
"\n",
"v_eq_init = optimizer.solid.v_def.clone().detach() #bookkeeping"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Objective after 19 optimization step(s): 5.8586e+02\n",
" Line search Iters: 8\n",
"Elapsed time: 683.7s. \n",
"Estimated remaining time: 755.7s\n",
"\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "fa1a13b5101d4629a3d07e84205409dd",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-0.497634…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"An exception occured: . \n",
"Halving the step.\n",
"Line search can't find a step to take\n"
]
}
],
"source": [
"optimizer.optimize(step_size_init=1e-4, max_l_iter=10, n_optim_steps=40)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAmcAAAGHCAYAAAD1HvUOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8/fFQqAAAACXBIWXMAAAsTAAALEwEAmpwYAABA2klEQVR4nO3deZxcVZ338c+3t+okXZ0ASTWQhD2EVRACiiB2jCPq6OCCI86Mj6Ij48gw+DzjxiyOOsO4LzjIKIIrYERcwAVGECIuLAIi+xIgkoVsQNLpLL3+nj/u7aZS6e70Ult3fd+vV72q6t5z655zqzv9zTn33qOIwMzMzMyqQ12lK2BmZmZmz3M4MzMzM6siDmdmZmZmVcThzMzMzKyKOJyZmZmZVRGHMzMzM7Mq4nBmVuUkLZN00UTLFKkuIemMUu9nspDUnh6T2RP8nG9K+mmx6jXMPg5I67qolPsxs4lzODOrEElzJV0iaZWkbkmrJX1N0rxxfNwbgfOLWLfhwsI+wE+KtZ/JRNIKSe8vWPw7kmPyzAQ//jzgbyb4GYOGCesrSep6T7H2Y2al4XBmVgGSDgTuBI4C3g4cQvLH+Ujg95IOGMvnRcSzEbGl2PUcYj9rI6Kr1PuZLCKiOz0mE7qbd0RsjohNRarWcPvoS+vaW8r9mNnEOZyZVcaXgX7gFRHxy4h4KiJuBl6RLv9yQfkGSRdKei59fEbS4O9vYU+JpCZJn0p75bZK+r2k0/I/UNJhkq6VtFlSp6RbJR0t6aMkgfHP02GwkNSebjM4rJmW/1zBZ7ZK2i7pDaOtRyFJr5L067Sdz0r6X0mHF5T5iKQ/SeqStFbSt3fzmadKul3SDknrJH1BUlPB8fvKcMdY0jJgf+AzA8ckXb7TsKakd6TH8tWSHpa0LT3GMyWdIemx9Hh/R9K0vP0P9lTmfWbhY1m6fi9J302P6XZJD0g6K/+zgJcB5+Rte8BQw5qjPC4XS/ovSRslrZf02fyfvWGO9zslPZW2/yeS3jtwzPLK/J2k5Up6jZdLenfB+plKepbXS9oi6VcFdZ+ZHsf1af2fkPS+keplNlk4nJmVmaQ9gVcBX46Ibfnr0vcXA6+WtEfeqr8m+X09Cfg74GzgfSPs5hskf6D/Cjga+BbwE0nHpHXYF/gNEMCfAceRBMJ64LPAVcCNJMNg+5AM3xW6HDiz4A/1m4DtwM9GU49hzAC+CJwItAOb022a0rq/CXg/8F5gAfBa4I7hPkzSXOA64A/AC4F3AW8FPlFQdKRj/EZgFfBxnj8mw8kA/5R+3hJgEXA1SeB9E/D6tM7vHWb7gaHSgcciYBOwLF3fDNydfsaRwIXAVyUtSdefB9xKcuwHPmNl4U7GeFx6gZcA/0ByTN4yXOMlnQRcSvLzdCxwLfCxgjJvAC4i+Z6PSttwsaTXpetF8jM0N23nC4FbgJskDRz7/yT5mXotcBjwTmD1cPUym1Qiwg8//CjjA3gRSSh6wzDr35CuPzF9vwx4FFBemX8FVuW9XwZclL4+mKT3bb+Cz/0xcHH6+gLgT0DTMHX4JvDTIZYHcEb6ei+gG1iSt/5G4Kujrccoj9cMoA84JX3//4BHgMZRbn8BsByoy1v2DqALmD6GY7wCeH/BZ7enx2R23ucGsDCvzGfT+s8e7viOcLynkQx//zC/bkOUWwpcOtTPQ96yA9K6LRrjcbm14HNuyN/XEHX5LnB9wbJLgMh7/1vg60P8zP0mff1yoBOYVlDmHuCD6etrgW8U43fSDz+q7eGeM7PKGe48JQ2x/raIyH9/KzBXUusQ2x+XfsaD6RBbp6RO4M9JAhMkPRG/iYjucVc+4hngf0l6Vkh7NBaT9KiNth67kHSwpCslPS6pA1hH0qO1X1rk+yS9R09KukzSmyVlRqjq4SQBoz9v2W+AJpJz/QaM5RiPpCsiHsl7vw5YGxEbC5blRvqQtPfomyS9mW8bqJukekn/IuleSc+kx/SNPH98Rmu0x+Xegu3W7Kbuh7FrT+btQ+z7twXLfgMckb4+HpgObCj42TmK5392/gf4S0l/TIdaXzZCncwmlYZKV8CsBj1GEryOJOlFKnR4uv7xcX5+Xbr9CUBPwbrt6bMojsuBSyS9l2RIbCXJH9nR1mMoPyEZnvq79LkXeJAkNBARKyUtJBkyfAXwOeDfJb0oIrYO8Xli+CA8oRP5h1F4wn2wa/uD3Z9W8hHgVOCEgna9n2TY9DzgPpIepv9iN2FvCKM9LmOt+0ifO9w+CpfVkQTYlw5RpgMgIq6TtD/wapKfhZ9J+n5EnDXENmaTinvOzMosIp4l6XF6r6Tp+evS9+cA16XlBrwo7UkZ8GJgTUR0DLGLP5D8gdw7IpYXPAbOybkbOCX/5O8C3SQ9NrtzTfr8WpIetCvyep9GU4+dSNqLJJz+V0TcGBEPAVkK/iMZETsi4mcR8X9Jwt+RwMnD1PFB4KSCc+NOSduYH4B3d4xHe0wmTMlFFx8ETo+IVQWrTwF+EhHfiYh7SNpwaEGZ0dR1tMdlrB4iOV8wX+H7h9J95TslrRMkP59tQP8QPzvrBzaIiI3pcXgHyTlzb99NL6rZpOBwZlYZ/0ASOG6U9HJJ85VcEXkDSaD5h4Ly+wJflLQw/cP9AeALQ31wRDwKXAF8U8kVggdJWiTp/ZLemBa7GGgBrpJ0gqRDJL1V0rHp+hXAUen+ZktqHGZfO0jOh/pXkmHMy/PWjaYehZ4DNgLvTuv0MuAr5PVGKbki8m+VXFl6IHAWSe/OY8N85sXp8btY0uGS/hz4JMk5WfkXZOzuGK8AXqrk/nQTuunsSCQdRXLhxD8DT0naO33smRZ5FFgi6RRJh5GcWH9gwcesAE5UcoXmbA19deVoj8tYfQl4paQPSFog6V0k51Hm+wzwNknnpGXOJQn3n07X30gy7HmNkitfD5R0kqSPSXopgKSPS3p9uv3hJEO7T4Rv9WJTgMOZWQVExOMkV+E9AHwHeAK4kqRH4YSIeLJgkytIekJuB74GXMYw4Sx1FsnVep8GHgZ+SjJE9qd0/6vT903AzSS9XOfyfAj6WlqXO4ENDN8rRVr/Y4C7056uUdejUHr+01uAFwD3k1zx928kJ6kP2ETSS/LrtMybgDcOccwGPnM1ydDXC0lOKP86yUnr/1xQdHfH+CPAfJJepQ1D7atIFpGcb/VF4Om8xw/T9f9Jck7XdSRXMG5N657vsyQ9YA+mdd3lfLQxHJcxiYhbgXcD/0hyvtrrgU8BO/LK/Jjk5+3/pnU8D3hvRPwkXR/Aa4CbSL6LR0iuIF5Ics4bJD8TFwB/JAlyWeB1E6m7WbXQzue/mtlkJOlW4FcR8eFK12UyUnIPsfsjorDH0opA0hdI7ul3dKXrYjYZuOfMbBKTlFFyY84jSXqRzCouHdI8Nh2afg/wHpKeOTMbBV+taTa5vRr4NskVjt+rcF3MBiwiuap0JvAkybyvF1a0RmaTiIc1zczMzKqIhzXNzMzMqojDmZmZmVkVmTLnnM2ePTsOOOCAku9n69atzJgxo+T7qUa13Hao7fa77bXZdqjt9tdy26G221+Ott91110bI2LOUOumTDg74IADuPPOO0u+n2XLltHe3l7y/VSjWm471Hb73fb2SlejYmq5/bXcdqjt9pej7ZKGvN8jeFjTzMzMrKo4nJmZmZlVEYczMzMzsyricGZmZmZWRRzOzMzMzKqIw5mZmZlZFXE4MzMzM6siDmdmZmZmVcThzMzMzKyKOJyZmZmZVRGHMzMzM7MqMmXm1iy1iGB7Tx/be4PN23uICPr6g/6A/gj60/cRpMsHHoy4buB9/rpZ05s4bO8szY31lW62mZmZlZnD2Sh1bO/lmI//Inlz4y9Kvr+GOnFoW5aj587k6HkzOXruTA7bJ0umwYHNzMxsKnM4G6XmpjrOf/VhPPHEExy64BDqBHUSdXWiTlAv7fy+TkhKl5Mu3/l14br6OpDE+o4d3Ld6M/eu2swvHlzL9+5cCSSBbeHeOwe2hXs7sJmZmU0lDmejlGmo5+9edjDLYiXtpxxY8v296qh9gGQ4ddVz27l/9WbuSx/XP7CWpb9PAltj/UBgm8XRc2fygnkzObQtS1ODTyc0MzObjBzOqpwk5u85nfl7TufVR+8c2AZ61+5bvYmf3buG797xFABN9XVJYEt7146e68BmZmY2WTicTUL5ge01eYHtqWe3Jb1rq5Ietp/8cQ1X3v58YDt8nyxHpb1rx++/J4fkWirZDDMzMxuCw9kUIYn995rB/nvN4LUv2BeA/v68wJaGtmvvWcMVaWD79QcXM3/P6ZWstpmZmRVwOJvC6urEAbNncMDsGbzumOcD23X3r+WcK+/mqWe3OZyZmZlVGZ+EVGPq6sRh+2QBWNexo8K1MTMzs0IOZzWorbUZgPVbuipcEzMzMyvkcFaDWjINTG+qZ32Hw5mZmVm1cTirUblshnVbPKxpZmZWbcoWziTNknS1pIclPSTppHT5uZIekfSApE/nlT9f0vJ03WnlqmetyGWb2eCeMzMzs6pTzqs1LwSuj4gzJDUB0yUtBk4HXhARXZJyAJKOAM4EjgT2BW6UdGhE9JWxvlNarjXD/as3V7oaZmZmVqAsPWeSWoFTgcsAIqI7IjYBfw98MiK60uXr001OB5ZGRFdEPAksB04sR11rRS7bzPotXUREpatiZmZmeVSOP86SjgUuAR4EjgHuAs4DfgtcA7wK2AG8PyJ+L+ki4LaIuDzd/jLguoi4uuBzzwbOBmhrazt+6dKlJW9LZ2cnLS2T/876P3+im6se7eF/XjGdaQ0a1TZTpe3jVcvtd9trs+1Q2+2v5bZDbbe/HG1fvHjxXRGxaKh15RrWbACOA86NiNslXQh8OF2+B/Bi4ATgKkkHAUOlhV1SZERcQhL6WLRoUbS3t5em9nmWLVtGOfZTas/NXMVVj/6RQ485gYPnjO4HcKq0fbxquf1ue3ulq1Extdz+Wm471Hb7K932cl0QsApYFRG3p++vJglrq4AfRuIOoB+YnS6fn7f9PGBNmepaE3LZ9F5nvijAzMysqpQlnEXEWmClpIXpoiUkQ5w/Bl4OIOlQoAnYCFwLnCkpI+lAYAFwRznqWity2QwA6307DTMzs6pSzqs1zwWuSK/UfAI4C9gKfF3S/UA38PZIToJ7QNJVJAGuFzjHV2oWl3vOzMzMqlPZwllE3AMMdeLb3wxT/gLgglLWqZa1Tmsg01DnnjMzM7Mq4xkCapQkcq0Zz69pZmZWZRzOalgu28y6DvecmZmZVROHsxrW5p4zMzOzquNwVsM8v6aZmVn1cTirYXOyGbZ09bKtu7fSVTEzM7OUw1kNG7zXmXvPzMzMqobDWQ1ra03vdebzzszMzKqGw1kNy7V6lgAzM7Nq43BWwwZmCVjnYU0zM7Oq4XBWw/aY3khjvdxzZmZmVkUczmqYJN9Ow8zMrMo4nNW4OdkM69xzZmZmVjUczmpcLpvxrTTMzMyqiMNZjWtrbfatNMzMzKqIw1mNy2UzbN7ew46evkpXxczMzHA4q3kD9zrb4N4zMzOzquBwVuNyg7ME+KIAMzOzauBwVuM8v6aZmVl1cTircc/PEuCeMzMzs2rgcFbj9prRRH2dfMWmmZlZlXA4q3F1dWJOS8bhzMzMrEo4nBm51oyHNc3MzKqEw5mRy2Z8Kw0zM7Mq4XBm5DxLgJmZWdVwODNy2QzPbu2mu7e/0lUxMzOreQ5nNng7jQ2d7j0zMzOrNIczy7sRrS8KMDMzqzSHM6NtcAon95yZmZlVmsOZDU5+7p4zMzOzyitbOJM0S9LVkh6W9JCkkyR9VNJqSfekj9fklT9f0nJJj0g6rVz1rEV7zWhCcs+ZmZlZNWgo474uBK6PiDMkNQHTgdOAL0TEZ/MLSjoCOBM4EtgXuFHSoRHRV8b61oyG+jpmt2Q8+bmZmVkVKEvPmaRW4FTgMoCI6I6ITSNscjqwNCK6IuJJYDlwYskrWsNy2Qzrt3hY08zMrNIUEaXfiXQscAnwIHAMcBdwHvAB4B1AB3An8E8R8Zyki4DbIuLydPvLgOsi4uqCzz0bOBugra3t+KVLl5a8LZ2dnbS0tJR8P+X2+bt2sGlH8PGTpw1bZqq2fbRquf1ue222HWq7/bXcdqjt9pej7YsXL74rIhYNta5cw5oNwHHAuRFxu6QLgQ8DFwH/AUT6/DngnYCG+IxdUmREXEIS+li0aFG0t7eXpPL5li1bRjn2U27XbbyXXz68fsS2TdW2j1Ytt99tb690NSqmlttfy22H2m5/pdtergsCVgGrIuL29P3VwHERsS4i+iKiH/gazw9drgLm520/D1hTprrWpLbWDM9s7aK3z7MEmJmZVVJZwllErAVWSlqYLloCPChpn7xibwDuT19fC5wpKSPpQGABcEc56lqr5rQ2EwEbO7srXRUzM7OaVs6rNc8Frkiv1HwCOAv4Uno+WgArgL8DiIgHJF1Fco5aL3COr9QsrcFZArbsYO+ZzRWujZmZWe0qWziLiHuAwhPf3jZC+QuAC0pZJ3ve4CwBvp2GmZlZRXmGAAPye84czszMzCrJ4cwAmN2ShLN1nsLJzMysohzODICmhjr2mtHknjMzM7MKczizQXOyGTZ4lgAzM7OKcjizQbnWZtb5ggAzM7OKcjizQZ5f08zMrPIczmxQW2uGjZ3d9PWXfr5VMzMzG5rDmQ3KZZvp6w+e3epZAszMzCrF4cwGDdzrzLfTMDMzqxyHMxuUS2cJ2ODbaZiZmVWMw5kNyp9f08zMzCrD4cwGzRkc1nTPmZmZWaU4nNmg5sZ6Zk5rdM+ZmZlZBTmc2U7aWjOsd8+ZmZlZxTic2U5y2WbPr2lmZlZBDme2k1w2w3rfSsPMzKxiHM5sJ7nWZjZ0dhHhWQLMzMwqweHMdpLLZujpC57b1lPpqpiZmdUkhzPbSa7VswSYmZlVksOZ7SSXTWYJ8EUBZmZmleFwZjtpS3vOfFGAmZlZZTic2U7cc2ZmZlZZDme2k2lN9WQzDe45MzMzqxCHM9tFrjXjnjMzM7MKcTizXXiWADMzs8pxOLNd5FozvpWGmZlZhTic2S5y2WRY07MEmJmZlZ/Dme2irbWZ7t5+Orb3VroqZmZmNcfhzHYxJ5vOErDFQ5tmZmblVrZwJmmWpKslPSzpIUkn5a17v6SQNDtv2fmSlkt6RNJp5aqn5d3rrMMXBZiZmZVbQxn3dSFwfUScIakJmA4gaT7wZ8BTAwUlHQGcCRwJ7AvcKOnQiOgrY31r1uAsAe45MzMzK7uy9JxJagVOBS4DiIjuiNiUrv4C8EEg/+zz04GlEdEVEU8Cy4ETy1FXg1yrZwkwMzOrFJXjijxJxwKXAA8CxwB3AecBS4AlEXGepBXAoojYKOki4LaIuDzd/jLguoi4uuBzzwbOBmhrazt+6dKlJW9LZ2cnLS0tJd9Ppf3dDVs5dV4Df314ZnBZrbR9OLXcfre9NtsOtd3+Wm471Hb7y9H2xYsX3xURi4ZaV65hzQbgOODciLhd0oXAR0l60145RHkNsWyXFBkRl5CEPhYtWhTt7e3Fqu+wli1bRjn2U2n7/P5mmmbOpL39uMFltdL24dRy+9329kpXo2Jquf213Hao7fZXuu3luiBgFbAqIm5P319NEtYOBP6Y9prNA+6WtHdafn7e9vOANWWqq5EMbW7wBQFmZmZlV5ZwFhFrgZWSFqaLlgB3R0QuIg6IiANIAtlxadlrgTMlZSQdCCwA7ihHXS2Ry2Z8Kw0zM7MKKOfVmucCV6RXaj4BnDVcwYh4QNJVJOeo9QLn+ErN8splm1nfsZ6IQBpqlNnMzMxKoWzhLCLuAYY88S1df0DB+wuAC0pbKxtOW2uG7T19dHb1km1urHR1zMzMaoZnCLAh5QbvdebzzszMzMrJ4cyGNDBLwLoOn3dmZmZWTg5nNqRcOr/mBvecmZmZlZXDmQ1pcJYA307DzMysrBzObEitzQ1kGuo8rGlmZlZmDmc2JEnkWjO+IMDMzKzMHM5sWG3ZZtb7RrRmZmZl5XBmw3LPmZmZWfk5nNmwklkCHM7MzMzKyeHMhjUnm6Gzq5dt3b2VroqZmVnNcDizYbX5dhpmZmZl53Bmwxq4Ea1vp2FmZlY+Dmc2LM+vaWZmVn4OZzastnR+TYczMzOz8nE4s2HNmt5IU32d73VmZmZWRg5nNixJzMlmfEGAmZlZGTmc2YjmZDPuOTMzMysjhzMbUVure87MzMzKyeHMRpTLNvtWGmZmZmXkcGYjymUzdOzoZUdPX6WrYmZmVhMczmxEA7MEbPDtNMzMzMrC4cxGNGfwRrQe2jQzMysHhzMb0fNTOLnnzMzMrBwczmxEz09+7p4zMzOzcnA4sxHtOb2Jhjp5CiczM7MycTizEdXVidktGQ9rmpmZlYnDme1WrtWzBJiZmZXLmMOZpPmSXlyKylh1ymWbfSsNMzOzMhl1OJO0n6TfAg8DN6bLzpB0aakqZ9Uh6TlzODMzMyuHsfScfRX4GZAFetJlNwB/NpqNJc2SdLWkhyU9JOkkSf8h6V5J90j6haR988qfL2m5pEcknTaGelqR5bIZnt3aTW9/VLoqZmZmU17DGMqeCPx5RPRLCoCI2Cxp5ii3vxC4PiLOkNQETAceiIh/A5D0j8BHgPdIOgI4EzgS2Be4UdKhEeE5hCpg4HYam7sczszMzEptLD1n64BD8hekIeqp3W0oqRU4FbgMICK6I2JTRHTkFZsBDPz1Px1YGhFdEfEksJwkHFoFDNyIdpPDmZmZWcmNJZx9FvippLOABklvBb4HfGoU2x4EbAC+IekPki6VNANA0gWSVgJ/TdJzBjAXWJm3/ap0mVVALpv0nDmcmZmZlZ4iRv8HV9LrgbOB/Ul6zL4aET8exXaLgNuAkyPidkkXAh0DQ5ppmfOB5oj4d0lfBm6NiMvTdZcBP4+IHxR87tlpfWhrazt+6dKlo27LeHV2dtLS0lLy/VST53b083+XbecvDw5es6C22p6vFr/7AW57bbYdarv9tdx2qO32l6PtixcvvisiFg21btTnnEmqT4PYj8dRh1XAqoi4PX1/NfDhgjJXklxw8O9p+fl56+YBawo/NCIuAS4BWLRoUbS3t4+jamOzbNkyyrGfatLb188//eo6ttNUc23PV4vf/QC3vb3S1aiYWm5/Lbcdarv9lW77WIY110q6WNLJY91JRKwFVkpamC5aAjwoaUFesb8guU0HwLXAmZIykg4EFgB3jHW/VhwN9XXs1ZLxBQFmZmZlMJarNV8JvBX4rqR+4LvAlRFx3yi3Pxe4Ir1S8wngLODSNLD1A38C3gMQEQ9Iugp4EOgFzvGVmpWVy2bYtGNrpathZmY25Y06nEXEH4A/AB+U9DKSoPZLSWsj4gWj2P4eoHBs9U0jlL8AuGC09bPSamttZvnqzkpXw8zMbMob79yajwAPkVxReUDRamNVK5fN+GpNMzOzMhjL9E2zJL1L0i+Bx4F2ktto5EpUN6siuWyGLd1Bb19/patiZmY2pY3lnLM1wO9Irqp8Y0RsLk2VrBrNaW0mgI2d3ew9s7nS1TEzM5uyxhLODo6Ip0tWE6tqbeksAeu37HA4MzMzK6ERw5mkUyPilvTt4ZIOH6pcRNxU9JpZVcml82uu6+iqcE3MzMymtt31nF0MHJW+vmyYMkEyPZNNYbm8njMzMzMrnRHDWUQclff6wNJXx6rVnIFw5p4zMzOzkhrL1ZrXDLP8h8WrjlWrxvo6sk2wfovDmZmZWSmN5T5ni4dZ3l6EetgkMCtTx/oOD2uamZmV0m6v1pT08fRlU97rAQeRTLtkNWBmRu45MzMzK7HR3Epjfvpcl/cakgsBVgIfLXKdrErtkRGP+oIAMzOzktptOIuIswAk/S4ivlb6Klm1mpkRG9Z00dcf1Nep0tUxMzObksZyzlmXpJ0mOJd0jKS3FblOVqVmZUR/wDNbPbRpZmZWKmMJZ/9BMoyZbyXwn8WrjlWzWZmkt8y30zAzMyudsYSzVqCjYNlmYFbRamNVbSCcbfBFAWZmZiUzlnD2IPCmgmVvAB4qXnWsms1Mw9k6307DzMysZMYy8fmHgJ9LegvwOHAIsAR4TSkqZtVnIJz5dhpmZmalM+qes4j4Dck8m78HZgB3AEdFxG9LVDerMk31Ytb0Rs+vaWZmVkJj6TkjIp6S9GmgLSKeLlGdrIrlshnW+YIAMzOzkhnL3JqzJF0J7ACWp8v+QpKv1qwhuWyzhzXNzMxKaCwXBHyF5OrM/YHudNmtwFuKXSmrXrnWDBt8QYCZmVnJjGVYcwmwb0T0SAqAiNggKVeaqlk1ymWb2dDZRX9/UOdZAszMzIpuLD1nm4HZ+Qsk7Qf43LMakstm6OkLntvWvfvCZmZmNmZjCWeXAj+QtBiok3QS8C2S4U6rEbnWDODbaZiZmZXKWMLZp4CrgC8DjcDXgWuAC0tQL6tSba3NgMOZmZlZqYz6nLOICOCL6cNqVC6b9Jx5lgAzM7PSGDGcSTo1Im5JX798hKLdwIqIWFXMyln1yWWTnjPPr2lmZlYau+s5u5hkVgCAy0YoVwfMlvSliDi/KDWzqjStqZ5scwPr3XNmZmZWEiOGs4g4Ku/1gSOVlTQHeBRwOJvictmMzzkzMzMrkbFcEICkekknS3qzpJdIqh9YFxEbgD8bYdtZkq6W9LCkhySdJOkz6ft7Jf1I0qy88udLWi7pEUmnjadxVhq5bLPPOTMzMyuRsUzf9ALgMeD7wAeAq4HHJB07UCYi7hzhIy4Ero+Iw4BjgIeAG0gmT38Beb1uko4AzgSOBF4FXJwfBK2ycq3uOTMzMyuVsfScfZ3kNhpzI+JEYC5wESOfiwaApFbg1IGyEdEdEZsi4hcR0ZsWuw2Yl74+HVgaEV0R8STJXJ4njqGuVkJtrcn8mskFvGZmZlZMYwlnhwJfTG+pMXBrjQuBBaPY9iBgA/ANSX+QdKmkGQVl3glcl76eC6zMW7cqXWZVIJfN0N3bz+btPZWuipmZ2ZSj0fZ+SFoKfC8ifpS37PXAWyLirbvZdhFJz9jJEXG7pAuBjoj4t3T9vwCLgDdGREj6MnBrRFyerr8M+HlE/KDgc88GzgZoa2s7funSpaNqy0R0dnbS0tJS8v1Uo4G237aml6/c28UFJ09jbnZMpy1Oav7u3fZaVMvtr+W2Q223vxxtX7x48V0RsWiodbu7z9l3gIH0Vg98T9KdJL1a84HjSWYJ2J1VwKqIuD19fzXw4XQfbwdeCyyJ55PiqvTzB8wD1hR+aERcAlwCsGjRomhvbx9FVSZm2bJllGM/1Wig7c1PPMNX7r2N/Q97AacsmL37DacIf/ftla5GRdRy26G221/LbYfabn+l2767+5wtL3h/f97rB4H/Hc1OImKtpJWSFkbEI8AS4EFJrwI+BLwsIrblbXItcKWkzwP7kgyd3jGafVnpDcwSsH6Lr9g0MzMrtt3d5+xjAJIagL8huVXGbGAj8EvgOxEx2hOPzgWukNQEPAGcBfweyAA3SAK4LSLeExEPSLqKJAD2AudERN9YG2elkUvn11zX4Ss2zczMim23c2tKmgn8AtgfuB64G9gH+ATw95JeERGbd/c5EXEPyXll+Q4ZofwFwAW7+1wrv5ZMA9Ob6t1zZmZmVgKjmfj8EyQ9ZS+PiK0DC9OrLa9K17+3NNWzajVwOw0zMzMrrtFcavd64O/zgxlA+v4c4A0lqJdVuTnZjOfXNDMzK4HRhLOZwOph1q0CWotXHZssPL+mmZlZaYwmnD0OvHyYdUtITu63GtPW2sz6Ds8SYGZmVmyjCWefB74t6U2S6gAk1Uk6A/hmut5qTC6bYXtPH51dvbsvbGZmZqO22wsCIuKbkvYiCWLflbSR5HYaXcDHI+Ibpa2iVaNca3Kvs3UdXWSbGytcGzMzs6ljNFdrEhGfk3QJ8BKev8/ZrRHRUcrKWfVqyyb3Olu/ZQeH5Gpzeg8zM7NSGFU4A4iILYxyRgCb+gZ6zjb4ogAzM7Oiqp1Zq62o5mQHZgnw7TTMzMyKyeHMxqW1uYFMQx3rPYWTmZlZUTmc2bhI8iwBZmZmJeBwZuOW3IjWw5pmZmbF5HBm45ZrzXhY08zMrMgczmzcclkPa5qZmRWbw5mNW641Q2dXL1s9S4CZmVnROJzZuOUGb0Tr3jMzM7NicTizcctlkxvRrve9zszMzIrG4czGra3VPWdmZmbF5nBm4zbQc+ZZAszMzIrH4czGbdb0Rprq6zy/ppmZWRE5nNm4SWJONuNhTTMzsyJyOLMJybV6lgAzM7NicjizCcllM6zzLAFmZmZF43BmE5LLNvtWGmZmZkXkcGYT0taaoWNHLzt6+ipdFTMzsynB4cwmZHCWAA9tmpmZFYXDmU3InNZ0lgBfFGBmZlYUDmc2IW2eX9PMzKyoHM5sQnKtnl/TzMysmMoWziTNknS1pIclPSTpJElvlvSApH5JiwrKny9puaRHJJ1Wrnra2Ow5vYmGOrHOPWdmZmZF0VDGfV0IXB8RZ0hqAqYDm4A3Al/NLyjpCOBM4EhgX+BGSYdGhC8JrDJ1dWJ2S8YXBJiZmRVJWcKZpFbgVOAdABHRDXSThDMkFW5yOrA0IrqAJyUtB04Ebi1HfW1s2jxLgJmZWdGUa1jzIGAD8A1Jf5B0qaQZI5SfC6zMe78qXWZVaE622T1nZmZmRaKIKP1OkvPJbgNOjojbJV0IdETEv6XrlwHvj4g70/dfBm6NiMvT95cBP4+IHxR87tnA2QBtbW3HL126tORt6ezspKWlpeT7qUbDtf2bD3Rx59peLloyUt6e/Pzdu+21qJbbX8tth9pufznavnjx4rsiYtFQ68p1ztkqYFVE3J6+vxr48G7Kz897Pw9YU1goIi4BLgFYtGhRtLe3F6WyI1m2bBnl2E81Gq7tf+x9jGUrH+Ulp5xKU8PUvQDY3317patREbXcdqjt9tdy26G221/ptpflL2lErAVWSlqYLloCPDjCJtcCZ0rKSDoQWADcUeJq2jgN3E5jQ6eHNs3MzCaqnN0c5wJXSLoXOBb4L0lvkLQKOAn4maT/BYiIB4CrSALc9cA5vlKzeuWySThb53udmZmZTVjZbqUREfcAhWOrP0ofQ5W/ALigxNWyIvD8mmZmZsUzdU8QsrJpGxjW9O00zMzMJszhzCZsr5YMdYJ17jkzMzObMIczm7D6OrFXi29Ea2ZmVgwOZ1YUySwB7jkzMzObKIczK4qcZwkwMzMrCoczK4pc1sOaZmZmxeBwZkWRy2Z4Zms3vX39la6KmZnZpOZwZkWRa20mAjZ2dle6KmZmZpOaw5kVhWcJMDMzKw6HMyuKXGs6S4Cv2DQzM5sQhzMrioFZAnxRgJmZ2cQ4nFlRzG7JIHl+TTMzs4lyOLOiaKyvY8/pTe45MzMzmyCHMyuaOdmMe87MzMwmyOHMiqattdkXBJiZmU2Qw5kVTS6b8a00zMzMJsjhzIom15phY2cXff1R6aqYmZlNWg5nVjRtrc30Bzyz1UObZmZm4+VwZkUzMEuALwowMzMbP4czK5o52YFZAnzemZmZ2Xg5nFnRuOfMzMxs4hzOrGhyg1M4OZyZmZmNl8OZFU2moZ5Z0xt9Ow0zM7MJcDizosplM+45MzMzmwCHMysqzxJgZmY2MQ5nVlRzshk2eFjTzMxs3BzOrKhy2aTnrN+zBJiZmY2Lw5kVVVtrht7+4Llt3ZWuipmZ2aTkcGZFlRu8Ea3POzMzMxsPhzMrqoF7nfl2GmZmZuNTtnAmaZakqyU9LOkhSSdJ2lPSDZIeS5/3yCt/vqTlkh6RdFq56mkTMzhLgHvOzMzMxqWcPWcXAtdHxGHAMcBDwIeBX0bEAuCX6XskHQGcCRwJvAq4WFJ9Getq4zQwrLnB4czMzGxcyhLOJLUCpwKXAUREd0RsAk4HvpUW+xbw+vT16cDSiOiKiCeB5cCJ5airTcy0pnqyzQ2s97CmmZnZuCii9Lc8kHQscAnwIEmv2V3AecDqiJiVV+65iNhD0kXAbRFxebr8MuC6iLi64HPPBs4GaGtrO37p0qUlb0tnZyctLS0l3081Gm3bz//1NvZtqePcFzaXoVbl4+/eba9Ftdz+Wm471Hb7y9H2xYsX3xURi4Za11DSPe+8n+OAcyPidkkXkg5hDkNDLNslRUbEJSShj0WLFkV7e3sRqjqyZcuWUY79VKPRtv3Ax25jR08f7e0nl75SZeTvvr3S1aiIWm471Hb7a7ntUNvtr3Tby3XO2SpgVUTcnr6/miSsrZO0D0D6vD6v/Py87ecBa8pUV5sgz69pZmY2fmUJZxGxFlgpaWG6aAnJEOe1wNvTZW8HrklfXwucKSkj6UBgAXBHOepqE5drbWZ9RxflGDI3MzObaso1rAlwLnCFpCbgCeAsknB4laR3AU8BbwaIiAckXUUS4HqBcyKir4x1tQnIZTN09/WzeXsPs6Y3Vbo6ZmZmk0rZwllE3AMMdeLbkmHKXwBcUMo6WWnkWp+fJcDhzMzMbGw8Q4AV3cCNaD1LgJmZ2dg5nFnRDc4S0OGLAszMzMbK4cyKLn9Y08zMzMbG4cyKriXTwIymetZv8bCmmZnZWDmcWUkM3E7DzMzMxsbhzEpiTjbjnjMzM7NxcDizkmhrbfY5Z2ZmZuPgcGYlkctmWNexw7MEmJmZjZHDmZVELpthR08/W7p6K10VMzOzScXhzEqibeB2Gr4owMzMbEwczqwkBm9E64sCzMzMxsThzEoi1+pZAszMzMajbBOfW22Zk02GNa+/fy3PbeuucG2KY/mKHp787ZMl+/zxXDsxnsst6gTTGuuZ1lQ/+Dy9qZ7mxnqmNzUky9LljfVC0jj2YmZm4+VwZiXR2tzAvD2mcf0Da7n+gbWVrk7xPPxgpWtQVvV1YnpjPXX0MfOOm/NCXBLgmpvqmV4Q9KY11jM908Derc3M22Ma8/aYRra5sdJNMTObNBzOrCQkcdM/tbOte+pcrfmb3/yWU045uaT7EOPopRrjJn39wY6ePrb39LG9O3ne1p283jHwuqeP7d296XM/j/9pJXvMnjVYdkdPH5u397C9p48d3X1sSz+rq7d/yH3OnNbI3FnT0rA2fTC0zdtjOnP3mMbMaQ5vZmYDHM6sZJoa6mhqaKp0NYqmpUnMmj512jMWy5atp739hbstNxD8tnb18vTmHax6bjurnts2+Lzima38+rGNbO/p22m7bHPDrqEtDXPz95hO67QGD6+aWc1wODOzoqmvEzMyDczINJBrbeaY+bN2KRMRPLetZzC0rc4LcE89s43fLd/I1u6C8JZpYO4eu/a8zZ2V9LztMb3R4c3MpgyHMzMrK0nsOaOJPWc08YJ5s3ZZHxFs3t5T0Ov2/OvbnniWzoKbG09rrGffWc3MzetxmztrGnPT57bWZurrHN7MbHJwODOzqiIlw8ezpjdx1NyZu6yPCDq297LyuW2s3pT0vOU/3796M89u3fkK4fo6sXdrc9L7lhfa9s173dxYX64mmpmNyOHMzCYVScyc3sjM6TOHDG8A27p7WbNpR15o2zYY3m574hnWduygv+A+JLNbmp4PbHmhbdXmPtqe7qCxvo7GetGQPjfW1dFQr3R5nXvmzKxoHM7MbMqZ3tTAIbkWDsm1DLm+p6+ftZt3sGbTzr1uqzdt55G1W7jp4fU7X3l66693u0+JJKjVicaGOhrq0hBXn4a4ujoaG0RDXR1N6bKG+jqa0jIzMg3MnNa4y6N1WkP6nLzPNLiHz2yqczgzs5rTWF/H/D2nM3/P6UOujwie2drN6ue2c9Otd3LY4UfS3ddPb1/Q299Pd1/Qm77feXnyuqevn560TE9fPz39A68H1vXT3dvP1u4+enqT951dvWze3sO2goshCjU31j0f3JrzQ9xQwW7n982Ndb5wwmwScDgzMysgidktGWa3ZHju8Qbaj96nbPvu7u2nY0cPm7cnj46C5/xHx/bkliUPr91Cx/YetnSNfF/Bg2bP4O/bD+b1L5xLY71n7zOrVg5nZmZVpKmhbjAYjlVvXz9bdiQ9cPkBb/P2HjZt6+Fn9z7NB66+l/++aTnnLD6YN7xwHk0NDmlm1cbhzMxsimior2OPGU3sMWPomyW/t/1gfvnQer5002N86Af38aVfLue9iw/mjOPn+Vw2syri/zKZmdUISbziiDauOedkvnHWCczJZviXH91P+2eW8e1bV7CjZ+Tz3cysPBzOzMxqjCQWL8zxo/e+hO+860TmzprGR655gFM/fTNf/82TbN/NRQlmVloOZ2ZmNUoSL10wh++/5ySufPeLOGjODD7+0wd56adv5mu3PMG27pEvMDCz0vA5Z2ZmNU4SLzl4Ni85eDa3P/EM/33Tci74+UP8z68eZ8ncYNFJvbRk/OfCrFzK1nMmaYWk+yTdI+nOdNkxkm5Nl/9EUmte+fMlLZf0iKTTylVPM7Na9qKD9uLyv30RP/j7kzh67ky+/2gPp3zqJi666TE6dvRUunpmNaHcw5qLI+LYiFiUvr8U+HBEHA38CPgAgKQjgDOBI4FXARdL8qVEZmZlcvz+e/Ktd57IR17czPH77cFnf/Eop3zyJr5446Ns3uaQZlZKlT7nbCFwS/r6BuBN6evTgaUR0RURTwLLgRMrUD8zs5p20Kx6LnvHCfz03FN48UF78cUbH+OUT93E537xCM8VTDBvZsWhiNh9qWLsSHoSeA4I4KsRcYmk3wGfiohrJP0/4GMRkZV0EXBbRFyebnsZcF1EXF3wmWcDZwO0tbUdv3Tp0pK3o7Ozk5aWoefrm+pque1Q2+1322uz7bBr+5/q6OPax3u4c10fzfWwZL9GTjuwkdamqTctlL/72m1/Odq+ePHiu/JGEndSzjM8T46INZJywA2SHgbeCXxJ0keAa4GB/4YN9Vu+S4qMiEuASwAWLVoU7e3tJal4vmXLllGO/VSjWm471Hb73fb2SlejYoZq//8BHlm7hYtuXs5P713DTav6edtJ+/Pulx7EnOzYZzaoVv7ua7f9lW572cJZRKxJn9dL+hFwYkR8FnglgKRDgT9Pi68C5udtPg9YU666mpnZyBbuneW/3/pCzluygC/fvJxLf/0E3751BW944Vz2mlH6gNZQL5ob62luqEueG+vJpK8zjemyhnqaG+vIFJSrr5t6vXw2tZQlnEmaAdRFxJb09SuBj0vKpWGtDvhX4CvpJtcCV0r6PLAvsAC4oxx1NTOz0Tsk18IX3nIs/5iGtB/cvZq+/tKeLhMRTGQXjfWiuSEJcZk0wA0Et+a8Zds3dfFUZgUL27IctncrM6c3Fq8RZiMoV89ZG/AjSQP7vDIirpd0nqRz0jI/BL4BEBEPSLoKeBDoBc6JCN+y2sysSh04ewafffMxfPbNx5Rlf339QVdvHzt6+tnR08eOnj66egde97Ojt4+u9PXO5QbWJc87etLXedtv3t7Djp5+1jzby83XPDC4z71bm1m4d5bD9s6yMH0ckmvxvKRWdGUJZxHxBLDLb2xEXAhcOMw2FwAXlLhqZmY2CdXXielNDUwfeo73orj55ps57LgX8/DaLTySPh5eu4VbH3+G7r7+wXocOHtGEtrasml4a2XeHtOo8/CpjZNv+WxmZjYESewzcxr7zJzG4oW5weU9ff2s2Lh1MLQ9vHYL967axM/ufXqwzIymeha07dzLdtjerew5o4Rp0qYMhzMzM7MxaKyvY0FblgVtWV6XNybU2dXLo+vye9k6+N8H1rL09ysHy8zJZpLAlvayHb5PK4fv0+qLFGwnDmdmZmZF0JJp4Lj99uC4/fYYXBYRbNjStVMv2yPrOvjObX+iqzcZGp05rZFTDpnNSxfM5tRD57DvrGmVaoJVCYczMzOzEpFErrWZXGszpx46Z3B5X3+w4pmt3L96M795bCO3PLaBn92XDIsePGcGpx46h1MXzOFFB+3J9Cb/qa41/sbNzMzKrL5OHDynhYPntHD6sXOJCB5b38ktj27glsc2cuXtT/GN366gqb6OEw7cg5cumMNLF8zmiH1aSe98YFOYw5mZmVmFSeLQtiyHtmX525cexI6ePn6/4llueXQDv35sI5+87mE+eR3Mbsmkw5+zOeWQOVNqRgZ7nsOZmZlZlWlurE97y5Kh0HUdO/j1Yxu55dEN/OrRDfzoD6sBOGKf1nQIdDbHH7CH77k2RTicmZmZVbm21mbOOH4eZxw/j/7+4IE1Hdzy2AZueXQDl/76Cb7yq8eZ1ljPiw/ak1MPTULdwXNmeAh0knI4MzMzm0Tq6sTR82Zy9LyZnLP4EDq7ernt8Wf49WPJEOjHfvIgAHNnTRu8AnS/PafTUC8a6kR9XR31EvWD7zXk+4jSTsNlw3M4MzMzm8RaMg284og2XnFEGwArn902OAT6s/ue3uk+a2NVf8PPqa9LQ9uwga6O+jpRJxBJT11hh91AD54G37NLucJtB1cNs20p5dRFe3vp9zMchzMzM7MpZP6e0/mrF+3HX71oP3r7+rl39WY2bumirz/o7Q/6I+jti8H3ff39ea/TMv3B8ieeZP5+++eVgb7+/l3KDbwfmPA+SJ/TjreB/rfnO+Ki4H1+mRhym8L1pdbUV9nhYIczMzOzKaqhvm6nm+KOxbL61bS3LyxyjSaHZcuWVXT/dRXdu5mZmZntxOHMzMzMrIo4nJmZmZlVEYczMzMzsyricGZmZmZWRRzOzMzMzKqIw5mZmZlZFXE4MzMzM6siDmdmZmZmVcThzMzMzKyKOJyZmZmZVRGHMzMzM7Mq4nBmZmZmVkUUEZWuQ1FI2gD8qQy7mg1sLMN+qlEttx1qu/1ue+2q5fbXctuhtttfjrbvHxFzhloxZcJZuUi6MyIWVboelVDLbYfabr/bXptth9pufy23HWq7/ZVuu4c1zczMzKqIw5mZmZlZFXE4G7tLKl2BCqrltkNtt99tr1213P5abjvUdvsr2nafc2ZmZmZWRdxzZmZmZlZFHM6GIOlVkh6RtFzSh4dYL0lfStffK+m4StSzFCTNl3SzpIckPSDpvCHKtEvaLOme9PGRStS1FCStkHRf2q47h1g/lb/7hXnf6T2SOiS9r6DMlPnuJX1d0npJ9+ct21PSDZIeS5/3GGbbEf+NmAyGaf9nJD2c/mz/SNKsYbYd8fek2g3T9o9KWp33s/2aYbad1N/9MG3/Xl67V0i6Z5htJ/X3DsP/jau63/2I8CPvAdQDjwMHAU3AH4EjCsq8BrgOEPBi4PZK17uI7d8HOC59nQUeHaL97cBPK13XErV/BTB7hPVT9rsvaGc9sJbkPjxT8rsHTgWOA+7PW/Zp4MPp6w8Dnxrm2Iz4b8RkeAzT/lcCDenrTw3V/nTdiL8n1f4Ypu0fBd6/m+0m/Xc/VNsL1n8O+MhU/N7TNgz5N67afvfdc7arE4HlEfFERHQDS4HTC8qcDnw7ErcBsyTtU+6KlkJEPB0Rd6evtwAPAXMrW6uqMmW/+wJLgMcjohw3dq6IiLgFeLZg8enAt9LX3wJeP8Smo/k3ouoN1f6I+EVE9KZvbwPmlb1iZTDMdz8ak/67H6ntkgT8JfDdslaqjEb4G1dVv/sOZ7uaC6zMe7+KXcPJaMpMepIOAF4I3D7E6pMk/VHSdZKOLG/NSiqAX0i6S9LZQ6yvie8eOJPh/4Geqt89QFtEPA3JP+JAbogytfIz8E6SXuKh7O73ZLL6h3RI9+vDDGtN9e/+pcC6iHhsmPVT6nsv+BtXVb/7Dme70hDLCi9pHU2ZSU1SC/AD4H0R0VGw+m6S4a5jgP8Gflzm6pXSyRFxHPBq4BxJpxasr4Xvvgn4C+D7Q6yeyt/9aNXCz8C/AL3AFcMU2d3vyWT0P8DBwLHA0yTDe4Wm+nf/VkbuNZsy3/tu/sYNu9kQy0ry/Tuc7WoVMD/v/TxgzTjKTFqSGkl+aK+IiB8Wro+IjojoTF//HGiUNLvM1SyJiFiTPq8HfkTSjZ1vSn/3qVcDd0fEusIVU/m7T60bGKZOn9cPUWZK/wxIejvwWuCvIz3RptAofk8mnYhYFxF9EdEPfI2h2zRlv3tJDcAbge8NV2aqfO/D/I2rqt99h7Nd/R5YIOnAtAfhTODagjLXAv8nvXLvxcDmge7QyS495+Ay4KGI+PwwZfZOyyHpRJKfo2fKV8vSkDRDUnbgNcnJ0fcXFJuy332eYf/3PFW/+zzXAm9PX78duGaIMqP5N2JSkvQq4EPAX0TEtmHKjOb3ZNIpOHf0DQzdpin73QOvAB6OiFVDrZwq3/sIf+Oq63e/UldMVPOD5Iq8R0muyviXdNl7gPekrwV8OV1/H7Co0nUuYttPIemmvRe4J328pqD9/wA8QHKlym3ASypd7yK1/aC0TX9M21dT333avukkYWtm3rIp+d2TBNCngR6S/xG/C9gL+CXwWPq8Z1p2X+Dnedvu8m/EZHsM0/7lJOfUDPzuf6Ww/cP9nkymxzBt/076O30vyR/cfabidz9U29Pl3xz4Pc8rO6W+97Qdw/2Nq6rffc8QYGZmZlZFPKxpZmZmVkUczszMzMyqiMOZmZmZWRVxODMzMzOrIg5nZmZmZlXE4czMqpKk/SR1Sqof5/adkg6qpjqZmY2Gw5mZFYWkd0i6T9I2SWsl/Y+kWWPYfoWkVwy8j4inIqIlIvrGU5902yfGs22p6jTOOnxT0n+Wa39mVnkOZ2Y2YZL+CfgU8AFgJvBiYH/ghvRO2mZmNkoOZ2Y2IZJagY8B50bE9RHRExErgL8kCWh/k5b7qKSrJX1P0hZJd0s6Jl33HWA/4CfpsOEHJR0gKdI5/5C0TNJ/SvpdWuYnkvaSdIWkDkm/l3RAXr1C0iGS9k3LDzy2SYq0zMGSbpL0jKSN6WfNGkOd9pV0raRnJS2X9O68/X9U0lWSvp229wFJi4Y5hpL0BUnrJW2WdK+koySdDfw18MGBNuft9weSNkh6UtI/Fux3yOOcrv+QpNXpukckLZnI929mxedwZmYT9RKgGfhh/sJIJki/DvizvMWnA98H9gSuBH4sqTEi3gY8BbwuHTb89DD7OhN4GzAXOBi4FfhG+nkPAf9euEFErEk/syUiWkgmbF6arhbwCZIpWg4nmdT4o+l2o6nTd0mmwNkXOAP4r4Kw8xfpvmaRTAl00TDteiVwKnBoWvYtwDMRcQlwBfDptA6vk1QH/IRkGp25wBLgfZJOy/u8IY+zpIUkU3CdEBFZ4DRgxTB1MrMKcTgzs4maDWyMiN4h1j2drh9wV0RcHRE9wOdJQt2Lx7Cvb0TE4xGxmST4PR4RN6b7/j7wwpE2lvQh4DDgnQARsTwiboiIrojYkNbpZaOpiKT5JPP0fSgidkTEPcClJOFxwG8i4ufpOWrfAY7Z9ZOAZJ7DbFo3RcRDEfH0MGVPAOZExMcjojs9r+5rJMF1wHDHuQ/IAEekoXhFRDw+mvaaWfk4nJnZRG0EZg8M9RXYJ10/YOXAi4jo5/lep9Fal/d6+xDvW4bbUNKrgfOA10fE9nRZTtLSdJivA7icncPkSPYFno2ILXnL/kTSmzVgbd7rbUDzUMcpIm4i6VX7MrBO0iXpcPFQ9gf2lbRp4AH8M9CWV2bI4xwRy4H3kfQOrk/bPpbjb2Zl4HBmZhN1K9AFvDF/oaQZwKuBX+Ytnp+3vg6YB6xJF0WpKpgO530L+MuIWJm36hPpfl8QEa0k58cpb/1IdVoD7Ckpm7dsP2D1eOoYEV+KiOOBI0mGNz8wTB1WAk9GxKy8RzYiXpNXZtjjHBFXRsQpJCEvSC7kMLMq4nBmZhOSDjF+DPhvSa9Kz206gGSYcRXJcN6A4yW9Me09eh9JqLstXbcOKOp9yWDwgoVrgH+NiN8UrM4CncAmSXN5PhANGLZOacj7HfAJSc2SXgC8i+QcsbHW8QRJL5LUCGwFdpAMQQ5VhzuAjvTE/mmS6tOLB07IKzPkcZa0UNLLJWXSfWzP24+ZVQmHMzObsPRk+X8GPgt0ALeT9PAsiYiuvKLXkJzs/hzJuVlvTM+LgqQX61/Tobr3F7F6xwELgc/nX7WZrvtYun4z8DMKLmoYRZ3eChxA0iv1I+DfI+KGcdSxleS8sedIhkafITmWAJeRnCO2SdKP0/PXXgccCzxJMmx8KcktTAYMd5wzwCfTbdYCOZLvzcyqiCJKNpJgZjZI0keBQyLibypdl6nMx9ls8nPPmZmZmVkVcTgzMzMzqyIe1jQzMzOrIu45MzMzM6siDmdmZmZmVcThzMzMzKyKOJyZmZmZVRGHMzMzM7Mq4nBmZmZmVkX+PzywSW3BSPrLAAAAAElFTkSuQmCC\n",
"text/plain": [
"<Figure size 720x432 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"plt.figure(figsize=(10, 6))\n",
"plt.plot(to_numpy(optimizer.objectives[optimizer.objectives > 0]))\n",
"plt.title(\"Objective as optimization goes\", fontsize=14)\n",
"plt.xlabel(\"Optimization steps\", fontsize=12)\n",
"plt.ylabel(\"Objective\", fontsize=12)\n",
"plt.grid()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Green (Initial guess for rest state) deploys to Black\n",
"\n",
"Blue (Optimized rest state) deploys to Yellow\n",
"\n",
"Red is the Target Shape\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "70afc138cf264b47b4fddfd6b9ae8624",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Renderer(camera=PerspectiveCamera(children=(DirectionalLight(color='white', intensity=0.6, position=(-0.497634…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"4"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"p = mp.plot(np.array(optimizer.solid.v_def) @ rot.T, tNP, shading=shadingOptions)\n",
"# p.add_points(np.array(optimizer.solid.v_def)[pin_idx, :] @ rot.T, shading={\"point_color\":\"black\", \"point_size\": 0.2})\n",
"p.add_edges(np.array(v_init_rest) @ rot.T, beNP, shading={\"line_color\": \"green\"})\n",
"p.add_edges(vNP @ rot.T, beNP, shading={\"line_color\": \"red\"})\n",
"p.add_edges(np.array(v_eq_init) @ rot.T, beNP, shading={\"line_color\": \"black\"})\n",
"p.add_edges(np.array(optimizer.solid.v_rest) @ rot.T, beNP, shading={\"line_color\": \"blue\"})\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"v_rest_optim_g = optimizer.solid.v_rest.clone().detach() #bookkeeping"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Add point load to the right most vertices\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"maxX = torch.min(v[:, 0])\n",
"f_point_idx = torch.arange(v.shape[0])[v[:, 0] > maxX - 0.01*aabb[0]]\n",
"\n",
"f_point = torch.zeros(size=(f_point_idx.shape[0], 3))\n",
"f_point[:, 2] = -5e3\n",
"\n",
"optimizer.solid.add_point_load(f_point_idx, f_point)\n",
"optimizer.set_params(optimizer.params)\n",
"v_def_optim_g_under_point = optimizer.solid.v_def.clone().detach() #bookkeeping"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"optimizer.reset_BFGS()\n",
"optimizer.optimize(step_size_init=1e-4, max_l_iter=10, n_optim_steps=20)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Green (Optimum rest state under gravity) deploys to Black with the additional point load\n",
"\n",
"Blue (Optimized rest state) deploys to Yellow\n",
"\n",
"Red is the Target Shape\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"p = mp.plot(np.array(optimizer.solid.v_def) @ rot.T, tNP, shading=shadingOptions)\n",
"# p.add_points(np.array(optimizer.solid.v_def)[pin_idx, :] @ rot.T, shading={\"point_color\":\"black\", \"point_size\": 0.2})\n",
"p.add_edges(np.array(v_rest_optim_g) @ rot.T, beNP, shading={\"line_color\": \"green\"})\n",
"p.add_edges(vNP @ rot.T, beNP, shading={\"line_color\": \"red\"})\n",
"p.add_edges(np.array(v_def_optim_g_under_point) @ rot.T, beNP, shading={\"line_color\": \"black\"})\n",
"p.add_edges(np.array(optimizer.solid.v_rest) @ rot.T, beNP, shading={\"line_color\": \"blue\"})\n"
]
}
],
"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
}